○guaranteed
Getting started and adoptionbasicmaintainerwrites state
A repository installed Majordomus before the .ai/ layer existed and keeps its policy under .majordomus/. Every command refuses to read it, and the maintainer wants to know what will move before anything does.
setup: legacy-layout
given:
- 'project data under .majordomus/, the pre-.ai layout, and no manifest'
steps:
- id: plan-it
run: ['migrate', '--dry-run']
note: 'every move named, nothing written'
expect:
exit: 0
stdout_contains: ['^migrate: \.majordomus/ \(pre-\.ai layout\) -> \.ai/', 'move \.majordomus/policy\.yaml -> \.ai/repo/policy\.yaml']
- id: do-it
run: ['migrate']
note: 'the files move, the old directory is backed up, the manifest is written'
expect:
exit: 0
stdout_contains: ['^migrated: \.ai/ is the layout']
- id: nothing-left
run: ['migrate']
note: 'a second migration says the layer is already there and moves nothing'
expect:
exit: 0
stdout_contains: ['^already migrated: \.ai/manifest\.yaml is present']
then:
- 'the migration is explicit, dry-runnable and backed up'
- 'afterwards every command reads the layer and refuses the old path by name'
migrate --dry-run lists every move; migrate performs it with a backup and writes the manifest; a second run says there is nothing to migrate, and doctor proves the layer is real.
The scenario
Executed against the real tool, in a repository prepared by legacy-layout, every time this site is generated. What follows is not a description of what should happen; it is what happened.
given
- ·project data under .majordomus/, the pre-.ai layout, and no manifest
when
- 1
majordomus migrate --dry-run - 2
majordomus migrate - 3
majordomus migrate
then
- ·the migration is explicit, dry-runnable and backed up
- ·afterwards every command reads the layer and refuses the old path by name
What the tool did
Evidence of the run this page was generated from: pass, 3 step(s). Output is normalised: the repository's path, timestamps, ids and durations are placeholders; nothing else is edited.
Tested by test/cases/94_use_cases.sh, which runs every scenario and refuses a step whose recorded output is not what it produced.
-
every move named, nothing written
migrate: .majordomus/ (pre-.ai layout) -> .ai/ (ai-repository/v1)
move .majordomus/policy.yaml -> .ai/repo/policy.yaml
move .majordomus/profiles/README.md -> .ai/repo/profiles/README.md
move .majordomus/profiles/debugging.yaml -> .ai/repo/profiles/debugging.yaml
move .majordomus/profiles/deep-work.yaml -> .ai/repo/profiles/deep-work.yaml
move .majordomus/profiles/implementation.yaml -> .ai/repo/profiles/implementation.yaml
move .majordomus/profiles/routine.yaml -> .ai/repo/profiles/routine.yaml
move .majordomus/project/README.md -> .ai/repo/project/README.md
move .majordomus/prompts/README.md -> .ai/repo/prompts/README.md
move .majordomus/prompts/continue.md -> .ai/repo/prompts/continue.md
move .majordomus/prompts/debug.md -> .ai/repo/prompts/debug.md
move .majordomus/prompts/handover.md -> .ai/repo/prompts/handover.md
move .majordomus/prompts/review.md -> .ai/repo/prompts/review.md
state .majordomus/state/decisions.md -> .ai/local/state/decisions.md
state .majordomus/state/ledger.jsonl -> .ai/local/state/ledger.jsonl
state .majordomus/state/open-questions.md -> .ai/local/state/open-questions.md
seed the rest of .ai/ from the skeleton: protocol, manifest, vendored rules, knowledge, workflows
state backup first: tmp/majordomus-migrate-backup/<utc>/state/, byte for byte; then moved, and taken out of the index
then .ai/local/ appended to .gitignore once; majordomus update --force; majordomus doctor
dry run: nothing written
- exit 0
- stdout_contains /^migrate: \.majordomus/ \(pre-\.ai layout\) -> \.ai//
- stdout_contains /move \.majordomus/policy\.yaml -> \.ai/repo/policy\.yaml/
-
the files move, the old directory is backed up, the manifest is written
migrate: .majordomus/ (pre-.ai layout) -> .ai/ (ai-repository/v1)
move .majordomus/policy.yaml -> .ai/repo/policy.yaml
move .majordomus/profiles/README.md -> .ai/repo/profiles/README.md
move .majordomus/profiles/debugging.yaml -> .ai/repo/profiles/debugging.yaml
move .majordomus/profiles/deep-work.yaml -> .ai/repo/profiles/deep-work.yaml
move .majordomus/profiles/implementation.yaml -> .ai/repo/profiles/implementation.yaml
move .majordomus/profiles/routine.yaml -> .ai/repo/profiles/routine.yaml
move .majordomus/project/README.md -> .ai/repo/project/README.md
move .majordomus/prompts/README.md -> .ai/repo/prompts/README.md
move .majordomus/prompts/continue.md -> .ai/repo/prompts/continue.md
move .majordomus/prompts/debug.md -> .ai/repo/prompts/debug.md
move .majordomus/prompts/handover.md -> .ai/repo/prompts/handover.md
move .majordomus/prompts/review.md -> .ai/repo/prompts/review.md
state .majordomus/state/decisions.md -> .ai/local/state/decisions.md
state .majordomus/state/ledger.jsonl -> .ai/local/state/ledger.jsonl
state .majordomus/state/open-questions.md -> .ai/local/state/open-questions.md
seed the rest of .ai/ from the skeleton: protocol, manifest, vendored rules, knowledge, workflows
state backup first: tmp/majordomus-migrate-backup/<utc>/state/, byte for byte; then moved, and taken out of the index
then .ai/local/ appended to .gitignore once; majordomus update --force; majordomus doctor
backup .majordomus/state/ -> tmp/majordomus-migrate-backup/<time>/state/ (byte for byte; verified)
WARN backup tmp/majordomus-migrate-backup/<time> — is not ignored by git; do not commit it [reproduce: git check-ignore -v tmp/majordomus-migrate-backup/<time>]
seeded
.ai/repo/README.md
.ai/repo/scope.yaml
.ai/repo/rules/README.md
.ai/repo/rules/project/README.md
.ai/repo/rules/vendor/majordomus/
.ai/repo/knowledge/
.ai/repo/knowledge/curated/README.md
.ai/repo/workflows/
.ai/repo/skills/
.ai/repo/adrs/
.ai/repo/sessions/README.md
moved 12 canonical file(s) into .ai/repo/, 3 state file(s) into .ai/local/state/, dropped 0 derived file(s), backed up and removed 0 provider file(s)
removed .majordomus/ (empty)
--- majordomus update --force
unchanged AGENTS.md
unchanged CLAUDE.md
unchanged GEMINI.md
generated 3 target(s) from policy 1791d4f72c77; each carries its own stamp
update: exit 0
--- majordomus doctor
OK knowledge graph — buildable here: 15 node(s), 0 edge(s), compiled into a temporary file and removed; no stored index to go stale
INFO knowledge links — 3 link(s) written in prose resolve to nothing in the graph; an author's link may point outside it, so none fails (majordomus knowledge validate lists them)
OK knowledge records — 0 record(s) valid against knowledge/v1; every reference resolves [reproduce: majordomus knowledge check]
OK knowledge candidates — none await review, cap 40
OK layout .ai/manifest.yaml — schema ai-repository/v1
OK layout .ai/repo/scope.yaml — version 1; 21 in pathspec(s); every key is one the schema declares
OK layout .ai/local/ — ignored, and nothing under it is tracked
OK layout .ai/ — every section the manifest names exists; the layer is readable without the tool
OK layout .ai/repo/prompts — present
OK layout .ai/local/state/handovers — present, 0 record(s)
OK layout .ai/local/state/checkpoints — present, 0 record(s)
OK records ledger.jsonl — every line is a well-formed event
OK policy 39 key(s) — every policy value the code reads is declared, with no reader-side default
OK bootstrap 3 projection(s) — each points at .ai/README.md, carries no rule of its own and names only rules in force
INFO catalogue - — the manifest names no use-cases section; nothing to resolve
OK context .ai/ — 13 context document(s) carry the contract; ids unique, references resolve, no final document superseded, no cycle [reproduce: majordomus context validate]
OK policy .ai/repo/policy.yaml — parsed, version 1
OK profiles 4 files — parsed; default 'implementation' exists
INFO adr .ai/repo/adrs/ — no decisions; nothing to validate
OK records decisions.md — every entry is attributable
INFO project .ai/repo/project — no canonical project model here; nothing to validate
OK command surface — 30 public command(s), reconciled against the dispatch table [reproduce: majordomus doctor]
OK doctrine 50 of 61 rule(s) carry a validator — validator, dispatch, propagation, test and CI resolve for every one of the 50; the remaining 11 declare no validator and this check does not examine them
FAIL wiring doctor-on-commit — hook .git/hooks/pre-commit does not exist [reproduce: ls -l .git/hooks/pre-commit]
FAIL wiring finish-on-push — hook .git/hooks/pre-push does not exist [reproduce: ls -l .git/hooks/pre-push]
OK command coverage — every public command is exercised and refuted [reproduce: bash test/run.sh 31_command_coverage]
OK resolver handovers — no record for this worktree and branch (absence, not a stale match) [reproduce: majordomus handover --resolve]
OK lifecycle activity — 0 episode(s) opened here; too few to judge whether the writers stopped
OK knowledge wiring — the close path and the compaction adapter call the deriver
OK knowledge deriver — no episode has closed here; nothing to judge
INFO project .ai/repo/project — no canonical project model here; nothing to validate
OK projection AGENTS.md — content matches its stamp
OK projection CLAUDE.md — content matches its stamp
OK projection GEMINI.md — content matches its stamp
OK budget AGENTS.md — 42 lines, budget 150
OK links AGENTS.md — all references resolve
OK counts AGENTS.md — no hardcoded counts
OK context builder — 21 lines, budget 300 [reproduce: majordomus context]
OK capture share/schemas/majordomus/capture/capture.v1 — 2 file(s) describe majordomus.capture/v1 at the path the identifier derives: the record as JSON Schema, the document as protobuf
OK capture .ai/local/prompts — ignored and untracked
OK capture .ai/local/prompts — no records yet; the archive is empty
OK capture .ai/local/prompts — every prompt is present as all three: the record, the Markdown and the YAML
OK prompts .ai/local/prompts — the archive and every record in it are readable by their owner only
OK prompts .ai/local/prompts — no records yet; the archive is empty
OK prompt 4 asset(s) — front matter valid, every token known
OK records open-questions.md — every entry parses
OK retention ledger — 3 lines, cap 5000
OK retention handovers — 0 files, cap 200
OK retention checkpoints — 0 files, cap 500
OK retention sessions — 0 records, cap 500
INFO project .ai/repo/project — no canonical project model here; nothing to validate
OK rules .ai/repo/rules/vendor/majordomus — every file matches the manifest (1 (1.0.0-alpha.8))
OK rules 61 rule(s) — resolve in one deterministic order; vendored baseline plus project rules, no override — 51 carry a validator, 10 are normative text this tool does not check
OK rules blocking — all 44 blocking rule(s) carry a validator
OK schema rules — 61 rule(s) resolve in one order: the vendored baseline in manifest order, then the project's, as a dependency graph
OK schema share/kinds.yaml — 37 kind(s); every one that carries metadata declares a schema
OK schema share/schemas — 54 schema(s), each valid JSON and each applied — by a kind, through an allow-list the tool reads, by a validator that names it, or as a generated document's contract (15)
WARN session .ai/repo/sessions/ — 1 record(s) exist here and are committed nowhere, so they reach no surface: README.md [reproduce: git add .ai/repo/sessions && majordomus derive]
OK records 0 record(s) — every changed_files names work, not generated or local state
OK session .ai/local/session-contexts — ignored and untracked
OK session .ai/local/session-contexts — no working contexts yet; the store is empty
INFO skill .ai/repo/skills/ — no skills; nothing to validate
OK skill reachable — no personal command or skill shares a name with a projected skill [reproduce: majordomus run editors.status]
INFO use-case - — no use-cases section in the manifest; nothing to cover
INFO env - — bash <version>, git <version>, jq <version>, shellcheck present
WARN clone unpushed — commits on no remote: main(3) — work on one disk is invisible to every other session and cannot be integrated [reproduce: git push -u origin <branch>]
OK clone uncommitted — no linked worktree, so no uncommitted work can sit in one
INFO clone trunk — no remote-tracking branch, so this checkout's distance from the remote trunk cannot be measured
INFO clone release — no remote-tracking branch, so whether the trunk's landed work is released cannot be measured
· reasoning <decided by the advisors and the executable of the recording machine>
· layout - — <judged when the recording machine has built the executable>
· budget doctor — <timed against the policy budget>
doctor: 2 failure(s)
doctor: exit 10
migrated: .ai/ is the layout; review with git status, then commit the tracked half
- exit 0
- stdout_contains /^migrated: \.ai/ is the layout/
-
a second migration says the layer is already there and moves nothing
already migrated: .ai/manifest.yaml is present and .majordomus/policy.yaml is not
- exit 0
- stdout_contains /^already migrated: \.ai/manifest\.yaml is present/
Why the tool knows this
The chain from this page to the code: the use case names commands, rules and claims; each rule is dispatched from the registry and each claim names the test that proves it; the scenario above executed the commands. Nothing in this chain is prose about intent.
The rules that make it hold
Guarantees exercised
Related use cases
Computed from what they share: claims, rules, commands, category, applications. Nobody maintains this list.
- Decide what belongs in the AI context, as dataRead the repository’s declared scope policy, see which paths are in and out of the index, and let doctor and the knowledge index apply it rather than a hardcoded list.
- Trace a change to the context documents it affectsList the scoped context documents, resolve which apply to a path and why, and find out which documents a change set touches before it lands.
- Install the tool in one command, on a machine with no toolchainPaste one line, get a verified binary for this machine, and run majordomus init — no git, no Rust, no Node, no Python, no root, and no way to install an archive whose digest does not match.
- Extend what the executable serves by adding a file, never by rebuilding itAdd a rule, a prompt or a knowledge node to .ai/ and have it appear as an MCP resource, an HTTP route and a reference entry, with the same binary.
- Say what your work is for, once, and have every surface say itRecord an operational failure mode as one file and have the command line, the API, MCP and the website answer it without registering it anywhere.