How intended behavior is written down, verified, and enforced across the Ascend stack. The harness keeps two states permanently distinct — the canon (what the product should do: backbone → specs → acceptance + evals) and the build (what the code is) — and measures one against the other mechanically, at every stage, on every push. Its operating rule: ship with gaps, never ship with divergences.
Development at agent speed produces more behavior than review at human speed can hold in memory. Behavior gets lost in a rework without anyone deciding to lose it; numbers drift from the models that define them; a change reaches into a contract nobody intended to touch. The harness closes that class of failure by making intended behavior a versioned, machine-checkable artifact — and by classifying every mismatch between canon and code as exactly one of two things:
The canon has it; the code doesn't yet. Every gap is recorded in a coverage ledger and dispositioned — built now, deferred, or consciously cut. A dispositioned gap ships: it is the burndown. An un-dispositioned gap blocks the merge — silent absence is precisely the failure the harness exists to stop.
The code asserts something the canon forbids: a failing must-pass scenario, a number that contradicts the live system, a diff inside a protected contract. Divergences are walls — continuous integration blocks them unconditionally, and no disposition waves one through.
One more reading exists on the evidence axis: UNRESOLVED — the checker itself cannot run (an unreachable oracle, an unresolvable pin). It never counts as a pass: the merge verdict fails closed on evidence it cannot check.
The chain is machine-walkable only when every node has a stable, greppable name — so any line of code resolves up to the job it serves and back down to the scenario that checks it. One grammar runs top to bottom. Case carries meaning: UPPERCASE filenames are harness artifacts (SPEC.md, EVALS.md, COVERAGE.md, JTBD.md); lowercase reserved names are OKF bundle files (index.md, log.md); everything else is kebab-case.
| Layer | Identifier | Example | Registered where |
|---|---|---|---|
| Actor | three-letter code | BOR · LEN · LIQ · HUB | the actor table atop JTBD.md |
| Job (JTBD) | J-<ACTOR>-<NN> | J-BOR-02 | one entry per job in JTBD.md |
| Spec | S<NNN> — monotonic, never reused | S002 | the SPECS.md ledger (hub root) |
| Acceptance file | capability slug | acceptance/borrow.feature | inside the spec folder |
| Scenario | immutable tag @S<NNN>-AC-<NN> | @S002-AC-12 | the tag line above the scenario |
| Eval case | EV-<TOPIC>-<N> | S002/EV-HF-2 | EVALS.md |
| Coverage row | capability slug + cited scenario | — | COVERAGE.md |
| Gate | G0–G5 | G4 | the gate table · scripts/ |
| Decision | dated ADR | 2026-07-16-….md | the decisions bundle |
A spec lives at
<spoke>/specs/S<NNN>-<slug>/.
The slug says what it specs; the ID gives it a citation handle; the date lives in
SPEC.md frontmatter and git history, not the
path — paths are identity, history is time. A folder is never copied to
-v2; it evolves in place through
re-baseline,
and its history is its git log.
SPECS.md at the hub root is the allocator
and the tombstone record — an append-only table, one row per spec. Allocating an ID means
appending the next row in the same PR that creates the folder: two authors grabbing the
same number collide as a textual merge conflict, which is precisely the race detector. Rows are
never deleted — a retired spec keeps its row so an ID can never rebind, and a rename appends an
alias row. G0 checks every folder against the ledger and every ledger row against the tree.
The JTBD registry (knowledge/backbone/JTBD.md) holds the actor table and one entry per job, linking to backbone sections — and carries no spec pointers. The spec→job edge lives only in the spec's jtbd:; job coverage is a set-difference G1 computes, never a hand-kept column.
One file per artifact kind. A legacy lowercase name
(spec.md) is accepted with a deprecation
warning only when the uppercase form is absent; the two coexisting, or a case-fold
collision, is a violation — a platform-dependent source of truth is worse than either name.
Identity in the path, time in git. No dates and no
-v2 in folder names; a spec is one folder that
evolves in place.
The hub holds only what is shared. knowledge/,
.claude/,
scripts/, the workflows. Each spoke holds only
its own specs, decisions, and mounted code. Specs never live in the hub; shared canon never lives
in a spoke.
Five layers, nested like a Russian doll: each wraps the next, is derived from the one above, and cites it explicitly — so the chain is machine-walkable in both directions, from any line of code up to the job it serves and back. The gates are not a layer of their own: they sit on the boundaries between layers, checking every crossing.
the everything-doc · money models + supervisory procedures as referenced canon · the JTBD registry
one ID'd folder per unit of work (the slug says what it specs; the date lives in frontmatter and git) — its jobs (jtbd:), its genre, its SHA-pinned sources
Gherkin scenarios, one Feature per file · quantitative oracle cases · the gap ledger · sources.lock, the pin twin
step definitions bind each scenario phrase to real actions; spec-local harnesses where they enforce the spec
Without one agreed statement of intent, every spec re-derives (and re-disputes) first principles. The backbone settles what the product is once, so everything below can cite it instead of re-arguing it.
The backbone states intent for the whole product at once — too broad to review, pin, or change-control per unit of work. A spec cuts it into a dated, owned, pinned unit that a pull request can actually be held against.
A spec is still prose, and two readers can "pass" the same sentence with different behavior. Scenarios give every criterion a binary verdict — given this, when that, then exactly this — so agreement is checkable, not assumed.
Scenarios verify behavior but treat numbers as text: a scenario can pass with the wrong haircut inside it. EVALS make each quantity falsifiable against the oracle, with a tolerance — catching what pass/fail steps cannot.
Scenarios and evals only test what someone remembered to write; both are silent about what is missing. COVERAGE fixes the denominator — every behavior gets a row — so absence becomes visible and must be dispositioned, never silent.
A scenario is executable in principle only. Step definitions bind each phrase to real actions against the app; without the binding, the canon is prose and nothing fails when the code disagrees.
Everything above is discipline, and discipline decays: sessions drift, people forget. The gates re-check the whole chain mechanically on every push and refuse the merge — the system stops depending on anyone's memory.
The citations are the structure: a scenario is cross-referenced by its coverage row; a spec's jtbd: cites registry jobs; the registry links the backbone's sections; eval cases cite the running system. Numeric sources — the models, the spreadsheets, the contracts — remain the system of record: cited into specs by pinned reference, never paraphrased into prose, never retired.
Every stage boundary carries a gap face and a divergence face, and each is guarded by a named gate. Gap-gates measure coverage completeness and block only un-dispositioned absence; divergence-gates detect contradiction and block it outright.
| G | Gate | Boundary | Class | The failure it prevents |
|---|---|---|---|---|
| G0 | spec-lint — genre: / sources: / pass-definition hard, rest warn | spec structure | Divergence | a spec nobody can trace or verify |
| G0b | sources-drift — the pin twin vs the moving world | pinned ↔ live | Divergence | a green gate proven against sources that have since moved |
| G1 | backbone-coverage — set-difference: registry jobs vs specs' jtbd: | backbone → spec | Gap | a job silently never specced |
| G2 | COVERAGE + scenario cross-reference | spec → acceptance | Gap | a rework silently deleting shipped behavior |
| G3 | acceptance-runner — must-pass scenarios execute | acceptance → code | Divergence | code contradicting a sanctioned scenario |
| G4 | eval-oracle — numeric invariants vs the running system | evals → code | Divergence | a copied constant canonizing a number the live system refutes; double-counted value |
| G5 | core-untouchable — anchored paths, renames rejected, recorded owner approval | diff ↔ contract canon | Divergence | a change mutating a sacrosanct contract |
Every change decomposes into per-behavior transitions — create, keep, modify, remove — so one process covers all three kinds of work. The field label sets the review focus, not the machinery:
| Field | Meaning | The ledger it keeps |
|---|---|---|
| Greenfield | net-new capability | forward — every criterion covered, deferred, or cut |
| Brownfield | rework of shipped behavior | preservation — every current behavior covered, intentionally removed, or deferred; never silently absent |
| Bluefield | rework + net-new in one change | hybrid — both ledgers in one file, routed per row by provenance |
Provenance on every criterion. Each acceptance criterion carries its origin —
current behavior, onchain rule, backbone intent, or a decided change — so inherited behavior
and new product decisions never blur.
Numbers come from the oracle. Numeric claims are read from the running system, never
copied from source constants — a copied constant can canonize a number the deployed system
quietly computes differently.
Core is extended, never edited. The protocol owner marks sacrosanct contract paths;
any diff touching one — renames included — blocks until a recorded owner approval. New
behavior around core arrives as a new module, not an edit.
What a unit of work looks like once the harness is in place — from the backbone to a merged pull request.
Entry differs by field. Greenfield opens from the backbone: a job-to-be-done in the registry, or a new one, registered first. Brownfield opens from the shipped surface: current behavior is inventoried — file:line-cited — before anything else is decided. Bluefield opens from both. A speccing session — the spoke owner plus whoever ships the change, an agent driving — walks the job (and, for brownfield, the inventory) against the backbone sections and models it cites.
The session produces the spec folder: SPEC.md (its jobs, its genre, its pinned sources) → acceptance scenarios → eval cases → coverage rows. People make every disposition in the room; a decision not yet taken is tagged pending — it blocks the eventual merge, not the spec.
Code and tests land in the mounted repo, step definitions binding scenarios to the implementation. The spec is the contract; the code is the response.
Coverage rows move to their final dispositions as the work lands. Deferred rows remain on the books as tracked gaps; nothing is allowed to be silently absent.
The gates run. Gap-gates block un-dispositioned absence; divergence-gates block contradiction. A green run means one thing: the code matches the canon, or every mismatch is on the books. Merge follows; if pinned sources moved intentionally, the re-baseline is recorded as part of it — the chain never silently outruns its pins.
The five steps run unchanged for all three fields; what differs is where entry starts, what COVERAGE tracks, and what counts as done.
| Field | Entry | COVERAGE ledger | Done |
|---|---|---|---|
| Greenfield | a registry job | the forward ledger — covered / deferred / cut | the burndown reaches zero un-dispositioned rows |
| Brownfield | the shipped surface, inventoried into preservation rows before the rework starts | the preservation ledger — covered / intentionally-removed / deferred, plus pending-decision rows that block the rework merge until dispositioned | zero silent absences against what shipped |
| Bluefield | both, at once | both ledgers in one file; a modification splits into two rows sharing a mod: pair key — the preserved invariant and the decided change — so both ledgers see it and the pairing is machine-checkable; enforcement flips capture (warn) → enforce (fail) at the rework merge | both ledgers clean in the same pull request |
This page describes the target state. Part of the harness is live today; the rest is sequenced in Linear. Until each check's migration lands it runs as a warning, never a silent skip — the compatibility window is explicit, not assumed.