@danilocampos/coherence
v0.37.1
Published
A spec + coherence harness for agent-developed projects: derive a multi-resolution graph from *.spec.md + code, render an explorable outline and an agent map, and verify claims/narrative/coverage. Language and platform specifics live behind adapters.
Maintainers
Readme
coherence: Spend less inference, build better projects
Robots can write code faster than any human can interpret.
That's the strange new problem of the agent age. An agent has inexhaustible stamina: it will generate, refactor, and patch for as long as you let it. But every choice it makes—every alternative it rejected, every argument it settled—evaporates when the session ends. The next reader arrives with the same questions we've always had: Can I change this safely? What breaks if I do? Why is it this way?
Answering those questions by reading code and simulating it in your head is inference, and it's the most expensive operation in software. Worse, it's paid again by every reader, forever—nobody's inference makes the next reader's cheaper. In a world where the readers are mostly agents burning tokens, that invoice compounds fast.
Coherence is a machine for spending less inference. It guides your agent toward more correctness, driving the irreducible complexity of your project toward its floor.
It gives a project durable, checkable spine of truth and design intention:
A decision journal records what was chosen, what was rejected, and why, so settled questions stay settled instead of getting re-litigated by every fresh session. This also gives you, the human, a highlight reel to review.
Machine-checkable specs tie documentation to the code with oracles that re-grade it every build—when the docs rot, the build says so. An agent orients in seconds instead of spelunking.
A work ledger coordinates whole swarms: who owns what, within what boundary, and where two agents are about to collide.
Think of an electrical panel. A tidy one—every breaker labeled, every label tested—can be acted on from its labels. A tangled one has to be carefully analyzed by tracing wires. Tracing wires is inference. Coherence keeps the panel labeled, and keeps proving the labels true.
It's platform- and language-agnostic, runs anywhere Node ≥22 runs, and installs in three commands.
Quick start: paste this into your agent
Install Coherence in this project and adopt its lifecycle control:
1. Run: npm install --save-dev @danilocampos/coherence
2. Create coherence.config.json in the project root. An empty object {} is a
complete config; add "typecheck", "test", and "testMatch" entries if the
project has those commands.
3. Run: npx coherence hooks install --host claude (use --host codex on Codex)
4. Commit coherence.config.json and the generated host control files.
5. Run: npx coherence verify, then npx coherence orient, and report what they say.
Before recording decisions, creating claims, or coordinating other agents, read
the full reference inside the <details> section of
node_modules/@danilocampos/coherence/README.md.A standalone coherence harness for agent-developed projects. It derives a
multi-resolution graph from a *.spec.md tree plus the code, renders a navigable
outline and an agent map, and verifies that the docs/claims haven't rotted.
npm install --save-dev @danilocampos/coherence
printf '{}\n' > coherence.config.json # declare the root — an empty config is complete, defaults do the rest
npx coherence hooks install --host claude # the lifecycle field: journal + instructions in every agent session
npx coherence verify # derive the graph and grade the claimsRequires Node ≥22. The full installation section covers configuration, the Codex host, and activating the lifecycle control properly.
That is the mechanism. The purpose is narrower and worth stating before any of it: the expensive resource in a codebase is not bytes and not lines — it is inference, and this is a machine for spending less of it. Read the economy of inference for that model; every command below is an instrument in that economy.
The core is platform- and language-agnostic. Project-specific knowledge lives behind two adapters:
- language adapter (
src/adapters/tree-sitter.ts) — symbols, imports, docblocks; grammar-backed built-ins for TypeScript, Python, and Ruby, project-extensible. - platform adapter (
src/adapters/cloudflare.ts) — infra bindings from wrangler config plus direct typedEnvcapabilities. Optional.
With "platform": "cloudflare", direct module-level Env / Cloudflare.Env
properties using the existing D1, KV, Vectorize, R2, and Workers AI types form the
committed capability floor; wrangler.jsonc / wrangler.toml
augment and confirm it. This keeps an optional binding's graph node stable when a local
deployment toggle is enabled or commented out. Agreement deduplicates; a source/config
type conflict refuses rather than picking whichever spelling was read last. Source
inference consumes the same filtered code-file population as the graph, so put generated
machine-local declarations such as worker-configuration.d.ts in ignore. Wrangler
runtime-variable names remain visible as declarations, but their deployment values are
discarded at the adapter boundary and never enter graph.json or an overview.
Agent and swarm quick path
Start with a heading, then buy only the context the task needs:
npx coherence orient
npx coherence context src/payments/settle.ts # bounded to 12,000 bytes by default
npx coherence context --changed --max-bytes 20000
npx coherence context src/payments/settle.ts --all # explicit unbounded expansionorient reads the strict decision projection, work graph, consequence links,
experiments, defects, and last verification. It emits one deterministic action:
REFUSE, RESOLVE-CONFLICT, REPAIR-NAVIGATION, UNBLOCK, SYNTHESIZE,
DISPATCH, CONTINUE, VERIFY, or STEADY. It never runs the action. If a
required ledger is damaged, the heading is REFUSE; damaged or unreadable surviving
evidence is not converted into an empty, healthy-looking swarm.
The examples use $COHERENCE_SESSION, $CHILD_SESSION, and $NEXT_SESSION for
exact host-provided session identities. Set them from your host or orchestrator before
writing; for a Codex parent, COHERENCE_SESSION="$CODEX_THREAD_ID" is the usual mapping.
Do not substitute a branch name, date, unknown, or a guessed newest session.
For coordinated work, make authority, ownership, success, dependencies, and write scope addressable before agents start:
WORK_ID="$(
npx coherence work create "harden settlement retries" \
--success "the retry oracle passes" --risk high \
--authority orchestrator-delegated --granted-by orchestrator \
--boundary "src/payments/** and its focused tests only" \
--owner-session "$CHILD_SESSION" --owner-agent retry-agent \
--read-scope src/payments --write-scope src/payments \
--session "$COHERENCE_SESSION" | awk '/^OPEN / {print $2}'
)"
npx coherence work inspect
npx coherence work transition "$WORK_ID" active --because "owner accepted the order" \
--session "$CHILD_SESSION"
npx coherence work handoff "$WORK_ID" --owner-session "$NEXT_SESSION" --owner-agent reviewer \
--because "implementation complete; verification remains" --session "$CHILD_SESSION"
npx coherence work close "$WORK_ID" completed --because "success criterion met" \
--evidence "focused oracle passed" --session "$NEXT_SESSION"The work ledger is append-only and inert: it coordinates but does not spawn agents or execute commands. Every mutation names an exact writer session; ownership changes only by handoff; state transitions carry their predecessor; dependencies determine readiness; simultaneously runnable work with overlapping write scopes is reported as a collision. An order cannot activate or complete until every dependency completed. A parent cannot become terminal while a child remains live, and closure names every completed direct child whose result it synthesized.
A completed child can remain visibly unsynthesized while one of its direct siblings is
still ready, active, or blocked. Synthesis is represented only by closing the parent, so
orient selects the live sibling's executable obligation first and emits SYNTHESIZE
only after that parent's direct children are terminal.
Work authority answers who may act and within what boundary. Decision authority is a
separate question: whose choice may ratify policy for the swarm. Record a choice at the
moment it is made, and add --subject whenever it should enter conflict analysis.
--authority is optional, but an ungraded choice cannot ratify or outrank another:
DECISION_ID="$(
npx coherence decide "use compare-and-append state transitions" \
--over "last timestamp wins" \
--because "concurrent histories must refuse instead of hiding one writer" \
--work "$WORK_ID" --subject work-state/concurrency \
--authority local-proposal --scope-file src/work.ts \
--session "$CHILD_SESSION" --agent retry-agent | awk '{print $1}'
)"Two local proposals with the same explicit subject and different choices need
ratification. An orchestrator-accepted or user-directed record can select one
choice; incompatible records at the highest authority remain contested. Prose similarity,
timestamp order, and “last writer wins” never decide policy. Historical journal rows
remain readable. Rows without a subject stay outside position analysis; rows with a
subject but no authority may align or conflict, but never ratify or outrank.
Finally, state relationships rather than asking later readers to infer them from time, paths, or Git proximity:
decision ──authorizes──▶ work ──produces──▶ commit
▲
verification ───verifies─────┘
work or commit ──repairs─────▶ defectnpx coherence consequence add "decision:$DECISION_ID" authorizes "work:$WORK_ID" \
--evidence "the accepted decision grants this work order" --session "$COHERENCE_SESSION"
npx coherence consequence add "verification:full@$(git rev-parse HEAD)" verifies "work:$WORK_ID" \
--evidence "full coherence verification passed at this commit" --session "$COHERENCE_SESSION"
npx coherence consequence inspect "work:$WORK_ID"The consequence ledger preserves the authored direction and renders both directions for
navigation. Its typed relation table rejects nonsense, but an edge remains an attributable
assessment—not proof of causality. A completed work order stays visibly unverified until a
verification --verifies--> work edge names it. Today that verification reference is an
assessor-authored address, not an existence-checked append-only receipt; record it only
after the named check actually ran. The output and Known limits section keep that ceiling
explicit.
Commit .coherence/work/ and .coherence/consequences/. They are repository evidence,
not machine-local queues; configure blanket .coherence/* ignore rules to re-include both.
This repository pins that requirement with a public-CLI write → Git commit → clone → strict
replay test. First-use repositories need no tracked empty directories, but once records
exist, a clone that drops them has lost the swarm's ownership and navigation state.
That is the shortest operating loop:
orientfor the fleet heading.contextfor a bounded, omission-accounted reading packet.workfor authority, ownership, dependencies, scopes, handoff, and synthesis.decidefor alternatives and ratifiable policy.consequencefor explicit provenance between durable records.verify, link the evidence, then runorientagain.
Validate a swarm in the field
A green harness proves mechanisms, not that a swarm delivered the right result. Keep two claims separate:
- a live canary proves that attribution, conflict sensing, handoff, synthesis, navigation, and fail-closed recovery work in the selected host;
- a matched efficacy trial asks whether those mechanisms reduce inference, conflict, rework, or defects without degrading the domain outcome.
Predeclare the canary before dispatch. Use experiment create to freeze its representative
task, actions, and observable criteria; choose the time/read budgets and sample counts now,
not after seeing the result. At minimum name the native domain outcome, assignment delivery,
actual-versus-declared write scope, one seeded collision and one dependency-serialized
overlap, decision ratification, handoff and child synthesis, hook reliability, transcript-free
navigation, damage recovery, and the final orientation:
npx coherence experiment create "the live swarm completes its domain task and leaves a recoverable field" \
--context src/changed-domain.ts \
--action "run the canary below with exact parent and child sessions" \
--success "the project-native acceptance criterion passes" \
--success "coordination, navigation, and recovery criteria all carry evidence" \
--session "$COHERENCE_SESSION"Run the canary on a disposable branch with one parent, two real child sessions, a dependency, a shared integration seam, and a reviewer handoff:
- Capture the clean baseline: project-native acceptance,
coherence verify, commit, elapsed time, and the expected changed paths. Prove the selected lifecycle bundle and current parent activation; have each child restate the exact work order itsSessionStartemitted. An unobserved bundle or empty event window is missing hook evidence, never a measured zero failure rate. - Make one pair of runnable orders claim an overlapping write scope. Before either writes,
orientmust sayRESOLVE-CONFLICT. Block or serialize one. A second overlapping order waiting on a declared dependency must remain potential overlap, never a live collision. Compare the actual diff and explicit-path trace with every declared write scope; the work ledger records authority but cannot prevent an out-of-scope write. - Have two children record incompatible
local-proposalchoices on one explicit subject. RequireRESOLVE-CONFLICT, then record the accepted authority and confirm timestamps did not choose it. Exercise early dependency refusal, handoff to the exact reviewer, and the parent's refusal to close until every live child is terminal and every completed child is named as synthesized. - Run the project-native acceptance and full verification. Preserve the command, output, and commit, then record the decision-to-work, work-to-commit, and verification-to-work links. The verification address is still assessor-authored, so the retained evidence is part of this criterion.
- Give a fresh agent no transcript. Its only starting surfaces are
orient, boundedcontext,work inspect, andconsequence inspect. Within the predeclared time and read budget it must recover the objective, authority, owners, dependencies, ratified choice, evidence, and next action, invent no causal edge, and make no out-of-scope follow-up edit. - In a disposable copy, damage one surviving ledger row and stale the verification. The
headings must move to
REFUSE, thenVERIFY, neverSTEADY. Restore the row, close every experiment criterion with evidence, and require native acceptance, current verification,regulate --checkrelease, andorientsteady.
The canary passes only with all assigned sessions accounted for, zero out-of-scope writes, the seeded collision detected before the first conflicting write, no false collision for the serialized pair, no old-owner write after handoff, complete child synthesis, zero hook failures across a nonzero predeclared event count, correct fresh-reader answers with zero invented links, and successful refusal and recovery. An empty trace cannot satisfy the hook criterion. A single canary establishes operability, not efficacy.
For efficacy, pre-register a matched task set or historical comparator and one primary metric. Report sample size and attribution grade beside median time to the correct heading, context bytes and outside reads, duplicate/conflicting edits, reviewer reconstruction time, rework, and escaped defects. Pass only if domain correctness does not regress and the predeclared primary metric clears its chosen improvement band; the other measures remain evidence, not a post-hoc score. A few matched tasks are a pilot, not a population claim.
Keep attribution at its weakest provable grade. Exact work owners and journal writers do not
make Codex descendant PostToolUse rows exact: those remain a parent-session-aggregate.
Explicit-path traces are a lower bound, shell/editor/remembered reads are absent, and current
verification references are not receipt-checked. Score per-child behavior only from exact
records, label aggregate measures as aggregate, and retain manual scope and verification
evidence rather than upgrading either ceiling by inference.
What this repository's V2 canary established
The run behind commit 5ff3e7d delegated three bounded implementation slices and then gave
a separate reader no transcript. That reader recovered the root objective, exact owners, a
host-session handoff, the accepted policy, explicit evidence links, trust ceilings, and the
executable next action from orient, context, work inspect, and consequence inspect
alone. The resulting tree passed 921 project tests and 80/80 coherence claims with all 60
declared invariants anchored.
The run also falsified four quiet paths before release: synthesis could outrank a live sibling
even though parent closure was impossible; same-HEAD source changes could leave verification
current; deleting the whole committed decision population could look like first adoption; and
the new work/consequence ledgers worked locally while Git ignored them. Each now has a named
negative control, and the last has an actual commit/clone/replay oracle rather than an ignore-
pattern proxy.
It did not establish hook reliability or swarm efficacy. Structural Codex control was
present, but this API-hosted parent session was unobserved and the experiment captured zero
trace and activity events, recorded honestly as none. No matched task population was run.
Treat this as strong mechanism and navigation evidence with an unproven host-telemetry arm,
not as evidence that swarms improve outcomes.
The rest of this README explains the trust model, adoption, claim language, instruments, and known ceilings behind that loop.
The economy of inference: what a codebase actually costs
A reader arrives at code with a question. Can I change this safely. What breaks if I do. Why is it this way. The reader is a human at 2am or an agent with a 400k window; the question is the same and so is the invoice. There are exactly three prices it can be answered at:
- Inferred — reconstructed by reading the code and simulating it in the reader's head. The most expensive operation in the system, and the only one that is paid again by every reader, forever: nobody's inference makes the next reader's cheaper. It is also the only price that is silent. Nothing in the repo records that it was charged.
- Read — looked up, because somebody cached the fact. A claim, an atlas entry, a journal line. The expensive reconstruction is paid once, at write time, by the party who already had the answer in hand and was therefore the cheapest possible payer. It is not free after that: every reader still pays retrieval, integration, and enough verification to trust the cache. The cache wins by making those payments smaller and bounded, not by making reading disappear.
- Unaskable — the question cannot arise, because the fact is structural. A capability that carries its own scope does not make a cross-tenant read checked; it leaves "could this read another tenant's rows?" with no site to be asked at. A sealed schema does not warn about an open object — an open object is a compile error. Zero read-time cost for that question; the design and migration that made it unaskable still had a construction cost, and future structural changes can incur another.
Coherence is a machine for moving facts down that ladder.
- A claim is a cached inference. "Does X hold across all of its sites?" — a question that otherwise costs a read of every site plus a mental proof — collapses to one anchored line with an oracle behind it. The build re-derives it so no reader has to.
- The atlas's
nonTransitionregistry is cached negative inference, which is the half nobody writes down. It pre-answers "do I have to worry about this symbol?" with a reasoned no. Without the entry, every future reader re-derives that no from scratch — and derives it slower, and less confidently, as the surrounding code grows. - The journal's
--overcaches the most expensive inference there is: the search that does not have to be run again. A settled question with its rejected alternatives attached is a question the next agent does not re-litigate, and re-litigation is full-price inference for an answer that already existed in the repo's history and was merely unaddressable. - The tier system is this ladder, priced. Tier-3 convention means the fact still lives at the inference rung — prose, sampled tests, memory. Tier-2 totality-checked means it has moved to read: one declarative home, plus an oracle that holds the cache warm and fails loud the moment the cache and the domain disagree. Tier-1 enshrined means unaskable: the type system or the addressing scheme carries it and no oracle is needed to keep it true. The enforcement ladder below and this one are the same ladder, read from opposite ends — enforcement is what it costs to keep a fact true, price is what it costs to find out.
- Ratchets exist because facts climb back up. A domain re-spelled by hand, a guard added at an N+1th site, an interpolation sink reopened: each is a fact demoted from read back to inferred, and each demotion is individually defensible. The baseline is what makes the aggregate visible.
The reducer function, in one line: dissolve > declare > infer. Make a fact
unaskable if it can be made unaskable; write it down if it cannot; and treat every fact
still living at the inference rung — on hot paths especially — as the tangle
inventory. That inventory is the work list, and it is the thing atlas, conventions,
redundancy, and contracts each report a slice of.
The symbol Σ makes the asymmetry explicit. It means “sum this cost over every instance,” here every future reader or change:
repository reading cost = Σᵣ (retrievalᵣ + integrationᵣ + verificationᵣ)For an implicit fact, retrieval includes discovery and verification includes rebuilding
the proof from code; for a declared fact, those terms are smaller but still present. The
problem agents sharpen is that reader r pays the whole term now while the damage from a
miss is propagated into later terms. Under a short-horizon prompt, reading broadly is a
visible cost and preserving context is mostly somebody else's benefit. The local gradient
therefore points toward the smallest patch that satisfies the prompt, including patches
that preserve or amplify a bad structure.
Coherence cannot repeal that gradient; it can move part of the future Σ onto the current
change. context makes a bounded, task-shaped first read cheaper without calling it
complete. signal --check makes significant new surface carry an anchor or a
patch-fingerprinted decision now. premise makes expired decision addresses visible.
The read/write hook trace and calibrate test economy's predicted context against what
agents actually loaded and whether labeled outcomes were clean. These are pressure and
instrumentation, not a proof that the right context was read.
Two secondary economies fall out of the same frame. Economy of writes is locality:
one intent should produce one write site, and co-change across a boundary is write
amplification — decompose and drift measure exactly that. Economy of reads is
context closure: how much has to be loaded before one thing can be changed safely.
Neither is byte count. Byte mass is orthogonal to all of it — 500KB of font files
carries zero inference surface, while a clever ten-line implicit coupling can be the
single most expensive object in the repo.
Two cautions keep the doctrine honest, and the harness is shaped by both. A cached
fact is itself mass, and it can go stale. Declared-but-wrong is strictly worse than
undeclared: the reader stops inferring, which was the entire point, and now stops at the
wrong answer. The whole "what happens when the claim being enforced is wrong?" discipline
— refutations, claim kinds, the never-red advisory, the meta-oracle's stated ceiling —
exists for this one hazard. And the bottom rung is the only free one. Every rung
above unaskable costs something to keep, which is why "declare everything" is not the
strategy and never was.
The maintenance-cost ledger: making the price perceptible
Trust accounting says where the untrusted becomes verified. Work accounting says what that costs to keep, continuously — and the instruments in v0.18–0.19 exist because the dissipation is otherwise imperceptible. A mechanical watch loses amplitude with every complication added and the watchmaker feels it; software's invoice arrives as velocity quietly decaying, months later, attributable to nothing.
masspins how much machine there is. Byte mass is not inference mass — but unpinned growth is where undeclared facts accumulate, and its failure message asks for the one thing the ratchet cannot know: name what the new mass buys (coherence decide), then re-pin.- holding cost (
verify) prices each promise: what this project pays, per run, to keep one cached fact warm. A claim eating a quarter of the run is not wrong — it is expensive, and expensive-to-keep-true is a real position to hold knowingly. - heat (
atlas) puts a temperature on every crossing: the share of recent commits touching a file that defines the chokepoint. A hot tier-1 crossing is not a finding; it is load-bearing and busy.
The compound reading is the useful one: heat × tier. High change traffic at an undeclared junction is a place where every edit is paying full inference price, over and over, in the busiest part of the map. That is where a fact should be moved down the ladder next.
The same ladder, read as trust
Everything above is the builder's view: what does it cost to work here. Turn the same ladder around and it is the relier's view: what may be safely assumed without checking. These are not two properties. Trust is the license to skip inference — and the ladder measures exactly that license, rung by rung. Going down it, cost falls; going up it, the license to rely weakens until it is nothing but hope.
The image is an electrical panel. A tidy panel — every breaker labeled, the work to code, the cover sealed, the permit history in the door pocket — is more trustworthy than a tangled unlabeled one, and the reason is not aesthetic. It is that a trustworthy panel can be acted on from its representations, while an untrustworthy one has to be re-derived by tracing wires. Tracing wires is inference. Every part of that panel that earns trust is a part that spared someone the trace:
- Labels are claims — and what makes a label trustworthy is not that it was written, it is that it was checked. A mislabeled breaker is worse than an unlabeled one: the unlabeled breaker gets traced, the mislabeled one gets believed. That is declared-but-wrong-is-worse-than-undeclared in one image, and it is why a claim without a live oracle behind it is a liability rather than an asset.
- Code compliance is dictionary conformance. The vetted pattern carries the safety
argument, so the inspector does not re-derive it per installation — which is precisely
what
conforms to <Word>buys, and precisely why a word's commitments have to be real (a greenconforms tothat ran none of its commitments would be a code stamp sold, not earned). - The sealed cover is tier-1. The mistake is not forbidden, it is made unrepresentable. Nothing to remember, nothing to check, nothing to re-derive.
- Trust is risk-weighted, and so is the compliance bar. "No security boundary without a totality oracle" is the same rule as stricter code on the 240V circuits — over- enshrining a lighting circuit is waste, under-enshrining a service entrance is how people get hurt. Match rigor to consequence.
- The permit and inspection history are the journal and the track record. Provenance
is a trust instrument: who decided this, over what, and why (
decide --over), and which checkers have ever actually been seen to fail (the never-red advisory'severFailed). - The atlas and
contractare inspection artifacts — the panel schedule taped inside the door. They are written for the relying party, not for the author, which is what makes them different in kind from the code they describe. - Refutations are proving the breaker trips. A checker never observed to fail has not been shown to be a checker. A negative control is how a label stops being a claim about the label and becomes a claim about the circuit.
Which closes the loop: dissolve > declare > infer is also the trust gradient. A fact dissolved into structure needs no trust at all; a fact declared and checked can be relied on to the exact strength of its oracle; a fact left at the inference rung can only be relied on by re-deriving it, which is to say it is not being relied on, it is being rebuilt. Economy and trust are one quantity seen from two chairs — the builder asking what this costs, the relier asking what may be assumed.
The assembly these parts build: an envelope, in constant motion
Scale the panel up and the whole discipline comes into view: coherence is an envelope
construction kit for software. A building envelope is the boundary assembly that makes
the interior governable, and its physics are the atlas's physics. Failures happen at
penetrations, so that is where the effort concentrates: every deliberate crossing is
flashed and sealed (a chokepoint with an oracle behind it), every non-penetration is
documented as one (nonTransition is the note that says this is wall, not window), and
the unsealed joints are on the drawing (knownPending) rather than discovered by the
weather. The envelope's cardinal property is continuity — a 99% continuous vapor
barrier is not 99% effective; one gap defeats it — which is the deep reason totality
oracles exist and their exact building-science name: a totality oracle is a blower-door
test. It does not check the seals you remember making. It pressurizes the assembly and
asks whether any gap exists at all. (A refutation is the smoke pencil: proof the test can
detect a leak.) And the payoff of the envelope is the interior: inside a sound one,
local reasoning is valid and context closure stays small. Interior code gets to be simple
because the boundary is doing the work — the economy frame and the envelope frame arriving
at the same place.
One thing separates this envelope from a building's: the primary weather is the
construction crew. A building is sealed once and then maintained against an exterior; a
codebase's chief threat and only maintainer are the same party, in constant motion, and
so maintenance signal cannot come from periodic inspection — it has to be produced as
exhaust of the work itself, at the moment of the work, by the worker, who is the only
party holding the answer at zero inference cost. That is what the journal is: signal
residue. Residue is what distinguishes a designed penetration from damage — a hole with
a decide behind it is a feature; a hole without one is a leak, and in a system in
motion that distinction cannot be recovered later, because it exists only at the moment
of the cut. And residue is what keeps the atlas an as-built drawing instead of a
blueprint: a static drawing of a moving building quietly stops resembling it, while a
drawing fed by the decision stream, the drift series, and the heat readings moves with
the walls.
The mental model: the enforcement ladder
Every rule a codebase depends on sits at one of three tiers. The harness exists to move rules up the ladder — which is the same motion as moving the fact down the price ladder above — and to make the current tier of every rule visible:
- Enshrined (structural) — the wrong state is unrepresentable. A capability type with no trust parameter to dial at a call site; a constructor that only produces the safe shape. The best tier: correct by construction, no oracle needed to stay correct. This tier belongs to the type system, not to coherence.
- Totality-checked — the rule has N sites, but ONE declarative home plus an
oracle that enumerates the live domain and fails loud when the declaration and
the domain disagree. Correct because checked every build. This is the tier the
boundaryclaim machinery targets. - Convention — N sites held together by memory. A latent tear: it holds only because everyone remembers, so it will eventually not hold.
The thesis in one line: conventions are failures lurking in the code; promote them to contracts — a type, a chokepoint, an oracle, a ratchet. Match rigor to consequence: not every rule needs tier-1 (over-enshrining is its own pathology), but a security rule at tier-3 is a bug waiting for a forgetful edit.
The honest ceiling, stated plainly: coherence verifies a boundary's anatomy — the invariant is named, the chokepoint symbol exists, the oracle runs and iterates a live domain. It does not verify that the wrong call is impossible (that's the type system's job, tier-1), and it does not verify that a claim is the right claim (that's the human's judgment — axiom #5, judge ≠ notary). It is a coherence layer, not a proof system. Treat every green run accordingly.
Install
Published on npm as @danilocampos/coherence
(the unscoped name was taken). Install it as a dev dependency; the coherence and
coherence-hook bins link into the consuming project:
npm install --save-dev @danilocampos/coherenceReleases are tag-driven with npm provenance. A repository ruleset makes existing v*
tags immutable; publication then requires that the exact tagged SHA passed main CI,
that the tag agrees with package.json, and that any existing npm version names that
same SHA. Use >=0.32.0 — the 0.31.0 artifact is deprecated (broken dependency
declaration).
To qualify unreleased work, a git dependency still works — npm clones the repo and
runs prepare (which builds dist/):
// package.json
"devDependencies": {
"@danilocampos/coherence": "github:daniloc/coherence#main" // or pin a tag/commit
}Then add scripts that call the bin:
"scripts": {
"coherence:graph": "coherence graph",
"coherence:docs": "coherence docs",
"coherence:verify": "coherence verify",
"coherence:claude-hooks-check": "coherence hooks --check --host claude",
"coherence:codex-hooks-check": "coherence hooks --check --host codex"
}Requires Node ≥22 in the consuming project (the build targets ES2022). One
runtime dependency: web-tree-sitter, the wasm parser runtime behind every language
instrument (the grammar binaries ship with the package under grammars/, provenance
alongside).
Configure the target project
Add coherence.config.json to the project root. Its presence is the declaration
that this directory is a coherence root: walking commands (verify, graph, the
ratchets) refuse without one — a configless run started in the wrong directory would
walk and grade everything under it — while journal, hook, and reference commands work
anywhere. {} is a complete config (the defaults do the rest). A useful minimal one:
{
"typecheck": ["npm", "run", "typecheck"],
"test": ["npx", "vitest", "run", "-t"],
"testMatch": "[1-9][0-9]* passed"
}Languages. Three are built in — "language": "typescript", "python", or "ruby"
resolves against grammar binaries that ship with the package (grammars/, provenance
alongside), and every instrument reads them through those grammars. What each language
gets today, with the per-instrument query tables as the source of truth:
| Instrument | typescript | python | ruby | your adapter |
|---|---|---|---|---|
| Graph, claims, prose (tree-sitter.ts) | ✓ | ✓ | ✓ | ✓ |
| Serial test oracles (test argv + testMatch) | ✓ | ✓ | ✓ | ✓ |
| Everything language-blind (hooks, journal, mass, drift, atlas) | ✓ | ✓ | ✓ | ✓ |
| Surface → zero-anchor alarm (SURFACE_LANGUAGES, novelty.ts) | ✓ | ✓ | — | — |
| via test oracle + parity analysis (oracle-domain.ts) | ✓ | ✓ | — | — |
| Duplicate-domain ranking (SITE_LANGUAGES, redundancy.ts) | ✓ | ✓ | — | — |
| Injection sinks (SINK_LANGUAGES, lint-sinks.ts) | ✓ | ✓ | ✓ | — |
| Batched oracles (testBatchFormat) | vitest-json | pytest-json | serial | serial |
A — costs you the instrument, never a false verdict: an uncovered language simply
contributes nothing there. One consequence worth knowing: via test claims in an
uncovered language fail oracle analysis rather than pass vacuously — set
"oracleDomain": false until the language has an oracle arm.
Serial oracles are runner-agnostic — the claim's oracle name is appended to test and
testMatch guards the output — so rspec is "test": ["bundle", "exec", "rspec", "-e"],
go is ["go", "test", "-run"], and so on. Only batch formats are enumerated.
A python configuration, end to end (pytest-json-report provides the batch report):
{
"language": "python",
"codeExt": ["py"],
"testDir": "tests/",
"test": [".venv/bin/python", "-m", "pytest", "-k"],
"testMatch": "[1-9]\\d* passed",
"testBatch": [".venv/bin/python", "-m", "pytest", "--json-report", "--json-report-file=.coherence/test-report.json"],
"testBatchFormat": "pytest-json",
"ignore": ["node_modules", ".git", ".venv", "__pycache__", ".pytest_cache"]
}A passes test claim cites the pytest function name (test_…); in batch mode it
matches every parametrized case of that function and all must pass.
Adding a language is a ladder — each rung is useful on its own:
Rung 1 — a built-in name. typescript, python, ruby: config only, nothing to
write. An unknown bare name refuses with the live built-in list rather than falling
back (a wrong grammar would grade a different tree than you configured).
Rung 2 — a project adapter: the graph tier for any language. language accepts a
./-relative module path, and for most languages you never write parsing: modern
tree-sitter grammar packages ship a prebuilt wasm (no native toolchain —
web-tree-sitter runs it sandboxed), and the shipped factory turns a grammar plus
~30 lines of capture queries into an adapter. Go, for example:
{ "language": "./.coherence/adapters/go.mjs", "codeExt": ["go"] }// .coherence/adapters/go.mjs — npm i -D tree-sitter-go for the grammar wasm
import { makeTreeSitterAdapter } from "@danilocampos/coherence/dist/adapters/tree-sitter.js";
export default await makeTreeSitterAdapter({
exts: ["go"],
symbolQuery: `
(function_declaration name: (identifier) @function)
(method_declaration name: (field_identifier) @method)
(type_declaration (type_spec name: (type_identifier) @type))
`,
importQuery: `(import_spec path: (interpreted_string_literal) @spec)`,
lineComment: "//",
}, new URL("../../node_modules/tree-sitter-go/tree-sitter-go.wasm", import.meta.url).pathname);Capture names become symbol kinds; the shipped specs in src/adapters/tree-sitter.ts
are the reference. Prose extraction is a named docStyle strategy ("line",
"jsdoc", "docstring"); a project module may instead supply its own docs
functions or hand-implement the five LanguageAdapter members directly — rung 2 is
code territory by definition, and both shapes serve the same seam. A wrong-shaped
module refuses naming the broken field. Importing the module executes project code —
the same declared trust as the config's test/typecheck argv.
Rung 3 — the full field: instrument rows. The instrument arms read languages as
pure data, so giving a language an instrument is a table row of capture queries, not
an analyzer: a SURFACE_LANGUAGES row (novelty.ts) feeds the zero-anchor alarm, a
SITE_LANGUAGES row (redundancy.ts) feeds duplicate-domain ranking, a
SINK_LANGUAGES row (lint-sinks.ts) feeds the injection ratchet, and an
ORACLE_LANGUAGES row (oracle-domain.ts) carries the via test analysis. The rule
every row lives under is enforced, not aspirational: a built-in pack carries queries,
patterns, and named strategies — never functions — and a guard sweeps every table
for violations (language-packs.ts). Rows live in the harness today, so rung 3 is a
contribution — small ones, as the ruby sinks row (three lines) shows — and the house
rule for every row is the one this repo's own migration was held to: build the new
reader beside an existing witness and gate them against a real corpus before trusting
it. A batch report format (testBatchFormat) is likewise a registered parser in
test-batch.ts; serial oracles need nothing.
Adopt the lifecycle control
Do this after npm install and after coherence.config.json is in its final
directory. Run the commands from that directory—the coherence root, where the
consuming package.json installed @danilocampos/coherence. npx below resolves that
project-local dependency; no global installation is assumed.
Name each host's project root when it differs. The host project root is the directory the agent host opens and where that host keeps project hooks. Claude owns
.claude/settings.json; Codex owns.codex/hooks.json. For an ordinary single-root layout, omit both fields. If coherence is installed inapp/while both hosts open the repository root, put this inapp/coherence.config.jsonbefore installing either control:{ "claudeProjectRoot": "..", "codexProjectRoot": ".." }codexProjectRootdefaults toclaudeProjectRoot, then".", but the explicit field is preferable when the layouts differ. In a Git checkout it must resolve to the same directory asgit rev-parse --show-toplevel: Codex's canonical command deliberately finds.codex/coherence-hookfrom that root. It is not an arbitrary directory in which to park hook files. Outside Git, Codex treats the session working directory as its project root; install and launch the session from this configured root. Current-session activation remains the runtime proof that the structural path actually fired.Converge one host's control ON. Do not author a repository-specific command or paste a near-equivalent hook block:
npx coherence hooks install --host claude npx coherence hooks install --host codex --session "$CODEX_THREAD_ID"--hostis deliberately explicit: a bare command remains Claude for compatibility, even whenCODEX_THREAD_IDis present.--sessionis optional for installation and does not activate a running session—it asks the report printed after installation to inspect that exact session.Installation preserves unrelated settings and hooks and publishes exactly one shared five-event bundle for the selected host. The lifecycle domain is the same, but the host syntax is not. Claude receives:
"$CLAUDE_PROJECT_DIR/.claude/coherence-hook" EVENTCodex receives its own matchers and launcher identity:
codex_root=$(git rev-parse --show-toplevel 2>/dev/null || pwd -P); "$codex_root/.codex/coherence-hook" EVENTIn particular, Codex
SessionStartmatchesstartup|resume|clear|compact, whilePostToolUsematchesBash|apply_patch|update_plan|mcp__.*. Layout is carried separately by the selected host'scoherence-rootfile. If installation reports a missing lifecycle target, stop: install dependencies in the coherence root before editing host settings.Commit the complete selected control. Track the selected host's three artifacts:
.claude/settings.json.claude/coherence-hook.claude/coherence-root
or:
.codex/hooks.json.codex/coherence-hook.codex/coherence-root
Also commit
coherence.config.json. In a nested layout these files deliberately live at different levels: the config/package in the coherence root, the three control files in the host project root..claude/settings.local.jsonis not part of Claude's shared control; the installer only removes recognized competing coherence actions from it. Codex's.codex/config.tomlis inspected but not owned: an inline hook table prevents a singular project path and makes installation refuse, whilefeatures.hooks = falseorallow_managed_hooks_only = trueleaves the files configured but the project control absent. User, managed, and plugin Codex layers remain outside a repository check's authority.Accept the structural bit. With no session in scope, this is the adoption gate and is suitable for CI:
npx coherence hooks --check --host claude npx coherence hooks --check --host codex # or use the corresponding host-specific package script aboveExit
0means the singular canonical bundle, launcher, root mapping, and runnable target are all present. Exit1means absent/noncanonical; exit2means the checker could not answer safely (for example, invalid settings). A partial block, duplicate, legacy spelling, competing action, drifted launcher, misaligned Codex/Git root, excluded or disabled Codex project hooks, or missing target is OFF.Activate and prove the current Codex session separately. Installation cannot make a hook fire retroactively. Review the exact project hook in Codex
/hooks, then start or resume the session soSessionStartcrosses the installed launcher. Inspect the same session by id:npx coherence hooks status --host codex --session "$CODEX_THREAD_ID" npx coherence hooks --check --host codex --session "$CODEX_THREAD_ID"With
--session,--checkrequires both the structural bit and an event delivered by the exact selected host, launcher transport, and installed bundle fingerprint. That fingerprint includes the hook-body protocol as well as settings and launcher bytes, so an event from an older wire contract cannot prove the new body ran. A manualcoherence hookprobe is reported as direct evidence, not activation; an older bundle is reported as stale. There is deliberately no “newest session” fallback—concurrency makes newest an attribution bug. When neither--session,COHERENCE_SESSION, norCODEX_THREAD_IDsupplies an identity,statussays the current session is unknown. Historical hook-opened sessions remain telemetry: yesterday's firing cannot redeem wiring removed today, and a newly installed runnable control can be present while this session's activation remains unconfirmed.Read the attribution ceiling. Codex
PostToolUsecarriessession_idbut noagent_id, and subagent hooks share the parent's session id. The parent session file may therefore aggregate parent and descendant tool activity. Coherence records those rows asparent-fallback, reports their count in session status, and never promotes them to exact child evidence. A matching host/launcher/bundle row may still prove that the installed control reached the named parent session; activation does not strengthen its attribution. This is an honest aggregate, not a best guess. A first-class experiment keeps valid fallback rows but labels the resulting telemetryparent-session-aggregate: it may include descendant work and is never presented as exact owner or child evidence. Exact agent/session rows areowner-session, an empty post-open window isnone, and trace rows written before observation metadata existed remain visible aslegacy-unscoped. Unreadable or internally unscoped/unknown rows refuse closure.noneis an attribution result, not proof of a zero failure rate: without current-bundle activation and a nonzero predeclared event denominator, hook reliability remains unmeasured. Likewise, aSubagentStopwithout an exact child id reports the child journal count as unavailable and takes no child calibration snapshot; the repository-wide open-conjecture reminder remains explicitly repository-wide.Watch the record as it is written. With the control on, every agent session journals as it works. From the coherence root, in a second terminal:
npx coherence journalThis is the payoff surface of the whole record: every stream interleaved, newest first, live.
⏎drills into an entry,clists the open conjectures,ffollows the tip as agents write. Leave it open while a fleet runs and you are reading your agents' reasoning at the moment it happens instead of reconstructing it afterward.
npx coherence hooks print --host codex (or --host claude) renders that host's canonical
settings, launcher, and mapping for inspection. It is not the preferred installer. The
launcher and mapping paths are coherence-owned: install repairs drift at those names,
while uninstall --host … removes them only if their bytes still prove coherence ownership.
The project's voice in the emissions
The canonical hook text is the harness's: identical in every adopting project, which is
what keeps it byte-testable. What your project knows and the harness cannot — its own
commands, its conventions, the one warning its history taught it — goes in per-event
files under .coherence/hooks/:
<Event>.override.mdreplaces that event's canonical emission<Event>.append.mdfollows whatever the base said
for any of the five lifecycle events. One composition rule, no conflict state: the
override (if any) is the base, otherwise the canonical text is; the append follows it.
An empty override deliberately silences the event. Events that canonically emit
nothing (main Stop, PostToolUse) speak only when a project declares a voice there —
and the stop-loop guard still outranks it. Tokens {{session}}, {{agent}}, {{cli}}
and {{scope}} substitute at emission ({{agent}} is only guaranteed at the start
events); a token the harness cannot supply stays honestly literal.
A customization file that cannot be read costs exactly the customization: the event falls back to its canonical emission rather than breaking the agent's session. The loud surface for both damage and review is:
npx coherence hooks reviewwhich prints every event's effective emission with provenance — canonical, override,
append, silenced — and a warning: line per unreadable file. Commit .coherence/hooks/
alongside the decisions folder: the voice is part of the field, and it should travel with
the repository.
Regulate the field: one next action
An instrument answers one question. A regulator decides which answer should change what
you do next. coherence regulate is the first deliberately narrow control loop over
the existing instruments: observe, select one intervention, act, then run it again. It is
not another dashboard or an umbrella verifier.
npx coherence doctrine # inspect the law being applied
npx coherence regulate # current host (Codex when CODEX_THREAD_ID is set)
npx coherence regulate --host claude
npx coherence regulate --host codex --since origin/main
npx coherence regulate --check --host codex
npx coherence regulate --host codex --jsonThe doctrine is versioned and built in, not a configurable score. Its potential is lexicographic: refuse an unavailable required reading, then require a decision, then redirect an absent lifecycle control to its one repair command, otherwise release. The first nonempty class wins. One run emits at most one executable command; lower-priority obligations are counted as withheld, and a rerun after the selected action reveals the next one. Insertion order, duplicate readings, and locally convenient weights cannot change that order.
V2 evaluates exactly four rules: the selected host's canonical lifecycle control is
present; swarm decisions and work are attributable, structurally readable, and
collision-visible; completed work carries an explicit verification-to-work consequence
link; and significant behavioral growth carries an invariant/boundary/parity anchor or a
standing patch-bound decision. That is the whole domain. release therefore means “no
intervention under those four live rules,” never “this repository is coherent” or “this
change is correct.” coherence doctrine [--json] prints the rules and their stated
limits from the same registry the selector executes.
--host selects the control being regulated; when omitted, a Codex process selects Codex
and other environments select Claude. The host is part of the reading and decision id,
and an absent control redirects to hooks install --host <that-host>—a present Claude
control cannot redeem Codex. The command is explicit and read-only. It installs no hook,
adds no anchor, writes no
attestation, and does not update the journal or status record. Without --check, a
redirect or decision requirement is advisory and exits 0; with --check, either exits
1. Release exits 0, and refusal—where the regulator could not obtain a required
reading—exits 2 in both modes. --json changes only the representation.
The full regulator selector is intentionally not in any hook yet. SubagentStop
independently reports the pre-existing change signal, but main-agent Stop emits no
feedback at all: it fires once per turn, and any output would make the host continue.
Putting an uncalibrated selector there would turn an explicit report into repeated ambient
control. The first release earns automation by direct use. A later rollout needs genuine
per-agent attribution before it can canary the same decision without charging one agent
for another agent's shared-worktree change.
Representative configuration (the Config interface in src/types.ts is authoritative;
defaults come from src/config.ts):
{
"outputDir": "docs/coherence",
"entryDir": ".",
"tooling": ["scripts"],
"ignore": ["node_modules", ".git", "dist", ".wrangler", "__tests__"],
"codeExt": ["ts", "sql"],
"typecheck": ["npm", "run", "typecheck"],
"test": ["npx", "vitest", "run", "-t"],
"testMatch": "[1-9][0-9]* passed",
"testBatch": ["npx", "vitest", "run", "--reporter=json", "--outputFile=.coherence/test-report.json"],
"testBatchFormat": "vitest-json",
"oracleDomain": true,
"staticOracleExistence": true,
"language": "typescript",
"platform": "cloudflare",
"claudeMdPath": "../CLAUDE.md",
"claudeProjectRoot": "..",
"codexProjectRoot": "..",
"dictionary": "dictionary",
"sources": ["src"],
"testDir": "__tests__",
"components": [{ "name": "billing", "files": ["src/billing/**"] }],
"conventions": { "guardVerb": "^(assert|require|check)", "seed": [], "dismissed": {} },
"sinks": { "safeSql": "quoteIdent\\(", "safeHtml": "escapeHtml\\(" },
"atlas": { "charts": {}, "transitions": {} },
"novelty": { "minSurface": 8, "minLoc": 400, "ratio": 12 },
"artifacts": { "worker": ["worker.ts", "entities/**", "shared/**"], "browser": ["web/**", "shared/**"] },
"contracts": { "sse-frames": { "producer": "Patient", "consumer": "readSse", "type": "SseFrames" } }
}Config reference
| Field | Default | Purpose |
| --- | --- | --- |
| outputDir | "public" | Where generated artifacts go (graph.json, _graph.html, _overview.html, ratchet baselines). |
| entryDir | "." | The entrypoint component's dir (. = root). |
| tooling | [] | Path prefixes demoted to a "tooling" group in the graph. |
| ignore | ["node_modules",".git","dist",".turbo",".wrangler"] | File or directory names the spec/code walk never enters. Put machine-generated environment declarations here when platform capability inference should use authored types only. NOTE: neither the meta-oracle nor the fast Vitest name floor reuses this graph list when hunting for oracle test files (see below). |
| codeExt | ["ts"] | File extensions treated as code for the tree. |
| typecheck | ["npm","run","typecheck"] | Command the typechecks claim shells. |
| test | [] | Base command for passes test "<name>" / boundary-oracle claims; <name> is appended as the final arg. Empty = those claims skip. |
| testMatch | unset | Optional regex the test output MUST contain to count as a pass. Guards the serial arm against runners like vitest -t that exit 0 when the name matched nothing. Not needed for batched claims — a batch report observes a missing test directly (see "Batched oracle execution"). It does not protect a node --test project at all (measured: a pattern matching nothing still reports the file as one passing test). |
| testBatch | derived from test | Command that runs the whole suite once and emits a machine-readable report. Left unset, coherence derives it when the runner is recognizable (vitest today), so batching needs no configuration. Set it explicitly for any other runner: ["npx","vitest","run","--reporter=json","--outputFile=.coherence/test-report.json"]. |
| testBatchFormat | "vitest-json" | The report format: vitest-json or pytest-json. An unknown value fails the run immediately — it is never a silent fallback to the slow path. |
| oracleExecution | unset (= batched) | Set to "serial" to demand the pre-0.17 profile: one full test-pool boot per claim. Supported, never implicit — see "Batched oracle execution". Same as the --serial-oracles flag, which wins if both are present. |
| oracleDomain | true (anything but false) | The META-ORACLE gate: assert a via test oracle iterates a LIVE domain. Set false to disable the gate. |
| staticOracleExistence | true when Vitest is identifiable | The runner-free verify --fast name-existence floor for conventional Vitest source. Set false when a project's test registry is assembled outside the scanner's direct-declaration grade; named executable claims then remain UNKNOWN/skipped and no source index is built. |
| language | "typescript" | Language adapter key. |
| platform | null | Platform adapter key, or null. |
| components | unset | Sub-component overrides for decompose/drift co-change analysis ONLY (globs relative to root; first match wins). The spec graph, verify, and coverage are untouched. |
| claudeMdPath | "CLAUDE.md" | Path to the CLAUDE.md whose fenced block coherence claude owns. May be ../-relative to escape the coherence root (repo-root CLAUDE.md above a sub-package). |
| claudeProjectRoot | "." | Path from the coherence root to the Claude project root whose .claude/settings.json owns lifecycle hooks. Set ".." when coherence/package.json lives in a sub-project but Claude opens at the repository root. The installed launcher remains identical; .claude/coherence-root carries the relative address back. |
| codexProjectRoot | claudeProjectRoot, then "." | Path from the coherence root to the Codex project root whose .codex/hooks.json owns lifecycle hooks. In a Git checkout this must resolve to git rev-parse --show-toplevel, because the canonical launcher is found from that root. The Codex and Claude controls remain independent even when their roots coincide. |
| dictionary | "dictionary" | Dir (relative to the coherence root) holding the pattern dictionary — one <Word>.md per word. A conforms to <Word> claim expands the word's commitments against the declaring component. A project with no such dir simply has no words (see "The dictionary" below). |
| sources | [entryDir] | Dirs the lint-sinks/conventions scans are scoped to — keep generated/vendored trees out. |
| testDir | "__tests__" | Path substring identifying test files for the ratchet scans. |
| conventions | unset | guardVerb (regex for guard-function NAMES), seed (extra guard names), dismissed (guard → why it's covered elsewhere). |
| sinks | unset | safeSql/safeHtml — regexes for interpolation expressions that are SAFE by construction. |
| mass | unset | The mass ratchet's project-owned half: measures ([{ key, cmd, unit? }] — each cmd runs from the project root and the last numeric token of stdout is the value; a nonzero exit or unparseable output is UNMEASURABLE and fails --check, never 0), deps (set false to drop the package.json / package-lock.json dimensions), tolerance (per baseline key — how much growth is allowed before the ratchet says so; default 0). |
| atlas | unset | Trust-manifold data: charts (trust domain → description), transitions (chokepoint symbol → crossing; each may set enshrined: true — see below), nonTransition (within-chart boundaries), knownPending (mapped symbols tolerated as not-yet-in-source). A transition's enshrined: true is an explicit attestation that the illegal value at that crossing is unrepresentable (a runtime-branded capability), promoting it to tier-1 — it is NOT inferred from a claim's verb, and it MUST be backed by a via guard boundary claim (an enshrined marker with no backing guard fails atlas --check). |
| novelty | unset | Thresholds for log's novelty-vs-anchor advisory: minSurface (8), minLoc (400), ratio (12). |
| redundancy | unset | Thresholds for the redundancy advisory (undeclared duplicated domains): minShared (3), containment (0.7), minScore (3.5), maxDf (6), top (10). Every knob trades recall for precision — a wall of candidates is worse than silence. |
| artifacts | unset | Deploy units for contracts: unit name → path globs. A file may belong to several (shared vocabulary typically does). |
| claimKinds | unset | The kinds a claim may declare via a trailing [kind], and each kind's policy: { "measured": { "policy": "warn", "why": "…" } }. pin gates normally; warn gates but reports every run. An undeclared kind fails the run. Unset = feature off, no output, no behaviour change. See "Claim kinds" below. |
| contracts | unset | Declared cross-unit data contracts: name → { producer, consumer, type, description? } (all symbols). contracts --check fails a contract that dangles or that no boundary/parity claim anchors. |
Then author *.spec.md files (a folder containing one is a node). A spec is
# Name, a one-line intent, an optional ## works when claim list, an optional
## invariants list, an optional ## refutations list, and an optional ## why
(protected rationale). Claims are a
grammar, not prose — the parser (src/walk.ts) strips markdown-formatter escapes
(\_ → _) so a prettified spec still parses.
The claim phrasebook (the ## works when grammar)
The claim grammar is a declarative registry — CLAIM_FORMS in src/phrasebook.ts,
an ordered list of forms where first match wins (the order IS the precedence).
evalClaim (src/verify.ts) is a thin loop over it. A line matching none of these
is SKIPPED (no verifier (dialect gap)) — it never goes red. A typo'd verb is
therefore a silent no-op; check verify's skipped count after authoring claims.
The index below is derived from that registry by coherence docs — the same
marker-pair machinery as the command index, checked the same way (docs --check and
test/commands.test.ts byte-compare it against the registry). It replaced a
hand-maintained copy that had drifted exactly as its own caveat predicted: 8 forms
listed while the registry carried 9 (lives in was missing), and a boundary grammar
that had lost the crossing clause. coherence phrasebook prints the same registry
at the terminal.
9 claim forms, in registry order — first match wins, so this order IS the
precedence. Derived from the same registry evalClaim executes (coherence phrasebook
prints it at the terminal), so it cannot drift from the grammar. The per-form notes below
the block are authored.
- typechecks [deterministic] —
typechecks
