This page was translated automatically from English, the only language this site is written in. Read the original.
majordomus capture
Record the person's raw prompts from a provider hook as a record with its renderings, draw the episode boundary from the provider's own lifecycle hooks, install those hooks, reconcile and prune the archive, and report what each provider actually does here.
Record the person's raw prompts from a provider hook as a record with its renderings, draw the episode boundary from the provider's own lifecycle hooks, install those hooks, reconcile and prune the archive, and report what each provider actually does here.
generated-output-mutating
memory
--json
Every prompt is kept three times under one stem: the .json record is the provider's own spans, pretty printed and unchanged, the .md beside it is that record rendered for a person and the .yaml the same for a machine. 'capture render' rebuilds a missing rendering from its record and never writes a record. The schema identifier resolves to share/schemas/<vendor>/<name>/<name>.v<n>, as .schema.json for the record and .proto for the document. 'capture prompt' and 'capture session' never exit 2: they run inside a provider hook, where that exit code can reject the person's prompt. Neither writes to stdout on a start event, because the provider adds it to the model's context. install refuses to rewrite a configuration it did not write, and prints the entries to add. A record carries the episode it belongs to, the provider session, the repository and the worktree, with the provenance of that link: the mapping from a provider session to an episode exists only while the episode is open, so a record that did not carry it could never be attributed afterwards. 'capture reconcile' links a record from evidence that survived the close and marks the rest unlinked rather than guessing; 'capture prune' applies prompts.retention_max_days and prompts.retention_max_bytes by taking the body and keeping the record, its identity and a tombstone. Credential shapes are replaced before anything is persisted, and the archive is mode 0600.
Only the codes this command can produce. The full contract is on the commands index.
0ok
2usage
10contract unmet
12missing artifact
13internal error
15refused
Raw prompt history, captured below the model
A worker cannot record its own prompts: it never sees what the person typed, and a record that depends on a model choosing to write it is missing exactly the prompts that mattered. Capture runs in the provider's hook instead, and the state a provider is in is decided by running that hook, not by finding a file.
Before anything is wired
hook installed
no
Given this repository
# A repository with Majordomus installed and projections generated, and one commit of work.
"$MJ" init >/dev/null
"$MJ" update >/dev/null
mkdir -p lib docs
echo a > lib/a
echo d > docs/d
git add . && git commit -qm base
Run
$ majordomus capture status
Output contains
claude-code
unconfigured
exit0
An adapter exists for Claude Code, but this repository does not wire it, and the command says so rather than reporting an empty archive as success.
After the hook is installed
hook installed
yes
Given this repository
# Installed, with the prompt-capture hook in place and majordomus on PATH, which is how a
# repository that is not the tool's own checkout reaches it.
. "$FIXTURE_SETUP/installed.sh"
PATH="$(dirname "$MJ"):$PATH"; export PATH
"$MJ" capture install >/dev/null
Run
$ majordomus capture status
Output contains
verified
synthetic payload
exit0
verified is not the presence of a file. The command drove a payload through the shim the provider would run, into an archive of its own, and read the record back.
A provider whose prompts cannot be observed
adapter exists
no
Given this repository
# A repository with Majordomus installed and projections generated, and one commit of work.
"$MJ" init >/dev/null
"$MJ" update >/dev/null
mkdir -p lib docs
echo a > lib/a
echo d > docs/d
git add . && git commit -qm base
Run
$ majordomus capture prompt --provider codex
Output contains
no adapter
claude-code
exit12
Exit 12 is MISSING_ARTIFACT, and the message names the providers that do have one. Note what it is not: capture prompt runs inside a provider hook, where exit 2 rejects the person's prompt, so it never returns that code whatever is wrong.
Reference
Record the person's raw prompts from a provider hook — as a JSON record with a Markdown
and a YAML rendering of it — draw the episode boundary from the provider's own lifecycle
hooks, install those hooks, and report what each provider actually does in this repository.
Why a hook and not an instruction. A worker cannot be asked to record its own prompts.
It never sees the bytes the person typed, only what the provider assembled from them, and a
record written by a model is missing exactly the prompts that mattered: the first one of a
session, which arrives before any instruction has been read, and every one where the model
was busy doing what it was asked. A line in AGENTS.md is a request, not a mechanism, and
no behavioural test can prove a request was honoured. So capture happens below the model,
in the provider's own hook, where running it is the proof that it works.
capture install writes a shim per event and the matching entries in
.claude/settings.json, and refuses to overwrite any of them: majordomus-capture for
UserPromptSubmit, majordomus-session-start for SessionStart and
majordomus-session-end for SessionEnd. When the configuration exists and does not name
one of them — including a configuration this tool wrote before it knew about the lifecycle
events — the command prints the entries to add, one per missing event, and exits 15 rather
than rewriting a file it did not write. A shim finds the repository from its own location:
the provider substitutes its project directory into the command string textually, so nothing
in the environment names the repository, and the working directory a hook runs in is not
contracted.
capture prompt reads one JSON payload on stdin and writes one record. It never exits
2, because in UserPromptSubmit that exit code rejects the person's prompt, and a broken
archive must never cost someone their input; a payload it cannot read is written to the
archive's own log instead — and doctor fails while that log is non-empty, because a
capture that failed means prompts were lost. That log is the only thing that can catch the
failure that matters: if the provider renames the field the prompt arrives in, the hook
still runs and the self test still passes, since it sends a payload of the tool's own
making. Read the log and delete it; it is a diagnostic, and unlike a record, nothing is lost
by removing it. The prompt text itself is the raw span from the payload, copied through
still escaped, so no decode and re-encode step can lose a character.
One prompt is two files under one stem, .ai/local/prompts/YYYYMMDDHHMMSS-<slug>.json
and .md, where the slug is the opening of the prompt itself:
Both formats, always, and they are not alternatives. The .json is the record: the
provider's own spans, still escaped, pretty printed one member per line — what everything
else is derived from and what capture must not lose. A record is read by people too, and a
single forty-kilobyte line is not something any editor or diff shows usefully; the
indentation lies outside the string spans, so the prompt's bytes are untouched by it. The .md is that record rendered for a person, in one shape
every rendering has: the fields as YAML front matter, the same fields again as a table, and
the prompt last under ## PROMPT, decoded and fenced.
Every case below runs in CI on Linux and macOS. The site refuses to build if a public command has no behavioural case, no negative case, or no demonstration.