Topological Sort
How Kahn's algorithm orders Kit execution across the spec graph — what batch groups are, how dependency resolution works, and how circular references are handled.
Topological Sort
Before any Kit runs, the generation pipeline must determine the order in which spec nodes should be processed. A service cannot be generated before the entities it depends on exist. An API endpoint cannot be generated before its service. A UI component cannot be generated before its data types.
computeTopologicalOrder(projectId) runs Kahn's algorithm over the spec graph's structural edges to produce a linearized build order where no node is processed before its dependencies.
Kahn's Algorithm
Kahn's algorithm processes a directed acyclic graph (DAG) by repeatedly removing nodes with no remaining in-edges (nodes whose dependencies have all been processed):
1. Compute in-degree for every node (count incoming dependency edges)
2. Initialize queue with all nodes whose in-degree = 0 (no dependencies)
3. While queue is not empty:
a. Dequeue a node N
b. Add N to the output order
c. For each node M that N points to:
- Decrement M's in-degree
- If M's in-degree reaches 0, add M to the queue
4. If output order length < total nodes → cycle detectedThe edges used for topological ordering are the spec graph's structural edges: contains, has_field, has_operation, flow_next, and implements. Semantic edges like guards or used_by are not used for ordering — they describe relationships, not dependencies.
Batch Groups
Kahn's algorithm naturally produces batches: groups of nodes that have no dependencies on each other and can be processed in parallel. The full generation pipeline uses 15 batches:
| Batch | Type | Contents |
|---|---|---|
| 0 | Deterministic | ScaffoldStep — package.json, tsconfig.json, Docker, .env template |
| 1 | Deterministic | SharedTypesStep — error types, response wrappers, utility types |
| 1 | Deterministic | DesignSystemStep — tailwind.config.ts, global CSS, theme tokens |
| 2 | LLM | EntityKit — Drizzle schema tables |
| 2 | LLM | SchemaKit — Zod validation schemas |
| 2 | LLM | GuardKit — auth/validation middleware |
| 3 | LLM | AuthKit — login, register, session, JWT |
| 4 | Deterministic | MigrationStep — drizzle-kit generate from schema files |
| 5 | LLM | ServiceKit — service class skeletons |
| 6 | LLM | OperationKit — method implementations |
| 7 | LLM | EndpointKit — Hono route handlers |
| 8 | LLM | APIClientKit — typed frontend client from endpoint contracts |
| 9 | LLM | ComponentKit — React UI components |
| 10 | LLM | PageKit — Next.js pages |
| 11 | LLM | LayoutKit — app shell, navigation, layout wrapper |
| 12 | LLM | WorkflowKit, WorkflowStepKit, NotificationKit |
| 13 | Deterministic | TestInfraStep — Vitest config, test DB setup, test utilities |
| 14 | Deterministic | CICDStep — GitHub Actions, Dockerfile, Railway config |
All Kits within the same batch number have no inter-dependencies and can execute concurrently. The BatchCoordinator dispatches them in parallel via the ContainerPlatform job queue.
What Nodes Enter the Sort
Only nodes with a spec node type enter the topological sort. These are the implementation targets:
spec_entity— domain entities (become Drizzle tables, service types, Zod schemas)spec_api_endpoint— API routes (become Hono handlers)spec_service— application servicesspec_guard— access control definitionsspec_page/spec_component— UI elementsspec_workflow— Inngest workflow definitions
Each spec node maps to a KitType based on its artifact_type. The BatchCoordinator uses this mapping to route each node to the correct Kit.
Cycle Detection
If the spec graph contains a cycle — for example, entity A has a field referencing entity B, and entity B has a field referencing entity A — Kahn's algorithm will detect it:
After the sort completes, if outputOrder.length < totalNodes, some nodes were never added to the queue. Those nodes are in a cycle. The generation pipeline:
- Identifies the cycle members by diffing the output order against the full node set
- Returns them in a separate
cycleGroupsarray in theTopologicalOrderResult - Pauses the generation run and surfaces the cycle in the project dashboard
Cycles in the spec indicate a modeling error that must be resolved before generation can proceed. The fix is usually to introduce a reference field (a foreign key pointer) instead of embedding the full entity, breaking the circular dependency.
Dependency Resolution Details
Within a batch, nodes are independent by definition (no edges between them). Across batches, the ordering ensures:
- Fields before entities that own them —
has_fieldedges are traversed to determine entity processing order. If entityOrderhas a field of typeCustomer,Customeris placed in an earlier batch. - Entities before services —
has_operationandusesedges ensure services run after all their entity dependencies. - Services before endpoints —
implementsedges ensure route handlers run after service definitions. - Types before components — UI component nodes run after all the data type nodes they depend on via
props_typeedges.
The result is a deterministic build order that matches how a human engineer would sequence the work: foundation first, then logic, then API surface, then UI.