The prompt that started the work is the one thing nobody keeps. It lives in a provider transcript that the next session cannot read, so six weeks later the repository holds the change and no record of what was asked for.
setup: capture-installed
given:
- 'Majordomus installed, with the provider hook written and the executable on PATH'
steps:
- id: is-it-real
run: ['capture', 'status']
note: 'the state is read from the provider configuration and a payload driven through the shim, not from a flag somebody set'
expect:
exit: 0
stdout_contains: ['claude-code', 'majordomus-capture']
- id: one-prompt-one-record
run: ['capture', 'prompt', '--provider', 'claude-code']
stdin: claude-code-prompt.json
note: 'what the provider sends on UserPromptSubmit; the record is the person half of the exchange'
expect:
exit: 0
files_exist: ['.ai/local/prompts']
- id: both-formats-or-neither
run: ['capture', 'render']
note: 'a prompt is kept twice under one stem: the record for a machine, the rendering for a person. render rebuilds a missing rendering from its record and never the other way round, because only that direction is possible'
expect:
exit: 0
stdout_contains: ['rendering']
- id: never-rejects-a-prompt
run: ['capture', 'prompt', '--provider', 'claude-code']
stdin: not-a-payload.txt
note: 'in a hook, exit 2 rejects the person prompt, so a payload the tool cannot read is written to the failure log and the turn goes on'
expect:
exit: 0
files_exist: ['.ai/local/prompts/.capture.log']
- id: a-subcommand-that-is-not-one
run: ['capture', 'nonsense']
note: 'the command line still refuses what a person typed wrong: only the hook path is forbidden to exit 2'
expect:
exit: 2
stdout_contains: ['unknown subcommand']
then:
- 'the archive is written under the ignored half of the layer and never committed'
- 'every prompt is present as both a JSON record and its Markdown rendering'
- 'a prompt the tool could not read is a named failure, not a silence'
The prompts are captured below the model, by the provider hook, into files the repository ignores and nothing loads into a context. Whether the wiring is real is answered by running a payload through it rather than by a claim, and a payload that cannot be read is reported instead of dropped.
Each prompt is kept twice under one stem, because the two readers are different. The .json record is what the provider sent — its own spans, still escaped, pretty printed one member per line — and it is what everything else is derived from. The .md beside it is that record rendered for a person: the fields as front matter and again as a table, then the prompt itself under ## PROMPT, decoded and fenced. An archive nobody opens is evidence of nothing, and a rendering nothing can be rebuilt from is not evidence at all, so the pair is enforced rather than left to habit. The asymmetry is what makes that safe: capture render rebuilds any rendering from its record, and no command can go the other way.
-
the state is read from the provider configuration and a payload driven through the shim, not from a flag somebody set
agents unsupported none as above: a convention hands nothing to anything.
agents:session unsupported none not a product but a class of tools defined by the file they read (agents.md). A convention has no process and no events; the concrete tool's own entry in this file is what says what it can do.
bb unsupported none same search, same date, same caveat: nothing published hands a prompt to an external command before the model runs. Because bb runs Claude Code and Codex as child processes, each child's own prompt hook fires underneath it — the coverage is the agent's, never bb's.
bb:session unsupported none no conversation-lifecycle hook is published. Searched <date>: getbb.app; github.com/get-bb/bb docs/ (25 files), plugins/ (31 plugins), README.md; docs/configuration.md ('Repository worktree hooks') and docs/cli-guide-and-skill.md document only .bb-env-setup.sh / .bb-env-teardown.sh, which fire on environment provisioning and removal. docs/provider-plugin-api.md names an internal system/interaction/lifecycle event stream with no external-command contract. The @get-bb/plugin-sdk surface is rendered in-app and not published, so this is an absence of documentation, not a documented absence. ADR 0024 reached the same conclusion: the agent bb runs keeps the hook.
claude-code verified hook .claude/hooks/majordomus-capture is wired, and a synthetic payload through it produced one record and its rendering
claude-code:session verified hooks .claude/hooks/majordomus-session-start, .claude/hooks/majordomus-session-end, .claude/hooks/majordomus-session-compact are wired, and a synthetic payload through the end shim reached the command
codex unadapted hook the provider documents a prompt event and this distribution ships no adapter for it — https://learn.chatgpt.com/docs/hooks — UserPromptSubmit, 'Runs when a user submits a prompt, before model invocation, and can block the prompt'; the raw text arrives as `prompt` (not `prompt_text`, as Claude Code sends it). Verified <date>.
codex:session unadapted hooks the provider documents session events and this distribution ships no adapter for them — https://learn.chatgpt.com/docs/hooks (canonical; developers.openai.com/codex/hooks 308-redirects to it) — SessionStart (source: startup, resume, clear, compact), SessionEnd, PreCompact and PostCompact; PreCompact can block with continue:false. Configured in ~/.codex/hooks.json or an inline [hooks] table in .codex/config.toml. Verified <date>. The `notify` key is NOT this: https://learn.chatgpt.com/docs/config-file/config-advanced says it fires 'whenever Codex emits supported events (currently only agent-turn-complete)' — after a turn, never at a session boundary.
gemini unadapted hook the provider documents a prompt event and this distribution ships no adapter for it — https://github.com/google-gemini/gemini-cli/blob/main/docs/hooks/reference.md — BeforeAgent, 'Fires after a user submits a prompt, but before the agent begins planning'; the raw text arrives as `prompt`. BeforeModel fires later, on the assembled request. Verified <date>.
gemini:session unadapted hooks the provider documents session events and this distribution ships no adapter for them — https://github.com/google-gemini/gemini-cli/blob/main/docs/hooks/reference.md and https://geminicli.com/docs/hooks/ — SessionStart (source: startup, resume, clear), SessionEnd (reason: exit, clear, logout, prompt_input_exit, other), PreCompress (trigger: auto, manual). Configured as a hooks block in settings.json. Tracker github.com/google-gemini/gemini-cli/issues/9070 'Comprehensive Hooking System' is closed, 34 of 34. Verified <date>. Two caveats an adapter must carry: the CLI does not wait for SessionEnd, and PreCompress is advisory and cannot block.
generic unsupported none no vendor documentation is involved: a worker with no convention of its own exposes no event, and the MCP connection cannot stand in. An MCP server receives initialize, tool calls and notifications; the person's prompt is never among them, in any version of the protocol (modelcontextprotocol.io/specification). Declared none rather than left blank so that no surface can imply otherwise.
generic:session unconfigured connection bin/majordomus-mcp does not exist, so no client can start the shared server whose connection is the boundary
(no adapter) n/a this tool ships no capture adapter for: agents bb codex gemini generic; they read the model and lose the automation
- exit 0
- stdout_contains /claude-code/
- stdout_contains /majordomus-capture/
-
what the provider sends on UserPromptSubmit; the record is the person half of the exchange
- exit 0
- file_exists .ai/local/prompts
-
a prompt is kept twice under one stem: the record for a machine, the rendering for a person. render rebuilds a missing rendering from its record and never the other way round, because only that direction is possible
0 rendering(s) written into .ai/local/prompts
- exit 0
- stdout_contains /rendering/
-
in a hook, exit 2 rejects the person prompt, so a payload the tool cannot read is written to the failure log and the turn goes on
- exit 0
- file_exists .ai/local/prompts/.capture.log
-
the command line still refuses what a person typed wrong: only the hook path is forbidden to exit 2
majordomus: capture: unknown subcommand 'nonsense' (prompt|render|reconcile|prune|session|install|status)
- exit 2
- stdout_contains /unknown subcommand/
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.
Computed from what they share: claims, rules, commands, category, applications. Nobody maintains this list.