Skip to main content

Plan Engine — the dev-workflow graph

The Plan Engine is a live Postgres graph that is the daily-driver source of truth for plans and tasks. It is editable and actionable from two clients — Claude (via MCP) and the admin UI — and is kept bidirectionally synced with GitHub issues/PRs and Vercel deploys.

It is the dev-workflow graph and must never mix with tenant/client data (see Product SoR and the two-database split in Platform map).

One graph, three views, actionable from two clients. The graph is the canonical end-state source of truth; GitHub stays a synced mirror (its elimination is deferred).

Where it lives

PieceLocation
Schema + migrations@tedos/db (packages/db) — Drizzle/Postgres, in a dedicated plan schema
APIThe Plan API in @tedos/api, prefix /plan/*
MCP9 plan_* tools wrapping the same service (one engine, two clients)
Admin/plan (editable tree), /roadmap (timeline), /work (where-we-stand)

Data model — 5 tables (plan schema)

TablePurpose
plan_nodeThe tree — kind (effort · sub · milestone · roadmap · note), status, role, priority, timeline_*, outcome_label (positive · negative · gold)
plan_dependencyThe DAG — depends_on / blocks, with a cycle-guard on insert
plan_versionAppend-only history — every write appends a row (before / after jsonb)
plan_linkExternal work — issue / pr / deploy / branch from github / vercel; the issue#↔node map
sync_eventIdempotency / loop-prevention — unique(source, payload_hash) dedupes the bidirectional sync

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

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 wrapping the same service layer, so Claude and the admin hit one engine, two clients — no second implementation.

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: HMAC-verified ingress webhooks update the graph; Vercel deploys land as plan_link(deploy).
  • Reconciliation: last-write-wins with a drift sentinel; sync_event dedupes; loop-prevention via origin tags + echo-skip (a change the bridge itself made is not re-emitted).

Admin views

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

Deploy topology

Deployed to the Fly app tedos-api (the same app as the product API) as two process groups: web (the Fastify API: /plan/*, webhooks, MCP) and worker (the pg-boss queue draining the graph→GitHub mirror jobs). See Deploy topology.

Why a graph instead of files

Plans used to be MDX files read from disk, editable only via an IDE + PR. The Plan Engine moves them to an editable, actionable graph: plan → subplans (parent + dependsOn + timeline), with the roadmap as a projection that stays aligned by construction. The decision records are ADR-004 (plans → graph) and ADR-005 (tasks → graph, GitHub mirrored) — see Decisions.