Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
332bfbe
design: adopt the Protocol at 5e29c03, the pin
darko-mijic Oct 3, 2026
68aa73f
design: the mention policy, every prose mention a relation or a liste…
darko-mijic Oct 3, 2026
95b6733
design: the projection contract split by maturity, and the history vi…
darko-mijic Oct 3, 2026
4179fd5
build: the pinned module of the history view's Specs, emitted from re…
darko-mijic Oct 3, 2026
7a2e88a
design: the history view, the implementation pass
darko-mijic Oct 3, 2026
87c710c
build: the pinned module of pack:history-view, with bound, ahead and …
darko-mijic Oct 3, 2026
3fe0607
design: the implementation pass, two more pins the module found
darko-mijic Oct 3, 2026
5e32a69
design: a design ahead of its code, the conventions and the pass
darko-mijic Oct 3, 2026
1ee24b5
design: the history view, the Convex pass
darko-mijic Oct 3, 2026
15fc574
design: the close of the unit design-in-the-graph
darko-mijic Oct 3, 2026
5b226be
design: check.py reads an entry address whole, and the row test says …
darko-mijic Oct 3, 2026
b81982a
build: the pinned module refuses a duplicate pin, and its ahead and t…
darko-mijic Oct 3, 2026
d39c89e
design: STATE.md takes the module's counts after the review's fold-in
darko-mijic Oct 3, 2026
746782c
docs: the feedback to the Protocol moves to docs/feedback/, with the …
darko-mijic Oct 4, 2026
d2889c9
build: the pinned pairs as data beside the type test, and the pinned …
darko-mijic Oct 4, 2026
4c95892
design: the register cites an open question by its text, and check.py…
darko-mijic Oct 4, 2026
3a6f44a
design: the rebuild splits its history path out, and the aggregate fo…
darko-mijic Oct 4, 2026
9687ceb
build: the Pack page generator, one page per capability Pack
darko-mijic Oct 4, 2026
491ab7d
build: anchors for the online rebuild and the write pause, and the si…
darko-mijic Oct 4, 2026
10e8824
Merge branch 'unit/design-in-the-graph' into unit/pack-page-pinned
darko-mijic Oct 4, 2026
f52731a
Merge branch 'unit/design-in-the-graph' into unit/pack-page-register
darko-mijic Oct 4, 2026
149be95
build: the pinned pairs follow the split, and the drift test finds it…
darko-mijic Oct 4, 2026
57496e5
design: the register follows the split, and its line pointers into th…
darko-mijic Oct 4, 2026
6e8a4c3
Merge branch 'unit/pack-page-pinned' into unit/design-in-the-graph
darko-mijic Oct 4, 2026
cb41a72
Merge branch 'unit/design-in-the-graph' into unit/pack-page-page
darko-mijic Oct 4, 2026
0d7bb56
build: the Pack page of pack:history-view, generated and checked
darko-mijic Oct 4, 2026
278ae07
design: the write pause's statements of the history path move to the …
darko-mijic Oct 4, 2026
e4e468a
build: the line pointers into the rebuild Spec become the entry addre…
darko-mijic Oct 4, 2026
d97c5f2
Merge branch 'unit/pack-page-page' into unit/design-in-the-graph
darko-mijic Oct 4, 2026
5eb158d
design: the history view's page, generated after the write pause's hi…
darko-mijic Oct 4, 2026
0f6e91b
build: the review's fold-in: check.py keeps a list of its pages, and …
darko-mijic Oct 4, 2026
62dde32
design: the close of the unit pack-page
darko-mijic Oct 4, 2026
15f3d04
docs: the second read of 2026-10-04 links into the repository, not in…
darko-mijic Oct 4, 2026
ee8846a
design: fold the domain and operator passes over the history view, fi…
darko-mijic Oct 4, 2026
c3637e9
design: the domain lens decides which declarations bind a history vie…
darko-mijic Oct 4, 2026
e54ae38
design: the history projection's one version and its handoff, and the…
darko-mijic Oct 4, 2026
8c217aa
build: adopt the Protocol at 27b896a and cite every open question by key
darko-mijic Oct 4, 2026
0c7b7aa
build: the Pack page and the corpus check read the Protocol's round-2…
darko-mijic Oct 4, 2026
adbd37f
design: pause the unit sdp-round-2 at a seam for a restart
darko-mijic Oct 4, 2026
f04f44f
build: pin the Protocol at dfa1bd9, the round-2 branch after its review
darko-mijic Oct 4, 2026
3545e9a
design: fold the adopter review into the history view's design and tools
darko-mijic Oct 4, 2026
265cb9e
design: the history view's release procedure, stated whole
darko-mijic Oct 4, 2026
7535f44
design: the protected abort at its edges
darko-mijic Oct 4, 2026
29d31ca
design: hold the paused examples' backfill by a tenant scope
darko-mijic Oct 4, 2026
22cc96b
design: close the unit sdp-round-2
darko-mijic Oct 4, 2026
5d9dd2e
Name the relay plugin: relay:job jobs, reports under ~/.relay-jobs
darko-mijic Oct 8, 2026
fd5b257
build: pin the Protocol at da56a34, the annotations branch
darko-mijic Oct 9, 2026
57ed4f8
fix: fold the anchor check into the annotations showcase
darko-mijic Oct 9, 2026
2a6842a
docs: the annotations arc in the state file
darko-mijic Oct 9, 2026
b948623
docs: the PR section for the annotations arc
darko-mijic Oct 9, 2026
5355ed3
fix: fold the anchor check into the annotations showcase
darko-mijic Oct 9, 2026
e26e03d
docs: the PR section records the owner's ready on MD-36 and MD-38
darko-mijic Oct 10, 2026
f945b48
docs: the PR section's recipe 27 totals re-measured at 5355ed3
darko-mijic Oct 10, 2026
04a6aff
build: pin the Protocol at f23e358, the rulings of 2026-10-10
darko-mijic Oct 10, 2026
ecba393
design: apply the rulings of 2026-10-10 to the anchors
darko-mijic Oct 10, 2026
ed4f97f
docs: the rulings of 2026-10-10 in the feedback, the state and the PR…
darko-mijic Oct 10, 2026
fe76a4f
fix: fold the sol check of the 2026-10-10 rulings
darko-mijic Oct 10, 2026
4a5b653
fix(pinned): defaults before required parameters, and scoped names
darko-mijic Oct 10, 2026
09224b5
merge: the review repair of feature/round-2 (F2)
darko-mijic Oct 10, 2026
2347cbc
build: regenerate after the merge
darko-mijic Oct 10, 2026
c3f76e8
docs: re-measure the PR 12 section after the merge
darko-mijic Oct 10, 2026
ab7a13f
merge: the sol check follow-up of feature/round-2 (typeScopeKey)
darko-mijic Oct 10, 2026
e2c77e8
fix: fold the sol check of the review repairs
darko-mijic Oct 10, 2026
36bc5a0
docs: re-measure the PR 12 section after the fold
darko-mijic Oct 10, 2026
b5fb659
build: pin the Protocol at 822aea4, its main after the merge of #29
darko-mijic Oct 10, 2026
e83eb34
design: refresh the state and the roadmap at the close of PR 12
darko-mijic Oct 10, 2026
f9eb362
docs: re-measure the PR 12 section at the pin 822aea4
darko-mijic Oct 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ jobs:
with:
python-version: "3.x"
- run: npm ci
- run: python3 design/tools/check.py
- run: PYTHONDONTWRITEBYTECODE=1 python3 design/tools/check.py
- run: PYTHONDONTWRITEBYTECODE=1 python3 -m unittest design/tools/test_decisions.py
- run: npm run typecheck
- run: npm run lint
- run: npm run format:check
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The Convex transactional domain platform. `docs/` holds the inputs. `design/` ho
## The advisor panel and the roadmap

- `design/ROADMAP.md` is the aggregated roadmap: the next units in rounds, what each needs first and shows, what is deferred and why. The owner rules on it; the main thread keeps it current at each close. The slices of `design/STATE.md` keep their names, and the roadmap orders the units inside and between them.
- Four advisors, `advisor-convex`, `advisor-domain`, `advisor-operator` and `advisor-product`, are Fable agents defined under `.claude/agents/`. Each is launched with a task file under `design/advisors/`: `task-sort.md` sorts the open decisions of its lens, `task-fork.md` writes one owner decision as a fork, `task-roadmap.md` proposes the milestones of one subject. The four run at once, with disjoint subjects, and the main thread merges or cuts.
- Four advisors, `advisor-convex`, `advisor-domain`, `advisor-operator` and `advisor-product`, are Fable agents defined under `.claude/agents/`. Each is launched with a task file under `design/advisors/`: `task-sort.md` sorts the open decisions of its lens, `task-fork.md` writes one owner decision as a fork, `task-roadmap.md` proposes the milestones of one subject, `task-pass.md` reads one capability's Pack from its lens before the capability is built. The four run at once, with disjoint subjects, and the main thread merges or cuts.
- Every lean, fork or memo is checked by a `gpt-6.1-sol` job of the other family before the owner sees it, on a copy that carries one planted false citation. `design/advisors/README.md` says how each run goes and what the first runs showed; `design/advisors/protocol.md` binds every sentence an advisor writes.
- The memos, briefs and plants of a run live outside the repository, in the unit folder the project notes name. A check job's report stays in its job directory.

Expand Down
3 changes: 2 additions & 1 deletion CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,8 @@ _Avoid_: failed, dead letter, stuck

Words for how the platform is specified and proven. They are lasting and may appear in a Spec. They do not name things inside the platform, so no type, table, event or error code is named after them.

- **Layer**: one of the seven steps of capability, each usable before the next exists.
- **Layer**: one of the seven steps of capability, each usable before the next exists. _Avoid_: architectural layer.
- **Architectural layer**: the Protocol's `layer` on a component anchor, one of edge, application, domain, adapter or infrastructure, always written in full; a bare numbered Layer n is a step of capability.
- **Profile**: a group of layers an application takes together: transactional, durable, workflow or agent.
- **Trigger**: the concrete need that activates a later capability. A possible future need is not one.
- **Law**: one of the twelve rules every decision is reviewed against.
Expand Down
46 changes: 44 additions & 2 deletions design/PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,31 @@ Detail levels follow the doc's rule "Detail follows the build" (decision method,

Merges and splits against the task's list: idempotency and receipts stay one component because D6 defines both together; the rejection boundary is folded into the outcome model's boundary contract because D4 and D7 describe one mapping; tenancy and authority stay one component (D11); operations are split between Package D (what applies from Layer 1) and Package E (what applies to obligations), following the doc's sentence that security, bounded resource use and useful errors start in Layer 1.

### 2.1 The code's components

The code declares its architecture where it is realized: twelve `component:` anchors, each with an architectural layer and a bounded context, and the `uses` between them. Recipe 28 reads them from the graph and `design/tools/check.py` prints them. An architectural layer is the Protocol's `layer` on a component anchor, and a numbered Layer of the table above is a step of capability; the two are different things (`CONTEXT.md`).

| Component | Code | Architectural layer | Context |
|---|---|---|---|
| `component:platform.kernel` | `src/kernel/` | domain | platform |
| `component:platform.context` | `src/context/` | adapter | platform |
| `component:platform.command` | `src/command/` | application | platform |
| `component:platform.read-model` | `src/read-model/` | application | platform |
| `component:platform.gate` | `src/gate/` | infrastructure | platform |
| `component:platform.audit` | `src/audit/` | infrastructure | platform |
| `component:platform.operations` | `src/operations/` | infrastructure | platform |
| `component:example.orders-domain` | `example/domain/order.ts` | domain | orders |
| `component:example.inventory-domain` | `example/domain/stockItem.ts` | domain | inventory |
| `component:example.orders-context` | `example/convex/orders/` | adapter | orders |
| `component:example.inventory-context` | `example/convex/inventory/` | adapter | inventory |
| `component:example.parent` | `example/convex/` | application | orders-inventory |

The layer rule, from the owner's rulings of 2026-10-10: `domain` is business meaning with no runtime import; `application` coordinates a use case; `adapter` realizes a kernel or command contract against Convex; `edge` is transport in; `infrastructure` is a shared mechanism that owns no use case. The gate supplies a pause and owns no use case, so it is `infrastructure`. The context library and the two context components realize the kernel's contract against Convex, so they are `adapter`.

The context rule, from the same rulings: `platform` stays on the seven platform components, because the platform is the key bounded context, with its own language (`CONTEXT.md`). The example keeps `orders` and `inventory` on its domain and context components and `orders-inventory` on the parent composition: orders-inventory is the supporting context and development tool, the composition of the two business contexts it models. Nothing in the platform's core carries orders-inventory specifics, and that is read from the graph: `design/tools/check.py` fails when a component in the context `platform` has a `uses` edge to a component in any other context, each end of the edge resolved to its component directly or through `memberOf`. The check reads the declared `uses`, not the imports.

`example/` is architecture: it is the production composition and the only place a composition and contexts exist as code, so it keeps its five components. `fixture/` has none.

## 3. SDP mapping

### 3.1 IDs, files, families
Expand Down Expand Up @@ -231,7 +256,7 @@ Fact slugs: `f01-serializable-mutations-under-occ`, `f02-component-calls-commit-
| `spec:application.parent-use-cases` | behavior | feature | defined | refines epic; decidedBy D1, D10; constrainedBy Law 1, 2, 9, F2, F3, F13, `spec:constraints.one-call-per-context-per-use-case`; dependsOn `spec:context.context-component`, `spec:context.batch-shaped-api`, `spec:command.command-pipeline` | Contexts never read each other or call each other; cross-context rules in the parent, same transaction, or in an obligation; no handler issues commands; one receipt and outcome per use case; PlaceOrder is one command; grouping by flow; too-large operations rejected or a separate import command; OQ3 max lines `[non-blocking]`. Example space for L2-1 |
| `spec:application.read-models` | behavior | feature | defined | refines epic; decidedBy D8; constrainedBy Law 9, F7, F15, `spec:constraints.zero-core-projection-jobs`; dependsOn `spec:context.queries` | The read-need table; zero projection jobs; projection properties; committed-or-later visibility; versions in results; reactive queries replace event-to-socket layers; CQRS as separate contracts. Example space for L2-2 |
| `spec:application.projection-contract` | contract | story | defined | refines read-models | E-9: the projection signature shared by live update and rebuild; row conventions (tenant, generation, key, source version) |
| `spec:application.rebuild` | workflow | feature | defined | refines read-models; decidedBy D9; constrainedBy Law 10, F1, F17 | The four online steps; the missing-row rule for incremental updates; version-compare backfill; verify, switch, keep old; per-entity markers for counts and sums; the write pause for cross-stream history views (gate, fence, resume, abort); never commands or effects. Probe 6 `[non-blocking]`. Example space for L2-5, L2-6 |
| `spec:application.rebuild` | workflow | feature | defined | refines read-models; decidedBy D9; constrainedBy Law 10, F1, F17 | The four online steps; the missing-row rule for incremental updates; version-compare backfill; verify, switch, keep old; per-entity markers for counts and sums; the write pause for cross-stream history views (gate, fence, resume, abort), in `spec:application.history-rebuild`; never commands or effects. Probe 6 `[non-blocking]`. Example space for L2-5, L2-6 |
| `spec:application.generation-registry` | contract | story | defined | refines rebuild; constrainedBy F13 | E-8: the generations table and states, the marker document, the migrations-component binding |
| `spec:application.write-pause` | contract | story | defined | refines rebuild | E-8: the maintenance gate document, the writer check, fencing of background writers, operator resume and abort |
| `spec:application.restore` | workflow | story | defined | refines `spec:operations.baseline-operations`; decidedBy D19; constrainedBy F12 | Restore with matching code and configuration; invariant checks for domain, journal and read models; what the recovery plan contains; restore is tested, not only backup. Example space for L2-8 |
Expand Down Expand Up @@ -298,7 +323,7 @@ Every row of the doc's acceptance table becomes at least one `example` Spec. Whe
| L2-3 | Orders of 1 line, 10 lines and the maximum | `spec:application.first-experiment` | `.order-of-1-line`, `.order-of-10-lines`, `.order-of-max-lines` | native | D |
| L2-4 | Rename a typed handler or break its argument contract | `spec:command.command-declaration` | `.renamed-handler-fails-build` | build | C |
| L2-5 | Rebuild a per-entity read model online while commands run; interrupt and resume | `spec:application.rebuild` | `.online-rebuild-interrupt-resume` | native | D |
| L2-6 | Rebuild a cross-stream history view under a write pause; interrupt | `spec:application.rebuild` | `.write-pause-rebuild-interrupt` | native | D |
| L2-6 | Rebuild a cross-stream history view under a write pause; interrupt | `spec:application.history-rebuild` | `.write-pause-rebuild-interrupt` | native | D |
| L2-7 | Rebuild a stream that has a baseline event | `spec:context.journal` | `.rebuild-from-baseline` | native | B |
| L2-8 | Restore a representative dataset with matching code and configuration | `spec:application.restore` | `.restore-representative-dataset` | end to end | D |
| L2-9 | Run native acceptance with production configuration | `spec:application.first-experiment` | `.native-acceptance-production-configuration` | end to end | D |
Expand Down Expand Up @@ -568,6 +593,23 @@ The design never resolves an inconsistency it finds in the doc. It records the t

Use the doc's terms as `spec:platform.vocabulary` fixes them. Stream version, never revision. Obligation for the record, effect for the change it makes, attempt for one try. Receipt for the stored idempotency outcome. Generation for a numbered read-model build. Where a Convex term and a doc term differ, the doc's word names the design concept and the Convex word names the mechanism (`obligation` is the record, `scheduled function` is the dispatch).

### 6.7 A design ahead of its code

A capability is designed in its Specs before anyone writes its code, at the rung its design has reached, and the graph shows which parts are realized, which are designed and which are deferred. These conventions keep it so.

1. **One maturity per Spec.** When part of a Spec's design is realized, deferred or reviewed apart from the rest, it moves into a Spec of its own, with its own readiness, open questions, relations, anchors and verifiers. This is the Protocol's promotion rule, and the content moves out. What the parts share keeps one home, and each part `dependsOn` it. The projection contracts show the three cases side by side: `spec:application.projection-contract` holds the per-entity form, which code realizes, and the conventions every form shares; `spec:application.history-projection` holds the history form, designed and checked by one pure test; `spec:application.aggregate-projection` holds the aggregate form at `scoped`, deferred with its trigger.
2. **A capability Pack.** A Pack gathers, in reading order and across packages, the Specs one capability rests on, such as `pack:history-view`. Its page in the Design Review (`npx sdp view`, then `generated/design-review/pack/`) presents the design: each member's stated rung, the floor it reaches, its implementation binding and its verifier binding. Recipes 5, 19, 20 and 21 read a Pack's members for review, planning, open questions and dependency footing.
3. **Pinned declarations compile.** A Design entry whose value opens with a code span is a pinned declaration (6.3), and recipe 24 lists them. `scripts/pinned.mjs` emits the declarations of one scope by key prefix into `generated/pinned/<scope>.ts`, with the declarations other Specs pin that the scope uses; `npm run sdp:build` runs it and `npm run test:types` compiles it, so a name used and never declared, or declared twice, fails. A name the design uses and no Spec pins is listed in the emitter's preamble, one line each, until a Spec pins it. Where code realizes a pin, `tests/types/pinned.test-d.ts` asserts that the code's type equals it, so a changed pin, or code that drifts from it, fails the typecheck. Where the design leads its code on purpose, the test lists the pair as ahead, with what closes it, and asserts that the two still differ, so the build that catches up fails the test until it moves the pair to the bound list. The module states shapes only; a rule in prose is still read by a person.
4. **An entry is cited by its address.** `spec:<id>#design.<key>` names one Design entry, as in `spec:application.history-projection#design.fnApplyHistoryProjection`. Validate checks an address in a Spec's prose, and `check.py` checks one in the code, the tests, the register, the ledger and the work documents. A line number is a pointer that the next edit moves; it never stands for an entry.
5. **A mention is a relation or a listed reason.** A Spec id in prose with no relation between the two Specs is a validate warning. Declare the relation when the mentioning Spec could not be built or read correctly without the other. Otherwise list the pair in `design/tools/prose-mentions.json` with a one-line reason; `check.py` refuses a pair that is neither, and a listed pair that no longer warns.
6. **A design is read from several perspectives before its code.** A pass reads a capability's Pack from one lens and changes Spec text only through the main thread's rulings; what it cannot settle becomes an open question or a register row. A pass needs no status: git holds the passes and the Pack shows the design as it stands. The questions each lens asks of a design, as a start:
- domain: does every interleaving of the sources give the same row, what survives a subject's end, what a migrated stream's baseline means to the design;
- Convex: what one transaction reads and writes, whether the stated bounds hold on the pinned release, what changes atomically and what across transactions;
- operator: what an interruption, a resume and an abort leave, who closes and opens a pause, what a deploy or a restore does to work in flight;
- implementation and verification: the pinned module compiles and the code agrees with it, each dependency stands at its rung (recipe 21), and each example names its tier and has a planned verifier.
7. **A Spec is satisfied only when its code realizes all of it.** Anchor targets name whole Specs, as `spec:decisions.anchor-binding-grain` rules, so the units that satisfy a Spec together realize every entry it promises. A pinned declaration is no exception: a pin that a designed form widens, which the pinned module lists as ahead (item 3), is a promise the code has not built, and the Spec that holds it is not satisfied while the pair is ahead. `typeReadModelBinding` takes a history projection beside a per-entity read model while the code takes the per-entity form alone, so the code references `spec:command.command-declaration` and does not satisfy it. For a Spec with a part no unit builds, a coherent part with its own trigger or step moves into a child Spec that refines it, its entries moved verbatim and its readiness stated on its own, and the parent keeps `satisfies` (item 1); otherwise the code references the Spec until it realizes what the Spec promises. A reference runs one way: the code answers to the design, because it follows it or realizes part of it. A design that builds on existing code says so by its own `dependsOn` or `refines` to the Spec that code satisfies, never by a reference from the code: `spec:application.history-rebuild` holds the paused path of a history view and depends on `spec:application.rebuild`, the online path's Spec, and the online rebuild's code carries no reference to it. A deferred form states in its own Spec how it extends the Specs it would widen when its trigger comes, and those Specs do not depend on it: `spec:application.aggregate-projection` says what the declaration's binding, the pipeline's step 9 and the registry's tables take when OD-075's trigger comes.
8. **`ready` states approved to build.** This is the Protocol's reading. The owner states `ready` on a Spec when its design is approved for its build, so recipe 1, the Specs that state `ready` and have no implementation binding, is the build backlog, and a Spec leaves it when an anchor binds its code. A designed Spec states `ready` after the passes of item 6 have read its Pack, as the history projection may once the domain and operator passes have run. No agent states `ready` (section 3.2).

## 7. Review rubric

Reviewers judge the merged corpus. Each item is checkable; an item that fails names the Spec and line.
Expand Down
Loading
Loading