Interactive markdown session. TRIGGER: user invokes /agent-doc <file>. Requires a markdown session document, installed CLI, and write+commit every cycle.
Installs into .claude/skills of the current project.
Are you the author of Agent Doc?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/btakita-agent-doc)
---
description: "Interactive markdown session. TRIGGER: user invokes /agent-doc <file>. Requires a markdown session document, installed CLI, and write+commit every cycle."
user-invocable: true
argument-hint: "<file>"
agent-doc-version: "0.35.519"
---
# agent-doc
Interactive document session — respond to user edits in a markdown document.
## Harness Compatibility
This shared hot path serves Claude Code, Codex, OpenCode, Cursor, and direct harnesses. Invocation wording is harness-specific; workflow and closeout are shared. Use [runbooks/harness-invocation.md](runbooks/harness-invocation.md) for harness details and [runbooks/commit.md](runbooks/commit.md) for closeout.
## Dynamic Context Map
Use this SKILL.md as the hot-path router. Load linked files only when their branch is active: invocation and harness drift → [runbooks/harness-invocation.md](runbooks/harness-invocation.md); preflight planning → [runbooks/planning-dispatch.md](runbooks/planning-dispatch.md); response and backlog updates → [runbooks/respond.md](runbooks/respond.md) plus [runbooks/pending-ops.md](runbooks/pending-ops.md); persistence and manual repair → [runbooks/persist-closeout.md](runbooks/persist-closeout.md) plus [runbooks/commit.md](runbooks/commit.md); context-authoring policy → [runbooks/dynamic-context.md](runbooks/dynamic-context.md); preset-associated procedure discovery/authoring → [runbooks/preset-runbooks.md](runbooks/preset-runbooks.md); durable concept definitions and vocabulary → [okf/index.md](okf/index.md). Do not copy runbook or OKF detail back into this file unless it is required every cycle.
## Invocation
```
/agent-doc <FILE>
/agent-doc claim <FILE>
/agent-doc compact <FILE>
/agent-doc compact exchange <FILE>
```
Arguments: `FILE` — path to the session document (e.g., `plan.md`).
**Note:** Slash commands (`/agent-doc`) are Claude Code-specific. Other harnesses receive the document path directly.
## Hot Path Digest
- **Document is the UI** — user edits ARE the prompt; respond in the document and console. The rule is hook-independent (`#chatprompt`, GH #140): OpenCode has no `UserPromptSubmit` hook, so its agent MUST self-apply the shared rule. A prompt the operator types in the harness chat instead (including a bare `prompt_presets` key such as `#upgrade`) is still a session turn (`#chatprompt`, GH #125): begin the `patch:exchange` response with `> **Chat prompt (#chatprompt):** <verbatim prompt>` and persist it through `respond` / `write --commit` with its queue/backlog mutations, never only in the chat transcript (on a pure chat turn `respond` reopens a cycle itself). The hook flags it as `[agent-doc] chat prompt for session document ...`; the next contract carries any still unrecorded in `chat_prompts` (with `no_changes: false`), and `session-check` warns `chat prompt not recorded` when a closeout skipped the record.
- **Harness-native `agent-doc` entrypoints start the binary-owned response cycle** — treat `/agent-doc <FILE>`, `agent-doc <FILE>`, or equivalent as executable workflow start, not a generic document-editing request. Do not manually patch the final assistant response into the document, and do not report success before the binary resolves and commits the turn through `agent-doc respond <FILE>` (`finalize` compatibility alias) or `agent-doc write --commit <FILE>`. In Codex, keep pre-closeout updates in the commentary channel: never emit the terminal `final` message before connected `agent_doc_finalize` succeeds (or the direct CLI closeout succeeds), because the Stop hook must otherwise create a same-turn continuation to preserve the write boundary. A semantically complete response checkpoint may already exist in Lazily/the document, but it is not proof that queue/backlog closeout or commit ran. Codex/OpenCode paths operating through direct exec also run `agent-doc session-check <FILE>` as the explicit distrust guard. Connected clients consume the terminal `agent_doc_finalize` result, including its queue-continuation fields, and do not invoke a second session check; the next turn's admit/preflight re-evaluates the same document integrity guards.
- **Imperative edits are executable directives** — `do #id`, `fix this`, `run tests`, `build + install`, `commit + push`, and similar edits authorize repo work. A document directive such as `go ...` / `push ...`, or a resolved preset whose effective body authorizes the action (including `#auth`), is already operator authorization for the work it names: do not ask the operator to say `go` / `push`, repeat the directive in chat, or invoke the preset again. This task authorization is distinct from a harness-native tool-permission prompt, which still requires the harness permission system's grant. **Focused extraction starts from document prose (`#focusedextractprompt`)**: when an edit says, for example, `Extract the legal concern into tasks/sdk-legal.md`, load [runbooks/transfer-extract.md](runbooks/transfer-extract.md), inspect the outline, exchange nodes, and managed components, author explicit semantic selectors/a selection manifest, and run the deterministic preview before applying. The imperative extraction request itself authorizes `--apply` after a clean preview; do not ask for duplicate semantic confirmation. When either document is attached to a capable JetBrains editor, use `--apply --coordinate-live-editors` to persist, freeze, detach, apply, and resume without GUI automation; if an endpoint lacks the advertised capability, present the concrete fallback to close the source/target tabs and retry. Exploratory wording such as `Can we extract ...?` authorizes only an answer or preview, and an ambiguous concern or destination requires one focused question. The operator never has to provide IDs, exchange-node handles, or a manifest; there is no `split` alias.
- **Project-scoped remote hosts** — globally approved SSH commands, ambient SSH config, and unrelated project history are not evidence that a named remote host belongs to the current document's project. Use a named remote host only when the current user prompt, this session document/frontmatter, project-local `.agent-doc/config.toml`, or project-local runbooks explicitly identify it; otherwise ask or record a follow-up to confirm the intended host.
- **Preflight is binary-owned (`#preflightinbinary`, `#hookcontractlost`)** — the turn context must contain the `[agent-doc] cycle contract (preflight already ran in the binary; ...)` success marker. Consume the contract sealed by that marker; never shell back to `agent-doc preflight <FILE>` or poll authority from the model turn. The hook always names its outcome on stdout, so read three states: that marker ⇒ admitted; `[agent-doc] cycle contract UNAVAILABLE (preflight admission failed; ...)` plus its `reason:`/`pending:`/`remedy:` lines ⇒ the hook ran and preflight refused, so report the reason, run `agent-doc session-check <FILE>` to relay any pending operator steering (`#refusalsteering`), and stop without recreating admission (a deadline refusal still permits the binary-owned repairs session-check names, `agent-doc backlog <FILE> requeue|done|keep-unqueued <id>`, never a response write; GH #129); neither ⇒ the hook never ran, a fail-closed harness-admission defect to repair.
- **Mid-turn steering arrives after tool calls (`#midturn-steering`)** — when tool output carries `[agent-doc] operator steering arrived mid-turn`, the operator edited the document while this turn runs. Act on each item by its `dispatch`: `address_now` (a new exchange prompt, an edit/removal of the queue item you are executing, or ordinary queue work explicitly sent with Run Agent Doc) → address it in THIS turn, adjusting or wrapping up the in-progress work; `drain_after_current` → acknowledge only, it is passively observed queue work that runs in queue order after the current item closes, never interrupt or interleave the current item; `subagent` → spawn a background subagent for it immediately (one per item), each in its own worktree outside the IDE-watched project when it touches a repo, never two subagents against one repo checkout, and keep working the current item yourself. Each item is surfaced once; do not re-answer prompts already committed. The hook keeps reporting after closeout while this harness turn still runs, and an idle owned pane is woken by its supervisor for settled, unclaimed steering (`#steeringwake`); a `subagent` item you already claimed never wakes it. Half-typed edits are held by the typing gate; an item marked `possibly_partial` was released by the max-hold, so act only on its unambiguous part and re-read it. Harnesses without the PostToolUse hook (OpenCode, Grok Build, Cursor) poll `agent-doc steering <FILE>` between long steps; to inspect without consuming, use `agent-doc steering --peek <FILE>`. Steering no hook delivered also arrives at the turn boundary as `[agent-doc] operator steering arrived during this turn and was not yet surfaced` in the `respond` / `write --commit` / `session-check` output: handle it before ending the turn or scheduling the next re-entry. Preflight lists new subagent-intent queue items under `queue_subagent_dispatch` (excluded from `selected_queue_prompts`): run each `claim_command`, then dispatch it to its own subagent instead of draining it inline. Preflight lists prompts covered by `<!-- agent:exchange subagents -->` under `exchange_subagent_dispatch`: dispatch each immediately without queue claim/release while the parent retains this response cycle and closeout. Under `<!-- agent:queue subagents -->` (`fan-out`, `=N`) you are the coordinator: dispatch every listed item each cycle, paste each entry's `authorization.subagent_prompt_preamble` verbatim at the top of its Agent prompt (the operator's preset/item/backlog authorization, `#waypostauthorization`; `agent-doc queue brief <FILE> --item <...>` re-prints it), keep long-running claims alive with `agent-doc queue claim ... --refresh --generation <N>`, subagents never `make install` or release and keep commit messages and temp files out of the shared scratchpad (`git commit -F -` or a file inside their own worktree), and immediately run `agent-doc queue report-complete ... --generation <N>` when each worker reports so the binary writes editor-visible feedback while retaining the claim. Integrate serially with ONE `make install` per batch, then persist each result in the same turn with the closeout that strikes its line (the verbatim `> **Queue prompt:**` echo or `--done <id>`), which also ends the claim. A generation-matched `agent-doc queue release ... --generation <N>` records that the worker reported (`#reportedrelease`): the line stays queued as `dispatch=persist_reported`, is never re-dispatched, and must still be persisted before the turn ends; `queue release --generation <N> --requeue` abandons it for a new worker. Harnesses without an Agent tool use `agent-doc orchestrate <FILE> --mode parallel`. When every remaining head is claimed or held, end the turn quietly. Full contract: [runbooks/respond.md](runbooks/respond.md) (`#queuesubagents`).
- **Subagent claims are attempt- and worker-fenced (`#queueclaimstate`, `#queueclaimworker`)** — save the generation printed by `queue claim`. A fresh claim displays `🟡` and means reserved/dispatching, not running. After the harness spawn succeeds and returns a concrete worker identity, immediately run the dispatch entry’s `start_command` (`agent-doc queue start ... --generation <N> --worker-id <WORKER>`); only that binary acknowledgement displays `🚧`. Later `queue claim ... --refresh --generation <N>` calls are heartbeats for an already-started worker and cannot promote `🟡`. Run `queue report-complete ... --generation <N>` when that exact worker reports; use `queue release ... --generation <N>` only for that attempt. A claim generation binds to at most one worker, one worker binds to at most one live claim in the document, and stale or conflicting acknowledgements are refused without mutation. Reported work displays `📥`, expired/lost work `⚠️`, and orphaned binary completion evidence that cannot bind to an attempt `🔄` (reconcile; never redispatch as fresh work). Once durable work evidence exists, a live row never regresses to bare; ordinary closeout removes proven integrated work. Never infer running from a label, claim, intended spawn, or process poll, and never reuse an old generation after redispatch. **Missed response repair is not child-agent dispatch:** when preflight/commit says the prior response is already committed and no new assistant body exists, recover through `agent-doc write --commit <FILE>` instead of rerunning an empty response cycle.
- **MCP auth / OAuth steps are sub-steps, not closeout boundaries** — after auth/browser approval, resume and still finish through the connected `agent_doc_finalize` boundary (or `finalize` / `write --commit` plus `session-check` on a direct-exec path).
- **Manual repo commits keep the session document on the finalize path** — stage and commit only the intended non-session repo files first, stop on any stage failure, verify the staged diff still matches the intended path set, then let `finalize` / `write --commit` own the session document.
- **Dispatch proof language must preserve scope** — when reading route diagnostics, `proof=accepted proof_scope=accepted_only` means pane-input acceptance only. Do not describe Claude Code/OpenCode dispatch-only routes as consumed/submitted unless logs show dispatch-start proof.
- **Starting actor reroutes are prompt-gated** — if route diagnostics mention a `starting` authoritative actor, treat dispatch as valid only after the live pane shows a harness-specific dispatch-ready prompt; otherwise the route path must fail closed before input.
- **Rare routing / tmux / startup-miss invariants live in runbooks** — consult [runbooks/harness-invocation.md](runbooks/harness-invocation.md), [runbooks/commit.md](runbooks/commit.md), and [runbooks/code-enforced-directives.md](runbooks/code-enforced-directives.md) for route/start/sync/session-check or sibling `src/tmux-router` work.
- Preserve user edits through Lazily-owned semantic response checkpoints and the final seal. Operator-visible document text is authoritative: never recover, patch, or hook-closeout by replacing it with `content_ours`, a snapshot, or a lazily visible-write receipt if that would drop operator text. Snapshots and incomplete token captures are backup/audit state, not hot-path authority; fail closed or retry through the editor instead. Checkpoint only complete `### Re:` sections with balanced fences/markers; stream incomplete progress only to the console.
## Workflow
### 0. Pre-flight
Detect subcommands before the normal workflow:
- `claim <FILE>` → run `agent-doc claim <FILE>` via Bash and stop.
- `compact <FILE>` → run `agent-doc compact <FILE> --commit` and stop.
- `compact exchange <FILE>` → follow [runbooks/compact-exchange.md](runbooks/compact-exchange.md) and stop.
**Auto-update skill:** Compare `agent-doc --version` to `agent-doc-version`. If newer, run `agent-doc skill install --harness claude --reload restart` unless `agent_doc_auto_compact` is explicitly set in frontmatter or `.agent-doc/config.toml`. On `SKILL_RELOAD=restart`, ask the user to restart Claude Code and re-invoke `/agent-doc <FILE>`, then stop. Use `--reload compact` and ask for `/compact` only when that explicit opt-in exists. If already up to date, treat as stale instruction drift, continue this turn, and use the installed Claude skill. If `agent-doc` is missing or versions match, skip. See [runbooks/harness-invocation.md](runbooks/harness-invocation.md).
**Preflight runs in the binary (`#preflightinbinary`)** — the `UserPromptSubmit` hook runs it when the `agent-doc <FILE>` trigger arrives, so the contract is already in context and sealed by the trailing `[agent-doc] cycle contract ...` success marker. Do **not** run `agent-doc preflight <FILE>` from the model turn. A missing marker is a fail-closed harness-admission defect, not a fallback path. Preflight owns recovery before diffing and prints the cycle contract: `no_changes`, `warnings`, `claims`, `slash_commands`, `builtin_commands`, `orchestration_request`, `prompt_presets_requested`, tier/model fields, `agent_model`, `diff_type`, and the diff contract.
- If `no_changes: true` → tell the user nothing changed and stop — unless this harness turn carries an operator prompt absent from the document (chat text after the trigger): that cycle is not idle, so record and answer it per `#chatprompt`. A non-empty `chat_prompts` is never `no_changes`: record and answer each one. Relay `no_changes_explanation.guidance` (which source preflight read, and any `waiting_queue_items` in a stopped queue) instead of guessing a cause; never tell the operator to save when `read_source` is `live_editor`.
- Surface any `warnings`; they are advisory and do not stop the cycle. For `harness_mismatch`, note that the document-declared agent differs from the active harness and continue with the active harness attribution/closeout path. For `stale_install`, continue the document task without rebuilding; only the cycle that owns development/release of this repository runs `make install`, and the supervisor owns the safe recycle. For `shadow_open_backlog_repair` (`#rundocsstop`), preflight admitted this turn over open `[#id]` rows outside `agent:backlog` because the operator's edit is its prompt: answer that edit, and in the same turn move each listed row back into `agent:backlog` or mark it complete.
- Print any `claims` to the console as a record.
- The cycle baseline is binary-owned: preflight captures it into `state.db` cycle state at a stable post-commit point, and `respond` / `write --commit` read it from there. There is no `--baseline-file` flag on any response-persistence command — do NOT pass one, and do NOT save your own baseline.
- First cycle only: if the document is not yet in context, run `agent-doc read <FILE>` to fetch HEAD content. Do NOT read the snapshot file directly.
### 0b. Slash Commands
Handle non-empty `slash_commands` or `builtin_commands` before responding. Claude Code invokes `slash_commands` via the `Skill` tool; other harnesses skip them. For `builtin_commands`, tell the user to run the command at the terminal. Trust preflight; do not re-validate fences or blockquotes. If `orchestration_request` is non-null, run `agent-doc orchestrate <FILE> --mode <orchestration_request.mode> --from-exchange` before manual response composition. If `prompt_presets_requested` is non-empty, read each preset body from `prompt_preset_expansions` in the same contract — preflight already resolved, validated, and expanded them. Do **not** route preset expansion through `orchestrate` (`#orchestratepresetexpand`): it never reads `prompt_presets` (alias `presets`) frontmatter, and it abandons the live `PreflightStarted` cycle this turn is sealed on. For each requested preset expansion carrying `runbook_load_instruction`, execute that required load before acting. The path is already project-contained and symlink-checked; do not substitute an unvalidated direct read. Use `agent-doc runbook list <FILE> --json` to navigate available project runbooks and `agent-doc runbook create` to author one. Full workflow: [runbooks/preset-runbooks.md](runbooks/preset-runbooks.md).
### 0c. Model Tier
Read `effective_tier`, `required_tier`, `suggested_tier`, and `model_switch_tier` from preflight; follow [runbooks/model-tier-gate.md](runbooks/model-tier-gate.md). For `run these in order`, `fan out`, or `after #a do #b`, prefer binary-owned `orchestration_request`; otherwise use [runbooks/command-synonyms.md](runbooks/command-synonyms.md) and [runbooks/compound-task-steering.md](runbooks/compound-task-steering.md).
### 0d. Plan / Dispatch
After preflight, run `agent-doc plan <FILE>` and treat `prompt_targets`, `execution_scope`, `repo_actions`, `required_commands`, `pending_mutations`, `handoff`, and `blockers` as the execution contract. Stop on `blockers`; `task_class: preflight_sealed` is not a no-op — preflight consumed the diff, so execute its contract (`#plansealedturn`). If `handoff=orchestrate`, run the emitted `agent-doc orchestrate ...` command before manual response. If `handoff=compact`, follow the emitted compact/restart instruction and stop before repo work or finalization. Full contract: [runbooks/planning-dispatch.md](runbooks/planning-dispatch.md).
### 1. Respond
- Address the user's changes naturally in the console; that response is the document response.
- Keep routine progress in the console; persist a standalone pre-closeout conclusion with `agent-doc salient-checkpoint <FILE>` (non-final, no queue-answer/commit authority, removed by final response). **Native choice prompts are document-visible (`#choicecheckpoint`):** immediately before opening a harness choice UI (Claude Code `AskUserQuestion`, Codex `request_user_input`, or an equivalent), pipe structured `context`, `question`, and `choices[{label,description}]` JSON through `agent-doc choice-checkpoint <FILE>`, present the identical choices, include only concise operator-visible facts/tradeoffs (never hidden reasoning), then retain the choices and selected answer in the cumulative final response. Full contract: [runbooks/respond.md](runbooks/respond.md); checkpoint mechanics: the streaming runbook.
- Reconcile the changed exchange tail oldest-first. Do not stop at the newest question; answer or group each unresolved prompt in that tail and each unresolved `prompt_target`; treat `content_edit` items as user corrections.
- Execute from the planning record. If `execution_scope=plan_backlog_only`, stay in plan/backlog capture mode. Otherwise complete the requested repo work before persistence or stop on a blocker. Do not keep appending "starting/continuing" status prose while the requested work remains undone. When draining a free-text queue head (no `#id`), quote it as a `> **Queue prompt:**` blockquote so `#ftstrike` can strike it (see [runbooks/respond.md](runbooks/respond.md), `#qdeferstrike`). To quote a head you are not finishing this cycle, open the paragraph after the quote with `**Deferred:**`; otherwise the quote strikes it (`#deferstrike`). A worker claim does not hold a quoted head back: echoing a claimed head with no deferral completes it, and finalize releases the claim in the same closeout (`#claimstrike`). A resolved `#preset` reference is a preset head, not ordinary free text: address every expansion supplied by preflight; no literal `#preset` quote is required. An **annotated `#id` head** (`do [#id]: <question>`, `[#id] <prose>`) carries operator steering for this turn on top of the backlog item: preflight lists it in `queue_head_annotations`. Answer it in the same response as `> **Operator note:** <annotation>` followed by your answer; the head's `> **Queue prompt:**` echo is not an answer, and closeout refuses an id completion whose annotation is unaddressed (`#qheadannotation`, [runbooks/respond.md](runbooks/respond.md)).
**Response header format (template mode):** use `### Re: topic` markdown headers — **not** bold (`**Re:**`). The `(HEAD)` boundary marker requires real headings.
**Model attribution and timestamp (`#timestampresponseheader`):** always append the resolved model short name and the response timestamp after a spaced em dash, joined to each other by a spaced middle dot: `### Re: topic — opus-5 · 2026-09-11T23:45-04:00`. Use `preflight.agent_model` if non-null; otherwise use your own model identity. Never use the harness label (`codex`, `claude`) as the model name, and never omit it. The timestamp is local time with its UTC offset (`YYYY-MM-DDTHH:MM±HH:MM`) taken when you begin composing; with no resolvable clock, emit the model alone rather than a dangling separator. The heading must contain exactly ONE spaced em dash — the timestamp joins the model with ` · ` because three heading parsers split on the FIRST em dash while `strip_re_heading_attribution` splits on the LAST, so a second one leaves the model behind and reads as permanent post-commit heading drift. The agent authors this attribution from the preflight contract; the production write path preserves the complete heading through `agent_doc_template::response_materialization::canonicalize_strict_closeout_response_heading`.
Full detail (session-accretion anchors, streaming checkpoints, `#agent-doc-bug` plan proof): [runbooks/respond.md](runbooks/respond.md).
### 1b. Update backlog (template mode)
Mutate `<!-- agent:backlog -->` only through granular `agent-doc write` flags (`--backlog-add`, `--done <id>`, `--backlog-edit "id=text"`, `--backlog-gate`, `--backlog-ungate`, `--backlog-reorder`, `--review-add`/`--review-edit`, `--icebox-add*`); full-replace via `patch:backlog`/`patch:review` is rejected.
**Backlog capture rule:** if the response creates concrete follow-up work, add it to `agent:backlog` in the same cycle. Plain `--backlog-add` prepends and is for work more urgent or relevant than the existing head. During an active drain, an adjacent, equal-priority, or lower-priority follow-up must not preempt the existing head; pass `--backlog-queue-placement append`. When one write carries several plain adds, they land in flag order top-down and the queue mirror matches that order. For a specific interleave use `--backlog-add-after <id>` or `--backlog-add-before <id>`; these anchors determine both backlog and queue-mirror order, and queue placement does not override them (`#queuemirrororder`). To fix an already-queued order, use `--backlog-reorder`: it cascades into the queue mirror, permuting only the named live heads among their existing slots (`agent-doc queue sync` cannot — it skips ids already present). If the item is only a recommendation, include `[recommended]`.
**Plan-backed backlog items:** create the plan file first and include that exact plan file path in the backlog text. For multi-phase implementation work, prefer one backlog ID per actionable phase (for example `#crdtrespfx1`, `#crdtrespfx2`) instead of one parent ID that gets repeatedly `--backlog-gate`d after partial progress; keep the parent plan file as context, but queue and close out concrete phase IDs.
**`do #id` closeout rule:** record `--done` (completed), `--backlog-gate` (code-complete, awaiting review/external validation), or explain concretely why it stays open — `session-check` enforces `pending_done_guard`.
**Complete over gate (default to finishing the work).** A turn's job is to **complete** its task, not to file a tracking item. Strongly prefer `--done`: do the implementation, tests, build/install, and verification this cycle and close the item. Use `--backlog-gate` / `agent:review` (a gated `[/]` item) **only under exceptional conditions** — work that is genuinely blocked on something this turn cannot do (a required live editor/pane verification, an external approval, a CI/billing outage), and say exactly what unblocks it. Do **not** gate to avoid effort, to "track for later" what you could finish now, or to record a hypothesis. An `[operator-verify]` tag alone is not a gate: if the item still names agent-executable steps, it belongs in `agent:backlog` and may retain that tag. When a review gate clears but work remains, use `--backlog-ungate <id>` (plus `--backlog-edit` if its text changed), not `--review-edit`; this preserves the id while returning the item to actionable work. When follow-up work is real but not blocked, put it in `agent:backlog` as an **actionable** item, not in `agent:review`. If a gated review item's blocking condition is not actually met or is stale, convert it to an actionable backlog item (or `--done` it if already satisfied) and remove the review item — keep `agent:review` small (target < 10) so the actionable list stays legible. Prefer automated completion detection (log/state checks the binary can evaluate) over a human-gated review item wherever possible. **Diagnosis is not a deliverable (`#diagnose-then-fix`):** when a turn diagnoses a reported bug, the SAME turn fixes it — implement, add regression coverage, run the full suite, build/install, `--done`. Filing backlog items for defects you already understand is not closeout; it turns finished analysis back into unstarted work. "It spans several crates", "deserves a focused cycle", and "I did not want to land a partial change" are stalls: land what you have proven and leave only a genuinely blocked remainder, with survivors at the TOP of `agent:backlog`.
**Repository issue filing (`#adminissue`).** When the operator explicitly authorizes creating an issue in `btakita/agent-doc`, use `agent-doc admin issue --title ... --handling human-only|agent-assisted`; use `--dry-run` when only a draft or payload review is authorized. This command is deliberately locked to `btakita/agent-doc`. Do not treat a future generic `issue` action as an alias: that name is reserved for callers filing in their own repositories. External issue creation still requires explicit operator authorization. Full detail (cross-document rule, icebox, `agent:done`): [runbooks/respond.md](runbooks/respond.md) and [runbooks/pending-ops.md](runbooks/pending-ops.md). **Brief operator TODO rule (`#operatortodo`):** when work leaves a concrete operator action, add a brief checklist item to `agent:todo` in the same cycle with `--todo-add "<action>"` on the closeout (the binary creates `agent:todo` if the document has none, so a missing component is never a reason to skip it). It may refer to an existing `agent:review` or `agent:backlog` item (or an `agent:icebox` item), but the action must be understandable by itself or visibly name the detailed source component and `#id`; a leading own-id or unqualified `#id` alone is identity, not operator-facing context. Do not write context-free prompts such as “Decide on this.” It may point to a draft file path instead of copying its contents, and `patch:todo` must preserve every existing checklist item. **Todo ids (`#todoids`, `#todoidlayer`):** every `--todo-add` item gets an own `[#id]` that layers on tracked work: when the action cites exactly one open review/backlog/icebox id the todo reuses it and appends `(source: agent:<component> [#<id>])` (`--todo-add "Verify [#gate1] live"` → `- [ ] [#gate1] Verify [#gate1] live (source: agent:review [#gate1])`; cite the item's id instead of coining a `td...` id); a surrogate id is minted only when the action cites several tracked items or none; an explicit `id=` naming open tracked work gets the same canonical locator, one naming no tracked work yields to the one cited item, and one naming resolved work is rejected. Preflight audits and authority-safely migrates legacy singly linked rows and emits `legacy_source_unqualified` in `todo_signals`. Prefer `--todo-add` over adding id-less lines in `patch:todo`. **Close it when it is finished (`#tododone`):** once the operator reports the action done (or you verify it), check it off in that same closeout with `--todo-done <id>` (own `[#id]`, a cited `#id`, or exact text); never append a second `[x]` copy. `--todo-edit "id=text"`, `--todo-reopen <id>`, and `--todo-remove <id>` cover the rest. A `[todo] advisory` after closeout means a new review gate has no TODO, or resolved work still has an open TODO. **The operator checks the box to finish a todo** (`- [x]` in the editor); never uncheck it (only `--todo-reopen` does, and a `patch:todo` cannot). Act on preflight `todo_signals`: `legacy_source_unqualified` → note the preflight migration audit; `operator_done_review_open` → resolve that gate this cycle (`--review-resolve <id>`, after reading any operator note) or record the follow-up; `operator_done_backlog_open` → the item is unblocked; `operator_done_icebox_open` → act on the operator's decision for the parked item (`--done <id>`, move it, or record the follow-up); `cited_work_resolved` → check the todo off with `--todo-done` if the operator action is finished. A closeout with tracked-work flags auto-checks a todo whose own id is resolved (`[todo] auto-done`).
### 2. Persist the response (MANDATORY — never skip)
Complete requested implementation, verification, build/install, and local inspection **before** this step. Complete response sections may already have crossed `agent-doc response-checkpoint <FILE>`; this step asks the binary to resolve the cumulative response, apply queue/backlog mutations, and commit once. For the normal cycle, pipe the response through `agent-doc respond --stream`. `finalize` remains an accepted compatibility alias, not a distinct required phase. **This step is MANDATORY every cycle unless the user explicitly told you to leave the response uncommitted.**
**Agent harnesses own tiered verification:** use `make check-fast` while iterating; it validates changed Rust packages plus their reverse-dependency closure and relevant non-Rust surfaces. Before release version projection, integrate every completed claimed queue fix ready at that batch boundary; later-ready claims wait for the next release. Run exactly one authoritative `make check` before `finalize` / `write --commit`. If release follows in the same worktree, `make release` runs cheap version/editor-parity checks first, reuses independently recorded full-suite and tmux-CI proofs only when their repository-bytes/toolchain fingerprint still matches, reports its timed tag/publish handoff separately from the batch's single local `install-full`, and then blocks on the exact tag's green GitHub Release and PyPI workflows plus public GitHub assets and PyPI wheel (`#releasepublishcontract`). A tag push, branch push, or local install is not completion of an explicit release/publish request; wait for the `public-publication` phase to complete. Preserve exit status and runner-native failure evidence. Do not rely on a pre-commit hook or waive red suites as "unrelated" or "flaky". See [runbooks/persist-closeout.md](runbooks/persist-closeout.md).
**Tmux CI review for test-bearing turns:** when the cycle runs tests or changes test/build/instruction surfaces, inspect the latest CI tmux-test result. Check the latest run status with `gh run list --workflow CI --limit 1`; if it is already red after runner startup, run `make tmux-ci` locally, fix it, and add deterministic SimWorld coverage for the regression class. If the latest run is queued or in progress, record that it is still pending and continue the turn from local verification evidence instead of waiting for CI to finish. Do not use `gh run watch` as a closeout gate unless the user explicitly asks. An empty-step job with no logs because GitHub never started a runner (for example billing/spending-limit exhaustion) is an external CI-start blocker, not a code/tmux regression; record the annotation and continue with local evidence.
The `respond` command is the binary-owned turn-resolution and final document-mutation boundary for the cycle (`finalize` is its compatibility alias). After `respond` / `write --commit`, do not start more long-running task work for that same turn. Codex hooks in user-level `$CODEX_HOME/hooks.json` plus project-local `.codex/config.toml` are a bounded, fail-closed backstop, not a replacement for binary-owned closeout; the supervisor owns retries that outlive a hook budget.
```bash
cat <<'RESPONSE' | agent-doc respond <FILE> --stream --origin skill
<template mode: wrap response in `<!-- patch:exchange -->` … `<!-- /patch:exchange -->` (BOTH markers); inline mode: plain text, no markers>
RESPONSE
```
**IMPORTANT — patch markers:** template-mode responses MUST include both the opening AND closing `<!-- patch:exchange -->` … `<!-- /patch:exchange -->` markers, or the write is rejected (`malformed template patchback`) / lost (`0 template patches found`). **Do NOT use the Edit tool for write-back** (concurrent-edit "file modified" errors).
`respond` requires the cycle to reach `committed` and its binary-owned post-commit session check to pass; on any reported interruption, continue recovery instead of reporting success. Operator steering that arrived after the commit is not an interruption (`#steerinterruptexit`): the report says `[session-check] steering pending: …` with each item verbatim and its `dispatch`, exits `0`, and the steering is the next cycle's input (see [runbooks/respond.md](runbooks/respond.md)). `finalize` is its compatibility alias, and `write --commit` shares that fail-closed boundary for repair writes. A connected `agent_doc_finalize` call returns this terminal report and any queue continuation itself; only direct-exec harness paths add the explicit `agent-doc session-check <FILE>` distrust check.
**Manual repair / missed patchback rule (all harnesses):** if the user's prompt is already present in the document, do **not** patch the assistant response directly into the file. Use `agent-doc write --commit <FILE>` so repair crosses the normal snapshot/commit boundary in one path. Do not document or follow a manual-repair flow that stops after bare `agent-doc write`. Direct file patching is only acceptable for inserting a missing user prompt into `exchange` before the response exists — including a chat-originated prompt that never reached the document (`#chatprompt`). **Captured finalize is binary-owned (all harnesses):** invoke `finalize` once for a response. After the binary durably captures it, `controller_model_backpressure`, a pending ACK, CAS race, or retained visible response is owned by the keyed supervisor worker and harness closeout hook. Do not recapture, re-answer, alternate `finalize` with `write --commit`, kill the controller, or elect `--force-disk`; observe the terminal `session-check` result while the binary resumes the same capture with bounded retry and exact-once commit semantics. An external disk change under a live editor is a separate Lazily decision: exact accept plus replica propagation, a newer editor edit, an exact editor save-flush, or final editor close resolves it. The agent never component-merges that disk candidate.
Full detail (ordering, full-suite + tmux-CI review, session-doc staging rule): [runbooks/persist-closeout.md](runbooks/persist-closeout.md), [runbooks/commit.md](runbooks/commit.md).
## Vision workflow for non-vision models
Non-vision models cannot read `` images. Delegate to `agent-doc describe-image <IMAGE> [--provider openai|anthropic] [--prompt "<question>"]` and reason over the stdout text. See [runbooks/describe-image.md](runbooks/describe-image.md). Do not reinvent per-session `claude -p @img` invocations.
## Runbooks
Use runbooks for detail that is not needed every turn. Key runbooks: [runbooks/dynamic-context.md](runbooks/dynamic-context.md), [runbooks/harness-invocation.md](runbooks/harness-invocation.md), [runbooks/planning-dispatch.md](runbooks/planning-dispatch.md), [runbooks/pending-ops.md](runbooks/pending-ops.md), [runbooks/commit.md](runbooks/commit.md), [runbooks/split-spec-files.md](runbooks/split-spec-files.md). `split-spec-files` applies across agent-doc-managed surfaces; custom root files stay opt-in unless they still match the generated baseline. Full catalog: `compact-exchange`, `transfer-extract`, `model-tier-gate`, `command-synonyms`, `compound-task-steering`, `streaming-checkpoints`, `document-format`, `code-enforced-directives`, `jb-cache-conflict`, `baseline-drift`, `describe-image`.
**Read each runbook at most once per session.** If you already opened a runbook earlier this session, its content is still in context — reuse it instead of re-reading. Re-open the same runbook only when its content changed or your earlier copy was compacted away. Redundant re-reads waste context tokens and are heaviest on per-cycle shell harnesses (for example Codex re-`cat`ing closeout runbooks every cycle).
## Auto-loop while queue is active (Claude Code)
**Queue subagent dispatch is a three-boundary protocol (`#queueclaimworker`):** run the entry's `claim_command` first (`🟡` means reserved/dispatching), invoke the harness spawn, and only after spawn returns a concrete worker identity run the entry's `start_command` with that identity and the claim's printed generation (`🚧` means worker-bound/running). Never infer running from a claim, label, intended spawn, process poll, or heartbeat. `queue claim --refresh` only extends an already-started attempt. A claim generation binds to at most one worker and one worker binds to at most one live claim in the document; stale generations and conflicting bindings are refused without mutation. After a successful `agent-doc finalize` / `agent-doc write --commit` cycle whose `agent-doc session-check` returns OK, check preflight's queue fields:
- `preflight.queue_active == true`
- `preflight.queue_continuation_required == true` — the authoritative "there is agent-drainable work the **in-session loop** can take" signal (`#cleardrainsignal`/`#qchurn`). As of `#qcontdrain` the in-session `/loop` **drains `[clean-session]` heads in place** (it no longer defers them to the supervisor — a supervisor that may itself be stalled left the queue stranded), so this is `false` **only** when every remaining head is `[operator-verify]` (needs a live editor/pane) or inert noise, even while `queue_active == true` and a `queue_trigger` is present. **When it is `false`, do NOT loop and do NOT re-dispatch from this session** — only operator-gated / noise heads remain. When it is `true` and a `[clean-session]` head is next, just drain it; the supervisor still owns context resets between items (idle-boundary recycle), but you no longer stop the loop merely because the head is `[clean-session]`. This signal is computed in the binary from the in-session drainability filter, which defers only `[operator-verify]` heads — the same rule the supervisor idle-watch applies. Never re-derive drainability by hand from the queue prose. `preflight.queue_drainable_head_count` is the underlying loop-scope count (`0` ⇒ stop the loop).
- `preflight.queue_trigger == "auto"` **or** `preflight.queue_trigger == "persisted"` — `auto` is a start trigger only; once the queue is active, a persisted-active queue (`queue_active: true` with no `auto` attribute) is equally continuation-eligible (`#active-queue-persisted-no-continue`). Do not require the `auto` attribute to keep draining an already-active queue.
- `preflight.queue_prompts.len() >= 1`
- `preflight.user_intent_prompt_changes` is empty (a real user prompt mid-loop takes precedence; do NOT auto-loop over it). Managed-component state edits — queue activity toggle, queue item add/strike, backlog/review/done item edits, `queue_active:` frontmatter flip — are filtered out of `user_intent_prompt_changes` so routine session bookkeeping does not block the auto-loop. Likewise, an edit the affectedness classifier scopes as independent of the current turn (`op_affectedness.turn_affected == false`, `#queue-no-stop-unrelated-edit`) is filtered out — an edit unrelated to the active turn never halts the drain; only a real user prompt, which edits the in-scope `exchange` tail and classifies as turn-affecting, preempts.
When all of these hold, drive the next cycle from the same Claude Code session by SUBMITTING `/loop agent-doc <FILE>` as a real prompt: call `ScheduleWakeup` with exactly that prompt. Do not call the `Skill` tool with `skill: "loop"` instead (`#loopskillnoadmit`): a Skill call is not a submitted prompt, so it never runs binary preflight or claims the drain-owner lease, and it seals no cycle contract. The binary claims the `claude_loop` drain-owner lease as part of each `/loop agent-doc <FILE>` admission, before preflight reconciles the prior continuation, so do not run a separate `agent-doc drain-claim`. That lease tells the supervisor idle-queue watch that `/loop` owns this drain, so it defers instead of *also* injecting `agent-doc <FILE>` into the input queue and flooding it with duplicate triggers. The lease is short-TTL and self-expiring — if the loop stops, the supervisor resumes draining on its own; you never need to release it manually. `/loop` self-paces the next invocation and terminates naturally when the queue drains, when the user interrupts, when `agent_doc_queue_max_iterations` (frontmatter or `.agent-doc/config.toml`) is hit, or when the environment hard-cap `AGENT_DOC_QUEUE_MAX_ITERATIONS_HARD_CAP` (default `50`) is exceeded. **A `/loop` re-entry seals its own cycle contract — do NOT stop for this (`#loopcontractseal`).** It arrives as an ordinary submitted prompt, so the `UserPromptSubmit` hook runs binary-owned preflight for it exactly as for an operator-typed trigger, including the `/loop agent-doc <FILE>` and `/loop <interval> agent-doc <FILE>` wrapper shapes the loop skill re-arms with. A genuinely absent marker is a harness-admission defect to repair (a binary predating 0.35.188 rejects the wrapped shape) — fail closed and say so; never recreate admission by shelling `agent-doc preflight` from the model turn, and never hand drainable heads back to the operator as if the missing contract were a valid stop reason.
**Do not defer a drainable queued item to a "fresh" or "focused cycle" (`#drain-no-defer`).** During an active queue drain, "this deserves its own focused/clean/fully-verified cycle" is a **stall, not valid closeout** — the drain IS the cycle sequence, and context growth between items is the supervisor's job, not a reason for you to stop. When `session_accretion` reports high accretion (`level: warn`/`block`, over `clear_threshold`) while `queue_active == true`, do **not** hand the remaining items back, ask the operator to compact, or pause for a clean session: finish the current item and keep looping. Context resets between items are the supervisor's job, never yours (`#bgclearpolicy`). With queue auto-clear on — the default for an active `agent:queue`; `<!-- agent:queue disable-auto-clear -->` turns it off, and preflight reports the setting as `queue_auto_clear` — the binary hands each item boundary to a ready supervisor: preflight reports `queue_auto_clear_yield: true` and session-check prints `queue_auto_clear_yield=true`, both with `queue_continuation_required == false`. Then end the turn after the proven closeout (no `ScheduleWakeup`/`/loop`); the supervisor resets the context at the idle boundary (`/clear` typed into the live Claude Code process, `/new` into OpenCode, a fresh restart for Codex, whose `/clear` exits the process) and dispatches the next item to the fresh agent. With auto-clear off, or when no ready supervisor can take the hand-off, `queue_continuation_required` stays `true` and you keep looping in the same context; only an explicit operator Clear resets it. A context clear never shrinks the document: accretion `block` measures the exchange size, which only compaction reduces. Only a genuinely operator-gated item (a required live editor/pane proof, an external approval/CI outage) may be left for the operator and tagged so go-mode defers it (`#goqueuestall`); an **agent-drainable** item is never deferred to "a fresh cycle."
Skip the auto-loop on any failed closeout, `session-check` interruption, or `lint-gate` block — those need explicit operator attention. `[session-check] steering pending: …` (exit `0`) is **not** an interruption (`#steerinterruptexit`): it lists operator steering that arrived after the committed response, each item verbatim with its `dispatch`; an `address_now` exchange prompt is the next cycle's input, so re-enter with `ScheduleWakeup` and the prompt `/loop agent-doc <FILE>` even when `queue_continuation_required == false` (that line reads `steering_pending=true`). Skip when `preflight.queue_active == false` (queue drained or halted) **or `preflight.queue_continuation_required == false`** (no *in-session*-loop-drainable head — `[operator-verify]`, `[focused-cycle]`, or noise heads remain; looping would just churn a no-op `#qchurn` cycle, so report the remaining heads in one line and stop without pausing or punting). As of `#qcontdrain`, `[clean-session]` heads are loop-drainable and therefore keep `queue_continuation_required == true` — do **not** stop the loop on them; drain them in place. High session-accretion is **not** in that skip list — keep draining (a context clear does not reduce it; only compaction does). `queue_auto_clear_yield` is not a stop either: it is the intentional auto-clear hand-off above, so end the turn and report "yielding to supervisor auto-clear," never an operator stall. **Stopping ≠ stalling — read `ui_outcome` (`#qfocsup`):** `deferred_for_supervisor_drain` (`yield_to_supervisor_clear_and_continue`) means a `[focused-cycle]` head remains that the CP/supervisor clear-and-continue path drains (it re-dispatches the head, clearing the context first when queue auto-clear is on — why the in-session loop yields it instead of running mid-drift), so just end your turn for the supervisor to take over and report "yielding to supervisor," never an operator stall (treating it as operator-gated is what wrongly idles the queue); `deferred_for_operator_proof` (`operator_proof_required`) means only `[operator-verify]`/noise heads remain (need a human / clearing) so report and stop; `no_drainable_work` means fully drained. `supervisor_drain_unavailable` (`drain_in_session_supervisor_unavailable`, `#supdrainlive`/`#focusedfallback`) means a `[focused-cycle]` head remains but its supervisor cannot drain it (none live, stale heartbeat, or stale binary) — the binary then reports `queue_continuation_required == true` and you drain that head IN-SESSION now; never hand it back to the operator or ask for a re-trigger, and never report "yielding to supervisor". **A head dispatched to a subagent is claimed; the loop does not re-enter for it (`#queueclaim`).** Before handing a queue head to a background subagent (for example under a `#subagents` preset), claim it: `agent-doc queue claim <FILE> --item <#id or the queue line text> --owner subagent:<label>`. A claimed head is in flight elsewhere: preflight does not select it, it does not count toward `queue_drainable_head_count` / `queue_continuation_required`, and the Stop hook does not ask for a `/loop` re-entry for it. If every remaining head is claimed or operator-gated, end the turn quietly and wait for the subagents' notifications; do not schedule a `/loop` re-entry. The claim ends when the item closes (it leaves the queue): when the subagent reports back, persist its result in the SAME turn with the closeout that strikes the line (the verbatim `> **Queue prompt:**` echo, or `--done <id>`), which also ends the claim. A generation-matched `agent-doc queue release <FILE> --item <...> --generation <N>` records that the worker reported (`#reportedrelease`), so the line is persisted (`dispatch=persist_reported`) rather than re-dispatched, and `--generation <N> --requeue` abandons it for a new worker. A claim attempt transitions to visible nonblocking `⚠️` lost when its TTL expires (default two hours, `--ttl-secs`), so a crashed subagent cannot strand the queue and its lineage is not erased. `agent-doc queue claims <FILE>` lists retained claim attempts and their typed state.
**The skip list above is exhaustive — do NOT invent stop reasons after a proven editor delivery (`#degraded-ipc-no-stall`).** A closeout is successful only after the monotonic write pipeline reaches `committed` and `session-check` is OK. A timeout, missing Lazily visibility proof, or `recovery=retry_without_disk_write` is not success: do not continue the queue or elect an attached-document disk write. The keyed worker retains the same intent in `state.db` and resumes it after the registered editor endpoint recovers. When `queue_continuation_required == true`, follow the binary's `queue_continuation_guidance`; high session accretion and semantic-completion warnings are not stop reasons after proven closeout. A stale binary recycles at the next idle boundary through the controller projection or `agent-doc admin recycle`, after which the loop continues on the fresh build. `make install` / `agent-doc lib-install` sends the shared `reload_library` intent to every live Lazily editor registration through its PID-scoped endpoint; `agent-doc admin reload-lib` repeats that typed fan-out. No filesystem delivery or reload broadcast participates in the hot path.
This section's `/loop` invocation is Claude-Code-specific. Codex auto-loops via its `Stop` hook in user-level `$CODEX_HOME/hooks.json`; with queue auto-clear on and a ready supervisor, that hook allows the stop instead, and the supervisor restarts Codex fresh and dispatches the next head. **OpenCode has no `/loop` and no `Stop` hook, so the OpenCode agent MUST self-drive the drain in-turn (`#ocdrainstall` Part A):** after a proven closeout (`agent-doc write --commit <FILE>` reached `committed` + `session-check` OK) with `preflight.queue_continuation_required == true` and `preflight.queue_drainable_head_count > 0`, do NOT end the turn — immediately run `agent-doc preflight <FILE>` again and drain the next head (preflight → respond → `agent-doc write --commit <FILE>`), repeating in the SAME turn until `queue_continuation_required == false`, `queue_drainable_head_count == 0`, a genuine stop condition (failed closeout, `session-check` interruption, only `[operator-verify]` heads remaining), or the `agent_doc_queue_max_iterations` / `AGENT_DOC_QUEUE_MAX_ITERATIONS_HARD_CAP` (default `50`) cap is reached. "Low context budget" and "this head deserves its own focused/clean cycle" are **NOT** stop reasons (`#drain-no-defer`) — they are stalls; keep draining. When `queue_auto_clear_yield` is true, end the turn after the proven closeout; the supervisor types `/new` and dispatches the next head. If context is genuinely saturated, finish the current item's proven closeout, then end the turn for the supervisor's idle-boundary recycle + re-dispatch — but never stop mid-queue without a proven closeout, and never hand an agent-drainable head back to the operator to re-invoke manually. See [runbooks/harness-invocation.md](runbooks/harness-invocation.md) and `tasks/agent-doc/plan-claude-code-queue-auto-loop.md`.