Skip to content

majordomus checkpoint

Record compact progress inside the active task, so the next worker does not start over.

Record compact progress inside the active task, so the next worker does not start over.

  • state-mutating
  • continuity
  • active task required

A progress note, not a report. A body over the policy's line cap is refused with the advice to write a handover instead, and identity fields in the body are rejected because they are computed from git. --derive composes the body from git and the ledger instead of reading stdin, so a hook can record one without a worker typing it.

Syntax

majordomus checkpoint [--derive] < body.md

Lifecycle

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

Other commands at this stage:context, decision, question, prompt, evidence, skills, capture.

Reads

  • .ai/repo/policy.yaml
  • .ai/local/state/current.yaml

Writes

  • .ai/local/state/checkpoints/
  • .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
  • 15 refused

A progress note, not a report

A checkpoint is what the next worker would need if you stopped now. It is capped in length on purpose: past the cap you are writing a handover, and it says so.

Recording progress

active task
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

the parser now refuses tabs; the message names the column

Run

$ majordomus checkpoint < body.md

Output contains

  • .ai/local/state/checkpoints/

exit 0

The file is written atomically at mode 0600 and is never staged. Its name carries the timestamp, branch and head, so a record can be placed without opening it.

With no active task

active task
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

…and this body on standard input

the parser now refuses tabs; the message names the column

Run

$ majordomus checkpoint < body.md

Output contains

  • .ai/local/state/checkpoints/

exit 0

A checkpoint belongs to the episode, not to a task. The record is written and its `task_id` is `none`, which is a fact a later reader can place; the ledger event it emits carries no `task_id` at all, because every reader of that event collects the field's distinct values as task identifiers. This scenario expected exit 12 until ADR 0052: refusing here meant that finishing a task silenced progress records for every episode after it, which is exactly what happened to this repository for six days from 2026-09-05.

Reference

Record compact progress inside an active task. Append-only; the body arrives on stdin.

A checkpoint is not a small handover. A handover is a deliberate continuation package written when a worker stops; a checkpoint is what was true a moment ago, short enough that the next worker's context can quote it whole. checkpoint.max_body_lines in the policy enforces that difference — a body over the cap is refused with the suggestion to write a handover instead, rather than truncated.

Writes: state/checkpoints/<ts>--<branch>--<head>--<rand>.md, mode 0600, created atomically with link, never staged. Front matter is computed exactly as for a handover; a body containing identity fields is refused. Also updates checkpoint_at on the task record and appends task.checkpoint to the ledger.

An empty body is allowed and writes no file: it updates checkpoint_at only, which is what check --checkpoint has always done. The two are the same operation; checkpoint is the one that can also say what was true.

  • --derive composes the body from git and the ledger instead of reading stdin: the commit, the working tree, how many files have changed since the task started, the commits since then, and the count of blockers. It stays inside the same cap, and it is what the provider's compaction event runs — a compaction discards the conversation while the work continues, and the moment it is announced is the only moment anything can be written about a context that is about to stop being reachable.
  • --show prints the newest checkpoint for this worktree and branch — for the active task when there is one, and otherwise the newest whatever task it names.
  • --list lists this worktree's checkpoints, newest first, with each one's git label.

A checkpoint belongs to the episode, not to a task. It is written whether or not a task is open and whatever outcome the last one reached. It relates the active task unless a different, still-open episode opened that task (ADR 0164); then its Markdown task_id is none, its ledger event omits task_id, and it leaves the task's checkpoint_at alone. check --checkpoint uses the same rule. A derived checkpoint uses that same prepared relation for its body; it cannot describe another episode's task as its own.

This refused with 12 (no active task) and 15 (the task is no longer active) until ADR 0052. Those two refusals are the 2026-09-05 outage: a task marked handed_over and never replaced silenced this repository's progress records for six days, while episodes went on opening and closing and every health check passed.

Exit 10 when the body carries identity fields or exceeds the cap.

$ majordomus checkpoint <<'EOF'
OAuth state mismatch reproduced with the fixture in test/fixtures/callback.json.
Cause is in callback normalisation, not in the comparison.
Next: regression test before touching the implementation.
EOF
.ai/local/state/checkpoints/20260903T194500Z--main--3f2a9c1--8c1d0e4a2b6f9317.md

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

Searches the titles and descriptions of every page this site publishes.