Skip to content

majordomus handover

Write an append-only continuation record, and resolve the most relevant prior one.

Write an append-only continuation record, and resolve the most relevant prior one.

  • state-mutating
  • continuity
  • active task optional

Resolution is scoped to this worktree and branch and reports absence rather than returning a stale match from elsewhere. The body carries the sections the policy requires and no transcript. --derive composes those sections from the task record, the ledger, git and the open questions, so the record exists whether or not a worker writes one.

Syntax

majordomus handover [--derive] [--close] [--no-task] < body.md

Lifecycle

  1. Set up
  2. Begin
  3. Work
  4. Verify
  5. Conclude
  6. Inspect

Other commands at this stage:finish, adr.

Reads

  • .ai/repo/policy.yaml
  • .ai/local/state/current.yaml
  • .ai/local/state/handovers/
  • .ai/local/state/ledger.jsonl

Writes

  • .ai/local/state/handovers/
  • .ai/local/state/current.yaml
  • .ai/local/state/ledger.jsonl

Exit codes

Only the codes this command can produce. The full contract is on the commands index.

  • 0 ok
  • 2 usage
  • 10 contract unmet
  • 12 missing artifact
  • 13 internal error

The record that survives the session

A handover is the one artifact designed to be read by someone who was not there. It carries the sections the policy requires and no transcript.

A body with every required section

all required sections present
yes

Given this repository

# ... and an active task scoped to lib, with work done inside that scope.
. "$FIXTURE_SETUP/installed.sh"
"$MJ" start "narrow the parser" --scope lib >/dev/null
echo work >> lib/a

…and this body on standard input

# Objective
narrow the parser
# Current State
tabs are refused with a message
# Next Action
none

Run

$ majordomus handover < body.md

Output contains

  • .ai/local/state/handovers/

exit 0

Objective, Current State and Next Action. The front matter — task, branch, head, worktree — is computed, so the body cannot disagree with git about where it was written.

A body missing a required section

all required sections present
no

Given this repository

# ... and an active task scoped to lib, with work done inside that scope.
. "$FIXTURE_SETUP/installed.sh"
"$MJ" start "narrow the parser" --scope lib >/dev/null
echo work >> lib/a

…and this body on standard input

# Objective
narrow the parser

Run

$ majordomus handover < body.md

Output contains

  • missing or empty section

exit 10

The sections are named in the refusal. A handover without a Next Action is the failure mode this record exists to prevent, so it is not accepted and quietly filed.

Reference

Write an append-only continuation record.

Record resolution — choosing which prior record is about the work happening here — is the same rule for handovers, checkpoints and context: same worktree and branch, else same branch, else nothing.

Input: the authored body on stdin. Required sections are the policy's handover.required_sections as level-one headings, each with non-empty content; template placeholders in angle brackets count as empty. Optional: # Completed, # Decisions, # Verification, # Risks, # Open Work.

--derive composes the body instead of reading stdin, from the task record, the ledger, git, the open questions and the newest checkpoint — each already written, already validated and already identity-checked. It calls no model and makes no network request, which is what makes it safe for a provider hook to run: a derived body can be wrong only if a record it reads is wrong, and every one of those has its own gate. The policy's required sections are emitted in the policy's order; a required section this generator cannot fill is refused by name rather than written empty, because an empty section passes the section gate and tells the next worker nothing. An authored body still says more than a derived one, and --derive exists so that the absence of somebody willing to type is no longer the same thing as the absence of a record.

Writes: one new file state/handovers/<utc-ts>--<branch-key>--<short-head>--<rand>.md, mode 0600, created atomically (temp file, then hard link; retry with a new random suffix on collision). Front matter is computed from git and from current.yaml; a body that tries to set identity fields is rejected.

Never stages, commits, or modifies any other file except the task record's checkpoint_at. Appends task.handed_over to the ledger. Prints the path.

--close additionally sets the task's outcome to handed_over, so that a new task may start in this checkout; the old record is archived by that start. Without --close the task stays active for the next session to continue.

The provider's SessionEnd event runs --derive --close when the task is still active, so an episode that ends leaves a continuation record and not only the envelope of what it produced. The two answer different questions: the session record indexes the episode, and a handover is what the next worker resumes from.

Refuses (10) if a required section is missing or empty, or if the body contains an identity field. Refuses (12) with no active task unless --no-task.

Resolve: majordomus handover --resolve finds the most relevant prior handover for the current worktree and branch: same worktree and branch first, then same branch, never a repository-wide fallback. Prints its git-state label and its body. No candidate is a normal outcome and exits 0 with No relevant handover. --path prints the path alone, for scripting; --no-task resolves without an active task.

Evidence

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.

Guarantees this command carries