12. Build plan
Structured for fleet execution. The schedule is not set by labor, it is set by three things that do not compress:
1. Contract freeze. Parallel agents cannot negotiate interfaces mid-flight. Twenty agents building against an unfrozen QueryIR produce twenty incompatible packages that each pass their own tests. 2. Verification against live services. RLS enforcement, PostgREST embed depth, pgmq visibility timeouts, Bun on Vercel. Wall-clock bound regardless of how many agents run. 3. Review bandwidth. Agents produce competent-average by default. The TUI feel, the error message wording, the docs voice: that is what decides whether anyone loves this, and it is not parallelizable.
Dependency graph
S1 decisions ──▶ S2 contract freeze ──┬──▶ S3 conformance suites ──▶ Wave A (drivers)
│ │
└──▶ Wave B (core subsystems) ───────┤
▼
S4 integration
│
▼
Wave C (surface)
│
▼
Wave D (launch)
Serial spine
S1. Decisions locked
Owner: human. Output: 02-decisions.md with no open items, and a chosen umbrella name.
Blocks everything. The name blocks the scope, which blocks every package name.
S2. Contract freeze
Owner: human plus an adversarial review loop. This is the gate.
Freeze and publish:
QueryIRandPredicateAdapter- All sixteen driver contracts and their capability shapes
- The error taxonomy
- The capability narrowing type machinery
HttpRequest,ActionResult,ViewResult- The event and listener contracts
Acceptance: @avelon/core typechecks, every contract has a doc comment, and the capability narrowing spike passes with every @ts-expect-error firing.
Nothing downstream starts before this lands. A contract edited later to unblock one package silently breaks every package built against the original.
S3. Conformance suites
Fans out one agent per contract, but must land before any implementation. These are the acceptance criteria that let Wave A run without human review.
Acceptance: each suite runs green against its fake and red against a deliberately broken stub.
S4. Integration
Owner: human plus a small team. The vertical slice end to end: a Post resource with routes, controller, request, model, ward, policy, views, one event with a queued listener, running on both database drivers.
This is where the estimate actually lives. The failure mode of fleet execution is twenty green packages that do not compose.
M0 spike
Runs before S2 and informs it. One week. Prove what can kill the design, build no features.
| # | Assumption | Fails if | Falls back to |
|---|---|---|---|
| 1 | Domain directories colocated inside Next's app/ survive next build and never route | any domain directory becomes reachable as a URL | src/app router, domain code renamed |
| 2 | Generated server actions dispatch to a controller class and return validation state | action serialization rejects the pattern | route handlers plus _method |
| 3 | Bun runtime on Vercel serves Next with ISR and middleware | any of the three breaks | Node runtime, Bun for tooling only |
| 4 | A ward compiles to RLS and the same predicate scopes a query identically | results diverge on any fixture | wards become client-side only, RLS documented as manual |
| 5 | Throwaway Hono adapter serves the same controllers | the contract needs Next-shaped concepts | adapter abstraction dropped, Next-only |
| 6 | The query IR compiles for both Supabase and Postgres with identical results | the IR cannot express one driver's needs | IR redesigned before S2, not after |
Assumptions 4 and 6 must run against a live database. No mocks.
Wave A: drivers
Parallel after S3. Acceptance is conformance green, no human read required.
| Package | Implements | Notes |
|---|---|---|
@avelon/supabase database | database | includes the ward-to-RLS compiler |
@avelon/supabase identity | identity | |
@avelon/supabase social | social | |
@avelon/supabase tokens | tokens | |
@avelon/supabase storage | storage | |
@avelon/supabase queue | queue | pgmq plus the drain worker |
@avelon/postgres | database | transactions, deep relations, window functions |
@avelon/resend | ||
| fakes | all sixteen | one agent, mechanical |
Wave B: core subsystems
Parallel with Wave A, gated only on S2.
| Package | Scope |
|---|---|
| query IR builder | predicate construction, relation planning |
| Scrivener | models, relations, scopes, casts, timestamps, soft deletes, lifecycle hooks |
| ward compiler | predicate to SQL policy, predicate to IR injection, drift detection |
| events | dispatch, priority ordering, propagation halt, afterCommit, until |
| errands | job contract, three delivery modes, retry, failure table |
| policies and gate | registration, authorize, can |
| requests | Zod integration, error shaping, authorize hook |
| config resolution | driver wiring, environment merge, capability matrix |
| error taxonomy | base classes, cause chaining, adapter mapping |
Wave C: surface
After S4.
| Package | Scope |
|---|---|
@avelon/next | route codegen, action dispatch, page wrapper, middleware bridge |
| hono spike | throwaway, proves the contract, never shipped |
@avelon/cli | TUI, every generator, stubs, doctor, tinker |
@avelon/bailiff | every rule with reasons and autofixes |
| assay | test harness, HTTP helpers, factories, seeds |
Wave D: launch
| Item | Scope |
|---|---|
| docs site | the Avelon vocabulary and command map is the landing page, not a sub-page |
| example app | one complete application, not a todo list |
docs:check | README linting for packages |
| driver authoring guide | how to write and certify a driver |
| npm publish | lockstep pack of @avelon/*; first public line is a human 0.1.0 |
Work package format
Every package, without exception:
Title
Scope: what is in, what is explicitly out
Depends on: contract or package names, never "the other agent's work"
Acceptance:
- bun test green, including conformance for any contract implemented
- tsc --noEmit clean under strict
- reeve bailiff clean
- named integration test passes
- reeve docs:check clean
Escalate if: the specific conditions that mean stop and ask
If acceptance requires a human to exercise judgment about whether the result is correct, it is not a work package. It is a design decision and it belongs in S2.
Estimate
Two to three weeks wall clock, and almost none of it is typing.
- S1 and S2 are a day or two of focused human attention.
- M0 is wall-clock bound by live service verification.
- Waves A and B are compute plus review queue depth.
- S4 is where the estimate lives.
- Waves C and D are fast but taste-bound, and taste does not parallelize.
The previous seventeen-week figure assumed human implementation. Roughly twelve of those weeks were implementation and five were judgment. Only the twelve collapse.