Skip to content

Guaranteed, advisory, planned

What Majordomus does today, row by row, with the file that implements it, the test that is to prove it, and what that test's recorded run says.

A supervisory tool that overstates itself is the thing it was built to prevent. This page is generated from docs/CLAIMS.yaml; the build fails if any source, implementation or test path in it does not exist, or if a guaranteed claim names no test. A status is what the file declares; the recorded evidence column is what majordomus evidence show derives from the runs actually recorded, and only a supported verdict is coloured as one.

The same data, drawn as the graph it is: every claim wired to the document, the implementation and the test behind it →

guaranteed

claimsourceimplementationtestrecorded evidence
Every platform, artifact name and installation URL is derived from one model, and a projection that disagrees with it is refused

share/distribution.yaml is the model. The installer's platform table, the release build matrix, the documentation's table, the website's dataset and the public release metadata are generated from it, and `majordomus generate --check` exits 10 naming any that has fallen behind.

docs/DISTRIBUTION.md apps/majordomus-cli/src/distribution/mod.rs test/cases/84_distribution_model.sh recorded evidence: not run
The documented one-line install command is composed from the model's parts, and a document that states a different one fails the suite

The download command, the base URL, the script name and the shell are four fields; the command is their composition. README.md and the installation guide are checked against it rather than trusted.

docs/INSTALL.md apps/majordomus-cli/src/distribution/mod.rs test/cases/84_distribution_model.sh recorded evidence: not run
The installer verifies an artifact's digest and inspects its archive before anything is unpacked, and any failure leaves the previous installation working

A wrong digest, a truncated download, an archive that escapes its own directory or carries a link, and an executable that reports another version are each refused by name, before the launcher is replaced. There is no warn-and-continue path and sudo appears nowhere.

docs/INSTALL.md share/install/install.sh.in test/cases/85_installer.sh recorded evidence: not run
A release publishes an artifact for every supported target or it is not published

The build matrix stops at the first failure, publication begins only when every build succeeded, and the recorder refuses to write a record missing a target the model marks supported.

docs/DISTRIBUTION.md scripts/release-record test/cases/87_release_pipeline.sh recorded evidence: not run
The install command this project advertises is fetched and run from the published site every night and on request, and the run that asked for it is red while it does not install a working tool

The line is run as it is written, pipe included, on Linux and macOS, into a home directory of the run's own. It proves the metadata resolves, the tool reports the version that was resolved, the MCP launcher runs with no toolchain, and a repository can be initialised. A release proves the same thing once at publication; this keeps proving it afterwards. ADR 0037 plans it on demand, because its job has a macOS leg and those runners queued for hours: the nightly schedule, a dispatch and a pull request labelled ci:full ask for it, a routine push does not.

docs/DISTRIBUTION.md scripts/ci/install-check test/cases/97_install_gate.sh recorded evidence: not run
A release archive carries every path once and carries nothing but files and directories

The packer reads back what it wrote and deletes an archive carrying a repeated path or an entry that is not a file or a directory; the verifier checks the same two properties again on the runner that built it. There is no fallback packer, because an archive packed another way is not the one that was verified.

docs/DISTRIBUTION.md scripts/release-package test/cases/87b_release_archive_shape.sh recorded evidence: not run
The canonical policy is parsed and rejected if it contains an unknown key

A restricted YAML subset. Tabs, anchors, flow maps and multi-line scalars are refused rather than guessed at.

docs/SCHEMAS.md lib/common.sh test/cases/00_yaml_flatten.sh recorded evidence: not run
Every profile is parsed, its unknown keys rejected, and the default profile proven to exist docs/SCHEMAS.md lib/doctor.sh test/cases/02_doctor_basic.sh recorded evidence: not run
Every enforcement the policy declares is reconciled against what actually runs

The path must exist, be executable, and be invoked by the named dispatcher. A hook that invokes it and discards the exit code counts as not wired.

docs/DESIGN.md lib/doctor.sh test/cases/03_update.sh recorded evidence: not run
Exit codes are a contract, and no code means "warn and continue" docs/CLI.md lib/common.sh test/cases/03_update.sh recorded evidence: not run
Provider instruction files are generated from the one policy, deterministically

Running update twice leaves the byte-identical file. The test asserts the hash.

docs/DESIGN.md lib/update.sh test/cases/03_update.sh recorded evidence: not run
Every generated instruction file carries its own stamp, and a hand edit is detected docs/SCHEMAS.md lib/update.sh test/cases/03_update.sh recorded evidence: not run
A projection can own only the region between its markers, leaving the rest of the file untouched

How a repository that already has a hand-written CLAUDE.md adopts Majordomus. An edit outside the markers is never drift; an edit inside them is caught. Malformed markers are refused, not guessed at.

docs/SCHEMAS.md lib/update.sh test/cases/13_region_projection.sh recorded evidence: not run
Wiring is found in a hook or in any subhook of the directory that hook dispatches to

The finding names the file that actually carries the invocation. A subhook that is not executable is reported as not wired, because the dispatcher skips it.

docs/CLI.md lib/doctor.sh test/cases/14_wiring_dispatcher.sh recorded evidence: not run
Every rule the tool enforces is declared once, as a rule object in the repository's effective set, and doctor proves each one is reached by the command that claims to run it

A doctrine whose validator does not exist, a validator no doctrine declares, a command that never dispatches, a blocking rule whose command cannot exit non-zero, a missing test, and CI that swallows the suite are six separate failures with six separate messages. Each is mutation-tested.

docs/DOCTRINE.md lib/doctrine.sh test/cases/18_doctrine_wiring.sh recorded evidence: not run
A doctrine's class is what decides whether a violation stops the command, not a label beside it

The test changes checkpoint freshness from advisory to blocking in a copy of the vendored rule file and asserts that check's exit code moves from 0 to 10. There are two classes and no override.

docs/DOCTRINE.md lib/doctrine.sh test/cases/17_doctrine_enforcement.sh recorded evidence: not run
Every line of the append-only ledger is a well-formed event, and a malformed line is a failure rather than a skipped record

The reader skips a bad line rather than crashing, and check, doctor and watch all report it. The ledger is the one durable record nothing else can reconstruct.

docs/DOCTRINE.md lib/check.sh test/cases/22_history.sh recorded evidence: not run
An entry in the questions store that does not parse blocks acceptance exactly as an unresolved question does

A gate that cannot read an entry can be bypassed by mistyping one, so an unreadable question is not a lesser problem than an open one.

docs/DOCTRINE.md lib/check.sh test/cases/21_decision_question.sh recorded evidence: not run
A declared provider hook captures the person's raw prompts below the model, and doctor proves it by running it

Covers the five states a provider can be in, idempotence on the provider's prompt identity, an archive that is ignored and untracked, a record set that cannot carry model output, the record-and-rendering pair every prompt is kept as, and the schema identifier resolving to the two files that describe both halves.

docs/DOCTRINE.md lib/capture.sh test/cases/29_prompt_capture.sh recorded evidence: not run
A captured prompt names the episode it belongs to or records that none could be resolved; the archive is private, bounded and free of credential material

The mapping from a provider session to an episode used to exist only while the episode was open, so a closed episode's prompts became unattributable at once — 713 of this repository's 974 records. The record now carries the episode, the provider session, the repository and the worktree, with the provenance of the link; the archive is 0600 and bounded by prompts.retention_max_days and prompts.retention_max_bytes, which prune the body and keep the record; and credential shapes are replaced before anything is persisted. The unlinked legacy records stay unlinked: the evidence for them does not exist, and this claim does not pretend otherwise.

docs/DOCTRINE.md lib/capture.sh test/cases/140_prompt_continuity.sh recorded evidence: not run
Every repository-local prompt asset renders, and an unknown token is a failure

Covers name and filename mismatch, an empty description, an unknown front-matter key, missing front matter, and a block token used inline.

docs/DOCTRINE.md lib/prompt.sh test/cases/24_prompt_search.sh recorded evidence: not run
A use case or application cannot name a command, doctrine or claim that does not exist, and the two catalogues cannot disagree about which applies to which

Prose describing the tool is checked the same way the tool checks itself. Cross-references are verified in both directions, so a use case naming an application that does not name it back is a failure rather than a silent asymmetry. An application declaring no does_not_fit_when is refused, because a catalogue that only lists fits is marketing.

docs/DOCTRINE.md lib/usecase.sh test/cases/28_catalogue.sh recorded evidence: not run
A hand-edited instruction file is never silently overwritten

update refuses, offers a diff, and overwrites only under an explicit flag.

docs/CLI.md lib/update.sh test/cases/03_update.sh recorded evidence: not run
An archive that lost the file modes fails rather than travelling

The mode of every entry is taken from the git index, written into the archive manifest and proved through the container before the command returns.

docs/ARCHIVE.md lib/archive.sh test/cases/130_archive.sh recorded evidence: stale
The always-loaded instruction file has a line budget with a failing check

Over budget, nothing is written at all.

share/skeleton/policy.yaml lib/update.sh test/cases/03_update.sh recorded evidence: not run
Every repository-relative reference in the always-loaded file is proven to resolve docs/DESIGN.md lib/doctor.sh test/cases/03_update.sh recorded evidence: not run
A hardcoded count in the always-loaded file is a failure docs/DESIGN.md lib/doctor.sh test/cases/03_update.sh recorded evidence: not run
The append-only ledger and handover store have retention caps that are checked share/skeleton/policy.yaml lib/doctor.sh test/cases/02_doctor_basic.sh recorded evidence: not run
Installing into a repository that already has an installation is refused docs/CLI.md lib/init.sh test/cases/01_init.sh recorded evidence: not run
Every finding carries the command that reproduces it docs/DESIGN.md lib/common.sh test/cases/02_doctor_basic.sh recorded evidence: not run
The shell tool performs no network call, evaluates no generated text and deletes nothing recursively; its one declared request is the loopback peer probe docs/DESIGN.md bin/majordomus test/cases/08_no_forbidden_constructs.sh recorded evidence: not run
A task is started with a declared file scope and a profile, and only one task is active per checkout

A scope path that escapes the repository root is refused. Starting a second task while one is active is refused.

docs/CLI.md lib/start.sh test/cases/04_start_check.sh recorded evidence: not run
Identity fields on every state record are computed from git and never authored

A handover body that tries to set one is rejected rather than merged.

docs/SCHEMAS.md lib/start.sh test/cases/04_start_check.sh recorded evidence: not run
check reports whether state, scope, blockers and checkpoint age are consistent right now docs/CLI.md lib/check.sh test/cases/04_start_check.sh recorded evidence: not run
A touched file outside the claimed scope is a failure, not a warning docs/DESIGN.md lib/check.sh test/cases/04_start_check.sh recorded evidence: not run
State read back is labelled exact, advanced, diverged or different_context against git docs/SCHEMAS.md lib/common.sh test/cases/04_start_check.sh recorded evidence: not run
Overlap between parallel workers is computed on claimed paths in both containment directions

Reported, never blocking. Work in progress is reported and never blocked.

docs/DESIGN.md lib/check.sh test/cases/04_start_check.sh recorded evidence: not run
watch reports policy, projection, state, scope, handover, verification, staleness and retention drift docs/DESIGN.md lib/watch.sh test/cases/07_watch.sh recorded evidence: not run
handover writes an append-only record with computed front matter and required sections

A body with a missing or empty required section is refused and names the sections.

docs/SCHEMAS.md lib/handover.sh test/cases/05_handover.sh recorded evidence: not run
A handover is a state record, not a transcript - its required sections must be present and non-empty, a placeholder body is refused, and its identity is computed and refused when written into the body; a transcript pasted into a section is not detected docs/DESIGN.md lib/handover.sh test/cases/05_handover.sh recorded evidence: not run
finish evaluates the finish contract line by line and refuses when any line is unmet docs/CLI.md lib/finish.sh test/cases/06_finish.sh recorded evidence: not run
every refused finish appends a task.refused event naming the outcome claimed, the unmet count and the doctrines that refused, and finish --check writes none share/events.yaml lib/finish.sh test/cases/06_finish.sh recorded evidence: not run
An outcome is a value from a closed vocabulary, not free text

no_match means the work was done and the thing sought does not exist. failed means the work could not be done. They are different facts.

docs/SCHEMAS.md lib/finish.sh test/cases/06_finish.sh recorded evidence: not run
The context a worker is given is assembled from durable state in authority order, and what a profile excludes is excluded

Git first, then task and profile, then blockers, then authored records, then history. The profile's context block decides what is offered; it is the only code that reads those fields.

docs/CONTINUITY.md lib/context.sh test/cases/23_context.sh recorded evidence: not run
The assembled context obeys a line budget, and every section it drops is named with its reason

The count is of the printed document, not of its sections. Git, task, profile and open blockers are never dropped; record bodies degrade to a pointer before they disappear.

share/skeleton/policy.yaml lib/context.sh test/cases/23_context.sh recorded evidence: not run
A checkpoint is a capped progress record with computed identity, and a body over the cap is refused rather than truncated

The cap is what keeps a checkpoint distinct from a handover. Mode 0600, created atomically, never staged.

docs/SCHEMAS.md lib/checkpoint.sh test/cases/20_checkpoint.sh recorded evidence: not run
The right prior record is resolved by worktree and branch, and an unrelated one is never offered

Same worktree and branch, else same branch, else nothing. Ties within one second are broken by ledger order. Absence is reported as absence.

docs/CONTINUITY.md lib/common.sh test/cases/23_context.sh recorded evidence: not run
The ledger is readable back as operational history, filtered by task, event and time

What happened, when, for which task, at which head, and what outcome was accepted. Never what anyone said.

docs/CLI.md lib/history.sh test/cases/22_history.sh recorded evidence: not run
Rotating the ledger archives the oldest lines and never deletes them

Refuses to overwrite an existing archive, and does nothing when the ledger is under its cap.

docs/SCHEMAS.md lib/history.sh test/cases/22_history.sh recorded evidence: not run
A decision is recorded with its reason and its task, and is superseded by a later entry rather than edited

--why is required. --supersedes refuses text matching no recorded decision. Task and head are computed.

docs/SCHEMAS.md lib/decision.sh test/cases/21_decision_question.sh recorded evidence: not run
An unresolved question blocks acceptance, and an entry the gate cannot parse is a failure rather than a silent pass

A gate that cannot read an entry can be bypassed by mistyping one, so check, doctor and watch all fail on a malformed entry.

docs/SCHEMAS.md lib/question.sh test/cases/21_decision_question.sh recorded evidence: not run
A repository-local prompt renders against a closed set of state tokens, and an unknown token is an error

No conditionals, loops, includes or shell. A prompt that silently renders a misspelled token as literal text is worse than one that refuses.

docs/SCHEMAS.md lib/prompt.sh test/cases/24_prompt_search.sh recorded evidence: not run
Durable records are searchable literally, across kinds, without an index

A fixed-string grep in authority order. No ranking, no embedding, no index that could fall out of step with the records it describes.

docs/CLI.md lib/search.sh test/cases/24_prompt_search.sh recorded evidence: not run
Every continuity store is proven reachable through its own command, not merely present on disk

doctor runs the resolver, renders every prompt asset, validates both parsed stores and builds the context. A directory of records that no command reads is the failure this tool exists to catch.

docs/CLI.md lib/doctor.sh test/cases/25_continuity_lifecycle.sh recorded evidence: not run
The context that applies to a path is composed from the layer's scoped documents, root to target, in one deterministic order with provenance, and a tree that does not validate resolves nothing

A document is a Markdown file under .ai/ whose front matter carries schema context/v1; the file name is a convention and the id is the identity. Ordering is depth, then declared order, then path. Providers and audience filter; extend, replace and final compose; a duplicate id, a dangling reference, a superseded final document or a cycle is refused by name.

docs/CLI.md lib/context_docs.sh test/cases/69_context_documents.sh recorded evidence: not run
Every directory of the layer carries a context document, the exemption is declared by the contract that governs the subtree, and a descendant may narrow that requirement but never weaken it

A directory inside the tree with no document is missing-contract, naming the directory and, where an ancestor made the requirement explicit, the contract that did. The exemption is children.require_contract on a subtree document; the value nearest a directory applies, absence means one is owed, and lowering an inherited true is illegal-override. The tree is the manifest's directory minus the half the manifest declares untracked; nothing else is skipped by name, and a subtree the layer carries but does not author is released by children.exempt on the contract that governs it, which may name only directories inside its own scope.

docs/CONTEXT.md lib/context_docs.sh test/cases/69_context_documents.sh recorded evidence: not run
The impact of a change set on the scoped context is reported from git — the documents, the scopes below them, moves with their ancestry, tracked sources to review, and stale projections — and an unrelated change reports nothing

context affected reads the working tree, the index, or a base ref, with renames detected; a tracked source change is a WARN to review and never an exit code, because the tool cannot decide whether prose still describes code. context check-sync is validate, then every projection against its stamp, then affected; test/cases/71_context_sync.sh proves the exit codes.

docs/CLI.md lib/context_docs.sh test/cases/70_context_impact.sh recorded evidence: not run
The repository's AI layer is one directory whose manifest names every section, readable without the tool

init seeds .ai/ from the skeleton and nothing else; no hook, no shell file, no tool installation. doctor fails on a section the manifest names that is absent, and on a manifest key nothing reads.

docs/SCHEMAS.md lib/init.sh test/cases/01_init.sh recorded evidence: not run
A layer installed with --personal is one person's own in a shared repository: it changes no tracked file, no .gitignore line and nothing git status shows, and the command that makes the claim verifies it

The cover is a marked block in the common git directory's info/exclude. init --personal exits 10 unless git status reads the same after it as before, the installer compares it again by itself, update and capture install cover each path before writing it and refuse a tracked one whole, and doctor re-verifies on every run. Whether a layer is personal is read from the block and stored nowhere. init --extend --shared removes the cover and commits nothing.

docs/INSTALL.md lib/common.sh test/cases/2979_a_personal_layer_changes_nothing_shared.sh recorded evidence: not run
Checkout-local state lives under .ai/local/, is ignored by git, and is absent on a fresh clone or worktree

The task record, ledger, decisions, questions, checkpoints and handovers never travel through git. A record that arrives in another checkout by other means names the checkout it belongs to and is enforced nowhere else, which test/cases/27_foreign_task.sh proves.

docs/SCHEMAS.md lib/common.sh test/cases/01_init.sh recorded evidence: not run
The rule baseline is vendored into the repository with a manifest naming every file and its hash, and a hand edit is detected and refused

rules vendor status reports an edited, missing or stray file; rules vendor update refuses over it until --force, and never touches rules/project/. A newer executable reports a newer package and does not apply it.

docs/DOCTRINE.md lib/rules.sh test/cases/67_rule_dag.sh recorded evidence: not run
The effective rule set is resolved as a dependency graph in a deterministic order, and a set that does not resolve is applied by nothing

A missing or deprecated dependency, a cycle, one identity claimed twice, a project rule in the vendor namespace, malformed front matter, an unknown key and an x-majordomus block missing its binding are each refused by name. Every dependency is listed before the rule that depends on it, and two runs agree.

docs/DOCTRINE.md lib/rules.sh test/cases/67_rule_dag.sh recorded evidence: not run
Provider instruction files are thin bootstraps that point at the AI layer and carry no rule of their own

Every projection names .ai/README.md, README.md names AGENTS.md, and doctor fails on a projection that grew a rules section of its own or names a rule id the effective set does not provide (case 899). A person and a worker reach the same layer by the same two hops.

docs/DESIGN.md lib/update.sh test/cases/03_update.sh recorded evidence: not run
A repository on the pre-.ai layout is migrated once, explicitly, with a previewed plan and a verified backup of its local state, and the command is idempotent afterwards

Every other command refuses the legacy layout and names migrate. --dry-run prints the plan and writes nothing. A file the version does not know is reported, never deleted; a destination that already exists refuses the whole run.

docs/CLI.md lib/migrate.sh test/cases/66_migrate_legacy.sh recorded evidence: not run
The tool runs read-only from any location, and two locations of one version agree about one repository

A checkout copy, an unrelated absolute path and a PATH install read the same repository files and write nothing into the distribution, whose hash is the same after every command. The distribution root is derived from the entry point, never from the environment.

docs/DESIGN.md lib/common.sh test/cases/65_tool_root_independence.sh recorded evidence: not run
What the tool knows about itself is written once and derived everywhere else, and a second copy is a test failure

Policy values have no reader-side defaults, claim statuses are declared in the matrix, the site fixtures derive their inputs from the generator, and every command, doctrine and claim is checked against a list read from the dispatch table or the registry rather than written into the test.

docs/CONCEPTS.md lib/doctor.sh test/cases/28_no_hardcoded_values.sh recorded evidence: not run
The public command surface is declared, and reconciled against the dispatcher

The registry is authority for what a command means; bin/majordomus stays authority for what runs. A command dispatched with no entry, and an entry nothing dispatches, both fail. A command is public exactly when the usage text lists it, checked in both directions.

docs/DYNAMICITY.md share/commands.yaml test/cases/30_command_registry.sh recorded evidence: not run
Every public command has a behavioural test and a negative test, computed rather than remembered

The requirement is derived from the registry, so a command added tomorrow is owed coverage from that moment. Coverage is declared in per-case headers rather than inferred, because the negative assertions that use stdin and the surface case's loop are invisible to any scan.

docs/DYNAMICITY.md test/cases/31_command_coverage.sh test/cases/31_command_coverage.sh recorded evidence: not run
The ledger's event vocabulary is closed, on the way in and on the way out

share/events.yaml declares every name, its emitting command and its required payload keys. An unregistered name is refused on write, refused as a history filter, and reported by history --validate when already stored.

docs/SCHEMAS.md lib/common.sh test/cases/33_event_registry.sh recorded evidence: not run
A task record names the checkout it belongs to, and another checkout is never held to its scope

The record is checkout-local and never tracked, so it does not travel through git; one that arrives by other means is recognised by the worktree it names. check, finish --check and watch report a foreign record and enforce nothing from it; finish refuses to write to it. A record from before the field existed is treated as local, so upgrading does not turn an installation red.

docs/SCHEMAS.md lib/check.sh test/cases/27_foreign_task.sh recorded evidence: not run
A blocking question keeps blocking after the work is handed to a new task

The gate reads every unresolved entry in the store rather than only the active task's, so a handover cannot launder a blocker. The store is checkout-local, so the questions a gate can see are the ones asked in this working copy. Only a completed finish is refused; blocked, partial, no_match and failed are honest non-completions and are not. Any task may resolve any question. The alternatives — transferring the question with the handover, or gating on scope overlap — were rejected in M001 with the case each gets wrong.

docs/CONTINUITY.md lib/check.sh test/cases/50_blocker_across_handover.sh recorded evidence: not run
What the context compiler selects, out of what it judged relevant, is counted in tokens of a named tokenizer and recorded, never estimated

The measurement is of selection, not of savings. `majordomus economics measure` records it; the metric carries its own statement of what it is not.

docs/ECONOMICS.md apps/majordomus-cli/src/economics/context.rs test/cases/430_economics.sh recorded evidence: not run
A quantity about tokens, context or cost typed into hand-written prose is refused, and a claim bound to a metric cannot be guaranteed while its evidence does not stand

`majordomus economics check` exits 10 naming every finding; the numbers readers see are generated from recorded runs into docs/generated/economics.* and the site's data.

docs/ECONOMICS.md apps/majordomus-cli/src/economics/claims.rs test/cases/431_economics_claims_gated.sh recorded evidence: not run
Milestones and issues are canonical repository files, and a key nobody reads is an error

The same allowlist rule the policy and the profiles live under. The filename is the id; a record that disagrees with its own filename is refused rather than reconciled.

docs/SCHEMAS.md lib/project.sh test/cases/40_project_model.sh recorded evidence: not run
Issue and milestone status is derived from recorded facts and is stored nowhere

An issue records started_at, verified_at, completed_at, cancelled and its evidence. BLOCKED, READY, ACTIVE, VERIFY, DONE and CANCELLED follow from those and from the state of its dependencies. `status` is not a field, so writing one is an unknown key.

docs/PLANNING.md lib/project.awk test/cases/41_project_status.sh recorded evidence: not run
A cycle, a self-dependency and a dependency on an issue that does not exist are each refused by name

Kahn layering; the issues that never leave the graph are the ones reported. A malformed graph fails doctor and is reported by watch as drift.

docs/PLANNING.md lib/project.awk test/cases/42_dag_waves.sh recorded evidence: not run
Execution waves are computed from the graph, and issues in one wave that touch the same paths are reported as serialised

Sharing a wave is necessary for concurrency, not sufficient. Overlapping declared scope serialises conservatively, because a false serialisation costs time and a false parallel costs a conflict.

docs/PLANNING.md lib/project.awk test/cases/42_dag_waves.sh recorded evidence: not run
An issue cannot be completed while a required piece of evidence is missing

Evidence is a command and its result, or an artifact. Narrative is refused. An issue whose completed_at is set but whose evidence is incomplete derives VERIFY, never DONE.

docs/PLANNING.md lib/plan.sh test/cases/41_project_status.sh recorded evidence: not run
The roadmap is derived from milestone state, and no document may be a second authority for it

The sequence is the topological rank of the milestone graph, so no list of versions exists anywhere. While a hand-written roadmap table survives, the doctrine holds it to the model in both directions; once it is gone the check holds trivially, which is the regression it exists to refuse.

docs/ROADMAP.md lib/doctor.sh test/cases/49_roadmap_doctrine.sh recorded evidence: not run
GitHub milestones and issues are generated from the canonical model, and a hand-edited generated region is reported rather than overwritten

The adapter lives in scripts/ because the tool itself makes no network call, which test/cases/08_no_forbidden_constructs.sh proves. The begin marker carries the hash of the record it was generated from, which is what separates a moved plan from a hand edit; a second marker carries the record's identity, so a title a person rewrites cannot orphan it. This claim is about what the adapter renders and refuses. Whether the remote has actually received it is a different question, and github-projection-gated is the claim that answers it.

docs/PLANNING.md scripts/github-sync test/cases/45_github_projection.sh recorded evidence: not run
A projection that stops being applied turns a build red, and a gate that cannot reach GitHub reports that it cannot rather than passing

The adapter has exited 11 on drift since it was written and nothing called it, so the projection decayed to a tenth of the model over five days with every build green. The gate refuses behind, edited, conflict, unmanaged, state and milestone outright, and ratchets the backlog of unprojected records against .ai/repo/ci/github-drift-baseline.txt, which may fall and may never rise. The one drift it reports without refusing is the drift a head or a young landing made itself, which projected-record-edit-is-landable covers.

docs/PLANNING.md scripts/ci/github-check test/cases/97_github_gate.sh recorded evidence: not run
A pull request that edits a projected plan record is not refused because GitHub does not show the edit yet, and the trunk is refused for it once a declared window has passed

GitHub shows the trunk's plan and nothing else, so a record a pull request edits is behind there until the pull request has merged and the landing has applied it (.github/workflows/github.yml). The gate reports behind, state, milestone and closed as PENDING for the records a head changed since its merge base, and on the trunk for the records that changed in a landing younger than apply.window in .ai/repo/ci/github.yaml. A record has changed when its file or its rendering differs. Drift on any other record is refused as before, and so is every record when the change cannot be measured.

docs/PLANNING.md scripts/ci/github-check test/cases/1038_a_head_is_judged_against_what_it_changes.sh recorded evidence: not run
An intent's stage is derived from the milestones that realise it and each criterion from the evidence ledger, and it is satisfied only while every criterion has a current passing run

Nothing about an intent is stored but its statement. A passing run while a milestone is open meets the criterion and satisfies nothing; closing the milestone satisfies it; changing the test after its run makes the evidence stale and the intent falls back to verifying, and a failing run is not evidence either, with the intent file untouched throughout. `majordomus intent list`, `intent show`, the HTTP API and the MCP tools answer from the same derivation.

docs/PLANNING.md apps/majordomus-cli/src/intent.rs test/cases/367_an_intent_is_satisfied_only_by_evidence.sh recorded evidence: stale
An intent whose criterion names evidence that resolves to nothing is refused by name

`majordomus intent validate` exits 10 with `unresolved_evidence_ref` naming the intent, and the intent-check gate runs it. A milestone or governance entry that resolves to nothing is refused the same way. A reference to nothing would otherwise read as a planned intent forever.

docs/PLANNING.md apps/majordomus-cli/src/intent.rs test/cases/367_an_intent_is_satisfied_only_by_evidence.sh recorded evidence: stale
Once work exists under an intent, a criterion no issue serves, an issue under its milestones that serves nothing and a link to a criterion that does not exist are each refused by name

An issue names the criterion it serves in `serves`; coverage from criterion to issue is derived on every read and `majordomus intent coverage` prints it, with the reason every issue exists. An intent with no work yet is `intent_not_planned`, a warning, because that is where every intent starts.

docs/PLANNING.md apps/majordomus-cli/src/intent_plan.rs test/cases/386_a_plan_is_held_to_its_intent.sh recorded evidence: stale
A gap that leaves a criterion unanswered is refused, and an issue serving an intent that started without a critique or while a blocking finding is open fails intent validation

The refusal is `majordomus intent validate` and the intent-check gate, after the start. `plan start` itself succeeds; refusing the start is intent-refused-at-plan-start, which is planned. Majordomus writes neither record. A worker observes and criticises, and the repository refuses what does not hold together.

docs/PLANNING.md apps/majordomus-cli/src/intent_review.rs test/cases/386_a_plan_is_held_to_its_intent.sh recorded evidence: stale
The work realising an intent is joined across sessions, providers and handovers, and an intent whose milestones are all closed while a criterion's evidence fails is refused naming that criterion

`majordomus intent realization` exits 10 with `closed_work_contradicted` and the intent-realization gate runs it; `intent explain` says why an intent stands where it stands. Every link says how it is known: declared, observed, derived or inferred. Closed work that is merely unproven, and an issue closed while the criterion it serves is unmet, are warnings rather than refusals.

docs/PLANNING.md apps/majordomus-cli/src/intent_realization.rs test/cases/388_an_intent_is_realised_across_providers_and_held_to_reality.sh recorded evidence: stale
Asked before work on an issue, `intent preflight` names the intents the issue serves with the governance that applies, or refuses naming the cause

`majordomus intent preflight --issue <id>` (or `--path`) follows the criteria the issue declares in `serves`, never its milestone alone, and judges each link by the coverage `intent validate` reports from. It answers `serves` (exit 0) when every link holds and each intent served has a critique with no blocking finding open, `maintenance` (exit 0) for an issue that serves nothing under a milestone no live intent names, and `refused` (exit 10) naming its cause: unknown_issue, no_issue_covers_paths, issue_serves_nothing, serves_another_intent, serves_unknown_criterion, intent_not_critiqued or open_blocking_finding. The MCP tool answers the same. `majordomus start` and `plan start` ask the same join through `intents.binding` (intent-in-session-context, intent-refused-at-plan-start).

docs/PLANNING.md apps/majordomus-cli/src/intent.rs apps/majordomus-cli/tests/intent.rs recorded evidence: stale
The Cockpit shows every intent with its derived stage at /cockpit/intents, and one intent's criteria, evidence, serving issues and realising work at /cockpit/intents/<id>

Both pages render the intent-realization capabilities and decide nothing themselves. Each criterion links the test object it names and the issue serving it, the realising work carries its providers and the provenance of its link, and an intent the repository does not hold is a 404.

docs/COCKPIT.md apps/majordomus-cli/src/cockpit/intents.rs apps/majordomus-cli/tests/intent_realization.rs recorded evidence: stale
A task that names the issue or the intent it serves is briefed with what that intent asks of the work, and a handover tells the next worker when the intent moved

`majordomus start --issue <id>` (or `--intent <id>`) asks `intents.binding` what the task serves and records what was named with two pins; `majordomus context` then carries an INTENT section read from the binding on every call — statement, invariants, the served criteria with the state of their evidence, the critique and the gap — and says `unknown` when the binding cannot be read. A handover pins the binding, and `handover --resolve` answers `unchanged`, `evidence_moved` or `plan_changed` (test/cases/961). A task that names nothing is briefed with no intent, and under policy `intent.binding: off` nothing asks. The record holds names and hashes only: no stage, verdict or criterion is stored (ADR 0111).

docs/PLANNING.md lib/intent_binding.sh test/cases/960_a_task_starts_bound_to_what_it_serves.sh recorded evidence: not run
Where the policy requires binding, starting an issue or a task that serves an intent with no critique, or with a blocking finding open, is refused when it is started rather than reported afterwards

Under policy `intent.binding: required`, `majordomus plan start`, the `plan.transition` capability and `majordomus start` each ask `intents.binding` and refuse a refused binding, with the same sentence in both plan engines, writing nothing; an answer that cannot be read refuses too. Under `advisory` a task's refused binding is reported and it starts; with the key absent or `off`, `plan start` succeeds as before and `intent validate` names `executing_without_critique` or `executing_with_open_blocker` afterwards (intent-plan-reviewed). This repository's own policy is `advisory`.

docs/PLANNING.md apps/majordomus-cli/src/intent_binding.rs test/cases/962_plan_start_asks_the_binding_in_both_engines.sh recorded evidence: not run
The Rust executable serves the repository's AI layer to an MCP client over stdio, with protocol frames alone on stdout

One process per client, alive as long as its pipe; diagnostics go to stderr; a session leaves the repository byte-identical. The crate's own suite, tests/mcp_stdio.rs, holds the same frames to the same rules.

docs/MCP.md apps/majordomus-cli/src/mcp/protocol.rs test/cases/72_rust_mcp.sh recorded evidence: not run
What the MCP server serves is decided by the manifest, the declared source classes and each file's front matter, so an object added to the layer is served with no change to the executable

The add, remove and break sequence through the built binary is tests/external_extension.rs in the crate; the shell case proves the layer init writes is served, and that a rule added to it appears.

docs/MCP.md apps/majordomus-cli/src/index.rs test/cases/72_rust_mcp.sh recorded evidence: not run
A file of the layer the MCP server cannot read is excluded with a diagnostic naming its path and the index says it is degraded, never silently smaller

Two files of one kind claiming one identity are both excluded and both named. --strict refuses to serve a degraded layer; --inspect exits 10 on one.

docs/MCP.md apps/majordomus-cli/src/index.rs test/cases/72_rust_mcp.sh recorded evidence: not run
Every capability of the Rust executable is defined once, and the registry refuses a duplicate id or a colliding projection name, naming both parties

Two sources, builtin executables and the layer's objects, meet in one registry built at one place; ids, MCP names and URIs, HTTP routes and CLI paths are each unique or the registry does not build. The crate's tests/registry.rs covers every collision.

docs/CAPABILITIES.md apps/majordomus-cli/src/capability/registry.rs apps/majordomus-cli/tests/registry.rs recorded evidence: not run
MCP, HTTP, OpenAPI, Swagger UI, the capabilities commands and the generated reference are derived from the registry, and a change to one definition reaches every one of them

No projection declares a name, a description, a schema or a route of its own; tests/projections.rs proves every declared projection present and none orphan, and that a changed input type and description reach the MCP schema, the OpenAPI operation and the reference. MCP and HTTP answer the same handler identically.

docs/CAPABILITIES.md apps/majordomus-cli/src/http/openapi.rs test/cases/76_capabilities_projections.sh recorded evidence: not run
How each declarative kind is read and which keys it may carry is data read at run time, and a repository adds a kind with its JSON Schema without a code change

share/kinds.yaml and share/schemas/*.schema.json are the distribution's; .ai/repo/knowledge/kinds.yaml and schemas/ are the repository's; a name declared by both is an error naming both files. The shell tool's allow-lists are generated from the same schemas.

docs/CAPABILITIES.md apps/majordomus-cli/src/metadata/mod.rs test/cases/76_capabilities_projections.sh recorded evidence: not run
Every generated artifact declares the document it projects, the encoding it is written in, the schema its content satisfies and its source, and a structured document is written in every encoding it is committed in from one value

JSON, YAML and Markdown of a document are renderings of one value, so they cannot disagree; each carries a provenance header in the form its encoding allows; the index of the whole set, docs/generated/artifacts.json, is itself generated and is read back by artifacts.list.

docs/CAPABILITIES.md apps/majordomus-cli/src/generate.rs test/cases/52_generated_artifact_typing.sh recorded evidence: not run
The committed projections, the OpenAPI document, the capability reference and the allow-lists, are regenerated from the registry and the schemas, and generate --check refuses a stale one

Byte-identical on regeneration, no timestamps, no absolute paths; --check writes nothing and exits 10 naming every file that differs or is missing. CI runs it.

docs/CAPABILITIES.md apps/majordomus-cli/src/generate.rs test/cases/76_capabilities_projections.sh recorded evidence: not run
One shared server serves a checkout: the first majordomus mcp binds loopback HTTP with Swagger UI and MCP over HTTP beside its stdio session and logs the URL, every later client of that checkout attaches to it, and it ends when the last client leaves

The election is one lease file under .ai/local/state/mcp/, created atomically and removed on exit; a stale lease is taken over; a bridged client whose server dies takes over or re-attaches without its client noticing. The crate's tests/mcp_shared.rs covers the socket side; --standalone keeps one process per client. A checkout, not a repository - a linked worktree is a checkout of its own and elects a server of its own (ADR 0035), which measured on 2026-09-11 meant seven live servers over one git repository. The word was wrong here until ADR 0044; what makes cooperation repository-wide is the gathered board of mcp-peers, not a shared process.

docs/MCP.md apps/majordomus-cli/src/lease.rs test/cases/90_mcp_shared_server.sh recorded evidence: not run
Nothing a client leaves behind locks another client out of the shared server; a corrupt, empty, stale or abandoned lease is taken over and named, a client that cannot share is served alone with the reason logged, and a signal removes the lease before the server dies

The election classifies the lease on every attempt and is bounded by time; commands/mcp.rs degrades to a standalone session when the lease or the server cannot be used; SIGTERM, SIGINT and SIGHUP unlink the lease in the handler. The crate's tests/mcp_shared.rs proves each path over real processes, and case 90 plants a corrupt lease behind the launcher. The doctrine is project.shared-server-resilience.

docs/MCP.md apps/majordomus-cli/src/lease.rs apps/majordomus-cli/tests/mcp_shared.rs recorded evidence: not run
A server whose lease was taken over says so on the endpoint every reader probes, refuses to take on a new client, and reports that what its health describes is another process; the sessions it already had continue and it ends with them

The takeover itself is correct, the stale-executable guard doing its job; what was unowned was the aftermath. The superseded process kept its socket, its peer board and its generation of the layer, and answered GET / with the same name and repository_id as the leaseholder, so every liveness test in the system passed on it. Measured on 2026-09-10 - two servers of one checkout, one lease, and a session whose environment carried the older address reading a board with one peer where the shared board had two. GET / now carries leaseholder (lease::LEASEHOLDER_KEY, read by lease::probe, so writer and reader cannot drift), a new initialize is refused 409 lease_lost naming the launcher, and health.report's server check warns that this process is not the server it describes. A server too old to answer the question is accepted by the probe on purpose - refusing it would take over a live server, which is how one checkout comes to have two. The doctrine is project.shared-server-resilience.

docs/MCP.md apps/majordomus-cli/src/lease.rs apps/majordomus-cli/tests/lease_lost.rs recorded evidence: not run
Every client attached to a checkout's server is a peer named by its own initialize, and majordomus_peers lists every worker of the repository - its own board and the board of every other checkout, each peer stamped with the worktree it is working in - while majordomus_announce tells the others what it is working on and which paths it expects to touch

Two registry capabilities, peers.list (a query) and peers.announce (the one command), projected like every other; each board is one process's memory and is gone with it. peers.list gathers - it enumerates the checkouts git registers, reads its own board from memory and asks every other checkout's server for its own board (checkouts=this, which is what keeps the gather one hop deep), and reports in boards and complete which checkouts it covered and whether it could read them all. Before ADR 0044 it answered one checkout's board while the bootstrap told every worker it had seen the repository; measured on 2026-09-11, nine agents across sixty worktrees each read a board that held only itself. A peer id is a position on one board and is reassigned on reconnect; the checkout a peer carries is the durable half. Announcements are informational and die with the connection; scope is enforced by the shell tool's task record, not here.

docs/MCP.md apps/majordomus-cli/src/peers.rs test/cases/250_the_board_is_repository_wide.sh recorded evidence: not run
An MCP client opened in the repository starts the server itself, through the client configurations at the root and bin/majordomus-mcp, without ever building the executable

.mcp.json (Claude Code), .gemini/settings.json (Gemini CLI) and .codex/config.toml (Codex) name the same launcher, which writes nothing to stdout and passes every argument to majordomus mcp; MAJORDOMUS_BIN and MAJORDOMUS_BUILD_PROFILE say which executable runs, and none is built (ADR 0224): `just build` is the build.

docs/MCP.md bin/majordomus-mcp test/cases/90_mcp_shared_server.sh recorded evidence: not run
A client that starts the MCP server from a checkout whose executable is missing or stale is served by the installed release when it is of this tree's generation and told so, and is otherwise refused with exit 12 naming the explicit build; nothing is built

The generation is the commit the release's RELEASE.json names, which must carry every crate input of the working tree unchanged (mj_rust_same_generation); a version string is not one (ADR 0224, superseding ADR 0172). Every command of the launcher gets the same answer, not mcp alone. A file inside the repository, and one that does not answer --version, is never taken for the installed release. The 2026-10-16 session that began as the only worker of a repository (ADR 0226) began with exit 12 on 4.5GB free; the 2026-10-22 pre-push that timed out at ninety minutes behind the build lock is why the launcher no longer builds at all.

docs/MCP.md bin/majordomus-cli test/cases/3177_a_client_always_gets_a_server.sh recorded evidence: not run
A bare claude in a supervised repository starts with the repository's session-start prompt as its first message, and a run with an argument, outside such a repository, with the opt-out, or with a prompt that cannot be rendered is left exactly as it was

Proved in bash and zsh against stubs of claude and majordomus; the real claude takes the prompt as its positional argument (claude --help). Only Claude Code is wrapped; Codex and Gemini are not claimed until each is proved against the real tool. The installer does not source the file, because it touches nothing outside its prefix.

docs/MCP.md share/shell/session-start.sh test/cases/3221_a_bare_session_start_joins_the_cluster.sh recorded evidence: not run
A tag with a pre-release part is published as a prerelease, recorded on the prerelease channel, and never resolved as the stable release, so an unpinned installer and a node that follows stable keep the highest stable version

Held at three places that must agree. scripts/release-record derives the channel from the tag and refuses stable on a suffixed tag; release.yml passes --prerelease --latest=false for it; Release::is_stable_candidate refuses any version with a pre-release part whatever its record says, and Release::findings reports the mislabel. The resolution and the semver precedence (alpha.2 before alpha.10, build metadata ignored) are proved by the crate's distribution tests, which the case does not run. Found by the audit before v1.0.0-alpha.1, when the pipeline would have published the alpha as the stable release.

docs/DISTRIBUTION.md scripts/release-record test/cases/3179_a_prerelease_is_never_published_as_stable.sh recorded evidence: not run
A majordomus:// URI resolves the same way through the MCP resource read, the majordomus_get tool and the HTTP object route, and majordomus://repository answers repository.info as a JSON document tagged builtin

One function, objects::resolve, is the resolution for every projection; a file of the layer is answered tagged declarative, a query with a resource exposure tagged builtin with its answer as data and the resource text beside it; an unknown URI is not found everywhere; the registry refuses a query read as a resource whose input requires anything. The crate's tests/objects.rs covers every branch at the library level.

docs/MCP.md apps/majordomus-cli/src/capability/builtin/objects.rs apps/majordomus-cli/tests/http_serve.rs recorded evidence: not run
Every gate the Rust executable must pass is one script, scripts/rust-check, and CI runs that script on every change that can reach the crate

Format, lints with warnings as errors (a public item without documentation is one), the suites with the doc examples, rustdoc with warnings denied, the benchmarks building, the registry's own validation, the committed projections in sync, the benchmark coverage complete, and line coverage above the committed floor; --ci adds the benchmark check against the platform's baseline and leaves coverage to its own job, --integration runs the registry checks alone for a change outside the crate. The rust job of the workflow runs the script itself with the mode the plan chose, so there is no second list to fall out of order; the case reads the script, the workflow and the justfile and fails when they disagree; with a toolchain present it also runs the doc examples and builds the benchmarks.

.ai/repo/rules/project/rust-cli-evidence.v1.md scripts/rust-check test/cases/77_rust_evidence.sh recorded evidence: not run
Coverage of the Rust crate is measured with test code out of the denominator, on every change that can affect the crate and on every push to master, and the build fails under the floor in scripts/rust-coverage-threshold or under the session/continuity domain's own floor in scripts/session-coverage-threshold

cargo-llvm-cov over all targets, exported as JSON and post-processed so that tests/, benches/, examples/ and every inline `#[cfg(test)]` item leave the fraction on both sides. --ignore-filename-regex cannot do the second, which is why this is a script and not a flag. Two floors, each one integer in a file of its own, each read by CI, by scripts/rust-check and by just coverage, and each a ratchet — the coverage measured when it was set, which may rise and never fall; the crate floor's bound is FLOOR_BOUND in scripts/ci/rust-command-check (rule project.rust-command-tested-in-file) and case 77 reads it there. The domain the second measures is declared in scripts/session-coverage-domain and the script refuses a domain naming a file the export does not carry, so narrowing it to raise the number is a visible edit. Branch coverage proper needs a nightly toolchain; region coverage stands in for it and is reported under that name. The coverage job runs when the plan selects the rust-coverage gate, which the rust and share classes of .ai/repo/ci/gates.yaml select and every full plan does.

.ai/repo/rules/project/rust-cli-evidence.v1.md scripts/rust-coverage test/cases/77_rust_evidence.sh recorded evidence: not run
The paths of the Rust executable that scale with the repository carry criterion benchmarks that build on every push, and every executable capability declares its benchmark policy, required or waived for a typed reason

The YAML subset and front matter, glob matching, the index, the registry, the OpenAPI document, one MCP listing and one capability call (benches/projections.rs); the router, the bridge and the shared server (benches/shared.rs). Numbers are reported, not asserted; no latency budget is promised until one is measured on CI. The capability! macro does not compile an executable without benchmark cases, the registry counts the required and the waived, and the case reads the policy of every executable back through capabilities list.

.ai/repo/rules/project/rust-cli-evidence.v1.md apps/majordomus-cli/benches/projections.rs test/cases/77_rust_evidence.sh recorded evidence: not run
A capability of the Rust executable is declared once and composed into its module, the root composes modules, and the registry refuses a capability outside its module's namespace

capability!, module! and compose_modules! build plain values handed to the registry builder; adding a capability to an existing module touches that module's file alone, and MCP, HTTP, OpenAPI, Swagger UI, the CLI, the benchmark targets, the cache and the generated reference follow. The crate's tests/registry.rs holds the invariants, tests/projections.rs the propagation.

docs/CAPABILITIES.md apps/majordomus-cli/src/capability/module.rs apps/majordomus-cli/tests/registry.rs recorded evidence: not run
Every externally callable operation of the Rust executable is a benchmark target with a denominator generated from the registry, and a missing case fails the structural check

Each required executable is a target directly and on every transport its exposure declares, with the cases its input type provides; the transports' own operations are targets declared once; waivers are typed and never counted. Runners time the executor, a real loopback socket and a real majordomus mcp child; results are majordomus/benchmark-result/v1; the baseline is per platform under .ai/repo/benchmarks/rust/ and the policy is data.

docs/CAPABILITIES.md apps/majordomus-cli/src/bench/coverage.rs apps/majordomus-cli/tests/bench_units.rs recorded evidence: not run
After startup, no MCP or HTTP request of the Rust executable scans the repository, builds the index or the registry, derives a schema or builds a projection, and perf.counters proves it

Process-wide counters, each incremented at one named place, answered as perf.counters over every transport; tests/hot_path.rs sends hundreds of requests through the real transports and requires the startup counters unchanged; the MCP listings and the OpenAPI document are prepared once.

docs/CAPABILITIES.md apps/majordomus-cli/src/perf.rs test/cases/91_canonical_architecture.sh recorded evidence: not run
A cached capability of the Rust executable answers the same value uncached, cold and warm, a hit runs no handler, errors and commands are never cached, and the key carries the registry fingerprint

One executor for every transport; CachePolicy on the descriptor; bounded per capability; the key is the canonical id, the input in canonical form and a sha-256 fingerprint of the repository content and the descriptors. tests/executor.rs and the proptest harness in tests/properties.rs iterate every cached capability with every case and generated inputs.

docs/CAPABILITIES.md apps/majordomus-cli/src/capability/executor.rs apps/majordomus-cli/tests/executor.rs recorded evidence: not run
The provider bootstraps the policy declares (AGENTS.md, CLAUDE.md, GEMINI.md, ...) are a target of majordomus generate, rendered from the policy and the provider templates byte for byte as the shell tool renders them, and generate --check refuses a hand-edited or stale one in CI

One renderer, two entry points that agree on every stamp; the policy is the only input (its hash and the profiles' is the stamp's first hash). A target outside the repository, a provider without a template, a token the policy cannot fill, and an always_loaded target over the budget are refused with the reason, and nothing is written. Region mode keeps the host document outside the markers. apps/majordomus-cli/tests/providers_projection.rs covers the fixture side; CI runs generate --check on every push.

docs/CAPABILITIES.md apps/majordomus-cli/src/providers.rs test/cases/93_rust_provider_projections.sh recorded evidence: not run
The site's Registry page is rendered from site/data/registry/registry.json, a dataset majordomus generate site derives from the registry and the index with their fingerprints, and generate --check refuses a stale one in CI

Every builtin descriptor in full with the file it was composed in, every module with its ids, every object of the layer without its content, every kind with its schema, the declared provider projections, the command line as clap declares it, the MCP tools and resources, the HTTP routes, the benchmark targets, coverage, policy and accepted baselines, and the registry and index fingerprints (majordomus-site-registry/v2); no timestamps of its own, no absolute paths, no git state. The directory is the Rust executable's alone, the shell site generator owns site/data/generated/ wholesale, so no directory has two writers. A canonical object added to the layer changes the fingerprint and the dataset and leaves the bootstraps alone; an exposure removed from a descriptor moves the routes and the coverage and nothing else; the test follows each mutation to exactly the projection it concerns.

docs/CAPABILITIES.md apps/majordomus-cli/src/site.rs apps/majordomus-cli/tests/projections.rs recorded evidence: not run
A change runs the gates its paths can affect, decided by one model of what affects what, and a change to the pipeline itself or a path the model does not know runs every gate

The gates that always run are in every plan; a changed path selects the gates of every class it matches, as a union; a class may escalate; a gate brings what it implies and what it requires; the plan is deterministic and says for every gate why it ran or did not. The workflow reads the plan's outputs and carries no path list of its own; test/cases/26_ci_wiring.sh proves every gate's job exists, is gated on the plan and is needed by the verdict.

.ai/repo/ci/gates.yaml scripts/ci-plan test/cases/94_ci_plan.sh recorded evidence: not run
The verdict status is green only when planning succeeded and every gate the plan selected ran in a job that succeeded; a failed or cancelled job, a selected gate whose job was skipped, or a plan that selected nothing is red

The verdict job always runs, so the status is never left pending by a job the plan skipped, and it writes a table of every gate with its job's result and the reason it was or was not planned. The case drives it with fixture plans and needs contexts through every red condition. This claim was worded "the one status a branch rule requires" until 2026-09-11, which asserted a branch rule that does not exist - see ci-verdict-is-required below. What scripts/ci/verdict computes was always true; what gated a merge on it was not.

docs/CI.md scripts/ci/verdict test/cases/94_ci_plan.sh recorded evidence: not run
The behavioural suite runs a bounded number of cases at a time with the serial runner's semantics, a case that writes into the checkout declares itself exclusive and runs alone, and the parallel phase fails naming the paths when the checkout changed under it

MJ_TEST_JOBS bounds the pool (xargs -P, never a bare ampersand); each case has its own log; the verdicts are rendered in name order with a failing case's whole log before its line; a filter that matches nothing and an empty case directory are usage errors in both modes; MJ_TEST_REPORT writes one row per case for the summary; test/lib.sh refuses a case that does not stand in the fixture test/run.sh made for it, so a case started by hand cannot write its fixture into a checkout. test/cases/26_ci_wiring.sh proves the failure semantics through both modes on a private harness.

docs/CI.md test/run.sh test/cases/94_ci_plan.sh recorded evidence: not run
The Rust executable the rust gate built is published as an artifact with its provenance, and every Rust case drives the executable MAJORDOMUS_BIN names instead of building one

scripts/rust-check --artifact DIR copies the debug executable beside a majordomus-cli.json naming the commit, the target triple, the toolchain and the Cargo.lock digest; the workflow uploads it as majordomus-cli-<target>. rust_bin in test/lib.sh hands a case MAJORDOMUS_BIN when set, refuses one that is not executable, builds once with cargo when unset, and asks the case to skip when there is neither; the launcher bin/majordomus-mcp honours the same variable.

docs/CI.md scripts/rust-check test/cases/401_the_rust_artifact_carries_its_provenance.sh recorded evidence: not run
Publication is gated on everything that can make the published site wrong and waits for nothing else; the checks that decide whether a change may merge run beside it on the same commit, not in front of it

The publication path proves the committed derived data current by its input hash, builds the site from it, runs every static check over the output and pushes gh-pages, in one job triggered directly by a master push on paths derived from the gate model; superseded runs are cancelled; the suite, the crate gates, coverage, the macOS suite, the benchmark check and the browser probe run in validate.yml on the same commit and gate merging. The case proves the trigger, the derived paths, the cancellation, the permissions, that no heavy gate is on the path and none left validate.yml, and that a build which skips the generation is byte for byte one that does not.

docs/GITHUB_PAGES_PERFORMANCE.md .github/workflows/pages.yml test/cases/97_pages_fast_path.sh recorded evidence: not run
The OpenAPI document is inferred, the tags are the modules, the examples are the benchmark cases, the responses are the router's statuses by kind, the prose is the one text every projection shares, and the site's API reference is rendered from the committed document

Tags from registry.modules(), examples from the capabilities' BenchmarkCases against the repository's index, responses from one status table keyed by kind, info from about.rs and the crate manifest; benches/routes.rs times every route with the same cases; the site projects the committed document, renders /docs/api/ and serves /openapi.json raw, and site-check refuses a build without them.

docs/CAPABILITIES.md apps/majordomus-cli/src/http/openapi.rs test/cases/92_openapi_reference.sh recorded evidence: not run
What a worker reads of the repository is declared once in .ai/repo/scope.yaml, out wins over in, and the Rust executable discovers, indexes and serves nothing outside it

The declaration is typed and schema-validated (share/schemas/majordomus/scope/scope.v1.schema.json; share/allow/scope.txt is generated from it for the shell tool); a repository declaring none is read under the distribution's default and says so; a discovered file outside the scope is an out_of_scope diagnostic naming the rule; a tracked secret is a tracked_secret warning; repository.scope and repository.scope_classify are projected to MCP, HTTP and `majordomus scope`. The crate's tests/scope.rs covers every branch at the library, socket and process level. The doctrine is project.scope-is-declared.

docs/SCOPE.md apps/majordomus-cli/src/scope.rs test/cases/93_scope_policy.sh recorded evidence: not run
Every public command is named and run by an active use case, a guaranteed claim or an MCP tool without one is a named gap, and the policy says which gaps fail doctor, check and finish

The tally is computed from the command registry, docs/CLAIMS.yaml, the executable's registry and the use cases under .ai/repo/use-cases/; a draft never counts. The doctrine is majordomus.use-case-coverage, gated by policy use_cases.coverage and the finish key use_cases_covered.

docs/USE_CASES.md lib/usecase.sh test/cases/94_use_cases.sh recorded evidence: not run
A use case's scenario executes against the real tool in a disposable repository, every step's exit code and output are asserted, and the normalised evidence is what the site shows

majordomus usecase run writes .ai/local/evidence/use-cases/<id>.json; scripts/generate-site-data runs the same scenarios and embeds the evidence into site/data/generated/catalogue.json, so --check proves the pages show what the tool did. Paths, timestamps, ids, hashes and durations are normalised; nothing behavioural is.

docs/USE_CASES.md lib/usecase.sh test/cases/94_use_cases.sh recorded evidence: not run
From the files a change touched, the tool names the commands, rules, use cases, scenarios and behavioural cases affected, and scaffolds a draft for a capability no use case covers

usecase impact maps paths through docs/RESPONSIBILITIES.yaml, the dispatch table, the rules' identities and the scenarios' fixtures; usecase scaffold writes drafts from the registry, the command fixture and the claims, and never a guarantee.

docs/USE_CASES.md lib/usecase.sh test/cases/94_use_cases.sh recorded evidence: not run
A live scenario may only run commands share/commands.yaml declares read-only, names no setup, and asserts an obligation only where something owes one; every other shape is refused by usecase validate with exit 10

The class is read from share/commands.yaml, the declaration the reference semantics already come from, so safety is a property of the declaration and not of the author's care (ADR 38). An obligation step is refused in a fixture scenario, because a disposable repository owes nothing, and a step that both runs a command and asserts an obligation is refused as neither.

docs/USE_CASES.md lib/usecase.sh test/cases/359_live_scenarios.sh recorded evidence: not run
A live scenario is not selected by a bare usecase run, its evidence is written under the ignored local half, and it never raises a command to covered; an undischarged obligation is reported unmet, never failed

A live result describes one tree on one machine at one minute, so it is written to .ai/local/evidence/live/<id>.json rather than beside the committed fixture evidence, and coverage counts it as naming a command and never as covering it. The run separates the two verdicts, because a failing fixture scenario is a defect in the tool and an unmet obligation is work not done (ADR 38).

docs/USE_CASES.md lib/usecase.sh test/cases/359_live_scenarios.sh recorded evidence: not run
The site is deployed by one script, scripts/site-deploy, from a terminal or from the publication workflow; it refuses a dirty tree, a commit master does not contain and a build that is not HEAD's, pushes site/public to gh-pages with the source commit named, and pushes nothing when the output is unchanged

The case runs the whole path against a local bare remote, no network; the footer of every page names the commit, so a deploy is verifiable with curl. The workflow calls the same script with --skip-build after its gate. Pointing GitHub Pages at the branch is a one-time --configure-pages. The hand procedure is .ai/repo/skills/deploy-site/SKILL.md.

docs/GITHUB_PAGES_ARCHITECTURE.md scripts/site-deploy test/cases/96_site_deploy.sh recorded evidence: not run
A commit whose derived data is behind its canonical inputs is refused before it exists, and a repository that unwires that gate is a doctor failure

The generator records the input hash in site/data/generated/source.json, so `pages current` answers "must this be regenerated?" in a fifth of a second without generating anything, which is what lets it run on every commit rather than only in CI. It is declared in the policy's enforcement list as derived-current, wired by git-hook:pre-commit, and doctor verifies that wiring the way it verifies the others - so the gate refuses the stale commit and the doctrine refuses the repository that removes the gate. Before this, staleness surfaced only after a merge, and the person it stopped was never the person who caused it.

docs/GITHUB_PAGES_ARCHITECTURE.md scripts/pages test/cases/56_derived_current_gate.sh recorded evidence: not run
The site's pages about the Rust executable (one per module, one per capability, the command line, the MCP surface, the benchmarks, the executable's own narrative) are derived from the registry manifest and the registry dataset the executable generates, nothing names a capability by hand, and a capability that joins or leaves the registry gains or loses its page, its index entries and its links from the generators alone

scripts/generate-site-data reads docs/generated/registry.json — a code-only projection, so no cycle runs through the index — for the ids it turns into routes, and writes site/data/generated/executable.json (routes, module and API links, source on GitHub, the claims attached to each surface by the path of their implementation) and the content stubs under site/content/registry/; the templates read every other fact from site/data/registry/registry.json. A manifest naming a module the registry does not hold, a source file the tree does not have, or another schema is refused and nothing is published. scripts/site-check proves the rendered side, every module and capability a page and no page without its entry, the CLI, MCP and benchmark pages rendering every command, tool, target and baseline, every capability page linking its module and its operation, and no template, generator or navigation file naming a capability. The manifest is among the site's inputs, so its hash moves with the executable's code.

docs/GITHUB_PAGES_ARCHITECTURE.md scripts/lib/executable-site.jq test/cases/95_executable_reference.sh recorded evidence: not run
Every committed derived artifact of the repository is regenerated by one command in dependency order and checked by one read-only command that names every stale artifact, a second regeneration on a clean tree changes nothing, and no derived file records the commit it lands in

scripts/derive runs majordomus generate (the code-only projections), scripts/generate-site-data (the site's data and content and the derived documents, reading the registry manifest and the OpenAPI document), and majordomus generate again (the site dataset and the benchmark matrix over the index the documents joined); no stage reads its own output. scripts/derive-check composes generate --check and generate-site-data --check and exits 10 with both reports. The commit a page was built from is a fact of the build (site/data/build.json, served at /build.json, never committed), so source.json no longer carries it and the generator is idempotent on a clean tree. just derive, just derive-check and the Pages workflow call the scripts; just test runs the check.

docs/GITHUB_PAGES_ARCHITECTURE.md scripts/derive test/cases/400_derivation_is_one_graph.sh recorded evidence: not run
A skill is one directory under the layer's skills section holding SKILL.md, discovered by the source class `skill` the shell tool and the Rust executable both read, so that adding the file is the whole registration and the catalogue, the command, doctor, MCP and the site all derive from it

share/kinds.yaml declares the kind, share/schemas/majordomus/skill/skill.v1.schema.json its contract, and the source classes skill and skill_example in .ai/repo/knowledge/sources.yaml which files carry it. Nothing else names a skill. lib/skills.sh derives one catalogue from that discovery for `skills list|show|check`, the doctrine validator and scripts/generate-site-data; the Rust index serves the same files as majordomus://skill/<id> resources, through majordomus_list and majordomus_get, with no code that knows the kind. The case adds, breaks, renames and removes a skill and watches every surface follow.

docs/SCHEMAS.md lib/skills.sh test/cases/95_skills.sh recorded evidence: not run
A task that declares obligations reaches the outcome completed only when each one has evidence recorded against it, and evidence taken over files that have since changed, or at a commit the branch has since left, no longer discharges anything

The obligation vocabulary is share/obligations.yaml, so a token is added there and not in shell. Evidence is a task.evidence ledger line carrying the hash of the tracked files the obligation names; the validator recomputes that hash and reports both values when they differ, the way the site's own currency check already does. An obligation whose fact is remote is bound to the commit it was taken at instead, and judged with mj_git_label rather than a fifth staleness vocabulary. An outcome other than completed is never refused for owing something.

docs/CLI.md lib/evidence.sh test/cases/103_obligations.sh recorded evidence: not run
An obligation whose fact the tool can hold is established live rather than recorded — a clean tree, a remote-tracking ref that reaches the head, a trunk that reaches it, a published site that serves it — and a hand-recorded line neither discharges it nor rescues it

share/obligations.yaml gives every token an established_by, and one of `none` obliges it to say in `unestablished` why nothing here can settle it. commit, push and target are settled from git without fetching, so a stale tracking ref can only say "not yet" and never "yes"; pages is settled by scripts/pages verify with --timeout 0, the single probe of the publication check the Pages workflow already runs, which now exits 12 rather than 10 when the site never answered — unreachable and unpublished are different findings. A fact established this way is taken at HEAD, which is mj_git_label's `exact`; the other three labels keep their meaning on the fallback path, and deploy and verify stay hand-recorded because a deployment is a fact about a machine this repository never contacts.

docs/CLI.md lib/evidence.sh test/cases/103_obligations.sh recorded evidence: not run
Every skill is validated against the allow-list generated from its schema, its directory, its sections and its references, a violation names the file and every reason, and a repository with no skills is reported rather than passed

`skills check` and the doctrine majordomus.skill-integrity (dispatched by doctor and watch) run one examination — unknown keys, schema version, integer version, closed status set, id equal to the directory, non-empty Purpose, Procedure and Output sections, duplicate ids, descriptions that do not tell two skills apart, related ids that resolve, examples with a heading — and print the counts of what they examined. The site generator refuses to build from a skill that does not validate.

docs/CLI.md lib/skills.sh test/cases/95_skills.sh recorded evidence: not run
The website's skills section is rendered from the skills catalogue, one page per skill carrying the skill's own body and examples, and a removed or renamed skill leaves no page, entry or link behind

site/data/generated/skills.json and site/content/skills/ are written by the generator from lib/skills.sh's catalogue, the content directory replaced whole on every run; generate-site-data --check exits 10 when a skill changed or the projection was edited by hand, and CI runs that check before the site builds.

docs/GITHUB_PAGES_ARCHITECTURE.md scripts/generate-site-data test/cases/95_skills.sh recorded evidence: not run
An operational moment is one file under the layer, discovered rather than registered, and answered by the command line, the HTTP API, the OpenAPI document, MCP, the derived graph and the website with nothing else changed

Removing the file removes it from all of them, which is what a hidden registry would fail.

docs/WHY.md apps/majordomus-cli/src/why.rs apps/majordomus-cli/tests/why.rs recorded evidence: not run
Every reference a moment makes resolves against the thing it names, and one that does not is an error carrying the nearest candidate

Audiences, areas and related moments against the catalogue; commands, capabilities, claims, rules and use cases against their own registries.

docs/WHY.md apps/majordomus-cli/src/why.rs apps/majordomus-cli/tests/why.rs recorded evidence: not run
A diagnosis of a reader's own symptoms is deterministic counting over the catalogue's metadata, and every recommendation names the moments that produced it

No weighting and no percentage; the questionnaire on the website and `majordomus why diagnose` run the same arithmetic over the same data.

docs/WHY.md apps/majordomus-cli/src/why.rs apps/majordomus-cli/tests/why.rs recorded evidence: not run
A closed execution episode is a shared object of the layer, written by the tool from git and the ledger, valid against a contract that admits no conversation and no absolute path, and discovered rather than registered

The record lives under the manifest's sessions section. Every field is derived - times from the clock, heads and commits from git, the reference lists from the ledger's events for the episode - and the body is the only authored part. The repository is named by its remote and the working copy by a hash, so the resolver can tell one checkout's records from another's without disclosing a path. One source class discovers them; the index, MCP, the object routes, the graph and the site follow.

docs/SCHEMAS.md lib/session.sh test/cases/63_session_records.sh recorded evidence: not run
A provider hook opens the episode below the model and hands the worker what the last one left, records what a compaction is about to discard, and closes the episode with a continuation record beside its envelope

The provider's own SessionStart, PreCompact and SessionEnd events draw the boundary and drive the lifecycle; each is idempotent because the events are, and doctor proves the wiring by driving a payload through the end shim with the mutation disabled. The start event writes the policy's briefing to stdout, which is how a continuation record reaches a worker who did not ask for one; the other two write none, because they fire inside a turn already under way. The working context each open freezes is local, appended to rather than rewritten, and refused if it carries a conversation.

docs/DOCTRINE.md lib/session_context.sh test/cases/54_session_lifecycle_hooks.sh recorded evidence: not run
The session subsystem has a reading of itself — every open episode of the store and not only the one the pointer follows, the episodes that can no longer close themselves, the commit the serving process is answering about, and what each provider's adapter declares

continuity.state answers the worker's question about one episode, which is right for a briefing and blind to the other windows open on the same worktree, because state/session-current.yaml is a symlink the most recent start event re-aims. The five lifecycle capabilities read the store, the ledger and the index instead, decide every standing from what the repository can observe, and carry a remedy with every finding. They never write, they judge no thresholds — age is session.freshness and continuity.state's to report — and they declare no command line, because they read .ai/local/ and are served rather than shipped. The Cockpit's Continuity page is their projection and holds no session model of its own.

docs/CONTINUITY.md apps/majordomus-cli/src/capability/builtin/lifecycle.rs test/cases/171_session_observability.sh recorded evidence: not run
A client with no provider hooks gets an execution episode from the MCP connection, resumes its own after a crash, and never has its prompt claimed to be captured

The episode opens on episodes.attach and never on initialize, so a client that only reads the layer leaves no record; it is keyed by the external identity the client supplies and never by a peer id, which is handed out per connection; every message the client sends is its heartbeat; losing the connection detaches rather than closes, and a reconnect under the same identity resumes the same episode. Nothing here writes a session record - the board runs `capture session --provider generic`, the same command a provider hook's shim runs, and reports what it said. Prompt capture is declared impossible for the connection and is not attempted: an MCP server is handed tool calls, never the prompt that produced them. ADR 0103.

docs/MCP.md apps/majordomus-cli/src/episodes.rs test/cases/200_generic_mcp_episode.sh recorded evidence: not run
What each provider can do about the session lifecycle, prompt capture, driving a browser beside this server and reporting what a session consumed is one declaration with a citation per cell, every surface projects it, and no provider the declaration names goes unreported

share/providers.yaml carries an offers block per provider - episode (hooks, connection, none), prompts (hook, none), browser (bridge, none; a bridge names its native messaging host and the person's setup step, which preflight's integration.browser reports when this machine lacks the host), and the citation each was verified from; a word outside either vocabulary fails the read of the file rather than degrading to none. share::ProviderDeclaration and product::ProductProvider carry it and every surface is a projection. `capture status` joins it with the tree and reports two facts per aspect, the vendor's capability and this checkout's state, over the union of the declared providers and the adapter tables; `unsupported` is reserved for a provider that cannot do the thing and `unadapted` names a gap in this distribution. scripts/provider-evidence-check is the gate and the case holds it to its own negative. ADR 0103, rule project.a-provider-capability-cites-its-evidence.

docs/CONCEPTS.md share/providers.yaml test/cases/200_generic_mcp_episode.sh recorded evidence: not run
An architecture decision is one file under the layer's adrs section, validated against the decision contract, with an identity nothing else claims, reciprocal supersession, and every reference it makes resolving

share/kinds.yaml declares the kind, share/schemas/majordomus/adr/adr.v1.schema.json its contract (share/allow/adr.txt is generated from it), and the source class adr in .ai/repo/knowledge/sources.yaml says which files carry it; nothing else registers a decision. lib/adr.sh derives one catalogue from that discovery for `adr list|show|check` and for mj_validate_adr, the validator of majordomus.adr-integrity dispatched from doctor and watch. The case exercises duplicate identities, an unknown status, a schema version nothing reads, one-sided supersession, a superseded record with no replacement and a missing body section.

docs/SCHEMAS.md lib/adr.sh test/cases/99_adr.sh recorded evidence: not run
A decision states what it put in force as typed references, each one validated, and the reverse direction — what a rule, a document, an implementation or a case was decided by — is read from the knowledge graph rather than written down a second time

The forward edge is `related` in the decision's front matter - rule:<id>, claim:<id>, file:<path>, test:<path>. Validation refuses a rule the effective set does not have, a claim the matrix does not have and a path the repository does not contain. The extractor turns each into a graph edge (declares, supports, references, tested_by) whose provenance is the front-matter key that stated it, so `knowledge edges` answers both directions from one authored fact.

docs/SCHEMAS.md lib/adr.sh test/cases/99_adr.sh recorded evidence: not run
majordomus adr propose writes a decision with status proposed and refuses to write any other status, allocates its identity under a lock so concurrent workers never collide, and records what the decision was derived from

Acceptance is the person's act — a tool that can write `accepted` turns its own inference into repository truth and a later reader cannot tell which, so propose has no flag for it and an extracted record claiming it is a finding. An extracted record must name its evidence in provenance.derived_from, whose references are typed and whose file and test references must resolve; it may still be superseded, because that is something a later record did to it. The identity is allocated under a lock over the section directory, and the case proves it with real concurrent processes, because this repository shipped two 0005s and two 0007s before anything checked.

docs/SCHEMAS.md lib/adr.sh test/cases/99_adr.sh recorded evidence: not run
Every command of the Rust executable's command line carries a summary, help on every argument and, where it runs, at least one example, the root carries a long description, and every example shown to a reader is executed against the built executable

The documentation and the examples are declared beside the clap declaration, as typed Rust; an example is an argument vector and the command line a reader sees is composed from it. capabilities validate exits 10 naming every violation with the command, the file and the rule; the suite parses every documented vector against the canonical parser and runs every example against the compiled binary in a disposable repository, the two long-running commands as bounded probes. An example the suite deliberately does not run carries its reason and shows it wherever the example is shown.

.ai/repo/rules/project/rust-cli-evidence.v1.md apps/majordomus-cli/src/cli.rs apps/majordomus-cli/tests/cli_examples.rs recorded evidence: not run
Every web surface is declared once at its producer and resolved into one topology, from which the router, the landing page and the machine-readable index are each projected

A surface is a mount, a producer, a kind, a category, a visibility and the provenance of each; the routes the executable answers come from the capability declarations that already exist, a generated directory declares itself beside its output, and the application's site is read from the site generator's configuration. There is no central list. The case asks the page and the index independently and requires the same set of public mounts, so a link written into the page by hand fails it.

.ai/repo/adrs/0013-every-web-surface-is-discovered-from-its-producer-resolved-o.md apps/majordomus-cli/src/web/model.rs test/cases/89_web_surface.sh recorded evidence: not run
/docs serves this repository's documentation and /swagger serves the Swagger UI, and neither may take the other's mount

The Swagger UI held /docs until documentation arrived and wanted the name it had already been given. The case asserts the mounts as data before asserting them as routes, refuses a Swagger producer at /docs, and proves the documentation mount does not swallow /swagger or /openapi.json once it is built. Unbuilt documentation answers 503 naming the command that builds it rather than the viewer that used to answer there.

.ai/repo/rules/project/web-surface-declared-once.v1.md apps/majordomus-cli/src/web/discover.rs test/cases/89_web_surface.sh recorded evidence: not run
The crate's rustdoc is a web surface at /rustdoc declared by discovery from the crate, present in the topology, its committed projection and the home page whether or not its producer has run, and a running server answers /rustdoc/ from the tree its producer wrote

The surface is declared from the crate the way the served documentation is declared from the site configuration, so nothing about it depends on one checkout having built it; built_from comes from the producer's surface.json and is left out of docs/generated/web.json, which must read the same in every clone. The Rust side is the web::discover tests. ADR 0086 decides the mount and why it is not under /docs.

docs/RUSTDOC.md apps/majordomus-cli/src/web/discover.rs test/cases/488_rustdoc_discoverable.sh recorded evidence: not run
The site build composes every published static surface into its output at that surface's mount, read from the committed topology with no surface named by hand, and refuses, naming the producer, when a published surface's artifact is absent

The list is docs/generated/web.json, read through mj_web_composed in lib/common.sh, which every check that must tell the site's pages from a composed surface's asks too. A mount the site itself already fills is refused as a second owner of one path rather than overwritten. The composition runs after Zola and after the site's accessibility pass, so the site's normalisation never touches another producer's pages.

docs/RUSTDOC.md scripts/site-build test/cases/486_rustdoc_composition.sh recorded evidence: not run
The crate's rustdoc is published at /rustdoc with the site, built from the commit the site names, and is never committed

The case deploys a composed site to a local bare remote, no network, and reads the published branch back. The site's /build.json records every composed surface with the commit its producer declared, so the commit the reference was built from is readable from outside beside the commit the site was built from. The tree is produced in CI by the rustdoc job of the pages workflow through .github/actions/rustdoc, the action the validation workflow uses too.

docs/RUSTDOC.md scripts/site-deploy test/cases/489_rustdoc_deploy.sh recorded evidence: not run
Every item the crate exports has its rustdoc page at the route derived from its path and kind, no item page exists without its item, and a module added to the crate becomes a required page with no list edited

majordomus quality rustdoc joins the rendered tree with quality::source::Inventory, the crate's own syntax-tree measurement of what it exports, which is also what the rust-quality gate measures. No inventory of modules or items is kept for the reference. The check exits 10 naming each missing or orphan page and 12 when there is no tree, never a pass over nothing. The crate's quality::rustdoc tests hold one fixture per way of failing.

docs/RUSTDOC.md apps/majordomus-cli/src/quality/rustdoc.rs test/cases/490_rustdoc_new_module.sh recorded evidence: not run
A rustdoc tree built from another commit than the one it is checked or deployed as is refused, by the integrity check before publication and by the public verification after it

Two witnesses that do not share a writer name the commit, the producer's built_from and the value rustdoc renders on the crate's own COMMIT constant page. scripts/pages verify-rustdoc is a hard step of the pages workflow once the site serves the deployed commit, so a deployment whose public reference is absent, broken or stale fails its run, and scripts/ci/pages-check asks again as the pages-live gate.

docs/RUSTDOC.md scripts/pages test/cases/487_rustdoc_staleness.sh recorded evidence: not run
Every builtin module's page on the site links the page the crate's reference documents that module on, from the one module-to-page mapping the rustdoc check holds the published tree to, and only in a build that composed the reference

The mapping is quality::rustdoc::module_routes, the crate's exported modules each with the page derived from the crate's own inventory, which majordomus quality rustdoc requires of the tree as a missing-page finding. majordomus generate carries it into the site's registry dataset as rustdoc on each module, and a module rustdoc gives no page (pub(crate) or private) carries none. The template joins it with the surfaces the build composed, from data/build.json, which scripts/site-build writes from docs/generated/web.json; it derives no route, and a build without the reference links nothing. scripts/ci/link-check resolves every such link against the composed site.

docs/RUSTDOC.md apps/majordomus-cli/src/site.rs test/cases/492_rustdoc_module_links.sh recorded evidence: not run
A branch's worktree path is derived from git identity and the branch name alone — the primary checkout's sibling named with -wt, then the branch with its hierarchy kept — and is the same answer from every directory of every worktree, with nothing registered or configured

The identity is the common git directory and the main worktree git lists first, never the current directory, so a linked worktree does not derive a container inside itself. The branch name is validated by git's reference rules and the derived path is proved on every call to stay below the container. Every registered worktree gets a standing and every condition a stable code with a remedy; the four worktree.* capabilities project the same typed topology to MCP, HTTP, OpenAPI, the Swagger UI and the Cockpit, and the pre-commit hook asks the guard.

docs/WORKTREES.md apps/majordomus-cli/src/worktree/path.rs test/cases/96_worktree_topology.sh recorded evidence: not run
A misplaced worktree is brought to its canonical path with its modified, staged, unstaged and untracked work intact, verified by a fingerprint taken before and after the move, and a step is reported as moved only when the two are equal

The plan changes nothing and is recomputed under the repository lock before it is applied. Git moves the directory in one rename; the container's occupant goes out to a staging path and then in; a move across filesystems is refused unless a copy is permitted, and then verified against a manifest of every entry before the original is removed. Nothing is reset, stashed, cleaned, checked out or overwritten: a destination that exists is a named conflict, a locked worktree waits, a detached one is never moved, and a session's scratch checkout is reported and moved only on request.

docs/WORKTREES.md apps/majordomus-cli/src/worktree/migrate.rs test/cases/96_worktree_topology.sh recorded evidence: not run
A product feature is one file under the layer, discovered rather than registered, and answered by the command line, the HTTP API, the OpenAPI document, MCP, the derived graph, the capability matrix and the website's homepage and feature pages with nothing else changed

Removing the file removes it from all of them, which is what a hidden registry would fail. The kind, its schema and its source class are declarations, so discovery, validation, identity, the index and the MCP resource needed no code of their own.

docs/PRODUCT.md apps/majordomus-cli/src/product.rs test/cases/97_product_features.sh recorded evidence: not run
The product is filed under a few domains declared once as files of the layer; a feature names exactly one, and every domain's members, interfaces, guarantees and use cases are derived from the features that name it, so a stable feature cannot be left out of the product map without the model refusing it

A domain lists no feature. A stable feature that names no domain while one is declared, or names one that does not exist, fails product validate; removing a domain file removes it from every answer (ADR 0104).

docs/PRODUCT.md apps/majordomus-cli/src/product.rs test/cases/800_product_domains.sh recorded evidence: not run
Which interfaces a feature is exposed through, what stands behind it and what it answers are derived from the registries that own those facts, never authored, and a source file that writes one down is refused by its schema

A feature file holds references and editorial decisions only. The surfaces come from what its modules, commands and kinds are projected onto, the counts from the registry and the index, the moments from the why catalogue, the route from the id. The schema sets additionalProperties false, so `surfaces:` or `route:` in a feature is a validation error rather than a claim a page believes.

docs/PRODUCT.md apps/majordomus-cli/src/product.rs apps/majordomus-cli/tests/product.rs recorded evidence: not run
Every reference a feature makes resolves against the registry that owns it, and one that does not is an error naming the file, the key and the nearest candidate rather than a page linking to nothing

A capability module, a public command or an object kind that no stable feature names is reported as a gap and shown on the matrix rather than refused: a thing the product does that the product page does not mention is what the model exists to make visible.

docs/PRODUCT.md apps/majordomus-cli/src/product.rs apps/majordomus-cli/tests/product.rs recorded evidence: not run
The dataset the website is rendered from carries only fields an allow-list names, so a feature's prose, a machine path or a field nobody allowed cannot reach a published page

The boundary is a field-by-field copy through PUBLIC_FEATURE_FIELDS, not an exclusion that could be forgotten: a field added to the model is absent from the site until somebody names it. The test asserts no un-allow-listed key, no body, no fixture root and no temporary path; scripts/generate-site-data refuses the artifact again before rendering anything from it.

docs/PRODUCT.md apps/majordomus-cli/src/site.rs apps/majordomus-cli/tests/product.rs recorded evidence: not run
The website's homepage and feature pages name no feature, module, command, provider or count of their own, and a stale product dataset is refused by the pre-commit hook and by the rust gate before it can be merged

scripts/site-check refuses a template, a generator, a jq program or a navigation file that names a feature route or a provider's own file, proves every public feature has its page and every page a feature, and compares the marks the matrix shows with the marks the model derived. `majordomus generate --check` refuses a tree whose dataset does not match the repository.

docs/GITHUB_PAGES_ARCHITECTURE.md site/templates/index.html test/cases/400_derivation_is_one_graph.sh recorded evidence: not run
A capability added to the registry appears in the Cockpit's listing, navigation and search and on a page of its own with a form generated from its schema, without one line of the Cockpit being edited

a_capability_the_repository_adds_reaches_every_cockpit_surface adds one rule to a fixture repository, which the registry makes a capability, and asks the Cockpit for it over a real socket in its listing, on its own page, on its object page with the navigation entry for its kind, and in its search; the_runner_form_is_generated_from_the_input_schema in the same file proves the form. Nothing of the Cockpit names that capability.

docs/COCKPIT.md apps/majordomus-cli/src/cockpit/mod.rs apps/majordomus-cli/tests/cockpit.rs recorded evidence: not run
The UI's dashboard at /ui links every capability it read to that capability's page, serves under the Cockpit's security policy, and never waits for the pull-request queue, which is a page and a fragment of its own

the_dashboard_is_served_at_ui_under_the_cockpits_policy starts a real server on a fixture repository and asserts that the page links /cockpit/capabilities/<id> for each of health.report, release.version, distribution.releases, plan.status, plan.roadmap and mesh.peers, carries the Cockpit's content-security policy, and pulls the queue from /ui/panels/pull-requests rather than citing integration.queue; the_queue_panel_is_a_page_and_a_fragment proves the queue's own route.

docs/MAJORDOMUS_UI.md apps/majordomus-cli/src/ui/dashboard.rs apps/majordomus-cli/tests/ui_surface.rs recorded evidence: not run
The Observatory at /ui/observatory and /ui/kiosk shows a scene only when the capabilities it requires answered with something to draw, or, for a scene of the mesh where no mesh runs, when the repository carries a recording of it, which it labels a replay; a scene without either carries no model and states the source's reason, on a server and in the published snapshot alike

The case starts a server over a freshly initialised repository and takes a UI snapshot of it, and on both reads the projection the page carries: eight scenes in the declared order, every source named with its state and a failed one with a reason, an available scene with a model and an unavailable one with none and a reason, the three mesh scenes unavailable where no mesh runs, the kiosk carrying the same scenes, the two redirects, and the snapshot holding the page and its script. It reads the projection, not the canvas: what the script paints from an available scene's model is not claimed here.

docs/MAJORDOMUS_UI.md apps/majordomus-cli/src/ui/observatory.rs test/cases/3011_the_observatory_draws_nothing_it_did_not_read.sh recorded evidence: not run
A deployment object that breaks its closed contract, whether by a key the contract does not have, another schema version, a missing field, an interface, port, machine count or health route that could not work, a provider the contract does not know or a provider address that is not https, a value that reads as a credential or an identity two files claim, is refused by doctor with the reason named

This is the shell half of the contract, decided without a Rust toolchain present. The semantic half, a health route no capability registers or a package the workspace does not contain, belongs to apps/majordomus-cli/src/deploy/mod.rs and is proven by that module's own tests, not by this case. The case matches each refusal by its reason; it does not require the finding to name the object, does not check doctor's exit status and does not show the object reaching the index or the object listing, so none of those is claimed here.

.ai/repo/rules/vendor/majordomus/rules/deployment-contract.v2.md lib/deployment.sh test/cases/84_deployment_contract.sh recorded evidence: not run
A claim's proof is an execution that was recorded with the commit it ran against, not a test path that resolves

The matrix binds a claim to a test by path, and the path check proves only that a file exists. The ledger under .ai/repo/evidence records the latest execution of every test with its outcome, duration, commit, tree state, source digest, time and origin; `majordomus evidence show` derives each claim's state from that join on every read, and a claim whose test nobody ran reads `not_run` rather than inheriting a guarantee from the file's existence.

docs/EVIDENCE.md apps/majordomus-cli/src/evidence/mod.rs test/cases/124_evidence.sh recorded evidence: stale
CI records every test it ran with the commit and the run it happened in, and the site publishes the tracked ledger's verdict at that commit as current only when CI's run of the published commit measured clean trees and nothing was absent, refused or failed; otherwise as stale with its distance or its failures, as unknown with its reasons, or as unavailable with the reason

The evidence job of validate.yml derives the report first, through `majordomus evidence show` on its own checkout, measured clean or dirty, with the downloaded reports and its output under the runner's temporary directory, so that report is the tracked ledger's verdict at the run's commit. Only then does it record the suite's and the crate's results through `majordomus evidence record --origin ci`, which stamps each execution with the run from its environment; those rows are a run record, counted in the manifest and deciding no verdict, and a trunk run's rows reach the tracked ledger only through ADR 0080's recording pull request. The suite and the rust job each measure the tree they ran in, excluding only the outputs their run names at the paths where that run writes them (rust-check writes in the crate's directory), and the manifest's working tree is derived from those measurements; a report measured at another commit is refused, and a report that yielded no execution is absent. For a pull request the recorded commit is the merge commit that ran, with the head beside it. `scripts/pages evidence` publishes the artifact of the commit being published or its nearest ancestor into site/data/evidence.json, never committed, and withholds current, with every reason, whenever the run cannot confirm the report, a report the collector failed to derive or did not carry included. ADR 68, as ADR 0087 amends it.

docs/EVIDENCE.md scripts/ci/evidence-collect test/cases/357_ci_records_evidence.sh recorded evidence: not run
Every rule names what proves it, and every path it names is in the tree

A rule could name a behavioural case in English prose, and nothing noticed when that case was renamed or deleted; the rule went on reading as enforced. The gate holds both halves of the relation. A named path that is not in the tree is `dangling` and is never ratcheted, because a proof that does not exist is worse than no proof. A rule of either class naming neither a validator nor a test nor a reason is `unproven`, which is the half no other check in this repository knows to look for; advisory is not an exemption, and the one exemption is a vendored rule tagged `principle`, which the package pins by hash and which the gate counts on every run. `majordomus rules` reports the same `unproven` state for every class but calls it a finding only for a blocking rule; the gate is what refuses it for an advisory one.

docs/DOCTRINE.md scripts/ci/rule-proof-check test/cases/125_rule_proof.sh recorded evidence: stale
Every claim, rule, feature, command, capability, MCP tool and use case the repository declares is an evidence subject whose members and tests are derived from declarations that already exist, and a committed subject index that differs from that derivation is refused

`majordomus generate site` writes site/data/registry/evidence-subjects.json from the claims matrix, the rules, the features, the use cases, the command registry, the documented command-line examples, the builtin capabilities and the first coverage and negative headers of every case; a capability is made of the claims implemented in the file its module is composed in, and a command word that both the shell tool and the executable answer to names both programs. Nothing lists a subject, a member or a route by hand, so adding a declaration adds its subject and removing it removes it. `generate --check` refuses a hand edit. The file carries structure only — no ledger row and no commit — so recording a run never makes it stale.

docs/EVIDENCE.md apps/majordomus-cli/src/evidence/subject.rs test/cases/507_evidence_subjects_are_derived.sh recorded evidence: not run
The behavioural and negative tests the subject index names for a command are exactly the ones its command page lists, both read from the first coverage and negative header of each case

The case headers had several readers before the index: the site generator's command pages, the command-coverage doctrine, the command-furnished gate and the use-case impact trace, and the last two match a command anywhere in any covers line between word boundaries rather than as a word of the first. The index reads the first line word for word, as the page does. The case holds the two equal, holds every case to one header of each kind, and holds each covers line to naming the same public commands under both readings, so the readers cannot yet disagree; the gate slice moves them onto the index. Two more shell derivations reach tests through declarations rather than headers: the capability pages give every capability the claims implemented in its module's file, and the doctrine pages name the first test of each rule. The same case holds each of them equal to the index.

docs/EVIDENCE.md apps/majordomus-cli/src/evidence/subject.rs test/cases/507_evidence_subjects_are_derived.sh recorded evidence: not run
A rule that no program can express says so in its own front matter, with the reason, and is counted apart from every rule that has a gate

Some rules genuinely have no machine expression: the provenance of what was written, whether a mechanism earns its cost. Before `reviewed_because` those sat in a debt list beside rules that merely had not been got round to, and the two were indistinguishable. The reason is the whole declaration — there is no flag beside it, because a flag can be set and a reason cannot be set without writing one. An executable proof always wins over it, so a rule that acquires a case stops being review-enforced without anyone remembering to delete anything.

docs/DOCTRINE.md scripts/ci/rule-proof-check test/cases/125_rule_proof.sh recorded evidence: stale
A rule's proof state is derived from the tree and the ledger together, and a mechanism that refuses violations is never reported as a run that passed

`majordomus doctrine` asks whether the repository satisfies a rule now; the proof graph asks whether the rule is in a state where it could be satisfied at all. Ten states, and the ones that must not collapse are `proven` (a passing run and nothing changed since), `gated` (an executable check a CI gate runs, with no verdict recorded here) and `reviewed` (a declared exemption). A rule's state is the weakest of its parts, so one dangling case makes the rule dangling however many others pass.

docs/DOCTRINE.md apps/majordomus-cli/src/rules/mod.rs test/cases/133_rule_graph.sh recorded evidence: stale
Every authored file in this repository is spelled in English, and a proper noun, a test fixture or a published translation that is not carries its reason beside the check

The check is an alphabet test and says so: a letter that occurs in Czech, Slovak or Polish and never in English, or a whole other script, is not English whatever else is true of the file. English prose carrying a sentence of another language spelled in ASCII passes it, and always will — the rule stays wider than the gate, which is the honest relation between the two. The two exemptions are declared with a reason: `name` for a proper noun, `fixture` for a file that uses a foreign string as data to prove the program handles one.

docs/DOCTRINE.md scripts/ci/english-only-check test/cases/138_governance_gates.sh recorded evidence: stale
A page of the published site written in a reader's language is declared with that language and a reason, and is refused unless the page declares the same language itself

project.english-only v2 (ADR 0077) keeps the repository's own artifacts English with no exceptions and names one population apart: content published for a reader in that reader's language. The permission costs three declarations that must agree — the path is under site/content-src/ or its projection, the allow list carries the language and a required reason, and the page itself declares the same language. The third is what stops an allow list from being a file an entry can be appended to in order to silence a finding.

.ai/repo/rules/project/english-only.v4.md scripts/ci/english-only-check test/cases/403_published_translation.sh recorded evidence: not run
Every finding a command reports names the command that reproduces it

`mj_finding` takes the reproduce command as its fifth argument and prints the finding without it when it is absent, so a caller that forgot one produced a finding that read exactly like a finding that could not have one. The gate counts the arguments of every call to the reporting helpers, treating a forwarding call as the caller's business and reporting a call it cannot parse as unreadable rather than passing it silently. It landed with an empty baseline: the nine violations it found were fixed rather than recorded.

docs/DOCTRINE.md scripts/ci/finding-reproduce-check test/cases/138_governance_gates.sh recorded evidence: stale
A run recorded against this commit and a run whose inputs merely have not changed since are reported as different states, never as one

`proven` means the diff between the execution's own commit and the working tree is empty, excluding the ledger's own row — the tree in front of you is the tree the run measured. `inputs_unchanged` is a passing run where nothing the claim names differs from the working tree — the absence of a known invalidation, not proof at HEAD, because a change the claim does not name could have broken it. Collapsing the two is how a badge stops meaning anything, so the states are distinct in the model, in every projection and in the gate.

docs/EVIDENCE.md apps/majordomus-cli/src/evidence/mod.rs test/cases/124_evidence.sh recorded evidence: stale
Recording one test's result never erases the evidence recorded for any other test

The ledger is merged per test, not rewritten per run, so `bash test/run.sh 84_distribution_model` followed by a recording updates exactly that one entry. A whole-ledger write would turn a one-case run into a repository that has proven one thing, which is worse than one that has proven nothing because it looks deliberate.

docs/EVIDENCE.md apps/majordomus-cli/src/evidence/ledger.rs test/cases/124_evidence.sh recorded evidence: stale
Every claim names the test meant to settle it and every test names the claims it is meant to settle, from one derivation

`evidence claim` answers what proves a claim and `evidence proves` answers what a test proves, including the other claims that share it. Both read the same join, so the two directions cannot disagree; a relation that could only be walked one way is half a relation.

docs/EVIDENCE.md apps/majordomus-cli/src/capability/builtin/evidence.rs test/cases/124_evidence.sh recorded evidence: stale
A claim that declares a guarantee no recorded run supports is named by a gate rather than displayed as guaranteed

`majordomus evidence show --check` exits 10 on any guaranteed claim the evidence does not support, and `scripts/evidence-check` ratchets that against .ai/repo/evidence-baseline.txt, which records the state per claim rather than a list of names. Two things fail: a guarantee the baseline recorded as supported that no longer is, and a supported guarantee that left the matrix. A claim the baseline never knew, with no recorded run, is new debt — named and counted, never a regression — because a ratchet that fired on growth taxed writing claims down. The gate is advisory while the baseline carries unsupported lines and `--strict` is the end state; only `guaranteed` is judged, because advisory, planned and rejected already say that a current proof is not what they claim.

docs/EVIDENCE.md scripts/evidence-check test/cases/124_evidence.sh recorded evidence: stale
A claim reads proven only when its passing run was recorded on a commit the presented revision contains, nothing but the evidence ledger changed between the two, and neither the recorded tree nor the presented tree was dirty; a run on a commit the presented revision does not contain reads stale; a named revision is judged by the ledger committed in it, and a clean failing run of the same test that the checkout holds uncommitted, recorded between the two, caps it at stale

`evidence::freshness` is the one truth table every surface reads, and only its proven row may render as verified. `evidence show --presented HEAD` judges the checked-out commit as committed, from the ledger that commit holds, against its tree measured without the working copy of the ledger; `--presented-tree` only weakens that measurement. A run the working ledger holds and the committed one does not is listed in `presented.uncommitted` and read only through the monotone rule, which withholds proven and never decides a verdict.

docs/EVIDENCE.md apps/majordomus-cli/src/evidence/freshness.rs test/cases/502_evidence_is_judged_at_the_presented_revision.sh recorded evidence: not run
A test that declined to run reads not_run with a detail saying it declined, never proven and never failing, and a test that errored or timed out reads failing

A recorded skip used to read failing, because every result that was not a pass did. It is the absence of a proof, not counter-evidence: the claim reads not_run with the detail that the test declined to run, and a guarantee resting on it is still a finding, one that says its test declined rather than that it was never recorded.

docs/EVIDENCE.md apps/majordomus-cli/src/evidence/freshness.rs test/cases/502_evidence_is_judged_at_the_presented_revision.sh recorded evidence: not run
A rule with a failing test reads failing whatever its other tests say, so a blocking rule whose tests are one failing and one not run is still a finding

`freshness::aggregate` is the one aggregation: failing if any part is failing, otherwise the weakest of the parts that can carry proof, otherwise the weakest of all. The declared order ranks failing above not_run, so a plain weakest-of over a failing test and a skipped one read not_run, which is not a finding, and hid the failure.

docs/EVIDENCE.md apps/majordomus-cli/src/evidence/freshness.rs test/cases/502_evidence_is_judged_at_the_presented_revision.sh recorded evidence: not run
No mesh discovery socket opens until the repository commits an enabled mesh declaration, and a disabled or absent declaration is reported as the reason, not an error

The shared server activates the mesh runtime only from an enabled mesh-declaration object (kind, schema and source class in the layer); tests prove a disabled declaration opens nothing, creates no identity, and answers why, and that activation never delays serving. The skeleton a new repository starts from ships no declaration (ADR 0050); this repository commits its own enabled, a reviewed decision that docs/MESH.md describes and test/cases/491_the_mesh_is_on_here.sh holds.

docs/MESH.md apps/majordomus-cli/src/shared.rs apps/majordomus-cli/tests/mesh.rs recorded evidence: not run
Every discovery observation converges into one bounded registry, deduplicated by node and runtime identity across sources, replay-protected per instance, and every surface projects that registry and holds no peers of its own

tests/mesh.rs proves the convergence to one record over a real socket, and the registry unit tests prove multi-source deduplication, replay drops, restart handling and the eviction bound; test/cases/130_mesh.sh covers the operator's path without a network and carries no part of this claim; scripts/ci/mesh-check (gate mesh-check) refuses a UDP socket outside src/mesh/ and a second MeshRegistry construction site. Since discovery protocol 2 (ADR 0067) a record is one runtime of a node, so two worktrees of one machine are two records under one key.

docs/MESH.md apps/majordomus-cli/src/mesh/registry.rs apps/majordomus-cli/tests/mesh.rs recorded evidence: not run
Two runtimes of one repository link only through a signed, replay-protected handshake that checks protocol, repository identity and trust, and every refusal is typed and visible

Separate server processes link over TCP and their refusals of malformed, forged, future-protocol, other-repository and untrusted hellos are read back over HTTP; the unit tests of mesh::cooperation cover replay of a hello and of a sync counter, and the self link. No TLS — authenticity is the protocol's, confidentiality the network's (ADR 0067).

.ai/repo/rules/project/mesh-cooperation-is-authenticated.v1.md apps/majordomus-cli/src/mesh/cooperation.rs apps/majordomus-cli/tests/mesh_cooperation.rs recorded evidence: not run
An exclusive claim made on one runtime excludes an overlapping claim on every linked runtime, and concurrent claims resolve to one named winner everywhere

Property tests of mesh::state prove the fold is independent of order and duplication and that no interleaving leaves two held exclusive claims meeting; the integration tests refuse a conflicting claim across two processes, across two worktrees and through a relay; the in-process partition test and the lab's network_partition scenario heal a partition into one held and one conflicted claim. Not strong consistency: under a partition both sides may hold, and the conflict is named after healing.

.ai/repo/rules/project/mesh-cooperation-is-authenticated.v1.md apps/majordomus-cli/src/mesh/state.rs apps/majordomus-cli/tests/mesh_cooperation.rs recorded evidence: not run
Runtimes serving different repositories never form a cooperative link, even when they discover each other

The repository identity is the digest of the root commits or a declared value, never a path; the handshake refuses a mismatch both ways and ingest refuses another repository's event. The lab proves it between Linux nodes that discovered each other by multicast.

.ai/repo/rules/project/mesh-cooperation-is-authenticated.v1.md apps/majordomus-cli/src/mesh/repository.rs apps/majordomus-cli/tests/mesh_cooperation.rs recorded evidence: not run
A runtime that stops beating — crashed, killed or partitioned — expires on its peers within the declared expiry, its claims stop excluding, and its restart reconnects as the same runtime without a duplicate

A server process is killed with SIGKILL and restarted in the same checkout; its peer lists it once with restarts counted and its previous run's claim stays expired. Beats are aged on each runtime's own monotonic clock. The lab repeats it with docker kill and a network disconnect.

docs/MESH.md apps/majordomus-cli/src/mesh/cooperation.rs apps/majordomus-cli/tests/mesh_cooperation.rs recorded evidence: not run
Runtimes linked only through a relay converge on one state digest, each event held once, with no replication loop

Three server processes in a line agree on the digest, hold the same event count, and carry no further events once converged; origin signatures survive the relay. Delivery is at-least-once with idempotent application, not exactly-once.

docs/MESH.md apps/majordomus-cli/src/mesh/journal.rs apps/majordomus-cli/tests/mesh_cooperation.rs recorded evidence: not run
Compacting the cooperation journal changes nothing live — a handover nobody has taken keeps its stream for a bounded retention, who took a handover and who answered a review stay with them, and a compacted runtime that beats again is fetched again whole

Case 493 runs tests/mesh_compaction.rs, two runtimes linked through the real signed protocol over an in-process transport, because no black-box knob reaches compaction in bounded time (a server compacts every sixty heartbeats and keeps a dead stream fifteen minutes; only the heartbeat and the expiry are declarable). The journal's unit tests reproduce each defect PR 588 recorded, and its property test holds that compaction changes no live claim, open session, handover taker or review answer over random histories. Not covered — two runtimes may print different digests while each compacts on its own schedule; what is live agrees, and docs/MESH.md says so.

docs/MESH.md apps/majordomus-cli/src/mesh/journal.rs test/cases/493_mesh_compaction_keeps_what_is_live.sh recorded evidence: not run
Separate Linux nodes on a real network discover each other by multicast, link, share claims and handovers, and recover from a partition and a crash, in a lab CI runs

Four containers on one docker bridge, no seeds; ten scenarios with measured timings written to target/mesh-lab/evidence.json; the mesh-lab CI job runs the lab and then scripts/ci/mesh-lab-evidence, which refuses a record that is not a complete acceptance — the lab does not judge itself. Case 414 covers that reader and holds its required set equal to the scenarios the lab reaches. One layer-2 segment and one kernel — not a routed, NATed or WAN topology; physical machines are a documented manual procedure.

docs/MESH.md test/mesh-lab/run test/cases/414_mesh_lab_evidence.sh recorded evidence: not run
A discovered node gains no execution and no authorization from being heard; only signed traffic from a linked node may change mesh state, and trust labels records under an explicit policy that defaults to deny_unknown

The trust unit tests prove deny_unknown observes without trusting, allowlist and TOFU semantics, and that a key change under a known node id is rejected; the integration test proves a forged envelope is a counted refusal. Discovery still grants nothing: being heard or trusted admits no link on its own — a link needs the handshake of mesh-cooperation-links — and nothing a linked peer sends executes anything; the cooperation commands write this runtime's journal and, for a consumed handover, one record in this checkout's handovers directory (ADR 0067).

.ai/repo/rules/project/mesh-is-observation-not-authority.v1.md apps/majordomus-cli/src/mesh/trust.rs apps/majordomus-cli/tests/mesh.rs recorded evidence: not run
An enabled mesh declaration whose shared server did not activate the mesh is a failed `runtime` check of `mesh doctor` (exit 10) carrying the server's reason, and a `Mesh: DECLARED ENABLED BUT NOT ACTIVE` line in the session-start briefing — never a quiet off; and this repository's committed declaration is tracked, enabled, allowlists the fleet and names a hub

The runtime keeps one bit saying whether a shared server decided anything (activated, or declined with a reason); the doctor's `runtime` check reads the server's decision when the bit is set and reports an absence when it is not, and `majordomus mesh doctor` asks the running server for its report so that the verdict is the server's. The hook library renders the briefing line from that report and sends no request of its own. apps/majordomus-cli/tests/mesh.rs proves the three verdicts over a real server; the case proves the briefing answers and the exit code through the provider's start event, and reads this repository's committed declaration (ADR 0059). It does not make the mesh reach anybody, and hubs are started by hand.

docs/MESH.md apps/majordomus-cli/src/mesh/doctor.rs test/cases/494_the_mesh_is_declared_and_held.sh recorded evidence: not run
The inbound admissions a mesh declaration implies on a machine — the multicast group's port, the declared hub ports whose address is the machine's, the server's port beyond loopback — are derived from the declaration by `majordomus mesh firewall`, which names the host's firewall backend, the commands that admit them and the firewall's own verdict, exits 10 when a rule is observed missing or the kernel logged a drop toward those ports, and whose `apply` runs the commands only as root and never over HTTP or MCP; `mesh doctor` carries the same verdict as its `firewall` check

The plan, the backend detection and every judgement of a firewall's output are pure functions with doctests; only observing and applying touch the host, and each says when it could not (unobservable without root is an absence, not a failure, unless the kernel log shows drops). The case runs the report over this repository's declaration and over fixtures, and never applies anything. It does not make a firewall nobody runs the command on admit anything.

docs/MESH.md apps/majordomus-cli/src/mesh/firewall.rs test/cases/495_the_firewall_admits_the_mesh.sh recorded evidence: not run
Vendors and models are declared once in share/models.yaml, and the CLI, HTTP, OpenAPI, MCP and Cockpit render that one declaration with no model name of their own

models.list and models.route are capability declarations, so every surface is a projection; the catalogue's own diagnostics report a duplicate alias or an undeclared vendor at first read. Declaration order is routing's preference order, verifiable by reading the file.

docs/MODELS.md apps/majordomus-cli/src/models/mod.rs test/cases/131_models.sh recorded evidence: not run
A routing decision names why the selected model was selected and, for every excluded model, the first check it failed — the same answer for the same question, with no hidden state

Routing is a pure function over the declared catalogue and the stated need; the unit tests prove the preference order, the fixed check order behind every exclusion reason, and that a model named outright is still checked against the requirements.

docs/MODELS.md apps/majordomus-cli/src/models/mod.rs test/cases/131_models.sh recorded evidence: not run
The model catalogue reports whether a vendor's named credential variable is set and can carry no secret value — the schema has no field one could hide in

credential_env names an environment variable and only std::env::var_os(..).is_some() is ever consulted; the test walks the catalogue's JSON schema and refuses any property name that smells like a credential.

docs/MODELS.md apps/majordomus-cli/src/models/mod.rs test/cases/131_models.sh recorded evidence: not run
A provider's end event and its compaction event derive the knowledge the episode produced into candidate records under the tracked tree, from the ledger and git and never from a conversation, whether or not a task is active, and record that they did so in the ledger

The close path in lib/session.sh runs the deriver after the session record is published and before session.closed is appended, so every close is followed by its knowledge.derived line; the compaction adapter calls it directly, independent of the checkpoint switch. A decision becomes a convention, a resolved question a fact, a blocked task a lesson, a verified completion a fact; a handover's Next Action is continuation state and is never derived from. Nothing reads a conversation, a prompt or a model.

docs/KNOWLEDGE.md lib/knowledge.sh test/cases/563_knowledge_is_derived_at_the_boundary.sh recorded evidence: not run
A second derivation over the same evidence writes nothing, says so, and leaves the working tree unchanged; the record id is derived from the episode and a digest of the evidence so the same fact always lands in the same file

The record is composed into a temporary file in the same directory and compared byte for byte with the file the id names; identical is reported unchanged and the temporary file removed, different is renamed over it. The date is the evidence line's day, never the clock, and no random value enters the bytes, so the same ledger and the same git yield the same file on every machine.

docs/KNOWLEDGE.md lib/knowledge.sh test/cases/564_knowledge_derivation_is_idempotent.sh recorded evidence: not run
A policy switch that is off derives nothing and says so; a derivation that fails inside a provider hook is recorded as provider.event.failed and the hook still exits 0

session.knowledge_on_end and session.knowledge_on_compact are the two switches, read from the policy and independent of checkpoint_on_compact. A hook must return success to its provider and returning success is not the same as claiming the work happened, so the adapter reports the failure through mj_capture_session_failed with the deriver's last line as the reason and closes the episode regardless.

docs/KNOWLEDGE.md lib/capture.sh test/cases/565_knowledge_switch_and_failure_leave_evidence.sh recorded evidence: not run
Every knowledge record is schema-valid with no unknown key, uniquely identified, evidenced when verified, resolves every reference it names, and carries no conversation; check, doctor and `majordomus knowledge check` refuse a record that is not

mj_validate_knowledge_integrity reads candidates/ and curated/ once, refuses a candidate that claims verified, a dangling derived_from or relations target, a duplicate id across both directories, a key the generated allow-list does not carry, a superseded record with neither superseded_by nor a rejection, and a title, description or body line matching the transcript check that never-store-transcripts is kept by. References to the ledger and git (session, task, decision, commit) are resolved here, in one pass, and are external to the graph compiler, which never reports them as dangling.

docs/KNOWLEDGE.md lib/knowledge.sh test/cases/707_knowledge_check_refuses_malformed_records.sh recorded evidence: not run
A repository in which episodes close and no knowledge derivation follows within the stale threshold is reported as a stopped writer by doctor and watch, and the finding clears when a derivation is written

mj_validate_knowledge_lifecycle compares two facts the way ADR 0052's validator does, episodes closing and derivations following, judged against session.freshness.stale_minutes; it also reads the lifecycle source for the call to the deriver while session.knowledge_on_end is on. A checkout whose ledger holds no knowledge.derived line is skipped rather than failed, so the day the deriver arrives no checkout turns red; the first derivation ends the skip. The Rust reader's knowledge_base.status makes the freshness half of the same judgement from the same numbers.

docs/KNOWLEDGE.md lib/knowledge.sh test/cases/708_health_sees_a_stopped_knowledge_writer.sh recorded evidence: not run
verified is written only by promote with evidence on stdin or by a person editing the file, never by the deriver; promotion moves the record to curated/ and rejection supersedes it with a reason, each leaving a ledger line

An empty stdin is refused because an act without evidence is an assertion, and stdin carrying a transcript marker is refused for the reason a record may not carry one. Promotion keeps derived_from, adds the promoting episode, appends the evidence under an Evidence section and writes knowledge.promoted; rejection rewrites the candidate in place as superseded with a Rejected section and writes knowledge.rejected. The deriver skips a promoted or superseded id, so neither act is undone by the next episode boundary.

docs/KNOWLEDGE.md lib/knowledge.sh test/cases/709_knowledge_promotion_is_an_act.sh recorded evidence: not run
knowledge observe records one observation.recorded ledger line with its kind, subject, statement and typed evidence references, inside an open episode, inside a task or outside one, and refuses any other shape, or the absence of an episode, without writing

The kinds are friction, workaround, defect, drift and repetition. A subject is a repository-relative path without '..' or capability:, rule:, command:, gate: followed by an identifier; an evidence reference is file:, test:, commit:, issue: or claim: followed by what it names. A statement that spans lines or opens like a turn of a conversation is refused. A malformed observation exits 2; one recorded with no open episode exits 10, naming session start, because the deriver selects lines by their episode stamp. Every refusal leaves the ledger as it was. The observation is derived into a candidate at the episode boundary (ADR 0118).

docs/KNOWLEDGE.md lib/knowledge.sh test/cases/980_a_session_records_what_it_observed.sh recorded evidence: not run
Each observation an episode recorded becomes, at that episode's end and with no model, a candidate of its own that says what it is about, and knowledge candidates groups the records about one subject with the episodes behind them; an episode that observed nothing records that it found nothing

The id is ADR 0091's, the episode followed by a digest of the kind, the subject and the statement, so every file is one episode's own and nothing is read, merged and written back. A rejected record stays rejected and a later observation of its subject is a new candidate listed beside it in the same group. The Rust reader knowledge_base.candidates answers the same groups. ADR 0118.

docs/KNOWLEDGE.md lib/knowledge.sh test/cases/981_an_observation_becomes_a_grouped_candidate.sh recorded evidence: not run
Every candidate derived from an observation carries a route — platform, enforcement, generator, documentation or project — derived from its subject's structure and never from its wording, and in a repository that adopted the tool its own share/ routes project

platform is a command:, capability: or rule:majordomus.* subject, a path under the vendored rules, or the tool's own share/ only when it lies inside the repository; enforcement is another rule: or a gate:; generator a path the scope declares generated or .gitattributes marks merge=derived; documentation a path under docs/; anything else is project. The route decides nothing; promotion and rejection stay acts. ADR 0118.

docs/KNOWLEDGE.md lib/knowledge.sh test/cases/982_a_candidate_is_routed_by_what_it_is_about.sh recorded evidence: not run
The start briefing names the candidates awaiting review on this branch, count and ids, bounded and within the briefing budget, and prints their absence rather than omitting it

The section sits after the open questions and before the handover in the derived briefing, bounded the way the open-questions block is. A candidate's branch is the branch of the episode its derived_from names, read from the tracked session record or, while that record is not yet written, from the ledger; a candidate whose episode this checkout cannot resolve is named on its own line as unattributed rather than hidden.

docs/KNOWLEDGE.md lib/derive.sh test/cases/710_briefing_names_the_candidates.sh recorded evidence: not run
More candidates than the policy cap, or a candidate older than the policy age, is an advisory doctor finding that names them; both numbers are declared once in the policy

knowledge.candidates_max_files and knowledge.candidate_max_age_minutes are read with mj_pol_req and a missing key is reported at the rule's class. Review age is the age of the queue entry — the oldest knowledge.derived line naming the file, then the commit that added it, then the record's date — never the evidence day alone. A candidate version control does not track is named too, with the git add remedy after fix:, because a record git does not hold is discovered by nothing. The rule is dispatched from doctor only, so a full review queue never turns watch red.

docs/KNOWLEDGE.md lib/knowledge.sh test/cases/711_candidates_are_reviewed_not_accumulated.sh recorded evidence: not run
The candidates awaiting review, one record by id with its references resolved, and the derivation status of the checkout are one declaration each and answer with the same content over the command line, HTTP and MCP

knowledge_base.candidates, knowledge_base.record and knowledge_base.status are capability! blocks in one module; majordomus generate projects them to the HTTP routes, the OpenAPI document, the MCP tools and resources, the executable's knowledge subcommands, the cockpit's module catalogue and the benchmark inventory. The reader takes candidates from the index, the ledger from the session module and the thresholds from the continuity module's reading of the policy; it writes nothing and reports absence as absence.

docs/KNOWLEDGE.md apps/majordomus-cli/src/capability/builtin/knowledge_base.rs apps/majordomus-cli/tests/knowledge_base.rs recorded evidence: not run
Every executable line, function and region a change adds or touches in the Rust crate must be covered by a test; a change that leaves new code uncovered is refused, and untouched legacy debt is left alone

scripts/ci/coverage-differential intersects the one canonical llvm-cov export (test code excluded) with the lines git says the change touched against the branch's merge-base with the trunk, working tree included, and exits 10 on the first changed executable item no test covers. The rule is project.new-code-is-covered; case 132 plants a deliberately uncovered changed line, proves the gate fails, then covers it and proves the gate passes, and proves an unresolvable base is UNKNOWN rather than a silent pass.

docs/COVERAGE.md scripts/rust-coverage test/cases/132_coverage_differential.sh recorded evidence: not run
A commit message that does not satisfy the repository's declared commit policy is refused at the moment it is written, and the history's existing debt is recorded and may not grow

The judge is commit.validate; .githooks/commit-msg asks it with the staged paths, which is the one moment a fix with no test among its files can still be noticed cheaply, and scripts/ci/commit-policy asks the same judge of the history through commit.history. The hook is declared in the policy's enforcement list as commit-policy-on-message and doctor reconciles that wiring. Before this the rule project.conventional-commits was advisory and its failure behaviour read "No command decides this rule; a reviewer does"; 48 subjects over the declared width and 38 unknown type words had accumulated unremarked.

docs/COMMIT.md apps/majordomus-cli/src/commit/verdict.rs test/cases/274_commit_policy.sh recorded evidence: stale
The scope vocabulary a commit is judged against is learned from this repository's own history rather than kept in a table, so a subsystem committed today is in the vocabulary today and a scope outside it is a warning rather than a refusal

commit.scopes reads at most 1500 commits of git log and counts each scope's association with directory prefixes four segments deep; suggest() reports a tie as ambiguous rather than breaking it, and answers nothing at all when no prior commit associates any scope with the paths - which is the honest answer for the first commit of a subsystem and better than the repository's most popular scope. The case proves the vocabulary follows the history: a scope unknown before a commit about it is known after.

docs/COMMIT.md apps/majordomus-cli/src/commit/scopes.rs test/cases/274_commit_policy.sh recorded evidence: stale
A commit plan carries a fingerprint of the repository, the worktree, HEAD and the exact change set it was derived from, so a plan acted on after the tree moved is refused with the reason rather than applied

Several workers share a checkout here and more share a repository, so a plan computed at one HEAD and executed at another would commit files somebody else staged under a message about work that is no longer what changed. The fingerprint is not a lock and does not prevent the race; it refuses to be the one that loses it silently. Two worktrees of one repository share a repository and differ in worktree, which is what keeps their plans apart. It also carries the project.derived-files-regenerated rule: a change set holding generated files is one commit, because only the last of several could carry current derived data.

docs/COMMIT.md apps/majordomus-cli/src/commit/plan.rs test/cases/274_commit_policy.sh recorded evidence: stale
A crate test binary that ran no test or only a filtered subset is recorded as a skip, one with a failed test as a failure and one that printed no result as an error, whatever colour codes its output carries, and what no claim can name yet is listed as dropped

The result line of cargo test says ok for a binary that ran nothing, so the recorder reads its counts. A failed count is a failure, a binary that passed nothing or ran a filtered subset is a skip, a line with no count or a binary with no result line is an error, and escape sequences are stripped first. The unit-test binary of the crate, its doctests, binaries outside tests/ and result lines no Running line named are listed in the recording as dropped, with the reason, because no claim can name them until a runner records them one by one.

docs/EVIDENCE.md apps/majordomus-cli/src/evidence/record.rs test/cases/506_a_crate_binary_that_ran_nothing_is_not_a_pass.sh recorded evidence: not run
A recorded execution carries the commit and the tree that its run's own measurement stated, a measurement that names the report it belongs to and never hides a tracked change; a measurement of another commit or another report is refused, a local measurement that names no report is refused, a CI report without a measurement is refused, and a report recorded without one carries an unknown tree, which never reads proven

evidence stamp measures the checkout as the run left it, the evidence ledger ignored and only the run's own untracked outputs excluded; evidence record --provenance carries that measurement into the report's executions and takes the digest of a clean run from the commit. The stamp measures when it is taken, not while the run executed, and a CI job's tree file names no report until CI stamps with its report.

docs/EVIDENCE.md apps/majordomus-cli/src/evidence/record.rs test/cases/505_a_recording_carries_what_its_run_measured.sh recorded evidence: not run
Every recording yields a run record naming its commit, the tree each report's run measured, its totals and what was absent, dropped or unknown, and a coverage summary joins it only bound to the commit and tree it was measured on

evidence record returns an EvidenceRunRecord and writes it with --run-record; --coverage binds the scripts/rust-coverage summary to its commit and measured tree with the floors the threshold files state. A run record decides no verdict; it can only withhold proven through the one monotone rule, which no surface applies yet.

docs/EVIDENCE.md apps/majordomus-cli/src/evidence/run.rs test/cases/505_a_recording_carries_what_its_run_measured.sh recorded evidence: not run
A recording made with --ledger local writes only the ignored local ledger, changes no tracked file and moves no verdict

The local ledger lives under .ai/local/evidence/, which the repository ignores; the tracked ledger stays the only authority a verdict is derived from.

docs/EVIDENCE.md apps/majordomus-cli/src/evidence/ledger.rs test/cases/505_a_recording_carries_what_its_run_measured.sh recorded evidence: not run
A test case that declines to run is reported as a skip and recorded as one, so the evidence ledger can never read a case that asserted nothing as the proof of the claim it names

A case that cannot meet a precondition said so with `exit 0`, which is the status the runner wrote `ok` for, so `majordomus evidence record` entered a run that measured nothing into the ledger as a pass. test/lib.sh's `skip` exits 4 and writes the file the runner names in MJ_SKIP_MARK, the runner writes SKIP into the TSV in both the serial and the parallel phase, and Outcome::Skip does not prove. A skip is still not a failure: the run's exit status is unchanged and a run whose cases all declined is a run rather than "no cases found". A failure is not a skip either: a case runs under `set -e`, `jq -e` fails with 4 when it produced no result, and a 4 without the mark is recorded as FAIL. The old shape is refused where it would be written: the case that proves this claim reads every file in test/cases/ and fails when one says `exit 0` before its last statement outside a heredoc body or a comment, so a case can leave with status 0 only by reaching its end.

docs/EVIDENCE.md test/run.sh test/cases/413_a_skipped_case_is_not_a_proof.sh recorded evidence: not run
Majordomus reasons, decides, implements and validates with no advisor at all: absent advisors are reported as ordinary state, material uncertainty gets a structured local review, and the conclusion claims no independent review

the case builds its environment from nothing (env -i, a PATH holding git and node only, no credential), so it holds on a machine where every advisor is installed.

docs/REASONING.md apps/majordomus-cli/src/reasoning/policy.rs test/cases/730_reasoning_works_with_no_advisor.sh recorded evidence: not run
Every advisor transport is held to one contract — typed failures, deadlines, cancellation, normalised answers, reported usage, redacted secrets — proven against fakes with no network, model or credential

node --test scripts/lib/advisors/contract.test.mjs; the case refuses a run of fewer tests than the suite holds.

docs/REASONING.md scripts/lib/advisors/contract.mjs test/cases/731_advisor_transport_contract.sh recorded evidence: not run
Advisors that disagree never decide by count: a conclusion is refused while a disagreement on its assessment is unsettled, a resolution needs evidence, and every answer received must be weighed

the case lets the minority position win once an experiment settles the disagreement.

docs/REASONING.md apps/majordomus-cli/src/reasoning/store.rs test/cases/732_disagreement_is_settled_by_evidence.sh recorded evidence: not run
An advisor that times out, is rate limited, refuses its credential or answers malformed is recorded as exactly that, never blocks the work, opens a circuit that closes again after its cooldown, and never fails the session

the circuit is derived from the recorded outcomes and the clock; the case shortens the cooldown with MAJORDOMUS_REASONING_COOLDOWN_SECONDS.

docs/REASONING.md apps/majordomus-cli/src/reasoning/availability.rs test/cases/733_advisor_failure_and_recovery.sh recorded evidence: not run
A decision and its provenance survive the session: the next session finds them in its context and handover, reuses a standing conclusion instead of consulting again, and reopens it only on new evidence

the scripted advisor of session B would hang if it were asked, so reuse is proven by the absence of a consultation.

docs/REASONING.md apps/majordomus-cli/src/reasoning/state.rs test/cases/734_reasoning_survives_the_session.sh recorded evidence: not run
Advisors are declared once, in share/advisors.yaml, by reference to the provider table and the model catalogue; a new advisor reaches the command line, the policy, HTTP, OpenAPI, MCP and the Cockpit with no consumer edited

the case appends a representative advisor to a fixture catalogue and reads it back from every surface, then removes its executable and sees it unavailable everywhere.

docs/REASONING.md share/advisors.yaml test/cases/735_a_new_advisor_needs_no_consumer_edit.sh recorded evidence: not run
A session cannot claim review it did not get: a consultation must name an advisor its recorded plan selected, a conclusion's review is computed, and reasoning check and doctor refuse a forged record, provider coupling, a model credential in CI and a refused claim in a document

a negative case: each defect is planted and must be named by reasoning check (exit 10) and by doctor.

docs/REASONING.md apps/majordomus-cli/src/reasoning/check.rs test/cases/736_reasoning_check_refuses_drift.sh recorded evidence: not run
Whether a task added debt, left a branch or a worktree behind, or grew the backlog is answered by the done invariant from git, the debt baselines and the recorded forge observation, measured from where the task started; a reading that was not taken is unknown, never a pass

the readings are the repository's, not one worker's; the case adds an entry to a baseline, creates a branch and a worktree, and breaks the task's starting commit to see unknown instead of pass.

docs/CLI.md apps/majordomus-cli/src/gates/closing.rs test/cases/1037_the_closing_question_is_measured.sh recorded evidence: not run
Every Mermaid diagram of the built site gets an id (diagram-<name>), the list of its nodes, the viewer's controls and a link for each node that is the identity of an object the site has a page for, all derived at build; a link to

the case builds a fixture site, runs the pass and scripts/ci/link-check over it, and plants an undeclared node, another diagram's node, an absent diagram and a duplicate declared name; the viewer's behaviour in a browser is the diagram spec of scripts/interaction-probe, which this claim does not cover.

docs/DIAGRAMS.md scripts/lib/diagrams.mjs test/cases/3006_diagrams_are_interactive_and_linkable.sh recorded evidence: not run

advisory

claimsourceimplementationtestrecorded evidence
A decision record without a task, a head and a reason is reported, and does not block

decisions.md is hand-editable by design, so an unattributable entry is a warning. finish still refuses when a profile requires a record it cannot find.

docs/DOCTRINE.md lib/check.sh test/cases/21_decision_question.sh recorded evidence: not run
Capability class, reasoning effort, output verbosity, context and verification are five independent axes

The axes are validated and projected into the worker's instructions. Whether a worker honours them is not observable from outside the worker.

share/skeleton/profiles/implementation.yaml lib/update.sh test/cases/03_update.sh recorded evidence: not run
A profile names a capability class rather than a vendor model

Mapping a class to a concrete model is left to whoever runs the worker. Majordomus never selects or invokes a model.

share/skeleton/profiles/deep-work.yaml lib/update.sh test/cases/03_update.sh recorded evidence: not run
A profile declares which context a worker should load and no more

majordomus context now assembles a briefing from those toggles, and that selection is guaranteed (see context-assembly). This claim stays advisory because nothing measures what the worker actually read once it has been handed the briefing.

share/skeleton/profiles/routine.yaml lib/check.sh test/cases/04_start_check.sh recorded evidence: not run
Escalating reasoning effort after repeated blocked attempts is recorded rather than assumed share/skeleton/profiles/debugging.yaml lib/update.sh test/cases/03_update.sh recorded evidence: not run
A profile sets how often a worker should checkpoint, and a stale task is reported

The staleness report is guaranteed. Whether the worker checkpoints is not.

share/skeleton/policy.yaml lib/check.sh test/cases/04_start_check.sh recorded evidence: not run

planned

claimsourceimplementationtestrecorded evidence
A task will know which commits it produced, rather than inferring them from a range

Today scope is checked with git diff base..HEAD, and a commit range has no author, so another session committing to the same branch in the same checkout has its files counted as this task's work. Recording each commit as it happens turns the question from a heuristic about ranges into an enumeration. The list would be a lookup that git overrules, never authority, and a rewritten commit would be reported as unrecognised rather than resolved to a guess.

docs/CONTINUITY.md — — recorded evidence: no test
Opt-in runtime adapters will clamp read size, output size and fan-out from profile-derived limits

The one implementation studied was rolled back within days because its limits were global constants rather than task-derived.

docs/DESIGN.md — — recorded evidence: no test
Execution telemetry will be recorded once a provider exposes it honestly

Nothing measures token spend today. Any estimated_ field is labelled and excluded from enforcement.

docs/DESIGN.md — — recorded evidence: no test
Majordomus will state a total-token change only from matched, verified runs that meet the methodology's publication rule

The benchmark, its runner and its calculator exist; what is missing is enough evidence. The generated report states what the recorded runs show and why no claim follows yet.

docs/ECONOMICS.md — — recorded evidence: no test
Cost per accepted outcome will be reported from measured data only docs/DESIGN.md — — recorded evidence: no test
Routing recommendations will be derived from measured cost and outcome data

Recommendations only. Majordomus does not invoke a model.

docs/DESIGN.md — — recorded evidence: no test
One policy will be shared across repositories and workers docs/DESIGN.md — — recorded evidence: no test
A milestone will be closed on GitHub only once the intents it realises are satisfied

The GitHub projection closes a milestone from its derived plan status alone and reads no intent. Closed work the evidence contradicts is refused by the intent-realization gate, not by the tracker.

docs/PLANNING.md — — recorded evidence: no test
A criterion settled by a command or by a deployment will be met by recorded evidence

Such a criterion resolves and reads `not_derivable`, and never meets a criterion by itself, because the evidence ledger records runs of tests and claims only. An intent whose criteria are all of these kinds cannot be satisfied today.

docs/PLANNING.md — — recorded evidence: no test
No pull request can be merged into the default branch while the verdict status is red, because a branch rule requires it

Named so the omission is visible rather than implied by the claim above, which asserted it. Measured on 2026-09-11 - `gh api repos/korczis/prismatic-majordomus/branches/master/protection` answers 404 Branch not protected and `gh api repos/korczis/prismatic-majordomus/rulesets` answers []. There is therefore no required status on master and every gate in this repository is advisory at the merge, which is why a red trunk can persist, why derived data can go stale across a server-side merge (docs/CI.md records both), and why the honest reading of a green run is "nothing objected" rather than "nothing could object". Closing this is a repository-settings act an operator takes, not a change to this tree; it is recorded here because a promise nobody keeps is worse when nobody can see it is unkept.

docs/CI.md — — recorded evidence: no test

rejected

claimsourceimplementationtestrecorded evidence
Ranked or semantic retrieval over durable records is deliberately not implemented

The corpus is a handful of Markdown files and one JSONL. An index or an embedding store would be a second source of truth that has to be kept in step with the first, and a scan is faster than that problem. Revisited only if a repository appears where the scan is measurably too slow.

docs/CONTINUITY.md — — recorded evidence: no test
A task record has no dependencies; sequencing between sessions is not represented

Refused for task records, and only for them. Issues and milestones do carry dependencies, validated as two graphs — see docs/ROADMAP.md and docs/PLANNING.md. What stays refused is a task, the record of one session's work, waiting on another; one task is active per checkout and coordination between checkouts is the scope overlap report. The original wording said no dependency graph existed anywhere, which stopped being true when the plan model landed.

docs/DESIGN.md — — recorded evidence: no test

Searches the titles and descriptions of every page this site publishes.