Saltar al contenido principal

Plan Engine — the dev-workflow graph (live)

:::note Contenido en inglés Esta página del wiki se sincroniza desde la base de conocimiento en inglés y todavía no está traducida. :::

canonical · tech-lead · updated 2026-09-05 · source

This describes what IS. The Plan Engine is live: a Postgres graph that is the daily-driver source of truth for plans + tasks, editable and actionable from both Claude (MCP) and the admin UI, kept bidirectionally in sync with GitHub issues/PRs and Vercel deploys. Decision records: decisions/adrs/ADR-004-plan-engine.md (plans → graph), decisions/adrs/ADR-005-tasks-on-graph.md (tasks → graph, GitHub mirrored), decisions/adrs/ADR-006-effort-workflow-trace.md (effort schema = training-trace schema). Built as effort #560.

1. What it is

One graph, three views, actionable from two clients. The graph is the canonical end-state SoT; GitHub stays a synced mirror (elimination deferred — see ADR-005). It is the dev-workflow graph and must never mix with tenant/client data (see §7 and .claude/rules/workflow-vs-product.md).

  • Package: @tedos/db (packages/db) — the Drizzle/Postgres schema + migrations.
  • API: the Plan API in @tedos/api (apps/api), prefix /plan/*.
  • MCP: 9 plan_* tools in apps/api/src/mcp wrapping the same service (one engine, two clients).
  • Admin: /plan (editable tree), /roadmap (timeline), and /work (repointed to the graph).

2. Data model — 5 tables (Postgres plan schema)

@tedos/db lives in a dedicated plan Postgres schema (isolating it from @tedos/api's own mission_snapshot migration journal on the same DATABASE_URL).

TablePurposeKey columns
plan_nodethe treeid, parent_id, kind(effort·sub·milestone·roadmap·note), title, body, status(draft·todo·in_progress·blocked·done·abandoned), role, priority, timeline_start/due, position, outcome_label(positive·negative·gold), created_at/updated_at
plan_dependencythe DAGsource_id, target_id, relation(depends_on·blocks), unique(source,target,relation); cycle-guard on insert
plan_versionappend-only historynode_id, changed_at, actor, op(create·update·status·link·move·delete), before/after jsonb — every write appends a row
plan_linkexternal worknode_id, kind(issue·pr·deploy·branch), provider(github·vercel), external_id, url, state, synced_at, unique(node_id,kind,external_id) — the issue#↔node map
sync_eventidempotency / loop-preventionsource(github·vercel·graph), external_id, payload_hash, processed_at, unique(source,payload_hash)

The schema is also the training-trace dataset schema (ADR-006): plan_version is the trajectory store, outcome_label carries the auto/gold reward signal.

3. API + MCP surface

  • Plan API (apps/api, Fastify v5, validated with @fastify/type-provider-zod): CRUD for nodes / dependencies / links, plus tree, roadmap, and history projections. Operator-gated. Every write appends a plan_version row by construction.
  • MCP server (apps/api/src/mcp): 9 plan_* tools that wrap the same service layer — Claude and the admin hit one engine, two clients. No second implementation (.claude/rules/kit-engine-boundary.md).

4. Sync bridge + loop-prevention

Bidirectional, so the graph (SoT) and GitHub (mirror) never drift:

  • graph → GitHub (mirror): pg-boss jobs propagate node create/update/status to GitHub issues + Projects v2 status.
  • GitHub → graph + Vercel → graph: ingress webhooks (HMAC-verified) update the graph; Vercel deploys land as plan_link(deploy).
  • Reconciliation: last-write-wins (LWW) with a drift sentinel; sync_event (unique(source,payload_hash)) dedupes; loop-prevention via origin tags + echo-skip (a change the bridge itself made is not re-emitted).

5. Admin views (3)

  • /plan — editable plan tree + node-detail editor (create/link issue, start, status).
  • /roadmap — the timeline projection of the same graph (aligned by construction; not a doc).
  • /work — repointed from live GitHub GraphQL to the graph (the "where we stand" surface).

All render the 4 mandatory states (loading/empty/error/success). These are builder-platform pages; the Paper design pass is deferred (flagged for Designer review).

6. Deploy topology

Deployed to Fly app tedos-api (the same app as Comprender E1 — see comprender-mvp-e1.md), with two process groups:

  • web — the Fastify API (/plan/*, webhooks, MCP).
  • worker — the pg-boss queue draining the graph→GitHub mirror jobs.

7. DB-separation decision + upgrade path

Decided (recorded here as the durable home): the plan training graph and the product system-of-record currently share one Fly MPG (managed Postgres) cluster, isolated by Postgres schema (plan vs product).

  • Why it's acceptable now: it honors ADR-006's intent — the training corpus is kept apart from tenant PII — and no real tenant data exists yet.
  • It is a documented compromise, not the end state. ADR-006 wants the training corpus physically apart from tenant data.
  • Upgrade path (tracked): move the plan graph to a dedicated cluster before any real tenant-PII volume lands, or before any external training-data export. Until then: schema isolation + the hard rule that no plan_* table ever holds tenant/client data.
  • Host migration in flight (effort #1604): a Fly→Render Postgres migration + rollback design (design-only, not yet executed) is a candidate moment to also resolve this upgrade path — see render-postgres-migration-plan.md.