Skip to content

Executable use cases

executable use cases: one file each under `.ai/repo/use-cases/`, the scenario that proves it against the tool, evidence, observed maturity, coverage gated by policy, impact analysis, scaffolding, and what the site derives from it

Rendered from docs/USE_CASES.md — the same Markdown GitHub shows.

A use case is a task somebody performs with Majordomus, written once as data and proved against the real tool. It is not a tutorial, not a feature list and not a page: it is one file under .ai/repo/use-cases/ whose front matter names the commands, rules, claims, responsibilities and applications it touches and carries a scenario, and whose body says what the situation is and what you are left holding. Every projection of it is derived: the page, the category it sits in, the links to and from it, the tally that says which capabilities have a use case, and the evidence a page shows. The rules behind it are majordomus.catalogue-integrity and majordomus.use-case-coverage in the vendored baseline, and the claims are use-case-coverage, use-case-evidence and use-case-impact in CLAIMS.yaml.

The principle, stated once: anything derivable is derived; anything not derivable has one canonical home; anything claimed as guaranteed is backed by executable evidence.

Where things live

whatwherewho owns it
a use case.ai/repo/use-cases/<id>.mdauthored; the manifest's use-cases section
the categories.ai/repo/use-cases/taxonomy.yamlauthored; presentation only
an application.ai/repo/applications/<id>.mdauthored; the manifest's applications section
the contractsshare/schemas/{use-case,application,taxonomy}.schema.jsonthe distribution; the shell allow-lists are generated from them
the prepared statestest/fixtures/commands/setup/<name>.sh, stdin/<name>the distribution's fixtures, shared with the command demonstrations
the evidence.ai/local/evidence/use-cases/<id>.jsonwritten by majordomus usecase run; never tracked
the site's copysite/data/generated/catalogue.jsonwritten by scripts/generate-site-data, which runs the scenarios itself
the pagessite/content/use-cases/**, site/content/applications/**derived; rm -rf'd and rewritten by the generator

majordomus init seeds the two sections with their context documents and the taxonomy, so the first use case of a repository is one file.

The use-case object

---
id: prove-a-rule-is-enforced          # the file name, stable, the URL slug
kind: use-case
title: 'Prove a rule is actually enforced, not merely written down'
summary: 'One sentence.'
category: policy                       # an id of taxonomy.yaml
status: active                         # active | draft | deprecated (authored)
target: guaranteed                     # guaranteed | advisory: what the author aims at
actors: [maintainer, reviewer]
difficulty: intermediate
commands: [doctrine, doctor]           # bin/majordomus must dispatch each
mcp_tools: [majordomus_repository]     # optional; the executable's registry must project each
doctrines: [majordomus.enforcement-wiring]   # rules with an x-majordomus block
claims: [dispatcher-wiring]            # ids in docs/CLAIMS.yaml
responsibilities: [doctor]             # ids in docs/RESPONSIBILITIES.yaml
applications: [ci-gated-project]       # each names this use case back
---

# Situation
...

# Scenario

```yaml
setup: installed                       # test/fixtures/commands/setup/installed.sh
given:
  - 'Majordomus installed; no git hook invokes the tool yet'
steps:
  - id: find-the-gap
    run: ['doctor']
    note: 'the enforcement the policy declares reaches no hook'
    expect:
      exit: 10
      stdout_contains: ['^FAIL wiring', 'doctor-on-commit']
then:
  - 'a declared enforcement that nothing invokes is a failure, not a green line'
```

# Outcome
...

What the object does not carry, because it is derived: a command's description or syntax, a rule's text, a claim's wording, an application's summary, captured output, the related use cases, the category's title or count, and the observed maturity. The schema refuses a key it does not declare, and majordomus usecase validate refuses a reference that does not resolve, a category the taxonomy lacks, a setup script or stdin body that does not exist, a scenario step that runs a command the use case does not list, a body without # Situation, # Scenario and # Outcome, an id that is not the file name, a duplicate id, and an active use case that targets guaranteed without a scenario.

Given, when, then

The scenario is the executable form of the narrative, and it is a body section rather than a header field: front matter says what an object is — its identity and its classification — and a twenty-line program is neither. # Scenario holds exactly one fenced yaml block, between # Situation and # Outcome, so that a reader meets the situation, the proof and the result in the order they happen.

setup names a prepared state (a shell script under the fixtures, shared with the command demonstrations, so what a use case starts from is what a command page shows); given says it in words. Each step is one real invocation of bin/majordomus, in a disposable repository, in order, in the same repository: run is argv, never a shell string; stdin names a body file; expect carries the exit code and, optionally, stdout_contains, stdout_not_contains, files_exist and files_contain (extended regular expressions over the combined output or a file). then says in words what the assertions proved.

Where a scenario runs

mode says which repository the scenario is about. It is fixture unless written down, and that is the whole of the paragraph above: a setup script, a disposable repository, committed evidence, reproduced by CI.

mode: live is the other one. A live scenario asks its questions of the repository the command was invoked in. It names no setup, because it prepares nothing, and it may run only commands share/commands.yaml declares class: read-onlyusecase validate refuses anything else, so a live scenario cannot change the thing it is asking about however it is written. Mutation stays with start, check and finish, which supervise it.

A live scenario may also carry a step that runs nothing:

  - id: the-cases-were-run
    obligation: tests

An obligation step asserts that one token of share/obligations.yaml has been discharged, through the same judgement check reads (mj_obligation_judge in lib/evidence.sh): established live where git or the published site can settle it, and read from the ledger where they cannot. It resolves to pass, unmet — owed and not discharged — or stale, meaning evidence exists and no longer describes this tree. Obligation steps are valid only in live mode; a disposable repository owes nothing.

This is what lets a class of work state what it owes. check judges the obligations one task declared at start; a task that declared none is told so, truthfully and uselessly. A live scenario says what a defect fix or a finished change owes whether or not anybody remembered to promise it, and because both read one judgement, a gate and a task's own contract cannot come to disagree about the same tree.

A live scenario's verdict is unmet, not fail. A failing fixture scenario is a defect in the tool; a live scenario with an outstanding obligation is work not done, and the two are not the same news. usecase run exits 10 for either — contract unmet — and counts and prints them apart.

majordomus usecase run                 # the fixtures, and only the fixtures
majordomus usecase run --live          # the live scenarios as well
majordomus usecase run know-whether-this-work-is-finished   # by name, whatever its mode

Live evidence lands under .ai/local/evidence/live/ and is never committed: it describes one tree, on one machine, at one minute, and a derived file whose content depends on who derived it is what ADR 5 forbids. For the same reason usecase coverage counts a live scenario as naming a command and never as covering it: a guarantee CI cannot re-run is not a guarantee. The decision is ADR 38.

Running and evidence

majordomus usecase run                     # every fixture scenario; exit 10 when a step fails
majordomus usecase run prove-a-rule-is-enforced --keep   # keep the repository for a look
majordomus usecase show prove-a-rule-is-enforced         # the file, and its last evidence

Each run writes .ai/local/evidence/use-cases/<id>.json: the use case, the setup, the result, and for every step the command, argv, stdin, exit code, expected exit code, the normalised output, every assertion with its result, and the timing. Normalised means the scenario repository's path, the tool's own path, the home directory, timestamps, task and session ids, record hashes and durations are replaced by placeholders; nothing behavioural is. Two runs of one scenario on one tool are byte-identical, which is what lets the site embed the evidence and --check prove it again.

Evidence is local state. The site generator does not read it: it runs every scenario itself, through the same command, and embeds the result in site/data/generated/catalogue.json. A page that shows what a command printed shows that; a pasted transcript has nowhere to go.

Maturity is observed

status is authored (active, draft, deprecated) and target is authored (guaranteed, advisory). What the use case is is computed when the site data is generated, from the evidence and from what it names:

observedwhen
draftstatus: draft
describedactive, no scenario
executableactive, a scenario, no passing evidence in this generation
verifiedactive, the scenario passed
guaranteedverified, and every claim it names is guaranteed, and every doctrine it names is enforced

Nobody writes guaranteed into a use case. A draft can be run (usecase run accepts it) and can be validated, and counts for nothing.

Coverage

majordomus usecase coverage [--json] [--check]

Every public command of the command registry, every guaranteed claim with a responsibility, and every MCP tool the executable projects is a target. For each, the active use cases that name it, the ones whose scenario runs it, and the ones with passing evidence are counted; the status is covered, partial (named, never run) or gap. The policy says what a gap means, per class:

use_cases:
  coverage:
    commands: required     # a gap fails doctor, check, finish and --check
    claims: advisory       # a gap is reported
    mcp_tools: advisory

The skeleton policy makes every class advisory: a fresh repository has no use cases and is told so, not failed. This repository requires every public command. The doctrine majordomus.use-case-coverage runs the same tally under doctor, check and, through the finish key use_cases_covered in verification.finish_requires, under finish: a required gap refuses completion, naming the capability and the scaffold command.

Impact

majordomus usecase impact [--base <ref>] [--json]

From the files changed since --base (the upstream by default) plus the work tree, the command names what is affected: the commands (a lib/<command>.sh, a responsibility's files, or everything when bin/majordomus, lib/common.sh, the registry, the manifest or the policy changed), the rules (by front-matter id), the use cases (their own file, their setup script, their stdin body, a command or doctrine they name, a claim when docs/CLAIMS.yaml changed, an MCP tool when the crate changed), the scenarios among them, and the behavioural cases that declare coverage of an affected command or that a rule names as its tests. The last line is the command to run next.

Scaffolding

majordomus usecase scaffold --missing              # one draft per required gap
majordomus usecase scaffold --for command:pack     # one draft
majordomus usecase scaffold --missing --dry-run

A draft is written to .ai/repo/use-cases/<command>-draft.md with what is already known: the command, a category from the command's lifecycle stage, the responsibility whose command it is, the guaranteed claims of that responsibility, and a scenario taken from the command's own fixture (its first scenario's setup, argv and expected exit). Everything else is TODO, status is draft and target is advisory. The draft validates and runs; it covers nothing until a person or an agent completes the narrative, tightens the assertions and sets status: active. Nothing here needs a model, and nothing a model writes is evidence.

What the site shows

The generator embeds, for every use case: the front matter, the body, the observed maturity, the evidence of its scenario (every step's command and normalised output), the resolved commands, rules, claims, responsibilities and applications, and the related use cases computed from what they share (same workflow of commands, shared claims, shared rules, shared commands, same category, shared applications, in that weight order; a use case may boost or suppress an id under related). The categories come from the taxonomy and carry their counts. /use-cases/ lists by category; /use-cases/<id>/ is the page; scripts/site-check refuses a page whose evidence, links or category do not resolve. Nothing on a page was typed into a template.

The gates, in one place

gatewherewhat it refuses
schemashare/schemas/majordomus/use-case/use-case.v1.schema.json, application, taxonomy; the Rust executable at index timea key nothing reads, a missing required field, a bad id
referencesmajordomus usecase validate; doctor under majordomus.catalogue-integritya command, rule, claim, responsibility, application, category, setup or stdin that does not resolve; a one-way application link
executionmajordomus usecase run; test/cases/94_use_cases.sh; the site generatora step whose exit code or output is not what the scenario says
coveragemajordomus usecase coverage --check; doctor, check, finish under majordomus.use-case-coveragea required capability no active use case runs
generated freshnessscripts/generate-site-data --check in CIsite data (evidence included) that differs from a clean regeneration
site integrityscripts/site-checka use-case page missing, a link that does not resolve, a category page without its members
impactmajordomus usecase impactnothing; it names what to run

Extending it

Add a file. Name what it touches. Give it a scenario. Run majordomus usecase validate, majordomus usecase run <id>, scripts/generate-site-data, and commit the regenerated data with it. There is no registry to edit, no index to update, no template to touch, no count to bump, and the related links of every other page recompute themselves.

Claims this document defines

Each links to its own page with implementation, test and where it is used.