Skip to content

NT-010 — The shape of the platform

Three drawings and what checks each one. The README shows what an operator installs; this note shows what happens inside it, and how the pictures are kept from drifting away from the code.

A diagram that disagrees with the code is worse than no diagram. It reads as authoritative and nothing fails when it stops being true. So each drawing below names what proves it, and where a drawing has no accuser this note says so rather than leaving the reader to assume one exists.

A worker folds ledger steps, asks a model to plan, sends the proposal through the Gate, and records an allowed call or a refusal before repeating or finishing.A worker folds ledger steps, asks a model to plan, sends the proposal through the Gate, and records an allowed call or a refusal before repeating or finishing.

The fold is the whole design. A run has no stored state — the state is a pure function of the steps recorded so far, so a worker that dies mid-run is replaced by another that reads the same ledger and reaches the same place. It is also why the ledger has no UPDATE and no DELETE: a correction is a new step, because amending an old one would change the answer the fold gives about the past.

The Gate sits between the proposal and the effect, always. There is no path from a model’s suggestion to a business system that does not pass through it, and the decision is appended before the effect happens rather than after — so a crash between the two leaves a record that something was allowed, never a mystery effect with no decision behind it.

What proves it: the engine tests drive this loop against an in-memory ledger, and the gate tests drive the seven checks and four verdicts directly. The ordering claim — decision appended before effect — is asserted in the runner’s tests.

HTTP API may import engine, engine may import ledger, and ledger may import domain. Dependencies never point back toward the HTTP API.HTTP API may import engine, engine may import ledger, and ledger may import domain. Dependencies never point back toward the HTTP API.

Arrows are “may import”. Nothing points the other way: domain is what everything is built on, so it is built on nothing of ours and on no third party except one named exception — Unicode normalisation, because memory identity is wrong without it and the standard library does not ship the table.

Interfaces are declared by the consumer. engine declares the Ledger and ContentStore it needs, and ledger implements them without importing engine — which is why the arrow can point inward while the dependency of meaning points outward.

What proves it: internal/arch/layering_test.go walks the import graph and fails on a reverse arrow or a third-party import inside domain. It reads production files for direction and every file for purity: a domain test that reaches for a driver has put the driver in the package’s world, but a ledger test that imports engine is asserting at compile time that it satisfies engine.ContentStore — the layering being proved rather than broken.

An append-only ledger step holds a reference and digest that resolve to erasable argument, result, and closing-answer bytes in the content store.An append-only ledger step holds a reference and digest that resolve to erasable argument, result, and closing-answer bytes in the content store.

The split is not about size. Tool arguments, tool results and closing answers routinely carry personal data, and run_steps is the one table an erasure request can never reach. The current engine therefore writes those three by reference and digest, never inline. The digest is enough for an auditor to prove which bytes were used without those bytes surviving to prove it.

This is deliberately narrower than saying that no personal or authored text ever reaches the ledger. Scope and conversation identifiers, policy evidence, human decision notes and stable failure summaries are part of the audit record. There is also one historical exception: runs recorded before closing answers moved to the content store keep RunFinishedPayload.Outcome inline. The chain is immutable, so readers retain that legacy field while current writers never populate it.

When the bytes are erased, anything resting on them is marked rather than left looking sound: a governed memory whose evidence was erased becomes source_erased and stops being recalled.

What proves it: TestAdvance_erasableContentStaysOutsideTheLedger runs a tool and finishes a run with distinct argument, result and answer markers. It proves that none enters a step payload, that every reference resolves before erasure, and that none resolves afterwards. The retention and erasure tests in internal/admin, and the memory reconciliation suite, drive an erased source through to the memory that cited it. The legacy outcome reader has its own compatibility test.

  • DP-001 — stored data, retention, erasure, boundaries
  • NT-004 — ledger volume, partitions, paging
  • PRD-001 — the product these shapes serve