# Versions

agent-doc is alpha software. Expect breaking changes between minor versions.

## 0.35.254

- **Fix: editor-restored prompt envelopes no longer manufacture an endless
  post-commit steering cycle.**

JetBrains may restore display-only `❯ >` / `❯ > **User prompt:**` envelope
lines and a previously answered quoted prompt block immediately before the
response that already owns it. The unstarted-prompt selector now treats empty
envelopes as projection metadata and walks the complete contiguous quoted block
when checking response adjacency. A restored answered block is therefore not
reported as new steering after every successful commit, while a quoted prompt
at the exchange tail with no following response remains actionable. The exact
live `agent-doc-bugs2.md` document now passes session-check under the patched
binary after previously reproducing the eight-fragment loop twice.

- **Fix: preflight now publishes the strict template response contract before
  an agent chooses its closeout command.**

For a document with `agent_doc_format: template`, stderr names the required
`--template` option and matching `patch:exchange` markers, and the JSON output
includes a structured `response_contract` with the same facts. Inline/append
documents omit the field. The strict write boundary remains fail-closed and does not
implicitly convert free-form stdin; the agent now receives the requirement at
preflight instead of discovering it only after a rejected closeout.

## 0.35.253

- **Fix: rerouting an already-busy owner no longer tries to restart it solely
  because its capability proof is missing.**

Capability proof remains a pre-dispatch admission gate for an idle pane. When
the authoritative same-document actor is already `busy` in the registered
owner pane, or that same pane has live harness-specific active-turn proof while
the persisted actor projection still says `ready`, route now coalesces the
active dispatch before capability reuse instead of issuing a restart that the
open cycle must reject. The explicit failed-proof state remains fatal. Focused
regressions cover pane identity, actor/live-state skew, Codex approval modals,
and the idle fallback. Live proof uses the shared prompt-at-cursor classifier,
so any non-empty same-owner pane that is not dispatch-ready is protected even
when an approval UI replaces the ordinary working spinner.

- **Fix: compact may subsume an exact retained editor projection instead of
prescribing an impossible response-cycle commit.**

When the pre-write delivery barrier reports its typed retained-projection state
and the relay still exposes the exact canonical base from which compact derived
its successor, the successor now composes over that pending cut. The ordinary
CRDT compare-and-swap and final editor-delivery proof still protect the new
target. A different live editor value, detached authority, or an unrelated
failure remains fail-closed; no disk, snapshot, or commit fallback is inferred.

- **Test reliability: the delayed Lazily-proof poll regression is synchronized
before its timeout begins.**

The test now proves its writer thread has started, retains and joins that thread,
and allows a bounded five-second scheduling window. This removes the prior
500ms thread-scheduling race under a saturated CI runner without weakening the
production polling assertion.

## 0.35.250

- **Fix: route-owned queue activation no longer strands an editor-retained write
  or rejects the fresh Codex turn that already accepted it.**

  A route mutation accepted by the CRDT plane now stays route-owned until its
  exact target becomes editor-visible; only then may queue activation checkpoint
  and dispatch. If it cannot settle, recovery is rerunning route rather than an
  owner-pane-only manual commit. After a `/clear`-driven fresh restart, a newer
  authoritative actor generation whose busy transition is the supervisor's own
  `auto_trigger_inject` is accepted as an owned dispatch instead of being
  misreported as “never became ready.”

- **Fix: the prompt visible when preflight opens is committed with its response,
  while prompts typed later still carry forward.**

  Write entry points capture a preflight application-base witness before pending
  response persistence advances the cycle. The visible document is adopted only
  when its hash is exactly the opening cycle's recorded `file_hash` and the
  explicit baseline matches the recorded snapshot. A replayed capture or any
  post-preflight text divergence keeps the older baseline, preserving the next
  prompt outside the current commit. Route projection, fresh-restart admission,
  and preflight/carry-forward regressions cover all three fixes.

## 0.35.249

- **Fix: a Codex owner pane now executes an unwrapped, multiline queue task
instead of dismissing the invocation as “already active.”**

Queue parsing now losslessly recovers the operator-authored shape used by
`backend.md`: introductory prose, balanced Markdown-fenced evidence, and a
trailing explicit request. The deliberately narrow recovery leaves log-only
dumps inert and yields to every canonical queue directive, while the shared
continuation detector exposes the recovered prompt to owner-pane preflight and
idle drain. Codex admission context also explicitly forbids the “already
active; resend the task” deflection when unresolved owner-pane work is present.
Parser, continuation, and admission-directive regressions cover the behavior.

## 0.35.248

- **Fix: a Codex owner pane now admits `agent-doc <FILE>` in the current turn
instead of shell-running the command back into itself.**

Codex lifecycle admission is now installed once at the user hook layer, so it
remains visible when a session document lives below a nested Git root, submodule,
or worktree. Reinstall retires only agent-doc's legacy project-local lifecycle
commands and preserves unrelated hooks, avoiding both missed admission and
double preflight. Successful preflight context now ends with an explicit in-pane
directive; tracking or preflight failures still emit no success marker and fail
closed. The existing process-entry recursion guard remains the last line of
defense for a genuine nested invocation, but no longer substitutes for handling
the unresolved prompt. Installer, route detection, and absolute-path hook
regressions cover the user-owned topology and nested-root case.

## 0.35.247

- **Fix: an editor-native save race no longer tells the foreground agent to
compete with captured-finalize by running `agent-doc commit`.**

When a response had reached canonical editor authority but its exact native-save
projection had not reached disk, the document actor correctly retained the same
write intent. The lifecycle verdict then saw `write_applied` and named manual
`commit` recovery even though the response capture and its captured-finalize
worker were still active. The agent obeyed that instruction, racing the worker
that already owned the terminal boundary.

Retained-capture ownership now takes precedence over the `write_applied` phase:
captured-finalize keeps responsibility for delivery settlement and terminal
commit, and the foreground recovery is observation through `session-check`
without resending or mutation. An uncaptured `write_applied` crash state still
names manual `commit`. The commit-side capture-materialization guard also treats
the capture it has already loaded as authoritative ownership evidence, so a
racing sidecar read cannot bounce recovery among `commit`, `write --commit`, and
a new cycle. Focused ownership, materialization, realtime retained-write,
session-check, and deterministic simulator regressions cover both branches.

## 0.35.246

- **Fix: direct `agent-doc <FILE>` no longer repeats the raw-commit refusal for
  a fresh typed-component prompt after a committed cycle.**

Raw `commit` correctly refuses to absorb queue, backlog, status, or other typed
component drift when the staged snapshot already matches `HEAD`: that drift is
the next unanswered prompt, and committing it would swallow the prompt. The
refusal's recovery names direct `agent-doc <FILE>`, but direct run previously
called the same raw commit path unconditionally before dispatch and propagated
the refusal. Reopening the document therefore repeated the identical error
forever.

Commit I/O now exposes the retained-write refusal as a typed lifecycle verdict.
The direct-run runtime maps only `UnansweredEditPending` to
`AnswerCurrentEdit`, and run policy skips only that precommit before dispatching
the real editor-authored diff. Synthetic queue prompts and every other commit
error remain fail-closed, while raw `commit` retains its original refusal and
operator-facing diagnostic. Regression coverage closes a prior cycle, adds a
fresh queue/backlog prompt, proves the configured agent is invoked, and proves
the prompt plus response land together in the next committed cycle.

## 0.35.245

- **Fix: a handoff replacement that lost its socket is reaped instead of wedging
  the project for hours.**

Observed 2026-08-11 on `src/haiven-dev`: controller generation 536 sat in a
handoff for **6h58m** while `controller.sock` resolved to nothing. Every editor
replica failed to attach ("the open editor replica could not be attached to its
owning project controller", which is what a `compact exchange` reported), and an
`agent-doc write --commit` blocked for 8h holding its harness session open. The
self-watchdog ticked throughout and said healthy every time, and `admin inspect`
plus `admin detect` were both green; only the controller's own argv showed the
wedge.

Four of the five suicide predicates key on `Preparing`, and `abort_handoff`
writes `Stable` with a cleared handoff clock while leaving `socket_path` on the
private path — so the replacement declared itself healthy while unreachable. The
fifth, `handoff_replacement_is_stranded`, required the temp socket to still
exist. That was backwards: a replacement whose socket is gone can never be
promoted, because promotion is a rename of that file, and can never be reached,
because no client resolves a path that is not there. The worst case was the
invisible one.

`handoff_replacement_lost_its_socket` closes it. The missing socket alone cannot
decide the question — it is equally the signature of a healthy completed handoff
— so the new rule pairs it with whether promotion was ever recorded, which only
`promote_handoff` and its self-promoting twin write. The existing stranded and
self-promote rules are unchanged, so the M1b promoted-but-stranded path keeps
completing its own promotion rather than suiciding. Regression coverage replays
the measured shape at both the policy and runtime layers, each with the
healthy-promotion negative control that keeps the rule from reaping every
successfully-promoted controller 45 seconds into its life.

- **Fix: a refused editor-replica attach reports its cause and remedy (JetBrains
  plugin 0.2.352).**

The same operator hit a second, unrelated wedge with an identical-looking
message: Compact Exchange said only that the replica "could not be attached to
its owning project controller", which points at the controller — and the
controller was healthy. The actual cause was an IDE that had been running since
before the plugin generation which sends the editor PID on `replica_register`
was installed. The relay refuses a registration carrying no live PID
(`detached_authority`), the plugin retried every 8s forever for every open
document in every project, and every version check read current because the
installed jar on disk WAS current — only the loaded generation was not.

The refusal reason was already in `idea.log` and thrown away at the operator
surface. `CrdtReplicaForwarder` now retains it, `CrdtReplicaManager` exposes it
per document with a remedy, and Compact Exchange reports both. For
`detached_authority` the remedy names the IDE restart and says explicitly that
reloading the native library or recycling the controller cannot replace a loaded
plugin generation — the same conclusion the controller's own refusal-storm
advisory reaches.

- **Fix: a registration that carries no explicit editor PID resolves it from the
  identity instead of being refused forever (`#registeridentitypid`).**

This is the runtime half of the same incident, and it removes the IDE restart
from the recovery path. A replica registration is refused as
`detached_authority` when it carries no LIVE editor PID — correct, because a dead
editor must never bootstrap authority. But the PID was read from exactly one
place: an explicit payload field. An editor generation that predates that field
sends nothing and is refused on every retry, forever, for every open document in
every project, while the plugin ON DISK reads current and only the loaded
generation is stale.

The PID was in the request the whole time: editor identities are
`jetbrains-<pid>-<uuid>:<path>`, and `editor_process_id` has always parsed them.
The controller register/deregister seam now falls back to that when the explicit
field is absent. The derived PID is liveness-checked exactly like an explicit
one and a headless identity still resolves to nothing, so both fail-closed
guarantees are unchanged — proven by the dead-editor and headless negative
controls beside the new test.

- **Fix: a retained canonical projection no longer adopts over unpublished
  operator text (JetBrains plugin 0.2.353,
  `#retainedprojectionclobbersoperatortext`).**

The third defect from the same incident, and the one that cost real work. While
registration was being refused the operator kept typing into a buffer that
reached nothing — not the controller, not the CRDT, not disk. A compaction
computed at 04:14:47 against disk could not be delivered, was retained, and at
04:23 was adopted over that buffer. The prompt existed only there, so it
survived nowhere: not git, not the compaction archive, not a snapshot.

Registration arms a retained-canonical suppression so a stale restarted buffer
cannot publish itself over controller state. That reasoning is right for a
restarted IDE and exactly inverted for a live one. The shadow separates the two
and needs no new state: it is the last text the plugin proved reached canonical,
and it is in-memory, so a restarted IDE has none and its adoption path is
unchanged. A live buffer that has moved past its shadow holds operator text the
controller has never seen, and the plugin now keeps it and forwards it as a
local delta instead of adopting over it. Unknown buffer text is not divergence,
so a closed document cannot strand a retained projection. Verified by mutation:
restoring the old unconditional adoption reddens the positive case while all
three negative controls stay green.

- **Fix: the editor stops registering markdown that is not an agent-doc session
  document (JetBrains plugin 0.2.354, `#replicarefusalstorm` editor half).**

The controller has refused non-agent-doc markdown terminally and cheaply since
the refusal-storm work — no refusal ledger, no `log_op` — and its comment named
the remaining half: "the durable stop is the editor only registering agent-doc
documents". That half was never written, so the plugin registered every `.md` it
opened and retried each terminal refusal on its 8s backoff indefinitely.
Measured 2026-08-12, twenty minutes after an IDE restart: ~150 retries each for
`README.md`, `CONTRIBUTING.md`, `sdk-market-research.md` and
`contract-source-of-truth-plan.md`.

The plugin now applies the same content test as the controller — `agent_doc_*`
frontmatter or an `<!-- agent:` marker — before registering, and skips without
arming a retry, since there is nothing to converge on. The test reads the CURRENT
text with no memoization, so a plain file that later gains markers registers on
the next observation with nothing to invalidate. The skip still records
`not_agent_doc_document` as the attach-failure reason, so an operator-facing
command explains itself rather than failing blankly. Verified by mutation:
registering unconditionally reddens three of the four cases.

- **Fix: a stale pane→window binding is rebound from the live pane instead of
  disabling editor focus sync forever (`#panewindowbindingrebind`).**

`session doctor` reports recorded-vs-live window drift and the pane-layout focus
effect refuses to mirror onto a drifted pane — both correct, and together they
mean a bad binding permanently kills editor→tmux sync for that document while
every diagnostic explains why and nothing repairs it. `project_binding_in` runs
at bind time and never again, so `window_id` (and the recorded harness) freeze at
whatever was captured then.

Measured 2026-08-13: `tasks/haiven-dev.md` was bound `window=@0` on both the
actor record and the registry entry while the live windows were `@6`
(`agent-doc`) and `@9` (`stash`). The drift was therefore true wherever the pane
sat — moving it back into the visible window changed nothing — and
`session doctor --repair`, `resync --fix` and `agent-doc sync` all left it. The
sibling `tasks/sdk.md`, bound `window=@6`, worked throughout. `@0` is not a
sentinel to special-case: a fresh tmux server really does hand out `@0`, as the
regression fixture found the hard way.

The repair rewrites the binding from the live pane, which is the same authority
`pane_window_binding_drifted` already trusts, applied in the repair direction.
Re-binding through `project_binding_in` also re-derives the recorded harness,
which had frozen the same way — the affected document was labelled
`[claude-code]` while its pane ran Codex. The pane binding and generation are
untouched: this corrects a stale FACT about a binding, it does not re-elect one.
An unreadable record or a window tmux cannot report is not drift and is never
repaired.

It runs FIRST in `session doctor --repair` and reports even when the rest of the
pass fails. Ordered behind the document-repair pass it was unreachable on exactly
the documents that needed it, which is how it was found: the live repair aborted
on an unrelated attached-editor condition before ever reaching the binding.

## 0.35.244

- **Fix: captured edit-and-gate replay converges after a partially published
  tracked-work transaction.**

An interrupted closeout can publish its tracked-work transaction before the
response commit completes. When that transaction both edited and gated one
item, the retry replayed the edit first against Backlog even though the item was
already in Review. The idempotent gate could have resumed, but the backlog-only
edit failed early and left the captured cycle open.

Captured-finalize replay now recognizes edit-and-gate pairs, idempotently gates
the item from either its pre-gate or post-gate location, and edits the resulting
Review item. Normal backlog edits remain backlog-only. Regression coverage uses
the already-gated state from the interrupted `#flakystatedbfixture` closeout and
proves that replay leaves one gated item with the captured edit applied.

## 0.35.243

- **Fix: closeout order repair preserves boundary-split multi-paragraph responses.**

A retained closeout can transiently place an exchange boundary inside the
captured response cell. The response-before-prompt repair previously accepted
one matching response signature line as proof that the response was complete,
then misclassified the captured suffix after the boundary as a user prompt and
moved it ahead of its own heading. The commit materialization guard correctly
refused to commit that reordered cell, leaving the cycle at `write_applied`.

Order repair now requires the full captured response-cell identity before it
moves content across a boundary. `(HEAD)` and boundary framing remain transient
for identity, but missing response paragraphs do not. Regression coverage uses
the observed four-paragraph split and proves both that the destructive repair
is rejected and that legitimate prompt-after-response repair still succeeds.
The existing exact retained-fragment replay then remains able to resume and
reassemble an already interrupted cycle without duplicating the response.

## 0.35.242

- **Fix: Codex UserPromptSubmit admission now emits exactly one valid hook JSON
response.**

The combined Codex admission hook wrote the pretty-printed preflight contract
and then a `[agent-doc]` success marker directly to stdout. Codex treats stdout
starting with `{` or `[` as structured hook output, so this became either two
concatenated JSON documents or a marker-shaped invalid JSON array and Codex
reported `hook returned invalid user prompt submit JSON output`. The same defect
also hid agent-doc's explicit admission-failure context.

Preflight now supports a caller-owned output writer. The hook captures the
contract in memory and emits one `UserPromptSubmit` `hookSpecificOutput` object,
with the complete contract and final outcome marker in `additionalContext`.
Ordinary `agent-doc preflight` output remains unchanged. The integration test
now parses the actual CLI stdout as one JSON document and separately validates
the embedded preflight contract.

## 0.35.241

- **Fix: validated stream pre-capture preserves the established empty-response
proof guard.**

The 0.35.240 pre-capture validation correctly prevented malformed strict
template payloads from opening a durable cycle, but checked the response
heading before the existing response-write proof. An empty `patch:exchange`
therefore reported a missing heading instead of the more specific
`no real response-body write` error and broke the integration contract.

Pre-capture validation now runs the response-write proof first, matching the
ordinary stream pipeline, then enforces strict marker and heading shape. Empty
shells retain their specific diagnostic, while unwrapped or headingless real
responses still fail before any durable capture. Legacy standalone finalize
calls without a preflight baseline first resolve current content through the
binary-owned authority adapter, preserving compatibility without bypassing
editor authority or reading the session file directly.

- **Fix: a route-created pane now reconciles into the existing editor split
instead of becoming a third pane.**

The controller projects the requested editor layout before route provisioning,
so that first pass cannot place a pane that does not exist yet. A successful
route now detects when it created the target pane and performs one exact-visible,
layout-only reconciliation with the freshly registered actor bindings. The
incoming pane replaces its outgoing column, while a live outgoing turn is moved
intact to `stash`.

- **Fix: switching to an already-open JetBrains editor now republishes the
spanning layout as well as focused-document state.**

IDEA does not emit `selectionChanged` when focus moves between existing split
editors. The focus listener previously sent only the narrow focus projection,
which deliberately cannot promote a stashed pane. Focus activation now also
queues the generation-guarded spanning surface observation, allowing the
controller reconciler to swap `agent-doc-bugs2.md` and `lazily.md` in place
without growing a two-column editor layout to three tmux panes.

## 0.35.240

- **Fix: strict stream closeout validates template patch shape before durable
response capture.**

Stream mode preserved the response and tracked-work mutation plan before
resolving editor authority so an authority failure could resume losslessly. Its
strict template-shape validation ran later, however, so an unwrapped response
was first recorded as `response_captured` and then rejected. The cycle became
unrecoverable: doctor correctly prohibited resubmitting the durable payload,
while `session-check` could not apply the invalid patchback.

Strict stream closeout now parses and validates the response against the cycle
baseline before creating the early capture. Invalid template input fails without
opening a captured cycle and can be corrected immediately. Valid payloads still
retain their complete response and mutation plan before authority resolution,
preserving the existing lossless-retry guarantee.

## 0.35.239

- **Fix: orphan replay now collapses exact split response fragments around the
  complete response cell it writes.**

The retained-response materializer added in 0.35.238 could reassemble the
pre-write split shape, but `agent-doc repair` replayed an orphan through the
strict write adapter without invoking that materializer. The strict replay
therefore inserted a complete response while preserving the misplaced suffix
before it and a duplicate heading-plus-prefix after it. Semantic response
presence passed even though the exchange still contained two response cells.

Repair now recognizes only the exact complementary suffix/prefix shell around
one complete captured response, removes those fragments through the authority
CAS path, checkpoints the repaired document, and lets normal closeout commit it.
Historical committed-capture routing keeps this shape eligible for repair even
though the middle response is semantically materialized. Any novel
non-transient line fails closed, and regressions cover both the live shape and
preservation of unrelated operator text.

## 0.35.238

- **Fix: retained streaming closeout no longer strands a response heading after
  splitting its body around that heading.**

An editor projection could preserve the first streamed response segment after
its transient `(HEAD)` heading while later response segments landed immediately
before the heading. The retained-response materializer only repaired the
all-body-before-heading shape, so retry appended instead of reassembling the
cell and the integrity gate correctly stopped on the apparent bodyless heading.

Recovery now recognizes the exact split retained cell, including the one
editor-added `❯` prefix that prompt normalization may have placed on quoted
queue context. It reconstructs one contiguous heading and captured body while
preserving transient guard markers. The classifier fails closed unless the
non-transient lines on both sides jointly equal the captured response, so it
cannot reorder operator text or an unrelated response.

## 0.35.237

- **Fix (`#dispatchonlyresubmitwiring`): dispatch-only reopen now submits its
own stranded trigger instead of only naming the recovery.**

0.35.235 added the safe classifier for the operator-reported `sdk.md` / pane
`%926` shape, but the route consumer still used the trigger-blind classifier and
always ended in a refusal. The new `StrandedTrigger` outcome and
`unblocker=resubmit_stranded_trigger` therefore described an action that no
production branch performed.

The starting-pane dispatch-only path now compares a visible composer draft with
the exact trigger for the document being routed. Only an exact match releases
the pending route guard, sends one bare harness submit key, and then awaits the
controller turn-admission projection. Missing admission fails closed without a
second submit. A trigger plus operator words, a trigger for another document,
or any other draft remains `OperatorDraft` and is never submitted by this
recovery.

Regression coverage pins both boundaries and both prompt sigils (`❯` and
Codex's `›`). An isolated live tmux test renders the reported `sdk.md` composer
shape with NBSP padding, makes controller admission conditional on the pane
receiving Enter, and proves the one-shot effect submits exactly once before the
reactive admission observation.

## 0.35.236

- **Fix (`#editorregisterbootstrap`): an open editor can now establish CRDT
  authority instead of being refused until authority already exists.**

Operator-reported 2026-08-10 on `tasks/agent-doc/agent-doc-bugs2.md`: JetBrains
showed a File Cache Conflict while the controller logged 6,677 consecutive
`replica_register ... reason=detached_authority` refusals. With no accepted
replica, preflight/session-check resolved the document from disk even though the
editor still had it open—the exact disk-behind-editor race the CRDT plane is
supposed to prevent.

Two independently incomplete seams formed the cycle. JetBrains and VS Code did
not include the already-defined `editor_pid` liveness proof in `crdt_replica`
payloads, and `handle_crdt_replica_rpc` refused every request while detached,
including the registration event that establishes editor authority. Both
plugins now attach their real process PID to every replica payload, and the
controller delegates a detached agent-doc `replica_register` to the relay. The
relay remains the sole liveness-policy owner: a live process-scoped editor may
allocate the routed hub; a generic or dead registration is still refused, as
are every non-registration operation and every non-agent-doc markdown file.

Regression coverage exercises the controller's complete detached-admission
table plus the JetBrains and VS Code payload builders. The existing relay tests
continue to prove that live PID registrations bootstrap authority while generic
and dead registrations cannot.

## 0.35.235

- **A corrupt `state.db` refusal now carries the offending bytes
  (`#flakystatedbfixture`), and the empty-file theory is retracted.**

  An earlier draft of this release removed a zero-byte `state.db` and
  reinitialised it, on the premise that a writer which created the file and died
  before writing the header left a file SQLite would reject. **That premise is
  false and the change is reverted.** Measured directly: a `state.db` of exactly
  0 or 1 bytes opens *cleanly* — SQLite initialises it as a new database.
  `NotADatabase` starts at **two** bytes of bad header. The branch was therefore
  unreachable through the failure it claimed to fix, all four of its tests
  passed with the branch deleted, and it was actively unsafe:
  `open_and_init_state_db` also fails on a *lock* error while a concurrent
  process is still initialising the same 0-byte file, so the branch could unlink
  a database another process was in the middle of creating — the exact
  concurrency the item is about.

  What actually shipped is the thing that makes the next sighting diagnosable.
  The refusal named the path and nothing else, so every occurrence restarted the
  diagnosis from zero — and for a test `TempDir` the evidence is gone before
  anyone can act on the "preserve the corrupt copy" advice. `open_state_db` now
  attaches the file's length, whether the SQLite magic is intact
  (`header=sqlite` vs `header=foreign`), the first sixteen bytes in hex plus a
  printable rendering, and the presence and size of the `-wal` / `-shm`
  siblings. Those separate the two shapes with opposite causes: a file that was
  never a database (foreign header, no siblings) versus a torn page-1 of a real
  ledger (live WAL alongside a lost magic).

  Fail-closed behaviour is unchanged — nothing is replaced or deleted, and the
  bytes stay on disk. **`#flakystatedbfixture` stays open**: 600 concurrent
  cross-process opens over 25 fresh project roots did not reproduce the
  corruption, so the writer that produces those bytes is still unidentified.

- **Fix (`#dispatchonlystrandedtrigger`): agent-doc's own unsubmitted trigger
  was classified as operator input.**

  Operator-reported 2026-08-10 on `sdk.md`: a dispatch-only reopen refused pane
  `%926` because the composer held
  `/agent-doc .../tasks/sdk.md` — agent-doc's own trigger for the very document
  being routed, injected by an earlier route and never submitted. The refusal
  emitted `unblocker=submit_or_clear_pane_draft`, asking a human to press Enter
  on agent-doc's behalf.

  `StartingPaneBlocker::from_composer_draft` classified ANY non-empty draft as
  `OperatorDraft`, never comparing it against the trigger being injected —
  even though `#jbtsiftnosub2` already established resubmit-once as the answer
  for the fresh-start path, using `pane_composer_has_pending_trigger` in the same
  module. New `StrandedTrigger` variant with
  `unblocker=resubmit_stranded_trigger`, matched EXACTLY (after stripping the
  harness sigil and padding, including the non-breaking space in the live
  report). A draft containing the trigger plus operator words, or a trigger for
  a different document, stays `OperatorDraft` — resubmitting either would send
  text the operator did not mean to send.

  Classification only. Wiring the dispatch-only route to actually resubmit is
  `#dispatchonlyresubmitwiring`, which needs a live-pane proof.

## 0.35.234

- **Fix (`#handoffdeadpredecessor`): a replacement still `Preparing` now reaps
  itself once its predecessor is known dead, instead of waiting out the 45s
  staleness threshold.**

  Second root cause of the 2026-08-10 `haiven-dev` wedge. `controller serve`
  takes `--previous-controller-pid` and carries it through every status
  projection, but nothing ever liveness-checked it. Both wedged controllers were
  `Preparing` against predecessors that were already dead (1329143 on dead
  1323307; 1324731 on dead 574967), so their handoffs were unsatisfiable from
  the moment those processes exited.

  `Preparing` means promotion has not happened yet, and promotion can only come
  from the predecessor — so a dead predecessor makes the handoff permanently
  unsatisfiable. `#preparingnoclockwedge` (0.35.233) already reaps that state on
  process age, so this is the sharper signal rather than a re-fix.

  Two things keep it from reaping healthy controllers:

  - The state check excludes `Promoted`. After promotion the predecessor exiting
    is the NORMAL end of a healthy handoff; only the pre-promotion state can be
    starved this way.
  - A 3s grace covers a `promote_handoff` still in flight when the predecessor
    exits.

  Unknown liveness (`None`) never counts as dead — the same
  `#idlerevisionreactive` rule whose inversion caused the original wedge,
  applied in the other direction. Liveness enters as a fact from the IO shell
  via the existing `process_is_alive`; the predicate stays pure.

## 0.35.233

- **Fix (`#preparingnoclockwedge`): a controller stuck in a handoff with no
  recorded handoff clock could never be reaped, and spun forever.**

  Operator-reported 2026-08-10: `agent-doc start` on `haiven-dev` failed
  repeatedly with `timed out after 5.0s waiting for project controller
  response`. Two controllers were `Preparing` against DEAD predecessor PIDs,
  their temporary handoff sockets absent from disk, `controller.sock` present
  but with no listener, and one burning **95.2% CPU** for minutes. The
  controller lease still named the terminated supervisor and read
  `runtime_state=busy`, so every client blocked on a dead owner, and
  `session doctor --repair` timed out on the Lazily actor too.

  The 45s self-reap has two escapes and BOTH were gated on evidence that is
  absent exactly when the wedge is worst: `preparing_controller_is_stale`
  returns `false` without a `handoff_started_at`, and
  `handoff_replacement_is_stranded` requires the temporary socket to still
  exist. Neither could fire, so nothing reaped the process. This is the
  `#idlerevisionreactive` inversion again — "I never looked" and "there is
  nothing wrong" must not collapse to the same answer.

  `preparing_without_a_handoff_clock` adds the honest fallback: the process's
  own age, which is always known. A handoff that never recorded its clock and
  has outlived the staleness threshold is reaped on age alone. Terminal states
  (`Stable`, `Retiring`, `Failed`) are never reaped this way however old they
  are, young handoffs are left alone, and a recorded clock still leaves the
  decision to the existing predicate.

  The same shape was recorded earlier as a wedge that survived three hours on a
  current binary with no explanation. This explains it.

## 0.35.232

- **Instrumentation (`#caswriteracechurn`): `ops summary` buckets CRDT settle
  waits by write site.**

  The item asked to measure the compare-and-swap settle wait before fixing it,
  and `ops summary` did not surface `*_crdt_convergence_wait` at all — the only
  way to see how often a closeout took the CAS branch, and which writer it
  contended with, was to grep the raw log by hand. The event name is dynamic
  (`pending_write_...`, `serialized_atomic_write_...`), so it cannot be a fixed
  `OpsLogEvent` token; `crdt_convergence_wait_site` recovers the site from the
  name and the classifier buckets on it. A site names one write path, so the
  bucket answers "what is the contending writer" directly.

  First measurement off this repo's `ops.log`, 196 CAS lines / 97 waits:

  - **100%** of convergence waits were `compare_and_swap_raced`. No other
    contention state fired even once, so this is not one reason among many.
  - 577.6s of settle wait total. `serialized_atomic_write` dominates (366.5s,
    max 65.0s), then `pending_write` (102.0s) and `start_resume_id_capture`
    (93.1s); `queue_sync`, `pending_maintenance`, and
    `session_check_response_replay_dedup` raced once each.
  - Of 34 contention windows, **12 are one session contending with itself across
    several write sites** — self-inflicted, the branch the item said to fix by
    collapsing writes. 15 have more than one session's lines interleaved, and 7
    are a single site.
  - The three sessions in the log write three DIFFERENT documents
    (`agent-doc-bugs2.md`, `lazily.md`, `tsift.md`) in one project root.
    Whether they actually contend or merely overlap in time is NOT established
    by the log — that needs the compare-and-swap's scope, which this change does
    not determine and which `#casselfinflictedcollapse` carries.

  The wait itself is unchanged. Shortening it is explicitly not the fix; the
  wait is what makes the write land.

## 0.35.231

- **Fix (`#coinedguardledgerasymmetry`): the two coined-id guards disagreed
  about what a done archive vouches for.**

  The `PreToolUse` guard ran a whole-text tag scan over the archive; the
  post-commit `session-check` guard read only the leading id of an archive
  entry. Same document, same archive, different answers — nothing failed, the
  guards simply diverged. That divergence is also what made a verification probe
  in this series pass for a reason its author did not intend.

  - `done_archive::archived_tracked_ids` is now the single predicate both guards
    read. The **narrow** reading wins: an entry has the binary-owned shape
    `- YYYY-MM-DD [#id] text`, and only that leading id names tracked work. A tag
    merely cited in an entry's prose is a reference, and vouching for it
    launders a coined id into legitimacy forever — the rule
    `#hookhashanchortags` rejected. Anchors quoted in archived prose stay
    allowed because `instruction_surface_anchors` covers them by name.
  - Resolution is the UNION of both strategies the guards previously used —
    declared `archive=` targets and sibling `<stem>.done.md` files walking up —
    so unifying the predicate cannot lose an archive either guard used to find.
    `done_archive_candidates` moved out of `agent-doc-hooks-io` to sit beside it.
  - `agent-doc-element-backlog-io/tests/coined_guard_predicate.rs` pins the
    invariant: every guard calls the shared predicate, and no guard resolves the
    archive itself. Scoped to archive resolution — both guards still call
    `extract_tags` on the live document's own components, which is correct and
    was never the drift. A third test asserts the retired call names still exist,
    so a rename cannot make the guard pass vacuously.

## 0.35.230

- **Fix (`#anchorsubmodulesurfaces`): 0.35.229's anchor harvest did not reach
  the submodule that DEFINES the anchors, so the reproduction it was meant to
  fix still fired.**

  `instruction_surface_anchors` read surfaces under the *document's* project
  root. For `tasks/agent-doc/agent-doc-bugs2.md` that root is the agent-loop
  superproject — but agent-doc's own development rules live one submodule down
  in `src/agent-doc/AGENTS.md` and never ship in the installed SKILL. So
  `#drain-no-defer` and `#preflightinbinary` resolved (they are in the shipped
  SKILL) while `#ci-no-closeout-wait` and `#deploy-just-do-it` did not.

  `submodule_instruction_files` now adds the root instruction files of every
  submodule declared in `.gitmodules`. Bounded on two axes, because this feeds a
  `PreToolUse` hook and this superproject declares 70 submodules: only declared
  paths (never a directory walk), and only each submodule's ROOT
  `AGENTS.md`/`CLAUDE.md`/`SKILL.md` — not its runbooks or specs, which is where
  the bulk of the bytes are. A path that is absolute or escapes the root is
  skipped. Measured on agent-loop: 212 files, 1.37 MB, 9 ms — and still only on
  the path where a tag is already about to be reported.

## 0.35.229

- **Fix (`#hookhashanchortags`): quoting an agent-doc instruction anchor by
  name was reported as coining an id.**

  `#preflightinbinary`, `#drain-no-defer`, `#ci-no-closeout-wait` and the rest
  are DEFINED in AGENTS.md, the SKILLs, the runbooks, and the specs — they name
  documented invariants, so citing one is the correct thing to do. But the
  known-id universe was only the session document plus its `.done.md` archives,
  so every such citation resolved to nothing and was flagged. Reproduced twice
  in one session on 2026-08-09.

  This is the half that 0.35.228's prose scoping deliberately did NOT fix:
  these tags really are in prose, so it was never a lexical problem. It is
  ledger coverage.

  - `agent_doc_fs::instruction_surface_anchors` harvests anchors from the
    project's instruction surfaces: root `AGENTS.md` / `CLAUDE.md` / `SKILL.md` /
    `SPEC.md`, `runbooks/*.md`, `specs/*.md`, every installed harness skill plus
    its runbooks, and `.cursor/rules/*.md`.
  - Both guards use it — the `PreToolUse` coined-id block and the post-commit
    `session-check` warning — from one definition, so the file set cannot drift
    between them.
  - The harvest is **lazy**: it runs only once a tag is already about to be
    reported. The overwhelmingly common call carries no id at all and never
    touches those files, which matters because `PreToolUse` fires on every Edit
    and these surfaces are hundreds of kilobytes.
  - Best-effort by construction: an unreadable surface contributes nothing.
    That set only ever widens what is allowed, so failing soft costs a false
    block at worst, never a missed one — the mirror image of
    `known_ids_for_document`, which fails closed because it is the primary
    ledger.
  - Rejected the broader alternative the item recorded (treat any id present in
    tracked repo content as vouched-for): it conflates "someone typed this
    before" with "this names tracked work", and would let a stale id in an old
    comment vouch for itself forever.

## 0.35.228

- **Fix (`#hookhashjsprivatefield`, `#hookhashfalsepositive`): the coined-id
  guard scanned code, so every language that spells something `#word` was a
  false block.**

  Third incident, third language. C preprocessor directives came first and were
  patched with an extension list plus a directive keyword list. Then ES2022
  private class fields: `coined_id_scan_text` returned the text unsanitized
  unless the extension was C-family, and `extract_tags`'s only guard is "not
  glued to a preceding word" — for `this.#entries` the preceding character is
  `.` and for an indented `#hlc;` declaration it is a space, so both pass and
  yield tags. Every `Edit` to a JS file with private fields was blocked.

  Enumerating languages one incident at a time does not converge, so the scope
  is inverted. A commit message is prose end to end and is still scanned whole.
  A source file is prose only in its comments, so
  `agent_doc_turn::prose_scope::prose_only` blanks everything outside them for a
  language whose comment syntax is known, preserving character positions and
  newlines. Code stops being scanned at all.

  - Covers C/C++/ObjC, Rust, Go, Java, Kotlin, Swift, Scala, C#, Dart, PHP, Zig
    and friends (`//`, `/* */`); the JS/TS family including template literals;
    Python/shell/YAML/TOML/Ruby (`#`); CSS/SCSS/Less; SQL/Haskell/Lua (`--`);
    HTML/XML/Vue/Svelte (`<!-- -->`).
  - An unknown extension and every prose format (`.md`, `.txt`, extensionless)
    are returned untouched and scanned whole, so widening the table can only
    reduce false blocks and never opens a hole.
  - Strings are skipped, so `"http://x/#frag"` does not read as a comment. An
    unterminated single-line string stops at the newline rather than swallowing
    the rest of the file.
  - Replaces `C_FAMILY_EXTENSIONS` and `C_FAMILY_PREPROCESSOR_DIRECTIVES`
    entirely, so a directive that was never on the keyword list is handled too.

## 0.35.227

- **Fix (`#skillinstallconfigpath`): `skill install` wrote machine-local junk
  into two Codex artifacts.**

  `.codex/config.toml` is project-local and usually tracked, but the installer
  baked the installing machine's ABSOLUTE path into the agent-doc MCP server
  args (`args = ["mcp", "serve", "--project-root", "/home/you/..."]`). The value
  churned per operator, and a temp-dir test install left
  `/tmp/agent-doc-v035139-final` committed in this repo until this cycle.

  - The installed args are now just `["mcp", "serve"]`. With no
    `--project-root`, `agent-doc mcp serve` walks up from its own working
    directory to the nearest project root; outside any project it stays put,
    which is what an absent flag already did. Reinstalling replaces the whole
    server table, so a legacy absolute entry is migrated away.
  - `.codex/hooks.json` was serialized without a trailing newline, so every
    install dirtied the file with a `\ No newline at end of file` diff. All
    three JSON hook writers (Codex hooks, Codex turn-status, Claude
    `settings.json`) now end with a newline.
  - Those writers and the TOML config writer skip the write entirely when the
    rendered content is unchanged. A byte-identical rewrite was churn in a
    tracked file and its mtime bump alone produced `audit-docs` staleness
    advisories.

## 0.35.226

- **Fix (`#skillinstallstalemirror`): nothing compared installed runbooks to the
  bundle, so a release could ship harness runbooks several versions behind the
  binary while every version marker matched.**

  0.35.224 shipped with the `.claude` / `.codex` / `.opencode` / `.cursor`
  runbooks still describing the retired `--baseline-file` command. `skill
  install` does content-compare each runbook, so the copies it manages were
  never the whole story — the hole was that `audit-docs` only checked the SKILL
  surfaces and the OKF bundle, never the runbooks, and `make check` (what the
  release process actually runs) did not call `audit-docs` at all.

  - `audit_managed_runbook_bundles` compares every installed runbook to the
    running binary: a stale file, a missing file, unbundled Markdown in a
    harness runbook directory, and a surviving retired mirror all block.
  - `make check` now runs `audit-docs`, so the release path exercises it.
  - `.codex/runbooks/` is formally retired. `skill install` removes the bundled
    files it left behind and the directory once empty, keeping anything
    agent-doc never bundled; `audit-docs` blocks while a copy survives. The
    managed Codex location is `.codex/skills/agent-doc/runbooks/`.
  - New project config `skill_runbook_mirrors` opts a project-owned directory
    (for example a superproject `runbooks/` that `CLAUDE.md` links to directly)
    into carrying bundled copies. `skill install` refreshes the bundled files
    there and `audit-docs` blocks on drift, but neither creates the directory
    and neither reaps a file agent-doc does not bundle — these directories also
    hold project-owned runbooks. Default empty, so an unrelated
    `runbooks/commit.md` is never clobbered by accident.
  - `release-version` no longer stamps the version into installed skill copies.
    Stamping a copy whose body was never regenerated made a stale install look
    current to every version-marker reader; the installer owns installed
    surfaces and `audit-docs` owns their freshness.
  - `skill install --root <PATH>` installs into an explicit root. Root
    resolution prefers the superproject, so the submodule-local
    `src/agent-doc/.claude/...` copies were previously unreachable; `make
    install` / `make install-full` now refresh them, and `audit-docs` audits
    that root alongside the superproject.

- **Fix: `admission_budget_honours_the_operator_override` read the real process
  environment**, so the suite went red for anyone with
  `AGENT_DOC_PREFLIGHT_HOOK_BUDGET_SECS` exported — including agent-doc's own
  dogfooding supervisor, which sets it. The parse now lives in a pure
  `resolve_hook_admission_budget`, and the test covers unset, blank,
  unparsable, zero, and a usable override.

## 0.35.225

- **Fix (`#clearsubmitunobserved`): a `/clear` that succeeded was reported as an
  unknown, blocking Run Agent Doc.**

  Operator-reported on `haiven-dev.md`: the supervisor was dead, the flow
  escalated to a cold start, the replacement was proven live — and then
  `session_clear` failed closed with `result=submission_unobserved
  command_visible=false`, "whether the clear ran is unknown".

  `poll_context_clear_submit_acceptance` can only observe a **transition**: it
  accepts when it sees the command in the composer at some poll, or sees the
  pane content change against the pre-delivery hash. But `/clear` is
  **idempotent** — its contract is a state ("this pane holds no conversation"),
  not a transition. A cold-started pane has nothing to clear, so a successful
  clear changes nothing, and a fast submit can slip between polls. Both signals
  are then absent for a clear that did exactly what was asked. That is why the
  failure showed up immediately after a cold start rather than at random.

  (The pre-delivery baseline is captured strictly before the send — that half of
  the original report's hypothesis was wrong, and the code is correct there.)

  Before reporting the unknown, the timeout path now asks the state question
  directly: capture the pane **including scrollback**
  (`agent_doc_tmux_io::capture_pane_history`) and accept as
  `AcceptedClearedState` only when the pane holds no conversation
  (`context_clear_history_proves_cleared_state`: command not in the active
  composer, a dispatch-ready prompt present, at most
  `CONTEXT_CLEAR_CLEARED_STATE_MAX_HISTORY_LINES` retained lines).

  This cannot weaken the guard. A clear that was genuinely lost leaves its
  conversation in scrollback — orders of magnitude over the threshold — so it
  stays blocked, as do a composer still holding the command, a capture failure,
  and a pane with no settled prompt. The only outcome that changes is the one
  where the desired end state demonstrably already holds. Callers gate on
  `ContextClearSubmitStatus::is_accepted()`; a caller still writing
  `== Accepted` would silently keep failing closed.

## 0.35.224

- **Fix (`#respondbaselineflag`): the MANDATORY persist step documented a flag
  that no command accepts.**

  `SKILL.md`, `runbooks/commit.md`, `runbooks/pending-ops.md`,
  `runbooks/streaming-checkpoints.md`, and `runbooks/baseline-drift.md` all told
  the agent to run `agent-doc respond <FILE> --baseline-file
  <preflight.baseline_file> --stream --origin skill`. Neither `respond` nor
  `write` has a baseline flag, and `preflight` emits no `baseline_file` field —
  `CycleState` retired it. An agent following the hot path verbatim got a clap
  parse error on the one step the skill calls mandatory.

  The binary had already made the decision: `agent-doc-workflow`,
  `agent-doc-turn`, `agent-doc-flow-io`, and `plan.rs` each assert that emitted
  commands contain **no** `--baseline-file`. Only the instruction surfaces and
  `specs/07-{closeout,core,orchestration}-commands.md` were stale. The baseline is
  binary-owned cycle state; the surfaces now say so explicitly, and a new
  `skill.rs` guard fails the build if any bundled surface puts the flag back into
  a command template or reintroduces the retired `baseline_file` field.

- **Fix (`#planhead`): `agent-doc plan` derived its own "which head is next" and
  disagreed with preflight.**

  `plan` called `queue_heads::active_queue_prompt`, which returns the first head
  regardless of drainability, while preflight's `selected_queue_prompts` applies
  the in-session drainability filter. On a go-mode queue whose head is
  `[operator-verify]`, plan named the human-gated item in `prompt_targets`,
  `repo_actions`, and `required_commands` — and SKILL.md step 0d tells the agent
  plan's output *is* the execution contract, so an agent following it works the
  wrong item and then `--done`s an operator-gated one. Observed three times.

  Two independent derivations is the defect, not the specific filter. New
  `queue_continuation::dispatchable_head_prompt_text` is the single source: it
  prefers the drain-active head and, on a queue that is active but not in
  explicit go-mode (no auto-drain, so the head is a manual prompt), falls back to
  the plain active head still filtered by the scope's deferral set — an
  `[operator-verify]` head is never selected either way. The architecture-policy
  guard in `tests/test_cli.rs` now forbids `plan.rs` from reaching back for the
  raw head.

## 0.35.223

- **Fix (`#failfastproxyload`): two fail-fast tests asserted a flat 1s wall clock
  and went red on a loaded CI runner.**

  `serialized_atomic_write_defers_zero_replica_editor_owner_without_touching_disk`
  turned the 0.35.220 CI run red while taking 0.02s locally, and the change under
  test does not touch that path.

  The assertion is a *proxy* for "did not enter the bounded editor-delivery
  wait", and the thing it distinguishes against is
  `VISIBLE_WRITE_CURRENT_TRANSITION_TIMEOUT_MS` — 5 seconds. One second left only
  5x headroom, which CI scheduling noise consumes. The bound is now derived from
  that stall threshold (half of it) rather than hardcoded, so the test still
  fails on a genuine stall — which costs 5s or more — without failing on load.

  Not waived as flaky: the fragility is real and lives in the proxy, so the proxy
  is what changed. Same class as 0.35.215's `clear_op_capture` budget.

## 0.35.222

- **Fix (`#mutprovenancepreresponse`): closeout provenance was recorded after the
  response, so a write that failed post-landing left nothing to prove the
  mutations were ours.**

  0.35.216 taught `session-check` and `commit` to tell "this closeout's own
  unlanded mutations" from "a fresh operator edit" by reading `CycleState`'s
  `pending_done_ids` / `pending_added_ids`. That works whenever the mutation
  phase ran. It does not when the *response write itself* fails after the
  response has landed: `apply_pending_and_status_mutations` never executes,
  nothing is recorded, `recorded_tracked_work_is_unlanded` correctly reports "no
  unlanded mutations of mine", and the divergence is classified
  `UnansweredEditPending` again.

  Observed 2026-08-09 closing `#hooktriggerunresolved`: `respond` hit the
  pre-write delivery barrier, the response reached HEAD, the `--done` never
  applied and was never recorded, and `session-check` gave the unanswered-edit
  verdict with the queue head unstruck. Recovery worked only because the operator
  still knew the intent — nothing in the document or state proved it.

  The requested mutations are now recorded where `#prmergeguardpr` already
  validates the envelope: after validation, before the response cell is
  published. Provenance therefore exists no matter where the write fails.
  `RecordedTrackedWork` carries the intent alongside the post-hoc record and
  treats them the same — both mean "this closeout asked for a mutation that is
  not visible yet" — and intent that HAS landed stops claiming the divergence, so
  later operator edits are not captured forever.

  Kept deliberately separate from `pending_done_ids` rather than recorded early
  into it: closeout-signal logic reads that field as "this cycle recorded a
  completion outcome", and a failed cycle claiming its queue head was answered
  would strike a head that never got done.

  Only explicitly-named add ids (`id=<id>` / `[#id]`) are knowable before the add
  runs; generated ids stay covered by the post-hoc record. Recording failure is
  logged, never fatal — provenance is a recovery aid, and failing the turn there
  would turn a bookkeeping problem into a lost closeout. Ordering is guarded
  structurally (`the_intent_is_recorded_before_the_response_write`), because a
  behavioural test that succeeds cannot see it. Mutation-checked.

## 0.35.221

- **Fix (`#resumeownerfmread`): `agent-doc start`'s resume-claim check paid two
  controller round trips per attached sibling document; it now pays one, total.**

  Measured 2026-08-09 over the window after 0.35.199 made resolve attribution
  trustworthy, `resume_id_owner` was the single largest resolve source (213),
  with `start_resume_id_capture` second (126). `#projpassstart` had already ruled
  out a projection pass — each candidate is read exactly once per `start`, so a
  memoizing cache has nothing to hit.

  The original plan for this item, a frontmatter-scoped relay read, was **wrong**
  and is corrected here: the cost is the round trip, not materialization.
  `canonical_text` goes through Yrs `get_string`, which walks the whole tree
  regardless of how few bytes the caller wants, so a prefix API would still pay
  the per-candidate trip.

  So the *question* moves to where the hubs already live. A `resume_claimant`
  controller RPC takes the candidate list and answers from the local hub registry
  with no IPC per candidate (`#lazily-hot-path`: ask the actor once instead of
  polling it N times). Candidates whose hub is not allocated are detached, so
  disk is already their authority and the controller reads their bytes directly.

  Fail-safe by construction: an `Err` — no controller, or one that cannot answer
  — falls through to the original per-candidate loop rather than reporting "no
  owner", because a missed claim adopts someone else's conversation. The claim
  rule is one predicate (`document_claims_resume_id`) checking BOTH `resume` and
  `agent_doc_session`, so the batch and per-candidate paths cannot drift.
  Mutation-checked: dropping the `agent_doc_session` half reddens its test.

  The `state.db` `documents.session_id` shortcut stays rejected — it is a durable
  sink (`#lzdurablesink`), and a stale row would either invent an owner (silently
  losing a conversation resume via the `#resumeclaim` start-fresh path) or miss a
  real one.

## 0.35.220

- **Fix (`#pullnoackdeadlock`, follow-up to 0.35.217): a replica that never ACKs
  re-earned the full redelivery budget on every new write.**

  0.35.217 cleared the redelivery streak on ANY enqueue, reasoning that "a NEW
  head is not a redelivery of the old one". That is right for a replica that was
  caught up and wrong for one already behind: each new write re-armed the whole
  budget, so the barrier came back for ~25 seconds per write instead of staying
  released. Observed immediately after 0.35.217 shipped, on the release cycle
  itself — the same client re-armed at `redeliveries=0` on generation 7 while its
  `last_ack_generation` had been stuck at 5, and two consecutive closeouts hit
  the pre-write delivery barrier again.

  The streak now clears only when the member was actually caught up
  (`pending.is_empty()` before the push) — forward progress, not mere activity.
  Both enqueue sites (fan-out relay and bootstrap) follow the same rule.

  Also removed a duplicated assignment left in the 0.35.217 patch at the fan-out
  enqueue; it was harmless but wrong-looking, and its indentation was mangled.

## 0.35.219

- **Fix (`#hooktriggerunresolved`): a trigger whose document path did not resolve
  went completely silent, which is the one state the hook contract forbids.**

  `run_preflight_for_prompt` had two silent exits that meant very different
  things. `invoked_document` returning `None` is a genuine non-trigger and a
  correct no-op. But `resolve_document` returning `None` means the prompt **was**
  an `agent-doc <FILE>` trigger and only the PATH failed — and that also returned
  `NotATrigger`, emitting neither the contract marker nor the admission-failure
  marker.

  That is exactly `#hookcontractlost`: the agent cannot distinguish an absent
  hook from a refusing one, is told to treat it as a harness defect it cannot
  diagnose, and stops. The branch immediately below already states the rule —
  "A trigger that reached preflight must never produce silence" — but the
  protection began one line too late.

  Observed 2026-08-09 on `tasks/agent-doc/agent-doc-bugs2.md`: a
  `/loop agent-doc tasks/agent-doc/agent-doc-bugs2.md` turn produced no marker of
  either kind and no preflight activity at all. A repo-relative target resolves
  against the hook's working directory, so any cwd that is not the project root
  silently un-triggers the hook.

  An unresolvable trigger now emits the admission-failure marker with the target,
  the cwd it was resolved against, and the remedy. A genuinely unrelated prompt
  is still a silent no-op — the hook must not start shouting about every prompt
  in the session, and that case has its own assertions. Mutation-checked.

## 0.35.218

- **Fix (`#queueblockquoteintent`): the binary's own queue-prompt echo stalled
  the binary's own drain, silently.**

  The `> **Queue prompt:**` blockquote is written by the closeout that drains a
  head, so `#ftstrike` can prove which head the response answered — the same
  scaffold `tail_repair` already refuses to accept as tracked component content
  ("binary conversation scaffold"). But it lives in `exchange` rather than in a
  managed component, so the per-line managed-state filter never saw it and it
  classified as a fresh user prompt.

  Observed on `tasks/agent-doc/agent-doc-bugs2.md`: preflight reported
  `queue_active: true` with four drainable heads and
  `queue_continuation_required: false`, because the single entry in
  `user_intent_prompt_changes` was the previous closeout's own echo of
  `do [#retainedmutdrop]`. Exactly the `#resumeuuidstallsdrain` shape: the
  binary's bookkeeping preempting the binary's drain.

  `text_is_managed_state_only` now recognizes the labeled echo block. It is
  deliberately strict — every line must belong to the quote block, so an
  operator who quotes a prompt and then writes prose underneath is still
  steering and still preempts.

- **Fix: a suppressed continuation was indistinguishable from a drained queue.**

  The preemption was ANDed into `raw_required` before
  `effective_continuation_output` saw it, so the result was
  `required: false, guidance: None` — the same output as a genuinely empty
  queue. In the case above, `ui_outcome` and `queue_continuation_guidance` were
  BOTH null with four heads remaining, and the cause could only be found by
  reading `user_intent_prompt_changes` by hand.

  The preemption is now passed in rather than pre-folded, and a drain stopped
  with heads remaining names its reason and points at the field that proves it.
  Recycle-yield still takes precedence, and a genuinely drained queue stays
  silent — there is no reason to give.

## 0.35.217

- **Fix (`#pullnoackdeadlock`): one editor replica that pulls and never ACKs
  wedged every write on the document, permanently.**

  `RelayHub::delivery_converged` required every **live** member to have an empty
  pending queue. Liveness only flips on explicit `disconnect`, so a replica that
  keeps pulling is maximally live — and the existing escape ("offline members are
  excluded so a slow editor cannot deadlock") could never fire for it. A replica
  that pulls the same undelivered head forever and never ACKs therefore held the
  delivery barrier with no exit.

  Observed on `tasks/agent-doc/agent-doc-bugs2.md`: editor client
  `5162727547735464` re-pulled `current_generation=5 last_ack_generation=4` at
  ~2/s indefinitely, with **zero** ACKs in the log. Every write blocked on the
  pre-write delivery barrier, `commit` refused, and preflight stopped admitting
  turns at all — `Lazily current authority remained delivery_pending`. The
  session could not make progress by any route.

  A member now holds the barrier only while its redelivery streak is within
  budget: each pull that hands out the same unacked head increments a counter,
  and any ACK that advances `last_ack_generation` (or a fresh enqueue) clears it.
  Past `MAX_REDELIVERIES_WITHOUT_ACK` (50 — roughly 25s at the observed pull
  rate, which no healthy editor approaches, since a healthy editor ACKs the
  delivery it just pulled) the replica stops blocking everyone else.

  It is **not** dropped: the update stays queued, so a recovered editor still
  receives it, and an ACK rehabilitates the replica for its next delivery.
  `nonconverging_replicas()` names them rather than releasing the barrier
  silently. Counting redeliveries instead of stamping a clock keeps the decision
  a pure function of the delivery stream, the shape `#idlerevisionreactive`
  settled on.

- **Fix: the same wedge re-flooded `ops.log`, defeating the `#crdtpullspam`
  guard.**

  That guard suppresses steady-state empty polls but logs whenever
  `current_generation != last_ack_generation` — which a permanently-unacked head
  makes true forever. 23372 lines were written for ONE undelivered generation.
  A pull whose replica has stopped holding the barrier now logs the transition
  once, naming `barrier=released reason=pull_without_ack`, then stays quiet.
  `ReplicaDeliverySnapshot` carries `redeliveries_without_ack` and
  `holds_delivery_barrier` so the state is inspectable rather than inferred.

## 0.35.216

- **Fix (`#retainedmutdrop`): a closeout's tracked-work mutations were
  reclassified as an operator edit and swept, while its response stayed
  committed.**

  This is the delivery half of `#prmergeguardpr`. 0.35.212 fixed the validation
  half — a rejectable flag can no longer split the transaction — but a *delivery*
  failure still could, and did.

  `session-check` and `commit` both decided "is this divergence the turn's own
  write or a fresh edit?" from **which components diverge**, on the premise that
  "`exchange` is where a response lives, so an exchange-clean divergence in
  queue/backlog/status is by construction not this turn's write". That premise is
  false: a closeout's write produces `queue`, `backlog`, `status`, `review`, and
  `done` as well — they are in the turn's own `write_set`. A response that
  committed while its tracked-work half did not produces exactly that shape, so
  it was labelled `UnansweredEditPending`, `commit` refused it on purpose, and
  the next cycle's recovery swept it.

  Observed end-to-end on `tasks/agent-doc/agent-doc-bugs2.md`: `respond` reported
  `completed and reaped 1 item(s) atomically: projpassstart`; the write was
  retained; `agent-doc commit` (the remedy the refusal itself named) landed the
  response into HEAD; `session-check` then declared `NO write is retained ... an
  UNANSWERED DOCUMENT EDIT to typed components this turn's write never produced`
  while its own `component_divergence` named `backlog`, `exchange`, and `queue`;
  the next preflight reported `recovered: true` and the mutations were gone —
  `#projpassstart` still open in backlog and still a queue head.

  Both sites now ask the fact that actually separates the two cases: are **this
  cycle's own recorded mutations visible on disk yet**? A recorded `--done` whose
  item disk still renders as `- [ ] [#id]`, or a recorded add that disk does not
  contain, is the binary's own unlanded write — `commit` reconciles it instead of
  stranding it. Once every recorded mutation has landed, a queue/backlog
  divergence really is a fresh edit and the refusal stays correct, which is what
  keeps `commit` from swallowing the operator's next prompt.

  The predicate is one shared function (`recorded_tracked_work_is_unlanded` in
  `agent-doc-turn`), so the two commands cannot drift back into contradicting
  each other the way `#strandedremedydeadlock` describes. Cycle state already
  recorded everything it needs — `pending_done_ids` and `pending_added_ids` — so
  no new state was introduced.

## 0.35.215

- **Fix (`#compactclearidempotent`): a compact reported a failed editor-op clear
  for an epoch that was already consumed, turning CI red.**

  `clear_op_capture` runs on a bounded 250ms budget. Under CI load that budget
  expired *after* the epoch was gone, so one compact logged the contradictory
  pair: `load_op_capture` read the capture as absent (the state was correct)
  while the same run logged `compact_pending_editor_cut_clear_failed` and never
  logged `compact_pending_editor_cut_consumed`. The 0.35.212 CI run failed on
  exactly that.

  `clear_replayed_editor_ops_after_compact` answers "is the epoch consumed?",
  not "did my transaction win", so it now re-reads the state after a clear error
  and reports `consumed outcome=already_absent_after_clear_error`. A clear error
  with the epoch **still outstanding** remains a real failure and still says so
  — this is not a blanket "report success on error".

  The decision is a pure function (`editor_cut_clear_outcome`) so all three
  cases are testable; the first attempt at this test was vacuous (with nothing
  to clear, `clear_op_capture` simply succeeds and the error branch never ran),
  which the seam fixes. Mutation-checked.

## 0.35.214

- **Fix (`#baretagidcollide`): a document-wide classification tag was single-use,
  and the second use failed the whole turn closed.**

  `explicit_custom_id` treated a leading bare `#tag` as an explicit id request,
  the same as `id=<id>` and `[#id]`. It is not one — it is an *inference* the add
  path makes so `[operator-verify] #someid text` keeps `#someid` instead of
  deriving a slug from the tag's own words. Because the inference was enforced
  like a request, the first `--backlog-add "#agent-doc-bug ..."` claimed the id
  and every later one was refused by `#preset-item-id-collision-enforce`, even
  though the caller never asked for an id at all. The spec's own rule is that
  auto-id adds are never blocked.

  An inferred id that is already active now means "this token was a
  classification tag": the add keeps the tag in the item text and takes a
  generated id. `extract_inline_tag_id` degrades the same way. An explicit
  `id=` / `[#id]` collision still fails closed — that really is an ambiguous
  request.

  Found by 0.35.212's own guard, on the turn that shipped it: the rejection
  arrived *before* the response was written and nothing was applied, which is
  exactly the behavior `#prmergeguardpr` added. Under the previous release the
  same input would have stranded a durable response beside unapplied mutations.

  Mutation-checked: reverting the collision filter reddens
  `a_taken_bare_tag_becomes_body_text_instead_of_failing_the_add`.

## 0.35.213

- **Fix (`#preflightprojpass`): the missing projection pass was at the write
  command, not inside session-check.**

  0.35.206 wrapped session-check's two entry points and claimed that stopped the
  guard suite re-resolving per guard. It did not, and the next closeout's
  measurement said so: `session-check:pending_done_mode` still resolved
  `uninstalled` four times. The guards that dominated the 190-of-226
  `uninstalled` measurement never enter a wrapped session-check entry point.
  `respond` / `write --commit` call `precommit_pending_done_check_with_options`,
  `run_closeout_pending_maintenance`, and `enforce_review_done_guard` **directly**,
  and the command's own readers — `observe_live_queue_heads` (18),
  `pre_write_guards` (17), `verify_pane_ownership` (17),
  `canonicalize_response_for_capture` (17),
  `captured_finalize_resume_pre_capture_editor_save` (13) — never go near
  session-check at all. The pass belongs one level up, at the write command.
  `run_command_inner` now opens it, guarded structurally by
  `the_write_command_entry_point_opens_the_projection_pass`. The 0.35.206
  VERSIONS.md entry carries the correction.

  Opening a pass over a *mutating* path is the part that can be wrong, so two
  properties had to hold first, and neither did:

  - **Every write self-invalidates.** Requiring each mutating caller to remember
    `invalidate_current_document_projection` is how a cache starts serving stale
    text: one forgotten call is a silent wrong answer, and "did every mutator
    remember?" is not a property review keeps true. Invalidation moved to the two
    write chokepoints in `agent-doc-document-realtime-io` — the rebased-write
    inner (wrapping its several success exits, since the CRDT/editor delivery
    returns never reach the disk write below them) and
    `atomic_write_authority_raw` (which force-disk writes reach without the
    former). Both run on failure too: a refused or retained write may still have
    advanced the CRDT, and a spare invalidation costs one re-resolve while a
    missing one is incorrect. `both_write_chokepoints_invalidate_the_projection`
    guards them structurally, because a behavioural test cannot see a *missing*
    invalidation on a path whose revision happens to move anyway.
  - **A revision that cannot move is never a cache key.**
    `CurrentRevision::EditorAttachedMissingReplica` is a unit-like constant: with
    no replica, neither the canonical state vector nor the disk bytes identify
    what a reader will get, so memoizing under it pinned the first resolve for a
    whole pass. It now returns no key at all and the read bypasses the pass,
    tagged `bypassed_no_content_identity` so the choice is visible in `pass=`.
    (`Detached` was already safe — the disk hash is substituted for it.)

  Mutation-checked: dropping the missing-replica case, dropping a chokepoint
  invalidation, and removing the pass wrap each redden their own guard.

  The preflight half stays closed: preflight phases contributed zero resolves in
  every window measured.

## 0.35.212

- **Fix: a rejectable tracked-work mutation could split the closeout
  transaction after the response was already durable (`#prmergeguardpr`).**

  `respond --stream --done ... --backlog-add ...` published the response cell
  first and applied the tracked-work mutations second. When a mutation turned
  out to be rejectable — an explicit `[#id]` colliding with an active backlog id
  per `#preset-item-id-collision-enforce`, a malformed `id=text` pair, a `--done`
  naming an item that is not there — the turn failed *after* the response was
  durable, leaving neither the `--done` nor the `--backlog-add` applied. The
  operator was then reading a document whose response claimed an item was done
  while the backlog still showed it open, recoverable only by a manual
  owning-pane commit. AGENTS.md requires response + queue-head consumption +
  backlog/done mutations + snapshot + commit to succeed as one transaction or
  none become authoritative; a mutation-validation error alone was enough to
  break that.

  Closeout now runs the mutation envelope twice. The first pass is a dry run
  through `with_pending_write_transaction_dry_run`: the identical mutation code
  against virtual document content, every buffered document/archive target
  discarded, no cycle state, queue projection, snapshot checkpoint, or status
  write recorded. It runs before the response cell is written, so a rejectable
  flag set fails the turn with nothing applied. The second pass applies and
  publishes as before.

  A tracked-work failure that survives the dry run is a delivery/projection
  failure rather than a validation error, and its message now distinguishes
  "response landed, mutations pending" (naming the half-applied state and both
  recovery commands) from the already-distinct "whole write pending" case.

  Regression coverage in `tests/finalize_integration.rs`: a colliding
  `--backlog-add` leaves no `### Re:` on disk or in HEAD and leaves the existing
  backlog item open; a valid envelope still applies the response and its
  mutations exactly once. A mutation probe with the validation call disabled
  reproduces the original split exactly.

## 0.35.211

- **Fix: 0.35.209's budget-overrun test was racy and turned CI red.**

  `budget_overrun_reports_a_reason_instead_of_hanging` drove the timeout with a
  1ns budget against real preflight on a nonexistent path. That is a race, not a
  test: preflight can fail *faster* than the budget expires, in which case
  `recv_timeout` returns the completed result and the assertion lands on the
  wrong branch. It passed locally and failed on CI, which is the signature.

  `run_within_budget` now takes the work as a parameter, so the overrun test
  supplies a worker that provably does not finish until released. Two
  complementary branches are pinned alongside it: work finishing inside its
  budget still admits (the guard must not refuse an ordinary fast preflight),
  and a failing preflight keeps its own reason instead of being relabelled as an
  overrun. Verified deterministic over 8 consecutive runs.

## 0.35.210

- **Fix (`#ctrlsockpathtoolong`): a project root long enough to overflow
  `sun_path` made every client poll for a controller that could never exist,
  instead of saying so.**

  Found while verifying the 0.35.209 hook budget: a **brand-new empty document**
  burned the full 90s preflight admission budget. Document size was never the
  driver — the session root was 108 bytes, so
  `<root>/.agent-doc/controller.sock` came to 135 against the 107-byte `AF_UNIX`
  limit. The bind failed with a bare OS error indistinguishable from "the
  controller has not started yet", so `wait_for_controller_path_with_timeout`
  polled it until the budget ran out. Every symptom read as "agent-doc is slow".

  The supervisor socket already handles this by relocating to a short runtime
  path. The controller socket deliberately does **not**: the JetBrains and VS
  Code plugins resolve `.agent-doc/controller.sock` themselves, so relocating it
  would bind the controller somewhere no editor ever looks — trading a loud
  failure for a permanently missing editor authority. An over-long root is
  therefore a permanent condition, and the fix is to name it immediately.

  `agent_doc_controller::paths::resolved_socket_path_rejection` is the one
  predicate, enforced at the three places that used to hide it: the wait loop
  bails before its first sleep, `connect_path` / `connect_path_async` replace the
  bare OS error, and `serve_with_options` refuses to start before touching the
  filesystem. It measures the path actually being bound, so a longer
  generation-scoped handoff socket is caught too. The message carries the path,
  its byte length, the limit, and the remedy.

  Measured on the original repro: **90s to 1.3s**. Regression coverage pins the
  budget property itself (a 30s wait must refuse in under a second, and must not
  report a timeout), that a single connect names the limit, and — guarding the
  false positive that would refuse every controller connect in the product —
  that an ordinary root still fails as an ordinary connect failure.

## 0.35.209

- **Fix (`#hookcontractlost`): a slow preflight still went silent — the harness
  killed the `UserPromptSubmit` hook at 30s and discarded its output.**

  Operator-reported 2026-08-09 on `tasks/software/lazily.md`:

  ```
  UserPromptSubmit hook timed out after 30s — output discarded.
  ```

  0.35.207 made preflight *name* its refusals on stdout, but a killed hook emits
  nothing at all, so this reproduced the original silence exactly: no contract,
  no reason, and no way for the agent to tell a slow hook from an unwired one.

  Measured on that document: preflight takes **23-34s** at **~10% CPU** — almost
  entirely blocked on controller round trips, not compute (`#preflightprojpass`
  tracks the redundancy itself). Claude Code's default hook timeout is 30s, so
  admission sat directly on the cliff and failed intermittently.

  Two bounds now, an outer and an inner. The installed Claude hook entry carries
  an explicit `timeout` (`PREFLIGHT_HOOK_TIMEOUT_SECS`, 120s), written on a fresh
  install *and repaired onto an already-installed entry* — the checkouts that
  need it most are the ones already wired without it. Inside that, the hook runs
  preflight against its own `HOOK_ADMISSION_BUDGET_SECS` (90s, overridable with
  `AGENT_DOC_PREFLIGHT_HOOK_BUDGET_SECS`) and converts an overrun into the normal
  `ADMISSION_FAILURE_MARKER` refusal naming the budget and where to look. A test
  pins `timeout > budget`: if they crossed, the harness would kill the hook
  before it could name its overrun and the silence would return.

  Ordering stays safe by construction — `CONTRACT_MARKER` is printed only after
  preflight returns, so a worker finishing mid-report can emit a truncated
  contract but never the seal, and the agent's three-state read still lands on
  "admission failed".

  Worth recording from the fix: a preflight against a **brand-new empty
  document** also blew the 90s budget, which rules out document size as the
  driver and points at project-controller bootstrap.

## 0.35.208

- **Fix (`#backlogclosemarkeroffbyone`): `backlog remove` / `icebox remove`
  could not remove anything — every invocation failed the structural-target
  guard, and matching by the id `backlog list` renders never matched.**

  `op_remove_matching_tracked_line` rebuilt the component body with
  `body.lines().join("\n")`, which drops the body's trailing newline. That body
  is spliced back *between* the component markers, so the closing marker was
  welded onto the surviving last item line and `persist_pending_write` refused
  the write:

  ```
  pending_write: refusing structurally invalid canonical target
  (non_standalone_component_marker:backlog:close:lineN)
  ```

  The reported line looked off by one because it named the welded *item* line,
  one above where the operator saw the marker — and it tracked the document
  because it is a real line number, not an offset. The guard was correct; the
  rendered target was genuinely invalid. It fired even when nothing matched,
  because the join ran unconditionally, so `remove` was wedged for every target.

  Removal now runs over parsed `PendingLayout` items rather than raw lines, so a
  multi-line item's continuation lines leave with it, and
  `preserve_trailing_newline_shape` pins the invariant the join broke: the body
  ends with a newline afterwards exactly when it did before.

  Second half: `remove` matched only `trim_tracked_parent_prefix(line) ==
  target`, so the identity `backlog list` prints (`[#someid]`) matched nothing —
  `remove someid` and `remove '#someid'` both answered `no matching item found`,
  and even a copied list line failed because `list` renders the `- ` bullet the
  comparison had stripped. `remove` now resolves the hash id first (bare or
  `#`-prefixed, via `normalize_pending_id`), then the full item line in either
  bullet form, then the bare item text. A no-match now skips the write instead
  of persisting an unchanged document, and says what it tried to match.

  Regression coverage: first-item and last-item removal round-trips on a
  document whose closing marker is the final component line, every rendered
  identity matches, continuation lines leave with their item, and a no-match
  leaves the body byte-identical.

## 0.35.207

- **Fix (`#hookcontractlost`): the `UserPromptSubmit` hook could run preflight
  and then emit nothing at all, leaving the agent with no contract and no
  reason.**

  A `UserPromptSubmit` hook injects **stdout** as turn context. Every preflight
  diagnostic went to **stderr**, so when preflight refused, the reason reached
  the operator's hook log and never the agent — which saw an ordinary prompt
  with no contract, indistinguishable from a hook that was never wired.
  Observed 2026-08-09 across three consecutive turns.

  The hook now always names its outcome on stdout. Three states are separable:
  the `[agent-doc] cycle contract ...` marker means admitted; the new
  `[agent-doc] cycle contract UNAVAILABLE (preflight admission failed; ...)`
  marker — followed by `document:`, `reason:`, and `remedy:` lines — means the
  hook ran and preflight refused; neither marker means the hook never ran. The
  failure marker is deliberately distinct from the success seal, since admission
  did fail and the agent must not proceed as though a cycle were open.

- **Fix (`#hookcontractlost`): a preflight recovery error that blocked contract
  emission could only be cleared by preflight, so the wedge sustained itself.**

  Preflight owns recovery *before* diffing, so `enforce_no_uncommitted_closeout_drift`
  bailing aborted the hook before any contract could be emitted. The remedy the
  error named (`agent-doc commit <FILE>`) can only be run by the operator,
  because the agent is correctly instructed never to shell preflight itself —
  and nothing else clears the drift, so every following turn hit the same bail.
  The reported instance needed a hand-run `agent-doc commit` to escape.

  Preflight now runs that remedy in place for *document-only* drift, mirroring
  the jb_cache_conflict_cancel auto-commit beside it. The predicate is read
  straight off the recovery classifier — `Clean`, `BoundaryOnlyDrift`,
  `QueueMetadataDrift`, and `NestedParentPointerStale` are exactly the states
  whose own recovery command is `agent-doc commit` and which have no response
  body at risk — so it cannot drift from the remedy the hint prints.
  `UnsafeUserContentDrift` (which says in so many words "do NOT `agent-doc
  commit`"), the response-bearing states, and states needing a different
  boundary are all excluded and still fail closed.

## 0.35.206

- **Fix (`#preflightprojpass`): the embedded closeout path never opened a
  projection pass, so every guard re-resolved the whole document.**

  With 0.35.203's `pass=` field finally making the outcome readable, the answer
  this item had been chasing for four cycles came out in one measurement. Of 226
  resolves on 0.35.204: **190 `uninstalled`** — no pass open at all — against
  **12** genuine `miss` and **zero** `bypassed_no_revision`.

  So the redundancy was never invalidation tuning. Both earlier hypotheses were
  wrong in the same way: the pass was simply not there. `run_with_options` (the
  `session-check` CLI) opened one; `enforce_clean_closeout` — the path `respond`
  and `write --commit` actually run — did not, while going through the identical
  guard suite.

  Note what that implies for the ~11 `invalidate_current_document_pass` calls in
  `session-check`: with no pass installed they were inert, so they have been
  correct-but-unused on this path. Opening the pass is what activates the design
  they were written for, and it is safe in the order this item recorded — the
  one mutating call on the path (`recover_retained_document_write`) already
  self-invalidates before any later read.

  A behavioural test cannot catch this class: an unwrapped path is merely
  *slower*, never wrong, which is exactly why it survived. The entry points are
  few and named, so `every_session_check_entry_point_opens_the_projection_pass`
  guards them structurally. Mutation-checked.

  The preflight half stays closed: preflight phases contributed **zero**
  resolves across every measurement window.

  **CORRECTION (0.35.213).** The claim above that wrapping
  `enforce_clean_closeout` stops the guard suite re-resolving per guard is
  **wrong**, and a post-install measurement of the very next closeout disproved
  it: `session-check:pending_done_mode` still resolved `uninstalled` four times.
  The guards that dominated the measurement never enter *either* wrapped
  session-check entry point — `respond` / `write --commit` call
  `precommit_pending_done_check_with_options`,
  `run_closeout_pending_maintenance`, and `enforce_review_done_guard` directly,
  and the command's own readers (`observe_live_queue_heads`,
  `pre_write_guards`, `verify_pane_ownership`,
  `canonicalize_response_for_capture`) never went near session-check at all. The
  missing pass was one level up, at the write command itself. 0.35.213 opens it
  there. The paragraph above about the ~11 `invalidate_current_document_pass`
  calls being inert stands; the sentence about "the one mutating call on the
  path" does not generalize to the write path, which mutates constantly — see
  0.35.213 for why that is safe.

## 0.35.205

- **Investigation (`#dupreheadingcollide`): two responses sharing a `### Re:`
  heading is not a defect. Closed with proofs, not a change.**

  Reusing `### Re: #resumeuuidstallsdrain — opus-5` across consecutive cycles on
  `tasks/agent-doc/agent-doc-bugs2.md` made cell-merge key the second block as
  `:duplicate:1`, and that set up `#commitwritecommitdeadlock`. The open
  question was whether the keying itself was wrong — whether an exchange
  response cell should key on its cycle/capture id instead of heading text.

  Two properties were measured rather than assumed, and both hold:

  - **The keying is lossless.** `duplicate_re_heading_keeps_both_bodies` drives
    the real `merge_by_component` path with a duplicate heading and asserts both
    bodies and both headings survive. Nothing was ever at risk of being dropped.
  - **The capture guard matches on BODY, not heading.**
    `a_shared_heading_with_a_different_body_is_not_materialized` asserts that a
    second response sharing a heading reads as *not* materialized against a
    document holding only the first — and that a genuinely present one still
    reads as materialized. A heading-level match would have been far worse than
    the deadlock: the guard would allow the closeout and the response would
    vanish from the commit with no error anywhere.

  So the answer to "re-key response cells on cycle id" is **no**. The text keying
  is sound, and changing it would put `#qdedup-directive-twin`'s intentional
  duplicate multiplicity at risk to fix nothing. The only damage the collision
  caused was the mutual-remedy deadlock, already fixed in 0.35.204.

  Both tests stay as permanent regressions: they pin properties a future keying
  change could otherwise break silently.

## 0.35.204

- **Fix (`#commitwritecommitdeadlock`): three commands named each other on a
  `write_applied` cycle.**

  Observed 2026-08-09 on `tasks/agent-doc/agent-doc-bugs2.md` at
  `cycle-1786271908581`:

  - `respond` failed with the `AwaitingTerminalCommit` remedy — "Finish it from
    the pane that OWNS this session: `agent-doc commit <FILE>`"
  - `agent-doc commit` reached the missing-captured-response guard — "Replay the
    captured response with `agent-doc write --commit <FILE>`"
  - `agent-doc write --commit` answered with the same `AwaitingTerminalCommit`
    remedy, naming `agent-doc commit` again

  A closed cycle with no exit. This is exactly the failure `#strandedremedydeadlock`
  fixed in 0.35.197 for the `commit`/`session-check` pair — in a **different
  pair that fix did not cover**. `AwaitingTerminalCommit` assumes `commit` can
  always finish a `write_applied` cycle; the capture-materialization guard is an
  independent predicate that can refuse exactly that.

  The guard now picks its remedy from the same shared predicate
  (`commit_is_the_named_recovery`, added by 0.35.197). When the verdict is one
  that routes through `agent-doc commit`, naming `write --commit` is what closes
  the loop, so the remedy names the next cycle instead — `agent-doc <FILE>`,
  whose preflight owns capture materialisation. That is the recovery that
  actually worked on the incident: it committed both responses with no text
  lost. The ordinary missed-patchback case, where nothing owns a retained write,
  still names `write --commit`, because there it genuinely works.

  `agent-doc-git-io/src/capture_materialization_guard.rs` joins the
  `#percellconverge` refusal-site guard as its **fifth** site. Two regressions
  pin both branches, mutation-checked; the message may still *mention* the
  wrong command to warn against it — prescribing it is what fails.

  Trigger context, recorded but not itself fixed: two consecutive responses
  shared an identical `### Re:` heading, so cell-merge keyed the second as
  `:duplicate:1` and the snapshot never took it. Filed separately.

## 0.35.203

- **Fix (`#passattrib`): a `realtime_doc_resolve` could not say why it missed,
  and `session-check`'s resolve source was unparseable.**

  Two instrument defects in the same measurement, both blocking the remaining
  `#preflightprojpass` work.

  **`session-check` composed its source with a space** —
  `format!("session-check {source}")` — so all 62 of its resolutions parsed as
  the bare token `session-check` from a space-delimited ops line. They were
  really ten distinct guards; the breakdown only appeared after re-parsing the
  field by hand (`pending_done_mode` 16, `late_ipc_response_overapplication` 11,
  `unstarted_prompt_bearing` 9, …). This is the same defect 0.35.199 fixed one
  level up, where a hardcoded `source=crdt_relay` hid every caller: an
  unparseable field is as good as a missing one. The separator is now `:`.

  **Every emitted resolve is already a pass MISS** — a hit never calls
  `resolve` — but the line could not distinguish the three kinds, which want
  completely different fixes: `bypassed_no_revision` (revision lookup failed,
  so the pass was never consulted, and no pass tuning can remove the re-read),
  `uninstalled` (no pass open on this thread), and `miss` (a pass was consulted
  and the revision or manual epoch genuinely moved). Each now reaches the log as
  its own `pass=` field.

  Why this and not the optimization itself: a live `session-check` spends
  **624ms across 8 resolves — 25% of a 2485ms run** — and the same handful of
  guards repeat 2–3× within one run. But every pass invalidation in
  `session-check` is correctly gated on an actual mutation, so the repeats are
  not obviously waste, and nothing on the line said whether a given re-read was
  necessary. Removing invalidations on a delicate correctness path without that
  evidence is exactly the guess this item has already been wrong about twice.

  Both properties are guarded and mutation-checked: restoring the space fails
  the session-check guard, and collapsing two pass reasons into one fails the
  projection guard.

## 0.35.202

- **Fix (`#addvseditidsyntax`): `--backlog-add "<id>=<text>"` silently filed a
  corrupted item instead of erroring.**

  `--backlog-edit` takes `<id>=<new text>`. `--backlog-add` takes
  `id=<custom> <text>` — the literal keyword `id`. The same `=` separator means
  opposite things between two adjacent flags, and getting it wrong produced no
  error at all: the entire string became the item body, so

      --backlog-add "resumeuuidstallsdrain=#agent-doc-bug FIXED in 0.35.201..."

  filed an item whose text opened with a stray `resumeuuidstallsdrain=` and
  whose id was auto-derived from the *following* words as
  `resumeuuidstallsdrainbug`. Three items in
  `tasks/agent-doc/agent-doc-bugs2.md` were written that way on 2026-08-09
  before it was noticed. The damage is not cosmetic: the id the author meant
  never exists, so a later `--done`, `do [#id]`, or cross-reference resolves to
  nothing — precisely what the coined-id guard exists to prevent, arriving
  through the one path that bypasses it.

  It now fails closed, naming both correct forms. The guard only fires when the
  leading token's pre-`=` part is a *valid id shape* and carries no leading `#`,
  so prose keeps adding cleanly (`fix the x = y + 1 off-by-one`, `kernel arg
  root=/dev/sda1 breaks the initramfs`) and the established bare-tag label form
  (`#lazilyspecpin=Pin the vocabulary`, which deliberately keeps an auto id) is
  untouched — the first draft broke that case, and its own regression caught it.
  Pinned by a second test, because a guard that starts rejecting ordinary text
  would be worse than the bug. Mutation-checked.

## 0.35.201

- **Fix (`#resumeuuidstallsdrain`): the binary's own `resume:` UUID rotation
  read as a user prompt and silently halted go-mode queue drains.**

  Observed 2026-08-09 on `tasks/agent-doc/agent-doc-bugs2.md` mid-drain:
  `queue_active: true`, `queue_drainable_head_count: 5`, and yet
  `queue_continuation_required: false`. The sole entry in
  `user_intent_prompt_changes` was `resume: 06ff3b9e-…` — a UUID the binary
  itself rotates every cycle.

  `line_is_managed_state_only` (`agent-doc-diff`) hand-listed the managed
  frontmatter keys it knew: `queue_active:`, `agent_doc_pipeline:`, and the
  pipeline block's children. It did not list `resume:`. So a routine managed
  write became "fresh user intent", `prompt_changes_preempt_queue` returned
  true, and the drain stopped with work still queued — the failure mode looks
  exactly like an operator interrupting, which is why it is easy to miss.

  `agent-doc-frontmatter` already owns the authoritative set:
  `is_agent_managed_frontmatter_key` (`agent_doc_session` / `session` /
  `resume` / `agent_doc_pipeline`), used by the frontmatter three-way merge to
  decide which keys the agent may write. The classifier now derives from it
  instead of keeping a second list that can drift — the same duplicated-predicate
  failure as `#percellconverge` and `#strandedremedydeadlock`.

  `managed_state_covers_every_binary_owned_frontmatter_key` asserts both
  directions: every canonical managed key classifies as bookkeeping, and
  operator-owned keys (`claude_model`, `agent`, `goal`) still read as intent so
  a real operator edit keeps preempting the drain. Mutation-checked against the
  pre-fix list.

## 0.35.200

- **Fix (`#strandedremedydeadlock` follow-up): the new verdict claimed an author
  it could not prove.**

  0.35.197 added `OperatorEditPending` so a refusal would stop naming a command
  that refuses. The classification was right; the *name* was not. The sites that
  set it compare typed components — none of them can establish who authored the
  difference, and the common non-operator case is an earlier `agent-doc write`
  whose own commit refused, which leaves exactly the same shape.

  Caught the same day it shipped, in production: a `--backlog-add-after` left
  that drift, and the diagnostic told the session it was looking at an
  "UNANSWERED OPERATOR EDIT" by the operator. The prescribed action was still
  correct — run the document again, which reads the edit and commits it — but a
  diagnostic that invents authorship is how the next investigation gets
  misdirected, which is the exact failure this whole item was about.

  Renamed to `UnansweredEditPending` / `unanswered_edit`, and the wording now
  says what it knows: the divergence is an edit this turn's write did not
  produce, either operator steering or an earlier refused write, and the check
  does not guess which. The `#percellconverge` guard tracks the new phrase.

## 0.35.199

- **Fix (`#preflightprojpass`): `realtime_doc_resolve` could not name the caller
  that asked for the resolve, so the redundancy it exists to measure was
  unmeasurable.**

  Six emit sites in `agent-doc-document-realtime-io`. Four hardcoded
  `source=crdt_relay` — which is what *answered* the resolve, not who asked —
  and two omitted `source` entirely, even though the caller's `source` argument
  was in scope at every one of them.

  This is why `#preflightprojpass` was re-narrowed twice onto the same wrong
  answer. Both narrowings read the log, saw `crdt_relay` on top, and concluded
  the relay was the largest consumer. It is a constant: it was always going to
  win, and it says nothing about which caller to fix. Measured 2026-08-09 after
  0.35.195, **489 of 494** resolutions carried that constant, and the three
  sources the item had queued up to check next (`session`,
  `idle_watch_active_queue_head`, `serialized_atomic_write`) did not appear at
  all — not because they are rare, but because no site could report them.

  The caller's `source` is now emitted at every site, with the relay/detached/
  disk-only fact moved to a separate `answered_by=` field so neither shadows the
  other. Both facts are present; neither is inferred.

  Coverage is two tests because one is not enough here: a runtime test resolves
  through whichever branch its fixture takes — the first draft passed unchanged
  when `resolve_disk_only_current_doc` was mutated back to the constant, because
  that fixture goes through the detached branch — so
  `no_resolve_emit_site_hardcodes_the_caller_field` checks all six sites at once
  and is mutation-checked against both pre-fix shapes (pinned constant, and
  omitted field).

## 0.35.198

- **Fix (`#run3rdpaneswitch`): switching documents while a turn was running grew
  a two-column editor into a three-pane tmux window.**

  Operator-reported 2026-08-09: run Agent Doc, then switch to another document.
  The pane auto-syncs, and a THIRD pane appears holding the PREVIOUS session
  with its prompt submitted and its turn still running — a live agent session,
  not inert layout drift.

  `src/layout.rs` was the wrong half. When it arranges panes to mirror the
  editor split, it breaks unwanted registered panes out of the target window —
  except that a pane running a live agent session was **skipped**: "skip busy
  panes (running agent-doc/claude sessions)". So the outgoing pane stayed
  visible, the incoming document's pane joined the same window below it, and a
  two-document projection became three panes.

  Skipping was never what protected the session. `break_pane` moves a pane to
  another window; the process and its turn keep running either way, so
  visibility and survival are independent properties. `sync.rs` had already
  settled this for open-cycle panes — "Stashing is non-destructive, so the
  closeout keeps running without forcing a third visible pane into a
  two-document editor projection" — and its regressions prove it stashes them.
  `layout.rs` never got the same treatment, which is why the two entry points
  behaved differently on the same layout.

  A busy pane now moves to the tracked `stash` window (`break_pane_to_stash`)
  rather than an untracked orphan window, so a later sync can rescue it. Busy
  changes *where* a pane goes; it never changes *whether* it leaves the visible
  projection.

  Coverage: `unwanted_pane_disposition` makes the rule a pure function pinned by
  `a_busy_unwanted_pane_is_stashed_never_left_visible`, and
  `switching_documents_while_the_outgoing_turn_runs_stays_two_panes` stages the
  operator's report end to end against live tmux through an injected busy probe.
  Both were mutation-checked against the pre-fix behavior; the live test
  reproduces the exact three-pane window.

## 0.35.197

- **Fix (`#strandedremedydeadlock`): `session-check` told the agent to run
  `agent-doc commit <FILE>`, and `agent-doc commit <FILE>` refused.**

  Observed 2026-08-09 on `tasks/agent-doc/agent-doc-bugs2.md` after
  `cycle-1786265239202` closed. `session-check` returned INTERRUPTED with
  `component_divergence=queue:<a>-><b>`, the `Stranded` verdict, and its remedy:
  "nothing owns this write … recover from the pane that OWNS this session:
  `agent-doc commit <FILE>`". Running exactly that command answered "refusing to
  close as already committed: the staged snapshot matches HEAD, but the
  editor-authoritative document has uncommitted queue, backlog, status, or other
  typed-component drift without an exact binary-owned retained-target proof".

  The named recovery was refused by the state that named it. Worse than a bad
  error message: `Stranded` means *nothing holds this write*, so no other
  command was going to commit it either — the operator's live queue edits had no
  path to `HEAD` at all, and an agent following the instruction faithfully had
  no next move.

  Two predicates, one state. `commit`'s already-current path held its own rule
  (any non-exchange typed-component drift without a retained-target proof is
  terminal) with no connection to the ownership predicate that sends agents
  there. This is the same lesson as `#percellconverge` and
  `#ownershipverdictdiverges`: consolidating *wording* does not stop *predicates*
  from diverging.

  So the verdict now owns both halves.
  `RetainedWriteVerdict::commit_is_the_named_recovery` is the single fact, and
  `commit` derives its refusal from it: `Stranded` and `AwaitingTerminalCommit`
  — the two verdicts whose remedy names `agent-doc commit` — reconcile, absorbing
  the editor-authoritative document into the snapshot and committing it through
  the normal path. `Deferred` still fails closed, because a durable holder does
  commit that write itself and absorbing its in-flight components would race the
  capture being awaited; that refusal now quotes the shared remedy instead of
  re-authoring one, so it can never again name a command that refuses.

  Two regressions pin the behavior, and a third pins the property: the
  `#percellconverge` architecture guard gains `agent-doc-commit-io` as its
  fourth refusal site, and `commit_accepts_exactly_the_verdicts_whose_remedy_names_it`
  asserts the biconditional across every verdict, so the remedy and the
  commit-side predicate cannot drift apart again.

## 0.35.196

- **Fix (`#backlogeditcorruptsreview`): a scoped tracked-work edit could destroy
  items in a DIFFERENT component, silently.**

  Observed 2026-08-09 on `tasks/agent-doc/agent-doc-bugs2.md`: one long
  `--backlog-edit`, issued in the same `respond` invocation as the response body,
  applied correctly to its backlog line AND ALSO truncated `agent:review` item
  `#stalesupervisorreexecdrain` mid-word — splicing the replacement text onto it
  — while deleting `#stickymdpaneverify` and `#operatorverifyrestart` entirely.
  Review count 8 -> 6. Only a clean HEAD made it recoverable; the CRDT replica
  pushed the corruption back over a `git checkout`, so it lived in the editor
  authority, and repair had to go back through `--review-edit` / `--review-add`.

  **The mechanism is still unproven, and this fix deliberately does not depend on
  one.** The staging transaction was audited and is sound — it reads virtual
  content between mutations and fails closed on a lost cursor. The same
  invocation logged a 24.9s CRDT settle and a `cell_merge` Content conflict on a
  DUPLICATE exchange heading identity, which is the strongest remaining lead.

  Whatever produced the target, the invariant holds: a write scoped to one
  component must leave every other component byte-identical.
  `ensure_only_component_changed` now checks that before the edit path publishes,
  and refuses otherwise. Silent destruction of tracked work becomes a failed
  command.

  Coverage includes the exact live shapes — vanished review items, and a
  replacement spliced onto a truncated neighbour. One test also pins a
  correction: `element::parse` does NOT error on unstructured text, it returns
  zero components, so the component-set comparison is what catches that case; the
  first draft of the test assumed a parse error and asserted the opposite.

  Scope: the guard covers the edit path where the corruption was observed. The
  add path and the multi-component `respond` envelope are not yet covered.

## 0.35.195

- **Fix (`#preflightprojpass`, relay half): the current-document projection cache
  split one document into two entries whenever callers spelled its path
  differently.**

  `pass_projection` and `invalidate_current_document_projection` keyed on the
  path AS GIVEN. Measured on `tasks/agent-doc/agent-doc-bugs2.md` in a single
  20-minute window: 43 resolutions under the absolute path and 40 under the
  repo-relative path — every one a guaranteed miss against the other spelling's
  entry, each costing a full CRDT materialization.

  The wasted work is the smaller problem. Two entries hold two independent
  revisions, and invalidation clears only the spelling it is handed — so an
  agent-authored mutation invalidated under one spelling leaves the other serving
  PRE-MUTATION text. That is precisely the trap the module's own doc comment
  warns about ("memoizing only by path can hide operator edits made during the
  pass"), reached by a route the comment did not anticipate.

  Both sites now key on the canonical path, falling back to the given path when
  canonicalization fails (a document that does not exist yet), which preserves
  the previous behavior for that case.

  `two_spellings_of_one_document_share_one_projection_entry` asserts the cache
  hit AND the cross-spelling invalidation. It uses a `..` hop rather than a `.`
  hop because `Path` equality normalizes `.` away component-wise, so a `.` alias
  would not have been distinct in the first place — the test would have passed
  without testing anything. Verified by mutation: reverting the key to
  `to_path_buf()` reddens it.

  **Scope: `#preflightprojpass` remains open.** This removes a major source of
  redundant relay resolutions but is not proven to be the only one, and the
  preflight half is still behind its recorded blocker (the seven mutating phases
  must self-invalidate before the pass may wrap `run_with_options`).

## 0.35.194

- **Fix (`#ownershipverdictdiverges`): the retained-write VERDICT contradicted
  `session-check`, even after 0.35.192 unified the wording.**

  `RetainedWriteOwnership::cycle_open` lumped all three open phases together —
  `preflight_started`, `response_captured`, and `write_applied` — and any of them
  produced `Deferred`, whose remedy promises "the same intent commits itself once
  delivery converges ... do NOT re-send".

  Those phases are not equivalent. At `preflight_started` and `response_captured`
  a state edge really does still fire. At `write_applied` the response write has
  ALREADY landed and only the terminal commit is outstanding — `session-check`
  reports that cycle INTERRUPTED, and only `agent-doc commit` advances it. So the
  write path told the agent to wait for something that was never going to happen,
  while `session-check` correctly called the same state interrupted. That is the
  exact predicate-level divergence `#percellconverge` exists to prevent, which
  0.35.192 relocated from the prose to the verdict rather than removing.

  Observed three times on 2026-08-09 on `tasks/agent-doc/agent-doc-bugs2.md`.
  Each time the refusal said to wait, `session-check` then reported
  `write_applied`, and one `agent-doc commit` finished it.

  `write_applied` is now its own verdict, `AwaitingTerminalCommit`, checked
  BEFORE the broader `cycle_open` arm that used to swallow it. Its remedy names
  `agent-doc commit <FILE>` — the command that actually recovers — while still
  forbidding re-send, force-disk, and recycle, because the body IS durable and a
  re-send would duplicate rather than repair. The previous remedy named only
  `session-check`, an OBSERVATION command, leaving an agent with an accurate
  diagnosis and no way to act on it.

  `session-check` now builds the predicate with the phase too, so the site that
  was already right and the write path derive the same answer from the same
  facts. The new verdict's phrase joins the anti-drift guard's protected set.

  Found by the compiler, not by review: adding the variant broke `session-check`'s
  exhaustive match, which is what surfaced that it was constructing the predicate
  without the phase.

## 0.35.193

- **Fix (`#queuestallloopfalsepositive`): `queue_stall_detected` fired on every
  healthy self-paced drain cycle.**

  The stall classifier is correct and already takes a `loop_is_continuing` fact.
  The defect was the evidence fed to it: preflight answered that question with
  `fresh_loop_drain_owner_lease`, i.e. the drain-owner **ownership** TTL of 90s.

  Those windows answer different questions. Ownership must expire fast so a
  stopped loop hands the queue back to the unattended drainer — that is correct
  and unchanged. Continuation is about whether a loop is still working the queue,
  and a self-paced cycle legitimately spans minutes: a full verification suite, a
  build/install, then a scheduler wakeup with jitter. Nothing refreshes the lease
  mid-cycle, so by the next preflight the lease had always expired.

  Measured on `tasks/agent-doc/agent-doc-bugs2.md`, from `ops.log` rather than
  inference: `queue_drain_stall_continuation_recorded` -> next
  `preflight_diff_start` gaps of 122s and 187s against the 90s TTL. The warning
  fired on three consecutive healthy cycles, each of which was in fact the
  continuation.

  Continuation now uses a separate `drain_stall_grace()` (default 600s,
  `AGENT_DOC_DRAIN_STALL_GRACE_SECS`, floored at the ownership TTL so a lease that
  still confers ownership always still counts as continuation). A genuinely
  stopped loop still trips the diagnostic once that longer window lapses — firing
  late is strictly better than firing every cycle, because a warning that is
  usually wrong trains its reader to ignore it, which costs exactly the real stall
  it exists to catch.

  `drain_stall_grace_outlasts_ownership_and_a_healthy_cycle` pins both halves: the
  grace exceeds the two measured healthy gaps, AND the ownership TTL is shorter
  than them — so the test also records the precondition that caused the bug.

## 0.35.192

- **Fix (`#retainedwriteremedy`): a retained-write refusal told the agent nothing
  had been written, when the write had already applied.**

  `defer_visible_delivery_projection` ended its message with "no secondary
  snapshot/commit or forced disk write was attempted" and stopped there. Its two
  sibling branches, in the same function, both append the remedy from
  `agent_doc_turn::write_ownership::retained_write_remedy` — this one did not.
  The message reads as *nothing happened*, but the cycle was in `write_applied`:
  the response body HAD landed and needed only the terminal commit, which
  `agent-doc commit <FILE>` completes. An agent that reads it as a lost response
  re-answers or resubmits, which is exactly what `#percellconverge` forbids.

  Observed twice on 2026-08-09 on `tasks/agent-doc/agent-doc-bugs2.md` — once
  from `respond`, once from a follow-up `write --commit` — both recovered by a
  single `agent-doc commit`.

  The existing guard could not catch it: it asserts each refusal-site FILE
  contains a call to the shared predicate, so one compliant call anywhere let a
  new branch beside it inherit a pass it never earned. The new
  `every_constructed_retained_write_refusal_names_its_remedy` is site-scoped — it
  walks each `await_editor_replica_no_disk_write(` construction, extracts its
  balanced argument expression, and requires the remedy inside that expression.

  It immediately found **eight more** refusals with the same defect. Rather than
  append the remedy at nine call sites — a rule that holds only while every
  author remembers it — construction now routes through one `retained_refusal`
  helper that appends it structurally.

  The guard is demonstrably able to fail: it went red three times while being
  written, surfacing progressively more sites, and the compiler validates every
  converted call site's arguments.

## 0.35.191

- **Perf (`#preflightprojpass`, partial): the resume-claim scan materialized
  EVERY document in `state.db` to read two frontmatter fields.**

  `resume_id_owner` walks every `canonical_path` in `state.db` looking for a
  document that claims the requested conversation id, and resolved each one
  through the realtime authority — a full CRDT materialization (~186ms measured)
  per document, O(documents) of them for a single claim check. The revision API's
  own doc comment warns against exactly this ("reaching for the text entry point
  and discarding the body").

  Measured on this project's `ops.log`: `source=start_resume_id_capture` (136)
  and `source=resume_id_owner` (54) were the two largest resolution sources,
  ahead of `session` (98) and `idle_watch_active_queue_head` (50).

  The scan now probes the cheap revision first. A `Detached` document — one no
  live editor owns — has disk as its authority, so the expensive path would only
  fall back to the same bytes; it now reads disk directly. Attached documents
  keep the authoritative path.

  Deliberately fail-safe: a document whose probe ERRORS also keeps the
  authoritative path. A missed claim adopts someone else's conversation, so an
  unclassifiable document is never downgraded to a possibly-stale disk read.
  `only_a_detached_resume_claim_candidate_reads_disk` pins all four cases.

  **Scope: this does not close `#preflightprojpass`.** The item's two named
  halves are untouched — the preflight half is still behind its recorded blocker
  (preflight's seven mutating phases must self-invalidate before the pass may
  wrap `run_with_options`), and the relay-consumer half remains. This fixes a
  THIRD consumer that the measurement surfaced as the largest one, and the item
  stays open, narrowed with these numbers.

## 0.35.190

- **Fix (`#idcollisionnamespace`): a derived item id could duplicate an id that
  was already active in another source, and nothing suffixed it.**

  `assign_unique_id` already appends a short hash when a derived id is taken —
  but its `taken` set was built only from the list being added to, while the
  `preset_item_id_collision` guard evaluates identity across `agent:backlog`,
  `agent:review`, sibling tracked-work lists, and prompt presets. A slug derived
  from an item's own words could therefore land on an id held by review or a
  preset, and the resulting two-active-meanings ambiguity blocked queue dispatch
  on something nothing had a chance to prevent. `explicit_new_item_id_collision`
  even documented the assumption — "auto-id adds never collide" — which was
  false.

  The cross-source identity map that the guard uses (`document_active_identities`)
  is now exposed as `document_reserved_identity_ids` and threaded into the add
  path via `op_add_at_with_outcome_reserved` / `op_add_with_outcome_reserved`;
  `backlog_cmd::add` and `review_add` pass the document's active ids. A derived
  id that duplicates one is suffixed exactly as an in-list duplicate already was,
  so readability is kept (`livepaneproof` -> `livepaneproof<hash>`).

  Deliberately narrow: EXPLICIT ids keep their own dedicated cross-source guard
  (`ensure_new_item_explicit_id_available`) so their fail-closed message stays
  specific, and the existing signatures delegate with an empty reserved set, so
  no caller changes behavior implicitly.

  Coverage: `a_derived_id_dodges_ids_reserved_by_other_sources`, plus
  `an_empty_reserved_set_reproduces_the_collision` — a sensitivity test that
  reproduces the old id exactly, so the fix is proven to be what does the work.

- **Verified (`#loopcontractseal`): the Claude Code queue auto-loop re-entry now
  seals a cycle contract end to end.** A `ScheduleWakeup`-fired
  `/loop agent-doc <FILE>` arrived with the `[agent-doc] cycle contract` marker
  and a bound `queue_task_id`, on the 0.35.188 wrapper fix — live proof, where
  0.35.188 shipped with unit-test proof only.

## 0.35.189

- **Fix (`#operatorverifyliveid`): a leading `[operator-verify]` tag swallowed the
  item's explicit `#id`, so every operator-gated follow-up derived the same id
  and collided.**

  `leading_bare_tag_id` promotes a `#tag` to the item id only in **leading**
  position. The SKILL tells agents to tag operator-gated follow-ups
  `[operator-verify]`, which pushes the explicit id out of that position — so the
  id the agent supplied was discarded and `derive_representative_id` minted a
  slug from the tag's own words instead. `[operator-verify] ...` yields
  `operator` + `verify` + the next word for *every* such item, so any two of them
  collide by construction.

  Observed live on `tasks/agent-doc/agent-doc-bugs2.md`: a new
  `[operator-verify]` backlog item and an existing `[operator-verify]` review
  item both landed as `#operatorverifylive`, and the resulting
  `preset_item_id_collision` warning blocked queue dispatch.

  Leading bracketed **state** tags are now split off before both steps: the
  explicit `#id` after the tag is promoted (with the tag preserved on the item
  text), and when there is no explicit id the tag contributes no words to the
  derived slug. The split is deliberately narrow — a bracketed custom id
  (`[#id]`) keeps its own earlier parser, and bracketed prose, unterminated
  brackets, and multi-word brackets are left alone.

  Known remaining gap, filed as actionable backlog: `assign_unique_id`'s `taken`
  set is built only from the backlog list, while the collision guard evaluates
  across backlog + review + presets, so a cross-component collision still is not
  auto-suffixed. This fix removes the collision *class* that the SKILL's own
  conventions create; widening `taken` needs the id namespaces threaded into
  `op_add_at_with_outcome`.

  Coverage: `a_leading_state_tag_does_not_swallow_the_explicit_id`,
  `a_leading_state_tag_does_not_contribute_id_words`, and
  `state_tag_split_is_narrow` in `agent-doc-element-backlog`.

## 0.35.188

- **Fix (`#loopcontractseal`): the Claude Code queue auto-loop could not seal a
  cycle contract, so a drain with work still on it stalled.**

  Settled the open question the item recorded, with a third answer neither of its
  branches anticipated. A `ScheduleWakeup`-fired `/loop` re-entry **is** a
  `UserPromptSubmit` event, and the hook **does** run — `.claude/settings.json`
  wires it unconditionally. But the loop skill re-arms with the original input
  prefixed by `/loop `, and the agent-doc SKILL dispatches the auto-loop as
  `Skill(loop, args: "agent-doc <FILE>")`, so the prompt's first word is `loop`.
  `invoked_document` matches the trigger strictly on the first word, so it
  returned `None`, preflight never ran, and no contract was emitted. The skill
  then (correctly) failed closed on the missing marker — leaving drainable heads
  with the operator, which `#drain-no-defer` forbids.

  Observed directly: the operator-typed `/agent-doc` trigger logged
  `preflight_diff_start` at 05:51:40Z; the `/loop agent-doc <FILE>` wakeup that
  fired ~25 minutes later logged nothing.

  `invoked_document` now consumes an optional leading `loop` / `/loop` wrapper —
  with the optional interval token the loop skill accepts (`5m`, `30s`, `2h`,
  `1d`) — before matching, so the auto-loop re-entry seals a contract exactly
  like the operator-typed trigger. Only that one wrapper is recognized, and it
  loosens nothing else: non-cycle subcommands, extra arguments, non-`.md`
  targets, nested `/loop /loop`, other wrappers such as `/schedule`, and prose
  after `loop` all still return `None`.

  Coverage: `the_queue_auto_loop_reentry_is_the_same_trigger` and
  `the_loop_wrapper_does_not_widen_admission` in `agent-doc-hooks-io`.

## 0.35.187

- **Fix (`#panewindowdrift`): editor auto-focus was mirrored onto a pane the
  operator could not see.**

  The pane-layout projection's file→pane assignment, the actor row, and the
  registry entry each carry the window captured when the pane was *bound*, and
  nothing re-derives it. Move that pane into a `stash` window and every
  record-vs-record comparison still agrees — so the layout survey admitted a
  stashed pane as a visible column and `apply_pane_layout_focus_effect` ran
  `select-pane` on it. `session doctor` reported the drift (0.35.186) but nothing
  prevented it.

  The reference window is now explicit: the projection's **own** target window —
  `invocation.window` when it is already a window id, otherwise the
  `agent-doc`-named window in the project's configured tmux session, which is the
  same resolution the drift survey already performs. It is never an inferred
  active, first-column, or majority window, so a legitimate multi-window layout
  keeps its own reference. Before `select-pane` the effect compares that window
  against the pane's live `#{window_id}` through the pure predicate
  `pane_window_binding_drifted` and, on drift, publishes
  `focus_pane_not_co_visible:<file>:<pane>:live_window=<w>:layout_window=<w>` with
  `focus_applied: false`. Since convergence is
  `report.synced && (!focus_required || focus_applied)`, a refusal cannot report
  converged: the structural reconcile runs and the retry focuses the pane once it
  is back in the layout window.

  The drift survey applies the same rule to its observation. An effect/reuse
  assignment naming a pane outside the target window's own ordered pane list is
  not a visible column, so it no longer satisfies a layout slot or stands in as
  `expected_focus_pane` — a stashed pane stops counting toward
  `observation=synced`.

  Strict tightening throughout, mirroring the predicate: an unresolved window on
  either side is never drift, so a transient tmux read or an unconfigured session
  cannot refuse a legitimate focus.

  Coverage: `focus_is_refused_for_a_pane_stashed_out_of_the_layout_window`,
  `unresolved_windows_never_refuse_focus`,
  `layout_target_window_prefers_an_explicit_window_id`, and
  `stashed_assignment_does_not_satisfy_a_layout_slot_or_focus` in
  `agent-doc-controller-io`, plus the SimWorld
  `pane_layout_stashed_column_model` — which carries a record-only mutation that
  reproduces the pre-fix mirrored focus, so the passing tests prove the live
  comparison is what does the work.

## 0.35.186

- **Fix (`#stalereexecstarve`): a supervisor whose queue drain kept bailing early
  could never self-recycle, and ran a deleted binary indefinitely.**

  The idle-queue watch does two independent jobs per tick: drain the go-mode
  queue, and decide whether a stale supervisor should hot-reload onto the
  freshly-installed binary. The recycle decision is sequenced *after* the drain,
  and the drain's legitimate early exits — `QueueHeadObservation::AuthorityUnavailable`,
  an in-flight context clear, a settling clear cooldown — each `continue`d the
  whole **tick**. Any supervisor parked in one of those drain states therefore
  never reached the recycle decision, never published `binary_stale`, and never
  wrote the `⚠ STALE SUPERVISOR` pane marker, so the failure produced no
  diagnostic of its own.

  Observed live 2026-08-09 while proving `#unrenderedframestormlive`: the
  supervisor for `src/boost-client/tasks/monsterrodholders.md` (PID 4069526,
  started 2026-08-05) had run four days on a **deleted** binary image
  (`readlink /proc/4069526/exe` → `/home/brian/.cargo/bin/agent-doc (deleted)`)
  while its controller projection stayed unavailable. Its ops log carried 227
  `stale_supervisor_cp_recycle_requested ... request_status=requested` lines and
  **zero** `supervisor_binary_stale_*` lines across 198,660 entries — the
  requests were written and never consumed. Every other live supervisor had
  re-exec'd normally, which is why the staleness looked policy-shaped rather
  than control-flow-shaped.

  The queue drain now runs inside a `'drain:` labeled block: an early exit leaves
  the **drain**, not the tick, and the recycle evaluation always runs. The facts
  the drain would have produced are hoisted with conservative "nothing observed"
  defaults, which is exactly what the policy needs — `stale_recycle_safe_checkpoint`
  (stale binary, no IPC handler in flight) already licenses a recycle without a
  turn boundary or a pending head. A new
  `supervisor_stale_recycle_drain_fallthrough` receipt makes the guard provable
  from ops.log instead of only from source.

  `rustc` rejects a bare `continue` inside the labeled block (E0695), so the
  literal regression can no longer compile;
  `agent-doc-start-runtime-io/tests/idle_watch_drain_scope_guard.rs` covers the
  half the compiler accepts (a labeled `continue` aimed past the block) and pins
  the drain-before-recycle ordering the fix depends on. SimWorld's
  `stale_supervisor_recycles_even_when_the_drain_bails_early` carries the
  behavioral half, and the same drain-scoped early return is repaired in the
  SimWorld model, which had faithfully reproduced the defect.

## 0.35.185

- **Fix (`#terminalproofphaseregress`): a closeout that had already committed
  failed at the terminal proof because the phase read back regressed.**

  Cycle phase is monotone (`resolve_closeout_phase`), but the terminal proof
  re-reads cycle state and that read can land *below* what the same closeout
  already proved. `reconstruct_cycle_state` decodes the last
  `turn_intent_checkpoint` and rebases the closeout projection over it only when
  the projection matches the cycle, so an absent or non-matching projection
  leaves the phase baked into that older checkpoint. Observed 2026-08-08 on
  `tasks/agent-doc/agent-doc-bugs2.md` minutes after an `admin recycle`:
  `ensure_cycle_committed` passed, the document commit landed (`89f255b0e2`),
  and `record_terminal_closeout_proof` then read `response_captured` and failed
  the closeout — leaving an operator to hand-repair with `write --commit` a
  cycle whose commit had already succeeded.

  `ensure_cycle_committed` now returns the cycle id it proved, and
  `CompleteRequiredCloseoutOptions::proven_committed_cycle` carries it into the
  terminal proof. A sub-`Committed` read naming that same cycle is a lagging
  replica: the proof logs
  `terminal_proof_phase_regressed_after_commit_proof`, repairs the phase through
  `mark_committed`, and continues. Callers with no prior proof (preflight, any
  first-time caller) keep the strict fail-closed guard unchanged.

## 0.35.184

- **Fix (`#dedupepresettle`): a repair write reported success before its
  projection converged, so a duplicated response survived a "successful" repair.**

  Two sites shared one root cause: a repair was judged by the sample taken the
  instant after the write, before the delivery projection had published it.

  `await_late_editor_repair_projection` gave its settle window only to the
  `LateEditorAttachment` shape and returned immediately on `Diverged`. But a
  projection that is merely still propagating *also* samples as `Diverged` —
  canonical and disk both differ from the target because neither has caught up —
  so an in-flight repair was classified terminal on its first read.
  `settle_atomic_repair_projection` then reported `did not converge exactly
  before settling deferred lineage` for a write that converged milliseconds
  later. Observed 2026-08-08 on `tasks/agent-doc/agent-doc-bugs2.md`, where
  `session-check`'s own `self_heal_response_replay_duplication` failed
  terminally and a byte-identical rerun converged. The window now covers every
  non-converged sample (`atomic_repair_projection_should_await`), so the
  terminal classification is decided when the window closes rather than the
  instant after the write. Both typed refusals are reached with exactly the same
  semantics — only later. Renamed to `await_atomic_repair_projection` /
  `ATOMIC_REPAIR_PROJECTION_SETTLE_TIMEOUT_MS`, and the wait policy is now a
  pure loop over an injected sampler so it is testable without a live race.

  `agent-doc dedupe` inherited the ordinary write path's deferred-delivery
  success: it printed `removed 3313 bytes` and exited 0 with both response
  copies still on disk, and only a second identical run converged. Dedupe is a
  repair command whose whole contract is "the duplicate is gone", so it now
  re-reads the authority after each write, reapplies the cut against what it
  actually finds (bounded at 5 attempts), and reports removed bytes only from a
  proven fixpoint. An unconverged repair fails loudly and names the recovery
  without offering the disk-write escape hatch.

## 0.35.183

- **Fix (`#dedupnoopretain`): `session-check`'s response-replay dedup published a
  no-op repair and retained it forever, wedging the document.**

  `self_heal_response_replay_duplication` treated the normalizer returning `Some`
  as "a change is required". It only means "a duplication shape was recognized" —
  an *unrepairable* shape (two full-bodied copies, where only proven-empty replay
  shells are removable) legitimately normalizes to itself. The caller then routed
  a write whose target already equalled the authority. With no visible delta for
  an editor to acknowledge, the intent was retained indefinitely, and every later
  `session-check` re-reported a terminal failure and blocked the next cycle.

  Observed 2026-08-08 on `tasks/agent-doc/agent-doc-bugs2.md`. The log states the
  contradiction outright:

  ```
  session_check_response_replay_dedup_crdt_relay_exact_noop   <- recognized as a no-op
  write_authority action=routed ... len=25480 hash=7d5b76e…   <- routed anyway
  repair_projection_late_editor_retained                      <- retained forever
  ```

  The disk content hash was *already* byte-identical to the retained target, and
  an IDE restart registering mid-flight made the wedge permanent. HEAD was
  correct the whole time — only the self-heal was stuck, and it took the document
  with it. The dedup now skips a cut identical to current content and logs
  `session_check_response_replay_dedup_noop_skipped`.

  **Test limits are stated, not papered over.** The branch that returns an
  identical cut is the CRDT-relay path, which needs a live controller and editor
  registration and is unreachable from a temp-file unit test. A behavioural
  fixture was written first and mutation-checked: it passed with the guard
  disabled, because two full-bodied copies make the normalizer return `None` and
  never reach the guard. It was replaced by a wiring guard (mutation-checked: it
  fails when the guard is removed) plus the reachable behavioural half — nothing
  repairable means no write and no drift.

## 0.35.182

- **Decided (`#claudesplitsubmit`): claude does NOT join the split text+Enter
  submit profile.** No behavior change — this records a measurement that was
  previously an inference, so the question stops being reopened.

  Measured with the corrected metric from 0.35.181 (`agent-doc ops
  submit-profile`) over 23 post-0.35.164 `harness=claude` repairs:

  - **12 controlled dispatches into an idle, unloaded pane: 12/12 submitted on
    the first send, 0 resubmits.** Verified independently against Claude's own
    session JSONL — 12 recorded trigger prompts for 12 dispatches — because
    `outcome=cleared` alone could be an unrendered-pane false clear, and every
    line reported `elapsed_ms=305`, the minimum two-settle path. (Pane scrollback
    cannot be used for this: Claude's TUI redraws in place, so `history_size` is
    8 lines and holds no transcript.)
  - **11 production repairs from the same day under real load: 8 needed at least
    one bare submit key (72%), `max_enters_sent=2`.**

  So the drop is **load-dependent, not inherent to the single-call send**, and in
  all 23 samples the settle-gated repair recovered the dispatch — every line is
  `outcome=cleared` with zero `exhausted_still_stranded`. No dispatch was ever
  lost. Adding claude to the split profile would pay 80ms on every submit to
  remove a retry that already succeeds 100% of the time.

  The reasoning now lives on `harness_uses_split_text_submit` and in a test that
  states the measurement, so a future change has to confront the evidence rather
  than re-derive the inference.

## 0.35.181

- **Fix (`#ptsubmitmetric`): the `#passthroughsplitprofile` unblock criterion
  counted an event that can never be logged.** The old criterion was "a nonzero
  `outcome=enter_resubmit` rate in ops.log". `EnterResubmit` is a NON-terminal
  action — the repair loop `continue`s on it, and only terminal actions reach
  `log_op` — so that counter is structurally always zero no matter how many
  submits drop. It could never have unblocked the decision.

  The signal that does exist is `enters_sent > 0` on a terminal line, because
  `enters_sent` is incremented only inside the `EnterResubmit` branch. Reading it
  meant hand-parsing every line and knowing that history, so the terminal
  `route_pass_through_submit_draft` line now states the conclusion directly as
  `resubmit_required=true|false`.

- **New: `agent-doc ops submit-profile [--since <RFC3339 prefix>] [--json]`.**
  Rolls up pass-through submit repairs per harness — repair count, how many
  needed a bare submit key, the resulting rate, the outcome breakdown, and
  `max_enters_sent` — so the `#claudesplitsubmit` sample can be read off one
  command instead of grepping ops.log. Lines written before `resubmit_required`
  existed are still counted from `enters_sent` and reported separately as
  `legacy_lines_without_field`, so a mixed sample is declared rather than
  silently folded in.

## 0.35.180

- **Fix (`#clearsubmitlabel`): `session_clear` reported
  `result=command_still_visible` on lines that also carried
  `command_visible=false`.** The label stated the opposite of the observation, so
  it could not be used to tell the two failures apart.

  `ContextClearSubmitStatus::TimedOut` carried two unrelated deadlines. The
  composer still holding the clear command at the last capture is a wedged pane
  — delivered, not consumed, and the only shape an Enter-resubmit may retry. The
  window closing with no evidence in either direction is an unknown: the command
  was never seen and the pane never changed, so whether the clear ran cannot be
  said. Only the first is "still visible".

  Split into `StillVisible` (`result=command_still_visible`,
  `issue=prompt_not_submitted`, `unblocker=clear_command_not_consumed`) and
  `Unobserved` (`result=submission_unobserved`, `issue=submit_unobserved`,
  `unblocker=clear_submission_unobserved`,
  `next_action=verify_pane_state_then_retry`). The operator-facing message now
  matches what was observed too: "restore an idle prompt" is a guess when nothing
  was observed, so the unobserved case says the outcome is unknown and to check
  the pane.

  **The acceptance rule is deliberately unchanged.** Neither status is
  `Accepted`; both still fail closed through
  `require_context_clear_submit_accepted`, and only `StillVisible` may drive an
  Enter resubmit — an unobserved submit must not blind-resend into an unknown
  pane. This is a reporting fix, not a loosening: a clear that may not have run
  must never be reported as done.

## 0.35.179

- **Fix (`#autotriggeradmissionstall`): a restart-fresh auto-trigger reported
  `Sent` 23-27s after the harness had already taken the prompt.**
  `verify_auto_trigger_submitted` waited on ONE blocking
  `turn_admission_projection` RPC for its whole 25s budget before it looked at
  the pane. A triggered turn that legitimately admits no NEW document cycle —
  the common `no_changes` preflight outcome on a restart-fresh dispatch — never
  projects an admission, so the controller answered `{"ok":true,"data":null}`
  only at the 25s mark. For that entire window the actor stayed pinned `Busy` by
  `auto_trigger_inject` and `auto_trigger_outcome` stayed `Pending`.

  Measured live on 2026-08-08 across three restart-fresh Claude spawns in a real
  9-row pane, sampling the pane every 250ms: the composer rendered at ~3.3s, the
  trigger was visibly submitted and the harness working by ~5s, and
  `auto_trigger_sent` was not recorded until 23-27s after attach — with
  `controller_response_missing_data command=turn_admission_projection` landing at
  exactly 25s every time.

  The admission await now runs on its own thread (still one RPC, so a null
  projection cannot spam `controller_response_missing_data` once per poll) while
  the supervisor samples the pane's busy cue every 500ms. The pane check is
  ACCEPT-ONLY: it can end the wait early with proof but never declares the
  trigger stranded, so the resubmit decision still happens only after the full
  admission window and cannot race a composer that has not finished rendering.
  An accept taken from the pane logs
  `auto_trigger_admission_accepted_from_pane`.

- **Diagnostic (`#startupmiss9row`): the 9-row startup-miss does NOT reproduce,
  and `#freshclaudectxless` was never its mechanism.** Four live restart-fresh
  spawns (opencode→claude, codex→claude ×3) in real 9-row panes at both 213 and
  106 columns reached dispatch-ready in 2.5-3.3s via `readiness_source=
  current_child_pty` and dispatched every time, well inside the 30s deadline.
  Claude Code suppresses its custom status line entirely at 9 rows — 0 of 237
  sampled captures contained it — so the status line cannot mask the composer in
  that geometry and the 0.35.176 fix does not apply to it. The earlier reading
  that put the startup path at 17-27s was measuring `auto_trigger_sent` log
  times, which carried the 25s stall above. The short-pane shape is now pinned by
  a real captured fixture
  (`fresh_claude_nine_row_pane_has_no_status_line_and_stays_dispatch_ready`).

## 0.35.178

- **Fix (`#qflood`): the idle-queue watch stacked `/agent-doc <FILE>` lines in
  the composer until the pane filled.** Operator-reported on `tasks/sdk.md`. The
  captured pane shows the payload repeated nine times, with
  `idle_queue_dispatch_not_consumed reason=dispatch_start_unproven` followed by
  `idle_queue_watch_resubmit key=Enter` every ~30s.

  The `#idlequeuedraftinject` guard in 0.35.177 did not cover this shape: once
  several payload lines pile up, the bottom-most carries no `❯` glyph, so
  `is_prompt_line` is false and the projection is `Absent`, not `OperatorDraft`.
  The watch therefore appended another copy and pressed Enter — which in a
  multi-line composer inserts a newline instead of submitting, growing the draft
  every cycle.

  A bare `Absent` must stay dispatchable (a stale or partial renderer tail
  carries no composer at all, and a ready actor legitimately wins there), so the
  guard is discriminated by composer **chrome**: when the frame is rendered
  (box rule, status line, permission footer) but the body is not an empty
  prompt, the composer is occupied and is never injected into. Regression test
  uses the pane text captured live from the reported session.

## 0.35.177

- **Fix (`#idlequeuedraftinject`): the idle-queue watch injected `agent-doc
  <FILE>` on top of text the operator was still typing.** Operator-reported on
  `tasks/haiven-dev.md`: an unsubmitted `/agent-doc …` prompt kept appearing in
  the Claude composer, then logged `idle_queue_dispatch_not_consumed
  reason=dispatch_start_unproven` on a loop.

  `idle_queue_prompt_visibility` guarded only against an *active* turn via
  `dispatch_blocker_reason`, then short-circuited on `actor_ready`. A
  completed-turn marker (`✻ Brewed for 7m 59s`) raises no busy cue, so
  `actor_ready` won and returned `Visible` without anything inspecting the
  composer — which at the time held the operator's own `keep going`.
  `dispatch_payload_pending_in_current_input` did not catch it either: that asks
  whether *this payload* is already pending, not whether the composer is
  occupied at all.

  The composer projection is now consulted before the `actor_ready`
  short-circuit, so a `PaneComposerProjection::OperatorDraft` is never
  dispatchable. Only explicit operator steering adds and submits a prompt. An
  empty composer on a ready actor still dispatches, so the queue does not stall.
  Regression tests use the pane text captured live from the reported session.

## 0.35.176

- **Fix (`#freshclaudectxless`): a freshly started Claude pane read as
  never-dispatch-ready, so restarts hit the 30s startup deadline and idle/route
  readiness checks stalled.** Claude's built-in status line carries a `ctx:N%`
  context-usage token, and `is_claude_status_chrome_line` keyed the whole chrome
  test on it. A **fresh** session has no meaningful context usage, so Claude
  omits that token entirely and renders
  `Opus 5 ~/…/src/boost-client main brian@cachyos-x8664`. That line was
  therefore classified as ordinary output, and because it sits *below* the
  composer it won `last_prompt_candidate` and masked the `❯` prompt above it.
  Every non-cursor-scoped readiness check — `output_prompt_visible`, and through
  it `current_child_prompt_visible` and `idle_queue_prompt_visibility` — then
  reported a genuinely idle pane as never-ready, until the session accumulated
  enough context for `ctx:N%` to appear. That is why the defect reproduced only
  on freshly spawned harnesses (`agent_restart_performed`, i.e. Restart Agent /
  an `agent:` harness switch) and never on an established session. The chrome
  test now also matches the ctx-less form structurally — a trailing `user@host`
  token plus a filesystem-path token, and never a `❯`/`⏵` composer row — so
  ordinary prose that merely mentions a host (`ssh brian@host`) is left alone.

- **Diagnostic (`#startupmissgates`): `no_prompt` startup misses now name the
  gate that never opened.** The auto-trigger's pane fallback requires
  `current_child_output`, `actor_ready`, and `owned_pane_dispatch_ready`, but the
  deadline logged only `reason=no_prompt_after_30s` — indistinguishable between a
  dead PTY relay, a stuck actor projection, and an unrecognized prompt, which are
  three different defects with three different fixes. `auto_trigger_timeout` now
  records each gate value plus a `blocking_gate=` summary from the new pure
  `auto_trigger_blocking_gate`.

## 0.35.175

- **Fix (`#boundaryprosecount`): a session document that *describes* the boundary
  marker in prose could never close out.** The committed single-boundary invariant
  counted markers with a raw `matches(BOUNDARY_PREFIX)` substring scan, so an
  inline-code mention like `` `<!-- agent:boundary: -->` `` — which agent-doc's own
  bug-tracking sessions write constantly — counted as a second marker. The
  self-heal then demanded a collapse that could never succeed: the collapse masks
  code ranges (`remove_all`) and correctly preserves that prose, so the recount
  stayed at 2 and every closeout on the document failed at
  `boundary_invariant_violation phase=post_commit_collapse`, after the commit had
  already landed. Counting now goes through `boundary::count_markers`, which masks
  the same code ranges the collapse preserves, so the counter and the collapse
  agree by construction. `agent-doc boundary`'s stale-marker diagnostic uses it
  too, and no longer over-reports quoted markers.

## 0.35.174

- **An answered `[operator-verify]` head stayed deferred forever (`#opverifyanswered`).**
  The tag defers a queue head because it needs a human to look at something. When the operator
  supplied that verdict in the head itself — `do [#id]: verified. looks good` — nothing consumed it:
  `drainable_head_count` stayed `0`, `queue_continuation_required` stayed `false`, and every
  following turn reported "nothing to do" while the operator's answer sat at the head of an active
  queue. The verdict IS the proof the tag was waiting for, so a head carrying one is drainable in
  both the in-session loop and the supervisor drain. Shape-based, not sentiment-based: `still broken`
  is as actionable as `verified`, and agent-written heads are bare `do [#id]`, so a trailing
  annotation is operator-authored by construction. `[focused-cycle]` is deliberately unaffected — it
  asks for a fresh cycle, which no amount of human confirmation supplies.

- **A queue head that QUOTED an id inherited that id's gating (`#ftquotedid`).**
  `extract_head_id` scanned the raw head text, so an operator bug report containing
  ``​`do [#foo]: ...`​`` in backticks was classified as the `#foo` directive. When `#foo` was
  `[operator-verify]`-deferred, the operator's brand-new free-text prompt was itself reported
  undrainable — the bug report stalled the queue it was reporting on. Id extraction now masks
  balanced inline-code spans first; an unbalanced backtick is prose and stays visible, so real
  directives (`do [#real] and mention `[#quoted]``) still resolve.

- **One settle window was still not proof that a pass-through trigger submitted (`#runsubmitclaude`,
  second pass).** 0.35.164 gated the `cleared` verdict on a 150ms settle window instead of reading a
  1ms capture. 150ms lands on the render boundary: observed 2026-08-08 16:23:39Z on
  `tasks/agent-doc/agent-doc-bugs2.md` pane `%25`, the repair logged
  `outcome=cleared enters_sent=0 elapsed_ms=153` and the operator then watched the trigger sit
  unsubmitted in the composer — the same pane at 16:22:28Z had seen the draft on its first look and
  repaired it. An IDLE pane showing no draft is ambiguous between "the turn already finished" and
  "the keystrokes have not rendered", so that verdict now needs a second consecutive idle-and-empty
  observation. A BUSY pane showing no draft needs none — the harness working is positive evidence
  the trigger crossed — so the fast success path is unchanged and only the genuinely ambiguous case
  pays one extra window.

## 0.35.173

- **A composite-build guard could not tell "absent" from "present but empty" (`#lzktemptydir`).**
  `editors/jetbrains/settings.gradle.kts` conditionally `includeBuild`s the sibling `lazily-kt` so
  monorepo checkouts exercise local edits, falling back to the published
  `io.github.lazily:lazily` when the sibling is absent. The guard tested directory *existence* — but
  an uninitialized git submodule is an existing **empty** directory, so the guard passed,
  `includeBuild` substituted the dependency with a build that has no projects, and Gradle failed
  with the opaque `No matching variant of project :lazily-kt was found ... No variants exist.`
  A standalone checkout has no directory at all, which is why only a PARTIAL monorepo checkout hit
  it. The guard now keys on the sibling's `settings.gradle{,.kts}`, making the fallback identical
  for "absent" and "present but uninitialized".

  Surfaced by the agent-loop superproject `check` job, whose submodule init listed every sibling
  except `src/lazily-kt` and `src/lazily-spec` (agent-doc's own `ci.yml` clones both, which is why
  it stayed green). Both are now initialized there as well — belt and braces: CI gets the composite
  build it intends, and any other partial checkout degrades to the published artifact instead of a
  confusing variant error.

  This was masked until now: the runner-disk Bus error (0.35.170's `#loopsuperprojectjob` fix) was
  killing the job before it ever reached `dev-harness-test`.

## 0.35.172

- **A response replay that lacks the queue-prompt quote is still a duplicate (`#dupereplayquote`).**
  Hit live while closing `#runpromptverbose`. Response-block dedup compared blocks byte-for-byte
  after normalizing only `<!-- agent:boundary: -->`, the `(HEAD)` suffix, and the `❯` prefix. But
  closeout also inserts the `> **Queue prompt:**` blockquote (`#qdeferstrike`) above the answer, and
  a replayed copy never carries it — so the committed block and its replay compared as *different*,
  `agent-doc dedupe` reported "no duplicates found", and the replay survived in the working tree.
  Blank-line spacing around the inserted quote diverged for the same reason.

  The consequence was not a cosmetic double-post. Session-check diffed the surviving working tree
  against the snapshot, found assistant prose it could not attribute to a `### Re:` block, and
  classified **seven lines of the agent's own response** as concurrent operator steering directives
  — interrupting an otherwise successful closeout (`INTERRUPTED: … 7 concurrent operator steering
  directives`) after the mutations had already committed.

  `normalized_response_block` now ignores the queue-prompt blockquote and blank-line spacing when
  comparing two `### Re:` blocks, exactly as it already ignores the boundary marker and `(HEAD)` —
  all binary-inserted decoration, never response content. Blocks whose prose actually differs still
  compare as distinct (regression-tested both ways). Verified against the live document that
  produced the bug: `dedupe` removed 3675 bytes of duplicate content, kept the copy carrying the
  queue-prompt quote, and session-check went from `INTERRUPTED` to `ok`.

## 0.35.171

- **Queue reopen triggers are harness-native again, on both dispatch paths (`#runpromptverbose`).**
  Reported as "`Run Agent Doc` sends a long multi-paragraph prompt; it should simply be
  `/agent-doc <file>`". The paragraph itself was already gone (`#qcontprose`), but the one-line
  trigger that replaced it was hardcoded to the bare `agent-doc <file>` form for *every* harness:
  - `owned_pane_queue_continuation_prompt` (supervisor idle drain) ignored the harness entirely.
    This silently violated `specs/07-session-tmux-commands.md` — "Claude/OpenCode drains inject the
    normal slash-command harness reopen, and Codex drains inject the bare `agent-doc <FILE>` reopen"
    — and contradicted `idle_queue_drain_payload_keeps_trigger_for_non_codex_harnesses`, which
    asserts that split against `HarnessConfig::trigger_command`. The pure helper was harness-aware;
    the production caller passed it a hardcoded string.
  - The editor route (`Run Agent Doc`) called `apply_plain_trigger_override` whenever
    `plain_trigger` was set, rewriting Claude's and OpenCode's `/agent-doc {file}` template to the
    bare form.

  Both now use `HarnessConfig::trigger_command`, so Claude/OpenCode get `/agent-doc <file>` and
  Codex keeps the bare form that is its harness-native entrypoint. `plain_trigger` retains its real
  meaning — attach no prompt-bearing context — and no longer doubles as a trigger rewrite; that
  conflation is what let the two dispatch paths disagree for the same harness, the exact divergence
  `#qcontprose` set out to remove. Path absolutization (`#qcontabspath`) is unchanged.

  Why it matters beyond tidiness: the bare form *is* admitted by the `UserPromptSubmit` hook, which
  is why the divergence never failed loudly — but only `/agent-doc <file>` also loads the skill
  deterministically, which is what a freshly-`/clear`ed pane needs. `apply_plain_trigger_override`
  now has no production caller and documents why adding one needs a reason. Spec updated to match.

## 0.35.170

- **tmux tab-switch latency: one document was entering the ownership loop twice (`#syncdupespelling`).**
  `auto_start_candidate_files` deduped on the raw `PathBuf`. The column set is a merge of the
  caller's `--col` arguments with remembered column memory, and the two disagree on spelling —
  memory retains absolute paths while the editor/CLI passes root-relative ones — so
  `/abs/root/tasks/x.md` and `tasks/x.md` keyed as two distinct candidates and the per-file
  ownership loop resolved the SAME document twice. Each extra pass runs the full stale-binding
  fallback (`alive but no longer proves ownership` → supervisor process-tree recovery), which costs
  seconds. Measured on a two-document layout: **three** `auto-start check` passes,
  `ownership_per_file_loop took 4120ms` against a 750ms budget, and one document listed twice in
  `unresolved files remain blocked`. Now keyed on the canonical path, matching the sibling
  `projected_sync_pane_count`, which already canonicalized for this exact reason. The emitted path
  stays the first-seen spelling (session ids and operator output derive from it) and an
  uncanonicalizable path falls back to itself, so the not-yet-created auto-start case is unchanged.
  After: two passes, **no latency-budget violation**, no blocked files; worst-case switch
  2829ms → 802ms, steady state ~550ms → ~450ms.
  This is the remaining wedge after items 1-5 of `tasks/agent-doc/plan-tmux-auto-sync-reactive.md`
  shipped — it was never a reactive-plane problem, so none of those conversions could have caught it.

## 0.35.169

- **The `agent:queue` mirror honours the backlog anchor, and `--backlog-reorder` cascades into it (`#queuemirrororder`).**
  `--backlog-add-after #X "[#y] ..."` placed `#y` after `#X` in `agent:backlog` but PREPENDED it to
  `agent:queue`: the backlog honoured the anchor while the queue applied
  `--backlog-queue-placement` independently, so the pair inverted exactly when the anchor was chosen
  BECAUSE order matters (observed in `tasks/haiven-dev.md`: backlog
  `[#reasoncoderegistry, #reasoncodeenum]`, queue `[#reasoncodeenum, #reasoncoderegistry]`).
  `enqueue_actionable_ids_in_content` now splices an anchored id directly after its anchor's live
  queue head and keeps `placement` (the `#queueatcreate` head default) only for ids with no
  already-queued backlog predecessor — every plain `--backlog-add`, which prepends to the backlog
  head, is unchanged. An anchor with no live slot (struck or completed-only) falls back to
  `placement` instead of vanishing, and the splice stays insert-only, so operator free-text lines
  survive byte-for-byte.
- **Recovery exists now.** There was previously no way to move an already-queued id:
  `agent-doc queue sync` skips ids already present (`reason: already_in_queue`, so it is
  append-only) and `--backlog-reorder` stopped at `agent:backlog`; the only remaining levers were
  `--done` / `--backlog-gate`, both of which assert something false about the item's state.
  `backlog_cmd::reorder` now cascades through `reorder_queue_mirror_in_content`, which permutes only
  the named live heads among the slots they already occupy — every other queue line keeps its exact
  byte offset, preserving `#qauthorder`.
- **A metadata-only write no longer trips the imperative-directive guard.** A pure reorder has no
  command output, no code diff, and no commit hash to cite, so the prose heuristics rejected every
  honest response it could produce (`rejected status-only/meta response for do [#typeroutersponses]`)
  and left the cycle stranded at `response_captured`. `WriteOptions::has_metadata_only_mutation`
  (reorder / text-metadata edits) now flows into
  `enforce_imperative_response_contract_with_mutation_evidence`: the binary applied the mutation,
  which is stronger evidence than any prose scan, so the guard defers to it.

## 0.35.168

- **A zombie harness child no longer pins the supervisor in an `execve` loop (`#zombiechild`).**
  `SupervisorShared::child_alive()` answered `Path::new("/proc/{pid}").exists()`. That entry
  survives for a **zombie** — a process that has exited but has not been reaped — and the
  supervisor is the parent that never reaps it, so a dead child reads as alive forever. Observed
  2026-08-08 on `src/haiven-dev/tasks/haiven-dev.md`: the Claude child (pid 1905542) died at 22:31
  the previous evening and went `[claude] <defunct>`; `supervisor_restart_admission` then deferred
  the restart indefinitely and every stale-supervisor recycle re-`execve`d with
  `via=execve_preserve_child`, preserving a corpse. Each pass opened a fresh PTY master
  (fd 72 → 75 → 80 …), minted a new resume id, wrote the whole 70 KB document through the CRDT
  relay, found no transcript for the id it had just minted, and recycled again — **68 recycles in
  27 minutes**, ~700 full-document CRDT writes per two minutes. The operator typing into that pane
  saw input periodically swallowed and arrow keys echo raw as `^[[A^[[B`, because the process
  reading their PTY was dead and the terminal was reset every ~10s. Liveness now reads the process
  state from `/proc/<pid>/stat`: `Z`/`X`/`x` are exits, everything else is a live child. An
  unreadable `stat` under an existing entry keeps the historical fail-open answer.

## 0.35.167

- **The Windows wheel builds again (`#watchsighupwin`).** `agent-doc-watch-io`'s
  `install_shutdown_signal_handlers` (`#stalewatchpid`) listed `libc::SIGHUP` unconditionally, and
  that constant does not exist on `x86_64-pc-windows-msvc` — so every tagged release failed its
  PyPI job with `error[E0425]: cannot find value SIGHUP in crate libc`, the only red leg in the
  publish. The handler installation is now `#[cfg(unix)]` with a no-op elsewhere: the pid-file
  lifecycle it protects is a unix daemon concern.

## 0.35.166

- **A `--done <id>` closeout no longer eats an unrelated free-text queue head (`#qftnodoneeat`).**
  `consume_queue_prompts_for_done_ids_*` asked the consume planner for a free-text budget of 1
  unconditionally. A `do [#id]` head is matched by id; a free-text head carries no id, so
  `leading_done_consume_count` was 0 and the planner fell straight through to "consume the leading
  free-text prompt" — with no proof of any kind that the cycle addressed it. Observed 2026-08-08 on
  `agent-doc-bugs2.md`: a `--done qheadnest` for an id that had never been in the queue consumed
  ``tmux pane auto-sync is still extremely slow when switching from lazily.md to
  agent-doc-bugs2.md. Fix it.``, and because that was the last head the queue also drained and
  flipped `queue: stop` — operator work deleted with no diagnostic. The budget is now 0 as soon as
  any id is named; an empty `done_ids` (the generic response closeout, whose callers already
  establish proof) keeps its budget of 1. A genuinely answered free-text head is still struck by
  the proof-bearing site (`project_answered_free_text_strike`), which requires the response to
  quote it. The refusal is logged as `queue_consume_refused_unproven_free_text_head`.

## 0.35.165

- **A queue head cited inside another prompt no longer counts as answered (`#qheadnest`).**
  `free_text_head_answered_by_response` fell back to searching one flattened blob of every `>`
  blockquote line in the response. An operator bug report about the queue is normally written by
  citing the queue line it is about, so echoing that report as a `> **Queue prompt:**` quote made
  the cited head look answered. Live on 2026-08-08 on `agent-doc-bugs2.md`: an operator note whose
  body quoted ``tmux pane auto-sync is still extremely slow …`` was consumed and echoed, and
  `session-check` then INTERRUPTED the closeout — "completed free-text agent:queue head(s) … are
  still active in the committed queue" — over a head nobody had touched, with the only offered
  repairs being to strike real work or disable the guard document-wide. The same blob match also
  drives `#ftstrike`, so it could silently strike an unaddressed operator report. The fallback is
  now anchored to where a quoted entry *starts*: candidates are each blockquote block joined from
  each of its lines to the end, so a head spanning several quoted lines still matches at any entry
  position while text buried mid-entry never does.

## 0.35.164

- **The `#runfilesubmit` draft check verified nothing, so "Run Agent Doc" still stranded its
  trigger (`#runsubmitclaude`).** 0.35.163 added `repair_pass_through_stranded_draft`, but its
  first pane observation ran with no settle window, and an empty composer was classified
  `Cleared` — terminal. `tmux send-keys` returns once the bytes reach the pty, long before the
  harness TUI has read and rendered them, so that first capture shows the pane *before* the
  trigger arrived. Live on 2026-08-08 at `06:15:08Z`, pane `%25`:
  `route_pass_through_submit_draft … outcome=cleared enters_sent=0 elapsed_ms=1` — one
  millisecond after the send — while the pane snapshot taken 44 seconds later still showed
  `❯ agent-doc …/agent-doc-bugs2.md` drafted on an idle pane. The repair reported success on
  exactly the strand it exists to fix. `classify_pass_through_stranded_draft_action` now gates
  the `Cleared` verdict on the settle window too: "I have not looked yet" and "I looked and the
  composer is empty" are different observations, and collapsing them inverted the check
  (`#idlerevisionreactive`, applied to a pane instead of a controller probe). The common case now
  pays one 150ms window — still two orders of magnitude under the dispatch-start proof budget
  this path exists to skip — and a genuinely stranded trigger gets its bare submit key.

## 0.35.163

- **"Run Agent Doc" no longer strands its trigger in the composer (`#runfilesubmit`).** The
  plain-trigger route path (`DirectPaneSubmitPolicy::PassThroughSingleSubmit` — what the JetBrains
  *Run Agent Doc* action and closeout drain use) sent text and the submit key in one
  `tmux send-keys` call and returned `TransportSubmittedOnly` **without ever looking at the pane**.
  When the harness TUI absorbed that trailing `Enter` into the same input burst, the trigger sat
  unsent in the composer forever while route logged `pass_through_single_submit`, `status=ok`, and
  `exit_code=0`. Captured live on 2026-08-08 at `04:27:01Z`: `route_latency … elapsed_ms=4
  outcome=pass_through_single_submit`, and the pane snapshot taken 429ms later still showed
  `❯ agent-doc …/agent-doc-bugs2.md` drafted on an idle pane. The pass-through branch now verifies
  the draft cleared and repairs it with a bounded bare submit key.
- **The stranded-draft predicate was blind to every Claude draft.**
  `route_trigger_visible_in_current_draft` treats "a prompt line appears after the trigger" as proof
  the trigger is consumed scrollback. Claude Code renders its permission-mode footer
  (`⏵⏵ bypass permissions on (shift+tab to cycle)`) *below* the composer and
  `HarnessConfig::is_prompt_line` matches it on purpose, so that footer terminated the scan on every
  capture — the predicate returned `false` for a trigger sitting in plain sight. That silently
  disabled the visible-draft `Enter` recovery on `try_late_direct_pane_enter_resubmit_after_unproven_dispatch`
  and `direct_pane_can_enter_existing_draft` too, not just the new path. Persistent status-footer
  chrome is now excluded from the later-prompt scan; a real composer prompt below the trigger still
  ends it, so consumed scrollback is still distinguished. Regression-tested against the verbatim
  captured pane.
- **The repair cannot type into someone else's turn.** It presses a submit key only when the trigger
  is the visible draft on an **idle** pane, only after the sighting survives a settle window (one
  frame of render lag is not a stranded draft), and at most
  `AGENT_DOC_PASS_THROUGH_STRANDED_DRAFT_MAX_ENTER_RESUBMITS` times (default 3). A pane-capture
  failure is never read as "stranded" — unknown pane state authorizes nothing. The common case
  where the submit crossed costs a single `capture-pane` and no settle, so the fast path stays fast.
- **The supervisor queue continuation is the plain trigger again, not a paragraph
  (`#qcontprose`).** When the idle-queue watch advanced the head on an already-owned pane it typed a
  five-line message into the operator's console — "Agent Doc queue state advanced for `…`. Continue
  this existing owner-pane session; do not invoke `agent-doc` recursively. / Execute the current
  active queue head: / `<head>` / Persist and finalize this cycle through the connected Agent Doc
  tools." — roughly 280 bytes per dispatch. It now sends the same portable bare `agent-doc <file>`
  trigger the route path has always dispatched into live owned panes
  (`route_dispatch_drain_plain_trigger_pass_through`), which is also what
  `agent_doc_queue::idle_drain`'s own contract test already asserted the payload should be.
  The recursion the paragraph warded against does not exist: in an owned pane the harness is the
  process reading input, so `agent-doc <file>` is a prompt its `UserPromptSubmit` hook admits and
  answers with an in-binary preflight — the same session continues. Naming the head was redundant
  too; preflight already selects it and hands it over as `selected_queue_prompts`. Two dispatch
  paths that disagreed about what a queue continuation looks like are now one.
- **The queue continuation dispatches an absolute path (`#qcontabspath`).** Follow-up to
  `#qcontprose` in this same release: the paragraph it replaced embedded the document path
  *descriptively*, so a relative one was harmless. As a trigger the string is **executed**, and it
  resolves against the **pane's** working directory — which is not guaranteed to equal the
  supervisor's. Observed within the hour: the supervisor dispatched
  `agent-doc tasks/agent-doc/agent-doc-bugs2.md`, the pane's cwd had moved into `src/agent-doc`, the
  path did not resolve, and the harness admission hook produced **no cycle contract at all** — a
  silently dead continuation with no error anywhere. `owned_pane_queue_continuation_prompt` now
  absolutizes via `std::path::absolute` (lexical plus the supervisor's cwd, the directory the
  relative path was expressed against). Doing it inside the function rather than at the call site
  makes the guarantee unconditional, and matches the absolute form the route path already sends.
- **A response that discusses its own queue no longer interrupts its own closeout
  (`#qcontamquote`).** The `#jb-run-agent-doc-response-queue-contamination` guard flagged any
  free-text queue prompt whose first 40 normalized characters appeared *anywhere* in the assistant
  response body. Contamination is a queue entry that **is** a response line — the original repro
  enqueued `- Yes. I drove the already-authenticated Google Ads browser session …` verbatim — but a
  response that merely mentions a queue item mid-sentence is the opposite thing, and it is what
  `SKILL.md` asks for: reconcile and quote the queue prompts. An unanchored `contains` could not
  tell those apart, so this cycle's own closeout INTERRUPTED and named the **operator's** queue line
  as contamination. The match is now anchored to the start of a response line; blockquote echoes
  were already excluded upstream. A response paragraph that begins with the queue text is still
  flagged, so the anchoring is not a loophole.
- **An editor column that detours into source keeps its tmux pane (`#stickymdpane`, JetBrains
  plugin 0.2.350).** Both surface reporters answered "which document is this editor window showing?"
  with `window.selectedFile?.takeIf { it.name.endsWith(".md") }`. Open a source file in one half of a
  two-column split and that window reported *nothing* — so the mirrored layout collapsed to a single
  tmux pane and the session pane the operator was working next to was gone until they navigated back
  to the document. A window that has shown a document now keeps standing for the last one it showed,
  resolved from that window's own `.md` tabs in `EditorHistoryManager` most-recently-used order (no
  plugin-side per-window bookkeeping, so it survives restarts and splits). A live `.md` selection
  always wins, and a window that never held a document still contributes no pane — closing a split,
  or opening a fresh source-only one, collapses the layout exactly as before.
- **Every pass-through submit now records its outcome.** `route_pass_through_submit_draft
  file=… pane=… harness=… outcome=cleared|deferred_pane_busy|enter_resubmit|exhausted_still_stranded
  enters_sent=N elapsed_ms=M capture_failed=…` makes the stranding rate measurable instead of
  invisible.

## 0.35.162

- **An OpenCode pane can become dispatch-ready again (`#opencodeharnessswitch`).** Switching a
  document from codex to opencode and restarting failed with
  `auto_trigger_timeout harness=opencode reason=no_prompt_after_30s`, because a genuinely idle
  OpenCode cold-start splash never satisfied any dispatch-ready predicate. Two causes, both
  reproduced from a live pane capture of OpenCode 1.18.14. First, the bounded bottom-of-pane
  idle-chrome window was measured in raw terminal rows: OpenCode pads the gap between its composer
  box and the bottom `<cwd>:<branch> … <version>` status line with roughly sixteen blank rows, so
  `take(12)` spent the whole window on padding and left one chrome line — under the `min_chrome`
  floor. The window is now bounded in *meaningful* rows, matching the normalization
  `recent_normalized_bottom_lines` already applied to the busy-cue window, and one shared scan
  serves both the readiness and stale-busy-cue consumers so they cannot drift. Second, the
  `opencode` wordmark is drawn with `█` full blocks, which were missing from the box-art glyph set,
  so the wordmark rows were non-ignorable and `last_prompt_candidate` resolved to one of them.
- **A drafted OpenCode composer fails closed.** OpenCode renders its idle chrome *below* the
  composer, so chrome alone reaches `min_chrome` before the scan sees the composer; only the
  padding-bounded window had been hiding that. A composer row carrying operator text is now an
  explicit reject, so route cannot inject a trigger over unsent input.
- **Editor-triggered sync stops paying for two unconditional layout repairs
  (`#syncdoctorconverged`).** `repair_layout`'s consolidation and index-normalization phases ran on
  every full sync — twice, once before and once after the reconciler — even when the layout was
  already correct, and each pass re-listed the session's windows three or more times (once more
  inside every per-window `normalize_window_to_index`). `repair_layout` now returns
  `AlreadyConverged` when one window observation proves those phases would move nothing, and issues
  no further tmux command; it returns `Repaired` only when it actually mutated tmux. Callers report
  a repair note only for `Repaired` — the unconditional note made the sync log claim a repair on
  every sync (2764 pre- and 2761 post-sync claims with zero convergence in the measured window),
  which is indistinguishable from a repair that never converges.
- **Both doctor-repair passes are metered.** `doctor_repair_pre` and `doctor_repair_post` join the
  sync phase log against a 250ms budget. They previously sat between measured phases and were
  invisible to the phase meter, which is how that cost hid on the editor hot path.

## 0.35.161

- **Codex harness-native entry now performs binary-owned admission and tracking in one hook.**
  `agent-doc hook codex-user-prompt-submit` durably binds the active document before running
  preflight, emits the cycle contract as Codex turn context, and appends the contract marker only
  after preflight succeeds. Ordinary prompts remain context-free, while malformed payloads and
  tracking/preflight failures emit no marker so the skill fails admission closed. Installer
  upgrades preserve the existing hook command and refresh its status label, and end-to-end
  coverage proves the same invocation opens a cycle that the Codex Stop hook can close and commit.

## 0.35.160

- **Run Agent Doc is a true pass-through steering action.** The editor's plain
  `route --dispatch-only --plain-trigger` path now bypasses open-closeout drain
  recovery instead of waiting or reporting a false-success coalesce behind the
  closeout owner. The authoritative boundary also drops any accidentally
  derived prompt context, preserving bare-trigger delivery to live ready, busy,
  and waiting-input actors for real-time steering. Direct-pane delivery now sends
  that trigger plus one submit key exactly once and returns on successful tmux
  transport, instead of spending the acceptance window pane-polling and repeatedly
  submitting Enter. Prompt-aware dispatches retain acceptance and dispatch-start
  proof.

## 0.35.159

- **Editor tab-switch cross-root pane resolves through the owning controller, not a `/proc` walk (`#tmuxautosyncreactive`).** A two-column editor surface with a cross-root document (e.g. a submodule doc in column 2) made every tab switch take **0.7–1.9 s**: the editor automatic path runs with `auto_start_mode=Full` (`exact_visible_projection=true`), so `skip_autostart_diagnostics` is false, the cross-root reactive branch at the top of the per-file loop (`skip_autostart_diagnostics && is_cross_root`) did not fire, and the walk-skip gate (`exact_visible_projection && !is_cross_root`) excluded the cross-root file — sending it to the exhaustive `project_authoritative_actor_binding` `/proc` ownership walk (`ownership_per_file_loop sa_ms` measured 670–1299 ms per cross-root file). For `exact_visible_projection && is_cross_root`, the per-file loop now resolves the pane from the owning controller's reactive actor store via `resolve_cross_root_document_pane` (the same call the SafePassive cross-root path already uses), skipping the `/proc` walk entirely. The owning controller's binding is the authority (`#lazily-hot-path`); a missing/dead binding falls through to the registry fallback, same as a failed walk. `/tmp/agent-doc-sync.log` shows the fast case (`reactive_actor_bindings` already populated, `ownership_per_file_loop=0 ms`) was the exception, not the rule — this makes it the rule for cross-root editor layouts. New live-tmux regression `exact_visible_cross_root_full_mode_resolves_via_owning_controller`.

## 0.35.158

- **`tmux_focus_state` reads the active pane from the reactive actor store, not SQLite (`#tmuxautosyncreactive` / `#lazily-hot-path`).** The JetBrains `TmuxPaneFocusSync` reverse-mirror polls `tmux_focus_state` every 500 ms to reflect the tmux-focused pane back into the editor selection. `handle_tmux_focus_state` reopened the project `state.db` and loaded the full actor store (`load_actor_store_from_db`) on every poll, then linear-scanned it for the active pane — a SQLite read on a hot path the controller runtime already owns reactively (`#lazily-hot-path`: SQLite is a durable sink, not transition authority). The RPC is now routed through the runtime like its siblings (`editor_surface_observe`, `tmux_layout_sync_state`); when a runtime is available it consults the in-memory reactive actor store (`ControllerRuntime::actor_store_snapshot`), and only the cold-start path (no runtime, the `tmux_focus_state` CLI entry) hydrates from SQLite. The live (non-blocked, non-closed) pane→record lookup is unified in a pure `actor_record_for_active_pane` helper so both surfaces apply one rule; the `/proc` process-owner fallback for an unbound active pane is unchanged. The tmux active-pane probe itself stays (tmux exposes no push event for arbitrary pane-focus changes, so the reverse mirror cannot become a pure state-plane subscription — see `plan-tmux-auto-sync-reactive.md` Item 2 note). This is the remaining reactive-state conversion for the editor tab-switch latency path after the headline item-1 fix (0.35.147–0.35.153) moved the Sync/Focus consequence off the editor socket.

## 0.35.157

- **Reactive sync lock: kernel-blocked `flock`, no sleep-poll, no `/proc` reap (`#tmuxautosyncreactive`).** `.agent-doc/sync.lock` acquisition slept on a 50 ms `try_lock_exclusive` retry loop and, on budget exhaustion, walked `/proc` to `SIGTERM` stale orphaned owners before retrying — an imperative poll + reaper compensating for a problem `flock(2)` already solves. `acquire_sync_lock` now wins the uncontended case (the controller `pane_layout_effect_worker` is single-threaded, so two editor-fired syncs never contend) on a single non-blocking attempt, and on contention blocks in the kernel until the holder releases or the configured wait budget elapses — a helper thread raced against `recv_timeout`, since `flock(2)` has no native timeout and there is no userspace polling. `flock(2)` is released automatically when the holding process exits (fd close), so a dead or orphaned owner can never wedge the lock; the `reap_stale_orphaned_sync_lock_owners` `/proc` reaper, `sync_lock_process_from_proc`, the `read_proc_*` / `proc_has_fd_for_path` helpers, `SyncLockProcess`, `is_stale_orphaned_sync_lock_owner`, `SYNC_LOCK_POLL_INTERVAL`, `STALE_SYNC_LOCK_OWNER_AGE`, and the `libc` dependency are removed. `SPEC.md`, `README.md`, `AGENTS.md`, and the session/tmux command spec are aligned on the kernel-blocked, self-releasing contract. The actual editor tab-switch latency fix (decoupling the eager Sync intent
consequence, plan item 1) follows in a separate release.

- **Collapse the `/proc` ownership walk to one pass per sync (`#tmuxautosyncreactive`).** `ProcessObservationState::tree_source` called `observe_proc_children()` — a full `/proc` directory walk — **per new pane root pid**, so a cold first pass over `P` panes paid `P` whole-table walks (each opening+parsing `/proc/<pid>/stat` for every process, plus a `ps` fork-exec per exiting process). `ops.log` showed `ownership_proof` at 9,010 ms and `ownership_per_file_loop` at 8,825 ms against a 750 ms budget (115 / 113 over-budget occurrences), with retry churning a single generation 40+ times. The map is now memoized once per `ProcessObservationScope` (`children_handle`) — matching what `refresh()` already did for re-observation — so the cold first pass is one walk regardless of pane count. The drift survey `tmux_layout_sync_state_for_invocation_with_effect_assignment` additionally opened NO `ProcessObservationScope`, so its per-pane `active_pane_process_owner_document` resolutions took the uncached path even mid-sync; it now opens a scope so its panes share the same memoized walk. A `pane_layout_survey_timing elapsed_ms=…` op-log entry meters the survey across every return path (it was previously unmetered, hiding its share of the worker convergence latency).

- **Interruptible structural sync effect (`#tmuxautosyncreactive`).** Rapid editor tab switches queued `pane_layout` generations faster than the ~850ms structural effect could converge, so every intermediate generation ran the **full** structural sync then was discarded (`pane_layout_projection_cancelled … reason=superseded`) — only the final switch converged, multiplying perceived lag. The pane-layout worker now binds its generation to the graph's latest published generation (`published_generation`), and `run_with_options_internal_at_root` polls `structural_effect_superseded` at the after-lock and after-prune phase boundaries, returning early when a newer layout supersedes it. The worker's existing post-effect supersession gate remains authoritative, so the bail is a pure optimization: a superseded generation stops spending ~850ms of tmux mutations that get thrown away. The standalone `agent-doc sync` CLI never binds the cancellation scope, so it is unaffected.

- **`reload-lib` reliably reloads a build-mismatched editor (`#replicarefusalstorm` / `#editorendpointzero`).** After a controller version bump, `reload-lib` reported endpoints "failed" while the editor listener was demonstrably alive (socket present, `connect` OK): the build-mismatch reload-compat path (`send_legacy_reload_to_pid`) shared the generic **6 s** `IPC_RECEIPT_TIMEOUT_SECS`, but the native generation handoff it triggers — drain in-flight native calls, unmap the old cdylib, prove the mapping is gone, load the replacement, re-register open projects — takes far longer. The receipt timed out mid-handoff, so the reload was delivered but never acknowledged, the editor stayed on the stale cdylib, and `replica_register` was refused as `detached_authority` in a chronic retry storm. `reload_library` now uses a dedicated `IPC_RELOAD_LIBRARY_RECEIPT_TIMEOUT_SECS` (90 s) budget, so the handoff completes and the editor re-attaches without an IDE restart. New regression `reload_library_receipt_outlives_a_slow_generation_handoff`.

- **Stop mirroring per-phase sync latency into the disk-backed `ops.log` from the structural-sync hot path (`#tmuxautosyncreactive`).** `log_sync_latency` wrote every sync phase (`window_resolution`, `prune`, the `prune_*` sub-phases, `ownership_*`, `tmux_router`, … — ~8–15 per run) to the project `ops.log` via `log_op`, which `open`+`write`+`close`s the file each call. `ops.log` is on disk (ext4) and contended by every controller thread (CRDT, RPC, idle-watch, editor traffic), so each per-phase `log_op` cost ~100 ms under load. The sync body's wall time was dominated by this logging, not by real sync work: a measured 798 ms `prune` phase whose actual prune work was 1 ms (sub-phases summed to 1 ms; `prune_pre` 0 ms, `prune_with_tmux_wall` 1 ms). `log_sync_latency` now records only to the tmpfs `/tmp/agent-doc-sync.log` (fast, uncontended); the latency data is preserved, just out of the contended disk log. The per-phase `prune_pre` / `prune_with_tmux_wall` meters are added so the prune window is no longer a measurement black hole.

- **Share the `/proc` ownership walk across syncs via a process-wide TTL cache (`#tmuxautosyncreactive`).** With the per-sync `ProcessObservationScope` the cold full-`/proc` scan ran **once per sync** (~600 ms to walk the whole process table), so even after the per-pane memoization every tab-switch re-paid it — `ownership_proof` / `ownership_per_file_loop` measured ~633 ms with only 2 panes. `observe_proc_children()` now backs a process-wide `Arc<ProcChildren>` cache with a 2 s TTL (the process table is effectively stable over that window), so consecutive syncs and the drift survey share one walk; `observe_proc_children_fresh()` is the tmux-mutation invalidation edge that forces a fresh walk and updates the cache. The ownership walk now costs ~hundreds-of-ms once per quiet window, not once per sync.

- **Exclude non-agent-doc markdown from the CRDT replica refusal storm (`#replicarefusalstorm` / `#replicaendpointtakeover`).** The editor registers a CRDT replica for *every* `.md` it opens; a non-agent-doc file (README, AGENTS, CONTRIBUTING, a draft, a plan note — no `agent_doc_*` frontmatter and no `<!-- agent:` markers) never attaches an editor authority, so the controller refused it as `detached_authority` on every retry — thousands of refusals per file (haiven-addendum-response-draft 4,338; haiven-offer-revision-email-draft 4,306; plan-tsift-extract-function 4,072; many README/AGENTS), each with its own `log_op` contending the disk `ops.log` and loading the controller (the cross-root RPC that dominates sync latency ran slow because the controllers were busy refusing). `handle_crdt_replica_rpc` now detects non-agent-doc files once (mtime-cached frontmatter/marker check) and refuses them cheaply + terminally as `not_agent_doc_document` — no refusal ledger, no `log_op` — so they no longer contend the disk log. The durable stop is the editor only registering agent-doc documents (the JB half); this keeps the controller off the contended path for the files that slip through.

## 0.35.156

- **Convergence honors operator-delete tombstones for directives (`#qdelauth`).** The directive-twin convergence floor (`authored…max(1)`) let a `do [#id]` directive the operator had deleted reappear when a stale or divergent editor buffer re-pushed it — because the floor keeps one copy of any id absent from the baseline, so it could not tell a *new* operator prompt from a *resurrected* deleted one. The durable `operator_delete_tombstones` ledger already records operator-deleted directive ids, but convergence never consulted it. `converge_queue_via_lifecycle` now takes a `deleted_directive_ids` set; an id in the ledger has lawful multiplicity **zero**, so a re-pushed deleted directive is dropped while a genuinely-new id still gets the `min 1` allowance. Preflight passes the persisted tombstone set (new public `queue_tombstone::current_tombstones`). The ledger self-clears on a legitimate re-add+commit, so an operator who changes their mind is not blocked. New regression `converge_suppresses_tombstoned_directive_resurrection`. **Scope:** this closes the leak for *detected* deletions (committed deletions, and deletions that survive until the next preflight observes the empty queue). It does not catch the immediate-re-push race where an editor re-pushes a deleted id *before* any preflight observes the deletion — that needs the editor-side re-read (`#evmh`) so a divergent buffer reconciles to the canonical instead of re-pushing stale content.

## 0.35.155

- **Guard replica updates that structurally corrupt the canonical (`#replica-structure-guard`).** A connected editor pushing a stale or truncated buffer could tombstone a component close marker (e.g. `<!-- /agent:done -->`), leaving the canonical with an unclosed component that every downstream parse (`exchange list`, `preflight`, `session-check`, `reset`) then refused — a fail-closed wedge with no in-band recovery while disk and `HEAD` stayed correct. `relay_replica_update_for_file` now captures the pre-update canonical, and when a merged editor update introduces a component *parse* failure (unclosed / mismatched / unmatched marker — the unrecoverable class), it restores the canonical to its clean pre-update text via `apply_canonical_replace` (which fans the restoration out to every live member, including the corrupting editor) and forces the corrupting editor to re-project. Duplicate boundaries and duplicate singletons are deliberately excluded so the existing normalization and response-cell repair paths keep working. New regression `relay_update_that_corrupts_canonical_structure_is_restored`; the preflight boundary-dedup test stays green.

## 0.35.154

- **Strengthen the session-hijack guard for copied documents.** The existing
  `resolve_resume_claim` guard detected when another document's `resume:` field
  claimed the same conversation ID, but only checked `resume` (not
  `agent_doc_session`) and only read from disk. A copied document carrying
  `agent_doc_session` without `resume`, or whose source document had unsaved
  editor changes, could bypass the guard. `resume_id_owner` now checks BOTH
  `resume` and `agent_doc_session` fields, and resolves through the realtime
  authority (editor buffer) before falling back to disk — so a copy cannot
  hijack the original document's session regardless of which frontmatter field
  carries the ID.

## 0.35.153

- **Skip the `/proc` ownership walk entirely for editor projection syncs where no
  reactive binding exists (`#tmuxautosyncreactive` / `#lazily-hot-path`).** The
  cold-start case — an editor layout containing a document with no existing tmux
  pane — spent **4.4 seconds** in `project_authoritative_actor_binding` walking
  `/proc` to discover an owner that doesn't exist, then another 2.6s in
  `tmux_router` auto-starting the pane. The controller IS the ownership authority
  for editor-managed panes: if the reactive bindings (actor store + structural
  receipt) didn't name a pane, no controller-managed pane exists. For
  `exact_visible_projection=true`, the walk is skipped and provisioning runs
  directly — the walk only prevented duplicate panes, but the controller knows it
  hasn't created one. Combined with the earlier fixes, editor-triggered syncs
  should now be sub-second for all cases (warm: ~120ms, cold-start new document:
  auto-start only, no `/proc` scan).

## 0.35.152

- **Also decouple the OperatorOwned projection lane + SimWorld regression tests
  (`#layoutconvergencelane`).** 0.35.151 fixed the Converged path but the
  OperatorOwned path still required `observation_is_current` (actor_bindings
  identity match), so operator-owned layouts still oscillated at ~1Hz under CRDT
  storms. Both Converged and OperatorOwned now derive from generation + survey
  alone. Three deterministic regression tests guard the invariant:
  `layout_convergence_lane_survives_actor_store_storm` (20 actor-store mutations
  must not invalidate a converged layout),
  `operator_owned_lane_survives_actor_store_storm`, and
  `a_new_desired_generation_re_derives_to_needs_effect` (a genuine layout change
  still re-syncs).

## 0.35.151

- **Break the reactive coupling that trapped the pane-layout worker in a ~1Hz
  re-sync loop (`#layoutconvergencelane`).** The Converged projection required
  the stored observation's `actor_bindings` to match the live Computed bindings
  derived from the actor store. CRDT replica storms, session heartbeats, and
  unrelated document events mutate the actor store, which invalidated the
  Converged projection back to NeedsEffect, re-triggering the worker — producing
  10–20 repeated convergence attempts (the ~20s latency). The convergence
  determination now derives from **generation + survey-synced alone** (a
  physical-layout fact), not from actor-identity (`session_id`, `generation`
  fields). Actor-store mutations can no longer invalidate a converged layout; a
  genuine layout change arrives as a new desired generation, which naturally
  re-derives. This is the "different reactive lanes" decoupling: the layout-
  convergence lane is independent of the CRDT/session lane.

## 0.35.150

- **Enrich the ownership fast path with structural file→pane mappings so ALL
  visible files skip the `/proc` walk, not just those with active session actors
  (`#tmuxautosyncreactive`).** Diagnostic evidence (`reactive_actor_bindings
  count=1`) showed the fast path hit for only 1 of 2 layout files — the second
  (no active agent-doc session) fell through to a ~300ms `/proc` walk per file,
  dominating the 615ms `ownership_per_file_loop`. The worker now enriches the
  invocation's actor bindings with the previous sync's structural receipt
  (`last_structural_file_panes`), which already proved the file→pane assignment
  for every visible file. Combined with 0.35.149's stash-purge skip, the
  editor-triggered sync should drop from ~1.5s to a few hundred milliseconds.

## 0.35.149

- **Skip the expensive stash-pane purge for editor-triggered syncs and add an
  actor-binding diagnostic (`#tmuxautosyncreactive`).** The worker's structural
  sync still runs the full prune (497ms, dominated by `prune_stash_panes`'s
  per-pane kill-pane spawns at ~20ms each). When `exact_visible_projection` is
  true the desired pane set is already known from the editor projection, so the
  stash purge is housekeeping — defer it to the periodic cleanup. Adds a
  `reactive_actor_bindings count=…` sync-log line so the next iteration can
  attribute the remaining 620ms ownership loop to empty bindings (actor-store
  miss) vs. stale panes, since the fast path (`exact_visible_actor_projection_reused`)
  is not currently hitting.

## 0.35.148

- **Editor tab-switch focus no longer round-trips through the controller command
  socket (`#tmuxautosyncreactive`).** 0.35.147 deferred the `Sync` intent, but the
  `Focus` intent (a tab switch within the same layout — the most common operation)
  still called `focus_document_pane`, which round-tripped through the command
  socket on every focus change. The production intent runner now defers `Focus`
  too, and `handle_editor_surface_observe` resolves + `select-pane`s the target
  pane **in-process** via `handle_focus_document_pane_with_policy` — a single tmux
  command with no socket round-trip, so the editor request returns in milliseconds.

## 0.35.147

- **Editor surface observation no longer blocks the editor socket request on a
  synchronous tmux structural sync (`#tmuxautosyncreactive`).** The eager
  `SurfaceIntent::Sync` consequence used to call `sync_tmux_layout`, which
  round-tripped through the controller command socket — so every multi-column
  editor observation waited for the full structural reconcile (up to the 35s
  command timeout) before the request returned. The production intent runner now
  defers `Sync` (returns immediately), and `handle_editor_surface_observe`
  publishes the desired layout **in-process** via `publish_pane_layout_desired`,
  scheduling the existing background `pane_layout_effect_worker` with no socket
  round-trip. The worker converges in the background and publishes the actual
  `TmuxLayout` back to every active editor client through the new
  `ControllerEditorSurfaceGraph::observe_tmux_for_project`, so each surface
  graph re-folds against its own editor surface and settles to `Idle` without the
  editor request waiting. `handle_editor_surface_observe` also folds against the
  retained tmux observation (no per-observation survey) — cold start derives
  `Sync`, which schedules the worker. The headline latency wedge from
  `#tmux-autosync-reactive` is resolved; `Focus` stays synchronous (single
  select-pane).

## 0.35.146

- **Controller tmux layout survey now batches per-pane session-name probes through
  the shared pane-snapshot scope (`#panesessionsnapshot`).** The drift-detection
  survey in `controller_tmux_layout_for_surface`
  (`agent-doc-controller-io/.../rpc.rs`) opened no observation scope, so every
  candidate pane in `pane_layout_observation_session` issued its own
  `tmux display-message -p #{session_name}` spawn — N subprocesses (~20ms each)
  serialized under the editor-observation mutex on every multi-column editor
  observation. The pane-snapshot format is now enriched to
  `#{pane_id}\t#{pane_dead}\t#{session_name}`, and `Tmux::pane_session`
  (`tmux-router`) reads the session from the shared `list-panes -a` listing when a
  scope is open, collapsing those N spawns to one listing shared with the
  liveness probes that already run in the same pass. The controller survey opens
  a `begin_pane_snapshot_scope()` for its read-only drift pass. Part of the tmux
  auto-sync latency investigation (`#tmux-autosync-reactive`); the headline
  retained-observation decoupling remains a separately-scoped phase.

## 0.35.145

- **Passive tmux sync now routes per-pane process-tree ownership reads through
  the reactive `owner_graph`.** The `sync --no-autostart` run opened tmux,
  pane-snapshot, and state-ledger observation scopes but not the
  `ProcessObservationScope`, so every candidate pane in the per-document
  ownership loop (`process_tree_contains_pid` /
  `process_tree_owner_document_other_than`) re-walked `/proc` from scratch — the
  exact "re-observe on every question" shape `owner_graph` (`#syncownerreactive`)
  exists to stop. Opening the scope batches those reads: each pane root PID is
  observed once and its derived owner cells are reused for the rest of the run.
  Moving or selecting a tmux pane does not change its process tree, so the
  `LocalReadScope` observation stays valid across the run's tmux mutations; a
  genuinely new pane is a new root PID and is observed on first request. Part of
  the tmux auto-sync latency investigation (`#tmux-autosync-reactive`).

## 0.35.144

- **Restart Agent now honors a newly persisted exact conversation binding.**
  The `agent:<mode>` intent remains attached to the pending restart until the
  old child exits. A same-harness continue restart validates current
  frontmatter `resume:` and may replace the supervisor's stale in-memory
  lineage; crash recovery and plain controller recycle remain pinned to the
  already observed thread.

## 0.35.143

- **Completed free-text work can now be acknowledged without resuming a paused
queue.** `agent-doc queue consume <FILE> --ack-text <TEXT>` requires an exact
match against the leading free-text row, refuses id-backed or ambiguous nodes,
adds the durable queue-prompt echo to the retained response, and converges the
strike through editor authority while preserving `queue: stop`. This closes the
provenance gap created when an operator finishes a stopped queue head through
direct Codex-console steering.

## 0.35.142

- **Controller-launch tests no longer treat a 500 ms process-start deadline as
semantic correctness.** Test builds now retain a bounded two-second publication
window and a three-second launch-claim window, matching the existing test RPC
budget. A healthy temporary controller descheduled by the full parallel CI suite
can publish normally without turning runner load into a flaky delivery failure.

## 0.35.141

- **Inter-item queue continuation now observes retained delivery settlement
  instead of whole-document equality with Git `HEAD`.** A newly added queue
  prompt or queue-control edit is next-cycle operator state, so it no longer
  wedges the supervisor at `convergence_gate unmet=editor_converged`.
  Genuinely unsettled retained writes still block the boundary.

- **JetBrains tab focus is now a retained, focus-only editor surface rather
  than a competing imperative command.** The selected document publishes to
  its own controller under a distinct latest-wins source; the controller
  derives pane selection without inferring layout changes. Reverse-focus
  suppression begins only after an exact successful selection receipt, so a
  missing actor cannot hold a stale 90-second lease or pull the editor toward
`stash`. JetBrains plugin `0.2.349` carries the adapter change.

- **A rebased CRDT delivery now settles through a retained reactive receipt.**
A short-lived caller publishes its semantic `Satisfied` observation into a
controller-owned Source; the controller's Computed joins it only if the intent
identity and retained target match and the receipt hash equals both live editor
authority and disk. The subscribed settlement effect then performs the durable
clear. Response canonicalization can no longer leave the controller stuck at
`PayloadAbsentFromConvergedContent` after the current document already proves
the response.

## 0.35.140

- **Owned-pane queue continuation is now derived state, not recursive command
  injection.** A process-scoped Lazily graph observes queue-head identity,
  document-state epochs, effect-retry epochs, and dispatch admission separately.
  It emits a continuation prompt for the existing pane instead of
  `agent-doc <file>`, and consumes the head edge only after the controller
  projects a new cycle identity; accepted-only delivery stays retryable.

- **A replaced recovery cannot permanently block the next queue head.**
  `abandoned` and `committed` are both terminal inter-item cycle boundaries once
  editor, IPC, and actor convergence hold. This removes the
  `convergence_gate_timeout unmet=committed` wedge that previously fed the
  recursive owner-pane refusal.

## 0.35.139

- **JetBrains restart restoration no longer contracts a two-pane surface into
  `stash`.** IntelliJ now settles structural and file-open edges across bounded
  later EDT turns, refuses to publish while any restored editor window is
  empty, derives visibility from restored windows instead of a lagging
  aggregate, and compares absolute selected/layout paths. A fresh plugin JVM
  recovers candidate controller roots from open markdown files and
  generation-fenced-retires every older `jetbrains-pid` projection at
  incompatible roots before replacement publication. Other editor families
  remain independent, and delayed old-JVM retirement cannot stop the
  replacement.

- **JetBrains tab navigation and i3 workspace return now have explicit reactive
  boundaries.** Selection callbacks only capture the event file and schedule
  projection. The next EDT pass snapshots immutable IDE state; project-root
  discovery, filesystem walks, patch-watch registration, JSON construction,
  controller delivery, and tmux work run on the serialized background lane.
  Returning through an application-activation edge republishes the
  bounded-settled surface, repairing a terminal tool window reconstructed while
  hidden without a manual **Sync Tmux Pane** action. JetBrains plugin `0.2.348`
  carries both fixes.

- **Completed free-text queue residue can recover from an unambiguous historical
  response heading.** The normal proof remains a verbatim
  `> **Queue prompt:**` echo. When an older answer omitted it, preflight may
  strike the committed head only if a `### Re:` topic repeats the same long
  underscore-delimited code identifier. Prose mentions, filenames, short or
  different identifiers, and explicitly deferred responses remain
  insufficient.

- **Retained editor delivery no longer races its own closeout wake.** Exact
  visible content first settles the durable write; a distinct Lazily
  `SettledCloseoutReady` projection then derives the captured-finalize
  continuation from the open cycle, latest converged write, and live editor
  content. This removes the one-shot wake that could fire before settlement and
  strand a cycle at `write_applied`. New ledgers record
  `editor_projection_pending`; the legacy `crdt_delivery_ack_pending` token is
  decode-only and migrates to the reactive reason.

## 0.35.138

- **Editor authority is now an exact-key reactive projection, not a
  request/acknowledgment handshake.** Focusing a document materializes one
  stable dependency source whose initial value is `Unavailable`; the editor's
  first and subsequent authority publications update that source directly to
  `Available(authority)`. The focused-document computation therefore acquires
  its dependency before any publication exists and reruns only when that exact
  document changes. The compatibility membership epoch and its imperative bump
  are removed. The Agent Doc workspace is unified on lazily `0.56.0`, whose
  sync, thread-safe, and async dependency maps share this contract and preserve
  source identity across unpublish/republish cycles.

## 0.35.137

- **JetBrains editor surfaces retire superseded controller roots.** A mixed-root
  split could publish the same editor surface through the workspace controller
  and then a nested-project controller without forgetting the first retained
  projection. Both controllers remained healthy but reconciled one tmux window
  from incompatible layouts, repeatedly moving the other live pane into
  `stash` and leaving it one line tall. The listener now keeps one active
  controller-root subscription per editor surface, generation-fenced-forgets
  older roots only after the replacement publish succeeds, and retries a
  failed retirement on the next publication. Component-focus events now stay
  on the targeted selection lane instead of republishing unchanged structural
  layout. JetBrains plugin `0.2.346` carries the fix.

## 0.35.136

- **Replay-repair settlement uses the same capture-scoped proof as
  session-check.** Version 0.35.135 detected and wrote lossless replay
  canonicalization with the document's durable response capture and baseline,
  but its git boundary re-ran only the context-free normalizer against `HEAD`.
  A fragmented captured-response replay could therefore reach exact editor and
  disk convergence, then be refused before commit. The commit transaction now
  re-proves `HEAD` through the same file-aware checkpoint path. A regression
  fixture confirms that a repair requiring capture evidence commits exactly
  while the context-free proof remains insufficient.

## 0.35.135

- **Session-check now commits the exact replay repair it owns.** A recovery
  pass could losslessly canonicalize a proven same-topic response replay and
  then report `OK` while its read-only wrapper suppressed the corresponding git
  settlement. The repaired document therefore remained dirty and a later
  `agent-doc commit` had no cycle boundary to finish. Session-check now proves
  that normalizing the `HEAD` document yields the exact current authority and
  disk bytes, commits only the session document through a private-index
  compare-and-swap transaction, checkpoints the committed baseline, and
  re-proves authority/disk/`HEAD` convergence before reporting `OK`. A process
  restart between repair and commit resumes from the same proof, while
  unrelated staged and working-tree changes remain untouched.

- **JetBrains immediate pane focus is separate from mixed-root layout
  observation again.** A refactor removed the plugin's
  `focus_document_pane` submission and relied exclusively on one editor-surface
  observation. Because that observation must choose one controller root for
  the complete visible layout, a split containing a superproject session and a
  submodule session could leave automatic pane focus on the previous document.
  Selection and component-focus events now use a lifecycle-safe,
  generation-fenced micro-coalescing lane that resolves the focused file's own
  controller root and submits the existing project-scoped latest-wins focus
  command. Layout observation remains independent and never chooses a pane.

## 0.35.134

- **Captured closeout recovery now collapses partial same-topic response
  replays exactly once.** A rejected editor receipt followed by compatibility
  patchback could leave one bloated response cell, one partial replay cell, and
  one exact captured cell. Their body-aware ids differed, so generic duplicate
  removal preserved all three and session-check could commit a structurally
valid but repeated response. Recovery now joins the durable capture with its
baseline, reconstructs a distinct earlier response from one exact/prefixed
baseline node, retains one exact captured cell only when every competing
same-topic line is proven to come from those two sources, and fails closed on
any novel/operator line or ambiguous baseline provenance.

- **A repair that races a late editor registration now settles or remains
  resumable.** If a document is detached at the write boundary and JetBrains
  registers immediately after the repair reaches disk, Agent Doc gives the
  exact pre-repair editor authority a bounded convergence window. A prompt
  acknowledgment completes normally; a slower acknowledgment retains the
  original compare-and-swap lineage for `session-check` instead of reporting
  the already-applied repair as a terminal failure. A different/newer editor
  cut still fails closed.

- **Automatic editor layout projection now preserves live operator-owned
panes.** The foreground route convergence introduced in `0.35.133` still
  reshapes exact-visible layouts when the operator explicitly runs Agent Doc,
  but a background JetBrains surface refresh first observes the physical tmux
  window and settles as terminal `operator_owned` when it contains another live
  actor. It no longer repeatedly swaps the active Agent Doc pane into `stash`
  or collapses that pane to a one-line placeholder.

- **JetBrains visual refresh disposal is race-free and component rows use a
  neutral default background.** Refresh scheduling and executor shutdown now
  share one lifecycle lock, eliminating the check-then-shutdown gap behind
  `RejectedExecutionException`; the rejection catch remains as defense in
  depth. Component bodies inherit the editor background unless explicitly
  overridden, avoiding the poorly composed theme-green row wash. JetBrains
  plugin `0.2.344` carries the fix.

## 0.35.133

- **Foreground editor routes now converge their exact visible tmux layout
  before dispatch.** `Run Agent Doc` promotes its `--col` / `--focus` payload
  into the controller-owned desired-layout Source and waits for the retained
  tmux effect before touching the routed pane. Editor-relative paths are
  canonicalized at that boundary. If the focused document is absent, exactly
  one explicit empty-column placeholder is materialized with it; zero or
multiple candidates fail before desired-state publication. A missing layout
or a routed document outside those columns fails closed. The convergence
observer derives an unconfigured shared tmux session from the structural
effect's file-to-pane assignment, so nested-project routes can acknowledge
the same session the effect actually changed. Provisioning exact-visible
effects also consume the controller's reactive actor bindings; an unrelated
pane in the same window can no longer be positionally mislabeled as the routed
document before dispatch restores the real actor. Live actors outside the
desired surface are no longer terminal `operator_owned` panes: reconciliation
moves them to `stash` without killing the harness and remains retryable until
pane count and order converge. This closes the route-time race that appended
a third pane beside an already-correct two-pane editor projection.
- **Foreground commands now re-observe focus and geometry before accepting a
cached layout projection.** Repeating the same desired columns no longer
reuses an old `Converged` value after tmux drift. Physical convergence includes
the active pane in the target window, so a successful `select-pane` effect is
not sufficient by itself; a focus mismatch remains retryable until the
requested document's actor pane is actually active.
- **JetBrains generation handoff no longer leaks disposed callbacks or native
receipt calls onto the EDT.** Patch application captures immutable post-write
content inside the editor closure, then publishes the content receipt from the
socket worker before acknowledgement. Visual-highlighter callbacks become
inert once their plugin generation is disposed and tolerate the narrow
scheduler-shutdown race. Cross-subproject markdown moves also choose one
controller root that contains both paths, instead of retaining an impossible
new-subproject transition forever.

## 0.35.132

- **Automatic tmux pane-layout reconciliation now retains retryable work until
  it converges.** A transient pane-count or focus mismatch no longer retires
  the only structural-effect worker after its first attempt. The latest-wins
  worker keeps ownership, retries with bounded exponential backoff, and wakes
  immediately when a newer desired layout or actor binding supersedes the
  pending attempt. Convergence and operator-owned layouts remain terminal.
  The backoff calculation also reaches its documented five-second ceiling
  instead of capping one step early.

## 0.35.131

- **Retained Lazily projections now have a reactive, idempotent CRDT fixed
  point.** If rebasing a retained Base→Target delta over the converged visible
  cut produces that same cut, the Computed derives `TargetVisible` and resumes
  closeout without issuing another CP write. The relay also guards its effect
  boundary against equal content, preserving the acknowledged delivery
  generation instead of emitting Yrs' otherwise-nonempty equal-replace
  transaction and making the retained version look stuck again.
- **Finalize recovery now removes the proven stranded duplicate response shell
before its integrity gate.** Compact already knew how to remove an empty
`### Re:` heading when an earlier response with the same normalized topic has
a real body, but recovery-capable session-check omitted that lossless
canonicalizer. A retained finalize could therefore reproduce the known shell
and then fail before reaching its own recovery. The shared realtime replay
normalizer now applies the same narrow repair through CAS authority, while
unique empty headings and every ambiguous shape still fail closed.
- **An accepted repair projection no longer fails on immediate disk equality.**
When the exact canonical repair target remains in the controller-owned retained
write graph, the repair boundary now reports the existing typed reactive
projection deferral and leaves that intent subscribed for convergence. It does
not mislabel asynchronous JetBrains CRDT/native-save projection as a failed
write, clear the only resumable lineage, poll, resubmit, republish, force disk,
or request a controller recycle. A mismatch without that exact retained owner
still fails closed.
- **The orphan preparing-controller scanner now follows the authoritative
  handoff clock.** A promoted controller retains its immutable
  `--handoff-state preparing` argv after bootstrap ownership moves to a fresh
  replacement. Process age no longer lets that discovery marker reap the
  predecessor during a healthy ownership transition; only an authoritative
  handoff that has itself exceeded the stale threshold exposes it to cleanup.
- **Controller recycle now invalidates cached editor members explicitly.** An
  attached `replica_pull` whose identity is absent from the replacement
  controller returns `missing_replica` instead of an inert empty delta batch.
  JetBrains and VS Code both atomically re-register on that typed projection,
  preventing a post-install recycle from leaving the new controller permanently
  memberless while the editor keeps polling with its old local client.
- **Retained editor delivery now reconciles across a newer converged editor cut
  after controller restart.** The controller can semantically rebase the
  retained response target over intervening operator edits when both cuts share
  a stable document anchor, then applies it with the current editor generation
  as its CAS base. Durable intent identity, original target hash, projected
  target hash, and latest-effect checks fence stale or superseded work, while
  unrelated replacement documents still fail closed. A completed response can
  no longer leave the same prompt active merely because the editor advanced
  before retained delivery resumed, and recovery does not duplicate the
  response or resurrect discarded content.
- **Codex routed-dispatch start proof now uses the same active-turn window as
  admission.** Both paths trim blank terminal padding, then classify the bottom
  eight meaningful pane rows through one harness-owned observation. A live
  `Working` line can no longer disappear behind unused tmux viewport rows, while
  a stale `Working` line farther up scrollback still cannot make the
  pre-dispatch baseline appear busy and mask the real false→true start edge
  (`#jetbrainsrunroute`, `#jetbrainsrunroute-8x66`,
  `#jetbrainsrunroute-tgp5`).
- **JetBrains startup restoration now publishes the completed editor surface
  when Markdown files finish opening.** IDEA can restore split containers and
  seed their structural shape before restored files enter the selected-file
  projection, without emitting another selection or container event. The
  `fileOpened` adapter edge now re-observes that completed surface; the
  controller-owned Lazily graph still exclusively derives and applies the tmux
effect. A cross-adapter contract test proves JetBrains and VS Code both bridge
visible-membership changes into `editor_surface_observe`, preserving plugin
parity.
- **JetBrains automatic layout projection no longer drops a tab/focus change
behind a slow prior sync.** A document-selection observation used to retain
pending-event precedence throughout controller delivery, which can spend
seconds proving pane ownership. A genuine later component-focus event for a
different visible document was then misclassified as a stale opposite-editor
callback and discarded. The pending slot now releases once the EDT captures a
self-consistent surface, restores only after a same-generation delivery
failure, and lets newer observations supersede in-flight work latest-wins.
- **Automatic editor-surface reconciliation now realizes missing visible panes
instead of requiring an imperative republish.** The controller previously
  translated every Lazily-derived `Sync` effect to `no_autostart=true`, so
  startup/file-open/tab-switch observations could only rearrange panes that
  already existed; switching to `lazily.md` stayed invisible until explicit
  `Sync Tmux Layout` repeated the same desired state with autostart enabled.
  Derived sync now remains `caller_kind=automatic` but permits the binary-owned
  safe-autostart policy to provision visible documents. Ambiguous/live owners
  still fail closed, inactive-editor focus is suppressed, background open files
  remain outside the desired columns, and JetBrains/VS Code share the same
  controller consequence.
- **JetBrains no longer turns a later whole-buffer read into operator-authored
  CRDT state.** Dirty incremental `DocumentEvent`s retain their exact
  offset/old/new fragments, validate them against the actor-owned shadow, and
  apply the bounded splice stream to the local replica before publishing one
  batched update. Clean incremental cache reloads, whole-text replacements, and
  remote/native projections are non-operator events that advance the projection
  epoch and fence queued local splices. A stale range is retained for canonical
  recovery instead of widening into a document replacement. JetBrains and VS
  Code now advertise and test the shared `bounded_editor_splices_v1`
  capability; regressions cover partial queue typing, cross-cell edits, Unicode
  offsets, reload fencing, and the absence of whole-buffer reads from the
  ordinary local-mutation path. JetBrains also consumes IDEA's explicit
  before/after file-content-reload lifecycle as non-operator provenance, so an
  incremental File Cache Conflict reload remains fenced even if the buffer is
  otherwise dirty.

## 0.35.130

- **Compaction now recovers the exact stranded duplicate response-heading
replay shape without weakening document integrity.** Response materialization
  canonicalizes an empty `### Re:` shell when an earlier response with the same
  normalized topic already has a real body, even after later responses make
  the shell non-tail. Compact performs that same byte-preserving
  canonicalization before its integrity gate, logs it, and then validates the
  resulting authority normally. It removes only the redundant heading line;
  every body, prompt, comment, and unrelated response byte survives. Unique
  empty headings and all other malformed documents still fail closed before
  tags, archives, snapshots, writes, or commits.

## 0.35.129

- **The first navigation from a one-pane document to a split editor surface now
  restores the complete tmux layout.** JetBrains selection events wait until
  both `FileEditorManager.selectedFiles` and the detected split columns contain
  the newly selected document. If IntelliJ leaves only one projection stale
  through the bounded later-EDT reads, the adapter applies the exact event
  old→new edge to that projection independently. A current selected-files list
  can therefore no longer mask stale one-column geometry until a second
  navigation event. Missing prior-file evidence remains fail-closed, and the
  Project Controller still exclusively owns focus/layout decisions and tmux
  effects.

## 0.35.128

- **Codex routed dispatch no longer rejects positive pane-start evidence while
waiting for a delayed prompt hook (`#jetbrainsrunroute`).** The tracker now
captures the pre-dispatch pane baseline and treats a changed pane with a newly
appeared `Working`/`esc to interrupt` cue as dispatch-start proof after direct
input acceptance. The submitted command may remain visible in Codex scrollback
without being mistaken for an unsubmitted draft. An unchanged pane, a drafted
command without a busy cue, and a pane that was already busy remain unproven.
- **Captured closeout recovery no longer loops forever after a retained
projection reverses the response body and heading.** Recovery recognizes the
  exact captured body immediately before its matching heading-only exchange
  tail, moves that existing heading ahead of the body without duplicating
  either, and preserves unrelated operator lines. If the deferred-write intent
  has already retired, `session-check` may now replay that semantic response
  cell through live editor/CRDT authority, wait for its delivery receipt, and
  persist the exact repaired cut. It still never force-writes disk or replaces
  concurrent operator edits.
- **A dispatch-created pane outside the current editor surface no longer keeps
  pane-layout reconciliation in an unbounded retry
  (`#panelayoutrace`).** Same-window actor bindings now participate in the
  reactive projection even when their document is absent from the desired
  columns. If every requested document is still present and the additional pane
  has a live dispatch owner, the controller reports terminal `operator_owned`
  with reason `active_dispatch_outside_surface` until either the surface
  generation or actor ownership changes. A missing requested pane still remains
  retryable. Dispatch acceptance logs the canonical target and the active
  editor-surface observation generations so the ordering is reconstructable.
- **Pane-layout convergence independently proves physical pane order.** The
  generation-scoped effect receipt's file-to-pane mapping is projected in
  desired document order and compared with tmux's observed left-to-right pane
  sequence. Matching document labels can no longer overclaim convergence when
  physical panes are reversed. Focus remains a file-to-pane lookup rather than
  a physical index.
- **Terminal convergence can now be scoped to exact retained-write component
  ownership (`#percellconverge` phase 3).** The default remains whole-document
  equality. Projects may explicitly opt in with
  `agent_doc_per_component_convergence = true` (or document frontmatter
  `agent_doc_per_component_convergence: true`). When enabled, `session-check`
  derives the owned component-name set from every retained write journal entry's
  exact expected→target transition and requires authority/disk equality only
  for that set. Operator-only divergence in `queue` therefore cannot hold an
  `exchange` response closeout, while malformed transitions and legacy intents
  without expected content fall back to whole-document equality. The gate does
  not authorize a disk merge or remove the whole-document hash. Adversarial
  SimWorld coverage types into `queue` on every tick during an `exchange`
  closeout and asserts every keystroke survives; a paired ownership-overclaim
mutation demonstrates that the survival oracle goes red when `queue` is
incorrectly claimed.
- **Opt-in commits now rebase the owned target onto the current operator cut
  (`#percellconverge` phase 4).** Before staging the session-document blob, the
  commit coordinator derives the same owned component-name set from exact
  retained expected→target transitions. It verifies that those owned components
  have reached current Lazily/editor authority, then checkpoints that current
  authority cut as the Git candidate. Operator edits in unowned components are
  therefore committed byte-for-byte instead of being replaced by the older
  whole-document snapshot. An owned mismatch fails closed rather than letting
  Git outrun editor delivery. Legacy intents without expected content and
  malformed transitions retain the whole-document fallback. The commit path
  never elects or component-merges an independent disk candidate while a live
  editor owns the document.
- **Retained closeout replay no longer wedges when a valid legacy response
  heading reaches the atomic response-cell fast path
  (`#responsecelllegacyfallback`).** Strict template compatibility accepts
  headings such as `## Re:`, while the realtime response-cell AST deliberately
  accepts canonical `###` response nodes only. The fast-path selector now proves
  that the entire patch parses as assistant-only response cells before asking
  the controller to add it. Legacy heading depths and mixed response/prompt
  patches fall through to the established compatibility patchback path instead
  of making every `response_captured` recovery retry fail with `response cell
  may contain only assistant response nodes`.
- **Tracked `/clear` recovery can replace an idle owner when an open retained
  closeout rejects the in-place fresh restart
  (`#trackedclearretainedreplacement`).** Route now distinguishes an accepted
  supervisor restart, a typed supervisor rejection, and an unavailable
  supervisor. It may escalate an open-cycle rejection to a forced controller
  replacement only when the pane is independently dispatch-ready and the durable
  phase is `ResponseCaptured` or `WriteApplied`; every other shape fails closed.
  Route then requires the authoritative actor to publish a newer generation
  before it attempts prompt dispatch, eliminating the former impossible advice
  to run `agent-doc start` manually from an already-owned pane.
- **Fresh Codex auto-trigger recognizes dynamic dim composer suggestions
  (`#codexdimautotrigger`).** Supervisor readiness, live payload observation,
  pane-content inspection, and post-submit verification now preserve ANSI so
  SGR-2 remains available to the harness classifier. An unlisted dim suggestion
  such as `Summarize recent commits` is idle placeholder chrome, while the same
  undecorated text remains drafted operator input. This closes the restart shape
  where the fresh child was ready but auto-trigger timed out as `no_prompt`.
- **The editor's "Restart Agent" action reached the controller and was rejected
  outright (`#agentrestartwire`).** `session_actor_cmd::restart_agent` encodes
  the operator's harness-replacement intent as `agent:<mode>` so it stays
  distinguishable from a plain controller recycle across transport. The
  supervisor IPC layer has always decoded that prefix
  (`decode_restart_intent`), but the controller's
  `parse_supervisor_replacement_request` did not — so every Restart Agent
  invocation failed with ``agent-doc command failed (exit 1): ... unsupported
  supervisor replacement mode `agent:continue` `` before any supervisor was
  asked, and the operator's `agent:` change never took effect. Live evidence:
  three `restart_agent_menu_invoked` markers for `tasks/software/lazily.md` on
  2026-08-03 with no matching
  `controller_supervisor_replacement_accepted`. The controller now parses the
  request into a `SupervisorReplacementIntent { mode, restart_agent }`, forwards
  `intent.wire_mode()` verbatim on the supervisor IPC `Restart` so the prefix
  still reaches `decode_restart_intent`, and — on the cold-start path — treats a
  Restart Agent as authorization to `RestartLiveHarness` even in continue mode.
  Preserving the child there would have discarded the operator's request and,
  after an `agent:` switch, kept the old harness for the rest of the session.
  The foreign-pane guard is unchanged: a live pane that is not running this
  document's own harness is still `BlockLiveNonShell`. Coverage:
  `supervisor_replacement_accepts_the_restart_agent_wire_prefix`,
  `restart_agent_wire_mode_round_trips_through_the_controller`,
  `restart_agent_intent_replaces_a_live_harness_even_in_continue_mode`,
  `restart_agent_prefix_still_rejects_an_unsupported_mode`,
  `restart_agent_intent_restarts_the_live_harness_in_continue_mode`.
- **A restart that re-resolved the harness and found no change left no trace.**
  `#agentreloadrestart` Phase 1b re-reads the current frontmatter on every
  restart iteration, but only the *changed* branch logged. That made "the
  restart came back on the old agent" undiagnosable after the fact: nothing in
  the logs separates "re-read the document and it still resolved `claude`" from
  "never re-read it at all", which is precisely the question an operator asks
  after editing `agent:`. The inert, failed, and unreadable-frontmatter branches
  now each write an ops-log line
  (`agent_restart_respec_inert` / `agent_restart_respec_failed` /
  `agent_restart_respec_skipped`) carrying the running harness, the re-resolved
  harness, and the frontmatter `agent:` value they resolved from.

## 0.35.127

_JetBrains plugin 0.2.340; VS Code extension 0.2.65; Zed extension 0.1.0._

- **Per-component ops are now recorded from the post-maintenance document.**
  `compute_with_current` runs before `run_queue_maintenance`, so any structural
  normalization maintenance applied was invisible to the content the op log was
  built from. The `#qfoldedhead` re-segmentation is the case that mattered: a
  pasted block that arrived folded is a single node in the pre-maintenance cut,
  so every id after the first never reached `op_log`. That is why a sixteen-item
  operator paste on 2026-08-03 recorded zero per-item ops, which starves every
  later per-component convergence phase of its substrate. Op recording now
  re-resolves the document as maintenance left it. Scoped to the op log — the
  preflight contract still reports from the original cut, so what is durably
  recorded changes without moving contract semantics.

## 0.35.126

_JetBrains plugin 0.2.340; VS Code extension 0.2.65; Zed extension 0.1.0._

- Version-only release so the operator-restart open-cycle gate from 0.35.124
  can actually be installed. A concurrent session had built and installed an
  unpushed local `0.35.125` whose tree predates that fix, so `binary-install`
  correctly refused the 0.35.124 downgrade and the landed gate was not the code
  running. Releasing from `main` — which carries the fix — supersedes that local
  build without overriding the downgrade guard.

## 0.35.124

_JetBrains plugin 0.2.338; VS Code extension 0.2.65; Zed extension 0.1.0._

- **An operator `session_restart` no longer spawns a second harness child over
  a live one.** The recycle path has always deferred while a document cycle is
  open (`supervisor_recycle_deferred_cycle_open`); the restart path had no
  equivalent gate. On 2026-08-03 recycle deferred on `agent_doc_cycle_open` at
  07:27:54 and again at 07:28:32 while `restart_continue_spawn` fired between
  them at 07:28:29, and with `--route-owned-reap-policy keep-alive` the previous
  child was never reaped — so two children rendered through one pane's PTY
  proxy and clearing the visible session revealed the other underneath. The
  controller's `ipc_accepted_deferred reason=live_supervisor_owns_drain` was
  misleading here: it only means the *controller* declined to escalate, while
  the request had already been accepted by the live supervisor. Restart now
  refuses when a cycle is open **and** the child is still alive, and says why
  rather than half-applying. A dead child still restarts, so a crashed harness
  is never stranded.

## 0.35.125

_JetBrains plugin 0.2.339; VS Code extension 0.2.65; Zed extension 0.1.0._

- **The IDE recovers from a host reboot on its own (`#rebootselfheal`).** A
  reboot leaves `.agent-doc/controller.sock` in one of two states that mean
  exactly the same thing — nothing is listening. The file went away with the
  tmpfs (`ENOENT`), or it outlived the process that bound it (`ECONNREFUSED`).
  Neither is fixable by retrying the connect, so the JetBrains plugin surfaced
  them verbatim — `Sync failed: pane-layout state projection publish failed via
  .../controller.sock: Connection refused` — and stayed broken until a human
  deleted the socket by hand. Reported 2026-08-03 after an X11 wedge and reboot.

  The binary already recovered from both: `connect_or_launch` adopts a live
  controller or launches one, and the bind path unlinks a stale socket file
  before listening. The editors simply had no way to ask. New FFI export
  `agent_doc_ensure_controller_running` exposes that existing recovery, and
  JetBrains calls it once on a connect failure that *proves* no listener, then
  retries. Recovery stays a single delegation — a plugin that grows its own
  unlink/launch logic is how the editor and the binary drift into disagreeing
  about controller liveness.

  Scoped to operator-initiated lanes (Sync Tmux Layout, Run Agent Doc, focus
  handoff). `editors/SPEC.md` requires the passive editor-surface observation
  lane to send "over the existing-controller socket; it never launches the
  controller", so a tab click still cannot start one. An ambiguous failure —
  permission, timeout, broken pipe — is never treated as proof of death, because
  relaunching over a live-but-slow controller is worse than reporting the error;
  that is the same rule the binary's `SocketLiveness` probe follows.

- **Editor parity for the above is machine-checked, not asserted.** VS Code
  already had this via a different mechanism (`controller status --ensure`
  before an operator route), and Zed has no controller socket transport at all,
  so the gap was JetBrains-only — but nothing enforced that. New capability
  `controller_reboot_self_heal_v1` in the editor contract, `required` in
  `editors/plugin-parity.tsv`, so the existing conformance test fails if a peer
  claims it without advertising it or drops it later.

## 0.35.124

_JetBrains plugin 0.2.338; VS Code extension 0.2.65; Zed extension 0.1.0._

- **A retained write now proves it has an owner before forbidding recovery.**
  Three sites tell an agent that its write is retained and that re-sending,
  `--force-disk`, `admin recycle`, and `admin reload-lib` are all forbidden:
  `crdt_relay_pending_refusal` (`agent-doc-git-io`), the authority/disk
  divergence INTERRUPT (`agent-doc-session-check-io`), and
  `await_editor_replica_no_disk_write` (`agent-doc-document-realtime-io`). That
  instruction is correct only while something durable actually holds the write.
  With no holder, no state edge can fire, so waiting never commits — and an
  agent that obeys loses the work.

  0.35.123 fixed `session-check` alone, which left the two paths contradicting
  each other. Observed three times on 2026-08-03 on
  `tasks/agent-doc/agent-doc-bugs2.md`: `write --commit` printed "this is a
  deferral, not a lost response" while `session-check` printed "STRANDED, not
  deferred" about the same state. `session-check` was right every time, each was
  recovered by hand with `agent-doc commit`, and whichever text the agent
  reached first decided whether the work survived.

  The predicate now lives once, in `agent_doc_turn::write_ownership`
  (`RetainedWriteOwnership::verdict` + `retained_write_remedy`), with
  `agent_doc_capture_io::retained_write_ownership` as its I/O shell, and all
  three sites derive their wording from it. The unowned case names the recovery
  instead of forbidding it; the 2026-07-26 do-NOT guidance is unchanged for the
  genuinely-owned case. A failed read is not ownership — an unproven site says
  stranded. Ownership is deliberately not keyed on editor attachment, which is a
  one-way latch (`#editormodelmissing`).

  Note that consolidating the *wording* into one function had already been tried
  and did not prevent this; the **predicate** is what drifted. So two guards in
  `agent-doc-turn/tests/retained_write_ownership_guard.rs` hold both halves: no
  site may re-author a verdict phrase, and every site must actually call the
  predicate. `commit_blocked_crdt_relay_pending` now records
  `ownership=deferred|stranded` so `ops.log` shows which verdict was printed.

  This is the write-path half of `#percellconverge`. Per-component ownership
  replaces the predicate's inputs later without changing the call sites.

## 0.35.123

_JetBrains plugin 0.2.338; VS Code extension 0.2.65; Zed extension 0.1.0._

- **An authority/disk divergence with no owner now tries to converge instead of
  stalling the queue forever.** `session-check` classified a divergence as
  `ObserveOnly` whenever the cycle was not `write_applied` with a retained
  block — including the case where the cycle is already terminal and nothing is
  retained. Nothing owns that state, so no controller edge can ever fire, yet
  the INTERRUPT told the session to stand down and stop draining. It stranded
  two separate batches of response and backlog edits on one document in a
  single evening. That case now asks the live editor to persist its own buffer
  — the same editor-owned effect the retained path already uses, never a
  competing document replacement — which is the only action that converges it
  without the agent writing the document. An open cycle or a retained write is
  still owned and still waits.
- **The divergence INTERRUPT no longer promises a recovery it has no record
  of.** It claimed unconditionally that "the controller owns the next closeout
  attempt and will wake on that state edge" and forbade every remedy. When no
  cycle is open and no capture is retained it now says the edits are stranded
  rather than deferred, and names the recovery — from the owning pane, since
  both commands abort elsewhere with `pane ownership mismatch`.

## 0.35.122

_JetBrains plugin 0.2.338; VS Code extension 0.2.65; Zed extension 0.1.0._

- **The document-only drift remedy now names the pane it must run in.** Both
  recovery commands go through `verify_pane_ownership`, so running the
  prescribed `agent-doc commit` from any other pane aborts with `pane ownership
  mismatch` — which reads as a second, unrelated failure rather than as the
  guard doing its job. The hint now states the constraint up front, names
  `agent-doc claim` as the only way out from another pane, and qualifies it:
  reclaiming seizes the document from a live session, so it is for a session
  that has finished, not a shortcut around the guard.

## 0.35.121

_JetBrains plugin 0.2.338; VS Code extension 0.2.65; Zed extension 0.1.0._

- **A response that calls a queue head untouched no longer strikes it.**
  `#ftstrike` treated a quoted-prompt echo as proof that the cycle answered a
  free-text head, but the echo is only the responding agent's assertion —
  nothing verified the work happened. A response that quoted a head and in the
  same breath called it "queued and untouched" still struck it, silently marking
  unrun work complete and losing the operator's request. The strike now reads
  the sentence next to the quote and defers when it says the head is still
  outstanding. Genuinely answered heads strike exactly as before, and the guard
  can only ever prevent a strike, never cause one.
- **A refused replica registration no longer floods the ops log.** A detached
  document is refused for as long as the editor retries, which is forever: a
  stale JetBrains plugin generation drove ~50 identical
  `controller_crdt_replica_refused` lines per minute indefinitely, burying every
  real diagnostic while Run Agent Doc silently did nothing. The first three
  refusals are logged verbatim, then at most one per minute, and crossing 25
  raises a single `controller_crdt_replica_refusal_storm` advisory naming the
  cause and the remedy — restart the IDE, since `reload-lib` and `recycle`
  cannot fix a Kotlin plugin generation mismatch. A successful registration
  clears the ledger so a later detach is loud again.

## 0.35.120

_JetBrains plugin 0.2.338; VS Code extension 0.2.65; Zed extension 0.1.0._

- **Queue heads folded into continuation lines by an editor paste are
  recovered.** Pasting one `do [#id]` per line can arrive from the editor with
  the bullet of every line but the first replaced by continuation indent. Those
  lines parsed as inert `Freeform` residue: never drainable, unreachable by
  `queue consume`/strike, and — because the backlog→queue mirror is multi-id
  aware — counted as already present, so the mirror never re-emitted them
  either. One paste of sixteen ids therefore collapsed into a single
  addressable head and never healed itself. Preflight now re-segments such a
  block back into one prompt per id, verbatim (an operator `do ` prefix
  survives), and reports it as a `queue_folded_head_recovered` warning.
  Genuine multi-line prose heads and fenced `---` prompts are untouched.
- **PyPI publishing is driven by the tag push.** The Release workflow creates
  the GitHub Release with the default `GITHUB_TOKEN`, and GitHub does not raise
  workflow-triggering events for token-authored actions, so the `release:
  published` trigger had silently stopped firing and 0.35.97 through 0.35.118
  never reached PyPI.

Use `BREAKING CHANGE:` prefix in version entries to flag incompatible changes.

## 0.35.119

_JetBrains plugin 0.2.338; VS Code extension 0.2.65; Zed extension 0.1.0._

- **Codex Stop no longer reopens a committed turn for an explicitly stopped
  queue.** The hook now distinguishes an ordinary manual queue head, which
  still protects against a chat-only response, from a head parked by
  frontmatter `queue: stop`. A committed cycle with a visible stopped head now
  passes through exactly like `session-check`'s `no_drainable_work` outcome
  instead of repeatedly demanding another finalize/write attempt.

## 0.35.118

_JetBrains plugin 0.2.338; VS Code extension 0.2.65; Zed extension 0.1.0._

- **Resync no longer multiplies editor panes from the session registry.**
  Registry repair preserves proven live owners in stash and never bulk-promotes
  every registered actor. JetBrains publishes the current exact-visible editor
  layout as one distinct reactive `resync` generation after cleanup, so
  repeated repair preserves pane cardinality. Bare `agent-doc sync` now fails
  closed without an explicit `--col` instead of replaying a possibly cross-root
  stale layout row. The deterministic SimWorld harness covers the reported
  two-to-five pane oscillation.
- **Completed tracked work can be explicitly reopened without hand-editing
  binary-owned archives.** `agent-doc backlog <FILE> reopen <ID> [--queue]`
  removes the id from inline or external `agent:done`, restores the newest
  archived item as open backlog work, and optionally reactivates exactly one
  live queue directive. The archive, document, and queue projection share one
  tracked-work transaction, so a reopened id is not immediately reaped again
  by stale completion evidence. Shared closeout help now also states that
  tracked-work-only commits use `write --commit`; strict `respond`/`finalize`
  requires an assistant response body.
- **Exact visible delivery targets acknowledge without waiting on a stale local
  replica.** The editor adapters now distinguish an unrelated divergent buffer
  from a buffer whose hash exactly proves the controller generation. In the
  latter state they atomically rebuild the native replica from controller
  canonical state, reject raced operator edits, and only then publish the
  visible receipt. Controller socket receipt alone remains insufficient.
- **Preflight admission no longer falls back to model-owned polling.** Claude
  skill installation now projects the binary `UserPromptSubmit` admission hook,
  the workflow fails closed when its cycle contract is absent, and both local
  install targets refresh all harness artifacts. The development harness checks
  that install contract.
- **Operator edits made while a response is converging keep their exact CRDT
  base.** JetBrains and VS Code now retain a typed canonical-base/visible-result
  cut, rebuild a stale native replica only when controller canonical still
  equals that base, and then publish the minimal local delta plus visible
  receipt. Successive queue or response edits are serialized and coalesced;
  mismatched canonical state remains fail-closed instead of losing the edit or
  adopting the whole buffer.
- **Retained response recovery keeps the complete closeout mutation envelope.**
  A streamed closeout now captures its typed backlog/status plan before its
  first editor-authority read, so an older pending editor delivery cannot stop
  the command between response capture and mutation-plan capture. Binary-owned
  replay applies the same `--backlog-add`/`--backlog-edit` intent or fails
  closed; it can no longer commit only the recovered response. The Rust and
  development harnesses enforce this ordering.

## 0.35.117

_JetBrains plugin 0.2.337; VS Code extension 0.2.64; Zed extension 0.1.0._

- **Standalone salient conclusions can become document-visible before
  closeout.** `agent-doc salient-checkpoint` validates a complete Markdown
  body, upserts one explicitly non-final cycle-scoped exchange node through the
  controller/CRDT authority, and never acts as queue-answer evidence. Final
  response application deterministically removes that node, including on
  replay.
- **JetBrains tab selection settles its own layout projection.** When IDEA emits
  `selectionChanged` before `selectedFiles` and the split-window model expose
  the new tab, the plugin retains the selected-document fact and re-reads the
  projection on bounded later EDT turns. A single cross-root tab switch now
  publishes the current editor surface without requiring manual **Sync Tmux
  Layout**. The JetBrains regression suite is part of `make check` through the
  agent-doc development harness.

## 0.35.116

_JetBrains plugin 0.2.336; VS Code extension 0.2.64; Zed extension 0.1.0._

- **Controller recycle rebuilds editor replicas only after public promotion.**
  Private handoff replacements still hydrate durable reliable-sync
  registrations, but defer editor rebuild effects until the public socket
  reports their exact stable pid and generation. Editor projections can no
  longer land in the predecessor during install-driven or aggressive mid-turn
  recycle and disappear at promotion, leaving captured responses stranded
  behind `missing_replica`.
- **A selected free-text queue head is not automatically answered.** The
  in-progress marker now proves selection only. Striking still requires exact
  labeled queue-prompt evidence in the response plus stable-baseline membership,
  so `#bugautostruck` cannot remove an unanswered FPE review or other free-text
  work. The controller and queue regressions are part of the local dev harness.

## 0.35.115

_JetBrains plugin 0.2.336; VS Code extension 0.2.64; Zed extension 0.1.0._

- **Response-aware prefix repair no longer corrupts assistant answers.**
  Markdown blockquotes inside `### Re:` response bodies stay quoted response
  context even when their text appears in the prompt-normalization target set.
  Rust and JetBrains regression harnesses cover the reported `(HEAD)` response
  shape and prevent later answer prose from acquiring `❯`.
- **Realtime steering is a durable identity-keyed observable set.** Each
  directive is keyed by its kind and complete normalized body, deduplicates
  across reconnects, and retracts independently while retaining document order
  and the compatibility aggregate. Both mutation and visible-projection CRDT
  edges refresh the controller set; VS Code preserves it, and session-check
  trusts its authoritative open-turn emptiness only when the controller-stamped
  canonical content hash matches the CRDT content being checked. Missing or
  stale receipts safely retain the baseline-comparison fallback.

## 0.35.114

_JetBrains plugin 0.2.335; VS Code extension 0.2.64; Zed extension 0.1.0._

- **Ordered nested queue lists compile to enforceable leaf dependencies.** Exact
  numeric list markers and indentation now round-trip byte-for-byte. Ordered
  sibling subtrees derive canonical `after=` edges between their leaf
  frontiers, merge with explicit dependency metadata, and re-derive when the
  operator reorders the list; group labels remain id-free and non-executable.
  Runtime selection, drainability, and deferred-head accounting now all use the
  same leaf-only projection, so a group label cannot masquerade as runnable work
  or allow a later ordered leaf to bypass a deferred prerequisite.

## 0.35.113

_JetBrains plugin 0.2.335; VS Code extension 0.2.64; Zed extension 0.1.0._

- **Connected MCP closeout is pinned to one admitted response cycle.** The
  integration contract now drives `agent_doc_admit` through plan, component
  read, and `agent_doc_finalize`, then proves the terminal committed cycle id
  is the original admitted id and no `repaired stale preflight_started`
  recovery occurred. Connected clients can therefore trust
  `preflight_required=false` without opening a second legacy preflight cycle.

## 0.35.112

_JetBrains plugin 0.2.335; VS Code extension 0.2.64; Zed extension 0.1.0._

- **Mid-turn controller recycle now uses successor-before-retire promotion.**
  The predecessor launches one replacement on a private socket, waits for its
  durable liveness fold, promotes that socket atomically, redirects new RPCs,
  and retires only after its already-accepted RPCs drain. Continuous IDE or
  supervisor traffic no longer starves recycle, and an open harness dispatch
  remains owned by its unchanged route supervisor and child.
- **Controller startup is serviceable before retained editor notifications.**
  The durable reliable-sync fold remains synchronous, while potentially slow
  dead-editor and replica-rebuild IPC effects run after listener readiness.
  Concurrent launchers fail closed when an existing socket is still hydrating
  instead of unlinking it and publishing duplicate listeners.
- **Empty controller roots are rejected before launch-claim namespacing.**
Invalid callers can no longer hold the shared SHA-256-empty launch endpoint
or spend the install retry budget on an unsatisfiable `current_dir`.
- **Automatic mixed-root tmux layout sync is pane-authority-only and fast.**
Exact-visible editor snapshots preserve cross-root columns, honor an explicit
validated `agent-doc` window, and resolve an existing nested-root pane from
that root's controller actor projection before any editor-content read. A
temporary content-authority outage can no longer drop the right-side pane or
stall the parent sync; no-autostart still forbids foreign pane creation.
- **Published pane-layout projections are successful command admissions.**
The command-plane terminal now distinguishes applying the desired projection
from observing the later tmux effect: `projection_published` returns an applied
command receipt while the embedded layout payload keeps `applied: false` until
the reactive effect converges. Automatic editor sync no longer reports a false
rejection after the controller has already accepted and enacted the request.
- **Automatic structural sync cannot converge from a stale pane assignment.**
An automatic `Sync` intent is already the sparse edge for a first editor
observation, changed columns, or controller-observed drift, so every such
generation now crosses the tmux effect boundary. Cached structural receipts
remain available for manual focus-only generations, while editor focus changes
continue to use their separate pane-selection effect.
- **Supervisor hot-reexec no longer impersonates a session restart.** A
replacement image detects a surviving inherited harness child before start
admission, validates the existing controller document/session/pane binding, and
retains its actor generation and runtime state while refreshing only the
supervisor transport lease. It emits no `session_start`, start hook, or ownership
transition; binding drift fails closed before any replacement generation is
published.
- **Mid-response operator prompts can no longer be acknowledged by an unrelated
later response heading.** Unstarted-prompt selection now retains unsuppressed
diff encounter order and requires an adjacent response association. Substantive
assistant prose or another prompt keeps the operator request actionable, while
marker-only `❯` normalization and proven contiguous plain-answer runs remain
non-actionable historical state. Explicit steering cannot be answered by later
binary-owned backlog/done maintenance, and preflight's post-repair drift gate
preserves the same fresh-prompt exemption as its entry gate. The narrow
inline-boundary-fragmentation repair now runs before the broad live-steering
guard because it proves the complete prompt survives; an actionable prompt no
longer leaves that structural transport artifact wedged.
- **Relative session paths no longer collapse the controller project root to an
empty path.** The shared project-root walker anchors relative documents to the
process working directory before walking `.agent-doc` ancestors, so supervisor
idle-watch projections reach the existing controller instead of asking launch
admission to claim `PathBuf("")`. Empty-root rejection remains the fail-closed
backstop. Tmux pane-CWD lookup still preserves a nonexistent reported path as
stale fallback evidence rather than anchoring it to the caller's repo.

## 0.35.111

_JetBrains plugin 0.2.335; VS Code extension 0.2.64; Zed extension 0.1.0._

- **Installed controller generations can promote mid-turn without interrupting
  the harness child.** The controller recycle gate now waits only for a stable
  handoff and a between-RPC cut; durable dispatches no longer pin stale
  controller code for the full Claude/Codex/OpenCode turn. Dispatch, CRDT, and
  closeout state rehydrate from SQLite, while the route-owned supervisor keeps
  ownership of the live child and remains turn-boundary gated. `--force` skips
  the controller debounce rather than weakening the active-RPC gate.

## 0.35.110

_JetBrains plugin 0.2.335; VS Code extension 0.2.64; Zed extension 0.1.0._

- **Mixed closeout mutations now publish as one semantic tracked-work
  transaction.** Review removals/resolutions and backlog ungates are evaluated
  before dependent additions/edits against a virtual document, then the
  primary session document and any external done archive publish only after
  every mutation validates. A duplicate or otherwise invalid late mutation
  rolls back the complete tracked-work projection instead of leaving a partial
  closeout, and MCP tool errors retain the full causal error chain.
- **Replica replacement now discharges its exact visible-state receipt.**
  Generation registration still proves the controller canonical bootstrap,
  while JetBrains separately projects the post-swap buffer when it exactly
  equals that canonical text and retries a failed projection through the
  retained remote-drain path.
- **Durable convergence no longer clears the only resumable closeout state
  before editor delivery.** The Lazily settlement Effect remains subscribed
  while a known live editor owes an exact projection receipt. Receipt
  convergence then emits the durable settlement/wake edge, allowing captured
  closeouts to finish without weakening the strict delivery commit barrier.

## 0.35.109

_JetBrains plugin 0.2.334; VS Code extension 0.2.64; Zed extension 0.1.0._

- **Automatic tmux pane synchronization no longer stalls controller IPC on a
  periodic probe.** The one-second synchronous tmux observation was removed
  from the Project Controller accept loop, and the tmux effect no longer sleeps
  and retries the same failed inputs. Observations, structural assignments, and
  effect receipts are keyed to the exact desired-generation plus actor-binding
  projection; `retry_pending` parks until either input changes, at which point
  the retained Lazily effect reactivates. The shared controller path gives
  JetBrains, VS Code, and Zed the same behavior.
- **Retained delivery now has one exhaustive typed state table and one Effect
boundary.** Durable Base → Target facts, current delivery, live membership,
convergence, and controller generation reduce to a single
`RetainedTransitionState`; only activation observation, guarded target apply,
and closeout resume project effects. Controller startup now binds its listener
before restored liveness publishes missing-replica targets, so a returning
editor projection cannot race an unopened socket and strand closeout in
`AwaitingDelivery`.
- **Each retained transition pins the continuation that produced it.** Event
replay copies the owning cycle/capture projection into the Base → Target intent,
so a later cycle checkpoint, abandonment, or supervisor recycle cannot make an
older transition consult unrelated current-cycle state. Existing retained
events gain the continuation during projection replay; no sidecar migration or
new acknowledgement request is required. The final effect fence and
supervisor-wake payload use that same pinned continuation, and the one-shot
published frontier advances only after the wake is successfully published.
Cold or recycled supervisors also derive the resumed operation key and exact
replay body from the pending transition continuation; current-cycle capture is
only a legacy fallback when no pinned continuation exists.
- **Retained transitions now flow back into attached editors as guarded
reactive projections.** The controller derives a transition only when the
  current visible projection still byte-matches its recorded base, then a
  per-document Effect submits it through the CRDT relay's compare-and-project
  guard. The transition contains explicit base and target content. The editor's
  later full-content projection is cumulative convergence
  proof; there is no editor ACK request, force refresh, reload-from-disk step, or
  forced disk write. Divergent operator typing and malformed targets remain
  fail-closed.
- **Running supervisors now coordinate retained closeout recovery through the
controller's reactive state plane.** Replica ingress publishes the full
converged visible projection, a typed `Computed` distinguishes exact delivery
from a post-commit reposition whose captured response is already materialized,
and one deduplicated Effect wakes the owning supervisor. Newer operator queue
edits are preserved; malformed or ordinary divergent projections remain
blocked without force-disk, refresh, no-op edits, or stale-target replay.
- **Controller request workers now read the local reactive document projection.**
Spawned request workers inherit controller identity and the in-process
projection reader, so Compact Exchange cannot recursively call its own
  controller and time out behind the state transition it is handling.
- **Compact Exchange now has an end-to-end exact-projection regression.** A
  retained `compact --commit` continuation remains blocked with only one
  projection plane, completes once authority and disk project the exact target,
  and records one identity-fenced Effect receipt even when that projection is
  replayed.
- **Same-closeout queue ingress preserves ordered batches.** Equal-priority
  `--backlog-add` follow-ups now retain their top-down backlog order in the queue
  projection; generated ids no longer act as an accidental lexical scheduling
  tie-break.
- **Exact retained continuations inherit their live closeout.** Once authority
  projects the pinned cycle/capture/response identity as ready, the captured
  continuation atomically succeeds the still-live foreground owner instead of
  waiting for its five-minute lease and an idle retry. Ordinary capture wakes
  and stale identities remain mutually exclusive.
- **A plain Run Agent Doc trigger now coalesces behind the active closeout
  owner.** Explicit prompt payloads remain durably queued, while a payload-free
  trigger no longer competes with or fails against an authoritative response
  that is already converging.
- **Per-document authority uses one cancellable Tokio task per warm document,
not one OS thread.** Those tasks share two asynchronous scheduler threads and
a separate bounded blocking-effect pool; descriptive pool names now make the
execution boundary explicit.
- **Retained closeout settlement projects from authority state rather than
transport acknowledgement control flow.** A receipt remains supporting
evidence, while the Lazily projection of the durable target, current authority,
and replica state decides convergence. Proven duplicate terminal debris is
repaired through that same authority effect; unique trailing text still fails
closed.
- **Answered free-text queue heads now strike through a reactive authority
  projection.** A durable captured response and the current document authority
  compute one exact target, and a per-document worker applies it without
  making write or commit callers remember a best-effort cleanup request. The
  same projection heals a torn `queue: stop`/live-rows state by deriving
  activation from the remaining queue rather than trusting the stale flag.
- **Tracked components reject captured conversation debris.** Editor delivery
and commit boundaries now preserve the last valid authority if response text
is welded into a review, backlog, queue, or done component.
- **A restarted editor now inherits unsettled canonical projection authority
from the controller.** Registration forces a full canonical bootstrap while a
durable write awaits visibility, transfers an exact-hash receipt to the
replacement replica identity, and withholds stale whole-buffer publication
until that bootstrap is visible and saved.
- **Repair reconstructs a lossy editor projection from durable operations.**
  Consumed editor-operation checkpoints remain available as historical
  evidence; when an exact merge-base replay explains a truncated response
  capture, repair restores the complete operator edit before materializing the
  retained response and preserves newer frontmatter metadata.
- **Repair removes proven duplicated editor tails before component parsing.**
  An exact duplicated document suffix may carry only a short torn comment
  terminator as additional debris; repair converges that cut through document
  authority before backlog or closeout parsing, while novel trailing text
  still fails closed.
- **One-line component patches preserve their closing marker boundary.** CLI
  arguments, stdin, hook output, and append/prepend composition now acquire a
  trailing newline when non-empty, so a replacement cannot weld prose to the
  binary-owned component close marker.
- **Dogfood mode is configurable per session document.** Set
  `agent_doc_dogfood: true` in frontmatter to append a stable, actionable
  Agent Doc repair prompt to terminal failures such as wedges and projection
  timeouts; an explicit `false` disables legacy path-inferred dogfooding.

## 0.35.108

_JetBrains plugin 0.2.331; VS Code extension 0.2.64; Zed extension 0.1.0._

- **Open-document authority and state-plane delivery now invalidate only their
  exact reactive keys.** Actor transitions no longer recompute every warm
  document, and a state-plane publication no longer clones all channel
  histories or wakes every socket subscriber. Retiring a document releases its
  history, Effect, sink, and dependency slot.
- **JetBrains editor selection projects from editor events without a polling
  retry or coalescing delay.** The selected document remains authoritative
  across IDEA's interstitial layout state, queued stale projections are
  generation-fenced, and native delivery stays off the EDT.
- **Native editor boundary work now uses a bounded Tokio blocking pool.** Tmux
  probes and surface ingress can no longer create unbounded OS threads during
  document churn; superseded probe results remain inert behind the latest
  generation fence.

## 0.35.107

_JetBrains plugin 0.2.330; VS Code extension 0.2.63; Zed extension 0.1.0._

- **A stale route supervisor now honors replacement at the first safe no-IPC
  checkpoint.** In-place `execve` preserves the harness child, tmux pane, and
  durable open-cycle checkpoint, so a stale Busy/turn marker can no longer pin
  the old binary after an accepted restart. Fresh-binary child relaunches keep
  the real turn-boundary interlock.
- **Supervisor replacement reporting is event-driven and requires positive
  binary identity.** The CLI no longer sleeps and polls for eight seconds or
  calls an unchanged old PID “proven live”; it reports pending until the
  supervisor maps the installed binary inode.
- **Open-document authority warming now exposes bounded readiness diagnostics.**
  Native startup and membership changes report subscribed, pending, ready, and
  unavailable counts, followed by one fully-warm transition. The diagnostic
  reads only the in-memory reactive authority workers and adds no SQLite or
  controller work to navigation.

## 0.35.106

_JetBrains plugin 0.2.330; VS Code extension 0.2.63; Zed extension 0.1.0._

- **Editor document selection now remains authoritative across split-editor tab
  replacement.** The JetBrains observation projection distinguishes explicit
  document selection, component focus, and layout events. A transient focus
  event from the opposite split cannot replace a pending selection, while a
  later genuine focus change still projects normally.
- **Tmux focus and layout effects no longer wait for terminal controller work.**
  The shared native editor-surface Effect publishes through asynchronous
  command-plane admission, where a newer focus generation immediately fences
  stale work before `select-pane`. Focus candidate lookup consumes the
  controller's in-memory actor Source instead of opening SQLite on the hot
  path. JetBrains and VS Code share this native consequence behavior.

## 0.35.105

_JetBrains plugin 0.2.329; VS Code extension 0.2.63; Zed extension 0.1.0._

- **A retained canonical editor projection can no longer be replaced by stale
  whole-editor recovery text.** The shared CRDT relay projects a typed adoption
  decision from its live delivery frontier and rejects request-full-state
  adoption until the controller-authored target has a visible-content receipt.
  This keeps Compact Exchange canonical even when native op-capture fencing
  times out behind another controller operation.
- **JetBrains and VS Code now retain canonical projection authority across the
  editor-apply boundary.** JetBrains records the retained target before its
  native fence while keeping retry scheduling independent; both adapters refuse
  reattach text adoption until the target is visible. Rust, Kotlin, and
  TypeScript regressions cover the stale-editor race and retry-safe parity.

## 0.35.104

_JetBrains plugin 0.2.327; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Actor authority is now a storage-independent reactive controller model.**
  `ActorState`, `ActorRecord`, transitions, and accepted store-write events live
  in `agent-doc-controller`, while SQLite only persists and cold-hydrates those
  facts. Durable actor writes publish their accepted event directly into a
  process-scoped Lazily `Source`; live bindings and pane-layout authority are
  pure `Computed` projections, with no SQLite reload, controller RPC, or
  filesystem canonicalization inside the computed path.
- **Automatic tmux layout sync consumes controller-proven actor bindings
  directly.** Exact-visible sync no longer re-resolves each already-owned
  document through content/frontmatter/registry authority checks. The bundled
  tmux-router 0.3.20 lets caller-proven bindings outrank spare geometry even
  when an unsaved editor document has no disk file, and passive reconciliation
  defers redundant whole-server tmux diagnostics. Ownership fallback reads the
  editor-authoritative current document rather than assuming disk authority.
- **JetBrains native handoff no longer reopens stale code when glibc retains an
  inert Rust mapping after `dlclose`.** Once native work is quiesced, guarded
  calls drain, owner threads terminate, and JNA closes the handle, the plugin
  publishes the replacement from its distinct shadow inode. A residual closed
  mapping may remain until process exit but cannot become the active generation.

## 0.35.103

_JetBrains plugin 0.2.326; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Editor-surface ingress no longer waits behind tmux RPC.** The deferred
  adapter publishes each editor observation into its reactive Source before
  starting the controller boundary probe. Probes run asynchronously behind a
  per-root generation fence, so a slow older probe cannot delay a newer tab or
  layout observation and its late result cannot overwrite the newer tmux
  Source.
- **External done archives commit atomically with their session document.**
  Commit admission now derives the exact typed `agent:done archive=...` side
  path and includes it in the same private-index transaction as the main
  document. An already-current main document is no longer a no-op when that
  binary-owned archive is dirty, while unrelated staged or dirty paths remain
  outside the closeout commit.

## 0.35.102

_JetBrains plugin 0.2.325; VS Code extension 0.2.62; Zed extension 0.1.0._

- **A converged retained tracked-work target cannot close as an already-current
  no-op.** Commit admission now joins the current editor-authoritative content
  hash to either the cycle's exact pending-only target or the durable latest
  `pending_write` convergence projection, checkpoints that proven target, and
  commits it. The durable projection provides rolling-upgrade recovery for
  targets settled by 0.35.100 before commit intent was preserved.
- **Unproved typed-component drift fails closed at an already-current commit
  boundary.** If the snapshot matches `HEAD` but queue, backlog, status, or
  another structured component is still ahead without exact binary-owned
  target proof, `agent-doc commit` returns an error instead of reporting
  success while Git remains behind. Generic out-of-band backlog adoption is
  now strictly additive: every existing item and its ordering must remain
  unchanged, so an edited existing row cannot be mislabeled as a safe add.

## 0.35.101

_JetBrains plugin 0.2.325; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Zero-member delivery retains the requested Git continuation.** The
  editor-disappeared barrier now carries the common retained-for-retry marker,
  so a `write --commit --backlog-only` that lands after re-registration records
  its exact downstream commit target instead of stopping after editor
  convergence. Mixed-generation callers also structurally recognize the
  0.35.100 zero-member error and preserve that commit intent.

## 0.35.100

_JetBrains plugin 0.2.325; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Tmux layout provisioning no longer selects an intermediate pane.** Pane
  startup inside reconciliation is focus-neutral until tmux-router places the
  complete visible projection; standalone Run Agent Doc retains deliberate
  foreground focus. Safe-passive blocked layouts also preserve the operator's
  current pane instead of reselecting a registry owner that may live in stash.
- **Sync session and template state derive from editor authority.** The
  reconciler resolves the live CRDT/editor projection once, then derives
  scaffolding, initialization, validation, session ownership, and pane routing
  from that value. Initial baselines accept the resolved projection directly;
  no initialization read or pre-stage can substitute stale disk beneath an
  attached editor.
- **Focus recovery reads the controller's live document Source.** Current
  frontmatter in an unsaved editor buffer wins over disk and durable actor or
  registry fallbacks. An attached editor whose replica is not settled remains
  unresolved and never grants permission to read stale disk; empty registry
  identifiers no longer mask a valid actor fallback.
- **Open closeout guidance preserves the incumbent continuation.** A routed
  request blocked behind `response_captured` or `write_applied` now reports
  that retained write/commit recovery owns the checkpoint and keeps the new
  route queued. It never recommends another finalize or write for an already
  captured response.

## 0.35.99

_JetBrains plugin 0.2.325; VS Code extension 0.2.62; Zed extension 0.1.0._

- **State-plane resume cursors are scoped to the controller generation.**
Subscriptions now send and receive the generation alongside the plane version,
so a replacement controller immediately cold-replays its covering snapshot
instead of waiting forever for its reset counter to exceed the retired
controller's cursor.
- **Rolling upgrades wake old supervisors across controller replacement.**
Plane versions are seeded into a monotonic controller-generation namespace, so
an already-running pre-0.35.99 supervisor sees the replacement snapshot as a
new edge even though it cannot yet send the typed generation cursor. If a
legacy cursor is nevertheless ahead of the replacement channel's nonzero
frontier, the controller cold-replays that covering snapshot instead of
lowering the cursor on an empty timeout and silently skipping the edge.
- **Captured-finalize recovery remains state-driven.** The supervisor carries
the returned generation/version cursor between event subscriptions; generation
change resets observation only and never resubmits finalize, session-check, or
a document write.

## 0.35.98

_JetBrains plugin 0.2.324; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Retained editor delivery resumes from reactive state after controller
  replacement.** Replica register, deregister, update, pull, and ACK events now
  project the post-event delivery frontier into a controller-scoped Source. A
  shared Computed joins that ingress to the exact retained intent, captured
  response, live membership, matching target hash, and controller activation;
  its Effect publishes the existing closeout wake without requiring another
  finalize, session-check, save, or ACK edge.
- **Keyed reactive joins subscribe to membership and value.** Retained delivery
  remains reactive whether the intent or replica arrives first, including a
  cold controller whose initial observation predates Source-map insertion. An
  exact-frontier receipt makes wake projection one-shot while still rearming
  for a newer delivery frontier or controller generation.
- **Zero-member delivery remains explicitly ineligible.** A canonical CRDT
  target with no live editor replica cannot prove an editor-visible write and
  therefore remains retained for the next replica-registration ingress.

## 0.35.97

_JetBrains plugin 0.2.323; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Zero-replica writes retain the exact downstream commit continuation.**
  Every editor-delivery deferral now carries the common retained-for-retry
  marker, so a late `write --commit --backlog-only --done <id>` preserves its
  target identity and done intent when no editor replica is registered.
- **Session-check fails closed without conflating recovery and preflight.**
  Preflight may still proceed through an unobservable transport plane, while
  session closeout derives a stricter policy from the same reactive settlement
  verdict and cannot report success for an unobserved durable write.
- **Undelivered editor-authoritative bases cannot supersede retained targets.**
Recovery recognizes the journaled expected base as proof that delivery has
not happened, infers a missing pending-only target only from the exact queue
strike plus backlog-row deletion, and resumes that retained target after
editor registration without another write submission or forced disk write.
- **Run Agent Doc resolves startup history against the live actor before
submitting.** A generation-matched authoritative actor that is already settled
now invalidates the historical startup-ready probe, so JetBrains dispatch does
not wait the 120-second slow-start allowance after the actor is ready. The route
also rechecks the reactive closeout projection before pane input; if a newer
same-document cycle started while the route waited, it exits without injecting
or queueing a duplicate trigger.

## 0.35.93

_JetBrains plugin 0.2.322; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Editor-visible CRDT targets acknowledge independently of template
validation.** Exact editor and replica revisions now advance the delivery
frontier even when semantic template validation reports repairable or invalid
structure, preventing retained Compact Exchange writes from wedging behind a
validation concern that does not govern delivery.
- **Template validation is shared reactive state.** Editor and remote-candidate
revision `Source`s feed memoized validation `Computed`s with independent
lifetimes, so validation invalidates from revision changes instead of relying
on imperative call-site coordination.
- **Ordinary native operations cannot poison reactive ingress.** Document,
  projection, editor-surface, and tmux calls use isolated caller lanes. Only
  explicit native-generation lifecycle operations use the poisonable lease, so
  a call blocked behind native global state cannot disable unrelated ingress.
- **Routed closeout and recovery honor unsaved editor authority.** Controller
dispatch evaluates ownership through the authoritative actor pane, while
preserve-session reset and terminal session checks converge the exact live
editor revision through native save without requiring the operator to save,
reload, or retry.
- **Reactive architecture review now includes ingress and computed extraction.**
The development harness prefers event-channel ingress, permits RPC or bounded
polling only at non-reactive boundaries, and requires derived validators,
readiness facts, authority decisions, caches, and retry state to be extracted
into lifecycle-scoped `Computed`s.

## 0.35.92

_JetBrains plugin 0.2.321; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Editor navigation now converges from exact pane assignments.** The tmux
  effect returns its file-to-pane mapping as a generation-scoped receipt, and a
  Lazily `Computed` joins that receipt to the current desired layout. Observation
  therefore recognizes panes owned by nested project roots without repeatedly
  misclassifying the already-correct physical layout as `pane_order_mismatch`.
- **Superseded layout evidence cannot select or validate a newer request.** A
  late receipt remains fenced to its original desired generation. Diagnostics
  log expected documents, actual documents, and panes from one coherent
  observation snapshot, while unit and anonymized SimWorld schedules cover the
  partial-actor-store navigation failure.
- **Full installs clean repository build outputs after successful installation.**
  `install-full` removes `target`, the JetBrains build directory, and validated
  local `.agent-doc-build-*` scratch directories while preserving Cargo's
  dependency caches. Set `AGENT_DOC_CLEAN_BUILD_ARTIFACTS=0` to retain
  incremental outputs intentionally.
- **The development harness now audits for reactive-first design.** Agent
  instructions require external facts and receipts as lifecycle-scoped
  `Source`s, derived decisions as `Computed`s, and imperative commands inside a
  Lazily `Effect` whenever a suitable graph exists. The release harness
  self-test fails if that guidance disappears or loses its scope/effect terms.

## 0.35.91

_JetBrains plugin 0.2.321; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Commit-required `--done` no longer leaves a checked backlog row for a
  second maintenance cycle.** Completion, reap, done archival, and top-backlog
  status reconciliation now form one editor-authoritative document target. The
  resulting target refreshes the response snapshot before commit, so both
  pending-only recovery and ordinary response closeout place the reap in
  `HEAD`.
- **Pending-only recovery participates in the reactive queue lifecycle.** A
  late `write --commit --backlog-only --done <id>` consumes the matching active
  queue head through the normal proof path and records `QueueHeadCompleted`.
  Completed is terminal within the hosting epoch, so delayed text projection or
  a stale selection observation cannot reactivate an already-answered item.
- **Recovery and SimWorld regressions cover the churn class.** Tests model an
  already-committed response with a late done mutation, prove no additional
  preflight/repair cycle is needed, verify the queue completion fact, and reject
  late re-selection deterministically.

## 0.35.90

_JetBrains plugin 0.2.321; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Automatic tmux layout sync is latest-wins and non-blocking.** JetBrains
  publishes a desired layout and returns instead of waiting up to 60 seconds for
  that exact plane version to converge. The controller coalesces superseding
  observations, interrupts stale retry waits, and applies only the newest
  generation. Faster selection and structural coalescing keeps editor-to-pane
  feedback responsive without allowing an old completion to steal focus.
- **Installed controller generations reconstruct exact retained-write
  lineage.** Durable editor and disk observations now carry their write-fact
  ordinal and survive state-actor replacement as separate authority planes. A
  replacement generation immediately subscribes its settlement Effect and can
  complete an exact retained write once both observations are at or after the
  intent; matching observations from before the intent remain rejected. An
  installed fix therefore takes effect through controller recreation without
  restarting IDEA or polling `session-check`.
- **SimWorld covers both replacement races.** Neutral document schedules prove
  that a replacement actor settles exact durable authority without a retry,
  refuses pre-intent observations, and that rapid pane-layout generations
  converge on the newest request without stranding work.

## 0.35.89

_JetBrains plugin 0.2.320; VS Code extension 0.2.62; Zed extension 0.1.0._

- **A canonical CRDT model no longer substitutes for the editor buffer's
delivery acknowledgment.** Even with one connected editor, agent-doc waits
until that exact revision reaches the editor replica before asking the editor
to save. This prevents a stale IDEA `Document` from being saved over the
retained target and eliminates the resulting operator “Save All” churn.
- **Retained editor saves now advance only from state edges.** The controller
wakes captured closeout when the exact replica ACK reaches delivery convergence,
and the typed native-save receipt feeds the retained-write graph. The save path
no longer polls disk for one second, while the stable revision-derived patch ID
keeps the Effect one-shot.
- **SimWorld covers the stale-single-editor failure.** The anonymized orchard
offer schedule proves that arbitrary timer ticks issue no save, delivery ACK
issues exactly one save, a stale save receipt cannot commit, and an exact receipt
finishes without replaying the response or asking the operator to save.
- **Codex child replacement preserves the exact document conversation.** Bare
`start --resume` now recovers the newest document-bound thread ID from the
durable Codex hook/controller ledger when frontmatter projection is missing,
and managed child termination launches `codex resume <thread-id>` instead of a
fresh session or `resume --last`. The binding stays authoritative across the
restart; frontmatter is backfilled as a compatibility projection. SimWorld's
anonymized orchard schedule covers termination with a missing projection.
- **Pure Codex threads no longer inherit project-local agent-doc work.** The
ambient Stop hook now requires an exact durable binding for its current Codex
`session_id`. A queue-continuation marker belongs to the document, not to every
Codex thread in the project, so an unbound thread passes through without
capturing, replaying, or continuing another thread's agent-doc cycle.

## 0.35.88

_JetBrains plugin 0.2.320; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Retained closeout convergence is now event-driven instead of retried.**
CRDT replica changes feed the controller's live-authority Source and editor
native-save receipts feed its disk Source directly; the shared Computed verdict
and settlement Effect advance the exact retained capture without requiring a
status RPC, repeated `session-check`, or a controller-side disk read.
- **Captured finalize resumes only from an explicit graph edge.** The controller
publishes a per-document Lazily state-plane snapshot, and the supervisor
consumes each state or failed-effect edge once. Idle timer ticks cannot produce
another finalize attempt. Only transient effect failures such as disconnect,
timeout, or backpressure receive bounded backoff. Focused graph tests and an
exhaustive anonymized SimWorld schedule prove that native save rearms once while
arbitrary timer ticks leave the attempt count unchanged.
- **Release version projection now belongs to the agent-doc development
harness.** `make release-version VERSION=<version>` updates every workspace
package, internal path constraint, lockfile entry, Python package version, and
both skill copies from one input. The full check verifies the projection and
exercises drift detection on an isolated fixture.

## 0.35.87

_JetBrains plugin 0.2.320; VS Code extension 0.2.62; Zed extension 0.1.0._

- **An editor-native save now resumes its exact retained closeout.** After a
  recovery-only session check requests a save and proves the same
  `write_applied` authority on disk, it resumes that already-captured cycle
  through checkpoint and commit. The permission is a bounded Lazily `Computed`
  fed by native-save, exact-convergence, cycle-phase, and retained-write
  observations; durable retained-write settlement remains controller-owned.
  General replay and repair remain disabled, so the bounded transition cannot
  write a second response or commit an unrelated cycle. An anonymized SimWorld
  schedule reproduces the former save-then-stall gap and completes without a
  separate controller-commit action, operator Save All, or IDE exit.

## 0.35.86

_JetBrains plugin 0.2.320; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Recovery-only session checks can now flush a retained `write_applied`
  editor authority without operator intervention.** The narrow effect asks the
  editor to save its own latest buffer only when a durable retained closeout
  proves that disk persistence is the remaining boundary. It still cannot
  replace, replay, repair, or commit document content. A production-backed
  SimWorld schedule covers the restarted-editor divergence and proves that
  restored operator text survives without requiring IDE exit.

## 0.35.85

_JetBrains plugin 0.2.320; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Live-editor commits now require the exact raw editor projection on disk.**
  CRDT delivery convergence no longer authorizes Git's normalized
  `already_current` shortcut while queue-only mutations remain unsaved. The
  controller requests one native editor save, rechecks the latest authoritative
  revision, preserves operator edits made during the save, and leaves closeout
  resumably pending when exact persistence is not yet proven.
- **Retained-response and queue continuation projections no longer manufacture
  churn.** An exact `commit_already_current` proof settles ownership of a
  response already materialized in HEAD, while a separate queue-only write
  remains retained until raw editor/disk convergence. Interrupted MCP closeout
  reports queue continuation as unknown instead of falsely returning
`required=false`, and preflight performs one bounded recovery transition
rather than nesting a full session-check that can exhaust the 300-second tool
deadline. Focused regressions and SimWorld cover the restarted-editor retained
closeout schedule.

## 0.35.84

_JetBrains plugin 0.2.320; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Post-registration editor effects are Lazily fenced.** Retained reconnect
intents are projected into the controller-owned CRDT authority instead of
returning whole-document bytes for Kotlin to apply. Every queued JetBrains EDT
effect carries a local intent generation and endpoint identity; retirement,
supersession, or a model-less endpoint refuses the effect before any document,
VFS, or disk mutation. Focused plugin tests and a generated SimWorld schedule
cover an intent retiring while its EDT effect is queued.

## 0.35.83

_JetBrains plugin 0.2.319; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Committed whole-document replay self-heals before structural guards.** A
  post-registration editor effect can outlive retirement of its retained intent
  and concatenate two complete committed projections with different transient
  boundary identities. Preflight now proves both copies against committed HEAD,
  restores HEAD through live document authority before singleton validation,
  and rejects any copy containing distinct operator or response content.
- **Reconnect effects are state projections, not RPC authority.** The realtime
  contract now makes the Lazily desired/observed intent generation authoritative
  for editor replay. RPC and in-process FFI may transport or wake that projection
  but cannot independently authorize a stale editor mutation; a registered
  endpoint without a model is an observed re-registration state, not a disk
  fallback or an operator reload requirement.

## 0.35.82

_JetBrains plugin 0.2.319; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Supervisor replacement preserves the exact active conversation.** An
  accepted non-forced replacement remains owned by the live supervisor until
  its turn drains; a controller observation timeout no longer escalates into
  killing the harness. Continue-mode preserves a live child and requires the
  editor-authoritative document's exact `resume:` id for a cold start, while
  only explicit fresh-mode may start another conversation. SimWorld reproduces
  the stale-binary, mid-turn timeout and proves same-pane in-place recovery.
- **Committed live-editor projections now converge without manual-save churn.**
  Session-check may request a native save for the exact committed editor cut
  even while an old delivery acknowledgement trails. The editor buffer remains
  content authority, disk is only the verified materialization, and identical
  retries share `canonical-save-<content-hash>` instead of creating a new save
  effect on every check.

## 0.35.81

_JetBrains plugin 0.2.319; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Queue closeout now retries transient controller-read errors instead of
  discarding an already-computed answered-row strike.** The shared typed
  authority-recovery policy treats controller observation errors like missing
  or syncing editor authority while retry budget remains. A regression covers
  the live `haiven.md` timeout-at-attempt-1/3 trace and proves the next current
  observation completes without another agent turn.

## 0.35.80

_JetBrains plugin 0.2.319; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Controller orphan-drain recovery now honors effective queue pause state.**
  A retained supervisor-scope head can no longer reactivate a document after
  `admin queue pause`, so the watchdog does not periodically rerun a paused
  `agent-doc` session.
- **Background orphan recovery is existing-pane-only and focus-neutral.** It
  never rescues a stashed pane, searches for an alternate, or auto-starts a
  replacement. A closed target, cross-document pane reuse, or changed ownership
  fails closed with structured focus-preservation evidence. SimWorld covers
  paused retained heads, closed owner panes, reused pane ids, repeated watchdog
  ticks, replacement-pane count, and operator focus.
- **Orphan-drain diagnostics now use one typed event vocabulary.** Queue-control
  read failure, authority/hash/lease/backoff failures, dispatch outcomes, and
  worker outcomes derive their stable log names from `OrphanDrainEvent` instead
  of inline strings.

## 0.35.79

_JetBrains plugin 0.2.319; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Passive editor-surface sync is now window-neutral throughout the tmux
  transaction.** The bundled tmux-router dependency is updated to 0.3.19.
  Background pane selection uses `select-pane` without first activating its
  window, and passive reconciliation omits every intermediate or final
  `select-window` effect. A deterministic SimWorld client-visibility trace and
  an isolated real-tmux regression test cover the transient stash-window focus
  steal that final-layout assertions could not observe.
- **Passive reconciliation also preserves the active pane inside the visible
  window.** An adjacent document pane is no longer selected even transiently
  while the operator types. Every passive selection records before/after window
  and pane snapshots; an unexpected movement emits a structured
  `focus_steal_detected=true` warning and immediately restores the operator
  pane.
- **Missing JetBrains replicas now re-register from the attached editor even
  when its current template structure is incomplete.** Registration records the
  exact editor cut as the authoritative CRDT baseline without normalizing,
  replacing, refreshing, or saving it. Retained reconstruction remains queued
  behind registration and must independently produce an exact validated
  structure before it may project a minimal edit, so editor authority no longer
  deadlocks recovery or weakens remote-write fencing.
- **JetBrains controller receipts treat JSON nulls as missing typed fields.**
  A malformed or mixed-generation pane-layout response now reports a stable
  controller/projection error instead of masking it with Gson's unhelpful
  `JsonNull` exception.

## 0.35.78

_JetBrains plugin 0.2.318; VS Code extension 0.2.62; Zed extension 0.1.0._

- **JetBrains typing now coalesces editor bursts before crossing the CRDT
  transport.** The document listener retains one pending-local fence and at
  most one latest queued flush, then computes a single Unicode-safe splice
  against the last actor-owned shadow. Slow native or controller operations can
  no longer multiply once per typed character and backlog the IDE.
- **Retained response replay resumes after editor registration without blocking
  registration itself.** Replay is queued as a second task on the same
  per-document lane, rechecks the exact live buffer and pending-local fences,
  and only then reconstructs and applies the retained semantic edit. This lets
  editor-owned recovery converge while keeping disk subordinate to the buffer.
- **Passive stash reconciliation no longer changes the selected session inside
  the stash window.** The bundled tmux-router dependency is updated to 0.3.17,
  which restores the destination window's internal pane selection after a
  detached stash move without activating that window.

## 0.35.77

_JetBrains plugin 0.2.317; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Automatic editor-surface observation no longer runs controller probes or
  tmux consequences inside the JetBrains native-call lease.** The plugin now
  crosses a bounded validate-and-enqueue FFI boundary. A per-project worker
  performs the controller work afterward and replaces any not-yet-started
  surface with the newest observation. A blocked route can therefore neither
  disable the shared native generation nor replay an intermediate layout that
  refocuses `stash` after the operator selects another i3 window.

## 0.35.76

_JetBrains plugin 0.2.316; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Forced JetBrains replica registration no longer reconstructs retained
  responses synchronously through JNA.** Registration publishes the exact live
  editor cut, reports reconnect propagation, and schedules ordinary controller-
  owned CRDT delivery. This keeps the editor-native boundary bounded so recovery
  for one large document cannot trip the native-call watchdog and disable the
  shared generation for every open document. The retained response still passes
  the normal editor, replica, pending-local, disk, and acknowledgement fences.

## 0.35.75

_JetBrains plugin 0.2.315; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Editor-owned missing-replica recovery now restores the replica instead of
  promoting disk to authority.** The binary first asks the editor to save its
  exact buffer, then sends a typed `observe_lazily_current` request to the same
  endpoint and resolves the document again through controller/CRDT authority.
  Both protocol-stable requests may cross a same-protocol older-listener build
  fence; no disk cut is adopted as live authority.
- **A sole exact editor can close an ACK-pending save boundary.** When one live
  editor already holds the exact canonical retained target, its typed save
  receipt plus exact post-save editor and disk proof settles the historical
  delivery gap. Multiple live editors still require full delivery convergence.
  This removes the stale-supervisor deadlock that left `agent-doc commit` and
  Haiven recovery waiting even though the visible editor buffer was correct.
- **Leaving JetBrains for another i3 window cancels late focus effects at the
  real effect boundary.** Editor-origin pane selection now checks the focused
  i3 window immediately before `tmux select-pane`, and automatic layout repair
  strips its focus target while another desktop window is active. A dormant
  reverse tmux-to-editor mirror also opens tabs with `focusEditor=false`, so it
  cannot reactivate the IDE or surface `stash`.

## 0.35.74

_JetBrains plugin 0.2.314; VS Code extension 0.2.62; Zed extension 0.1.0._

- **An attached editor with a missing CRDT replica now repairs itself through
  editor authority.** Current-document resolution asks the registered editor to
  save its exact buffer without applying a projection, requires the typed
  applied receipt, and only then reads that editor-written disk cut to rebuild
  the controller model. This removes the close/reopen or IDE-reload requirement
  for already-open documents such as `haiven.md`, while continuing to refuse an
  unproved disk fallback.
- **Save-only recovery crosses a same-protocol build fence safely.** When a
  freshly installed CLI reaches an older live editor listener, it retries only
  the non-projecting save request with the listener's reported build identity.
  All document-mutating editor intents remain strict build-matched operations.

## 0.35.73

_JetBrains plugin 0.2.314; VS Code extension 0.2.62; Zed extension 0.1.0._

- **JetBrains document selection reaches tmux promptly without stale focus
  effects.** Selection and component-focus notifications are micro-coalesced
  into one current intent, and an inactive IDE window or intent older than 500
  ms is refused before submission. Component focus no longer starts a redundant
  surface reconciliation, and the low-latency lane only selects already-visible
  panes; the authoritative surface graph retains pane recovery and creation.
- **Accepted focus commands are latest-wins at the effect boundary.** The
  controller registers the project-scoped focus intent at async admission,
  checks supersession and the 750 ms deadline before expensive work, and holds
  that fence across the final `tmux select-pane`. An older worker can no longer
  select another session after a newer editor event or delayed focus transfer,
  eliminating the brief wrong-pane selection and its window-manager pointer
  movement side effect.

## 0.35.72

_JetBrains plugin 0.2.313; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Retained closeout now saves the editor-owned revision automatically.** When
live editor authority differs from disk, preflight/finalize recovery sends the
typed `save_document` intent to that editor and proves the exact revision on
disk before it evaluates or replays a retained operation. Captured-finalize
resume uses the same save barrier before superseding an older retained target.
An unavailable, rejected, timed-out, or concurrently changed save remains
retained and never falls back to a direct disk write. Operator `session-check`
remains a read-only observer.

## 0.35.71

_JetBrains plugin 0.2.313; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Large-document typing no longer manufactures stale CRDT baselines.** A
  routine Lazily current-document observation returns immediately when the
  document replica is already attached; it cannot normalize the whole Markdown
  file or advance the incremental shadow past queued `DocumentEvent`s. The
  per-document actor retains and updates the exact native-text projection,
  removing the full FFI text materialization previously paid per keystroke.
- **Exact-editor recovery waits for its controller projection.** The bounded
  text-adopt intent is retained first, then receives a synchronous
  `crdt_text_adopt` acknowledgment before atomic replica replacement. A frame
  waiting behind an older suffix can no longer authorize a stale re-register
  and another multi-megabyte `ensureEditorText` publication.
- **Op capture has no legacy authority fallback.** Runtime reads consume only
  typed `EditorOpCaptureCheckpointed` / `EditorOpCaptureCleared` state. A live
  actor timeout fails closed, and obsolete bespoke-table rows are not read,
  imported, cleared, or counted by normal GC.

## 0.35.70

_JetBrains plugin 0.2.312; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Document replicas no longer share one blocking execution lane.** JetBrains
  runs attach, normalization, local edits, delivery, and ACK work on one lazy
  FIFO worker per document; the native generation uses a bounded owned worker
  pool, and Rust locks each CRDT replica independently. A large document's
  startup attach therefore cannot block another document's registration or
  reliable-sync liveness for every other open document.
- **Native call watchdogs distinguish queue delay from a wedged invocation.** A
  call that times out before starting is cancelled while the generation stays
  live and usable. Only a call which actually ran beyond its lease disables the
  native generation. Regression tests block one document/native lane and prove
  another document still completes while same-document work remains FIFO.

## 0.35.69

_JetBrains plugin 0.2.311; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Filesystem sidecars are one-way crash state, never fallback authority.**
  Document baseline checkpoints commit typed `state.db` facts first and then
  invoke `CrashStateEffects` to write the hashed markdown crash-state sidecar.
  Baseline resolution, closeout, reset, and rename never read or scan those
  files; rename transactionally rekeys typed history and durable registry
  identity while leaving crash-time sidecars untouched.
- **Mixed-root editor layouts surface the owning document actor.** Parent
  workspace sync asks a nested project controller to resume a missing actor and
  carries its controller-proven pane binding only as ephemeral tmux-router
  input. The router prefers that binding over visible spare geometry, so
  a nested document brings its real supervisor pane out of stash instead of
  aliasing an unrelated pane while reporting success.

## 0.35.68

_JetBrains plugin 0.2.311; VS Code extension 0.2.62; Zed extension 0.1.0._

- **Cross-editor SimWorld now distinguishes protocol simulation from native-plugin execution.** The fast deterministic layer treats JetBrains, VS Code, and Zed as equal CRDT peers and proves concurrent-edit convergence plus offline reconnect. `make cross-editor-simworld` separately launches the shipped JetBrains and VS Code forwarders, Project Controller transports, and native FFI nodes against a real `agent-doc` controller; capability staging keeps Zed out of this native peer group until its endpoint and receipts exist.
- **VS Code native CRDT state buffers are copied before their Rust allocation is freed.** Koffi no longer returns a borrowed view after `agent_doc_free_state`, preventing corrupted `replica_update` payloads. The controller transport also surfaces rejected updates instead of silently continuing with a divergent local replica.
- **Cumulative CRDT ACKs are idempotent for every delivery in a pulled batch.** A causally-ahead peer may ACK the current canonical hash, and later per-item receipts for generations already drained by that cumulative ACK succeed without reintroducing delivery backpressure.

## 0.35.67

_JetBrains plugin 0.2.310; VS Code extension 0.2.61; Zed extension 0.1.0._

- **Nested project editor replicas now attach to the same controller as the document.** JetBrains and VS Code resolve each session file through the native nearest-project resolver instead of assuming the outer IDE workspace root, retain that root through close, and atomically bootstrap detached authority only for a live registering editor PID. JetBrains Compact Exchange also proves the open document's replica attachment before launching the CLI. This closes the MonsterRodholders file-cache-conflict path that supervisor recycle alone could not repair.
- **A valid retained Compact Exchange target survives transient malformed editor cuts.** After a valid canonical target is retained, the realtime convergence loop no longer rebases a structurally invalid post-apply editor snapshot over it; it keeps the original intent and asks replica reconciliation to finish delivery. Component parse failures are typed, so duplicate-scaffold recovery no longer depends on diagnostic wording; unmatched-close diagnostics name the component without embedding a literal HTML marker that IDE notifications hide.
- **Editor plugins can finish a proven retained target without an ACK retry storm.** JetBrains and VS Code accept a structurally exact remote target over an invalid or repair-required editor baseline only when the live buffer exactly matches the delivery's expected baseline. If a prior attempt already applied the delivery to the native replica, the plugins compare its text to the delivery's target hash and project that exact result without decoding the CRDT delta twice; if the target is already visible, they emit the missing ACK directly. Novel editor text still fails closed. This lets the retained EquityFundingSource response repair or resume its known baseline, ACK once, and persist normally instead of replacing the native replica, broadcasting a megabyte-scale bootstrap, and pulling the same delta again.
- **Supervisor-owned captured finalize replay no longer waits behind the stale target it supersedes.** Recovery uses a typed `captured_finalize_resume` closeout-owner role instead of impersonating a foreground finalize. When the exact response is already captured, its semantic replay may supersede an unrelated older retained delivery rather than holding the lease inside the fresh-capture guard until an operator is asked to force disk. Fresh response admission remains fail-closed behind historical retained writes.
- **Already-current commit closeout persists its stronger terminal observation.** Re-entering a committed cycle with a verified no-op commit now checkpoints `commit_already_current` even when the prior event was `commit_success`, so the durable projection cannot recreate an obsolete missing-response guard and interrupt every later session-check.
- **Captured-response diagnostics no longer prescribe an impossible commit.** When authority and disk agree on bytes that still lack the captured response, `session-check` now classifies that state separately from a converged non-capture projection. It keeps the exact replay and its editor ACK pending instead of claiming the working tree already contains the response and directing `commit` into the staged-snapshot rejection.

## 0.35.66

_JetBrains plugin 0.2.309; VS Code extension 0.2.60; Zed extension 0.1.0._

- **Default CLI focus and editor focus now share the same controller-owned resume transition.** `agent-doc focus <file>` no longer bypasses the Project Controller for local registry lookup, and a known closed actor or missing actor projection with a durable session may cross the typed `resume_latest` policy before pane selection. Focus restart uses typed `ProvisionOnly` startup policy: it creates, starts, and selects the replacement inside the short focus RPC, while readiness and layout convergence remain asynchronous. Provision-only startup anchors exclusively in the visible `agent-doc` window—an old registration in `stash` is never used as a split anchor—preventing an invisible replacement/third-pane result. Explicit `--pane` and `--blocking` remain operator escape hatches. Internal focus rejection causes are a closed-set `FocusPaneRejectReason` instead of free-text control flags.
- **Editor integrations now bound refresh and lifecycle work to the affected document.** The shared `refresh_vcs` intent carries an absolute file path; JetBrains dirties only that file and VS Code refreshes only its containing repository instead of invoking a workspace-wide Git refresh. JetBrains also replaces recursively-installed Swing container listeners with one filtered AWT listener, starts native endpoints only for the project root and roots containing open Markdown files, performs targeted VFS lookup, and stops inbound native listeners before CRDT teardown. Native callbacks never wait indefinitely for an IntelliJ read permit, and CRDT re-registration captures editor objects on the EDT before continuing on a pooled thread, eliminating the reload/read-lock convoy that periodically froze IDEA.

## 0.35.65

_JetBrains plugin 0.2.308; VS Code extension 0.2.59; Zed extension 0.1.0._

- **JetBrains focus and layout sync no longer compete or block the IDEA event thread.** Editor selection, mouse focus, and structural split changes feed one latest-wins surface graph; the old second CLI layout planner is removed. UI snapshots remain on the event thread, while controller/native work and startup root discovery run on pooled executors, and the native bridge rejects future event-thread waits.
- **Killed document sessions resume through a typed focus policy.** JetBrains and Rust share the closed-set `MissingFocusPanePolicy`; first-party focus uses `resume_latest`, so switching back to a document whose tmux actor pane died restarts the latest session before focus instead of leaving the editor and tmux panes out of sync.
- **Replica PID registrations are generation-safe Lazily sources.** Deregistering an old JetBrains `refresh-N` identity no longer clears the same document/PID source while its replacement identity is live, eliminating re-registration/ACK churn and stale editor authority after reconnect.
- **Operator `session-check` is status-only.** It no longer resumes captured finalize, settles retained writes, requests supervisor recovery, or mutates document projections. Finalize/write/preflight and the supervisor own recovery; only metadata repair of an exact already-committed historical snapshot remains permitted.
- **A committed response can release an obsolete retained reposition target while preserving newer queue prompts.** Terminal proof now requires exact cycle/capture/response identity, response materialization in `HEAD`, and snapshot = `HEAD`; visible queue-only drift no longer falsely retains response ownership. Terminal hash agreement is a closed-set enum rather than a free-text tag.

## 0.35.64

_JetBrains plugin 0.2.307; VS Code extension 0.2.59; Zed extension 0.1.0._

- **Retained editor deliveries now have one typed, terminal closeout gate across preflight, finalize, and session-check.** The runtime rejects a genuinely new finalize response before capture/admission when a historical effect remains, allows only a retry whose captured response is present in the retained target, retries controller settlement without force-disk replacement, and prevents session-check from reporting success while the same gate would reject the next cycle.
- **Visible-delivery receipts distinguish editor delivery from a target merely retained for retry.** Post-commit boundary repositioning no longer reports a refined retained intent as delivered, compact waits behind unacknowledged delivery, and a reliable editor owner with zero live replicas retains the merged canonical target without crossing a zero-recipient CRDT write.
- **Superseded captured-only recovery reaches a fixed point in one bounded metadata pass.** The recovery classifier drains the finite unambiguous stale prefix, rejects repeated identities or an excessive stack, resolves editor-visible content once, and refreshes recovery projections once after the batch instead of churning snapshot/CRDT state for every rejected historical finalize.
- **Closed-set workflow provenance is typed at development call sites.** Retained-write cycle boundaries and captured-finalize native-save sources are enums; architecture tests forbid the former free-text siblings and require the historical-effect guard to precede the response write/capture boundary.

## 0.35.63

_JetBrains plugin 0.2.307; VS Code extension 0.2.59; Zed extension 0.1.0._

- **Supervisor recycle no longer lets a fresh process's empty embedded relay hide the long-lived controller replica.** Startup and current-document reads accept the embedded route directly only when it reports `Current`; with a live editor, `editor_attached_model_missing`, `editor_sync_pending`, and `detached` observations consult controller authority before the read path may use disk metadata. Start-admission fallback also checks the controller before its local rebuilding state, while preserving that local rebuilding evidence when the controller still reports a stale detach. This removes the transient Haiven reopen/disk-divergence loop without forcing a disk write or asking the operator to reload a healthy editor.

## 0.35.62

_JetBrains plugin 0.2.307; VS Code extension 0.2.59; Zed extension 0.1.0._

- **Strong document convergence no longer discards the visible-delivery receipt before queue or backlog maintenance returns.** The runtime convergence effects now cross the shared ACK barrier after both attached writes and already-current targets, so `queue consume`, queue sync/prune, tracked-work edits, and other structural follow-up cannot report success and begin a second mutation while the first target exists only as a retained delivery intent. Raw capture remains retainable for asynchronous recovery; only APIs promising completed convergence acquire the stronger contract.

## 0.35.61

_JetBrains plugin 0.2.307; VS Code extension 0.2.59; Zed extension 0.1.0._

- **Visible writes now treat an unacknowledged editor delivery as ACK backpressure, never as sixty seconds of “typing.”** The shared bounded recovery barrier replays delivery, requests a force-refresh, invokes rolling-upgrade recovery for typed build mismatches, reconciles dead/live-unACKed replicas, and returns a typed retained/no-disk-write outcome after eight seconds. Embedded relay waits now retain a weak reference to their policy-owning controller runtime, closing the path that previously detected build skew but could not reload either side.
- **Compact Exchange cannot start secondary durability work behind a retained editor delivery.** Matching canonical text is no longer mistaken for convergence when `delivery_converged=false`; post-write snapshot, CRDT recovery-sidecar, and commit work all require the shared ACK barrier. This prevents a retained compact target from being followed by a second raw sixty-second write guard during commit cleanup.
- **Captured closeout now repairs JetBrains authority/disk divergence through the editor’s native save path even after the retained-write record is gone.** Native-save build mismatches route to the controller, which decides whether to recycle itself or reload the editor listener; standalone `session-check` reaches that same policy over controller RPC and retries until a replacement listener is observable. The captured-finalize provenance tags are a closed Rust enum, and no forced disk projection is introduced.

## 0.35.60

_JetBrains plugin 0.2.306; VS Code extension 0.2.59; Zed extension 0.1.0._

- **Retained CRDT delivery now recovers across missing liveness registrations and rolling binary upgrades.** ACK-owner routes are recovered from live CRDT replica identities as well as the reliable-sync liveness plane. Typed IPC build mismatches now choose the upgrade direction: a stale controller exits at the first exact idle cut without waiting through the normal recycle debounce, while a current controller requests reload of the stale editor listener. Delivery recovery keeps exact durable targets asynchronous when a live route exists, and controller document graphs are seeded and refreshed from durable projections so settlement cannot remain one intent behind.
- **Compact Exchange now upgrades legacy dynamic-context ledgers that predate `context_manifest.harness`.** The canonical additive schema convergence adds the non-null column with an `unknown` default for existing rows, keeping old manifests readable while new records preserve their actual harness. A legacy-schema regression exercises the same latest-session manifest query that previously failed `compact_document`.

## 0.35.59

_JetBrains plugin 0.2.306; VS Code extension 0.2.59; Zed extension 0.1.0._

- **Restarted harnesses now honor the documented 30-second startup deadline (`#nsfz` / `#startupdeadline`).** Live dogfood against a deliberately non-ready Codex child exposed that the fail-closed path and durable `startup_miss` marker were correct but still used the older 60-second constant. The production budget is now pinned to 30 seconds by a regression assertion, and the supervisor diagnostic example matches the executable contract.

## 0.35.58

_JetBrains plugin 0.2.306; VS Code extension 0.2.59; Zed extension 0.1.0._

- **Editor IPC now negotiates protocol and build identity before admitting document intents (`#ipcverhandshake`).** Each PID-scoped connection performs an `ipc_hello` / `ipc_hello_ack` exchange carrying the protocol version and a build-scoped ID shared by the CLI and native library. Missing, protocol-skewed, or build-skewed peers receive a terminal rejection before the plugin callback can mutate document state. The non-mutating `reload_library` control intent remains available on a narrow pre-handshake compatibility path so a newly installed build can replace an old listener.

## 0.35.57

_JetBrains plugin 0.2.306; VS Code extension 0.2.59; Zed extension 0.1.0._

- **Closeout now has one whole-document authority (`#ipcauthority`).** The typed realtime policy can represent only current reliable-sync/CRDT operator text as whole-buffer authority; file reads, snapshots, and `content_ours` remain audit provenance. IPC repair rejects audit candidates before full-buffer editor delivery, disk fallback, baseline checkpointing, or success. Component-scoped replay may still preserve a response over live operator edits, but it must return a fresh visible-write receipt that upgrades the candidate before closeout continues. An exhaustive transition matrix plus runtime adapter regressions keep the boundary fail-closed.

## 0.35.56

_JetBrains plugin 0.2.306; VS Code extension 0.2.59; Zed extension 0.1.0._

- **VS Code merge ownership now uses lazily-js's typed state chart (`#adstatechartjs`).** The extension dependency advances from `@lazily-hub/lazily-js` 0.27.0 to the latest published 0.29.1; the historical `1.1.0`/`@lazily/js` backlog names were superseded by the package's actual scoped `0.x` release line. `CrdtReplicaForwarder` now derives attachment from a `ChartBuilder`-defined `StateChart`, routing registration, buffer observation, and retirement through the same six-phase/seven-event vocabulary as the binary rather than mutating a standalone Boolean. The Rust/controller projection remains authoritative for disk-write permission. Full transition-matrix and forwarder lifecycle tests keep rejected edges fail-closed. Local lazily-js 0.30.1 remains independently blocked on its existing npm Trusted Publisher/2FA operator action; the extension does not depend on that unpublished build.

## 0.35.55

_JetBrains plugin 0.2.306; VS Code extension 0.2.58; Zed extension 0.1.0._

- **JetBrains merge ownership now uses lazily-kt's typed thread-safe state chart (`#adstatechartkt`).** The plugin dependency advances from lazily-kt 0.36.1 to 0.39.0, and every CRDT replica forwarder joins a project-lifetime `ThreadSafeContext`. Registration, buffer observation, and retirement now traverse a `ChartBuilder`-defined `ThreadSafeStateChart` matching the binary's merge-ownership vocabulary instead of mutating a standalone volatile Boolean. The Rust/controller projection remains authoritative for disk-write permission; focused tests pin the happy path, fail-closed edges, cross-thread reads, and forwarder lifecycle integration.

## 0.35.54

_JetBrains plugin 0.2.305; VS Code extension 0.2.58; Zed extension 0.1.0._

- **The typed local-process state chart is now load-bearing at the commit boundary (`#adstatechart3`).** The CRDT relay remains the authoritative editor-sync fact producer, while the default `adstatechart` feature routes commit acceptance through the typed Lazily `ThreadSafeStateChart` `send("commit", guards)` edge. Status observation now uses the thread-safe chart projection, feature-off builds preserve the former relay check as a fail-closed fallback, and a deterministic 64-cycle wedged-finalize simulation proves stale disk never commits.

## 0.35.53

_JetBrains plugin 0.2.305; VS Code extension 0.2.58; Zed extension 0.1.0._

- **Current typed closeout-recovery evidence is no longer vetoed by a stale compatibility snapshot (`#sidecarstate`).** The loader still requires its visible-document hash to match current document authority, but snapshot-baseline drift remains advisory evidence rather than overriding the Lazily state projection.

## 0.35.52

_JetBrains plugin 0.2.305; VS Code extension 0.2.58; Zed extension 0.1.0._

- **Editor-operation capture now lives in the typed Lazily state backbone (`#opcapturelazily`).** Complete, monotonic op epochs are projected beside document and cycle state; matching-base bursts append in controller-serialized order, base changes start a fresh epoch, consumes emit a typed clear, and cycle abandonment clears the stranded epoch in the same reducer transition. The former `editor_op_captures` table is one-way migration input only. Controller-local merges read the live projection directly instead of replaying the ledger or self-RPCing, while actorless startup retains a single transactional fallback.

## 0.35.51

_JetBrains plugin 0.2.305; VS Code extension 0.2.58; Zed extension 0.1.0._

- **Go-mode idle-queue delivery now fails closed on ambiguous composer state (`#30p6`).** The watcher classifies payload recognition and dispatch readiness from one pane capture: an unavailable capture or operator-owned draft defers without writing, a proven queue draft receives at most one bare Enter, and a proven empty composer clears only the matching stale prompt-dispatch projection before a fresh text+Enter retry. Redacted, change-deduped snapshots retain each live capture with its computed decision, so a future stall has race-free forensic evidence instead of a pane-only guess.

## 0.35.50

_JetBrains plugin 0.2.305; VS Code extension 0.2.58; Zed extension 0.1.0._

- **Every Run Agent Doc preflight now recovers any proven-stranded retained write (`#0dsr`).** The shared state-backbone policy distinguishes a delivery still converging from an intent absent from an authority/disk-agreed cut. Only the latter replays through the content-bearing journal, semantically rebasing the retained agent delta over newer operator text and clearing it after canonical and disk equality. A newly launched controller whose reactive pending-intent projection lags the durable journal can no longer suppress this recovery: its verdict is reconciled with the same policy evaluated over the caller's current durable observations.

## 0.35.49

_JetBrains plugin 0.2.305; VS Code extension 0.2.58; Zed extension 0.1.0._

- **Dead Zed language-server replicas no longer hold the delivery-ACK barrier forever.** The relay now extracts the sidecar PID from `zed-<pid>-…` identities as well as JetBrains and VS Code identities, so its existing stalled-delivery reconciliation removes a Zed replica after the LSP process exits even while the Zed application remains open. End-to-end reconciliation coverage uses the Zed identity shape that previously escaped liveness cleanup.

## 0.35.48

_JetBrains plugin 0.2.305; VS Code extension 0.2.58; Zed extension 0.1.0._

- **Codex authentication screens no longer impersonate a dispatch-ready composer (`#nsfz`).** The first-run sign-in selector reuses Codex's normal `>` glyph, so the supervisor previously injected the agent-doc reopen into the login UI instead of reaching its bounded startup-miss path. Harness readiness now classifies sign-in selection plus browser/device-code waits as explicit blockers until a genuine composer appears after the latest authentication marker. A real isolated unauthenticated Codex restart remained `prompt_ready=false`, emitted the 60-second fail-closed diagnostic, and recorded `startup_miss` with `reason=no_prompt`.

## 0.35.47

_JetBrains plugin 0.2.305; VS Code extension 0.2.58; Zed extension 0.1.0._

- **VS Code's packaged ESM extension now reaches the native CRDT replica seam (`#crdtauth6`).** `native.ts` resolves the external Koffi dependency with `createRequire(import.meta.url)` instead of an undefined CommonJS global, and owned Rust C strings are bound as opaque pointers so the wrapper decodes and frees each allocation exactly once rather than passing Koffi's eager JS string conversion back into native code. A live controller registration from the compiled VS Code classes bootstrapped the exact 94,865-character canonical document and deregistered cleanly; the existing JetBrains listener/socket path independently demonstrated local apply, Rust fan-out, remote apply, and hash-matched ACK.

## 0.35.46

_JetBrains plugin 0.2.305; VS Code extension 0.2.57; Zed extension 0.1.0._

- **Zed now has a frontmatter-gated supplemental Markdown language server.** The extension launches `agent-doc zed-lsp` for Zed's existing Markdown language, but only valid non-empty `agent_doc_session` frontmatter attaches a realtime CRDT replica. Ordinary Markdown stays a strict no-op. Local changes publish controller deltas; remote changes cross `workspace/applyEdit` and are ACKed only after their visible hash returns through `didChange`. Explicit sidecar PID registration and PID-scoped detach preserve crash-safe authority and concurrent JetBrains/VS Code replicas.

## 0.35.45

_JetBrains plugin 0.2.305; VS Code extension 0.2.57._

- **Codex's exact `› |` empty-composer rendering is dispatch-ready.** The trailing bar is Codex's cursor placeholder, not unsent operator input. Dispatch-only reroutes no longer strand an idle rescued pane behind the impossible `submit_or_clear_pane_draft` unblocker; real populated composer suffixes remain protected.

## 0.35.44

_JetBrains plugin 0.2.305; VS Code extension 0.2.57._

- **Controller and idle-watch recovery now close transparent-restart replica gaps.** The controller sends its targeted missing-replica rebuild to peer-pull-capable editors instead of assuming their transport-recovery hook observed the restart. JetBrains peer pull remains a complementary startup/reconnect repair, and an explicit attached-missing-replica or sync-pending idle observation requests one bounded current publication before the existing 30-second backoff. High-frequency controller liveness reconciliation also stops appending a success/error event for every probe, removing a major `ops.log` and IDE background-work multiplier.
- **JetBrains editor selections focus tmux on an immediate background lane.** Selection and editor-focus events submit a project-scoped latest-wins focus command before the 100 ms surface debounce and full layout reconciliation. Native project-root discovery and the controller round trip stay off the IntelliJ event thread, while the debounced exact-visible sync remains authoritative for pane creation and replacement.
- **Closeout followers wake at the incumbent lease boundary instead of their 30-second ceiling.** The controller wait now schedules the otherwise eventless lease-expiry edge, observes the fired clock into keyed controller-owned Lazily state, and lets the shared Computed closeout gate derive whether the owner still blocks. A retained `session-check` recovery therefore cannot make a concurrent finalize sleep past the advertised stopgap and then retry into another stale polling cycle. Explicit owner release and terminal-cycle facts still wake immediately. Owner coordination gets a 15-second response budget instead of the generic 5-second read budget, and the controller stamps the lease at execution time, preventing a just-landed claim from becoming an unguarded stale owner when brief state-store pressure delays its reply.
- **`agent-doc-skill-harness` owns the optional skill-harness adapter (`#skillharnessplugin`).** The focused integration crate activates only for an `AGENT_DOC_SESSION`, registers the project-local `.agent-doc/plugins/agent-doc/SKILL.md` at a stable priority, and keeps both Agent Doc and generic `skill-harness` free of reverse dependencies. Architecture and adapter tests pin the one-way package boundary.

## 0.35.43

_JetBrains plugin 0.2.304; VS Code extension 0.2.57._

- **The `agent-doc-orchestration` package is deleted after its focused-crate cutover (`#orchdecomp`).** Production command, turn, document-realtime, supervisor/controller, editor/tmux, route/session, closeout/commit, and repair behavior already lived in focused crates; the remaining package was only a source shell plus a broad dependency manifest and timestamp `build.rs`. Removing that nested Cargo project prevents Cargo/IDE discovery from indexing the obsolete graph. The CLI architecture guards now inspect the real root adapter and focused owners directly, while a dedicated decomposition test rejects any restored package, dependency, or Rust facade.

## 0.35.42

_JetBrains plugin 0.2.303; VS Code extension 0.2.57._

- **The former `agent-doc-core` surface is now guarded as a permanent focused-crate decomposition (`#coredecomp`).** Lossless-tree projection and node-keyed patch C ABI ownership moved from the root cdylib module into `agent-doc-ffi`; the root only force-links those exports. Pure native sync-lock acquisition policy moved from the FFI host into `agent-doc-sync`, leaving atomics, clocks, editor callbacks, and other effects at the ABI boundary. A workspace architecture test pins the deleted core paths, the element/backlog/queue/template/merge/FFI owners, effect-free dependency direction, and the absence of duplicate root policy.

## 0.35.41

_JetBrains plugin 0.2.302; VS Code extension 0.2.57._

- **Live document semantics now have a default-off incremental CellDocTree cutover (`#cdtcutover`).** Opted-in relay hubs share one thread-safe Lazily graph for canonical text, per-node document cells, and memoized unresolved-prompt queries. Every canonical delta—including editor relay, commit barrier, controller replace, recovery, and authoritative compaction—updates the existing nodes in place; finalize no longer constructs and discards a whole tree. Controller current-text responses expose the optional semantic projection, and idle-watch, orphan-drain, and unchanged preflight queue paths use a zero-work proof before falling back to the exact queue parser. The generic thread-safe tree, reconciliation, and semantic primitives ship in Lazily 0.53.0.
- **JetBrains typing bursts now persist editor operations in one bounded SQLite transaction (`#qbasehashmemo`).** The quiet-period worker resolves the merge base once, sends the ordered burst through one native JSON batch, and uses a 250 ms ledger busy timeout so optional op-capture evidence cannot starve controller reads. The safe diff-guess remains the fallback if the ledger is busy. This removes the per-keystroke open/transaction/growing-JSON rewrite amplification observed during live IDEA typing.

## 0.35.40

_JetBrains plugin 0.2.300; VS Code extension 0.2.57._

- **Managed Codex capability proof is now explicit and faster.** The proof gate is off by default and activates only when document frontmatter sets `managed_proof: true`; project/global configuration cannot opt a document in, and managed OpenCode behavior is unchanged. Opted-in Codex probe children now run ephemeral, ignore project rules, force low reasoning effort, and combine simultaneous network and writable-root checks into one child while preserving the existing exact marker contracts. Frontmatter, gate-transition, launch-argument, and single-child regressions cover the policy and latency paths.

## 0.35.39

_JetBrains plugin 0.2.300; VS Code extension 0.2.57._

- **Dynamic-context decisions now have a durable, queryable SQLite ledger (`#adctxledger`).** Each cycle records its prompt manifest, harness, pack/chunk identities, source hashes/ranges, and expand/reference/suppression mode in the canonical state database. Manifest rows, injection rows, and cycle state commit atomically; exact retries are idempotent, conflicting reuse fails closed, prior cycles remain append-only, and explicit document/session/cycle clearing affects context memory without deleting lifecycle state. Indexed document/session duplicate lookups and per-cycle diagnostic queries let later prompt assembly avoid replaying a full session while preserving proof of why each chunk was used or suppressed.

## 0.35.38

_JetBrains plugin 0.2.300; VS Code extension 0.2.57._

- **Compact, commit, closeout, and session-check now share a CRDT-revision-scoped current-document projection (`#compactreactiveprojection`).** The pass cache derives the authoritative document, pipeline frontmatter, and queue components once per Yrs state vector (or detached disk hash), while operator additions and deletions invalidate the graph before the next reader. This removes repeated controller authority resolution without making a cached markdown copy durable; focused latency and concurrent-edit tests prove unchanged readers reuse the projection and changed revisions never hide edits.
- **JetBrains native calls now fail closed after a bounded wait, and stale typing recovery is coalesced.** A live IDEA thread dump showed the serialized `agent-doc-native-generation` lane wedged inside reliable-sync push for more than 27 minutes while current-document and CRDT delivery workers waited behind it. Calls now time out after ten seconds, poison the stuck generation without unsafe `dlclose`, and make later work fail fast until restart. When a local shadow discovers a stale native baseline, an entire typing burst now replaces one quiet-period recovery task and adopts the latest exact editor cut once, instead of re-registering a 10MB retained state for every intermediate keystroke.

## 0.35.37

_JetBrains plugin 0.2.299; VS Code extension 0.2.57._

- **Manual Sync Tmux Layout now completes a missing-pane create-and-route boundary before reporting success (`#tmuxsyncnewpane`).** Full `agent-doc sync` and manual Project Controller sync resolve the intended tmux session, create and register the pane, wait for its harness to become dispatch-ready, and submit the document reopen in one fallible operation. The JetBrains action waits for the terminal command receipt and surfaces pane/start/route diagnostics directly; automatic and passive sync keep their admission-oriented behavior. Controller, sync/route, and plugin regressions cover manual-versus-automatic routing and terminal feedback.

## 0.35.36

_JetBrains plugin 0.2.298; VS Code extension 0.2.57._

- **A terminally committed response can no longer retain stale write ownership and block the next queue cycle.** Retained-effect arbitration now treats an exact terminal closeout proof—matching cycle, capture, response hash, and file/snapshot/HEAD agreement—as stronger than a superseded byte target. Ordinary authority drift and mismatched proofs remain fail-closed, while a proven committed capture releases the old effect even after queue maintenance or editor reconciliation advances the live document.

## 0.35.35

_JetBrains plugin 0.2.298; VS Code extension 0.2.57._

- **Session-check now publishes proof-bearing replay repairs through an exact-current authority CAS.** The duplicated-suffix normalizer already proves both the observed invalid generation and its structurally valid retained prefix; its self-heal no longer sends that prefix through the ordinary component merge against the invalid editor generation, which could recreate the unmatched component close and retain the cycle indefinitely.

## 0.35.34

_JetBrains plugin 0.2.298; VS Code extension 0.2.57._

- **Replica-refresh suffix recovery now recognizes exact replay cuts at every UTF-8 character boundary.** A complete document that ends without a newline can be followed by an exact retained suffix beginning mid-line or mid-token; recovery now collapses that proof-bearing shape while retaining the existing structural-validity, component-scaffolding, and exact-suffix gates. This closes the remaining production variant that could leave an unmatched component close and prevent retained closeout replay.

## 0.35.33

_JetBrains plugin 0.2.298; VS Code extension 0.2.57._

- **Forced JetBrains reconnects now finish retained closeouts after registering the exact live editor cut (`#lazilyconcurrentcloseout`).** The plugin keeps editor-first registration, then requests a validated semantic replay from a new post-registration ABI, fences the editor/replica/pending-local/disk state, applies the minimal editor mutation, saves it, and explicitly publishes the resulting CRDT delta. This prevents the former re-register loop where controller authority held the completed response while IntelliJ repeatedly restored and saved its shorter pre-closeout buffer. The legacy reconnect ABI remains fail-closed for rolling upgrades, and regression coverage proves the replay preserves an unsaved queue deletion while materializing the retained response.

## 0.35.32

_JetBrains plugin 0.2.297; VS Code extension 0.2.57._

- **Replica refresh can no longer append an exact partial component tail after a complete document.** Recovery now recognizes only the proof-bearing shape where a structurally sound document prefix is followed by an exact suffix of itself containing agent-doc component scaffolding. It collapses that suffix before the generic integrity gate while preserving repeated operator prose and every non-exact tail. This closes the live `review` → `icebox` → `done` duplication that left one unmatched `agent:review` closer and blocked preflight/finalize.
- **Stale Codex resume cleanup repairs safe structural replay duplication in the same authority CAS.** When Codex reports `No saved session found with ID <id>`, the matching frontmatter pointer is removed from the safely normalized current editor cut in one expected-current write. A malformed replay suffix can no longer make the stale pointer survive and repeat the same failed resume on every supervisor restart.
- **Backlog adds and ungates now populate the explicit go-mode queue atomically (`#backlogqueuepopulation`).** Closeout records a separate mutation-scoped `pending_actionable_ids` set, then insert-only mirrors exactly those newly actionable ids after consuming the current head. The inserted block is topologically ordered by hard `after=#id` dependencies before priority, while every existing queue byte remains untouched. This fixes triage closeouts that committed ungated backlog phases but left the Queue containing only the answered triage prompt, without regressing operator-authoritative queue deletions through an all-open-backlog sweep. Coverage spans cycle-state normalization, write-runtime add/ungate collection, and the live closeout queue shape/order.
- **Editor reruns, prompt replacements, and realtime steering now share one plain `agent-doc <file>` dispatch path (`#adreplacementturndispatch`).** Saved document edits remain the sole prompt payload: selection state no longer forks JetBrains into a separate supervisor steering RPC, plain triggers bypass prompt-aware queue synthesis, and actual busy agent turns accept the same bare trigger while protected interactive substates still fail closed.
- **Capacity-paused closeout preserves operator queue deletions (`#pauseddeletetombstone`).** When the disk document still equals a paused cycle's base, the write runtime now asks document realtime I/O to replay exact durable editor operations before taking the detached-base shortcut. The original disk bytes remain the atomic-write expectation, while the replayed operator cut drives response merge, snapshot construction, and queue reconciliation. Captured operations are consumed only after a successful write. Unit and finalize-integration coverage reproduces the Lazily no-op convergence race and proves deleted struck queue rows are not resurrected.
- **Preflight is derived from controller-owned reads and signal-gated effects (`#preflightreactive`).** Normal short-lived `agent-doc preflight` CLI processes publish typed diff/baseline, queue, tier, agent-model, claim, linked-document, and accretion facts to one controller-owned per-document `Computed`; probes use the same derivation actorlessly. Output is projected from those reads, while exhaustive effect gates enforce `repair -> prior-cycle commit -> derived reads -> baseline checkpoint -> cycle-open`. Cycle-open remains a durable exactly-once identity. The controller RPC is additive and no on-disk or stored-state migration is required.
- **Compact Exchange now honors queue deletions captured while an editor is paused (`#compactcachedeletetombstone`).** The controller replays an exact base-keyed durable editor-op epoch before deriving the compact target while retaining the observed bytes as the convergence compare-and-swap base. Operator deletes and the exchange rewrite therefore land in one authoritative mutation instead of resurrecting struck rows and provoking a JetBrains File Cache Conflict. The epoch clears only after write/snapshot success and survives failed compaction for retry.
- **Malformed retained closeouts can no longer churn forever (`#retainedlegacypatchcapture`).** Template, stream, and IPC closeout routes now enforce the same strict patch-marker and response-heading contract before saving a durable capture, including recovery calls that intentionally replace an existing pending response. For captures already written with the retired `agent:patch:*` wrapper, binary-owned replay performs a narrow, transient normalization only when the entire payload is a complete exchange response; it never rewrites the session document or recaptures the malformed body.
- **`session-check` now follows a competing closeout to its terminal fact (`#closeoutwaitchurn`).** A route supervisor is a long-lived process, so its PID no longer stands in for the lifetime of one finalize request. Recovery subscribes to the exact cycle projection, wakes when the request-scoped owner guard releases or the cycle commits, and retries a crashed-owner claim after a 30-second bound; the five-minute foreground lease remains only a safety stopgap.
- **Managed closeout no longer spawns a redundant `session-check` (`#sessioncheckturnreactive`).** `agent_doc_finalize` now returns its terminal binary-owned closeout report together with the next queue prompt/id/reason, and its MCP instructions explicitly forbid a companion session-check call after success. Sequential/DAG orchestration likewise consumes strict finalize directly. The next turn's preflight already resolves the live document and applies the same open-cycle, prompt-only-tail, and bypassed-patchback guards; opening a new cycle clears the prior closeout owner in the reactive projection, so a superseded check cannot retain a lease. The explicit CLI/tool session-check remains available for direct-exec distrust checks and diagnostics.

## 0.35.31

_JetBrains plugin 0.2.297; VS Code extension 0.2.57._

- **Concurrent multiline prompts can no longer land below their streamed response without `❯ ` prefixes (`#haivenresponseorder`).** Production evidence from `haiven-sdk.md` showed a prompt growing from 384 to 816 bytes after the response baseline was captured; stream patchback inserted the response ahead of that authoritative prompt, moved the boundary below both blocks, and the old repair required either an intervening boundary or a complete prompt match in the earlier partial baseline. Response-order repair now accepts an exact prompt sequence from the full pre-write authority when its first line is already present in the partial admission baseline, even when the boundary already trails the prompt; it binds to the newest matching response across transient `(HEAD)` annotations, then reruns prompt normalization after reordering. The Haiven-shaped regression preserves all multiline prompt prefixes, places the complete prompt before its response, and proves the response remains singular. A wholly new follow-up arriving after preflight still fails closed and remains outside the committed response snapshot.

## 0.35.30

_JetBrains plugin 0.2.297; VS Code extension 0.2.57._

- **CRDT delivery convergence is now subscription-driven instead of controller-polled (`#crdtwaitsubscribe`).** Every relay delivery-fold input publishes its monotonic revision through Lazily 0.51.1's `ThreadSafeQueueCell`; a condition-variable adapter closes the publish-before-park race while retaining only the latest revision. Current-text reads carry that cursor through one long-lived controller await, and both the RPC-hosted and embedded-relay paths share the same convergence/recovery timer implementation. The pre-write current-transition guard uses the same quiet subscription, cutting the delayed-ACK regression from 40 repeated current-text observations to 8 while preserving retained intent, the absolute foreground deadline, ACK replay/force-refresh recovery, and the bounded compatibility fallback when no owning controller can be reached.

## 0.35.29

_JetBrains plugin 0.2.297; VS Code extension 0.2.56._

- **Parsed `ops.log` events now have one typed vocabulary (`#adopseventenum`).** `OpsLogEvent` provides exact token parsing and compiler-coupled names for correctness-sensitive producers and consumers across session-check, doctor/autofix, convergence playback, op-capture verification, and ops-summary clustering. Prefix lookalikes no longer satisfy event checks, and the remaining dependency-cycle diagnostic parser is pinned to the typed token by a cross-crate regression test.
- **Contributor mutation probes can no longer silently poison later Cargo runs.** Repository instructions now require every temporary Rust source restore to refresh its mtime before another build and prescribe narrow package cleanup before a full cache reset. This captures the stale-rlib failure observed while implementing this release, where clean source text and a clean diff still produced missing-symbol errors from mutated artifacts.

## 0.35.28

_JetBrains plugin 0.2.293; VS Code extension 0.2.56._

- **Editor routes now have one end-to-end terminal-result budget.** Pane resolution, startup recovery, and dispatch-only retries share an absolute deadline; a protected operator composer draft is detected before any readiness wait. Proven hook/actor/pane dispatch-start evidence keeps a slow fresh turn alive without writing a false startup-miss marker.
- **Delivery-ACK recovery no longer churns replicas or healthy supervisors.** The controller requests a targeted editor re-registration first and reserves supervisor recycle for event-publication failure. JetBrains coalesces repeated re-registration requests per open document and urgently drains the retained replica between attempts.
- **Interrupted-cycle recovery commits one diagnostic-complete image.** Preflight folds its IPC dogfood note into the still-open recovery image before repair commits it, avoiding the post-commit mutation that forced a duplicate response cycle.
- **Reaped external history remains valid coined-ID evidence.** Session-check includes IDs from the configured external done archive when validating response references, so a just-completed archived work ID is not reported as newly coined.
- **A slow live controller no longer fails a `document_state_projection` read.** `Run Agent Doc` (and any cold projection read) now treats a controller recv-timeout (`EAGAIN` / "Resource temporarily unavailable") the same as a momentarily-unreachable socket and falls back to the cold `state.db` projection, instead of surfacing `query live document_state_projection through the Lazily controller: … Resource temporarily unavailable (os error 11)`. The state-wire transport classifies a timed-out read as a distinct `Timeout` so hot-path writes still fail closed. The owner-pane wedge backstop falls back the same way.
- **Document closeout no longer takes bespoke advisory flocks (`#lazily-hot-path`).** The repo `CommitLock`, per-document snapshot lock, route-queue lock, and direct-run doc lock are removed; commit serialization relies on git's own `index.lock` retries and route-queue writes on CRDT merge retries. `flock` is no longer used anywhere on the document-closeout hot path.
- **A fresh Claude composer's ghost hint is no longer misread as an unsent draft (`#panedraftunblocker`).** `❯ describe a task for a new session` is a rendered placeholder in an empty composer; dispatch-only reopen now treats it as dispatch-ready and injects, instead of refusing with `composer holds unsent operator input ("❯ describe a task for a new session")` — an unblocker the operator could never satisfy because there was nothing to submit or clear.
- **Requires lazily 0.51.1 for the `ThreadSafeContext` stale-derived-value fix.** Upstream `9f14f40`: a `clear` arriving during an in-flight recompute was lost, so a derived cell served a stale cached value and never re-derived (surfaced upstream as `ThreadSafeQueueCell` "losing" a push — storage was correct, `len` was stale). agent-doc's controller projection is built on `ThreadSafeContext`, so this is the stale-projection class directly; the dependency floor is raised across all 18 member manifests rather than left to a caret resolution.
- **Deferred document writes now carry a typed, cross-version-safe source (`#adwritesourceenum`).** `DocumentWriteSource` replaces prefix/equality checks spread across the realtime, controller, session-check, and IPC paths; unknown tokens round-trip through `state.db` without failing deserialization. Ordered closeout stages plus projection-assigned write ordinals let retained-write settlement prove that a newer queue mirror or post-commit reposition superseded an earlier response write, while rejecting older high-rank facts and same-stage writes; the content-delta proof remains as the operator-rebase fallback.
- **Composer *autosuggest* ghost text is no longer misread as an unsent draft.** The previous fix allow-listed the two static placeholder strings, which could not cover Claude Code's **dynamic** autosuggest hint (e.g. `❯ yes, compact it`) — route still refused dispatch with `unblocker=submit_or_clear_pane_draft`, an unblocker with nothing to submit or clear. Draft detection now rejects any composer body that renders **dim/faint** (SGR `2`) in the raw capture, using the harness-agnostic `prompt_candidate_is_dim_placeholder`. Styling, not wording, is the discriminator, so it generalizes to hint text no list can enumerate; real normal-intensity operator input at the same prompt is still protected.
- **Focusing a document whose session finished no longer leaves the tmux pane behind.** `focus_document_pane` is a non-mutating UI handoff, so a `Blocked`/`Closed` durable actor projection no longer vetoes the focus (`actor_not_focusable`) when the pane's *live process tree* still exactly owns that document — clicking the opposite split's document now selects its still-open pane. A pane genuinely reused by a different document stays refused (ownership is proven from the live process tree, not stale durable state), so no cross-document focus steal.

- **Committed captured closeouts retain ownership of their durable editor-save effect.** `session-check` now resumes a captured closeout even after its semantic cycle reached `Committed` when Lazily has the response and a deferred projection still exists. Superseded deferred lineage no longer blocks the native-save sink when the converged canonical document already materializes that response; exact disk proof remains required before intents are retired.
- **`session-check` now finishes a durable captured closeout even when no route supervisor exists.** The CLI invokes the same keyed, idempotent captured-finalize replay used by the background supervisor before running the canonical closeout check, so a Codex Stop hook can settle a response and its captured queue mutation plan synchronously instead of cycling forever at `response_captured`.
- **Route and supervisor readiness now ignore arbitrary user status chrome by terminal structure instead of parsing presentation text.** Claude Code and Codex direct-pane checks scope their prompt/draft evidence through tmux's cursor row, while OpenCode keeps its boxed-composer and lifecycle-event proof. This removes the customizable-status false negative behind JetBrains `editor_route did not publish a terminal result within 20000ms`, preserves busy/drafted-input vetoes, and falls back to the compatibility classifiers when cursor evidence is unavailable.
- **Editor reroutes distinguish slow startup from a real Claude composer draft.** JetBrains `Run Agent Doc` again matches VS Code's 120-second dispatch-readiness budget instead of timing out after 15 seconds, and harness prompt parsing accepts Claude's non-breaking space between `❯` and drafted text. Exact coverage pins both behaviors, so slow startup gets the shared allowance while `❯ do #haivenask` reports the protected draft instead of an unsatisfiable boot-timeout loop.

## 0.35.27

_JetBrains plugin 0.2.292; VS Code extension 0.2.56._

- **Free-text queue heads now carry execution context without synthetic tracked work (`#ftimmediate`).** Inline `[focused-cycle]`, `[operator-verify]`, and `[clean-session]` tokens use the same parser and drain policy as backlog items: a focused head yields the in-session loop and is picked up by the supervisor after a forced clear, operator verification remains human-gated, and clean-session work stays drainable while retaining its reset reason. Production-backed SimWorld coverage pins the focused-cycle yield → clear → dispatch path, and no extra ids, side records, or persistence layer were introduced.

## 0.35.26

_JetBrains plugin 0.2.292; VS Code extension 0.2.56._

- **Queue activation edits now converge bidirectionally without go-marker churn (`#qactsync`).** A fresh `start` / `go` marker gesture overrides stale `queue:` frontmatter long enough to become the canonical durable value, while stable-snapshot provenance lets a one-sided marker or frontmatter edit win in either direction. Genuine simultaneous conflicts resolve deterministically to canonical frontmatter with a visible diagnostic, and production-backed SimWorld coverage proves both edit directions, conflicts, bounded sequences, and fixed-point convergence.

## 0.35.25

_JetBrains plugin 0.2.292; VS Code extension 0.2.56._

- **Closeout ownership and mutation capture now stay on the live Lazily actor path (`#lazily-hot-path`).** Foreground finalize, retained recovery, and repair use typed actor transitions for claim/heartbeat/release instead of independently arbitrating through SQLite and `flock`; the transactional store remains the actor-emitted restart projection, and captured tracked-work mutations replay from the same durable response intent.
- **Binary installation and MCP upgrades are monotonic.** An older checkout cannot overwrite a newer installed `agent-doc` unless the operator explicitly sets `AGENT_DOC_ALLOW_DOWNGRADE=1`. A long-lived MCP server detects a replaced executable by inode and `execve`s the installed binary while replaying the in-flight stateful request over the same stdio transport, so upgrades no longer strand a live session on stale closeout logic.
- **Strict closeout canonicalizes harmless response-heading drift before capture.** A descriptive leading heading such as `### Plan:` becomes a canonical `### Re:` heading, and body-only prose receives a small response heading; valid headings remain byte-stable while empty or non-exchange patchbacks still fail the existing structural guards.

## 0.35.24

_JetBrains plugin 0.2.292; VS Code extension 0.2.56._

- **JetBrains native reload now preserves the last valid generation when the JVM cannot unload it.** A restart-required update reopens and resumes the retired native shadow instead of leaving FFI unavailable; CRDT replica registration therefore remains live, and visual-token refreshes retain the last valid Markdown highlighting when native data is temporarily unavailable.
- **Routed dispatch cancels an exact empty preflight before resolving the authoritative document.** A replay-safe `open_empty_preflight` closeout is abandoned without a document read when it has no captured response, mutations, or tracked maintenance, unblocking `Run Agent Doc` while preserving the rule that an editor-attached document may never fall back to disk authority.

## 0.35.23

_JetBrains plugin 0.2.291; VS Code extension 0.2.56._

- **State-event retention now follows the slowest exact live editor acknowledgement (`#retentionwatermarklive`).** The controller passes its authoritative PID-scoped registration set into SQLite; a live peer without an acknowledgement pins the minimum at zero, while registrations removed by the durable OS-exit/cold-start liveness path have their stale ack rows evicted atomically. Ledger rows strictly below `MIN(acked_version)` are deleted while the minimum row remains as the monotonic version anchor, and the existing fact-specific count caps remain the bounded-storage backstop.

## 0.35.22

_JetBrains plugin 0.2.291; VS Code extension 0.2.56._

- **Committed boundary cleanup now proves its own self-heal (`#boundaryaccum1`, `#ipcpostcommitinv`).** Binary-owned snapshot and staged content collapse scattered `agent:boundary` markers independently of active-run and IPC-listener guards; those guards apply only to editor working-tree rewrites. If a post-commit check finds multiple markers, the exact-path repair commit must succeed and a fresh HEAD read must contain exactly one marker, otherwise closeout fails loudly with a phase-labeled invariant error. Template coverage and an eight-cycle deterministic wedged-handoff simulation prevent recurrence.

## 0.35.21

_JetBrains plugin 0.2.291; VS Code extension 0.2.56._

- **State-event replay now collects a durable acknowledgement per live editor peer (`#fmgc`).** `state_event_peer_acks` is keyed by the PID-scoped `(document_hash, pid, editor_id)` registration, advances each peer monotonically, and rejects cursors above the durable document high-water. Project Controller subscriptions carry the `document_version` captured from the exact ledger snapshot used to build the outgoing graph; JetBrains and VS Code report that version only on the next subscription after successfully applying the snapshot/delta, so delivery or a concurrent append cannot manufacture an acknowledgement. Migration, stale-writer, independent multi-peer, and cross-editor apply-then-ack regressions are included. Retention still uses the existing fact-specific count caps; switching to `MIN(acked_version)` remains gated on live-peer selection and deterministic crashed-peer eviction landing together.
- **Project Controller editor routes accept the empty `--col` placeholder used for non-Markdown IDE splits.** The validator now preserves the established sync-layout contract while continuing to reject empty `--focus` and whitespace-only layout values, so `Run Agent Doc` no longer fails with `editor route layout arg --col has empty value`.

## 0.35.20

_JetBrains plugin 0.2.290._

- **Selected text from JetBrains `Run Agent Doc` now safely steers the active turn (`#selectedturnsteering`).** The action captures the exact selection before asynchronous dispatch and sends it through a dedicated Project Controller and supervisor `Steer` admission instead of the ordinary dispatch-only reopen path. The supervisor accepts steering only while the authoritative actor is busy, deduplicates retries by steering id, returns a typed `delivered`/`duplicate` acknowledgement, and leaves the active turn running. Invocations without a selection retain the existing busy-pane no-new-trigger refusal. Live-socket coverage proves exact-once byte delivery and stable busy state; controller and plugin tests pin the typed acknowledgement and verbatim payload.

## 0.35.19

_JetBrains plugin 0.2.289._

- **JetBrains native hot reload is restored with a proved one-generation handoff (`#jbnativehotreload`).** Linux JetBrains advertises `native_hot_reload_generation_v1` and marshals every JNA call through a generation-owned thread. Reload stops and joins CRDT replicas and IPC listeners, drains calls, quiesces the old cdylib, terminates the owner thread so Rust TLS destructors run, closes the JNA handle, and verifies the old `/proc/self/maps` entry is absent before loading the replacement. A failed drain, unmap, or ABI proof loads no second generation and requires IDE restart; a replacement-load failure can restore the prior named shadow only after its old mapping is gone.
- **Reloadable editor code no longer owns the durable reliable-sync outbox.** Raw liveness/document frames go through a typed `reliable_sync_outbox` controller command; epoch allocation, append-before-send, replay, ACK pruning, and `SqliteOutbox` all live in the project controller. Embedded controller launch uses a short-lived external helper, so a controller-lifetime child-reaper thread cannot pin the old cdylib. The real-process regression cycles two generations with an active listener and CRDT replica, proves each old mapping disappears, verifies the plugin process has no `state.db` descriptors, rejects deleted DB/native mappings and split DB/WAL/SHM inode generations, and finishes with `PRAGMA quick_check = ok`.

## 0.35.18

_JetBrains plugin 0.2.288._

- **JetBrains native and package upgrades now fail closed to a full IDE restart.** The plugin loads exactly one `libagent_doc` generation per JVM, no longer reloads on mtime changes or `reload_library` intents, and declares `require-restart="true"` so a package update cannot dynamically replace the classloader and bypass that invariant. This prevents independent SQLite/WAL lock domains from corrupting `state.db`.
- **Library reload fanout is adapter-policy aware.** VS Code remains explicitly hot-reload capable, while JetBrains and unknown registrations are counted as restart-required and receive no unsafe reload intent. Install and admin reports expose the restart-required count.

## 0.35.17

_JetBrains plugin 0.2.286._

- **Live frontmatter harness changes are accepted as safe-boundary handoffs (`#harnesshotrebind`).** `Run Agent Doc` never injects the pending reopen into the previous harness and no longer asks the operator to restart a healthy supervisor. The supervisor preserves an active turn, replaces the child at its first idle boundary, and auto-triggers the owning document on the new harness; paused queues hold for resume, repeated routes coalesce, and an explicitly disabled switch policy remains fail-closed.
- **Supervisor state exposes the live harness identity.** Route reconciles a stale actor-record harness after a completed in-loop switch instead of scheduling a second handoff, with focused policy, IPC snapshot, route, and SimWorld regressions covering ready, active-turn, paused, disabled, in-flight, and stale-record cases.

## 0.35.16

_JetBrains plugin 0.2.286._

- **Claims can explicitly provision a fresh pane in the authoritative tmux session (`#claimnewpane`).** `agent-doc claim <file> --new-pane` bypasses current-pane resolution and cross-session reuse, provisions through the existing route startup path, and conflicts with force/position/window/pane/isolate targeting. Normal claims still provision automatically when the resolved pane belongs to another document, and `session set` remains the supported managed-window round trip between sessions.
- **Cross-session editor recovery now offers “New Pane in This Session.”** JetBrains and VS Code map that choice to the binary-owned `--new-pane` mode without forwarding a positional or force flag; command builders and CLI parsing are regression-tested so the choice cannot silently replace the current pane or migrate its window.

## 0.35.15

- **Supervisor stdin forwarding now has one interruptible implementation (`#af88`).** The obsolete `PtySession::forward_stdio` path that held `stdin.lock()` across a blocking read is removed. Production forwarding remains in `io_threads`: Unix polls raw stdin beside a stop pipe, joins promptly on child exit, and releases stdin before restart/quit prompts.
- **IPC wedge defenses are pinned end to end.** Deterministic regressions now prove that the connect watchdog returns before a wedged `connect_sync` worker and that a listener closes a half-open connection after its per-connection read deadline. These lock in the existing caller-side connect bound and listener-side fd/thread backstop.

## 0.35.14

- **A newer durable response capture now supersedes prior-cycle deferred CRDT/CAS lineage.** Recovery first proves exact current authority/disk equality, retires journal entries whose targets do not contain the active capture, and then submits the response cell from that one current base. It no longer replays stale queue/backlog branches or malformed `-->` scaffolding into the newer response.
- **Open-cycle capture identity stays coherent with the content-bearing state ledger.** If a retry appends a newer same-cycle capture fact after the turn-intent checkpoint, the cycle projection adopts that capture/hash while it remains open, so `session-check` cannot wedge on a projected-body/checkpoint-hash mismatch.

## 0.35.13

_JetBrains plugin 0.2.285._

- **Missing controller replicas now recover even when JetBrains retains a local forwarder.** `ack_recovery_force_refresh` unconditionally replaces and registers the document replica from the exact visible editor cut; a plugin-local cache can no longer suppress recovery after controller recycle.
- **Standalone orphan HTML-comment terminators have a narrow binary-owned repair lane.** Canonical validation still rejects bare `-->` lines, while template normalization removes only standalone orphan lines outside balanced comments, blockquotes, and code fences. This lets malformed editor authority register as repair-required and makes the `#dbj7` empty-comment artifact recoverable without force-disk.

## 0.35.12

- **CRDT writes now carry one captured CAS base through the serialized writer.** Stream, inline, recovery, and compare-if-current writes no longer pre-apply a whole-document CRDT target and then submit it again through the disk-projection path. One queued mutation rebases over a newer editor cut, waits for delivery plus the native editor save, and fails closed if a detached disk replica changed.
- **Post-commit boundary moves refine retained targets without replaying their stale document branch.** The committed/current cut already contains the response, so boundary normalization replaces the retained target exactly while preserving its original reconnect base. This prevents the observed duplicate `#dbj7` prompt and bare `-->` suffix from a deferred-target composition race.
- **Canonical structure validation rejects standalone orphan HTML-comment terminators.** A bare `-->` outside code, blockquotes, quoted literals, or a balanced multiline comment can no longer pass the write-authority gate as an empty response.

## 0.35.11

_JetBrains plugin 0.2.284; VS Code extension 0.2.55._

- **Editor-owned save observability now has one shared Rust schema (`#vscsaveproj`).** `agent_doc_record_editor_surface_event` owns ops-log timestamping, relative-path/document attribution, and event formatting for both editor plugins. VS Code's typed `save_document` socket handler records `missing_file`, `missing_document`, `saved`, and `failed`, while JetBrains no longer builds or appends editor-surface log lines in Kotlin. The VS Code behavioral regression proves an open document is saved before its post-save content receipt and success event are published.

## 0.35.10

- **Attached-editor reads no longer descend to stale disk after replica recovery exhausts (`#missingreplicarebuild`).** Realtime and preflight adapters now share a pure authority-recovery decision table in `agent-doc-turn`: missing replicas request the bounded plugin refresh, sync-pending may run its single model rebuild, and both fail closed while the editor remains open. Disk becomes read authority only after proven detach, preventing saved projections from resurrecting operator-deleted document nodes.
- **Post-CAS response retries are pinned to semantic idempotency (`#patchretryidem`).** The component-idempotent retained-write path shipped in `6e6830a8d` is now covered by the exact reported artifact shape: a second response image already containing the quoted `> **Queue prompt:**` block is composed once over the original content-bearing base, concurrent operator text survives, and later retries keep one response, boundary, exchange close, and no bare `-->` terminator debris.

## 0.35.9

- **Missing Codex resume sessions now recover with a fresh session.** When Codex definitively reports `No saved session found with ID <id>`, the supervisor removes only that matching frontmatter `resume` pointer and immediately relaunches fresh instead of retrying the stale ID forever. It also rechecks document authority before a cached resume retry, so an operator removal made while the supervisor is running takes effect immediately.
- **Ctrl+C now stops the supervisor during crash backoff.** The bounded restart delay polls supervisor stdin while the child is absent, so raw terminal mode no longer swallows the operator interrupt.

## 0.35.8

- **Windows release builds no longer type-check Unix-only auto-install file-descriptor routing.** The explicit log-fd stdio plan is now compiled only on Unix; Windows keeps its existing null-stdout/inherited-stderr plan.
- **Formal checks track the current official TLA+ 1.8.0 artifact and recover from stale cached jars.** The pinned checksum now matches the release asset published by the TLA+ project, and a mismatched local cache is refreshed before verification.

## 0.35.7

_JetBrains plugin 0.2.283._

- **JetBrains CRDT delivery no longer turns one overlapping event into permanent workspace-wide polling (`#crdt-drain-idle-quiet`).** Backoff resumption now consumes the already-retained path flags instead of manufacturing a new drain-all request, drain-all clears the per-file requests it already covers, and the request/release handoff closes its lost-wakeup race. Controller delivery events no longer enqueue a second generic activity drain, retry diagnostics use one bounded reason token instead of appending `-backoff` forever, and expected empty pulls warn only when they are genuinely slow. In live verification, reopening the 420k-file workspace attached the existing replicas and then emitted zero `remote-drain` / `transport.pullDelivery` entries beyond the old 30-second retry ceiling; the prior build pulled all 11 replicas every 30 seconds and logged an ever-growing reason string.

- **Compact Exchange (and every editor-attached write) no longer pays a fixed ~2s stall waiting for the ACK-recovery escalation (`#crdtpushdrain`).** The plugin's `requestRemoteDrain` gated *controller-published* CRDT remote events behind the speculative no-op drain backoff — a gate that exists to stop a self-driven drain spin when there is nothing to pull, and that escalates toward its 30s ceiling on an idle document. That is exactly the state a document sits in when the operator triggers Compact Exchange, so the published frontier was suppressed and nothing moved until the binary escalated to `ack_recovery_force_refresh` after `CRDT_ACK_FORCE_REFRESH_AFTER_MS` (2000ms) — the only path that called the backoff-bypassing `requestUrgentRemoteDrain`. Measured against `ops.log`: compact `wait_ms` sat at 2646–8695ms with a ~4.5s floor that did **not** scale with `update_bytes` (46KB→6007ms, 130KB→2646ms), confirming fixed overhead rather than payload cost. Every controller push now drains urgently; `request_full_state` remains exempt because it owns the text-adopt path. Controller pushes are externally rate-limited (one per ACK-replay signal interval, only while a write awaits ACK), so eager draining cannot reintroduce the no-op spin the backoff was added to prevent. The binary-side 2s escalation is retained as a backstop for older plugin builds.
- **Route no longer defers a `Run Agent Doc` dispatch after a single unproductive drain request (`#crdtpushdrain`).** `await_idle_with_max_wait_and_effects` latched its urgent CRDT delivery drain behind a one-shot `urgent_drain_requested` flag. A single urgent drain can legitimately apply nothing — `drainRemoteUpdatesFor` returns early while the path has pending local edits or is mid editor apply, and the forwarder may not be registered yet — and its only follow-up is the *gated* `requestRemoteDrain`, which an idle document's escalated no-op backoff suppresses. Route then spent its remaining budget polling a frontier nobody would pull and failed with `route deferred for <FILE>: Lazily current transition remained delivery_pending for 5000ms`. The urgent drain is now re-requested every 750ms while delivery stays pending, so an unproductive attempt gets several retries inside the same budget; a genuinely never-converging delivery still fails closed at `max_wait`.
- **A successful urgent drain now resets the escalated no-op backoff counter (`#crdtpushdrain`).** `requestUrgentRemoteDrain` never cleared `consecutiveNoOpReschedules`, so even after it applied useful work the gate stayed parked at its previous (up to 30s) delay and re-suppressed the *next* controller push — compounding the stall across successive writes.

## 0.35.6

- **A completed `agent:` harness switch is now persisted to the authoritative actor record (`#actorharnessrecordwriteback`).** `SupervisorShared::set_current_harness` previously updated only the in-memory identity used by the tmux submit profile; nothing wrote the record `route` reads, and `transition_state_*` carries the stored harness forward on every later lifecycle transition. A codex→claude switch therefore left the record reading `codex` forever, so `Run Agent Doc` kept deferring to a boundary restart that had already run. The supervisor now also writes the switched harness through `agent_doc_session_actor_io::set_record_harness_direct`, preserving pane/generation/state.
- **Route's harness-mismatch guard normalizes both sides (`#actorharnessnormcompare`).** `expected_harness` was normalized (`claude-code`) while the stored record harness was compared raw, so two spellings of the same harness read as a mismatch.
- **A harness-switch defer no longer tells the operator to interrupt its own in-flight restart (`#actorswitchdeferbusyself`).** A switch necessarily drives the actor `busy` while it respawns; the defer used to read that as a blocked pane and advise "Use Interrupt and restart to force the harness switch" — asking the operator to abort the restart that was completing their switch. The recovery hint now recognizes self-induced restart windows (`actor_busy_is_self_induced_restart`) and says to wait for the boundary. The JetBrains notification drops its restart action in that state.
- **A restart-fresh pane can no longer strand the trigger in the composer (`#restartfreshtriggerstranded`).** After a harness-switch restart the supervisor's auto-trigger typed its prompt into the freshly spawned pane and reported `Sent`, but the submit key raced a still-initializing composer and the prompt sat there unsubmitted — the operator-visible "the prompt was sent but not submitted". Route's fresh-start path already resubmitted a stranded trigger (`#jbtsiftnosub2`), but a restart-fresh spawn never reaches it. The supervisor inject now waits for a document cycle ack, and on a dispatch-ready pane that still shows the trigger it resends one bare submit key and re-checks, failing closed rather than reporting a delivered prompt.
- **Fixed a test that documented behavior it did not check (`#actorharnessswitchcoverage`).** `set_current_harness_updates_state_backbone_harness_identity` claimed IPC `state` "feeds the persisted actor record" while asserting only an in-memory getter round-trip, which is why the missing persisted half shipped. The persisted behavior is now covered in `agent-doc-session-actor-io`, and end-to-end by the SimWorld scenario `route_sim_harness_switch_persists_record_so_post_restart_dispatch_does_not_defer` — a codex→claude restart that asserts the persisted record and a following route dispatch that does NOT defer, with a stale-record negative control that reproduces the exact reported `stored_harness=codex expected_harness=claude-code` marker.

## 0.35.5

- **Route startup actively wakes a retained editor delivery before waiting for convergence.** When Lazily reports an attached document with a pending delivery frontier, the route gate sends one urgent ACK-recovery drain signal and then continues polling the canonical current state. This lets an already-batched retained update cross an editor's ordinary background backoff within the route deadline while preserving fail-closed dispatch if convergence still cannot be proved.

## 0.35.4

- **JetBrains Compact Exchange ACK recovery no longer replays the pre-compact editor buffer.** When a retained CRDT delivery is waiting behind the plugin's background no-op drain backoff, the two-second recovery event now performs one immediate targeted pull on the existing replica. It does not re-register from stale visible text, so controller-owned compaction reaches the editor and the compact same-cell guard remains reserved for genuine operator edits.

## 0.35.3

- **The live tmux sweep no longer executes retired authority-rollback tests.** Two obsolete `--force-disk` integration tests were removed from the generic `--ignored` sweep, while deterministic SimWorld fault coverage now explicitly proves every interrupted closeout retains its captured response authority through recovery.
- **Concurrent state-ledger schema opens now serialize instead of failing startup.** The SQLite state-store retries only `SQLITE_BUSY`/`SQLITE_LOCKED` initialization failures against the same authoritative database for the existing bounded timeout. Controller startup and simultaneous status probes no longer race `PRAGMA journal_mode=WAL`; corruption and every non-lock error still fail closed without replacing or quarantining `state.db`.
- **Repair closeout follows the materialized editor rebase.** Repair writes now return the canonical document actually retained by Lazily/CP, and template normalization, prompt-prefix cleanup, scaffold repair, completed-backlog reap, disk proof, and snapshot checkpointing carry that value forward. When an editor cut advances after repair composition and the replica then disappears, zero-replica recovery recomputes the semantic three-way merge before projecting the rebased target; it no longer wedges on byte inequality with or checkpoints the stale pre-rebase candidate.

## 0.35.2

- **Newer Compact Exchange state retires stale 0.35.0 composition chains without replay.** Recovery recognizes only strictly older binary-generated compact archive pointers from the historical `post_commit_reposition` / `serialized_atomic_write` paths, requires exact canonical, disk, editor-delivery, target-hash, source, and reason proof, and clears those intents while preserving newer queue deletions and post-boundary prompt edits byte-for-byte. Unrelated retained writes remain fail-closed.

## 0.35.1

- **Transient prompt working state can no longer wedge closeout.** Preflight publishes the syntax-aware `❯ 🚧` exchange marker through one best-effort canonical CRDT projection, never the durable serialized-write pipeline. A delayed editor ACK or projection error cannot fail preflight or create a retained document-write intent.
- **The 0.35.0 marker-intent churn self-heals without overwriting operator text.** Session recovery retires only a stranded `serialized_atomic_write` whose target contains `🚧` and is exchange-prefix-equivalent to its expected content. Ordinary retained document writes still require canonical/disk native-save proof.

## 0.35.0

- **Compact Exchange preserves independent live cells.** After CRDT convergence, compaction rebases its live and committed projections onto authoritative sibling components before checkpointing or committing. Queue/backlog deletions made while exchange compaction is running now survive normally. The post-`agent:boundary` exchange tail is also independent live operator state: it may change during compaction without failing, remains visible after compaction, and stays out of the committed compact snapshot; only drift in the compact-owned archiveable prefix fails closed (`#compact-independent-cells`).
- **Active exchange prompts expose syntax-aware working state.** Preflight marks the selected prompt's first free-text line with `❯ 🚧` and prefixes subsequent prose with `❯`. Lists and fenced regions are untouched; headings retain their `#` structure and carry `❯ 🚧` inside the heading text. Closeout clears the cosmetic marker while retaining prompt identity (`#exchange-active-prompt-marker`).
- **Lazily performance baseline upgraded.** The release consumes Lazily 0.41.0 across the realtime, CRDT, controller, state, and durable IPC paths, including its inline value storage, reusable graph-traversal scratch space, single-lock invalidation frontier, and cached read-guard improvements.

## 0.34.175

- **Controller lifetime and editor status refresh are crash-bounded.** Lazy controller launch now creates a new Unix process session, so a Codex/terminal launcher crash cannot kill the project daemon through inherited process-group cleanup. JetBrains banner collection is cache-only and no longer feeds projection completion back into another projection request; open/selection/editor-intent events remain the only refresh triggers.
- **Strict state-ledger upgrades cannot prevent controller startup.** Schema initialization transactionally retires the redundant `pending_response_captured` and `pending_response_cleared` rows before projection, records the migration once, and keeps the runtime free of a legacy deserializer. This fixes the post-upgrade controller exit that left the editor registered in Lazily but unable to reach `controller.sock`.
- **BREAKING CHANGE: Lazily current state and the transactional state ledger are the only hot-path authorities.** Filesystem live-buffer, patch inbox, queue journal, queue tombstone, queue continuation, editor-op capture, transport-health, turn-scope, owner-pane counter, Codex hook-session, and controller-cooldown state paths are removed rather than imported or replayed. Snapshot/CRDT files remain recovery projections only.
- **Operator queue deletion is monotonic.** Queue consumption uses an exact authority-shape compare-and-swap, and the journal code that unioned missing historical prompts back into the queue has been deleted. Queue continuation and delete intent now share the project ledger, so reconnect cannot resurrect an unsaved Lazily deletion from a stale sibling.
- **The write pipeline is an executable state machine.** `IntentCaptured -> CanonicalApplied -> ReplicaAccepted -> ReplicaVisible -> DiskProjected -> Committed` is enforced as monotonic, no-skip transitions in the real closeout projection and exercised by exhaustive transition, crash/retry, endpoint-churn, and 10,000-run simulation tests.
- **Editor intent has one cross-language vocabulary.** Rust, JetBrains, and VS Code use the same intent names for canonical apply, reposition, save, refresh, Lazily observation, CRDT delivery, VCS refresh, and library reload; ABI parity tests prevent plugins and the binary from independently reinterpreting an operation.
- **Corrupt `state.db` fails closed.** Normal execution no longer quarantines the ledger and creates an empty replacement, because recovery projections cannot safely recreate captured intent. Explicit repair is required, preserving the corrupt authority for forensic recovery.
- **Editor delivery is PID-scoped.** A controller socket can no longer impersonate an editor endpoint; delivery requires the matching live Lazily registration and PID endpoint, eliminating false ACK waits and unrelated focus/transport interference.
- **The hot path has one database, not a family of hidden sidecars.** Reliable-sync inbox/outbox rows, document operations, and callback exchanges now share `.agent-doc/state.db`; the separate `reliable_sync_outbox.db`, `op-log.db`, and callback request/response files and their compatibility readers are removed. Explicit snapshots and diagnostics remain cold recovery projections only.

## 0.34.174

- **Closeout progress is monotonic across lagging replicas.** A stale closeout projection can no longer regress a newer lifecycle phase, and conflicting terminal facts remain explicit instead of being resolved by read order.
- **`session-check` repairs the exact `response_captured` / `write_applied` split without a sidecar decision.** When the captured response is already present exactly once in both the live authority and disk, the durable backbone advances to write-applied and continues to commit. It does not read capture-state JSON, recapture the response, replay a whole document, or recommend `write --commit` in a loop.
- **The document-turn state-machine and sidecar-deletion contract is now load-bearing architecture.** Lifecycle transitions live in the Lazily-backed state ledger; plugins, editor, disk, and Git submit typed evidence or execute effects. Routing, liveness, capture-state, cycle-state, snapshot-authority, and ACK-content files have no authority exception during retirement.

## 0.34.173

- **A retained capture cannot converge on a target that dropped its response.** When editor, Lazily/CRDT, and disk exactly match an incomplete reconnect target, `session-check` now replays the durable response cell over that editor-authoritative cut, preserving newer operator prompts and queue deletions, then settles only after editor ACK and disk projection.
- **JetBrains reconnect settlement is reported without reinstalling a whole document.** Successful replica registration publishes the exact live editor bytes to the binary's reconnect decision state machine; the plugin never fetches, applies, or saves a retained whole-document target during registration.

## 0.34.172

- **JetBrains re-registration is editor-first and non-destructive.** Forced refresh now validates and publishes the exact live IntelliJ buffer before swapping replica generations; it no longer fetches, installs, or saves a deferred whole-document reconnect target. Retained responses and granular mutations replay afterward over that authoritative cut, preventing prompt loss, deleted-queue resurrection, repeated queue blocks, and duplicate exchange boundaries.
- **Attached-document recovery fails closed instead of promoting a stale CRDT projection.** A missing in-memory model waits for an exact editor republish; restore-time liveness reconciles durable open pids with the OS and durably retires dead IDE processes. The Haiven regression proves one prompt/response/boundary, monotonic queue deletions, replay-block deduplication, and stable convergence.
- **The formal cache-conflict model now encodes operator intent.** TLC models unsaved prompt insertion, queue deletion, exact editor-baseline re-registration, granular response replay, singleton-boundary integrity, and eventual convergence; the old save-before-register transition is forbidden.

## 0.34.171

- **A first-phase `[x]` completion cannot block its own retained closeout as a resurrection.** The reaped-item guard now distinguishes terminal `Done` state from a genuinely reopened `Open`/`Gated` copy. The same-capture retry therefore proceeds from mark to archive instead of repeatedly refusing the still-visible two-phase checkbox, while a stale editor that actually reopens the item remains fail-closed.
- **Claude artifact attachments no longer make an idle pane look busy.** A bare `⧉ <label>` attachment chip is generic idle composer chrome, independent of its session-owned label, so JetBrains `Run Agent Doc` finds the earlier `❯`/permissions composer and dispatches normally. The actual online-artifact picker (`Enter to open` plus its artifact URL) remains a typed operator-owned blocker and receives no injected keys.

## 0.34.170

- **JetBrains refreshes are one generation-fenced logical replica, not transient collaborative heads.** `:refresh-N` registrations are serialized per document, publish the successor identity before changing hub membership, retire every prior raw incarnation, rotate and checkpoint the document lineage, and terminally ignore late direct frames. Durable frames from the retired incarnation are rejected by the same lineage fence, including concurrent refresh-registration schedules.
- **Already-concatenated generations self-heal before the integrity gate without electing disk over the editor.** Preflight and `session-check` recognize only a strict shape where one complete branch is byte-identical to the durable pending target, then semantically rebase that target over the other operator branch. Queue/backlog deletions and newly typed prompts survive, the retained response lands once, and ambiguous states still fail closed.
- **The refresh/recovery state space is executable.** Focused relay tests cover sequential and simultaneous replacement, late raw and durable frames, stale deregistration, and idempotent current replay; exhaustive SimWorld exploration covers two successive refresh generations while preserving one live head, one document, one boundary, and one operator prompt.

## 0.34.169

- **Editor delivery ACK no longer retires a write before native disk-save proof.** The CRDT delivery loop previously emitted `DocumentWriteConverged` immediately after the plugin ACKed the canonical frontier. Direct queue/backlog maintenance could therefore lose its only durable projection intent while the IDE buffer was still ahead of disk, leaving `session-check` with no safe recovery lineage. ACKed writes now remain retained until the exact canonical bytes are proven on disk; only the enclosing native-save or explicit settlement path clears them.
- **A valid live editor cut can recover historical ACK-only state without force-disk.** For no-cycle and preflight-only recovery, `session-check` now treats a structurally valid, member-backed, delivery-converged editor authority as the source of truth and requests the editor's native save. It verifies the same canonical bytes in both editor authority and disk before proceeding. This preserves unsaved operator queue deletions and prompts, and repairs states created by older binaries that prematurely cleared their intent.

## 0.34.168

- **False-stale retained captures now reopen coherently and terminalize from the state backbone.** Repair could retire a captured cycle as `Abandoned`, while a later exact-target `session-check` reopened only the direct cycle file; the authoritative backbone projection stayed terminal and the same retained response remained pending forever. Cycle state now emits a typed false-stale reactivation fact carrying the document, cycle, capture, response hash, and retirement reason. The backbone accepts it only for the exact matching false-stale abandonment, after which closeout settlement clears the retained intent and commits exactly once. An integration regression reproduces the abandoned/discarded capture with canonical and disk already at the retained target.
- **Prompt-bearing Claude blockers now queue before regular route recovery as well as dispatch-only routing.** A JetBrains `Run Agent Doc` request that finds an active Claude turn or online-artifact picker no longer enters the regular route's auto-fix, focus, interrupt, or injection path. The binary records the prompt-bearing work behind the typed blocker and returns it as already running; operator input remains untouched, and dismissing the artifact picker with `Esc` lets the existing owner continue. Focused route tests cover the modal with and without pending prompt work plus unknown-blocker fail-closed behavior.

## 0.34.167

- **An exact retained target now terminalizes even after disk has already caught up.** `session-check` previously attempted captured-finalize recovery only while canonical authority and disk differed. If the JetBrains save/reconnect completed first, both sides exactly matched the retained target but the capture record remained pending forever. The terminal convergence rung now resumes any resumable capture regardless of byte equality, clears its retained intent, advances write-applied state, and commits exact-once. A regression covers the observed equal-authority/equal-disk state.
- **Claude online-artifact pickers no longer trigger document repair or a routed-injection failure.** The harness recognizes the bottom-of-pane `Enter to open` plus `claude.ai/code/artifact` modal as a typed, operator-owned blocker. Prompt-bearing JetBrains `Run Agent Doc` requests queue durably behind it and resume after the operator presses `Esc`; agent-doc never sends `Esc` or `Enter`, preserving operator activity. The fallback diagnostic names the exact dismissal action.

## 0.34.166

- **Retained replay normalization now runs after replay, not only before it.** Live proof on `agent-doc-bugs2.md` showed canonical Lazily authority was structurally valid with one boundary at `session-check` entry; the retained response resume then created the second boundary later in the same call, after the entry normalizer had already passed. Both captured-resume validation branches now normalize the replay result before their integrity check, preserving the operator cut while preventing the resumed delivery from blocking itself.
- **Post-resume ordering has a dedicated regression.** The session-check fixture starts from the exact replayed two-boundary shape and proves the shared post-resume validator retains the latest frontier, operator prompt, and response before accepting integrity. The existing TLA+ model already permits duplicate-boundary replay at any point, requires fair normalization, and forbids commit while two boundaries exist.

## 0.34.165

- **A replay-created second exchange boundary can no longer block its own recovery.** Before the generic integrity gate, session-check and preflight now have a strict fallback for exactly two standalone protocol boundaries inside one parseable exchange: the earlier stale frontier is removed, the latest is retained, and the result is accepted only when the complete projection becomes valid. The current Lazily/editor cut remains byte-stable otherwise, preserving prompts and unsaved queue/backlog deletions.
- **Boundary integrity uses one fenced-code policy.** Structural validation, recovery validation, response-cell normalization, and final lint now agree that marker examples inside fenced code are prose, not live protocol. Ambiguous shapes—multiple exchanges, more than two live boundaries, inline markers, and malformed component trees—still fail closed.
- **The recovery model includes duplicate-boundary replay as a real transient.** `CrdtLineageFence` now explores replay producing two boundaries, proves fair normalization returns to one, and forbids commit while the duplicate exists without weakening queue-deletion tombstones or exact native-save requirements.

## 0.34.164

- **Response-bearing semantic rebases no longer wedge on missing whole-document lineage.** A retained non-capture response target is now recognized as a semantic cell, reconciled over the current Lazily operator cut, and settled through a native editor save. Operator prompts and queue/backlog deletions remain authoritative; non-response whole-document targets still require causal operator-cut proof.
- **A replay-duplicated component close no longer blocks its own recovery.** Preflight and session-check recognize exactly one whole-line unmatched close only when that component already closed normally and removing the replay artifact restores the complete structural contract. The repair flows through Lazily, preserving prompts after the exchange boundary and every queue/backlog deletion in the live editor cut; unmatched operator-authored markers still fail closed.

## 0.34.163

- **Fresh route-owned supervisors redirect stderr before the binary starts.** Route provisioning and controller-owned cold replacement create `.agent-doc/logs/supervisor-stderr.log` before submitting the pane command, then render an exact shell-level `2>>` redirection. Argument parsing, admission failures, and every other pre-`SupervisorStderrRedirect` diagnostic therefore stay out of the agent pane; the in-process redirect remains defense in depth.
- **The initial-boot fd invariant is executable.** A deterministic process test launches a fake route-owned agent-doc command and proves its boot diagnostic reaches only the supervisor log while the pane stdout/stderr captures remain empty. Architecture checks require both fresh-route and replacement-supervisor call sites to use the redirected renderer.
- **Retained response recovery no longer resurrects operator-deleted queue or backlog entries.** Response delivery now rebases as a semantic cell over the current Lazily editor cut. A missing response appends without replaying stale components; once the response is live, projection retains the editor cut byte-for-byte, including unsaved deletions and newer prompts.
- **Post-cell projection cannot create the duplicate-boundary wedge.** The semantic rebase bypasses the whole-document component merge that could duplicate an exchange boundary after delivery, while preflight/session-check retain the narrow self-heal for artifacts created by older binaries. Exhaustive Rust fixtures and the `CrdtLineageFence` TLC model cover deleted queue state, newer prompts, idempotent replay, and the single-boundary invariant.

## 0.34.162

- **Response persistence is now incremental at semantic boundaries.** `response-checkpoint` writes cumulative, complete `### Re:` cells into Lazily without queue/backlog mutation or commit; later checkpoints replace the prior uncommitted tail. `respond` is now the primary binary-owned exact-once turn-resolution command, with `finalize` retained as a compatibility alias rather than a distinct phase or the first document write.
- **Retained response replay can no longer wedge behind its own integrity gate.** Duplicate response cells and standalone boundary-only transients normalize through the current document authority before preflight/session-check validation; boundary examples inside fenced code remain untouched, and malformed component structure or inline protocol markers still fail closed. A new TLA+ response-transaction model checks monotonic checkpoints, repeated operator typing, normalization-before-seal, and commit gating.
- **Editor close and queue closeout preserve operator intent.** JetBrains 0.2.272 publishes the exact serialized final document cut before releasing the last replica; the binary materializes that retained cut without treating zero members as delivery proof. Free-text heads cannot auto-strike until their response is actually present, and idle prompt redraws cannot falsely retire a live owned turn.

## 0.34.161

- **Retained captures now rebase over the current Lazily/CP editor authority before settlement.** Recovery preserves unsaved or newly saved operator text, replays the same content-bearing intent journal, delivers the response once, and requests the plugin's native save automatically; it never asks for Ctrl+S, preflight repair, recapture, or force-disk recovery.
- **Malformed CRDT and REPLACE projections are fenced before they enter the editor.** The shared structural validator now rejects duplicate exchange boundaries, every retained target is validated before Lazily/CRDT retention or reconnect delivery, and JetBrains validates whole-document REPLACE payloads and replica-registration text so a duplicated document is recovered from the exact coherent editor baseline rather than made authoritative.
- **Response closeout and tracked-work mutation are one monotonic envelope.** ACK-retained write outcomes keep the same `--done`/backlog mutation facts, and a replayed `--done` is idempotent after the item has already been reaped into the external archive.
- **Background control-plane work cannot change the operator's selected JetBrains document.** Startup refreshes open document replicas without installing the reverse tmux-to-editor focus mirror; only explicit operator selection controls IDE focus.
- **Recovery policy and simulation coverage now match the authority model.** Doctor/autofix classify Lazily-ahead-of-disk as binary-owned recovery with no operator-save action, and exhaustive lineage transitions cover tracked-mutation capture, commit gating, malformed-target rejection, and monotonic progress.

## 0.34.160

- **Live editor text now has one authority: the Lazily/CP CRDT.** Full `.agent-doc/live-buffer` snapshots are no longer written or read for content, liveness, targeting, stale-plugin detection, or capability proof. Open/close state, sync epochs, editor identity/version, and capabilities travel as monotone reliable-sync registrations; legacy live-buffer files are delete-only. JetBrains 0.2.270 also closes the native editor-op epoch before applying an agent or remote projection.
- **Deleted queue heads cannot be replayed by an append-only journal.** Startup retires and clears `.agent-doc/queue-journal`; queue additions and deletions survive only through the same CRDT lineage, and preflight consumes CP current directly.
- **Retained semantic rebases require causal proof.** Exact byte equality settles delivery-only intents, while editor-cut, reconnect, external, and legacy semantic intents remain retained until lineage proof. The TLC model and exhaustive SimWorld cover deletion/crash/replay and editor-op epoch fences.

## 0.34.159

- **Exact non-capture editor-reconnect projections now settle monotonically.** A queue-normalization or other deterministic preflight projection is no longer misclassified as a captured assistant response. Once the retained target hash, canonical editor/CRDT authority, and native-saved disk bytes are identical, `session-check` clears that intent without requiring inapplicable response-materialization proof; unresolved instances retain the same intent and use non-capture diagnostics. The CRDT relay regression fixture covers ACK-before-save retention followed by exact-save settlement.

## 0.34.158

- **A converged CRDT delivery ACK no longer writes the session file behind an active editor.** The binary asks the owning editor to save its already-authoritative buffer, requires disk to contain that exact editor version, and revalidates the canonical version before settlement. A newer operator edit invalidates the old proof while preserving the durable agent intent for rebase; a missing save keeps the same capture retained. This removes the direct-disk race that caused JetBrains File Cache Conflict, stale-disk restoration, and retained closeout wedges.
- **Background tmux/controller work no longer steals the active JetBrains editor selection (JetBrains plugin 0.2.269).** While the project window is active, its selected Markdown editor outranks a different tmux document reported by the polling mirror. Tmux-to-editor following resumes after the operator actually leaves the IDE for tmux, so recovery of `agent-doc-bugs2.md` cannot switch an operator away from another document.
- **The formal and executable recovery models now separate delivery ACK, native editor save, and commit.** `CrdtLineageFence.tla` and the exhaustive Rust SimWorld explore save requests, exact saves, and operator advances between request and save, and prove commit requires the exact saved editor version in addition to the existing lineage, tombstone, and pending-intent invariants.

## 0.34.157

- **Retained-capture recovery now runs before the terminal authority/disk divergence guard as well as before integrity failure.** Live recovery of `agent-doc-bugs2.md` showed a second valid state: replica replacement had already removed the duplicate boundary, so integrity passed, while the clean response remained retained only in canonical editor authority. `session-check` now gives that resumable captured/write-applied/abandoned cycle one exact-once replay, re-resolves and revalidates both projections, and only then applies the generic divergence refusal. A focused regression proves divergent authority triggers replay while converged authority never does.

## 0.34.156

- **Whole-document CRDT replacements now fence obsolete replicas by lineage instead of union-merging their stale Yjs updates into a new document.** Registration returns the canonical lineage; JetBrains and VS Code attach it to every durable document-op frame; replacements rotate it; and stale or legacy frames after rotation are terminally quarantined while their reliable-sync cursor still advances. This prevents the observed full-exchange duplication, duplicate boundary markers, deleted-queue resurrection, and unbounded ACK retry after an editor/model rebase.
- **CRDT lineage survives process recycling without being mistaken for a different projection.** The relay persists lineage beside the Yrs checkpoint with the exact projection hash, restores it only on a hash match, and fails closed to a fresh lineage after an incomplete or mismatched checkpoint. Retained agent intent remains independently durable and can be replayed over the editor-authoritative projection.
- **Session repair can resume a retained captured response before a corrupted projection trips the integrity gate.** `session-check` now performs the existing lossless capture recovery once for captured/write-applied/abandoned cycles, then revalidates the reconstructed authority. This removes the circular wedge where duplicate boundaries prevented the only safe non-force-disk repair.
- **The concurrency contract is now executable.** `CrdtLineageFence.tla` exhaustively checks the bounded control-state model for monotonic queue deletion, no stale-lineage corruption, commit-after-apply, operator-intent preservation, durable pending agent intent, and eventual stale-frame quarantine/ACK. A matching exhaustive Rust `SimWorld` model explores all reachable action schedules in normal tests. JetBrains plugin `0.2.268` carries the lineaged transport.

## 0.34.155

- **JetBrains Compact Exchange no longer wakes unrelated document recovery (JetBrains plugin 0.2.267).** The action saves only its selected Markdown document before routing instead of calling `saveAllDocuments()`, so a retained sample-portal ACK recovery cannot make a sample-app compact fail with the sample portal's delivery error. A target-save failure is logged and compaction continues from live editor/CRDT authority.

## 0.34.154

- **Corrupted editor projections recover without sacrificing operator intent.** When agent-written CRDT content duplicates an exchange or boundary, reconnect reconstructs the operator cut from durable editor ops over the expected base, validates it, and replays agent intents; buffer-only directives such as `queue: stop` survive without a force-disk reset.
- **Deferred agent mutations are now an ordered, content-bearing journal.** Every pending change is replayed in order and ACKs settle only the proven prefix, so a later backlog mark cannot erase an earlier backlog add and an already-visible unacknowledged response cannot erase newer operator text.
- **Multi-head free-text consume is one monotonic transaction.** `queue consume --count N` plans one leading free-text prefix from one editor-authoritative cut and writes once, stopping before id-backed work; a stale snapshot is rebased as a projection instead of vetoing progress or repeatedly selecting the same head.
- **Queue deletions and stop controls remain monotonic across snapshot lag.** Tombstones retain the last observed editor-authoritative id frontier, preventing a newly-added-then-deleted queue item from being resurrected by backlog mirroring, and explicit `stop` now dominates conflicting stale `go` state.

## 0.34.153

- **Editor-delivery recovery now distinguishes a live IDE buffer from a live plugin worker.** Maintained JetBrains and VS Code plugins heartbeat from their delivery execution context; stale workers are removed from routing while their unsaved buffers remain authoritative, the exact response target stays retained, and safe-checkpoint hot-reexec breaks the former open-cycle/ACK circular wait without force-disk recovery.
- **Empty replica sets no longer satisfy delivery convergence.** A pending target with zero live replicas requires an exact durable visible-write receipt, so editor disappearance cannot vacuously authorize settlement or disk projection.
- **Transient tmux observation failures no longer erase actor state.** `tmux-router` exposes a fallible live-pane snapshot and destructive registry prune preserves all records when that snapshot fails.

## 0.34.152

- **Supervisor hot-reexec transport state no longer poisons the preserved capability-proof fingerprint.** The replacement now consumes the child-pid, PTY-fd, and preserved-contract handoff before resolving the managed child environment, and launch-spec assembly defensively strips those supervisor-only variables as well. Repeated reexecs therefore compute the same exact child contract and reuse the proven gate instead of running another network probe merely because a file descriptor or prior fingerprint changed.

## 0.34.151

- **Unrestricted Codex network proof no longer spends a second model turn to execute a fixed shell command.** When the exact launch args select `danger-full-access`, agent-doc runs the existing DNS/HTTPS probe command directly with the resolved child environment and bounded process-group cleanup, recording `network_probe=unrestricted_shell_dns_https`. There is no Codex sandbox in that mode for a nested `codex exec` process to prove, and the old inference-backed probe repeatedly hit its 45-second cancellation deadline even while the live Codex child and the identical direct check had working network. Sandboxed Codex and OpenCode launches retain their managed-child proofs.

## 0.34.150

- **Supervisor hot-reexec no longer launches redundant managed capability probes for the child it preserves.** A proven gate now hands an exact hash of the harness command, launch args, resolved environment, network requirement, SSH targets, and writable-root contract to the replacement binary. The replacement reuses it only when that same child survived and the current contract matches, records a new post-start `status=proven source=reexec_preserved_child` event, and otherwise runs the bounded probe normally. This prevents repeated 45-second Codex proof timeouts and retries from turning a healthy adopted session `blocked` after every local install/recycle while retaining fail-closed behavior for a fresh child or changed capability contract.

## 0.34.149

- **Concurrent repair no longer retires a captured response whose editor delivery is still in flight.** Stale-capture policy now treats the retained document-write slot as durable ownership evidence, so a reused response heading cannot misclassify the exact active capture as a superseded captured-only orphan.
- **`session-check` repairs the historical false-retirement shape without recapture.** When the identical discarded capture and abandoned cycle still match by capture ID and response hash, and the full response body is proven in a converged editor/canonical/disk cut, recovery reactivates that same capture/cycle, refreshes the snapshot, and commits it exactly once. Genuinely superseded or incomplete captures remain retired.
- **PlusCal/TLA+ covers concurrent repair during retained delivery.** `CloseoutChurn.tla` now requires that an in-flight retained write cannot lose its capture and that the repair attempt eventually preserves the same capture through settlement.

## 0.34.148

- **`session-check` now finishes a retained capture after replacement-replica bootstrap.** When JetBrains has installed and saved the exact retained canonical target, replacement registration legitimately has an empty ACK queue. The check recognizes exact canonical/target/disk convergence, retires the historical deferred slot, refreshes the response snapshot, and commits the same durable capture without asking the agent to finalize again.
- **Retained-closeout guidance is single-command and non-recursive.** It now says to retry only `session-check`; it never suggests another finalize/write payload or force-disk recovery. The pending-capture guard also accepts a named same-turn completion such as ``#item, now done`` without masking unrelated future-work recommendations.
- **PlusCal/TLA+ covers the reconnect settlement.** `CloseoutChurn.tla` proves that an empty-ACK replacement bootstrap eventually clears the retained slot, refreshes the capture snapshot, and commits with exactly one response/backlog application.

## 0.34.147

- **A live editor advance after CRDT delivery proof no longer turns a successful capture into an agent retry.** The document authority internally rebases the same retained projection intent over the newer canonical cut, repeats the ACK barrier, and preserves operator text while applying the response and any backlog mutation exactly once. If the bounded foreground retry cannot settle, `session-check` reports binary-owned continuation and explicitly forbids recapture, another finalize/write, or force-disk recovery.
- **PlusCal/TLA+ covers the post-proof race.** `CloseoutChurn.tla` now checks the transient editor advance, eventual same-intent rebase, exact-once response/backlog application, and the rule that commit cannot precede the rebased delivery proof.

## 0.34.146

- **Retained JetBrains closeouts no longer overload their own ACK path.** CRDT bootstrap/delta operations now use a compact, UTF-8-safe zstd/MessagePack envelope over the existing plugin string FFI seam while retaining legacy-JSON decode compatibility. Foreground canonical-text proof gets a five-second controller budget, idle revision polling remains at 750 ms, and repeated pressure signals are cooldown-coalesced. This is a native-library/control-plane update and does not change the JetBrains Kotlin ABI.
- **Closeout recovery is exact-once across the churn edges.** Strict template responses without exchange patch markers fail before capture or mutation; stale-cycle dedupe requires the full response body rather than a reused heading; retained pending writes suppress the false direct-patchback/recapture diagnosis; and duplicate live queue occurrences no longer prevent independently matching snapshot done-id updates.
- **PlusCal/TLA+ exhaustively covers the combined failure sequence.** `CloseoutChurn.tla` checks compact-payload service, eventual ACK observation, full-body response identity, one capture/copy, partial queue-snapshot progress, strict unmarked rejection, and bounded pressure-marker writes. Alloy is intentionally not added because these regressions are temporal ordering/liveness properties rather than arbitrary-size relational topology constraints.

## 0.34.145

- **JetBrains no longer recursively refreshes project content after an external agent-doc commit (JetBrains plugin 0.2.264).** VCS signals now dirty only the VCS scope, so an unrelated unsaved agent-document buffer is never pushed into IntelliJ's memory-vs-disk resolver. Remote CRDT delivery refreshes only its clean target file before mutation, revalidates that the document stayed clean, and fails closed to exact-editor-baseline retry when operator memory is unsaved. This also updates the target `VirtualFile` modification stamp before `saveDocument`, eliminating byte-equal-but-stamp-stale conflict dialogs.

## 0.34.143

- **JetBrains tab selection now swaps the matching tmux pane on the same editor side.** Automatic exact-visible sync was submitted correctly but then preserved the stale layout because the SQLite migration made safe-passive ownership proof skip every authoritative actor lookup. Controller-owned sync now consumes the authoritative actor row as a local SQLite read, allowing the existing no-autostart atomic swap path to run; standalone passive CLI sync still avoids a nested Project Controller RPC. Unit and TLA+ regression coverage keep the two proof modes distinct, prove the visible/stashed pane partition, and require controller-local sync liveness without permitting autostart.

## 0.34.142

- **`make install` now converges every existing JetBrains agent-doc package instead of refreshing only the CLI/native library.** The local package is rebuilt, installed non-interactively into each IDE that already has agent-doc, and verified against the exact build version; failures stop the install, while the activation message correctly requires an IDE restart.
- **The plugin CLI has an explicit all-installations coherence path.** `agent-doc plugin install jetbrains --local --all-installed` updates only existing agent-doc installations, avoids ambiguous single-IDE selection, and fails closed when no matching installation exists.
- **PlusCal/TLA+ joins Lean and SimWorld in `make check`.** A pinned, checksum-verified TLC runner translates the concurrent install/editor/closeout algorithm and checks response uniqueness, steering preservation, generation separation/adoption, bounded ACK recovery, and eventual commit.

## 0.34.141

- **Replay/ACK recovery recognizes a plugin updated after the turn started.** The repair boundary re-reads the latest live editor registration and explicitly supersedes a stale preflight generation. Diagnostics now distinguish plugin-package code from `libagent_doc`: `admin reload-lib` refreshes only the native cdylib and is no longer suggested as a substitute for installing/restarting an older JetBrains or VS Code plugin.
- **Retained responses survive monotonic authoritative drift without an operator reset.** Repair keeps typed editor/disk authority, proves that the current cut preserves every captured-baseline line in order, rebases the matching open capture, and replays exactly once over later steering. A drifted `WriteApplied` capture is no longer discarded merely because its hashes changed; non-monotonic edits and superseding answers still fail closed.
- **Console steering has a binary-owned persistence path during closeout.** `exchange add-prompt` now preserves live editor authority and the active capture instead of electing force-disk while rebasing. Harness instructions require persisting a new console prompt before resuming the older closeout, preventing a retry wedge from leaving the follow-up only in transcript memory.
- **Lean and SimWorld cover the interaction.** Lean proves live registered generation adoption, native-reload/plugin-generation separation, safe authoritative rebase, and preservation of retained response/steering tokens. Exhaustive SimWorld schedules cover mid-turn plugin update, native-only reload, later steering, generation revalidation, safe/conflicting rebase, exact-once replay, and commit.

## 0.34.140

- **Compact Exchange closeout recognizes exact archived capture materialization.** The shared capture policy permits an open captured cycle to close when its staged compact document references an archive containing the exact captured response, while missing or unrelated evidence still fails closed. This removes the false already-committed refusal after JetBrains compaction without weakening response-loss protection.
- **A retained canonical write no longer reports a failed finalize solely because editor delivery ACK is delayed (JetBrains plugin 0.2.263).** Once CRDT authority still holds the exact target and the asynchronous editor-recovery signal has been accepted, the foreground command completes as retained-for-delivery; canonical loss or a missing recovery signal still blocks. JetBrains external events now respect the single-flight ACK backoff, preventing event storms from bypassing the retry gate.
- **Controller commit handoff uses the stream that proved liveness.** Commit no longer probes one socket and reconnects through a recycle race, and stale socket reaping is covered directly. Repeated response attempts converge through the existing atomic response-cell supersession path, preserving intervening prompts and leaving one exact final response.
- **The failure interactions are executable and proved.** Exhaustive SimWorld schedules cover compact/finalize overlap, delayed ACK, external retry events, controller recycle, stale sockets, repeated finalize, and canonical loss. Lean proves the exact-archive closeout condition, retained-delivery safety, retry admission, and same-stream delegation invariant.

## 0.34.139

- **The Stop hook resumes a retained editor-convergence capture even when its route-owned supervisor still runs an older inode.** The hook invokes the freshly installed binary's keyed strict-repair path before returning the status-only block. This is a version-independent liveness boundary: it preserves editor authority, never recaptures the response, and never elects force-disk; an absent capture or unresolved convergence still fails closed with the original response retained.

## 0.34.138

- **A Stop-hook-blocked turn no longer starves its binary-owned closeout worker.** Once a response has a durable capture identity, the supervisor may resume it while the harness actor is still active; editor typing, IPC in-flight work, controller pressure, urgent maintenance, and the keyed single-flight worker remain fail-closed gates. This removes the circular wait where the actor could not become idle because Stop required the very convergence that the idle-only worker was waiting to perform.

## 0.34.137

- **Captured closeout status is binary-owned end to end.** `editor_convergence_required` no longer records or prints a manual `write --commit` recovery command, and terminal authority/disk divergence now schedules replica settlement while declaring `session-check` status-only. Regression tests reject guidance that would stack another finalize/write operation or elect force-disk over the retained capture.

## 0.34.136

- **Harness skill installs preserve binary-owned finalize recovery.** The bundled `SKILL.md` is now the same source of truth as the development Claude skill, and an install-time regression guard prevents OpenCode, Codex, Claude, or Cursor from reverting to agent-driven settle/retry churn.

## 0.34.135

- **Finalize recovery is binary-owned and keyed by durable capture identity.** The repair command, supervisor worker, and Codex Stop hook resume one captured `(cycle, capture, response)` operation with bounded backoff and exact-once commit semantics. Retryable ACK/backpressure/CAS states no longer ask the agent to re-run finalize, kill controllers, or stack response cells; timed-out owners reap only proven descendants and classify intentional exit 144 as cancellation.
- **Every disk change under an open editor is a separate pending decision (JetBrains plugin 0.2.262; VS Code 0.2.52).** The file watcher retains exact external bytes in an independent Lazily slot without replacing the pending response or mutating CRDT. Exact accept/reload propagates the visible buffer before settlement; a newer edit clears it; an exact editor save-flush overrides and clears it; final editor close falls back to disk. Multiple reconnecting replicas remain mutation-free until an editor cut is proven.
- **JetBrains and VS Code share the same reconnect/settlement FFI.** Both adapters can install only a binary-proven candidate, retire the stale forwarder, seed/register from the visible editor buffer, and settle after propagation. VS Code keeps this work off the text-change UI path, and cross-editor static guards pin parity.
- **The realtime model and Lean kernel cover external disk decisions and expose the Lazily gap.** Generated traces now include external writes, accept, edit, save, and close; Lean proves pending disk writes cannot mutate live editor/canonical state and that save/edit/close clear pending safely. `PendingExternalDiskDecision` is a typed Lazily capability requirement rather than agent-doc-only hidden behavior.
- **Harness policy is explicit:** OpenCode/Codex/Claude installs receive the current skill, local verification precedes closeout, and CI is inspected once; red relevant CI is fixed, while green/queued/in-progress CI is recorded without polling or waiting.

## 0.34.134

- **Live editor authority survives stale replicas and Save echoes (JetBrains plugin 0.2.261; VS Code 0.2.51).** Both adapters verify the native replica against the captured shadow before forwarding a local delta. A mismatch adopts the exact structurally-valid editor once and atomically re-registers instead of projecting stale canonical text over an unsaved edit; saving reflects the same semantic queue identity without replay or duplication.
- **Realtime, IPC, and cycle behavior now share an executable fault model.** Deterministic generated traces cover retained delivery, duplicate/drop/delay, controller recycle, editor interruption, queue admission, durability-only Save, projection, ACK, and commit. Typed capability gaps distinguish missing Lazily primitives from implementation mismatches, and the Lean safety kernel proves idempotent Save, set-like queue multiplicity, fenced editor adoption, and exact-visible ACK requirements.
- **Rejected template canonicals recover symmetrically in JetBrains and VS Code.** A valid unchanged editor baseline is adopted through a bounded text transition and atomic replica replacement; invalid, advanced, missing, or recovery-in-flight editor state remains fail-closed with bounded retry.

## 0.34.133

- **Visible editor proof now settles JetBrains CRDT deliveries (JetBrains plugin 0.2.260).** Remote delivery ACK eligibility is based on the exact visible editor projection, independently of whether disk persistence is deliberately deferred. Genuine editor races back off instead of immediately re-pulling and decoding the same large delivery.

## 0.34.132

- **Editor closeout delivery is single-flight and reconnect-safe (JetBrains plugin 0.2.259).** A retained ACK frontier blocks another decode of the same remote delivery, controller transport loss triggers bounded replica re-registration, and a reconnecting live editor fences a previously-authorized force-disk mutation before disk can change.
- **Commit-seam cleanup no longer asks the controller for a free-text queue head.** The strike reads disk inside its serialized transaction, and stale `.overlay.yrs` model sidecars cannot become a baseline unless an explicit markdown baseline was supplied, preventing phantom goals and controller-lookup hangs.
- **Shared controller pressure now quiets every idle watcher in the project.** One `controller_model_backpressure` event establishes a project-wide cooldown with deterministic recovery jitter, while exact response cells materialized by reconnect merges collapse idempotently instead of duplicating the answer.
- **Timed-out agent runs terminate and reap their whole process group.** Background preflight descendants cannot survive their owner timeout to contend with the next run, and the diagnostic classifies the resulting termination as cancellation.
- **Release and plugin automation is deterministic.** JetBrains install/update accepts `--plugins-dir` and fails clearly instead of blocking ambiguous non-interactive jobs; PyPI publishing builds a zig-backed `manylinux_2_17` wheel; the managed Claude, Codex, OpenCode, and Cursor skills carry the same backpressure recovery rule.

## 0.34.131

- **Realtime document edits are now first-class turn state in both editor mirrors.** Every accepted CRDT replica update during an open closeout cycle is compared with the saved turn baseline, reduced to the aggregate steering kind/count/preview/verbatim payload, and appended as an idempotent state-backbone fact. State-wire snapshots and deltas carry that same cycle-scoped projection, so JetBrains and VS Code render live steering from the authoritative mirror instead of losing it when the direct FFI path is bypassed.
- **Operator settlement is explicit without submitting incomplete edits.** The realtime aggregate remains attached to the active cycle until commit or abandonment, while `Run Agent Doc` continues through the serialized project-controller route and queues behind an active turn. An operator can finish editing and invoke the action once to hand off the settled document; the running turn and session-check both retain the full aggregate steering evidence.

## 0.34.130

- **JetBrains `Run Agent Doc` can provision nested-project documents into a shared root tmux window without manual pane deletion.** When the target project's registry has no live anchor but the target session already has an `agent-doc` window, route startup now inspects every visible pane's process-tree document owner. It uses the requested left/right edge pane only as a split anchor when all owners are proven and different from the requested document; an unknown owner or an unregistered same-document owner still fails closed. This fixes the live `sampleorders.md` refusal where the nested `sample-app` registry was stale while the shared window's `%14`/`%15` panes safely owned root-project documents.
- **Active typing cannot age out while a current-document authority query is blocked.** The resolver now snapshots the typing indicator before querying the controller/CRDT model and checks it again before idle disk fallback. If either observation is active, it fails closed, preventing controller/SQLite contention from turning a read that began during an operator edit into disk authority. This also removes the parallel-suite race in the missing-model fallback regression.

## 0.34.129

- **Stale response captures now settle when only the live document carries an exchange prompt prefix.** Semantic response materialization strips a transient `❯ ` prefix symmetrically from captured and visible lines after removing transport wrappers and control markers. An already-visible response therefore refreshes the stale capture baseline and reaches committed closeout instead of retrying, while an absent or rewritten response still fails closed.
- **A response-internal prompt prefix no longer becomes an unresolved prompt after its response boundary.** Exchange-tail classification now accepts ambiguous `❯ ` prose only when the latest committed capture is fully materialized and contains that line, while retaining prompt-only protection for unmatched text before or after the boundary.
- **Session-check no longer fights the editor over a healthy post-commit `(HEAD)` annotation.** When live authority is exactly committed Git content plus transient response HEAD markers, closeout accepts the editor projection without writing; stale guard or boundary lineage still takes the existing CRDT settlement path.

## 0.34.128

- **Whole-document replay repair now recognizes transiently annotated monotonic projections.** The doubled-document live corruption contained one current projection plus one stale projection, but the current copy had editor-facing ` (HEAD)` response annotations and exchange prompt-prefix normalization that the stale copy lacked. Those are already shared semantic-equivalence rules, not operator-content differences. Replay coalescing now compares the code-block-aware transient-heading normalization and exchange-scoped prompt-prefix normalization while retaining the original current superset bytes. Divergent/reordered content still fails closed. A regression combines both transient differences, and the live 99,367-byte corruption artifact is proven to coalesce to one structurally complete projection without writing it.

## 0.34.127

- **Structural response corruption now repairs from bounded Lazily history instead of a whole-document sidecar fallback.** The observed failure shape—a long unchanged prefix followed by partial prior-response lines, a deleted component middle, and a duplicated suffix—cannot be produced by the response-cell tree operation; it identifies an older non-atomic whole-document replacement/rebootstrap materialization. Repair now reads a newest-first indexed window of content-bearing `ResponseCaptured` facts, requires the malformed live buffer to be fully explainable by one valid historical baseline plus its response, rejects any novel operator text, reconstructs the valid response tree, and CAS-publishes it through editor authority. The active capture is then rebased in Lazily first, with JSON capture state updated only as a compatibility projection. The merge entry point also canonicalizes exact/monotonic complete-document replays before structural reconciliation and rejects two divergent complete projections instead of concatenating them through the whole-document fallback. Regression coverage reproduces the cross-component splice, proves unfamiliar operator text fails closed, coalesces a safe replay, and rejects an ambiguous replay.
- **Historical committed-capture repair now respects a semantically materialized response.** Capture transport may retain wrapper/control lines such as `<!-- patch:exchange -->` and `<!-- no-pending-capture -->` that the visible document correctly omits. Recovery now compares the materialized response projection, not the byte-exact transport body, both when selecting a historical capture and before removing partial response lines. A restored response therefore cannot be deleted merely because its capture has transport-only markers; regressions cover that restored-response failure shape.
- **JetBrains remote CRDT delivery no longer leaves replica-only dirty buffers that arm File Cache Conflict (plugin 0.2.257).** Before acknowledging a remote delivery, the plugin now requires raw disk to equal either the guarded editor baseline or the converged target, applies the minimal Document edit, and saves it to a clean IntelliJ state. Novel external disk text rejects the delivery without mutation or overwrite. This closes the conflict window created when a non-operator remote apply remained unsaved while the disk projection advanced independently.
- **JetBrains tab navigation focuses tmux after passive layout rescue (plugin 0.2.257).** A selection still submits the immediate focus fast path, then submits guarded `sync_tmux_layout --no-autostart`; after sync admission it now queues one latest-generation focus command behind that sync. A pane initially parked in stash can therefore be rescued and selected in controller order instead of returning `actor_pane_not_visible` once and leaving tmux on the prior document.

## 0.34.126

- **A restarted editor process can no longer leave an unreachable CRDT member holding the response commit barrier open.** The relay IO boundary records recognized JetBrains and VS Code replica identities with their encoded process ids. On replacement registration it conservatively proves prior editor processes dead, removes only those memberships before the new canonical bootstrap, and logs the prune count. Opaque, legacy, and test identities are never inferred or reaped. The update-driven controller-recycle auto-heal records the same metadata, so every registration path preserves the invariant without adding an idle poll. Coverage queues a complete response to a simulated dead member, proves the barrier is initially blocked, registers its replacement, and proves the stale member is gone and delivery converged.

## 0.34.125

- **Project-root incarnation checks are safe on filesystems that immediately reuse inode numbers.** Detached controller startup retains an open handle to the caller's original directory until all startup checks finish, preventing the deleted inode from being recycled into a same-path replacement that could otherwise pass an inode-only comparison.
- **JetBrains forced replica refreshes obey IntelliJ model read access (plugin 0.2.256).** Both delivery-ack recovery and native-library reload capture open `VirtualFile`/`Document` state inside a bounded read action, then perform logging and asynchronous CRDT re-registration after releasing it. This removes the `ThreadingAssertions` exception emitted by 0.2.255 from the background CRDT and library-reload watcher threads without moving full replica work onto the EDT or adding a poller.

## 0.34.124

- **Idle queue observation is change-driven.** Editor-attached supervisors compare a compact CRDT canonical state vector plus liveness/convergence state before materializing markdown; detached or deliberately controller-suppressed sessions compare disk metadata. Full canonical reads, hashing, serialization, and queue parsing now run only after that lazy revision changes, after probe failure, or on a 60-second safety reconciliation.
- **The controller exposes a log-silent compact revision RPC.** `crdt_revision` never flushes a commit barrier or materializes the document, so the five-second quiescent liveness pass remains cheap while editor changes still invalidate the queue projection promptly.

## 0.34.123

- **Idle means idle even when an editor remains attached or a queue head is stably blocked.** The 500 ms watch tick now performs only local binary/zombie liveness probes between five-second reconciliation passes; pane inspection and full CRDT text/controller reads no longer happen twice per second merely because the document is open.
- **A stale supervisor hot-reloads at the first handler-safe checkpoint in every turn stage.** Once no supervisor IPC receipt is in flight, `execve` preserves the live harness child and durable cycle checkpoint instead of waiting for an idle prompt or a closed agent-doc cycle.

## 0.34.122

- **Quiescent supervisors no longer execute the full idle reconciliation pipeline twice per second.** The idle watch keeps its prompt authoritative queue-head poll and installed-binary freshness check at 500ms, but collapses pane probes and the burst of controller-backed projection reads to a five-second liveness pass when there is no head or the same head has already settled. New work and stale binaries bypass the throttle immediately, preserving prompt dispatch and automatic recycling while removing the idle controller request storm.
- **Detached controllers cannot resurrect deleted temporary project roots.** Controller startup captures the caller's project-root incarnation before creating bootstrap state and verifies it again before and after publishing the socket. A temp root deleted during detached startup now fails closed instead of being recreated as a permanent orphan controller.
- **In-place supervisor upgrades reap controller zombies left by earlier reaper threads.** The idle watch periodically reaps only already-exited Linux children whose process name is exactly `agent-doc`; live harness children are excluded. This cleans up historical controller children whose dedicated waiter thread was destroyed by `execve` while retaining the per-child waiter for new launches.

## 0.34.121

- **Forced IDEA reconnects now make the visible editor and replacement replica one atomic target.** When queue consumption or another post-response mutation advances the retained target, JetBrains compare-and-swaps the reconciled bytes into the open document under the non-operator mutation guard before it registers and swaps the replacement replica. Editor drift and pending local work fail closed, so a target-only replica can no longer ACK while stale IDEA text later replays an older boundary.
- **Every deferred realtime merge keeps the newest target's singleton boundary.** Settled-operator rebases, chained deferred targets, and reconnect merges now canonicalize boundary control state after CRDT composition. Regression coverage reproduces the live queue-consume reconnect that previously retained a stale editor boundary, drops all older boundary identities, and proves boundary-free or malformed targets cannot mint or strip protocol markers.

## 0.34.120

- **ACK recovery can no longer resurrect the response target it just superseded.** When a response-cell retry is already the live CP authority, deferred-write retention now replaces the older target and rebases reconnect lineage on that exact failed-ACK editor cut instead of three-way-merging the stale assistant response back in. Reconnect returns the newest complete response directly for an unchanged buffer and semantically preserves later `❯` operator prompts with one boundary.
- **The live IDEA failure is covered at both persistence layers.** Regression coverage reproduces an older deferred response, a newer canonical response, the forced replica reconnect lookup, and an operator prompt appended after the failed ACK; it proves the newest target remains retained and the stale response cannot reappear.

## 0.34.119

- **A complete retry now supersedes older uncommitted response tails atomically.** Before adding a response cell, the controller compares the live exchange with `HEAD`, anchors at the last unchanged committed response, removes only later assistant-response nodes, preserves every operator prompt, and emits the latest complete response with one terminal boundary. Interrupted partial/full variants no longer accumulate and do not require `agent-doc repair`.
- **Response-cell materialization proof is semantic across transient heading markers.** The post-ACK gate now uses the existing normalized response replay proof, so a terminal ` (HEAD)` annotation cannot make an acknowledged response look absent and strand the cycle in `write_applied`.
- **The parent-submodule Stop-hook regression is self-contained in clean CI.** Its fixture now creates the parent agent-doc project root explicitly instead of passing only when an ambient `/tmp/.agent-doc` happens to exist.

## 0.34.118

- **Automatic controller recycling now reconnects editor replicas end to end at every turn stage.** The common stale-supervisor/controller recycle operation now emits the existing forced-reregister event for preflight, write/finalize, commit, and session-check callers alike. Both zero-replica timing windows retain the complete target, refuse disk fallback, and make JetBrains or VS Code replace cached open-document forwarders against the new controller before the identical closeout retry.
- **Native-library reload broadcasts refresh open documents, not only bindings.** Both editor plugins force-refresh every open markdown replica after a reload broadcast; focused source-contract tests distinguish the broadcast handler from the already-correct socket handler.
- **Release history identifies 0.34.116 correctly.** The accidentally duplicated `0.34.117` heading for the preceding boundary-convergence release is restored to `0.34.116`.

## 0.34.117

- **A late editor baseline can no longer roll back an already-committed response.** Repair recognizes the exact capture baseline as a stale authority regression only when the corresponding committed capture is present in `HEAD`, then restores that committed projection through authority CAS, advances the snapshot, and clears superseded deferred intents. Preflight now fails closed on every response-recovery error instead of continuing into a generic commit that could persist the regressed baseline.
- **Preflight-discovered stale routes recycle automatically.** A queue-convergence send or receipt failure discovered after the entry-stage stale probe now schedules the same safe-boundary editor/supervisor recycle used by the other turn stages, so finalize does not inherit a newly wedged route.
- **Session-document commits exclude unrelated staged work.** The commit transaction builds the snapshot-selected document blob in a private index rooted at the observed `HEAD`, creates the commit tree, and advances `HEAD` with compare-and-swap retries. Only the owned document's real-index entry is aligned afterward; every foreign staged entry remains staged and outside the commit.
- **Committed response recovery is no longer an operator repair workflow.** Regression coverage reproduces the stale-authority rollback, validates automatic restoration to one terminal boundary, and proves that pre-staged foreign files cannot be swept into response commits.

## 0.34.116

- **Post-commit boundary reposition now converges editor, disk, and Git.** The CRDT reposition path previously waited for the editor ACK and let Git adopt the new singleton boundary, but returned before materializing those acknowledged bytes to the working tree. The same authority-CAS write now crosses the disk projection barrier before success, preventing an immediately dirty session, repeated boundary drift, and a terminal `HEAD`/authority versus disk split.
- **Document-model merge no longer duplicates old and new response boundaries.** Boundary markers are transaction control state, so closeout now canonicalizes a merge to the response branch's boundary ID at the terminal exchange position before the mandatory integrity gate. Live-buffer reconciliation can preserve concurrent text without producing a fragmented document or requiring repair.
- **Historical post-commit projection splits recover without repair.** When `HEAD` and canonical editor authority already contain the same committed response, disk has the same semantic document with only older transient markers, and no conflicting deferred lineage exists, `session-check` now crosses the authority/ACK/disk barrier and restores exact equality automatically. Any semantic difference or mismatched retained intent still fails closed.
- **Incomplete editor receipts no longer preserve partial response fragments for manual repair.** The file-IPC closeout model now requires a rejected partial materialization to restore the exact converged pre-write projection. It may retain the complete response capture for binary retry, but neither a fragment nor an unproven full fallback may remain in the document or reach the snapshot/commit boundary.

## 0.34.115

- **Retained committed responses resume automatically after editor reattach.** If closeout reached exact committed CRDT authority while the editor owner temporarily had zero registered replicas, `session-check` now recognizes the exact `HEAD` target plus its content-bearing deferred base, re-enters the ordinary CRDT delivery/ACK barrier after replica registration, materializes disk, and clears the entire deferred lineage. The resumption fails closed if the live editor or disk has advanced beyond that retained base; it never asks an agent to repair or reconstruct the response.

## 0.34.114

- **Committed transient tails self-heal without repair.** When canonical authority and disk agree on an older deferred projection that differs from committed `HEAD` only by agent-doc transient markers, `session-check` now clears every obsolete deferred intent, CAS-restores the exact committed bytes through editor authority, and proves `HEAD == authority == disk` before success. This removes leaked `no-pending-capture` markers and stale boundary ids automatically while preserving genuine post-commit operator edits.

## 0.34.113

- **Multi-stage repair can no longer replay an intermediate document.** After exact canonical and disk proof, repair settles the entire superseded deferred-intent stack and retains only the newest reconnect target while preserving the original editor cut as its merge base. A later marker cleanup or boundary normalization therefore cannot uncover and project an older response image.
- **Zero-replica editor authority is now an automatic supervisor-recovery condition.** Writes retain the complete target, request a safe supervisor recycle, and direct the caller to retry the same binary-owned finalize path rather than run response repair or force disk. Turn-stage staleness checks also classify a reliably-open editor with zero registered relay members as stale even when its binary inode is current.
- **`session-check` now proves terminal authority/disk equality.** It refuses success when canonical CRDT text and the working-tree projection differ, records an automatic stale-editor-replica recycle request, and reports both content hashes. A committed cycle can no longer hide a late reconnect replay behind a clean lifecycle projection.

## 0.34.112

- **Explicit repair completes retained zero-replica writes.** When an editor owner exists but has no registered relay replica, ordinary writes still fail closed without touching disk. If an explicit repair has already retained its exact CAS target in CRDT + Lazily state, repair now performs an audited force-disk projection of only that target, verifies canonical and disk equality, replaces the deferred write with force-disk reconnect lineage, and then continues snapshot/commit closeout. A retry can no longer say “No pending response” while the session file remains fragmented.

## 0.34.111

- **Repair is authoritative-CAS, not a stale whole-buffer merge.** Every repair mutation compares against the exact realtime document image it analyzed, then proves the resulting CRDT authority and disk projection are byte-identical to the intended repaired image before publishing a snapshot or reporting success. A stale IDE/disk projection can no longer resurrect fragmented responses or duplicate `agent:boundary` markers after repair.
- **Stale controller replacement is directional and works after reinstall.** Controller requests carry the caller's full binary identity. A stale controller accepts replacement only when the caller proves a newer release or a newer same-version binary mtime, while an older caller cannot evict a newer controller. This closes the stale-supervisor recycle refusal that could wedge every later stage of a turn.

## 0.34.110

- **Session responses are final-only transactions.** Streaming and orchestration keep partial chunks outside the document, bare/non-committing session writes fail before input or mutation, and one complete payload owns response placement, queue/backlog changes, snapshot publication, and commit.
- **Malformed documents can no longer look clean or be laundered through compact.** Mandatory integrity checks reject unbalanced component trees and invalid exchange-boundary shapes even when dialect lint is off; preflight, session-check, stream, and compact fail closed, while semantic no-op compact creates no tag, archive, or commit.
- **Healthy streaming no longer depends on repair.** Partial checkpoints remain recovery-only sidecars, final stream commit failures propagate instead of printing a skipped-commit success, and exact final-response proof—not a prefix or heading—owns `AlreadyApplied` recovery.
- **A proven-stale supervisor now recycles automatically from every turn stage.** Preflight, generation, stream, finalize/write, commit, compact, session-check, and closeout evidence all write the same idempotent safe-boundary recycle request, even when proactive auto-recycle was disabled; the old operator-facing stale marker is only removed from documents and is never inserted again.
- **Exchange boundaries are a hard singleton invariant.** Inline and repeated `agent:boundary` markers fail the mandatory integrity gate, final placement removes every non-code marker before appending one standalone boundary, and explicit legacy recovery reconstructs the fragmented prompt/response ordering left by historical partial patchback before committing it.

## 0.34.109

- **JetBrains recovery now performs atomic replica replacement and structurally safe response replay.** Forced refresh registers a distinct replacement before retiring the cached member, failed registration preserves the old forwarder, and replica membership no longer mutates reliable-sync document-open authority. CP refusal reasons such as `detached_authority` are surfaced directly. Live-prompt recovery now uses the typed `IpcLivePromptDriftState` enum; malformed component targets are rejected or rebuilt from a structurally valid full-document target before CRDT/Lazily delivery, preventing the response double-materialization that produced File Cache Conflict, partial closeout, and ACK churn. Rust 0.34.109; JetBrains plugin 0.2.254.
- **Lost JetBrains delivery ACKs recover without controller recycling or operator prompts.** Successful editor applies retain an idempotent ACK replay ledger keyed by patch/generation and always re-prove the current buffer hash. The binary wakes replay every 250 ms, requests one targeted replica re-registration after two seconds, and bounds the synchronous wait at eight seconds while the full response remains in CRDT + Lazily deferred-write state. A later same-target finalize is a no-op, and final-hash ACKs retire a coalesced pending prefix.
- **Document convergence and recovery states are typed enums.** CRDT wait phases, replica-event reasons, and `DocumentWriteDeferred` causes no longer flow through free-text comparisons. Stable snake-case tokens remain only at JSON/log persistence edges, with legacy deferred-reason deserialization retained for existing controller databases.
- **JetBrains controller recycle recovery now performs a real replica re-registration.** A forced refresh retires the cached CRDT forwarder before issuing `REGISTER`; native-library reload broadcasts actively refresh every open Markdown document instead of merely swapping the cdylib. Re-registration consults Lazily deferred-write state first, restoring the canonical target for a stale clean buffer and component-merging any later unsaved operator edits.
- **Explicit `--force-disk` writes retain safe editor-reconnect lineage.** Before disk changes, the binary records the complete pre-write base and target in a content-bearing `DocumentWriteDeferred` fact. Disk/commit may complete without waiting for a missing relay member, while a later editor reconnect restores or merges from that durable lineage—never from Git HEAD.
- **Compact Exchange composes concurrent recovery state before archiving.** Active captured responses missing from the current projection are folded into the compact model, and successive deferred targets are component-merged instead of replacing one another. Zero-replica idle polling backs off to the disk queue projection between bounded membership probes, eliminating controller/event churn while the plugin re-registers.

## 0.34.107

- **Captured-response recovery is content-bearing Lazily state and never restores Git HEAD.** Final capture facts now retain the complete editor-visible baseline alongside the response body. Repair can remove only multi-line, multiplicity-proven partial response fragments—even when JetBrains delivered them out of order—publish the reconciled buffer through document authority, and replay/commit the full response once. Legacy hash-only captures may use a hash-matching HEAD solely as a historical comparison anchor and immediately fortify the capture projection with those bytes. Raw template captures are serialized as explicit exchange patches before strict replay, and commit-boundary recovery refuses to mark a capture committed unless its response is actually materialized in HEAD.
- **Editor closeout authority is now content-bearing, durable Lazily state.** JetBrains `already_applied` recovery no longer reconstructs a response from matching hashes or falls through to file IPC after a generic timeout. Visible-write receipts persist the complete acknowledged buffer, legacy hash-only receipts get one bounded current-buffer publication that upgrades the same patch fact, and a stale receipt superseded by a newer live/worktree cut fails closed without offering a Git `HEAD` restore. If an editor-owned document has no registered replica, the binary retains the complete intended target in a Lazily `DocumentWriteDeferred` fact, returns promptly, and performs no disk projection; replica bootstrap/publication restores and proves the target before commit. Relay mutations with zero delivery targets are no longer reported as converged. This puts response finalize, File Cache Conflict recovery, and Compact Exchange on one editor-visible lineage and prevents a committed response/compaction from leaving stale or partial text uncommitted in the editor.

## 0.34.106

- **JetBrains `Compact Exchange` preserves the compacted editor lineage after commit.** The controller now carries the intentional live/committed split through buffer flush, relay convergence, snapshot staging, and HEAD verification. An unresolved post-boundary prompt remains live but uncommitted; closeout no longer resets the editor to the prompt-free HEAD projection and therefore cannot replay delayed pre-compact JetBrains events into uncommitted text. The stale zero-editor relay fallback is repaired to the live target, while concurrent live-editor drift fails closed.

## 0.34.105

- **JetBrains `Run Agent Doc` operator reopens no longer disappear behind stale dispatch backpressure.** The production Project Controller now classifies `managed_reopen` / `dispatch_only_reopen` with the same operator policy already covered by SimWorld, so an explicit editor run bypasses an older same-generation in-flight receipt while automatic redispatches remain coalesced. Controller regression coverage reproduces the `waiting_input` + open-receipt route that previously settled exit 0 without submitting anything.
- **Strict empty closeout now adopts and commits a live editor's last response across harnesses.** Visible-response recovery recognizes durable editor/CRDT authority instead of relying on a Codex-only hook session, including an already-`committed` cycle whose snapshot/`HEAD` image was compacted while JetBrains still held the expanded response. The recovered `AlreadyApplied` response now crosses the ordinary converged commit/session-check boundary exactly once; the binary does not restore the editor buffer to `HEAD`, ask the agent to choose a restore, or create another response turn. A live-controller integration test covers a Claude Code document, registered JetBrains replica, delivery ACK pump, stale compacted snapshot/HEAD, empty `write --commit`, and the single recovery commit.

## 0.34.104

- **JetBrains `File Cache Conflict` recovery now preserves editor authority.** Socket `already_applied` recovery waits on one shared lazily deadline for the editor's visible-write receipt; it never reconstructs a response from stale disk or writes that document behind an attached editor. Missing or inconsistent receipts retain the response operation for CP/CRDT replay, and the same bounded receipt budget is shared by socket, file-watcher, normalization, and convergence paths to coalesce retry pressure. File-IPC also no longer treats disk equality as an editor ACK; an unconsumed response operation remains queued without mutating the document. Prompt-shaped divergence is reported as user typing only after that authoritative cut also proves the response is present.
- **Editor response ACKs now prove visible text convergence.** CRDT delta pulls carry the canonical target hash, JetBrains `0.2.252` and VS Code `0.2.50` return the actually applied editor-buffer hash, and mismatches retain the delivery and schedule a true canonical re-bootstrap instead of opening the disk-materialization barrier. Coalesced final hashes cumulatively acknowledge older generations, and re-bootstrap discards divergent replica lineage rather than minting corrective ops. This fixes the equity finalize File Cache Conflict: agent-doc had confused a generation receipt with convergence, not an agent response with operator typing.
- **Response CRDT deltas are proportional to the changed span.** Canonical writes preserve shared prefix/suffix lineage and apply one Unicode-codepoint span edit instead of deleting and reinserting the whole document. A short response can no longer turn a ~32 KiB session document into multi-megabyte tombstone-heavy deliveries that stall the IDE and widen cache-conflict races.
- **Run Agent Doc is single-admission across editor, controller, supervisor, and pane.** JetBrains uses the route attempt as its command idempotency key; the controller coalesces against the durable open dispatch receipt even if a premature Ready projection drifts; fresh dispatch and owned-turn grace prevent the idle watcher from clearing Busy; supervisor IPC performs bounded Enter-only draft recovery and never reinjects prompt text. A fast, unobserved composer transition retains the accepted receipt instead of inviting a second click, while a still-visible draft fails closed. Route preparation also coalesces a stale whole-document replay when one complete copy is an order-preserving subset of the other, retaining every live addition and converging the single projection through CRDT before the duplicate-residue guard runs.
- **Finalize and Compact Exchange absorb closeout congestion under one lazily deadline and declare no-followup intent in one attempt.** `--no-followups` (alias `--no-pending-capture`) records transient capture evidence without exposing a marker in the document, so agents no longer search shell help and retry only to suppress a guard. Short controller read timeouts are backpressured inside a lazily-rs `DeadlineCore`-owned 60-second convergence budget and reported through the coalesced wait notice instead of escaping as repeated finalize/compact attempts around an already-delivered frontier.
- **Compact Exchange is one CP-owned transaction.** CLI, JetBrains, and VS Code submit the operation but never mutate the document. The CP computes the archive and replacement, applies the CRDT projection, waits for the visible hash, and commits inside an in-process document-mutation scope, so relay reads/writes and commit cannot recursively time out on the controller socket. Stale client binaries are rejected before mutation and receive one promotion retry.
- **Editor-origin admission is strict and cross-editor.** JetBrains and VS Code forward only genuine user deltas into canonical CRDT state. Clean file-cache reloads, whole-buffer refreshes, and CP-applied projections fence queued deltas and drive canonical state back into the editor without setting typing or unsynced-user flags. This prevents File Cache Conflict from mistaking an agent response projection for operator typing.
- **Local VS Code installs fail closed on stale packages.** `plugin install --local vscode` now requires the VSIX version to exactly match `editors/vscode/package.json` instead of choosing whichever old artifact has the newest filesystem timestamp.
- **JetBrains local installs target real IDE data roots.** Discovery excludes config and service directories, recognizes only versioned product roots, and reads the installed version from the plugin JAR so install/list/update agree with `idea.plugins.path`.

## 0.34.103

- **Fresh document-bound replacements resume without a second editor click.** The one-shot auto-trigger still prefers the current child's private PTY prompt, but now coalesces a renderer-miss fallback from the owned tmux pane only after the current generation emits output, its actor independently reaches `ready`, and the pane remains dispatch-ready for two consecutive polls. Stale pane history, unstable readiness, missing ownership, and help screens continue to fail closed; accepted fallback proof also retires stale Ctrl+D suppression and records the prompt latch for the current child.
- **Editor-created sessions remain interactive across successful commits and atomic installs.** Run Agent Doc and layout/focus provisioning now start route-owned supervisors with an explicit `keep-alive` policy, while controller recovery keeps bounded one-shot `auto` reaping. Install handoff readiness accepts only the replacement child plus actor and owned-pane proof, preventing cross-document history resume and false crash/recreate churn.
- **Realtime pressure is shaped by the released lazily distributed primitives.** Rust supervisor readiness composes through lazily-rs `ReadinessCore`, awareness is an ephemeral lazily-rs presence map rather than durable document state, and JetBrains `0.2.250` / VS Code `0.2.48` use lazily-kt/js `DebounceCore` KeepLatest generations for full-buffer reporting. Close, dispose, and superseding input atomically cancel the exact generation, so typing bursts coalesce without stale cleanup dropping new work.

## 0.34.102

- **Managed restarts cannot hijack another document's harness conversation.** The supervisor no longer uses process-global history selectors (`claude --continue`, `codex resume --last`, or `opencode --continue`) when replacing a document-owned child. A replacement starts a fresh harness and re-submits only that supervisor's document trigger. This fixes the live failure where equity pane `%83` remained registered to `sampleportal.md` but CP recycle launched Claude with `--continue`, attached session `cb1415be…`, and began lazily-kt work from another session.
- **Install promotion preserves live session handoffs instead of creating a recycle storm.** `make install`, `make install-full`, and `self-install` build a complete executable and atomically rename it over the installed path; controller/supervisor exec calls therefore never observe the `No such file or directory` window produced by `cargo install --force`. Background auto-install now waits for a clean committed checkout, suppresses `lib-install`'s generic recycle fanout, then performs exactly one ordered wave (durable supervisor handoffs first, controllers second). This fixes the live reproduction where one dirty-tree auto-install marked five supervisors, marked four again, removed `%83`/`%87`, and left a replacement Codex actor waiting without a reachable supervisor.

## 0.34.101

- **Editor-selected documents stay synchronized with tmux focus across stale actor projections.** Project Controller focus now reconciles closed or missing actor/registry state against the latest open session-log pane only when that pane is alive, still runs an agent, and its process tree exactly owns the selected document. Cross-document pane reuse and bare-shell remnants fail closed. The reverse `tmux_focus_state` projection also identifies a live route-owned document from the active pane's process tree when the actor row has already been pruned. Editor focus commands use one project-scoped latest-wins idempotency key, so rapid tab changes coalesce instead of replaying stale pane selections. A short editor-focus intent lease prevents the tmux-to-editor poll from echoing the previous pane back into the editor while the latest selection crosses the controller, breaking the observed equity `%83` / bugs `%87` feedback loop. JetBrains plugin `0.2.249` carries the coalesced focus intent and echo suppression.

## 0.34.100

- **Response-cell finalize now reaches visible and disk convergence before commit.** The semantic CRDT response fast path no longer treats durable `ResponseCellAdded` state as permission to commit ahead of outbound editor ACKs. It applies the ordinary quiescence/backpressure barrier, waits for the visible frontier, and materializes the acknowledged canonical cut before snapshot/commit. Retained attached authority with zero registered relay members still projects its canonical response to disk, preventing a committed response from coexisting with a stale pre-response working tree.

## 0.34.99

- **Preflight self-converges byte-exact legacy whole-document replays through CRDT.** Before prompt parsing, diff generation, or orchestration classification, the normal binary-owned preflight boundary detects only the provable complete-session shape repeated two (or a power-of-two number of) times, waits for typing to settle, coalesces it to one projection through visible-replica acknowledgement, and continues from the converged text. The agent never performs a document-repair workflow; non-identical operator content remains untouched. Pure policy, attached-controller integration, and deterministic SimWorld coverage prevent the replay from expanding into a giant diff or bogus multi-task dispatch.

## 0.34.98

- **Attached editor writes settle and converge through one CRDT mutation plane.** Binary-owned writes wait for typing quiescence, apply acknowledgement-driven backpressure while a delivery frontier is in flight, coalesce repeated intent to the newest operator cut plus the original agent target, and rebase with the component-aware CRDT merge before retrying. An accepted CRDT replacement is never replayed through legacy editor IPC, and disk materialization occurs only after exact canonical text is visibly acknowledged; bounded failure retains the change and fails closed instead of provoking a JetBrains File Cache Conflict.
- **The installed agent-doc skill now bundles the JetBrains File Cache Conflict runbook.** Fresh and in-place harness installations receive the quiescent CRDT delivery, legacy-dialog recovery, and deterministic SimWorld guidance already named by the skill catalog.

## 0.34.97

- **Codex skill updates reload in place instead of interrupting the active conversation (`#codex-skill-reload-in-place`).** The Codex auto-update path installs without a reload request, re-reads the installed skill completely, and continues the same turn; it no longer stops and replaces the live child through `restart-supervisor`.
- **JetBrains CRDT editor delivery uses keyed RelayCell backpressure (`#jb-crdt-relay-backpressure`).** Remote bursts retain the oldest guarded baseline, newest converged text, and acknowledgement union in one coalesced hot head per document. One `invokeLater` mutation is admitted per EDT turn, so a blocked UI cannot grow an unbounded apply FIFO or leave the replica worker synchronously parked in `invokeAndWait`; no-op retry backoff is scheduled and coalesced instead of parking that worker for up to 30 seconds. Plugin `0.2.247` consumes lazily-kt `0.28.0` for the relay algebra.
- **Direct-pane routing preserves Codex DIM styling while protecting drafts (`#codex-route-dim-suggestion`).** The route preflight captures ANSI for composer classification and uses a stripped projection only for trigger matching and diagnostics, so generated suggestions such as `Summarize recent commits` no longer block JetBrains `Run Agent Doc`, while identical non-dim operator text remains protected.

## 0.34.96

- **The printed preserve-session baseline recovery now actually unblocks retained-response replay.** `reset --from-current --preserve-session` continues to preserve the response payload, capture state, cycle, and visible document, but explicitly rebases the active capture's file/snapshot hashes to the operator-approved current markdown. A following `write --commit` no longer loops forever on the same stale-baseline refusal.
- **A response CRDT cell may atomically contain several assistant headings.** Multi-topic closeouts remain one ordered, body-aware, replay-safe cell; any embedded operator prompt still fails closed instead of being absorbed into agent output.

## 0.34.95

- **Assistant responses finalize as idempotent semantic CRDT cells instead of fragile whole-buffer/visible-receipt writes.** The controller applies the body-aware cell to the apply-time canonical, durably checkpoints its CRDT projection, and records `ResponseCellAdded` as the realtime backbone's `WriteApplied` receipt. Closeout keeps the inbound live-editor consistent cut while allowing the proven cell to commit before asynchronous outbound editor acknowledgement.
- **JetBrains plugin (`0.2.246`) coalesces repeated `Run Agent Doc` clicks and recovers stale boot-state routing.** A timed-out startup classification receives one bounded retry, while an authoritative `Ready` actor with a real busy blocker falls through to normal queue handling instead of remaining falsely “still booting.”
- **Delayed direct-pane resubmission no longer injects into an exited harness.** Bare-shell panes and panes without a recognizable Codex/Claude surface preserve the drafted trigger and refuse the late `Enter`, preventing reopen thrash and accidental shell execution.

## 0.34.94

- **CP-owned commits read the already-converged CRDT canonical in-process instead of repeatedly requesting it back through the controller socket (`#cp-commit-local-read`).** The `commit_document` handler crosses the commit barrier before entering commit-io, so each current-document read can safely use the local relay and retain the normal authority resolver only as a degraded-state fallback. This removes the cumulative controller queue/timeout cost that made JetBrains Compact Exchange appear to spend 20–30 seconds in git even though the selective `hash-object`/`update-index` transaction itself was fast.
- **JetBrains plugin (`0.2.245`) shows `⟳ agent-doc: Compacting Exchange` in the editor-top turn banner for the complete asynchronous action.** A token-guarded transient overlay wins over ordinary CP turn projection until the command succeeds or fails, stale completion callbacks cannot clear a newer operation, and the banner tooltip explains that compaction and authoritative commit are in progress.

## 0.34.93

- **The agent-doc Rust workspace is private and releases no longer fan out across crates.io.** Every root and internal Cargo package now declares `publish = false`, and `make version-sync` fails if a new package omits that boundary. The release target publishes only the PyPI distribution; GitHub Releases remain the primary binary channel, while `tmux-router` keeps its independent public release lifecycle. Installation docs no longer advertise `cargo install agent-doc`, and `agent-doc upgrade` discovers versions from the GitHub latest-release API before trying the prebuilt binary and PyPI fallback. Existing crates.io uploads cannot be deleted under the registry's permanent-archive contract and remain historical artifacts rather than receiving new versions.

## 0.34.92

- **Codex skill-version mismatches now self-heal through the project controller (`#codex-skill-reload-cp-restart`).** After a real `agent-doc skill install --harness codex --reload restart` update, the installed Codex instructions immediately request `agent-doc session restart-supervisor <FILE>` and stop. CP accepts the busy owner-pane handoff, restarts the child in continue mode (`codex resume --last`), and automatically re-submits the document trigger with the updated skill. A successful managed-session handoff no longer asks the operator to restart manually; manual guidance remains only for an unmanaged session that CP cannot restart. The generic skill source, Codex-rendered instruction surface, harness invocation runbook, README, functional spec, and generated-content tests carry the same contract.

## 0.34.91

- **Direct-pane routing injects each full trigger at most once.** A successful tmux transport is now an irreversible injection boundary: an empty capture or missing dispatch-start proof can no longer be misclassified as proof that the payload had no effect. Recovery is limited to bounded bare-`Enter` retries while the exact routed draft remains visible, preventing fast harness consumption from amplifying one editor action into repeated prompts.
- **Recovery writes fail closed when editor-liveness authorities disagree.** Component patch delivery now requires the reliable document-open set and the captured legacy endpoint to agree that the editor is live; both absent selects disk authority, while either one-sided result blocks before payload materialization. A captured prior response therefore remains retryable instead of being partially inserted during a later preflight. JetBrains plugin `0.2.244` packages the updated native library and restored-tab liveness behavior.

## 0.34.90

- **Idle supervisors no longer amplify document-authority history into sustained CPU load.** Current-document polling now persists only the final selected authority, coalesces consecutive identical observations, and keeps disk/editor transitions durable. Cycle closeout replay excludes authority-only facts through a dedicated partial SQLite index, so its cost stays proportional to lifecycle events instead of total authority history.
- **Large editor deltas use linear batch CRDT edits.** Agent-doc consumes lazily Rust `0.38.2`, whose `TextCrdt::insert_str` projects visible order once and whose new `delete_range` tombstones a precomputed range. The native editor replica no longer rebuilds the full origin tree for every inserted or deleted character; JetBrains plugin `0.2.243` carries the updated native library.

## 0.34.89

- **Compact Exchange ignores quarantined stale live-buffer sidecars (`#compact-stale-sidecar-quarantine`).** Files already renamed to `{stem}.stale-*` were re-enumerated as current editor buffers, so an old providerless snapshot could make compaction fail with `live editor buffer unknown lacks required capability operator_text_authority_v1`. Live-buffer enumeration now excludes quarantine files while preserving fail-closed handling for active legacy and named editor snapshots. Coverage spans debounce enumeration and the shared compact/write converger.

## 0.34.88

- **Reliable-sync liveness now survives controller recycle after the sender has pruned acknowledged frames (`#docop-plane` P4).** The controller commits each receive cursor and liveness batch to SQLite before returning its ACK, restores that journal before exposing a recycled controller socket, and lets cold authority readers fold both the committed receiver state and any unacknowledged sender suffix. Receive cursors use an atomic max UPSERT, so an out-of-order or stale delivery cannot regress the ACK frontier. The controller-originated process-exit fact is journaled before it enters the in-memory projection as well.
- **The plugin-owner lease is retired from the default authority and disk-fallback paths.** Document realtime, write convergence, and controller authority all read the same durable reliable-sync OR-set projection; a zero-member relay can no longer demote an editor that the durable open set still says is live. The explicit `AGENT_DOC_RELIABLE_SYNC_AUTHORITY=0` rollback retains the old lease behavior, while live-buffer files remain compatibility/diagnostic data rather than canonical content authority.
- **Restored editor tabs re-announce liveness on plugin activation.** JetBrains enumerates `FileEditorManager.openFiles` and VS Code enumerates `workspace.textDocuments`; both liveness graphs suppress a duplicate open callback so startup seeding emits exactly one durable fact. JetBrains plugin `0.2.242` and VS Code extension `0.2.47` include the matching startup path. Agent-doc now consumes lazily Rust `0.38.1`, including the cross-implementation stale-handle cursor fix.
- **Verified editor-visible writes now cross the project-controller ownership boundary before closeout.** Socket and file-IPC receipts already proved the candidate reached the editor, disk, and snapshot, but a short-lived CLI could adopt that text only into its own in-memory relay while the controller-owned canonical remained missing or stale. A dedicated controller RPC now folds the proven text into the canonical model consumed by the commit barrier. Detached writes use durable reliable-sync absence to bypass IPC before queueing, and an authority cold miss no longer falls back to the process-local open-docs compatibility projection.
- **The P4 release gate is complete across agent-doc and the lazily substrate.** Agent-doc's full gate reports 7,523 passed and 240 skipped tests; tmux CI, 149 VS Code tests, the JetBrains build/sign gates, and full local installation are green. The follow-up parity run also passes the native full gate and package validation for lazily-spec, lazily-formal, Rust, Python, Kotlin, JavaScript, Dart, Zig, Go, and C++ with byte-identical mirrored stale-handle fixtures.

## 0.34.87

- **Document operations now continue feeding the lazily relay canonical while the live-editor count is transiently zero (`#document-op-replication`).** JetBrains and VS Code bootstrap from the canonical frontier, send incremental `TextOp` deltas through a file-scoped reliable-sync endpoint, and advance their frontier only after the frame has been durably appended and delivered. The controller folds those frames directly into the document canonical without requiring a registered relay member, and the zero-live write path no longer demotes that continuously-fed canonical to disk authority. The shared push endpoint resumes retained frames after restart through lazily 0.38's storage-independent `Outbox<S>`; `agent-doc-sqlite` now re-exports its SQLite implementation instead of carrying a second protocol copy.
- **Reattach recovery is one-shot, bounded, and feedback-safe after the full-state runaway incident (`#reattach-adopt`).** A genuine replica reattach may rebuild the canonical from the editor's visible text, never its tombstone-heavy full operation log; same-text adoption is a hard no-op, event timestamps suppress duplicate requests, and both plugins cap recovery to the reattach transition. SimWorld exercises twenty rounds of tombstone-churning same-text feedback and proves the canonical neither grows nor rebroadcasts. JetBrains plugin `0.2.241` includes the matching thin FFI bridge.

## 0.34.86

- **The CP controller can perform the git commit for a document with a live editor (`#cp-commit`).** When a live editor owns a document, the CLI is only a non-authoritative relay replica, so `Compact Exchange` / `write --commit` / `finalize` used to fail closed (`editor is the current authority ... was not used as commit authority`) whenever relay convergence lagged. The commit now delegates to the CP controller — the authoritative owner of the converged relay canonical — which commits IN-PROCESS where its own canonical is authority. `commit_with_outcome` calls `commit_document_via_controller`: for a headless document it returns `Ok(None)` (the CLI commits locally as before); for an editor-attached document with a reachable controller it sends a new `commit_document` RPC. The controller handler (`handle_commit_document_rpc`) flushes live editor ops into the canonical (`commit_barrier_for_file`), then runs the commit via the `ProjectControllerRuntimeEffects::commit_document` port (the binary wires it to `agent-doc-commit-io`, which depends on `agent-doc-controller-io`, so the controller cannot call it directly). A `CONTROLLER_COMMIT_IN_PROGRESS` scope marks the in-process commit so it neither re-delegates over the socket (which would deadlock the single request-locked controller) nor fails the barrier — the handler already converged it. Delegation is skipped under `--force-disk` and only ever targets an already-running controller (no launch mid-commit); any delegation error falls through to the local commit, preserving the existing fail-closed safety. Coverage: `commit_document_via_controller_is_none_for_headless_document`, `commit_document_payload_round_trips_and_defaults_false`, `controller_commit_scope_preconverges_barrier_and_suppresses_reentry`.

## 0.34.85

- **Compact Exchange no longer leaves an uncommitted summary when a phantom editor lease froze the lazily canonical (`#jb-compact-commit-stale-relay-canonical`).** Observed live on `agent-doc-bugs2.md`: with an older plugin whose CRDT replica register failed, the editor held a phantom lease (`live_editors == 0` yet the reactive open-docs projection still reported the editor open). The compaction wrote the compacted content through the stale-lease disk-authority path (`crdt_cp_write_disk_authority_stale_lease`) to disk + snapshot, but the lazily relay canonical stayed frozen at the pre-compact text. `commit_compacted_authoritative` then resolved the commit's document content through the realtime authority (`try_resolve_current_document_content`), which kept editor authority for the phantom lease and returned the frozen pre-compact canonical — so the commit landed pre-compact content in HEAD (`compact_commit_head_mismatch`) and the compacted summary was never committed. Fix: the compaction commit now converges the lazily canonical to the authoritative compacted content (new `RelayHub::adopt_authoritative_text` / `agent_doc_crdt_relay_io::adopt_authoritative_text_for_file`) **before** the commit reads it — the reliable-sync plane is authority, so the compacted content must reach the plane, not only the disk/snapshot durability sidecars. Authority-gated + fail-open (headless / missing relay model leaves the disk+snapshot write authoritative), and `verify_compact_head_landed` still fails closed if HEAD does not land the compacted content. Coverage: `adopt_authoritative_text_converges_canonical_without_a_baseline`, `adopt_authoritative_text_converges_a_stale_canonical_for_the_commit_read`, `adopt_authoritative_text_is_none_when_headless`.
- **JetBrains plugin (`0.2.240`): the CRDT replica register failure cause is surfaced at WARN instead of swallowed at DEBUG.** A failed `register()` logged only a bare `[crdt-replica] register failed` WARN with no reason, so a live wedge (controller socket unavailable, `ok=false`, oversized bootstrap round-trip, or a native `replica_open` ABI/bootstrap rejection) was undiagnosable from `idea.log`. The transport now records the specific `send`/register error (`socket_unavailable`, `controller_error`, `missing_data`, `missing_client_id`) and `register()` logs it, distinguishing a transport-null failure from a `native.open` rejection; `NativeReplicaNode.open` now logs its throwable at WARN. Aligns with the "NEVER swallow errors" convention.

## 0.34.84

- **`ProvenPane` capability type makes provenance-unchecked input submission a compile error (`#proven-pane`).** 0.34.82 and 0.34.83 were the same bug in two code paths — a pane resolved for input/status without verifying it is still ours (not a foreign session that reused it). Rather than rely on remembering the manual check at every future call site, the direct-submit path now carries a `ProvenPane` newtype whose only constructors are provenance-gated (`from_recorded_owner`, which runs `recorded_owner_pane_is_safe_target`, and `from_verified_live_owner` for panes the sync resolver already proved). `send_clear_to_pane` now takes `&ProvenPane`, so a caller cannot submit into an unchecked pane without a compile error. This is the prototype scope (direct-submit path); rolling `ProvenPane` out to route dispatch and the tmux submit helpers is the follow-up that retires the remaining manual provenance checks. Behavior is unchanged from 0.34.83 — this is a structural guard, verified by the existing direct-submit + `evidence_pane_is_foreign_reuse` tests.

## 0.34.83

- **`session clear` / interrupt / resubmit no longer send input into a foreign pane that reused ours (`#stale-actor-pane-collision` part 2).** `resolve_direct_submit_pane` returned the authoritative-actor (or registry) pane as a direct-input target on `pane_alive` alone — the same stale-actor blind spot fixed for status in 0.34.82, but on the DANGEROUS side: with no reachable supervisor and a recorded-owner pane that another Claude session had reused, a `session clear` / interrupt / resubmit would have injected `Ctrl-C` / `/clear` / prompt text into that foreign session's pane. The direct-submit resolver now gates the authoritative-actor and registry panes through `recorded_owner_pane_is_safe_target`, which shares the provenance rule with the live-pane evidence path: a reachable supervisor is trusted, otherwise the recorded child/registry pid must still live in the pane's process tree. The `find_normal_path_owner_pane` (live-owner) branch already carried process-tree provenance and is unchanged. Audited siblings: route prompt dispatch is supervisor-IPC-gated (fails closed when the supervisor is unreachable, so it cannot raw-send into a foreign pane) and the sync/focus live-owner resolver already verifies process provenance — both were confirmed safe. Coverage: the shared `evidence_pane_is_foreign_reuse` decision tests.

## 0.34.82

- **A live pane is no longer mistaken for our actor after another session reuses it (`#stale-actor-pane-collision`).** When a supervised session's harness exited (clean_exit) and its tmux pane was later relaunched as a DIFFERENT Claude session, the authoritative actor record still claimed that pane, and `live_pane_evidence_for_pane` reported it `alive-idle` from `authoritative_actor` because the pane was alive and showed a claude idle prompt — so `session status`, sync, and focus all treated a foreign pane as our live actor (observed: running `Sync Tmux Layout` on `sampleportal.md` around an IDE-restart / harness-switch window left `agent-doc-bugs2.md` pointing at pane `%85`, which had exited and been reused by another session — the bugs2 pane appeared to "crash"). The evidence path now verifies pane provenance: when no reachable supervisor can vouch for the pane (`Unreachable`/`NoSocket`) and the pane came from a recorded owner, the recorded child/registry pid must still live inside the pane's process tree (`agent_doc_process_owner_io::process_tree_contains_pid`); otherwise the pane is a foreign reuse and the evidence is reported as `projection-stale` so resync reclaims it instead of dispatching into someone else's pane. A healthy/reachable supervisor stays authoritative on its own, and an unresolvable pane pid keeps the prior aliveness evidence (fail-safe). Coverage: `foreign_reuse_when_unreachable_supervisor_and_recorded_pid_absent_from_pane`, `not_foreign_reuse_when_recorded_pid_still_owns_pane`, `not_foreign_reuse_when_supervisor_reachable`, `not_foreign_reuse_without_recorded_pid_or_from_live_session_log`.

## 0.34.81

- **A harness switch (`agent:` change) now writes the new harness into the authoritative actor record immediately, instead of leaving the persisted lazily state reading the old harness (`#actor-harness-switch-writeback`).** When frontmatter switched an active document from `codex` to `claude`, the in-loop restart path (`run.rs`) spawned a fresh harness (`agent_restart_performed`) and updated its local `harness` binding, but `SupervisorShared.harness_binary` was an immutable `String` fixed at construction. The IPC `state` response — which feeds the authoritative actor record in the state backbone — kept reporting the OLD harness until an unrelated later reconcile (e.g. `idle_pane_reconcile`) happened to refresh it. In that window `route` saw `stored_harness=codex` vs `expected_harness=claude-code` and emitted `route_authoritative_actor_harness_mismatch_deferred` ("deferring to boundary agent restart instead of replacing live pane"), which reads like a failure even though the switch had already been performed. `harness_binary` is now a `Mutex<String>` with `current_harness()` / `set_current_harness()`; the fresh-spawn branch calls `set_current_harness(&new_harness)` so IPC `state` reports the switched harness right away and the mismatch defer stops firing on stale state. The harness-specific tmux submit behavior (`auto_trigger_inject` / `auto_trigger_clear_command`) also reads the current (post-switch) harness. Coverage: `set_current_harness_updates_state_backbone_harness_identity`.

## 0.34.80

- **Exact-visible focus sync swaps an off-screen owned pane into view instead of preserving the stale layout (`#exact-visible-focus-swap`).** A deliberate editor tab/focus change emits `sync --focus <file> --exact-visible --no-autostart`. Under that projection `skip_autostart_diagnostics` forced `registered_live_owner` false for every file, so each one was marked `blocked_unresolved` and the layout-preservation guard early-returned before the `tmux_router` reconcile ever ran — a focused document whose owned pane was merely parked off-screen (a stash window / another window) never got swapped into view (observed: focusing `sampleportal.md` left another document's pane in the visible column with no auto-sync). `agent-doc-sync-io::sync` now resolves a proven-live registered owner even on the exact-visible path (`safe_passive_exact_visible_resolve_live_pane`) so it enters the reconcile and the SWAP fast path brings it into the exact-visible columns. The resolve is gated by the shared pure rule `exact_visible_focus_eligible_for_owned_pane_resolve` (exact-visible + pane alive + not claimed by another document this run) followed by the short-circuited live-owner proof: it only reuses a pane that already exists (never cold-starts a missing pane under no-autostart) and, when ownership cannot be proven — e.g. the supervisor identity is unavailable mid-recycle — it falls back to the safe layout-preserving behavior. Coverage: SimWorld `sync_sim_exact_visible_focus_swaps_offscreen_owned_pane_into_view` + `sync_sim_exact_visible_focus_preserves_layout_when_owner_unproven` (shared decision rule) and `exact_visible_focus_resolve_eligibility_rule`.

## 0.34.79

- **`Compact Exchange` (and every editor-convergence write) resolves to disk authority instead of refusing forever when the editor endpoint is a stale-lease phantom (`#stale-lease-cp-authority`, write-converge mirror).** Live repro: a JB editor genuinely had `sampleportal.md` open, the controller/supervisor recycled (`#statedbgc`), the hub was rebuilt from the durable `.yrs` projection with **zero live replicas** while the plugin-owner lease survived — so `editor_attached()` still reported "live". `Compact Exchange` then tagged `pre-compact-N`, ACK/rejected patches against that phantom endpoint, never saw a visible-write receipt, and hard-failed exit-1: `compact: refused direct disk write … editor convergence is unproven (reason=no_visible_write_receipt, editor_endpoint=live)`. The relay's own CP-write path already resolved this to disk authority (`crdt_cp_write_disk_authority_stale_lease`), but `try_editor_converge` discarded that signal and kept trying to converge through the phantom. It now short-circuits: when `apply_canonical_replace_if_attached` returns disk authority (`None`) **and** the recovery authority proves `live_editors == 0 && delivery_converged` under a held lease, it logs `…_writeback transport=disk_stale_lease reason=zero_live_replica_disk_authority action=demote_phantom_editor` and takes the guarded detached disk write instead of refusing. `refuse_unproven_editor_delivery` / `try_detached_disk_write` demote the same stale lease (a `Current{live_editors:0}` from a genuinely live editor is still protected). Coverage: `try_compact_editor_converge_stale_lease_zero_live_replica_resolves_to_disk` (`agent-doc-write-converge-io`).
- **Phantom-editor auto-heal: a dropped replica re-registers on its next `replica_update` instead of hard-failing "not registered" (phantom-editor prevention).** Root cause of the phantom above: after a controller recycle the in-process `hub_registry` restarts empty and the hub is rebuilt from the durable projection with only the canonical replica, so the still-open editor's stable client-id is unknown to the hub. The editor keeps shipping `replica_update`s (on edits **and** on the `publish_live_buffer` recovery path via `ensureEditorText`), but `relay_replica_update_for_file` rejected them with `replica {id} is not registered`, leaving `live_editors == 0` forever until the doc was re-opened. Since the caller already proved `editor_attached()` and passes the editor's stable `identity`, the relay now re-registers the dropped replica (seeded from the recovered canonical) before relaying: it logs `crdt_replica_reattach_on_update … recovery=reregister_dropped_replica`, integrates the editor's update idempotently, and restores `live_editors` to 1 — healing the phantom from the binary with no plugin change (keeps the plugin thin per the FFI-first contract). Coverage: `relay_update_reattaches_dropped_replica_after_recycle` (`agent-doc-crdt-relay-io`).

## 0.34.78

- **Preflight self-heals an uncommittable backlog-capture cycle instead of hard-erroring (`#capturebacklogatomic`).** When `finalize` captured a response but failed before recording the `--backlog-add` mutations, the cycle stranded in `response_captured` with `requires_backlog_capture=true` + `pending_added_this_cycle=false`; preflight replayed the capture, the commit refused (the backlog gate can never be satisfied from the capture), and it hard-errored `previous cycle is still response_captured after recovery/commit` — forcing a manual `mv` of the capture/cycle-state aside (hit live 2026-07-10). `enforce_cycle_completion` (`agent-doc-preflight-io`) now detects that exact unrecoverable shape (`!committed && ResponseCaptured && requires_backlog_capture && !pending_added_this_cycle`) and abandons the stuck cycle (terminal; repair's `state_is_open` replay guard then skips it), logging `preflight_abandoned_uncommittable_backlog_capture … recovery=abandon_and_regenerate`. The captured response was never committed (regenerable) and the operator prompt stays uncommitted on disk, so a fresh cycle re-generates it with its backlog — nothing is lost. Tightly gated so it never touches an otherwise-recoverable capture. Coverage: `enforce_cycle_completion_self_heals_uncommittable_backlog_capture`.
- **`Compact Exchange` handles a live-editor CRDT drift cleanly instead of exit-1 (`#compactcrdtretry`).** When the operator kept editing during compaction, the CP converge was refused with `recovery=retry_crdt_merge` and compact hard-failed with the raw error. `apply_compacted_document` now converges through `converge_compacted_with_retry`: it retries a bounded number of times **only** when the live canonical text has re-settled to the compacted base (a transient in-flight editor delta), and it **never** rewrites the now-stale compacted output over a genuine concurrent edit — a real drift fails closed with an actionable "document changed during compaction; re-run when the editor is idle" message. The zero-live-editor variant is already resolved to disk authority by `#stale-lease-cp-authority` (0.34.75). Coverage: `converge_compacted_retries_transient_resettled_crdt_merge_then_succeeds`, `converge_compacted_fails_closed_on_genuine_concurrent_edit`.

## 0.34.77

- **A corrupt `state.db` self-heals on open instead of wedging every caller (`#statedbgc`).** Live 2026-07-10: `.agent-doc/state.db` had grown to 4.3 GB with no valid SQLite header, so every `agent-doc preflight` hard-failed with `Error: file is not a database` (SQLITE_NOTADB, code 26) from the actor-GC / state-backbone open — the document was un-runnable until the DB was manually moved aside. The closeout-projection *read* path already degraded gracefully to sidecar state, but `open_state_db` propagated the corruption. `open_state_db` (`agent-doc-sqlite/src/state_store.rs`) now detects a corruption error on open/init (`file is not a database`, `database disk image is malformed`, …), quarantines the corrupt `state.db` plus its `-wal`/`-shm` siblings aside as `state.db.corrupt-<pid>-<ns>` (rename, forensics-preserving), and rebuilds a fresh DB. The JSON sidecars remain the authoritative fallback the state backbone rebuilds from, so no durable authority is lost — only the derived projection cache. Coverage: `open_state_db_quarantines_and_rebuilds_a_corrupt_non_sqlite_file`. Complements the `crash_recovery_markers` growth cap (69eabac2) that bounds how the DB grows.

## 0.34.76

- **Route auto-reclaims a DEAD legacy associated pane instead of failing closed (`#routelegacypane`).** `Run Agent Doc` found legacy pane-association evidence (e.g. `%19 cmd=claude sources=session-log`) and refused to re-elect ownership — "the normal path will not re-elect ownership from …; inspect / claim / kill" — even when that candidate pane had already exited. That fail-closed guard is correct for a *live* ambiguous owner but was pure friction for stale evidence. `resolve_or_create_pane_dispatch_only` (`agent-doc-route-io/src/pane_resolution.rs`) now probes `tmux.pane_alive` for the selected winner and every redundant candidate: when none are alive it logs `route_associated_pane_dead_auto_reclaim … recovery=fall_through_normal_route` and falls through to the normal cold-start path instead of bailing. A live (or ambiguous) candidate still fails closed with the explicit claim/kill guidance. Coverage: `dead_legacy_associated_pane_auto_reclaims_instead_of_failing_closed` (tmux-ci).
- **JetBrains: `Run Agent Doc` survives a corrupt IntelliJ Local History store (`#jblocalhistcrash`).** `SubmitAction` force-saves the active document before dispatch; IntelliJ's Local History VFS storage can throw an internal `java.lang.AssertionError` (`AbstractRecordsTable.createNewRecord`) from that save when its store is corrupt, which aborted the whole action. The forced `saveDocument` is now wrapped: a platform `Throwable` is caught, logged (`active_document_save_failed`), and dispatch proceeds — the document text is authoritative and local-history recording is best-effort. Operator remediation for the corrupt store remains File → Invalidate Caches / Restart. JB plugin 0.2.232.

## 0.34.75

- **CP relay writes resolve to disk authority instead of CAS-refusing forever when the plugin-owner lease is stale (`#stale-lease-cp-authority`).** Live dogfood repro on `agent-doc-bugs2.md`: the plugin-owner lease still claimed editor attachment, but the JB editor had exited so the relay had **zero live editors** while the CP canonical stayed frozen at a longer, stale image (`authority=cp_model live_editors=0`, `relay_hash != disk_hash`). Every `finalize`/`compact` CP write then CAS-refused with `expected_hash=… current_hash=… recovery=retry_crdt_merge` because the frozen canonical could never match the disk-derived `expected_current`. That wedged the cycle into `response_captured` and churned preflight repair (each repair replayed the captured response and re-refused against the same stale hashes, forcing manual `mv`-aside recovery). The **read** path already resolved `authority=disk` on zero live editors (`realtime_doc_resolve_crdt_no_live_editors_disk_authority`) — the **write** path disagreed because it derived authority from the lease sidecar (`authority_for_file`) rather than actual replica liveness. `apply_cp_write_for_file` now mirrors the read path: when a hub exists but `hub.live_count() == 0`, there is no live editor buffer to protect, so it logs `crdt_cp_write_disk_authority_stale_lease … recovery=disk_authority_no_live_replica` and returns disk authority (`Ok(None)`) instead of CAS-refusing. The caller commits to disk and the controller's existing disk-change watcher reconciles the stale relay canonical, so it cannot resurrect over disk on editor re-attach. A missing hub stays the distinct `#cpcwritemissingreplica` durable-projection recovery path; a hub with a genuinely live replica still fails closed on a stale baseline (unsaved editor buffer protection is unchanged). Coverage: `cp_relay_write_stale_lease_zero_live_editors_resolves_to_disk_authority` (`agent-doc-crdt-relay-io`).

## 0.34.74

- **A no-ack fresh route pane whose composer still shows the injected trigger is treated as a stranded, unsubmitted prompt instead of a healthy idle no-op (`#jbtsiftnosub2`).** Live repro on `sitscape.md`: JB `Run Agent Doc` on a document with no tmux pane created the pane, typed `agent-doc start --route-owned`, and then dispatched the reopen trigger via supervisor IPC — but the submit never registered against the still-initializing harness composer, so the trigger sat typed-but-unsubmitted. The fresh-start no-ack classifier only checked for a dispatch-ready prompt candidate, so it misread that stranded pane as a legitimate idle no-op (`IdleNoOpKeep`) and kept the session with the operator's request never submitted. `fresh_start_ack_outcome` now takes a `trigger_pending_in_composer` fact and returns a new `StrandedTriggerResubmit` outcome; `pane_composer_has_pending_trigger` (whitespace-insensitive so a column-wrapped trigger still matches) detects the leftover draft from a single ANSI-free capture. On that outcome `route.rs`/`startup.rs` resubmit the stranded draft once with a bare harness `Enter` and re-check for a document-cycle ack: the session is kept only if the resubmit acknowledges, otherwise route records a `FreshStart` startup-miss and fails closed so the miss is visible and recoverable. An empty dispatch-ready composer is still a legitimate idle no-op. Coverage: `fresh_start_ack_outcome_resubmits_stranded_unsubmitted_trigger`, `pane_composer_pending_trigger_matches_wrapped_and_ignores_absent` (`agent-doc-controller`). Follow-up `#restartstderrbleed2` tracks the separate stderr-bleed-on-manual-submit symptom reported in the same session.

## 0.34.73

- **Recovery commands (`session_clear`, `session_interrupt_clear`, `session_restart`) now accept `Blocked` actors instead of hard-rejecting them (`#clear-blocked-actor`).** A `Blocked` actor (one that timed out during starting) is exactly the stuck state that recovery commands are meant to fix, so rejecting the recovery command on the state that most needs recovery was self-defeating — the operator saw `operator command `session_clear` rejected for ...: generation N is blocked` and had no binary-owned path out. The project controller's `handle_operator_command` now treats `Blocked` the same as `Closed` for recovery commands: `session_clear` / `session_interrupt_clear` reset the session context and `session_restart` supersedes the blocked generation (blue/green `#supkill-bg`). Non-recovery commands (normal dispatch) still reject on a `Blocked` actor. The `session_restart` supersede redirect log now records `action=supersede_blocked_actor` (or `supersede_closed_actor`) so log forensics can distinguish the prior actor state. Coverage: `controller_session_recovery_commands_accept_blocked_actor_generation`.

## 0.34.72

- **Idle-queue watch backs off a degraded project controller (`#idlewatchctrlbackoff`).**
  The supervisor idle-watch polled the controller's CRDT-model read every 500ms
  per owned document to check for a drainable queue head. When the controller
  fell behind, every poll paid the full read timeout (0.8s) and re-saturated the
  controller — observed live with three route-owned supervisors (2 reads/s each)
  pinning the controller at ~82% CPU and producing a multi-hour
  `document_model_controller_lookup_error` / `controller_crdt_current_text_read_unavailable`
  timeout storm. The watch now records controller degradation (set when
  `observe_live_editor_authority`'s controller RPC fails, read via the new
  `controller_failed_within`) and, once degraded, reads the queue head from disk
  for a 30s cooldown — the same disk authority the paused-queue path already
  uses (`#qchurn`) — then probes the controller once per cooldown window. This
  cuts per-document controller load from ~2/s to ~1/30s during a wedge without
  losing queue-drain readiness, letting the controller recover instead of
  feeding the feedback loop. Coverage: `controller_failed_within_is_true_after_degradation_recorded`.

## 0.34.71

- **Supervisor fd leak across self-`execve` recycles fixed (`#supfdleak`).** The
  route-owned supervisor self-`execve`s on binary hot-reload, and `execve` does
  not run `Drop` impls — so every non-`FD_CLOEXEC` helper fd survived each
  recycle and accumulated as orphans until the process hit `Too many open files
  (os error 24)`. Live telemetry showed supervisors holding 287–415 fds (267
  `/dev/ptmx`, 45 `supervisor-stderr.log`, ~45 stop-signal pipe pairs) spanning
  days of recycles, which then surfaced as the recurring
  `[supervisor::ipc] accept error: Too many open files` and the multi-hour
  controller-lookup timeout storm (the wedged supervisor could no longer accept
  IPC). All pty-master dups now go through a new atomic
  `dup_cloexec` (`F_DUPFD_CLOEXEC`): the shared inject writer's write fd
  (`dup_write_fd`), the resize handle, and `AdoptedMaster::dup_file`. The
  master-fd projection handed to the reexec path is now CLOEXEC too — which
  matches the documented design ("the original fd closes on exec as usual"),
  since the reexec already creates its *own* non-CLOEXEC dup for adoption. The
  stop-signal pipe (`StopSignal`) and the auto-install child-stdio dups
  (`auto_install_stream_dup_fd`) are CLOEXEC as well, as is the
  `SupervisorStderrRedirect` saved-stderr dup. Sidecars-to-sqlite was *not* the
  fix — the leaked fds were duped pty/log/pipe handles, not sidecar file
  writes. Coverage: `dup_cloexec_sets_fd_cloexec`,
  `pipe_cloexec_sets_cloexec_on_both_ends`,
  `stop_signal_pipe_ends_are_cloexec`.

## 0.34.70

- **Finalize/write now skips the editor-IPC cascade when the relay reports zero
  live editors (`#6b5h` write-path parity).** The read path already demoted
  zero-live relay text to disk authority (`553904e8`), and the `--force-disk`
  authority gate already honored `live_editors: 0`, but the normal (non-force-disk)
  write gate only checked `patches_dir.exists()` — so a pure-CLI session with no
  live editor buffer wedged for ~8s on socket→file-IPC `no_ack` timeouts before
  either bailing ("refusing direct document write") or recovering. The write path
  now probes the controller (hub authority) with a local-relay fallback and, when
  it sees `Current { live_editors: 0, delivery_converged: true }`, skips straight
  to the disk write — disk is authority when no editor owns the document.
- **JB plugin `CrdtReplicaManager` no longer busy-spins when the CP controller
  socket is unavailable (`#crdt-drain-backoff`).** A zero-update drain cycle
  rescheduled immediately via `requestRemoteDrain(reason = "rescheduled")` with no
  delay; against a missing `controller.sock` this generated ~70MB/min of
  `[crdt-replica]` log lines and froze the IDE (observed 2026-07-08, 7×10MB log
  rotation in ~7 min). Rescheduled drains now apply exponential backoff
  (100ms → 5s cap) when the previous cycle applied zero useful updates, and reset
  immediately once real CRDT traffic arrives.
- JB plugin bumped to 0.2.229.

## 0.34.69

- **A proven editor-IPC write wedge now auto-recycles the supervisor mid-turn
  (`#midturn-wedge-recycle`).** Previously `supervisor_recycle_action` gated every
  recycle behind a turn boundary and deferred on an open cycle — but a wedged
  turn/cycle can never reach its own boundary (closeout is blocked on a convergence
  receipt that never arrives), so a wedge on a fresh (non-stale) supervisor
  deadlocked until the operator ran `admin recycle` by hand. A latched wedge now
  escalates immediately, bypassing the boundary and cycle-open gates, and is
  guarded once-per-episode by a `recycle_attempted` flag on the dewedge marker so it
  cannot recycle-loop. The in-flight response is capture-backed and recovers via
  redispatch/replay on the fresh supervisor.

- **A Ctrl+D buffered across an `execve` recycle no longer kills the freshly-adopted
  agent (`#stale-ctrl-d-arm`).** Stale-Ctrl+D suppression (`suppress_stale_ctrl_d_
  until_prompt`) was never armed anywhere — dead code — so a Ctrl+D (EOF) that
  arrived before a fresh child printed its first prompt reached the agent and exited
  it cleanly, dropping the interrupted turn to the restart-or-quit prompt ("the
  session crashed and did not restart the turn"). Every child launch (first run,
  restart, and recycle-adopt) now arms the guard via `ChildLaunchPlan.
  arm_stale_ctrl_d_suppression` until the prompt is seen; an intentional operator
  Ctrl+D at a live prompt still reaches the child.

- **`make test` can no longer report success on a failing nextest run.** The
  cargo-nextest branch now guards its exit explicitly (`if ! ...; then exit 1`)
  instead of relying solely on `set -e`.

- **Editor turn-state status now reads the Project Controller lazily projection,
  not sidecars.** JetBrains and VS Code call the Project Controller
  `state_subscribe` endpoint, mirror the returned lazily snapshot/delta, and
  derive the in-flight status-bar label from that mirror. The editor hot path no
  longer reads or watches `.agent-doc/turn-scope/`, `.agent-doc/state/cycles/`,
  or sidecar compatibility files; sidecars are reserved for crash recovery or a
  documented exceptional path where they must be used. If the Project Controller
  is unavailable, the editor status bar shows `agent-doc: Project Controller
  disconnected` instead of silently falling back.

- **JetBrains claim/run recovery now preserves the editor-selected document under
  active editor authority races.** `Claim for Tmux Pane` keeps the just-written
  claim scaffold/frontmatter/defaults as the next in-memory baseline instead of
  re-reading through a typing-windowed editor model, records the previously
  focused tmux document as already seen before a claim attempt, and only runs
  layout sync after a successful claim so a failed `sitscape.md` claim cannot
  recall the editor to the stale `haiven.md` pane. Route-owned queued dispatch
  writes now retry a bounded CRDT merge on Project Controller relay
  `recovery=retry_crdt_merge` hash mismatches, re-reading the authoritative
  current queue and preserving concurrent editor queue edits before saving the
  route snapshot.

- **Session ownership now uses the SQLite durable registry instead of the
  legacy JSON registry/projection files.** `agent-doc-session-registry-io`
  reads and writes `.agent-doc/state.db` through `tmux-router`, while actor
  authority stays in the SQLite `documents` table and tmux-router metadata is
  isolated in `registry_entries`. The old `sessions.json` /
  `session-actors.json` support paths and actor/session projection refresh
  hooks are removed; startup, sync, focus, preflight, gc, and integration
  fixtures now go through the durable registry APIs. `tmux-router` defaults its
  CLI and documentation to `.tmux-router/state.db`, with focused SQLite
  coverage for metadata round trips, lookup, window updates, and CAS-safe
  router saves that do not create actor rows.

## 0.34.68

- **`Run Agent Doc` now routes through the lazily command/RPC message plane
  (`command-plane-v1`, `#lzmsgpcp`) by default.** The project controller serves a
  new `editor_command_submit` endpoint beside the classic `editor_route` (shadow
  mode): it decodes an `agent-doc.editor_route.v1` `CommandSubmit`, dispatches the
  existing route path unchanged, and returns a folded `CommandProjection` with
  progress events plus a terminal causal receipt (`applied` on success,
  `rejected` on failure). Both the JetBrains and VS Code plugins send this
  envelope and resolve the action **only** on the terminal receipt — a transport
  ACK or `accepted`/`started` progress never completes the command. Set
  `AGENT_DOC_COMMAND_PLANE=0` to fall back to the classic `editor_route` request.
  Terminal semantics, idempotency, generation guards, and reconnect projection
  are owned by lazily (`lazily-spec` `message-passing.json` + `lazily-rs`
  `command` module); agent-doc owns the payload schemas
  (`editors/command-payloads.md`).

## 0.34.67

- **Claim/sync now prefer the current live `agent-doc` tmux session before a
  configured project pin.** `Claim for Tmux Pane` accepts panes in the
  operator's current session without requiring a temporary
  `.agent-doc/config.toml` edit, while `route`/`sync` follow the current session
  only when it already contains an `agent-doc` window. The configured
  `tmux_session` remains unchanged and still acts as the fallback. Coverage adds
  focused claim policy tests plus current-session route/sync regressions.

- **JetBrains read-authority refresh routes through the CRDT relay before the
  compatibility live-buffer projection.** Route document prep can need the
  editor to republish an attached document model before disk is safe to read.
  `publish_live_buffer` remains read-only, but `send_publish_live_buffer` now
  waits for the terminal applied receipt so success means the plugin finished
  registering/refreshing its CRDT replica instead of merely accepting the socket
  request. The controller `crdt_current_text` RPC is now a pure CP relay read:
  recovery publish requests happen outside the controller handler and then poll
  CP state through the same relay, so the plugin has no separate authoritative
  editor-write path. Controller self-recycle also waits for active client
  requests to drain, preventing install/recycle recovery from terminating the
  CP while a relay poll is in flight. Coverage:
  `publish_live_buffer_message_is_readonly_and_requests_early_receipt` and
  `send_publish_live_buffer_waits_for_terminal_applied_receipt`,
  `crdt_current_text_rpc_reads_relay_without_publish_recovery`, and the
  controller CRDT checkpoint tests.

- **JetBrains `Clear Session Context` uses the same bounded Enter-resubmit
  state machine as direct-pane route dispatch.** Clear already detected a
  visible `/clear` or `/new` draft and sent one bare submit key, but a focused
  Codex/OpenCode composer can swallow more than one Enter. The verifier now
  keeps resubmitting while the clear command remains visible, bounded by
  `AGENT_DOC_DIRECT_PANE_MAX_ENTER_RESUBMITS`, and logs each
  `session_clear_submit_resubmit` with `attempt` and `max_attempts`.

- **Editor plugins leave debounce ownership to the CP/binary.** JetBrains and
  VS Code `Run Agent Doc` no longer force `agent-doc route --debounce 0`, and
  editor patch/save/reposition handlers no longer wait on plugin-side typing
  idle before processing CP requests. The plugin still keeps stale-generation
  proof checks and retry handling for actual apply conflicts.

- **More launch and sync policy moved out of orchestration.** Claude JSON argv
  construction and Codex default/structural launch argv now live in
  `agent-doc-turn-executor`, while editor sync column-list projection now lives
  in `agent-doc-tmux`. Orchestration imports those focused owners directly and
  keeps only backend process spawning, pane/file IO, and sync adapters. The
  Codex default remains behavior-preserving (`exec --json -s workspace-write`),
  and boundary tests now guard against reintroducing orchestration facades.

- **GitHub Actions nested-submodule test setup is deterministic.** The
  `agent-doc-git-io` submodule test helper configures a local test author before
  committing submodule additions, so CI runners without global git identity no
  longer fail `external_git_dirs_for_submodule_include_nested_submodule_gitdirs`.

## 0.34.66

- **JetBrains `Compact Exchange` no longer leaves an uncommitted summary
  (`#jb-compact-editor-buffer-flush`).** The editor-IPC `op:replace` convergence
  updates only the live editor's in-memory buffer; the plugin never saves it, so
  the working-tree file stayed at the pre-compact content. The `--commit`
  selective commit then compared that stale working tree against the compacted
  snapshot, treated the snapshot as historical exchange drift, and repaired it
  back to HEAD — leaving HEAD and disk pre-compact. `agent-doc compact --commit`
  now asks the live editor to flush its buffer to disk (the same `save_document`
  IPC preflight uses for `live_prompt_drift`) *before* the re-read and commit, so
  the working tree holds the compacted content when the commit stages it. The
  flush is fail-open — `commit_compacted_authoritative` still verifies HEAD
  landed the compacted content and fails closed otherwise. No plugin change: the
  `save_document` handler already ships. Regression test:
  `compact_with_commit_flushes_editor_buffer_to_disk`.

## 0.34.65

- **More orchestration helper seams moved to focused owners.** Snapshot exchange
  stripping now lives in `agent-doc-element-exchange`; cross-document owner
  command-line detection lives in `agent-doc-controller`; pure git path/output
  interpretation lives in the new `agent-doc-git` crate; harness name
  normalization lives in `agent-doc-harness`; and ownership generation/event
  formatting lives in `agent-doc-supervisor`. Orchestration imports those owners
  directly and keeps only git/process, snapshot, pane, registry, and actor-store
  adapters. Exchange-only post-commit IPC reposition safety now also lives in
  `agent-doc-element-exchange`, leaving git orchestration to provide only the
  HEAD document inputs and signal decision. Queue context-clear in-flight marker
  payload construction now lives in `agent-doc-queue`, leaving orchestration to
  provide marker storage and stale-file cleanup. Codex transcript
  `session_meta.cwd` parsing now lives in `agent-doc-model-tier`, leaving
  orchestration to handle only transcript file discovery and reads. Drain-stall
  continuation-pending marker construction now lives in `agent-doc-turn`, with
  orchestration retaining only marker path, clock, JSON, and file IO.
  Transcript-content context percentage policy now also lives in
  `agent-doc-model-tier`; orchestration only reads transcript files and renders
  the existing operator diagnostics. Route-submit marker JSON serialization and
  fresh/stale/malformed classification now lives with the marker schema in
  `agent-doc-supervisor`, leaving route orchestration to handle only sidecar
  file IO, cleanup, and ops-log reporting. Deferred operator-clear marker
  payload construction and JSON parsing now lives with queue preemption policy in
  `agent-doc-queue`, while orchestration keeps marker path resolution,
  read/write/remove effects, and idle-watch delivery. Editor column split
  classification now lives in `agent-doc-tmux`; route orchestration only passes
  file/column facts into the focused tmux layout policy. Startup path rewriting
  for narrowed pane working directories now lives in `agent-doc-fs`; route
  startup only supplies the document path, resolved cwd, and original CLI path.
  Supervisor reexec candidate ordering now lives in `agent-doc-supervisor`;
  orchestration only gathers current-exe/install-path facts before attempting
  the platform `execve`.

- **Pure helper layer extracted from orchestration.** Exchange shrink/ack retry
  and prompt-dedupe helpers now live in `agent-doc-element-exchange`; preflight
  prompt accumulator helpers now live in `agent-doc-prompt-contract`; harness
  output prompt visibility now lives in `agent-doc-harness`; OpenCode permission
  prompt stdin normalization now lives in `agent-doc-turn-executor-tmux`; and the
  direct-pane retry budget parser now lives in `agent-doc-controller`.
  Orchestration imports these focused owners directly and keeps only the effect
  adapters for file IO, supervisor state, pane capture, and logging.

- **Harness policy extracted and legacy API surfaces removed.** Harness config,
  prompt/chrome classification, busy/idle blockers, and restart argument policy
  now live in the focused `agent-doc-harness` crate. Orchestration imports that
  crate directly and no longer exposes a `harness` facade module. The same cleanup
  removes compatibility surfaces that had no greenfield consumers: preflight JSON
  now emits `user_intent_prompt_changes` only, `patch:pending` and its
  `--allow-patch-pending` / `AGENT_DOC_ALLOW_PATCH_PENDING` escape hatches are
  rejected, and tracked-work completion accepts only `--done` rather than the old
  `--pending-done` / `--backlog-done` spellings.

- **Deprecated tracked-work aliases removed from active APIs.** The `backlog` and
  `icebox` CLI surfaces now expose `reap` as the only completed-item removal
  command; the old `prune` subcommand is rejected. Preflight JSON now emits only
  `backlog_reordered` and `backlog_gated_count`, dropping the legacy
  `pending_*` aliases. `agent-doc-template` also imports boundary-id helpers
  directly from `agent-doc-element` instead of exposing a template id facade.

- **Open agent-doc cycles now defer every supervisor recycle arm until commit or
  bounded resume escalation.** The `#midturn-recycle-resume` interlock now wins
  over explicit admin recycle, editor write-wedge recycle, and failed-reexec
  escalation while a closeout cycle is still open. A forced recycle after the
  bounded never-closing-cycle threshold preserves the open durable checkpoint
  instead of marking it abandoned, so the fresh supervisor can adopt a surviving
  child or re-dispatch the interrupted turn exactly once. Stalled-cycle cleanup
  now runs only at a true turn boundary, preventing a long active Codex turn from
  being force-abandoned mid-closeout.

- **Editor prompt pollers removed.** The JetBrains plugin no longer starts the
  defensive `agent-doc prompt --all` timer, no longer registers submitted files
  for prompt polling, and no longer ships the bottom prompt panel. The VS Code
  extension now has parity: it no longer imports the prompt-polling helper, no
  longer starts a prompt QuickPick poller after Run, and no longer calls
  `prompt --answer` from the extension. This removes the timer-based tracked-file
  prompt path that could mutate or gate a live editor buffer outside an explicit
  agent-doc write. JetBrains plugin `0.2.202` and VS Code extension `0.2.36`
  include the removal.

- **Editor IPC conflict replay is fail-closed across JetBrains and VS Code.**
  JetBrains no longer keeps conflict-deferred patch ids or replays old IPC
  payloads after an IntelliJ File Cache Conflict resolves; conflict detection now
  records `file_cache_conflict_pending`, deletes the queued file patch, refreshes
  visual tokens, and leaves response retry ownership with the binary. VS Code now
  mirrors the same no-replay boundary for stale editor-generation apply-proof
  failures while preserving active-typing debounce retries. JetBrains plugin
  `0.2.201` and VS Code extension `0.2.35` include the editor-side changes.

- **Socket `already_applied` now requires response materialization proof.**
  The already-applied IPC path no longer saves `content_ours` as the committed
  snapshot unless the expected response is actually present in the selected
  snapshot. Response-less already-applied claims now fall back to file IPC
  instead of closing from a prompt-only editor buffer.

- **Synced editor-visible snapshots can commit while disk still lags.**
  The stale-disk commit guard now distinguishes "commit from stale disk" from
  "stage the synced editor-visible snapshot". When an operator-authority
  live-buffer snapshot matches the staged snapshot and its epoch is already
  synced/proven, closeout can commit that staged content even if the working
  tree has not caught up yet; the same proof also allows already-current
  closeout when HEAD/snapshot have the response and only disk lags. The match is
  tolerant of transient boundary-marker churn that can happen between the editor
  proof and commit staging.

- **Commit-barrier live-buffer publish ACKs now clear stale in-flight epochs.**
  When a closeout times out waiting for editor delivery, the recovery path asks
  the editor to publish its visible live buffer and then stamps any
  operator-authority live-buffer snapshot as synced/proven on ACK. This keeps
  older JetBrains publish implementations, which report through the normal
  document-changed path, from re-opening the in-flight barrier immediately before
  commit.

- **Historical snapshot repair no longer restores stale pre-compact HEAD over
  the visible editor document.** The `head_local_drift` repair path now runs the
  stale-snapshot reset guard against HEAD before saving it as the snapshot. Safe
  visible compactions rebase the snapshot to the editor-visible file, and unsafe
  large shrinkage fails closed instead of feeding stale HEAD back into IPC/CRDT
  convergence after an IDE restart.

- **Ack-content closeout now clears the live-buffer commit barrier.**
  Socket `already_applied` retries that adopt a JetBrains ack-content proof now
  mark the targeted live-buffer sidecar as synced before commit-barrier checks.
  This keeps a response that is already visible in the editor from staying
  blocked on an older unsynced live-buffer epoch while disk still lags the
  editor. JetBrains plugin `0.2.200` also publishes the same synced-buffer proof
  through the editor FFI when it writes ack-content.

- **Socket `already_applied` closeout now carries ack-content proof.**
  JetBrains patch-id dedup paths now write the current editor buffer to the
  normal `.agent-doc/ack-content/<patch_id>.md` sidecar before returning
  `already_applied` or deleting a stale file-IPC patch. The Rust
  `already_applied` branch now prefers that sidecar when disk still lags the
  editor buffer, so a retry no longer wedges with "visible editor buffer differs"
  after the response is already present in the editor. JetBrains plugin
  `0.2.199` includes the editor-side fix.

- **Tmux input diagnostic formatting moved to `agent-doc-tmux-commands`.**
  Structured input-event field sanitization, payload hashing, byte/key naming,
  editor-route attempt-id correlation, and verbose-diagnostic gating now live in
  `agent_doc_tmux_commands::input_diag`. Orchestration keeps only the
  stderr/ops-log emission adapter.

- **Auto-DAG schedule kernel moved to `agent-doc-work-graph`.** The schedule
  model, task parser, session-review guard classifier, blocker wording, and node
  readiness/state-transition policy now live in
  `agent_doc_work_graph::schedule`. The root `auto_dag` module keeps only
  document/file IO, schedule-id hashing, and tsift evidence adapters.

- **Executor capture-delta policy moved to `agent-doc-turn-executor`.** The
  watch daemon now calls `agent_doc_turn_executor::capture` directly for pane
  capture deltas and bounded line windows. Orchestration keeps only tmux
  capture polling and document flush adapters.

- **Prompt-target diff extraction moved to `agent-doc-workflow`.**
  `prompt_context` now calls `agent_doc_workflow::session_cycle` directly for
  prompt-bearing diff target extraction and imperative-directive fallback.
  Orchestration keeps bounded prompt-pack rendering.

- **Status projection policy moved to `agent-doc-document`.** Top-backlog
  status reconciliation and stale-supervisor status-marker insert/remove now
  live in `agent_doc_document::status_projection`. `status_cmd` remains only the
  file/editor writeback adapter, and compact/repair/preflight callers import the
  focused document policy directly.

- **Prompt-cache policy moved to `agent-doc-prompt-cache`.** Stable-prefix
  boundary rendering, replay-key construction, prompt-cache effectiveness
  samples, miss-cause ranking, and trend checks now live in the focused pure
  crate. Orchestration keeps only JSONL history file adapters, and run prompt
  assembly imports the focused API directly.

- **Session-cycle workflow policy moved to `agent-doc-workflow`.**
  `SessionExecutionScope`, finalize pending mutation vocabulary, prompt-target
  extraction, execution-scope classification, and finalize-command rendering now
  live in `agent_doc_workflow::session_cycle`. Orchestration keeps only
  `FlowEvent` adaptation for session-cycle events and imports the focused API
  directly.

- **Release publish contract fix for `#suprestassoc`.** Supersedes the unpublished `0.34.64` crate attempt by depending on `lazily 0.13.1`, which publishes the `CellTree` / `SemTree` / `TextCrdt` / reconcile API that `agent-doc-core` already used through the local path dependency. The behavior change remains the `restart-supervisor` document-scoped registry lookup described in `0.34.64`; this version is the publishable release artifact.

- **Realtime write/reconnect policy moved to `agent-doc-document-realtime`.** Visible-write idle admission, full-content source proof/replacement rejection, reconnect-buffer reconciliation, and editorless disk fallback decisions now live in `agent_doc_document_realtime::write_policy`. Orchestration keeps only sidecar/editor/git/file adapters and flow-event formatting, and callers import the focused realtime API directly rather than using orchestration facades.

- **Realtime exchange recovery policy moved to `agent-doc-document-realtime`.** Exchange response-block parsing, safe historical exchange-reduction classification, live-prompt-drift recovery target construction, and dropped-prompt containment checks now live in `agent_doc_document_realtime::write_policy`. Orchestration keeps only cycle-state, IPC, snapshot, CRDT, ops-log, and transient-marker normalization adapters.

- **Safe out-of-band mutation classification moved to `agent-doc-document-realtime`.** Agent-doc snapshot/file mutation classification for safe status, exchange, pending/backlog, committed historical exchange drift, reaped pending-id reintroduction, user-follow-up exchange growth, and empty bootstrap scaffold detection now lives in `agent_doc_document_realtime::write_policy`. Orchestration keeps git/snapshot/file/ops-log effects and imports the focused realtime policy directly.

- **CRDT authority policy moved to `agent-doc-document-realtime`.** CRDT
  authority classification, liveness-derived authority selection, sync
  admission, and commit-barrier gating now live in
  `agent_doc_document_realtime::crdt_authority`. Orchestration keeps only
  plugin-owner liveness IO and CRDT relay/backbone adapters.

- **Operator-clear guard policy moved to `agent-doc-controller`.** The
  operator-clear input-state vocabulary and guard outcome table now live in
  `agent_doc_controller::operator_clear`. Orchestration keeps only the
  `FlowEvent`/ops-log adapter and CLI call sites import the focused controller
  policy directly.

- **Controller status projection moved to `agent-doc-controller`.** Controller
  process freshness, control-plane status, actor status, bootstrap status
  projection, and handoff-state parsing now live in
  `agent_doc_controller::status`. Orchestration supplies SQLite counts, process
  inode facts, bootstrap facts, duplicate-pid facts, and socket/process effects.

- **Closeout guard vocabulary moved to `agent-doc-turn`.** The stable closeout
  guard reason labels, terminal guard outcome table, and closeout cycle-phase
  parsing now live in `agent_doc_turn::closeout_guard`. Orchestration keeps only
  `FlowEvent`/ops-log formatting and no longer owns a duplicate closeout state
  vocabulary.

- **Agent streaming output parsing moved to `agent-doc-turn-executor`.** The
  shared `StreamChunk` type, Claude `stream-json` line parser, and Codex JSONL
  line parser now live in `agent_doc_turn_executor::agent_stream`. Orchestration
  keeps only streaming backend adapters and imports the focused parser/chunk APIs
  directly.

- **Template patchback policy moved to `agent-doc-template`.** Patchback shape vocabulary/classification, marker/component counting, pure parse-plan construction, and the orchestrate patchback contract now live in `agent_doc_template::patchback`. `flow::document_mutation` keeps only file-scoped ops-log and FlowEvent adaptation, while write and orchestration-batch callers use the focused template API directly.

- **Child template patchback normalization moved to `agent-doc-template`.** The
  child-orchestrate plain-response wrapper and explicit/rejected/unparseable
  normalization decision now live in `agent_doc_template::patchback`.
  `flow::orchestration_batch` keeps only the FlowEvent adapter.

- **Template response materialization moved to `agent-doc-template`.** Response
  write-proof detection, canonical patch serialization, response
  materialization probes, trailing-newline equality, materialization segment
  appending, and zero-patch marker rejection now live in
  `agent_doc_template::response_materialization`. Orchestration keeps only
  template parsing, IPC payload, file, and log adapters.

- **Strict template response-heading policy moved to `agent-doc-template`.** The strict closeout `### Re:` heading requirement and streamed-visible-prefix proof now live beside response materialization in `agent_doc_template::response_materialization`. Write adapters call the focused template API directly instead of re-exporting it through orchestration.

- **Queue head classification moved to `agent-doc-queue`.** Free-text vs id-backed queue-head classification, registered-preset detection, tracked directive-id detection, bare-do directive recognition, and queue activation-trigger text recognition now live in `agent_doc_queue::queue_response`. Orchestration keeps queue mutation/file adapters and imports the focused queue policy directly.

- **Route textual predicates moved to `agent-doc-controller`.** Codex shell-search blocker recognition, context-session normalization, and stash-window name classification now live in `agent_doc_controller::dispatch`; route, sync, and resync modules consume those predicates directly while keeping tmux/process effects local.

- **Auto-DAG schedule decision vocabulary moved to `agent-doc-work-graph`.** The
  ready/session-review-blocked schedule decision and stable reason labels now
  live beside the source-agnostic Auto-DAG model. Orchestration batch flow keeps
  only event rendering and logging.

- **Orchestration batch progress policy moved to `agent-doc-work-graph`.** The
  continue/source-changed/child-not-completed decision and stable labels now live
  beside the source-agnostic work graph model. Orchestration adapts child
  closeout outcomes into the focused boolean input and keeps only flow-event
  formatting.

- **Cross-cutting workflow kernel moved to `agent-doc-workflow`.** The pure
  evidence-to-decision-to-mutation/proof transition table for stale supervisors,
  queue drainability, captured responses, and live-buffer drift now lives in a
  focused pure-policy crate. Orchestration no longer exposes a
  `flow::workflow_state` module.

- **Workflow invariant catalog moved to `agent-doc-workflow`.** The stable
  invariant ids, fact-source vocabulary, remediation actions, catalog builder,
  and JSON serialization now live in `agent_doc_workflow::invariants`.
  Orchestration doctor/autofix commands consume that focused catalog directly
  and no longer own a `flow::workflow_invariants` module.

- **Append response heading normalization moved to `agent-doc-turn`.** The helper
  that strips echoed `## Assistant` / trailing `## User` headings before append
  writes now lives in `agent_doc_turn::response_text`; orchestration calls the
  focused API directly and no longer defines the write-local normalizer.

- **Future-work response signal policy moved to `agent-doc-turn`.** Deferred-work
  phrase detection for `worth revisiting`, `revisit later`, `follow-up needed`,
  and `future work` now lives in `agent_doc_turn::heuristics`; the write path
  only emits the warning when no `--pending-add` was provided.

- **Queue deletion identity policy moved to `agent-doc-queue`.** The queue-row
  count comparison used to prove live editor buffer deletions now lives with
  queue syntax and identity normalization. Preflight maintenance keeps only the
  live-buffer/document mutation adapter and calls `agent_doc_queue` directly.

- **Queue response/head matching policy moved to `agent-doc-queue`.** Response
  heading topic parsing, done-id normalization, exact id/topic resolution, and
  queue prompt text matching now live in `agent_doc_queue::queue_response` and
  `agent_doc_queue::queue_directive`. Orchestration keeps only lifecycle,
  document IO, and mutation adapters.

- **Free-text queue head answer-matching policy moved to `agent-doc-queue`.** The
  normalized free-text head matching, explicit queue-prompt echo proof,
  in-progress marker detection, and fenced-log prose-prefix matching now live
  beside queue response/head matching. Queue consume, preflight maintenance, and
  session-check import the focused queue API directly.

- **Imperative response contract policy moved to `agent-doc-turn`.** The
  status-only/meta-refusal/blocker/evidence classifier used by the executable
  directive backstop now lives in `agent_doc_turn::response_text`; orchestration
  keeps only diff inspection, ops-log emission, and rejection formatting.

- **Cycle phase label policy moved to `agent-doc-turn`.** Canonical
  `preflight_started` / `response_captured` / `write_applied` / `committed` /
  `abandoned` rendering now lives on `agent_doc_turn::CyclePhase`; route and
  session-check code no longer keep local match tables.

- **Closeout prompt/response text matching moved to `agent-doc-turn`.** Prompt
  prefix normalization, exchange prompt-line matching, `### Re:` heading
  classification, queue-continuation heading classification, and assistant
  response-body extraction now live in `agent_doc_turn::closeout_signal`.
  Direct response patchback heading detection and binary recovery-diagnostic
  heading exemptions also moved there; session-check keeps only file/cycle/ops-log
  guard adapters.

- **Exchange-tail prompt policy moved to `agent-doc-turn`.** Unresolved
  exchange-tail prompt detection, prompt-only closeout-tail detection, and
  tail response-heading detection now live in `agent_doc_turn::exchange_tail`.
  Session-check keeps only file/context adapters and guard message formatting.

- **Closeout metadata-drift authority moved to `agent-doc-turn`.** The
  `QueueMetadataDrift` / `RecoveryProjectionVisibleDrift` authoritative-side classifier now
  lives in `agent_doc_turn::closeout_recovery`. Orchestration keeps only HEAD,
  snapshot, visible-file loading, git/sidecar mutations, and calls the focused
  recovery policy directly.

- **Closeout recovery mutation reasons moved to `agent-doc-turn`.** The stable
  closeout recovery reason labels and capture-baseline refresh event/message
  mapping now live in `agent_doc_turn::closeout_recovery`. Capture, repair, and
  closeout code import the focused enum directly instead of routing through an
  orchestration-owned reason table.

- **Closeout recovery state decisions moved to `agent-doc-turn`.** The typed
  recovery state vocabulary, decision input, action-shaped decision enum, and
  pure state-to-decision mapping now live in `agent_doc_turn::closeout_recovery`.
  Orchestration keeps evidence gathering and file-specific recovery-command
  rendering, then calls the focused turn policy directly.

- **Bare prompt-prefix diff slicing moved to `agent-doc-diff`.** The
  marker-scoped helper that scans a unified diff before an inserted response
  heading now lives beside the prompt-bearing diff classifier. Session-check
  closeout guards call `agent_doc_diff` directly and no longer own a local copy.

- **Unstarted prompt-bearing diff selection moved to `agent-doc-diff`.** The
  queue/comment/frontmatter filtering helper, answered-existing-response
  suppression, and first actionable prompt-bearing change selector now live
  beside the prompt-bearing diff classifier. Session-check keeps snapshot,
  HEAD, and current-file adapters only.

- **Post-exchange comment directive policy moved to `agent-doc-diff`.** The
  ordinary HTML comment scanner after `agent:exchange`, user-note exemption, and
  post-exchange directive signal extraction now live beside comment stripping
  and prompt-bearing diff policy. Preflight keeps only warning assembly.

- **Lease TTL freshness moved to `agent-doc-lease`.** Drain-owner, plugin-owner, queue-edit-owner, recycle-yield, and recycle-inflight sidecars now call `agent_doc_lease::timestamp_is_fresh` directly instead of each re-owning the same saturating timestamp policy. The domain modules keep only lease/request bodies, paths, TTL env knobs, and side-effect adapters, with boundary coverage preventing local `*_is_fresh` wrappers from returning.

- **Managed capability-proof retry policy moved to `agent-doc-turn-executor`.** The proof retry budget, probe timeout defaults, frontmatter/config precedence, and exponential backoff decision now live in `agent_doc_turn_executor::capability_proof`. The supervisor start path gathers frontmatter/config facts and calls the focused API directly, while `agent::mod` keeps only backend resolution/runtime helpers and no longer owns the policy or retry decision.

- **Managed capability-proof status message policy moved to `agent-doc-turn-executor`.** The operator-facing managed proof status line now lives beside the retry/timeout policy in `agent_doc_turn_executor::capability_proof`. The start supervisor path still handles tmux display and stderr side effects, but it no longer owns the message template.

- **Auto-trigger readiness policy moved to `agent-doc-turn-executor`.** The auto-trigger monitor, clear-cooldown deadline action, no-prompt hard-deadline action, and timeout/cancelled stop outcome now live in `agent_doc_turn_executor::auto_trigger`. The start supervisor thread keeps only sleep, pane inspection, logging, and startup-miss side effects while calling the focused executor API directly.

- **Codex resume launch policy moved to `agent-doc-turn-executor`.** The `codex resume` restart argument transformation now lives in `agent_doc_turn_executor::codex_launch`, including sandbox flag translation, conflicting sandbox rejection, missing-value diagnostics, and `--add-dir` stripping for resume. `harness.rs` keeps only harness selection and calls the focused executor API directly.

- **Context usage policy moved to `agent-doc-model-tier`.** Harness transcript token aggregation, Claude project transcript path composition, model-context window lookup, Codex `token_count` percentage parsing, and the clear/no-clear diagnostic decision now live in `agent_doc_model_tier::context_usage`. Orchestration's `context_pct` module keeps only file-backed transcript reads and newest-transcript discovery, and the Codex hook calls the focused clear-decision API directly.

- **Preflight model attribution policy moved to `agent-doc-model-tier`.** Harness alias canonicalization, frontmatter-vs-active harness mismatch warning facts, short response-header model attribution, and deferred Claude Code `opus` attribution now live in the focused model-tier crate. Preflight keeps only JSON warning adaptation and calls the model-tier API directly.

- **Claim cross-session admission moved to `agent-doc-controller`.** The `CrossSessionDecision` enum, structured reject marker, stale-session/force decision, and foreign-supervisor lease guard now live in `agent_doc_controller::claim`. The `claim` orchestration module keeps only tmux/session/file side effects and calls the focused controller API directly.

- **Cross-document owner command-line recognition moved to `agent-doc-controller`.** The pure process-command classifiers that identify long-lived agent-doc/harness owner sessions and extract the bound markdown document now live in `agent_doc_controller::command_line`. Sync keeps only the claimed-file path-matching adapter and calls the focused controller API directly.

- **Route dispatch drain-retry policy moved to `agent-doc-controller`.** The `#pcp3a` concurrent-finalize drain retry decision now lives in `agent_doc_controller::dispatch` beside the other dispatch admission helpers. `route.rs` still performs repair/session-check/file IO, but it calls the focused controller API directly instead of owning the retry decision table.

- **Dispatch-only busy/probe policy moved to `agent-doc-controller`.** The decision for when a dispatch-only route should wait for a busy actor to become ready, and when it should probe the live pane for active-turn wording before direct submit, now lives in `agent_doc_controller::dispatch`. `route.rs` maps persisted actor state into the controller dispatch vocabulary and keeps only tmux capture, logging, queue fallback, and refusal formatting.

- **Authoritative actor dispatch guard moved to `agent-doc-controller`.** The supervisor-health/runtime-state gate that decides whether an authoritative actor can accept routed dispatch now lives in `agent_doc_controller::dispatch`. Orchestration maps supervisor IPC facts into the controller dispatch vocabulary and keeps only degraded-pane fallback, logging, and tmux/process adapters.

- **Dispatch-start proof classification moved to `agent-doc-controller`.** The routed dispatch-start proof vocabulary, accepted-only fail-closed decision, and dispatch-only proof-required default now live in `agent_doc_controller::dispatch`. Orchestration still observes tmux/hooks and formats operator diagnostics, but calls the focused controller policy directly.

- **Direct-pane submit policy moved to `agent-doc-controller`.** The direct-pane submit acceptance status, acceptance timeout/budget, and acceptance-vs-dispatch-proof outcome classifier now live in `agent_doc_controller::dispatch`. Route keeps tmux capture/polling and latency logging, but imports the focused controller policy directly without wrapper functions.

- **Direct-pane dispatch-start wait policy moved to `agent-doc-controller`.** The rule that dispatch-only direct-pane sends can skip the optional dispatch-start proof after accepted input, while timed-out or startup sends still await stronger proof, now lives in `agent_doc_controller::dispatch`. Route keeps only its mode flags and tracker/tmux effects.

- **Direct-pane resubmit proof-line rendering moved to `agent-doc-controller`.** The `route_submit_resubmit` result label and operator-greppable message shape now live with the rest of controller dispatch logging policy. Route still supplies file, pane, harness, editor-attempt, and tmux submit-key facts, but no longer owns the formatting rule.

- **Routed trigger payload admission moved to `agent-doc-controller`.** The Codex-specific guard that rejects non-bare or multiline reroute payloads now lives with controller dispatch admission policy. Route builds the concrete payload and asks the focused controller API directly; the trivial `routed_trigger_payload` wrapper and route-local validator are deleted.

- **Direct-pane acceptance polling moved to `agent-doc-controller`.** The stable-empty-capture window and "visible draft disappeared" acceptance state machine now live beside the direct-pane submit outcome policy in `agent_doc_controller::dispatch`. `route/dispatch.rs` still captures panes and writes diagnostics, but it adapts each poll observation into the focused controller state instead of owning the transition rule.

- **Direct-pane Enter-resubmit policy moved to `agent-doc-controller`.** The default retry cap, visible-draft eligibility, bounded re-submit continuation rule, and existing-draft submit gate now live in `agent_doc_controller::dispatch`. Route keeps the env override and tmux submit-profile adapter, but no longer owns direct-pane re-submit policy wrappers.

- **Dispatch-only retry budgets moved to `agent-doc-controller`.** The authoritative-actor ready retry budget and dispatch-only starting-pane ready/recovery timeouts now live in `agent_doc_controller::dispatch`. Route still applies the operator `--wait-for-ready` override and performs tmux polling, but the harness/test-mode timeout table is no longer owned by `flow::routed_reopen` or route-local wrappers.

- **Starting-timeout recovery policy moved to `agent-doc-controller`.** The durable `starting_actor_timeout` reason, blocked-actor facts, and prompt-proof recovery classifier now live in `agent_doc_controller::dispatch`. Route still persists timeout sidecars and polls tmux panes, but it adapts actor records into the focused controller policy directly instead of owning the recovery rule.

- **Authoritative actor effective-state policy moved to `agent-doc-controller`.** The rule that persisted terminal actor states (`blocked`/`closed`) win over fresher runtime IPC, while non-terminal records may be refreshed by runtime state, now lives in `agent_doc_controller::dispatch`. Route only maps SQLite actor states into the focused lifecycle enum before applying the controller policy.

- **Route closeout-block action selection moved to `agent-doc-controller`.** The decision that prefers queuing an available reroute prompt after closeout, otherwise waits behind an existing active queue head, otherwise fails closed now lives in `agent_doc_controller::dispatch`. Route still computes closeout recovery and reads the document queue head, then calls the focused controller classifier directly.

- **Route startup-miss recovery policy moved to `agent-doc-controller`.** The fresh-start, live-owner restart, newer-open-start supersession, and stranded-session fail-closed decisions now live in `agent_doc_controller::dispatch`. Route keeps the startup-miss sidecar/log IO and maps those records into focused controller facts before acting.

- **Fresh-start ack outcome policy moved to `agent-doc-controller`.** The
  no-cycle fresh-start decision that keeps dispatch-ready idle no-ops but reaps
  genuine startup misses now lives in `agent_doc_controller::dispatch`. Route
  startup still captures panes and detects dispatch-ready prompts, then passes
  those facts into the focused controller policy.

- **Routed cycle-ack policy moved to `agent-doc-controller`.** The decision that prompt-bearing reroutes require a new cycle ack only when no baseline cycle is already open, and the Codex live-child missing-ack optimism rule, now live in `agent_doc_controller::dispatch`. Route still waits on cycle-state sidecars and records startup-miss evidence, but it adapts cycle/harness facts into the focused controller policy.

- **Route trigger matching policy moved to `agent-doc-controller`.** Recent
  composer-line trigger detection, prompt-prefix stripping, whitespace-compacted
  wrapped-trigger matching, and trigger boundary checks now live in
  `agent_doc_controller::dispatch`. Route and supervisor detection keep only
  tmux capture/current-output adapters.

- **Route submit-observation policy moved to `agent-doc-controller`.** The accepted/trigger-still-visible/capture-failed/dispatch-proof observation vocabulary, issue mapping, and structured route-submit log rendering now live in `agent_doc_controller::dispatch`. Route remains the ops-log adapter that supplies file, harness, elapsed-time, and editor-attempt facts.

- **Route latency policy moved to `agent-doc-controller`.** The route latency budget status classifier and structured `route_latency` log formatter now live in `agent_doc_controller::dispatch`. Route still measures elapsed time and emits ops-log/stderr records, but it adapts those timings into the focused controller policy.

- **Route dispatch diagnostic message policy moved to `agent-doc-controller`.** Startup-miss, busy-route, queued-busy, and dispatch-only busy-refusal operator message templates now live beside controller dispatch policy. Route still supplies file, harness, recovery-hint, wait-time, and outcome-field facts, but no longer owns those diagnostic templates.

- **Duplicate-pane route diagnostic policy moved to `agent-doc-controller`.** The fail-closed message for refusing to provision duplicate route panes now lives in `agent_doc_controller::dispatch`. Route startup still supplies tmux session, file, anchor-pane, and split-failure facts, but the inspection/cleanup command template is no longer owned by orchestration.

- **Route dispatch bug-report item policy moved to `agent-doc-controller`.** The `#jbrunautobug` backlog item template, symptom dedupe marker, route-submit issue marker, and dispatch proof evidence fields now live in `agent_doc_controller::dispatch`. Route still supplies actor generation, editor attempt, diagnostic path, ops-log path, and performs the configured target-document write.

- **Dispatch-only proof outcome policy moved to `agent-doc-controller`.** Dispatch-only delivery vocabulary, unproven-progress policy, accepted-only proof log lines, sent-console text, and accepted-only refusal text now live in `agent_doc_controller::dispatch`. Route dispatch keeps tmux/file side effects and imports the focused controller templates directly without route-local message wrappers.

- **Routed reopen timeout budgets moved to `agent-doc-controller`.** Dispatch-start proof, fresh-start ack, routed-cycle ack, and existing-pane readiness timeout tables now live in `agent_doc_controller::dispatch`. Route modules pass harness/live-child/test-mode facts directly and no longer wrap the budget functions through `flow::routed_reopen`.

- **Routed reopen decision policy moved to `agent-doc-controller`.** The route decision enum, authoritative actor dispatch-state vocabulary, reopen action classifier, prompt-ready barrier classifier, starting-actor log templates, busy-pane auto-fix decision, degraded-authoritative-actor admission, and dispatch-only blocked guard reason now live in `agent_doc_controller::dispatch`. `flow::routed_reopen` is reduced to the FlowEvent/log adapter, and route modules import the focused controller API directly.

- **ACK-mismatch recovery policy moved to `agent-doc-document-realtime`.** The
  pure classifier that distinguishes stale queue-prompt ACK artifacts from
  missing-agent-response ACKs now lives in `agent_doc_document_realtime`. Write
  convergence keeps editor refresh effects and supplies the existing transient
  marker normalizer as an adapter.

- **Tmux submit profile policy moved to `agent-doc-tmux-commands`.** The harness submit profile, submit-mode/key vocabulary, trailing-newline trimming, and text/Enter command builders now live in `agent_doc_tmux_commands`. `sessions` remains the effect adapter that executes tmux commands, while route/start/idle-watch import the focused command policy directly instead of routing through `sessions`.

- **Route submit diagnostics no longer use an orchestration wrapper.** Route dispatch now calls `agent_doc_tmux_commands::tmux_submit_transform_for_harness` and `tmux_submit_key_for_harness` directly for text-submit diagnostics. The local `routed_trigger_submit_diagnostic` facade is deleted and covered by the tmux command boundary guard.

- **Submit-text normalization no longer routes through orchestration.** The supervisor IPC `normalize_submit_text` helper and route `routed_trigger_submit_payload` wrapper are deleted. Raw PTY, supervisor IPC, queue dispatch, route, and session-clear callers now consume `agent_doc_tmux_commands::submitted_text_without_trailing_line_endings` directly, leaving orchestration to add only transport-specific submit bytes.

- **Pane position selection moved to `agent-doc-tmux`.** The tmux pane geometry format, parser, and left/right/top/bottom selector now live in the focused tmux crate. `sessions` only queries `list-panes` and calls `agent_doc_tmux::select_pane_by_position` directly, with boundary coverage preventing the parser from returning to orchestration.

- **Bare-shell pane command policy moved to `agent-doc-tmux`.** The `#{pane_current_command}` shell-name classifier for dead harness detection now lives with focused tmux observations. Route dispatch still reads tmux state and captures panes, but imports `agent_doc_tmux::pane_current_command_is_bare_shell` directly instead of owning the shell list.

- **Route-owned reap policy moved to `agent-doc-supervisor`.** The route-owned supervisor completion reap policy, liveness reason vocabulary, and hidden CLI reap-policy parser now live in `agent_doc_supervisor::route_owned`. The start path keeps only document liveness/file adapters and calls the focused supervisor API directly; the CLI shell imports the focused type instead of preserving an orchestration API path.

- **Route-owned document liveness policy moved to `agent-doc-supervisor`.** Backlog body, queue body, and exchange-tail prompt classifiers used by route-owned reap decisions now live beside the route-owned reap policy in `agent_doc_supervisor::route_owned`. The start path keeps only file reads, component slicing, and committed-hash comparison.

- **Stale install-artifact policy moved to `agent-doc-supervisor`.** The `#install-stale-guard` grace window and timestamp classifier now live in `agent_doc_supervisor::config` beside auto-install/stale-binary policy. Preflight still discovers artifact mtimes and formats the warning, but calls the focused supervisor API directly instead of owning the staleness rule.

- **Supervisor prompt/exit-code policy moved to `agent-doc-supervisor`.** Restart/quit prompt input classification and forwarded Ctrl-C clean-exit normalization now live in `agent_doc_supervisor::crash_policy` beside the child crash/restart policy. The start supervisor loop still owns stdin, tty, and child status adapters, but calls the focused supervisor API directly.

- **Supervisor restart-continuation policy moved to `agent-doc-supervisor`.** Clean-exit prompt-vs-continue resolution, failed resume handoff tracking, early clean-exit-before-prompt detection, and restart-continuation branch selection now live in `agent_doc_supervisor::crash_policy`. The start loop adapts harness and auto-trigger observations into the focused API and keeps only the prompt/tmux/process effects.

- **Supervisor child-launch planning moved to `agent-doc-supervisor`.** The
  fresh-vs-continue launch argument decision and auto-trigger resubmit flag now
  live in `agent_doc_supervisor::run_loop`. `start/run.rs` keeps only harness
  argument construction, process IO, and logging.

- **No-follow-up closeout heuristic moved to `agent-doc-turn`.** Explicit "no actionable follow-up" response detection now lives in `agent_doc_turn::heuristics` beside pending-capture recommendation detection. Session-check and pre-commit guards import the focused turn API directly, and boundary coverage prevents `prompt_contract` from re-owning the phrase policy.

- **Reaped directive response-loss detection moved to `agent-doc-turn`.** The pure detector that decides whether a reaped `do #id` queue head lost its `### Re:` response now lives in `agent_doc_turn::closeout_signal`. Session-check still gathers cycle state, live exchange text, and HEAD compact archives, but calls the focused turn API directly and keeps only guard formatting/ops-log adapters.

- **Blocked follow-up and gated-phase closeout policy moved to `agent-doc-turn`.** The phrase policy for blocked future-action closeouts, explicit no-follow-up justifications, paragraph-scoped id tying, and gated multi-phase body split detection now live in `agent_doc_turn::closeout_signal`. Session-check keeps only review/backlog file adapters and warning formatting, with boundary coverage preventing those detectors from returning to orchestration.

- **Queue-audit partial-completion collapse detection moved to `agent-doc-turn`.** The pure classifier that catches "none complete" queue audits while completed substeps are cited now lives in `agent_doc_turn::closeout_signal`. Session-check still loads the committed capture and formats the warning, but no longer owns the phrase table or none-complete regex.

- **Free-text queue response-proof policy moved to `agent-doc-turn`.** The string-level checks for bare-heading residue under `<!-- no-free-text-queue-head-guard -->` and plausible free-text response proof now live in `agent_doc_turn::closeout_signal`. Session-check keeps only cycle-state/document adapters and calls the focused turn API directly, with boundary coverage preventing local helper copies from returning.

- **Partial-closeout shipped/remaining-work policy moved to `agent-doc-turn`.** The phrase table and shipped-response predicate behind `#do-id-partial-closeout-state` now live in `agent_doc_turn::closeout_signal`. The partial-closeout session-check guard still adapts capture/cycle/backlog state, but calls the focused turn API directly instead of importing phrase helpers through another guard module.

- **Closeout response text normalization moved to `agent-doc-turn`.** The template-patch-aware helper that chooses exchange/findings/unmatched text for closeout guards now lives in `agent_doc_turn::closeout_signal`. Session-check and pre-commit pending checks call the focused turn API directly, and the old `session_check::response_text_for_guards` route is removed.

- **Queue command/prompt classification moved to `agent-doc-queue`.** The response-contamination guard no longer owns the queue directive-shape classifier or slash-command-reference detector. Those pure queue prompt decisions now live in `agent_doc_queue::queue_command`, and `session_check::response_guards` calls the focused queue API directly.

- **Queue prompt preservation identity moved to `agent-doc-queue`.** Dropped queue prompt recovery now uses focused queue APIs for line normalization, struck/consumed id accounting, and queue component matching. `response_guards` keeps only guard-state and exchange-response adapters, with boundary coverage preventing local queue identity helpers from returning.

- **Supervisor idle-reconcile policy moved to `agent-doc-supervisor`.** The stale busy-over-idle and ready-with-queued-draft reconcile decisions now live in `agent_doc_supervisor::idle_reconcile`. `start` and idle-watch gather pane/harness facts and pass their debounce thresholds directly to the focused policy; boundary coverage prevents the orchestration decision functions from returning.

- **Supervisor self-kill policy moved to `agent-doc-supervisor`.** The graceful turn-boundary action, force-kill grace escalation decision, and route-owned supervisor cmdline parser now live in `agent_doc_supervisor::selfkill`. Orchestration keeps only sentinel files, `/proc` inspection, and signal adapters, with boundary coverage preventing local self-kill policy helpers from returning.

- **Supervisor write-wedge evidence classification moved to `agent-doc-supervisor`.** The pure `write_wedged` classifier that turns repeated active-listener IPC write refusals into supervisor recycle evidence now lives beside `supervisor_recycle_action` in `agent_doc_supervisor::lifecycle`. `write::converge` keeps only de-wedge marker persistence and ops-log adapters, with boundary coverage preventing the classifier from returning.

- **Manual queue-addition compatibility shim deleted.** `agent-doc-queue` now exposes only `operator_authored_prompt_identities` for the operator-added prompt identity path; the unused `annotate_manual_queue_additions` shim is removed and covered by a source guard.

- **Focus pane selection moved to `agent-doc-tmux`.** The stale projection/registry vs live-owner pane decision now lives with focused tmux state policy. `focus` imports `agent_doc_tmux::decide_focus_pane` directly and remains only the file/session/tmux adapter; boundary coverage prevents the pure focus decision from returning to orchestration.

- **Merge-control ownership no longer has an orchestration facade.** The pure merge ownership state machine is imported from `agent_doc_merge::ownership` directly, while the runtime plugin-owner lease adapter (`ownership_liveness_for_file`, `disk_write_permitted_for_file`) now lives beside the plugin-owner sidecar code that supplies those facts. Deleted `agent-doc-orchestration/src/merge_control_state_machine.rs` and added a boundary guard so orchestration cannot re-export that focused merge API again.

- **Drain-stall turn policy no longer routes through an orchestration re-export.** `preflight` imports `classify_stall`, `StallFacts`, and `StallVerdict` directly from `agent_doc_turn::drain_stall`; `agent-doc-orchestration::drain_stall` now owns only the one-shot continuation marker sidecar IO. Added a boundary guard to keep the pure classifier in `agent-doc-turn`.

- **Log timestamp helpers no longer route through `ops_log`.** Orchestration log writers and readers call `agent_doc_log_time::{format_log_timestamp, parse_log_timestamp}` directly, while `ops_log` keeps only operation-log persistence/content concerns. Added a boundary guard so the timestamp facade does not return.

- **Controller dispatch helpers no longer route through `project_controller::rpc` facades.** The RPC adapter imports `agent_doc_controller::dispatch` privately, and route authorization calls the focused coalesced-dispatch classifier directly. Added a boundary guard so pure dispatch admission helpers stay owned by `agent-doc-controller`.

- **Cycle phase vocabulary no longer routes through `cycle_state`.** Orchestration sidecar code and CLI helpers import `agent_doc_turn::CyclePhase` directly, while `cycle_state` keeps only durable cycle sidecar persistence and transition application. Added a source-wide guard against the old `cycle_state::CyclePhase` facade path.

- **Project tmux-session helpers no longer route through `config`.** The global config module now owns only user/global config types and loading; session, claim, route, resync, and start paths call `project_config_io::{project_tmux_session, update_project_tmux_session, clear_project_tmux_session}` directly. Added a guard so the config facade does not return.

- **Project-root discovery no longer routes through `snapshot` or orchestration.** New focused crate `agent-doc-fs` owns project-root discovery and optional file reads; `snapshot`, orchestration, and CLI callers import `agent_doc_fs` directly. Added a source-wide guard so the project-root facade does not return.

- **Session actor storage types no longer route through `session_actor`.** `ActorState`, `ActorRecord`, and `ActorLastTransition` are imported directly from `agent_doc_sqlite::state_store`; `session_actor` now keeps only ownership-transition lifecycle helpers. Added a guard so the SQLite actor-type facade does not return.

- **Project controller status types no longer route through `project_controller`.** Controller/operator status records and SQLite state helpers are imported directly from `agent_doc_sqlite::state_store`; `project_controller` keeps only controller lifecycle, socket, and projection glue. Added a guard so the SQLite controller-status facade does not return.

- **Tmux-router types no longer route through `sessions`.** Callers now import `Tmux`, `IsolatedTmux`, `PaneMoveOp`, `Registry`, `RegistryEntry`, and `RegistryLock` from `tmux_router` directly, while `sessions` keeps only agent-doc-specific registry/path/capture helpers. Added a guard so the tmux-router type facade does not return.

- **Terminal resize effects moved to `agent-doc-supervisor-process`.** `ResizeWatcher` and `query_terminal_size` now live in the focused process-effect crate; orchestration's supervisor module no longer owns or re-exports `supervisor::resize`. Added a guard so the resize facade does not return.

- **Queue continuation guidance moved to `agent-doc-queue`.** The no-stall/recycle-yield guidance constants and pause-aware `continuation_guidance` builder now live beside the pure queue continuation drainability policy; preflight and session-check import `agent_doc_queue::queue_continuation` directly, and orchestration keeps only file/controller/marker adapters. Added a guard so the guidance facade does not return.

- **Review projection and ungate planning moved to `agent-doc-element-review`.** `ReviewItemView`, `ReviewListFilter`, `UngateTasksReport`, and the pure review-item projection/filtering plus ungate-task planning logic now live with the review element model. `pending_cmd` remains only the file-IO/write adapter, and the CLI constructs filters from `agent_doc_element_review` directly. Added a guard so the projection/planning facade does not return.

- **Partial-staging closeout diff policy moved to `agent-doc-diff`.** The source/test path relatedness filter and changed string/backtick literal extraction now live with pure diff classification. `session_check::partial_staging` remains the git/file adapter and calls `agent_doc_diff` directly. Added focused unit coverage and a boundary guard so orchestration does not re-own the policy.

- **Id-backed queue directive parsing moved to `agent-doc-queue`.** `do #id` / `do [#id]` / optional bare leading `#id` target extraction now lives in `agent-doc-queue::queue_directive`, backed by ordered tracked-work `#id` scanning in `agent-doc-element-backlog`. Session-check, preflight, and controller callers import the focused queue API directly instead of routing through `session_check`. Added focused parser coverage and a boundary guard so the directive parser does not return to orchestration.

- **Closeout response and done-signal parsing moved to `agent-doc-turn`.** `agent_doc_turn::closeout_signal` now owns response-heading completion detection plus explicit/plain done-signal parsing, reusing the tracked-work id scanner from `agent-doc-element-backlog`. Session-check keeps only file/cycle adapters and calls the focused turn API directly, with boundary coverage preventing the old orchestration-owned parser from returning.

- **Supervisor child crash/restart policy moved to `agent-doc-supervisor`.** `agent_doc_supervisor::crash_policy` now owns the child-exit classifier, bounded restart history, health state, and restart action decision. The start and in-process supervisor adapters import the focused policy directly, and the old `supervisor::state` orchestration module is deleted with boundary coverage preventing a facade from returning.

- **Supervisor config and controller recycle policy facades removed from `project_controller::rpc`.** File/env-backed orchestration adapters now call `agent_doc_supervisor::config` and `agent_doc_controller::recycle` directly. Boundary coverage prevents the pure precedence/debounce/force-bypass wrappers from returning.

- **Stale-queue dispatch recovery moved fully to `agent-doc-controller`.** `agent_doc_controller::dispatch` now owns the typed stale-queue recovery record and binary proof vocabulary, route authorization calls the focused classifier directly, and `project_controller::rpc` no longer wraps that recovery policy. Boundary coverage prevents the RPC facade from returning.

- **Stale host-supervisor binary policy moved fully to `agent-doc-supervisor`.** `agent_doc_supervisor::config` now owns the auto-install retry decision and route-owned host supervisor inode staleness predicate. `project_controller::rpc` calls the focused crate directly, with boundary coverage preventing those wrappers from returning.

- **Controller command-line recognition no longer has orchestration wrappers.** Project-controller process scanning and duplicate-controller matching call `agent_doc_controller::command_line` directly. Boundary coverage prevents the local forwarding helpers from returning.

## 0.34.64

- **`#suprestassoc` — `restart-supervisor` registry re-association is scoped to the requested document, not just the reused session id.** The CLI restart path now resolves registry entries by exact canonical document path first and only keeps the legacy session-id fallback for fileless registry rows; registry keys that explicitly name another document are rejected, so restarting `sampleportal.md` cannot adopt a stale `agent-doc-bugs2.md` pane/session projection that happens to carry the same session id. Coverage: deterministic registry lookup regressions for rejecting a foreign same-session entry and preferring the exact target document entry. Live editor/pane confirmation remains an `[operator-verify]` follow-up.

## 0.34.63

- **Agent-doc skill installs now ship an OKF concept bundle for durable dynamic-context vocabulary.** The shared `SKILL.md` router points concept/vocabulary lookups at `okf/index.md`, while branch procedures remain in runbooks and current-state packs remain in `preflight`/`plan`/`tsift` output. `agent-doc skill install` now reconciles bundled OKF Markdown files beside each managed harness surface (`.claude/skills/agent-doc/okf`, `.codex/okf`, `.opencode/skills/agent-doc/okf`, plus Cursor/Generic paths), reaping stale managed Markdown while preserving local non-Markdown artifacts. README, SPEC, and dev instructions now treat bundled OKF resources as part of the instruction-surface contract. Coverage: installer unit tests for OKF paths/reaping/all-env installs plus the CLI skill-install integration test proving the OKF index is installed.

## 0.34.62

- **`#closeoutstall` — proven live-editor no-ACK closeouts now have a typed blocked state instead of an ambiguous direct-Codex stall.** When the write path has live editor evidence but cannot prove editor convergence (`no_listener`, `no_ack_content`, `ack_mismatch`, `no_ack`, or `send_failed`), it now records `blocked_closeout.kind=editor_convergence_required` on the open cycle with the source, reason, optional patch id, `retry_without_disk_write`, and a copyable recovery command. `session-check` reports that canonical operator-gated diagnostic before falling through to generic repair/commit-boundary recovery, and committed cycles clear the blocked state. This preserves the safety invariant — no automatic disk write behind a proven live editor — while making the stalled response actionable. Coverage: focused `session-check` regression plus the existing editorless-socket no-ACK write-path test.

- **`#tmuxsynccrash` — `repair_layout` no longer creates a stash window for an already-correct zero-stash agent-doc layout.** The root crash loop was the full sync doctor repair never converging on clean one-window layouts: `target=true stash_count=0` forced `ensure_stash_window`, then later syncs bounced through destructive `move-window`/`swap-window`/`join-pane`/`resize-window` churn that can crash tmux 3.7 under a JB Sync Tmux Layout burst. The repair fast path now skips the destructive rescue phase when the target window exists with either zero stash windows or one canonical stash; target/stash index normalization still runs afterward. The branch already had the companion `#tmuxsynccrash` guards for passive JB supersede reruns and per-session destructive repair throttling. Coverage: pure rescue-phase regression plus an ignored live-tmux no-op regression for `make tmux-ci`.

## 0.34.61

- **`#rtphase2watch` — filesystem watch changes now route through the per-document actor and persist typed backbone events.** Phase 2 of the reliable realtime cutover now has a durable event seam for watcher decisions: `state_events` is an append-only SQLite ledger keyed by idempotent `event_id`, `StateFact::FileWatchChangeObserved` reduces into the document projection, and controller startup rebuilds the in-memory `StateBackboneProjection` from that ledger. The legacy `watch.rs` debounce loop no longer calls `run_with_context` directly; it hands the settled event to `document_watcher::route_legacy_submit`, which serializes the submit through `SessionOpKind::FileWatch` while the admission scheduler is still being built. Coverage: state-store ledger count/idempotency, backbone watch replay, and watcher actor-routing projection reload.

- **`#qmarkerauth` — adding an attribute to a component marker (e.g. `<!-- agent:queue priority go -->`) is no longer silently deleted on the next merge.** Live repro (operator report #S9208): the operator added attributes / a `go` activation to the `agent:queue` marker while a turn was running and the CRDT merge reverted the marker to the agent-side (snapshot) framing, dropping the operator's edit. Root cause: `reconcile_component` (`agent-doc-core/src/crdt.rs`) and the cell-merge component branch (`agent-doc-core/src/cell_doc.rs`) both reframed the merged body with `ours_open`/`ours_close` unconditionally, so an operator marker edit on `theirs` (the live editor / disk side) was always discarded. Fix: a new `reconcile_marker_operator_authoritative` helper merges the marker line 3-way — `theirs` (operator) wins whenever it differs from base, an agent-side change is honored only when the operator left the marker at base, and with no base epoch the operator side is authoritative. This is the marker analogue of the field-wise frontmatter merge (`#fmreset`) and the list-order theirs-spine (`#qauthorder`): an operator marker edit is never reverted by the CRDT/snapshot path. Coverage: `merge_by_component_preserves_operator_marker_attribute_qmarkerauth`, `reconcile_marker_operator_authoritative_resolution`, plus the cell-merge equivalents.

- **`#pcwcrt` — the post-commit working-tree revert tower is removed; post-commit drift is now observe-only.** The legacy `postcommit_worktree_lost_committed_content` / `send_postcommit_editor_refresh` tower (`agent-doc-orchestration/src/git.rs`) would, after a commit, silently revert the working tree back to HEAD or push a stale-snapshot-derived buffer back to the editor when it judged committed content "lost" — the recurring `#postcommit-ipc-worktree-corruption` source that reverted legitimate operator edits and re-derived the buffer from a stale snapshot. It is replaced with observe-only handling: the binary records post-commit drift (and preserves a carry-forward superset) but never reverts an operator working-tree edit. The FlowCore hot-path `reason=` token budget for `git.rs` drops 20 → 7 accordingly (the surviving non-reverting `reason=` annotations are re-audited in `tests/test_cli.rs`). Coverage replaces the auto-reconcile tests with `postcommit_worktree_observe_only_never_reverts_lost_content` and `postcommit_worktree_preserves_carry_forward_superset`. Together with `#qmarkerauth` this closes the operator's "agent-doc deletes attributes added to `agent:queue` / CRDT reverts my changes" report.

## 0.34.60

- **`#qcompactfp` — a queued bug-report head that begins with "compact exchange" no longer falsely aborts finalize.** Live repro this session: the operator added an `agent:queue` free-text head `- Compact exchange should commit the compacted content` mid-turn; the next `finalize` aborted with *"bare `compact exchange` directive detected in the current diff"* and refused to commit the response. Root cause: `detect_exchange_compaction_request` (`agent-doc-core/src/diff.rs`) scanned every added diff line and matched any whose normalized text starts with `compact exchange` / `compact the exchange`, with no awareness that the line was a queue **task entry** rather than a same-turn exchange directive. Fix: a genuine same-turn compaction directive is exchange prose (optionally `❯`-prefixed), never a markdown list item, so the detector now skips added lines that open with a list bullet (`- `/`* `/`+ `/ordered `N. `) via the new `trimmed_is_markdown_list_item` helper (mirrors the existing blockquote skip). The bare-directive and `❯`-prefixed positive cases are unaffected. Coverage: `detect_exchange_compaction_request_ignores_queue_list_item` (3 bullet spellings + a still-matching prose control).

## 0.34.59

- **`#qheadstrikeauto` — an answered free-text drain target is auto-struck off the binary's marker identity, not the agent's prose formatting.** Operator: "the binary should do this automatically...not the agent." Previously `strike_answered_free_text_queue_heads` (via `answered_free_text_head_node_keys`, `queue_consume.rs`) only struck a free-text head when the committed response *quoted* it as a `> **Queue prompt:**` blockquote (`free_text_head_answered_by_response` searches only the response's blockquote region), so a head answered in plain prose was never struck and the go-mode loop churned no-op `#qchurn` cycles until the operator cleared it by hand. Fix: the binary already stamps the cycle's drain target with the in-progress `🚧` marker during preflight queue maintenance (`set_first_prompt_in_progress`). A **free-text** head carrying that marker IS the head this cycle was dispatched to drain, so on a committed (non-empty) response it is answered by definition — struck without requiring any agent prose. New `head_carries_in_progress_marker` helper detects the `🚧` marker (cosmetic-marker-aware via the existing `strip_priority_markers`/`display_queue_prompt_text` path); `answered_free_text_head_node_keys` now strikes a head when it is the drain-target marker head OR the response prose-matches it (the secondary `#ftstrike` signal that still strikes non-marker free-text heads answered at any position). Safety preserved: the `#qstrikeexplain` Phase-2 baseline gate still defers a `🚧` head that first appeared this turn (in-flight operator edit), and P4 still routes id-backed marker heads through `--done` (the free-text pass only ever strikes free-text marker heads). Coverage: `marker_head_struck_without_blockquote_quote_qheadstrikeauto`, `marker_head_not_struck_when_absent_from_baseline_qheadstrikeauto`, `id_backed_marker_head_not_struck_by_free_text_pass_qheadstrikeauto`. Plan: `tasks/agent-doc/plan-freetext-queue-head-autostrike.md`.

## 0.34.58

- **`#actorprune` — dead actor records are pruned, not accumulated forever.** `close_stale_starting_actors` only TRANSITIONS `Starting` actors to `Closed`; nothing ever removed records already `Closed`, so the controller actor store (`documents` table) and `admin list` grew without bound (operator observed 251 dead `session=session-clear` rows in one project). New `prune_dead_actors` (`project_controller.rs`) hard-removes a record when ALL hold: state is `Closed`, `last_transition.timestamp` is older than `DEAD_ACTOR_PRUNE_AFTER` (1h, matching the stale-`Starting` window), AND no fresh/alive supervisor lease owns its document/generation (`supervisor_lease_is_fresh_or_alive` — the pid-alive check means a dead supervisor's lingering lease never blocks the prune, and a live actor is never pruned). Backed by a new `delete_actor_document_tx` (`state_store.rs`) that removes the `documents` + `actor_transitions` + `supervisor_leases` rows for one document_id in a single transaction. Wired into `gc.rs` (default-on, dry-run honored) so `agent-doc gc` cleans the accumulated backlog. Every prune is logged (`gc_pruned_dead_actor document_id=… generation=… state=… age_secs=… reason=dead_closed_record`) — never silent. Subtlety handled: the prune refreshes the legacy `session-actors.json` projection DIRECTLY from the post-delete db rather than via `emit_actor_projection`/`load_actor_store`, because those re-run `migrate_legacy_actor_projection`, which — once the prune empties the `documents` table — would re-import the just-deleted rows back from the stale json and resurrect them. Coverage: `prune_dead_actors_removes_old_closed_records`, `prune_dead_actors_keeps_recent_closed`, `prune_dead_actors_keeps_live_lease`, `prune_dead_actors_dry_run_preserves`. Plan: `tasks/agent-doc/plan-prune-dead-actors.md`.
- **Workspace version consistency restored for the publish contract.** 0.34.57's bump moved only the top-level `[package]` version; the `agent-doc-core`/`agent-doc-orchestration`/`agent-doc-sqlite`/`agent-doc-markdown-ast` sub-crate versions and the inter-crate dependency `version =` refs stayed at 0.34.56, which `test_manifest_uses_publishable_dependency_contract` flags (it would break `cargo publish`). All workspace crate versions and dep refs are now bumped together.

## 0.34.57

- **`#provauth3` — binary-origin compaction is authoritative; the recovery heuristics no longer mistake it for a "manual cleanup" or a user prompt.** Replaces the highest-value content-inference recovery guesses with a *recorded origin* check, where origin is known (the binary authored the compaction). Live dogfood repro (agent-doc-bugs2.md, this session): a session resumed after `/clear` saw the stale pre-compact snapshot (33119 bytes) against the compacted visible file (19123 bytes) and (a) tripped `guard_no_stale_snapshot_reset_drift`'s "looks like a manual cleanup" refusal, forcing a manual `reset --from-current`+`commit` recovery, and (b) the preflight `repair` prompt-prefix normalizer (`normalize_user_prompts_in_exchange`) stamped every `### Session Summary` / `*Compacted…*` / `- Archived N response topic(s)…` line with `❯`, so the prompt classifier + session-check treated the binary-authored summary as an unresolved user prompt and falsely INTERRUPTed closeout, stalling the queue. Root cause is the #provauth thesis: agent-doc *inferred* provenance from a snapshot↔file content diff instead of reading a recorded origin fact, and the inference cannot tell a binary-authored compaction rewrite from operator text. Fixes:
  - `classify_stale_snapshot_visible_rebase` (`write/converge.rs`) no longer hard-requires a live `turn_scope` (absent after `/clear`). It consults the existing binary-authored `recent_exchange_compaction_timestamp` marker (survives `/clear`) as a known-origin signal: an exchange-only safe reduction rebases the stale snapshot when there is *either* a live turn scope (the pre-existing in-session path) *or* a recorded binary compaction. Non-exchange component drift still requires the scope and fails closed without it; a compaction-shaped shrink with **no** provenance signal still fails closed with the deterministic `reset --from-current` guidance (safety rail preserved).
  - New `line_is_binary_authored_compact_summary` (`agent-doc-core/src/diff.rs`), mirroring `line_is_binary_authored_ipc_proof_diagnostic`: recognizes the binary-authored Session Summary shapes (tolerating a `❯` an earlier mis-classification already applied) and is wired into `is_recovery_artifact_line`, `classify_prompt_bearing_block` (a block that is entirely summary content → `RecoveryArtifact`, never `PromptTarget`), the prompt-prefix normalizer's insert filter (never stamp `❯`), and the session-check unresolved-prompt-tail guard (`closeout_guards.rs`). A genuine user prompt that merely mentions compaction in prose still classifies normally.
  - Scope boundary: the operator-origin half of #provauth3 (treat an *operator*-origin working-tree change as authoritative, replacing `content_ours` / stale-editor resurrection heuristics) needs the live operator-origin delta signal from the `#crdtauth6` seam and is filed separately (#provauth3b) — origin is not yet "known" for operator edits until that lands. Coverage: `line_is_binary_authored_compact_summary_recognizes_summary_shapes`, `compact_summary_replacement_is_not_a_prompt_target`, `normalize_user_prompts_compact_summary_not_prefixed`, `stale_snapshot_reset_drift_rebases_compact_summary_after_clear_via_binary_origin_marker`, `stale_snapshot_reset_drift_blocks_compact_summary_without_scope_or_marker`; `make check` green (5434 tests).

## 0.34.56

- **`#midturnresumeb` Phase B reconciled onto main — active turn-resume on a fresh recycle boot.** Grafts the `midturn-recycle-resume` branch (`boot_resume_action`) onto current main, layered with the already-landed `#suprecyclespin` stalled-cycle-resolve (0.34.51) + `#recycledeadlock` break (0.34.52) it was originally cut before. New `boot_resume_action` (`start/decisions.rs`) decides, on a fresh supervisor image: **re-dispatch** the genuinely-interrupted turn from the `#durablerecycle` checkpoint (`queue_task_id`/`prompt_targets`) when the cycle is open AND the harness child died across the recycle AND the checkpoint is not yet consumed; **adopt-without-retrigger** when the child survived (the common Phase-A steady state); **None** for a closed/consumed checkpoint. Wired in `start/run.rs`; idempotency via a new `recycle_resume_consumed` cycle-state latch (`mark_recycle_resume_consumed`). The branch's escalation-counter backstop (`cycle_open_defer_escalates` / `MAX_CYCLE_OPEN_DEFER_TICKS`) is **kept and layered** on top of the stalled-resolve rather than dropped: the stalled-resolve clears `cycle_open` for an abandoned-older superseded cycle, and the escalation forces the recycle DECISION (`effective_cycle_open`) for a cycle that never closes for some *other* reason — so a never-closing cycle can never starve the stale-binary self-recycle. The escalation gates only the `supervisor_recycle_action` decision; the operator-restart reexec gate keeps plain `cycle_open` (it has its own `#recycledeadlock` path) and the post-decision `abandon_wedged_cycle_for_recycle` calls keep `cycle_open` so a forced recycle still abandons the open cycle. Coverage: `boot_resume_action_redispatches_only_when_cycle_open_and_child_died`, `mark_recycle_resume_consumed_latches_and_is_idempotent`, `cycle_open_defer_escalates_after_threshold_only`, the preserved `open_stalled_resolves_only_abandoned_older_turns`, plus SimWorld `never_closing_cycle_escalates_recycle_then_boot_redispatches_interrupted_turn` + `recycle_boot_with_surviving_child_adopts_without_redispatch`. The live mid-finalize-recycle proof (child dies → turn resumes once; second boot does not double-run) remains an `[operator-verify]` follow-up.
- **session-check is HEAD-tolerant — no more false "direct response patchback" wedge (`#patchback-head-tolerant`).** The patchback heuristic (`detect_bypassed_response_write`) compares the snapshot against the working tree, never against HEAD, so a response that WAS committed through `finalize` (it reached HEAD) but whose snapshot sidecar is stale, or whose working tree was re-drifted by the post-commit IPC listener (`#postcommit-ipc-worktree-corruption`), tripped a FALSE `INTERRUPTED: found likely direct response patchback without agent-doc cycle`. Now, before interrupting, `response_marker_committed_in_head` (`session_check.rs`) checks whether the flagged `### Re:` heading is present in HEAD (transient `(HEAD)`/boundary markers normalized); if so the binary's write/commit path DID run for it — it is not a bypassed patchback, so the guard logs `session_check_patchback_tolerated_head_committed` and falls through to the remaining guards (post-commit drift etc.) instead of wedging the session. A genuinely uncommitted response (heading absent from HEAD) still interrupts. Coverage: `response_marker_committed_in_head_is_head_tolerant`.
- **"tracked side-effect edits" no longer lists unrelated dirty submodules (`#side-effect-exclude-submodules`).** `tracked_modified_paths` (`git.rs`) enumerated every dirty tracked path from the git root — including unrelated sibling submodule gitlinks (`mail`, `src/sample-app`, `src/eval-runner`) that the agent-doc cycle never touched — making the closeout "side-effect" diagnostic misleading. A new best-effort `submodule_paths` (`git submodule status`) excludes registered submodule pointers from the side-effect accounting; a failed listing simply excludes nothing.

## 0.34.55

- **Live deletions racing finalize are preserved, not discarded (`#crdtsvdom`, content form — supersedes the literal state-vector-dominance mechanism).** Implements the keystroke-preservation plan (`plan-crdt-overlay-live-keystroke-preservation.md`). The CRDT merge-base classifier `overlay_carries_unbaselined_content` (`snapshot.rs`) decided overlay-ahead (preserve live keystrokes) vs overlay-behind (discard stale) by a markdown **line-multiset** scan. That scan only sees "ahead" when the overlay holds a line the baseline *lacks* (additions / within-line edits) — it is **blind to a pure deletion / within-line shrink** (overlay ⊆ baseline), which is content-identical to a genuinely older committed subset, so a line the operator deleted in the preflight→finalize window was classed "behind" and the overlay rebuilt from baseline, **resurrecting the deleted line**. **Why not literal yrs state-vector dominance (the plan's original mechanism):** the overlay sidecar is **always** a fresh, lineage-free `from_markdown(markdown).encode_state()` projection (`save_document_crdt` / `rebuild_overlay_crdt_locked`) — there is no shared causal history between the overlay and a baseline-projected doc (random `Doc::new()` client ids, no incremental op application), so a state-vector dominance test would be mathematically vacuous (always "divergent"). The only causal discriminator the architecture leaves is the live editor **op-capture** sidecar (`op_capture.rs`, `#qnodemerge4wire`): **pending** (not-yet-merged) editor ops prove a divergence is a live keystroke edit, while a stale overlay has none (a committed merge consumes+clears them). Fix: new `op_capture::has_pending_editor_ops` is consulted when the overlay projection differs from the baseline but the multiset scan found no unbaselined line — pending ops → ahead/preserve (the deletion survives in the sidecar for op-capture + next-cycle base); no pending ops → stale/discard (GC behavior unchanged). Over-reporting is the safe direction: a lingering sidecar at worst delays GC, never discards a live edit. The deeper lineage-ful fix remains the op-capture / live-delta-forwarding track (`#crdtauth5/6`). Coverage: `crdt_merge_base_state_preserves_pure_deletion_with_pending_editor_ops` (subset overlay → discard with no ops, → `OverlayAheadPreserved` with a pending delete op); existing stale-GC + ahead/behind/divergent suite (64 snapshot tests) green.

## 0.34.54

- **Multi-id directive queue heads are no longer struck before their work is done (`#qmultiidstrike`).** Live production repro (agent-doc-bugs2.md): the operator queued `do [#syncbarrier] [#crdtsvdom]`, and preflight's orphan-response repair struck it the moment a prior cycle's response *mentioned* the ids in prose — even though neither `#syncbarrier` nor `#crdtsvdom` had been implemented. Root cause: the free-text/id-backed classifier (`queue_head_is_free_text_prompt` + the per-entry `queue_prompt_text_is_free_text`, `write/queue_consume.rs`) recognized a head as id-backed only when it resolved to **exactly one** id (`topic_resolves_to_exact_id`). A **multi-id** directive head (`do [#a] [#b]`) resolves to more than one id, so the single-id test failed and the head fell through to "free text" — which the positional repair strike (`strike_recovered_free_text_queue_head`, `repair.rs`) and the finalize blockquote-echo strike then consumed by position/echo before the referenced ids were ever reaped. Fix: new `topic_resolves_to_only_id_directives` recognizes a head composed **entirely** of `do` + one or more `[#id]`/`#id` tokens (any count) as id-backed regardless of id count; it returns `None` (→ free text) the moment any token is free-text prose (`do [#foo] then ship it`, `re [#id]`, `Approve [#shoptiers]. What next?`), preserving the existing single-id, preset carve-out (`#qpresetstrike`), and prose-mention behavior. An id-backed multi-id head is now struck only once every referenced id is reaped (`--done`/`--pending-gate`/`queue consume`), never by the positional free-text heuristic. Coverage: `multi_id_directive_head_is_id_backed_not_free_text` (helper + both public classifiers end-to-end); full `queue_consume` suite (55 tests) green including the `qmisstrike` reordered-id-head regression guard.

## 0.34.53

- **IPC-truncated working tree recovers from the live editor buffer, not git (`#ipctruncrecover`, Phase 0).** Operator directive while dogfooding `agent-doc-bugs2.md`: a degraded editor-IPC write can leave the on-disk working tree TRUNCATED below HEAD while the live editor buffer still holds the authoritative content; the agent should not have to run `git stash`/`reset`/`checkout` to recover. The preflight layout guard (`enforce_no_uncommitted_closeout_drift`, `preflight.rs`) previously bailed on this `SnapshotDiffersFromHead` shape with a manual-recovery hint. It now adds a fail-open recovery arm BEFORE the bail: when a live IPC listener is attached, it flushes the editor buffer to disk (`send_save_document` — editor = source of truth), verifies via `editor_buffer_preserved_head_exchange` (`write/converge.rs`) that the flushed buffer did **not** drop HEAD's committed `exchange` response, then resets the snapshot to HEAD so the operator's edits read as the normal next-cycle prompt diff. Fail-open by construction: a missing listener, an un-acked flush, or a buffer that itself lost the committed response all fall through to the existing bail+hint — it never blocks typing and never auto-commits a response-less document. Markers: `ipc_truncation_recovered_from_editor_buffer` / `ipc_truncation_recover_rejected`. Phase 0 of the editor-sync barrier plan (`tasks/agent-doc/plan-editor-sync-barrier.md`); Phase 1 (epoch barrier prevention) + Phase 2 (`#crdtsvdom` state-vector dominance) follow. Coverage: 3 unit tests for the containment guard (preserved-with-edits / dropped-response / boundary-marker-insensitive).

## 0.34.52

- **Stale-binary recycle deadlock on an open cycle fixed (`#recycledeadlock`).** Live production repro (agent-doc-bugs2.md, route-owned host supervisor pid 124146): a `finalize` failed the editor-IPC proof on a stale binary (`supervisor_binary_stale` → `missing_response_probe` → `recovery=retry_without_disk_write`), leaving the cycle in `response_captured`. The operator/agent then ran `agent-doc admin recycle`, but the supervisor never recycled — a classic deadlock: **the commit needs the fresh binary, but the recycle needs the cycle closed.** Root cause: `supervisor_recycle_action` (`start/decisions.rs`) returned `DeferCycleOpen` for *any* open cycle, winning over `explicit_admin` / `write_wedged` / `reexec_failed`. That `#midturn-recycle-resume` interlock is correct *only* when the cycle can actually commit (then it closes sub-second); when the binary is **stale** AND a fact proves it cannot commit the open cycle, "wait for it to close" is an infinite wait — the finalize re-fails the IPC proof on the same bad binary forever and `DeferCycleOpen` fires ~2/sec indefinitely. Fix (Part A): the `cycle_open` defer no longer wins when `stale && (explicit_admin || write_wedged || reexec_failed)` — those three facts each PROVE the running binary cannot commit the cycle, so the recycle falls through to the stale arms (`RecycleImmediate` / `EscalateKillRelaunch`) and breaks the deadlock. A healthy open cycle (binary current enough to commit, or no admin/wedge/reexec proof) still defers, preserving the `#midturn-recycle-resume` and `#wd40` fresh-binary-flush guarantees. Fix (Part B — restart the interrupted task): when a recycle fires past an open cycle, the idle-watch (`start/idle_watch.rs`) force-abandons that wedged cycle (`abandon_wedged_cycle_for_recycle` → `mark_abandoned`, logged `supervisor_recycle_wedged_cycle_abandoned … reason=restart_interrupted_task_on_fresh_binary`) before the `execve` / kill+relaunch, so the fresh supervisor sees a clean slate and re-dispatches the still-unanswered `agent:queue` head that drove the interrupted turn instead of inheriting an un-committable cycle that would immediately re-wedge. The `#suprecyclespin` stalled-cycle resolver remains the backstop. Coverage: `supervisor_recycle_action_defers_while_agent_doc_cycle_open` extended with the three deadlock-break cases (stale + admin/wedge → `RecycleImmediate`, stale + reexec_failed → `EscalateKillRelaunch`) alongside the preserved healthy-defer + fresh-binary-flush-defer cases.
- **Stale-supervisor freshness warning no longer cautions against force/discard recovery.** With the recycle deadlock above self-healing, the `host_supervisor_stale_warning_message` (`project_controller/rpc.rs`) "Avoid force/discard recovery for stale-binary refresh; those paths are only for wedged owners." sentence was removed — it was both unnecessary (the recycle now clears the open-cycle wedge on its own) and misleading (force/discard was sometimes the only escape *because* of the deadlock). The warning still points routine refreshes at idle-boundary `agent-doc admin recycle` or normal `agent-doc session restart-supervisor <FILE>`. README aligned; `host_supervisor_stale_warning_message_uses_non_destructive_refresh` still pins the no-`--force` / no-`interrupt-clear` non-destructive guidance.

## 0.34.51

- **Supervisor recycle spin-loop on a stalled cycle fixed (`#suprecyclespin` / `#midturnresumeb` Phase B).** Live production repro (alex-lee.md supervisor pid 999966): `supervisor_recycle_deferred_cycle_open` fired ~2/sec **forever** and the stale supervisor never hot-reloaded onto the installed binary. Root cause: the `#midturn-recycle-resume` interlock (`supervisor_recycle_action` `DeferCycleOpen` arm) correctly refuses to `execve`-recycle while an agent-doc cycle is open — but the `cycle_open` fact at the idle-watch call site (`start/idle_watch.rs`) was computed purely from `CycleState::is_open()`, which stays `true` indefinitely for an *abandoned older* turn (e.g. `cycle-…652506`) that a *newer* cycle (`synthetic-…767123`) already committed/superseded. The open-but-never-closed older cycle wedged the recycle in permanent deferral. Fix: at the call site, a cycle that is still open at a harness `turn_boundary` with **no IPC ack connection in flight** and **untouched past `STALLED_CYCLE_RESOLVE_SECS` (45s)** is now *resolved* — force-closed to `abandoned` via `mark_abandoned` so the gate clears and the `execve` recycle reaches its boundary — instead of deferred. A genuinely live cycle is never touched: each `preflight → finalize` phase ticks `updated_at` and finalize holds IPC inflight, so only a crashed/superseded turn is reaped. The discard is logged (`supervisor_cycle_stale_resolved file=… cycle=… turn=… phase=… stalled_secs=… reason=abandoned_older_turn_superseded`) so it is never silent. New pure helper `CycleState::open_stalled(inflight, now_secs, deadline_secs)` with unit coverage `open_stalled_resolves_only_abandoned_older_turns` pinning the deadline boundary, the IPC-inflight guard, the fresh-cycle guard, and the committed-cycle case. Plan: `tasks/agent-doc/plan-crdt-overlay-live-keystroke-preservation.md` §spin-loop.

## 0.34.50

- **Live keystrokes silently dropped by overlay-stale rebuild fixed (`#crdtlivedrop`).** Live production repro (alex-lee.md): typing `model: opus` / `claude_model: opus` into frontmatter during a concurrent finalize, plus intermittent 1-character deletes while typing. This is *data loss*, distinct from the `#qbasehashmemo` typing-lag fix — a fresh binary did not help. Root cause: `crdt_merge_base_state` (`agent-doc-orchestration/src/snapshot.rs`) classified **any** overlay whose markdown projection `!= fallback_markdown` (the cycle baseline) as "stale" and called `rebuild_overlay_crdt_locked(&path, fallback_markdown)` — **wiping the live overlay sidecar down to baseline**, so just-typed keystrokes that lived in the overlay (and fed op-capture + the next cycle's merge base) disappeared. Plain string inequality conflates two opposite cases: *overlay-behind* (older content → correct to discard) and *overlay-ahead* (newer live keystrokes → discarding silently drops the user's edit). The fix distinguishes them by line-multiset containment (`overlay_carries_unbaselined_content`): when the overlay holds any non-empty line the baseline lacks it is *ahead/concurrent-divergent*, so the live overlay sidecar is **left intact** (merge base stays the committed baseline — the true common ancestor of the downstream merge's `ours`=response and `theirs`=live buffer, so both survive with no old-content replay); only a genuinely *behind* overlay is rebuilt to baseline, and that discard now logs `overlay_discarded_bytes file=… overlay_len=… baseline_len=… first_diff_offset=…` so the loss is never silent (new `crdt_merge_base_overlay_ahead_preserved` marker + `OverlayAheadPreserved` source). New `#crdtedgetests` 8-case matrix in `snapshot::tests` (overlay ahead-by-1-char, ahead-by-frontmatter-block, strictly-behind+logged, concurrent-divergent, empty-doc-first-keystroke, rapid-burst, decode-error, frontmatter-edit-during-exchange-convergence); cases 1–3 were red before the fix. Known residual: a pure-*deletion* live edit (no new line) racing a finalize is still classified behind and discarded — far rarer than additive typing, tracked separately. Plan: `tasks/agent-doc/plan-crdt-overlay-live-keystroke-preservation.md`.

## 0.34.49

- **Per-keystroke typing slowdown across all documents fixed (`#qbasehashmemo`).** Live diagnosis from `ops.log`: every editor `DocumentEvent` (JB `TypingTracker.reportEditorOp`, and the VS Code equivalent) calls the `agent_doc_document_base_hash` FFI → `op_capture::current_base_hash`, which rebuilt the **full-document** CRDT merge base (`crdt_merge_base_state` → `CrdtDoc::from_text` over the entire markdown → decode → `to_text` → SHA256) *on every single character*. The base hash is a pure function of the committed snapshot + overlay CRDT sidecar, and neither changes during a typing burst, so this recomputed a constant at O(doc-size) per keystroke — making large documents (the live repro showed ~3–7 whole-doc merges/sec at `len=26650`, 98,922 merges in one session across `sampleportal`, `agent-doc-bugs2`, `lazily-rs`, …) progressively harder to type in, exactly matching the operator's "all documents are difficult to type in" report. `current_base_hash` now memoizes its result keyed on a cheap `(len, mtime)` fingerprint of the snapshot + overlay files: a typing burst is an O(1) cache hit, and any real write boundary (snapshot/overlay change) invalidates the memo. A stale fingerprint can only ever force a recompute — never return a wrong hash — and even a wrong hash would degrade to the existing diff-guess merge fallback, so the memo is safe under any mtime granularity. This is an FFI-layer (Shared Foundation) fix, so both the JetBrains and VS Code editor plugins benefit without plugin changes. New eval `current_base_hash_is_memoized_until_base_changes` pins the cache-hit-during-burst + invalidate-on-base-change contract via a process-isolated recompute counter.

## 0.34.48

- **JB plugin 0.2.195 — actor-switch defer route errors are actionable (`#actorswitchdefer`).** When `Run Agent Doc` hits the route-side harness-switch guard (`authoritative actor record ... running harness <old>, but frontmatter now resolves to <new>; deferring to boundary agent restart`) the plugin now classifies it as a typed recovery state instead of a persistent route failure. Paused queues show an information notification with `Restart Supervisor and resume`, not-ready panes show `Interrupt and restart`, and healthy deferred switches offer `Restart Supervisor`; all variants keep `Show status` and `Copy details`. Coverage pins the live `claude-code -> codex` paused-queue output plus the `--force` not-ready path, and the JetBrains plugin patch version is bumped to 0.2.195.
- **JB plugin 0.2.194 — eliminate the remaining behind-editor File Cache Conflict trigger (`#p2j4` / `#jbcfdiag`).** IPC-first writes removed the agent's behind-editor disk writes, but the operator still saw IntelliJ "File Cache Conflict" dialogs. Root cause: `PatchWatcher` ran a content-bearing `VirtualFile.refresh(false, false)` *unconditionally* at the start of every apply/reconcile path (`applyPatch`, `repositionBoundaryViaDocument`, `applyReconnectReread`, the socket `refresh_content` handler) before checking whether the in-memory `Document` was unsaved. IntelliJ's `FileDocumentManagerImpl` arms its memory↔disk conflict dialog *during* that refresh whenever an unsaved buffer's disk bytes have diverged — so the plugin itself was popping the dialog right before reconciling through the Document API, and its own reflection-based `hasPendingMemoryDiskConflict` deferral ran too late. All four apply-time refresh sites are now gated by `shouldRefreshVfsBeforeApplyUtil(documentUnsaved)`: the VFS refresh runs only for a clean (saved) buffer; for an unsaved buffer the refresh is skipped (the buffer is the source of truth and the apply path reconciles via `setText`). The **dominant** remaining trigger was in `PromptPoller`: its `autoSaveTrackedFiles`/`refreshTrackedFiles` loop ran a content-bearing `VirtualFile.refresh(false, false)` on the **1.5s poll timer regardless of any agent activity** — `autoSaveTrackedFiles` even refreshed *specifically* in the `isDocumentUnsaved` branch — so an unsaved buffer whose disk bytes had diverged (e.g. after an agent commit wrote the working tree) armed the dialog every cycle, not just during an apply. Both poll-timer refresh sites are now gated by the same `shouldRefreshVfsBeforeApplyUtil(documentUnsaved)` predicate; for an unsaved buffer the refresh is skipped and disk divergence is detected by comparing the `Document` against the VFS-cached disk bytes (the `BulkFileListener` still delivers real external writes via `VFileContentChangeEvent`, and `mergeOrReload` reconciles via the Document API). The Rust post-commit reconcile paths (`#pcwc`/`#pcwcdiskfree`/`#pcwcwarn`/`#pzjy` in `git.rs`) were already IPC-first (try `send_postcommit_editor_refresh` before any `std::fs::write` when a listener is active), so the trigger was plugin-side. The Rust `--force-disk` operator escape hatch is unaffected. `RefreshBeforeApplyConflictTest` pins the pure decision util plus the invariant that no apply-time refresh in `PatchWatcher` and no poll-timer refresh in `PromptPoller` remains ungated.
- **Multiline phantom-pin flood dedup (`#rt83qflood`).** `dedup_free_text_heads` collapses duplicate binary-injected free-text queue pins beyond their snapshot-authored multiplicity, but `free_text_dedup_key` early-returned `None` for **multiline** prompts — so a multiline `:round_pushpin:`/`:pushpin:` actor-switch paste block (operator line + fenced route-error body), re-emitted verbatim by a stale-CRDT / supervisor convergence, was never deduped and flooded the queue with ~5–7 near-identical copies (live repro: a JB `Run Agent Doc` harness-switch report re-injected every preflight, repeatedly blocking clean closeout with `#qheadresidue` session-check interrupts). Multiline free-text pins are now keyed on their whitespace-collapsed lowercased text (pin marker stripped) so a verbatim phantom re-emit collapses to the authored count, while genuinely-distinct multiline pins and intentional authored duplicates survive (snapshot-multiplicity guard). New evals `dedup_free_text_heads_collapses_multiline_phantom_pin_flood` and `dedup_free_text_heads_preserves_distinct_multiline_pins`.
- OpenCode auto-loop self-drive (`#ocdrainstall` Part A): the installed OpenCode skill now mandates in-turn drain self-drive — after a proven `write --commit` closeout with `queue_continuation_required=true` and `queue_drainable_head_count>0`, the agent keeps running preflight→respond→write --commit in the same turn instead of stopping and forcing per-head operator re-invocation. "Low context budget" / "focused cycle" are documented as stalls (`#drain-no-defer`), not stop reasons.
- **Bounded session-startup deadline (`#startupdeadline`).** The `start.rs` auto-trigger thread used to log a *provisional* `no_prompt_after_30s` timeout and then keep watching the harness child forever, so a harness that never became dispatch-ready (hung TUI, auth wall, stuck network) left the session silently hanging with no recoverable signal. The 30s `AUTO_TRIGGER_TIMEOUT` is now a **hard deadline**: on expiry without a dispatch-ready prompt the thread fails closed via `record_session_startup_miss` — recording a `startup_miss` marker against the owned pane and surfacing an actionable `session did not become dispatch-ready in Ns` diagnostic on stderr — instead of polling indefinitely. The same fail-closed path now also covers the clear-cooldown and managed capability-proof waits. Mirrors the existing bounded `route.rs` `wait_for_ready` / `fresh_route_start_missing` startup-miss path so neither the route nor start side can hang. New unit eval `auto_trigger_no_prompt_continues_before_deadline_then_fails_closed`.
- Manifest pin reconciliation: completed the half-landed 0.34.48 bump by syncing the `agent-doc-core` / `agent-doc-markdown-ast` / `agent-doc-sqlite` crate versions and the root dependency pins from `0.34.46` to `0.34.48`, satisfying the `test_manifest_uses_publishable_dependency_contract` exact-version contract (cargo's caret resolution had masked the mismatch in builds).

## 0.34.47

- `queue recover-lost`: git-history-only candidates are now classified as restorable vs non-restorable/foreign, and `--restore-patch <PATH>` emits an operator-reviewed restoration patch (JSON) separating restorable prompts from foreign-owned (cross-document contamination) ones. The session document is never mutated. Closes `#sampleqrestore`.

## 0.34.46

- **IPC proof dogfood diagnostics no longer become unresolved prompt-bearing exchange items (`#ipcqproof`).** The `ipc_proof_insufficient` note appended during interrupted-cycle recovery (e.g. a queue-consume `socket_ack_content` ACK mismatch on `live_prompt_drift_after_preflight`, or a `missing_response_probe` patch consumed without the response body) now opens with a `### Re:` response heading. The prompt-bearing diff classifier therefore treats it as a binary-authored `RecoveryArtifact` instead of a user `PromptTarget`, so it is no longer `❯`-normalized into a prompt-only exchange tail that forced a follow-up acknowledgment cycle. Recovery stays fail-closed on the binary-owned path (`recovery=retry_without_disk_write` / `content_ours_snapshot_next_cycle`, never `direct_write_fallback`). New regression `ipc_dogfood_note_is_recovery_artifact_not_prompt_bearing` pins the classification, prompt-prefix normalization, prompt-only-tail, and fail-closed invariants for both diagnostic variants.

## 0.34.45

- **Cross-editor live IPC state-projection proof is pinned (`#lzliveproof`).** The Rust state backbone now exposes a `ProjectionSummary` / compact summary contract matching the JetBrains and VS Code editor bridge helpers. Coverage drives JetBrains socket IPC, JetBrains file IPC, and VS Code file IPC through queued, retry, and ack transport transitions plus route started/proven/blocked events, then asserts the authoritative `DocumentStateProjection` fields and compact route/transport/proof summaries.

## 0.34.44

- **Package-level lazily state-projection parity is explicit (`#lzpkgwire`).** `lazily-kt` and `lazily-js` now expose pure bridge helpers for canonical document hashing, `StateEvent` JSON, compact projection summaries, and native projection pointer/free lifecycle. The editor bridges stay plugin-local canonical under their current packaging constraints (JetBrains IntelliJ/Kotlin/JBR toolchain, VS Code CommonJS extension versus ESM `@lazily/js`), and tests pin VS Code against the package helper contract while Kotlin package coverage mirrors the JetBrains helper behavior.

- **Dogfooding bug backlog capture can target a configured document.** Project config now accepts `agent_doc_bug_target_document = "<document>.md"` as the default target for `#agent-doc-bug` and route-filed `#jbrunautobug` backlog items. The default remains the current document, and explicit prompt text such as "Add to the backlog of tasks/bugs.md" still wins. `agent-doc plan` emits `--pending-add-to` hints for configured dogfooding targets, and route failure auto-filing writes to the configured target while logging both source and target files.

- **VS Code editor actions now match the JetBrains Run/menu contract.** VS Code `Run Agent Doc` now invokes `agent-doc route --dispatch-only --plain-trigger --debounce 0 --wait-for-ready 120` with the visible markdown layout and focus arguments, and the extension exposes Fix Document, Load Tmux Window, and direct Interrupt and Clear Session Context actions from the command surface/popup menu. Lower-frequency Stop Agent, Cancel Turn, Kill Supervisor, Resync/Fix Sessions, and GC Stale Sessions actions remain available from popup overflow. Coverage pins the route flags, command contributions, and popup-menu parity. The VS Code extension version is bumped to 0.2.32.

- **Finalize stale-snapshot failures are all-or-nothing for backlog mutations, and protected route refusals show the blocking draft (`#ipcproofcloseout` / `#crdtreset1` / `#routeblockux1`).** `finalize` / `write --commit` now run the stale snapshot/CRDT reset-drift guard before granular `--pending-*`, `--review-*`, or `--status` mutations, so a refused closeout cannot add or alter backlog items without also landing the exchange response. Dispatch-only route refusals for protected Codex prompt input now include a bounded redacted `draft_preview=...` field alongside pane and snapshot diagnostics, and the JetBrains notification surfaces that preview so the operator can clear the right draft without opening the snapshot. SimWorld now pins that protected-prompt refusal as a no-dispatch-churn scenario. The JetBrains plugin patch version is bumped to 0.2.189.

- **Editor integrations now report and consume the Rust-owned state projection (`#lzstatewire1`).** JetBrains and VS Code bind the existing `agent_doc_state_projection` / `agent_doc_record_state_event` FFI ABI, compute the canonical document hash used by snapshots, and report editor IPC patch queued/ACK/retry events plus route dispatch readiness/proof events into the shared state backbone. Both editors expose compact route/transport/proof projection summaries instead of re-deriving those state slices from local booleans, while older native libraries fail open to the prior behavior. The state-backbone spec now records the editor projection bridge contract, VS Code native tests cover hash/event/projection parsing, and JetBrains coverage pins the same helper behavior. The JetBrains plugin patch version is bumped to 0.2.188.

- **Run Agent Doc no longer re-submits cancelled scrollback prompts over a newer Codex draft.** Direct-pane dispatch now treats any later harness prompt, including a non-empty draft such as `Use /skills...`, as proof that an older `agent-doc <FILE>` line is scrollback rather than the current composer draft. If protected live input is present, route now fails closed with a snapshot diagnostic instead of sending Enter or appending a fresh trigger into the user's text, and the JetBrains plugin reports that state as an actionable warning instead of a generic route failure. The JetBrains plugin patch version is bumped to 0.2.187.

- **Editor markdown emphasis now respects theme foreground and identifier underscores.** JetBrains bold/italic visual tokens now inherit the editor's normal foreground instead of the identifier color, so regular markdown text no longer turns yellow in dark themes. The shared visual-token parser also rejects underscore emphasis delimiters inside identifier words, so `foo_bar_baz` and `foo__bar__baz` stay regular text while `_foo_` and `__foo__` still render italic/bold. Coverage pins the underscore delimiter regression, and the JetBrains plugin patch version is bumped to 0.2.184.
- **JetBrains component bodies inherit theme foreground.** Agent-doc component body text in the JetBrains plugin now keeps the editor's normal foreground color while retaining the subtle component background, so queue/backlog/body content no longer renders as yellow metadata text in dark themes. Test `component body inherits editor foreground`, and the JetBrains plugin patch version is bumped to 0.2.186.

- **Op-capture live verification now has durable merge-consumer evidence and a focused checker (`#qnodemerge4verify`).** `agent_doc_record_editor_op` keeps logging producer-side `editor_op_recorded` markers and now includes byte-count detail (`delete_len`, `insert_bytes`, `insert_non_ascii`) without logging inserted text. `merge_contents_crdt_with_ops` logs `editor_ops_for_base accepted=true` when the base-keyed sidecar is actually offered to the CRDT merge, including accepted offsets and aggregate byte counts. New `agent-doc verify-op-capture <FILE> [--expect-cafe-demo]` fails closed unless the target document's `ops.log` proves both the editor reporter and merge consumer ran; cafe-demo mode verifies the canonical `café 日本 😀` byte-offset contract (`offset=6`, `delete_len=6`) for live JB/VS Code plugin tests.

- **Focused-cycle supervisor handoffs now leave end-to-end proof (`#qfocsup` / `#tb4q`).** `session-check` records a `session_check_supervisor_drain_handoff` ops-log marker tied to the current focused-cycle head hash when it emits `ui_outcome=deferred_for_supervisor_drain` / `next_action=yield_to_supervisor_clear_and_continue`. The supervisor idle-watch now distinguishes `[focused-cycle]` from `[clean-session]` in its context-reset reason, logs `#qfocsup` for the forced `/clear`, and includes `proof=go_drain_dispatch` on the following queue dispatch. SimWorld covers the full stale-supervisor path: yield, in-place recycle if needed, context reset, settled go drain, and fresh-agent response-materialization evidence.

- **Recycle-boundary Run Agent Doc proof is now deterministic in SimWorld (`#jbdisprecycle`).** The recycle-dispatch model now records `route_dispatch_submit_recycle_settle` when the post-recycle dispatch resumes after settle and records a `dispatch_start_proof ... proof=submitted` marker when the pending dispatch is proved. The targeted recycle-boundary scenario now asserts no injection while recycling, exactly one post-settle `dispatch_inject attempt=1`, exactly one recycle-settle submit marker, exactly one dispatch-start proof, no `attempt=2`, and no stranded `start_session failed` text.

- **JetBrains paused-queue route failures now render actionable notifications (`#qpauseux`).** `Run Agent Doc` route output with `failed_stage=queue_paused` is classified separately from persistent route failures, so the plugin no longer writes a raw error toast or saves the route output as a generic failure. Deliberate/admin pauses show an information notification with Resume queue, Show status, and Copy details actions; stale-supervisor churn-stop pauses offer Restart Supervisor and resume. Resume first reads `agent-doc session status` to prove the current actor generation, then runs `agent-doc admin queue resume --observed-generation <gen> --json`. The notification and action paths leave `jb_queue_paused_route_notification` / `jb_queue_paused_route_action` markers in `.agent-doc/logs/ops.log` for render/ops proof. Coverage pins queue-paused classification, stale-supervisor action selection, resume command generation, receipt parsing, notification labels, and ops-log marker format.

- **Stale snapshot reset drift now auto-rebases unrelated visible cleanup (`#docdriftgrace`).** When the visible session document intentionally trims complete historical `### Re:` exchange blocks while preserving the active turn and adding only turn-independent queue work, the stale snapshot size guard now refreshes the text snapshot and CRDT sidecars from the visible file instead of forcing `agent-doc reset --from-current`. The rebase is component-scoped: only `agent:` frontmatter changes, complete historical response-block removals, and node-proven turn-independent non-exchange edits are accepted; active capture response removal and active queue-driver structural changes still fail closed with the existing reset guidance. Coverage adds positive historical-trim-plus-sibling-queue and negative active-driver-removal regressions.

- **Finalize/write stale-snapshot recovery accepts compact summaries (`#docdriftfinalize`).** The same reset-drift classifier now treats compact-generated `### Session Summary` exchange replacements with compact archive markers as safe historical reductions, so `finalize --stream` can refresh stale pre-compact snapshot/CRDT sidecars instead of refusing and forcing `agent-doc reset --from-current`. Arbitrary session-summary rewrites without compact markers still fail closed. Coverage pins the stream-write compact-summary rebase and the unsafe fake-summary rejection.

- **Replay-neutralized queue additions are now auto-committed after editor-buffer-wins recovery (`#editorbufwin`).** When `content_ours` / HEAD already contains the response but the live editor buffer holds a preserved `agent:queue` addition, replay normalization intentionally treats the queue component as neutral, which previously let the commit path close as already-current while the operator's queue edit stayed local and could trip session-check. The commit seam now detects the narrow safe shape: non-queue content matches under replay normalization, every HEAD queue entry is still present, and all extra queue entries are active prompts. It then saves the visible queue addition into the snapshot and creates the follow-up commit, so HEAD, snapshot, CRDT sidecars, and the visible document agree while the prompt remains live for the next drain. Unproven queue deletions and stale completed-row unstrikes still fail safe through the existing guards. Coverage adds the already-current replay-neutralized queue-addition regression and updates the stale-exchange-collapse repair to prove the preserved queue follow-up is committed.

- **Editor menus now label the supervisor restart action as Recycle Supervisor (`#recyclerename`).** JetBrains keeps the stable `AgentDoc.RestartSupervisorProcess` action ID, VS Code keeps the stable `agentDoc.restartSession` command ID, and both continue to call `agent-doc session restart-supervisor` while presenting Recycle Supervisor alongside Restart Agent and Stop Agent in operator menus. Fallback success hints now say the recycle was requested.

- **Restart Agent menu invocations now leave an optverify proof marker (`#j9ja` / `#restartagentmenu`).** The JetBrains `RestartAgentAction` records a best-effort `restart_agent_menu_invoked file=<relative-path> source=jetbrains action=restart_agent` line in `.agent-doc/logs/ops.log` before calling the existing restart-supervisor path, so stale gated review items can be proved from structured ops-log evidence instead of an IDE-only logger line. The supervisor-side `agent_restart_performed` marker remains the proof that the boundary restart actually spawned the fresh harness. Coverage pins the action wiring and marker format.

- **State ownership is now documented as Cycle FSM plus typed projections (`#statebb1`).** The new state-backbone spec keeps the Cycle State Machine scoped to response-turn closeout, while queue, document, transport, supervisor, route, and proof state are modeled as append-only typed events reduced into deterministic projections with actor ownership/epoch guards. The docs also record where local FSMs, coroutines, and behavior-tree-like policy helpers fit, and why GOAP/MPC-style planning is not the durable correctness model for agent-doc closeout.

- **JetBrains route/dispatch proof failures now file a deduped session-doc bug item (`#3ygp`).** When the routed `Run Agent Doc` submit/proof path exhausts its bounded Enter or dispatch-start proof window, route now adds a `#jbrunautobug #agent-doc-bug` backlog item to the session document. The item records the failure class, document, stage, pane, best-effort actor generation, editor attempt id, dispatch proof, saved route-submit diagnostic path, and `ops.log` marker/path. Repeated failures for the same document/stage/failure share a symptom key and append evidence to the existing item instead of creating duplicates. The existing direct-pane retry loop remains bounded by `AGENT_DOC_DIRECT_PANE_MAX_ENTER_RESUBMITS` and still retries visible drafted triggers before the bug is filed.

- **Queue closeout no longer strikes a free-text head without exact response proof (`#qstrikework`).** The closeout queue-consumption decision now requires a free-text `agent:queue` head to be explicitly answered by the current response, using the same quoted-prompt proof as the position-independent answered-free-text striker. Generic repair/no-op responses and responses to unrelated exchange prompts keep the free-text head runnable instead of silently advancing the queue; `--review-resolve <id>` also now counts as an explicit completion signal for id-backed heads. Regression coverage pins both the answered-head and false-strike shapes.

- **Post-commit editor cleanup no longer resurrects completed queue heads (`#pzjy`).** When a stale live editor buffer rewrites a committed completed queue row such as `- ~~:pushpin: do [#id]~~` or an answered free-text row back into an active prompt, the post-commit drift check now restores that queue completion state before the generic editor-buffer flush can accept the stale copy. The repair is directional: committed completions win over stale unstrikes, while genuine editor-owned new strikes still follow the existing editor-wins queue merge behavior. Regression coverage includes pinned id-backed rows, answered free-text rows, and preservation of unrelated new queue work.

- **`--force-disk` closeout now covers pending maintenance reap.** The explicit operator recovery path already bypassed stale active listeners for response placement, queue consumption, and done-id marking, but strict closeout could still fail in the intervening pending-maintenance reap phase with `reason=ack_mismatch`. Closeout now threads `force_disk` through pending maintenance as well, recording an attributable `pending_maintenance_writeback ... transport=disk_force reason=force_disk` marker while leaving ordinary preflight/route maintenance fail-closed under active editor listeners. Coverage: `force_disk_closeout_pending_maintenance_bypasses_active_listener`.

## 0.34.43

- **Prose queue heads no longer disappear as pruneable noise (`#freshprosequeue`).** The queue drainability classifier now treats ordinary operator prose as real queue work even when it is phrased as a declarative bug report rather than an imperative. `queue prune-noise`, `session-check` stale-noise counts, and supervisor idle-watch dispatch still skip/clear structural artifacts such as console status lines, log-only fenced blocks, bold response fragments, and agent comments, but they no longer strike natural-language queue items like "Queue items are being struck without being worked on" before an agent answers them. Coverage updates the continuation, prune-noise, and preflight drainability contracts.

## 0.34.42

- **Windows release builds compile the library GC liveness probe.** `agent-doc gc-libs` now keeps Unix `kill(pid, 0)` probing behind Unix guards and uses the native Windows process handle API for PID liveness on Windows, so release packaging no longer trips over the missing `libc::kill` symbol while still cleaning stale versioned library locks.

- **Cycle-state sidecar mutations now pass through a lazily transition table (`#c7j5`).** The first CP/session-actor cutover slice adds `cycle_state_machine::CyclePhaseMachine`, backed by `lazily::ThreadSafeStateMachine`, and routes the phase-changing sidecar mutators through typed `CycleEvent` transitions before the durable `.agent-doc/state/cycles/<hash>.json` journal is written. This keeps the sidecar as crash recovery while giving the controller/session actor a shared transition authority for the next cutover slices.

- **Editor-IPC shorter ACK mismatches now replay missing agent responses and stale CRDT overlays self-heal (`#ack-shorter-replay`).** When an editor ACK sidecar is shorter than the intended target only because it is missing a newly materialized `### Re:` response block, the convergence path now hash/length-proves the stale buffer and refreshes the editor to the target response instead of refusing the write and leaving the cycle interrupted. Stale overlay CRDT projections are also rebuilt from the authoritative fallback baseline on first mismatch, so repeated merge-base calls stop re-reading the same stale overlay and stop producing fallback-overlay hot loops. Coverage adds regressions for safe shorter ACK replay and stale-overlay rebuild/rate limiting.

## 0.34.41

- **Windows release builds compile the supervisor hot-reload path again.** The `#ctlrecycle` Unix `execve` adoption path now keeps its stderr redirection, raw-fd adoption, and startup-miss UTC formatting behind platform guards, so non-Unix release builds fall back to the normal spawn/relaunch behavior instead of compiling POSIX-only symbols.

## 0.34.40

- **Completed short free-text queue heads no longer survive as active queue residue (`#qheadresidue`).** Short heads such as `deploy` now count as answered when, and only when, exchange history contains an explicit labeled `> **Queue prompt:**` echo for that exact head. `session-check` now interrupts if that proved-answered free-text head is still active in `agent:queue`, preventing a later queue cycle from re-running stale completed work.

- **Paused-queue supervisor failsafe no longer self-stalls behind its own drain-owner lease (`#qstallguard-failsafe-lease`).** The paused-queue fallback used the same drain-owner sidecar as an in-session `/loop` and wrote `owner=supervisor-failsafe` before later gates proved a trigger was actually submitted. If a later gate skipped (or after the fallback-drained turn closed), the fresh self-written lease made the pause gate report `queue_control_paused` and suppress the next valid drain until the 90s TTL expired. Drain-owner freshness now has a loop-only reader; idle-watch pause and stale-recycle-yield gates defer only to a real `/loop` owner, stale `supervisor-failsafe` sidecars are ignored, and the failsafe proof log is emitted only after an actual submit/resubmit succeeds. Coverage: loop-only drain-owner test plus updated idle-watch lease integration.

- **Preset-backed free-text queue reports with fenced diagnostics now drain instead of disappearing behind operator pins (`#qfreetext-sep`).** A live `agent-doc-bugs2.md` queue head was typed as prose plus a fenced route error followed by a `---` separator, ahead of several `[operator-verify]` pinned heads. The parser kept that prose as inert `Freeform`, and the drainability classifier also treated prose+fence as noise under a preset, so preflight reported only the operator-verify heads and `queue_drainable_head_count=0`. The queue parser now treats separator-terminated prose blocks as multiline prompts, and a preset-bearing queue treats a prose lead followed by fenced diagnostics as drainable work while preserving pure all-log blocks as pruneable noise. `queue prune-noise` now deletes only all-log multiline evidence blocks under a preset and preserves prose reports for closeout response/strike.

- **Explicit `agent:` harness switches replace stale authoritative actor bindings instead of hard-failing route (`#actor-switch-rebind`).** When a document switches from `agent: claude` to `agent: codex`, an old healthy `claude-code` actor record no longer produces a permanent `bound to harness claude-code, not codex` failure. Route now recognizes the explicit frontmatter harness change, logs the mismatch as stale, and falls back to the normal create/rebind path for the newly resolved harness. Healthy wrong-harness actors still fail closed when the document did not explicitly declare the new expected harness.

## 0.34.39

- **Manual Sync Tmux Layout now closes crash-left response commit boundaries (`#sync-jbccc-repair`).** Full `agent-doc sync` / `session doctor --repair` now runs the existing `jb_cache_conflict_cancel` detector before pane-liveness checks can return early. When a machine crash or canceled editor writeback leaves the visible session document and snapshot containing the assistant response while `HEAD` still lacks it, the sync repair path performs the same narrow `git::commit(file)` recovery as preflight and proves the recoverable shape is gone before continuing with layout repair/reconcile. Coverage adds a deterministic sync repair regression for the committed-cycle/snapshot-drift shape, alongside the existing preflight recovery tests.

## 0.34.38

- **JetBrains `Run Agent Doc` now fences the full startup ready-probe from supervisor `/clear` injection (`#jbrunclear`).** Dispatch-only reroutes write a short-lived route-submit marker before the latest-run prompt-ready wait begins, not only after text injection starts, so the idle-queue supervisor cannot interleave a context-reset `/clear` or `/new` while an editor route is still proving the recovered pane is dispatch-ready. The marker records `reason=dispatch_only_ready_probe`, set/clear ops lines, and the idle watcher logs when a persisted or orphan context-clear draft waits on `reason=route_submit_in_flight`, giving the 2026-06-23 reboot shape direct proof instead of leaving a stray clear in the composer.

- **Dispatch-only reroutes no longer let a stale startup-log window override authoritative ready proof.** Operator correction on 2026-06-23: the JetBrains `Run Agent Doc` failure was not a slow boot — the Codex pane was up in under two seconds, idle, and manually usable, while route still refused with `unblocker=wait_for_dispatch_ready_prompt` because `dispatch_only_requires_ready_probe` trusted the latest open `codex_start` log and then only accepted a short live pane-capture proof. The dispatch-only startup gate now also consults the authoritative actor binding for the same session/pane and bypasses the stale startup-log wait only when the healthy supervisor reports a current-generation `Ready` actor and the normal prompt-ready barrier is satisfied (`prompt_ready`, `dispatch_ready_prompt`, or `idle_pane_reconcile`). Busy/starting/degraded/wrong-pane actors still fall through to the existing fail-closed live probe. This keeps startup reroutes prompt-gated without blocking an already-proven idle actor on a stale log tail.

- **JetBrains `Run Agent Doc` keeps a 120-second dispatch-readiness window as extra slow-start margin.** The editor action invokes `agent-doc route --dispatch-only --plain-trigger --wait-for-ready 120`, but this is not the root fix for the stale-startup-log/ready-actor dispatch bug above; it only prevents genuinely slow starts from exhausting the editor-side wait too early.

- **Release packaging now uses publishable manifests and an ordered crates.io publish path (`#98t4` / `#adpublishpkg`).** Internal `agent-doc-*` crates now carry the release version and versioned path dependencies, so Cargo can strip paths when publishing instead of rejecting path-only dependencies. `agent-doc-sqlite` and `agent-doc-orchestration` are publishable crates, `make version-sync` verifies all internal publish-unit versions match the top-level crate and PyPI metadata, and `make publish-crate` publishes `tmux-router`, internal crates, and the CLI in dependency order with crates.io index visibility waits and skip-existing behavior. The tracked manifests now use registry `agent-kit 0.4.1` and `lazily 0.12.0`; the live tmux-router API dependency is explicit as sibling `tmux-router 0.3.11`. PyPI builds now check out that sibling in CI and local publish includes sdist again, fixing the prior sdist path-dependency/lock collision shape.

- **ACK-mismatched queue-consume convergence now clears only the proven stale editor artifact (`#fcc0-ack-mismatch`).** When an active editor listener ACKs a queue-consume patch but the ACK-content does not match the intended target, the write still fails closed and refuses the external disk write. The new recovery step first proves the mismatch is the narrow stale queued-prompt blockquote artifact in `agent:exchange` with no drift outside `exchange`; only then it sends a hash/length-guarded `refresh_content` message to restore the editor buffer to the pre-consume document, preventing a later editor flush from persisting the stale queue strike. If the ACK content contains a real concurrent prompt or other non-artifact drift, the refresh is skipped and the editor-owned content is preserved. Coverage includes positive and negative queue-consume ACK-mismatch regressions, the existing editor-IPC success path, and the FlowCore reason-budget audit.

## 0.34.37

- **Supervisor restart/context-clear recovery now submits visible `/clear` drafts before queue triggers (`#clearresubmit`).** The idle-watch pending-payload detector now treats context-clear slash commands separately from `agent-doc ...` triggers, using the same active-composer evidence as explicit `session clear`: a visible Codex `/clear` or OpenCode `/new` draft is recognized only when no later idle prompt proves it already submitted. The supervisor also runs an orphan-clear recovery before the paused-queue gate, so a recycle, marker expiry, or durable `admin queue pause` cannot strand `/clear` in the input and require the operator to press Enter before the next `agent-doc <FILE>` drain. Coverage includes Codex/OpenCode active-composer detection and stale-scrollback rejection.

## 0.34.36

- **Realtime cross-editor broadcasts now deliver node-keyed patches (`#rtndsync`).** The `realtime_model::broadcast_editor_change` path no longer queues peer editor convergence as component-only replacement payloads. It now computes `node_patches` from the target peer buffer to the CRDT-merged buffer, includes peer-baseline raw/transient-normalized hashes for generation fencing, and logs node/component patch counts. JetBrains can therefore allow unrelated live-buffer drift while ACK-gating the targeted node proof; VS Code consumes the same native node patch plan under its editor-generation apply proof. Legacy component patches remain in the payload as older-plugin fallback and are skipped by current plugins for components already covered by node patches. Coverage includes the realtime payload unit test and the SimWorld two-editor broadcast convergence path.

- **Node-keyed IPC patches now carry target-node source proof before ACK (`#node-ack-merge`).** Existing-node `node_patches` (`remove`, `replace`, `move`, `strike`, `unstrike`) now include the expected target-node markdown in IPC payloads, and the shared native patcher rejects the mutation when that exact node has drifted. JetBrains uses the same native dry-run proof to bypass whole-document generation drift only for pure node-patch payloads whose targeted nodes are still current, so unrelated editor-buffer drift no longer blocks an ACK-able node merge, while stale target nodes still fail closed before ACK. Socket/file IPC payloads also carry baseline hashes for normal generation fencing. Coverage spans markdown-AST stale-node rejection, FFI drift preservation, orchestration payload JSON, and JetBrains/VS Code schema parsing.

- **Editor file-IPC patches are no longer deleted without ACK-content proof (`#ackcontent-delete`).** JetBrains and VS Code patch watchers now treat the `*.ack-content` write as part of patch success for response patches and `save_document`: if the editor cannot write the ACK-content sidecar (missing FFI/root, write failure, or failed document save), `applyPatch` returns false and the single-use patch file stays in place for binary retry instead of being deleted with only a transient editor-buffer mutation. This closes the observed stale-state/File Cache Conflict path where a live editor consumed `.agent-doc/patches/<id>.json`, failed to leave the ACK-content proof, and the binary later saw `no_ack` with no patch left to replay. Source guard tests cover both editor integrations.

- **Orphan-response repair now fails closed when the captured response does not materialize (`#sample-response-loss-stalled-queue`).** The repair path that replays retained/captured responses now re-reads the repaired document and requires the normalized captured response block to be present before it clears the pending capture or advances the cycle. A malformed replay that only leaks body bullets into a previous response, drops the `### Re:` heading, or otherwise leaves a prompt-only tail now preserves the capture for retry instead of committing a false-success repair. Regression coverage pins the body-only/materialization-missing shape observed during the sample app/install closeout recovery.

- **Strict template closeout now rejects body-only assistant patchbacks (`#strict-re-heading`).** `finalize` / strict session-document `write --commit` paths now require a real `### Re:` response heading in `patch:exchange` or unmatched response text before response capture or visible mutation, so a stale-supervisor/IPC retry cannot commit body bullets without the assistant heading. Queue-continuation guidance now distinguishes degraded transport from stale-binary supervisors: recycle/yield the stale supervisor, then continue draining on the fresh binary.

## 0.34.35

- **`#freshqueueauth` — fresh operator queue heads stay authoritative unless an explicit removal proof exists.** `queue consume` now tells agents the safe next operation for an id-backed head: complete/gate it through closeout, explicitly acknowledge a correction head with the new `agent-doc queue consume --ack-id <id>` path, or leave it queued. `--ack-id` strikes an exact id-backed queue head while preserving the still-open backlog item, so correction/acknowledgement heads tied to open work can be cleared without falsely marking the work done. `queue prune-noise`, session-check guidance, and queue removal ops logs now use predicate/proof wording (`base_hash`, `source_component`, `operation`, `proof`) so fresh drainable operator prompts are not described as stale/noise unless the exact noise/orphan predicate was proven.

- **`#orphanqhead` — `queue prune-noise` now bulk-strikes orphan id-backed queue heads.** A `do [#id]` / `[#id]` head whose id names no open `agent:backlog` item ("orphan") was already excluded from `queue_drainable_head_count` (`head_is_drainable`), but it had no bulk removal path: `queue consume` rejects id-backed heads, `--done <id>` is a no-op, and `prune-noise` skipped anything carrying an `#id`. So the orphan sat at the queue head, was excluded from the drainable count, yet BLOCKED the leading-run `queue consume` from reaching answered free-text heads behind it — the `#qchurn` no-op loop (the live `:pushpin: [#kcb5]` repro, whose backlog item had been dropped as a `#6b5h` duplicate). Fix: `prune_noise_queue_heads` now also collects orphan id-backed head node keys (`orphan_id_queue_head_node_keys`) and strikes them alongside noise, through the same editor-IPC-converged write path. Gated on an `agent:backlog` component being present (a free-form id-head queue treats id-heads AS the work and is left alone), and preserves any id still naming open backlog work — including deferred `[operator-verify]` / `[focused-cycle]` items. Complements the existing targeted `queue consume --id <id>` escape hatch with a position-independent bulk sweep. Full suite + clippy green.

## 0.34.34

- **`#qstallguard` Layer C HOTFIX — rate-limit the paused-queue failsafe drain (it was re-flooding).** The 0.34.32 Layer C fall-through had NO rate-limit: on every supervisor idle-watch tick where a paused queue had a drainable head and no in-session loop owner, it logged `queue_paused_failsafe_single_owner_drain` and fell through — reintroducing the exact `#rt83`/`#qflood` per-tick (~2/sec) flood the pause exists to prevent (observed live: 3000+ ops.log lines; log-only — the downstream `turn_active` guard prevented actual pane dispatch, but the supervisor never reached an idle recycle boundary). Fix: the drain-owner lease is now computed ONCE before the pause gate and the failsafe CLAIMS it as `supervisor-failsafe` when it dispatches, so the gate (`paused_idle_watch_should_skip`, keyed on a fresh lease) defers every subsequent tick until the lease TTL (90s) expires — single-owner cadence (≤1 dispatch / 90s), never the per-tick flood. The dispatch this tick still proceeds because the drain decision uses the PRE-claim lease value. A fresh lease now means "in-session `/loop` owner OR the supervisor's own recent failsafe claim" — either defers. Confirmed live: flood rate dropped from ~16/8s to 0/15s after the host recycled onto this build. Full suite + clippy + `make tmux-ci` green. (Known follow-up: the drain-owner lease path is keyed on the raw doc-path string, so relative-vs-absolute callers can hash to different lease files — a `[focused-cycle]` item; within a single supervisor process the claim/read agree, so the rate-limit holds.)

## 0.34.33

- **`#qstallguard` Layer B/C interaction fix — the supervisor failsafe drain no longer false-fires the stall guard.** Layer B (`drain_stall`) drops a continuation-pending marker at a clean in-session closeout; Layer C lets the supervisor idle-watch perform a single-owner failsafe drain of a paused queue. Without coordination, when the supervisor drained (paused-failsafe OR normal go-mode), the next drained agent's preflight would see the marker with no in-session drain-owner lease and emit a spurious `queue_stall_detected` — even though the supervisor *was* actively continuing the drain (not a stall). Fix: the idle-watch clears the continuation-pending marker at its drain-`Dispatch` decision, so a supervisor-progressed drain is correctly not classified as an in-session stall. Full suite + clippy + `make tmux-ci` green.

## 0.34.32

- **`#qstallguard` — make non-stalling of a drainable queue a code-enforced invariant, not advisory prose.** A live dogfooding stall (the binary reported `queue_continuation_required=true` / `queue_drainable_head_count=1` and the agent stopped anyway) exposed that the extensive "do not stall" guidance in `SKILL.md` is advisory — an LLM can always synthesize a plausible stop reason from item prose. Three defense-in-depth layers, each pure-function unit-tested (the regression-proofing that survives refactors):
  - **Layer A — drainability is a typed attribute, never inferred.** New `[focused-cycle]` execution-context tag (`agent_doc_core::pending`): the operator's binary-read knob for "agent-doable but needs its own dedicated cycle, do not auto-drain in the loop" (e.g. merge-core / supervisor-core work needing `make tmux-ci` across live panes). `ExecutionContext::loop_undrainable()` is now the SINGLE authority for "the loop must not auto-drain this head" = `[operator-verify]` (needs a human) ∪ `[focused-cycle]`; `[clean-session]` is excluded (it drains in place, `#qcontdrain`). `deferred_backlog_ids` (continuation calc) and `partition_drainable_backlog_ids` (backlog→queue sync) both key off `loop_undrainable()`. The agent can no longer reclassify a drainable head as undrainable by reading its description — absent a tag, a drainable head is drained.
  - **Layer B — binary-detected stall guard** (`drain_stall.rs`). A clean closeout that still requires continuation drops a one-shot continuation-pending marker (`session-check`); the next preflight reconciles it and emits a hard `queue_stall_detected` warning + `ops.log` line when drainable work remained, the loop did not continue (no fresh drain-owner lease), and no valid stop reason applied (a real user prompt / `queue: stop` / drained queue — a degraded/stale supervisor, high accretion, and `semantic_completion_match` are explicitly NOT valid stop reasons). The marker is one-shot so the diagnostic fires once per stall.
  - **Layer C — pause throttles to single-owner, it does not disable the failsafe** (`start/idle_watch.rs`). An accepted `admin queue pause` is the `#rt83`/`#qflood` flood guard; it previously made the attended in-session `/loop` the ONLY drainer, so a stalled loop stranded the queue. Now the supervisor idle-watch skips a paused queue ONLY when an in-session loop owns the drain (fresh drain-owner lease) or nothing is drainable; with no loop owner and a drainable head it performs a single-owner failsafe drain (falling through to the normal `turn_active` / route-in-flight / cooldown-guarded drain decision — one dispatch per turn, never the 2/sec flood). Logs `queue_paused_failsafe_single_owner_drain`.
  - New unit tests: `focused_cycle_tag_is_loop_undrainable_but_clean_session_is_not`, the `drain_stall` suite (stall fires / no-marker inert / loop-continuation clears / each valid stop reason suppresses / degraded supervisor is not a valid stop / marker one-shot roundtrip), `paused_failsafe_drains_only_when_no_loop_owner_holds_a_drainable_head`. Full suite + clippy + `make tmux-ci` green. End-to-end SimWorld coverage of the pause-failsafe + the operator live two-pane verification are tracked as a `[focused-cycle]` follow-up.

## 0.34.31

- **`#orchver` — the stale-binary warning no longer lies "launched as 0.1.0".** `supervisor_stale_warning_message` (and the `content_ours_adoption_refused_stale_supervisor` ops.log lines it feeds) stamped the controller/supervisor version from `ControllerBinaryIdentity.version`, which was recorded via `env!("CARGO_PKG_VERSION")` **inside the `agent-doc-orchestration` crate**. That internal workspace crate is pinned at `0.1.0` and never bumped in lockstep with the top-level `agent-doc` binary (now `0.34.x`), so **every** controller/supervisor reported "launched as 0.1.0" regardless of the real build — misleading the operator into thinking an ancient binary was running when only the binary len/mtime comparison actually drives staleness (the version field is display-only; it never affected the `recorded != current` decision). Observed dogfooding on `sampleorders.md` (sample-app), whose long-lived `agent-doc start --route-owned` supervisor genuinely needed a recycle after a `cargo install` but reported the bogus `0.1.0`. Fix: the binary crate injects its real `CARGO_PKG_VERSION` once at `main()` startup via `project_controller::set_binary_version`; `current_binary_identity()` stamps that injected value and falls back to the orchestration crate version only for library-only callers / tests. New unit tests `identity_version_prefers_injected_binary_version` + `identity_version_falls_back_to_crate_version` cover both paths. The underlying staleness detection is unchanged — a genuinely stale supervisor is still flagged; the warning now names the true installed version so `agent-doc admin recycle` guidance is trustworthy. Full suite + clippy green.

## 0.34.30

- **`#qconvbaseline` — ROOT FIX for the "every finalize drifts while I edit the doc" race.** When a live JB plugin listener owns a document, preflight queue maintenance converges the corrected queue shape (auto-pins, backlog→queue mirrors, do-prompt sort, `queue:` control) into the **editor buffer + snapshot** via IPC with **no disk write**. But the baseline was saved (`run.rs`, just before queue maintenance) from the **pre-convergence disk** content, so at finalize the converged editor buffer differed from both the baseline and `content_ours` *outside* `exchange` — tripping `live_prompt_drift_after_preflight` on **every** cycle, which forced the `content_ours` carry-forward + a recovery `agent-doc commit`. That is the recurring race observed dogfooding this session (and a contributor to the `#editorbufwin` / `#docdriftgrace` / `#hap7` family). Fix: after queue maintenance, `realign_baseline_to_converged_queue` splices the converged **queue component** into the pre-maintenance disk content and re-saves the baseline — so `content_ours` matches the editor buffer's queue and only GENUINE concurrent user edits trip the drift guard. The splice is queue-scoped: `exchange` / boundary markers are preserved exactly, so non-queue preflights (e.g. orchestrate streaming) are untouched, and a no-convergence cycle is a no-op. New regression test `queue_convergence_realigns_baseline_so_finalize_sees_no_false_drift` proves the false drift before / clean after / and that a real concurrent user prompt still drifts. Full suite (4837) + clippy green.

## 0.34.29

- **`#kcb5` Phase 1 groundwork (`#kcb5a`): editor-less CLI finalize-wedge decision primitive (seam-isolated, not yet wired).** A pure-CLI agent-doc session (no JetBrains IDE; `controller serve` as the sole daemon) wedges every finalize: the controller hosts the editor-IPC socket even with no plugin attached, so `is_listener_active` returns true (socket connectable) while there is no editor endpoint behind it — the fail-closed disk-write guard then refuses the write (`no_ack` → `retry_without_disk_write`) and only `--force-disk` succeeds. Root cause: "socket connectable" ≠ "live editor present." This release lands the safe decision core only: `decide_editorless_disk_fallback(socket_connectable, editor_endpoint_proven, consecutive_no_ack, threshold, force_disk_requested) -> {FailClosed | ForceDiskNoEditor | ConvergeViaEditor}` in `agent-doc-orchestration::flow::document_mutation`, with the safety invariant that a PROVEN live editor still fail-closes on unproven delivery (preserves `#editorbufwin` / the FCC guard) while an editor-less / no-listener / `--force-disk` case routes to disk. Unit truth-table coverage + a `editorless_cli_sim_force_disk_but_live_editor_fail_closed` SimWorld scenario. **No live-path rewire yet** — the finalize/converge guard still behaves identically; wiring is Phase 3 (`#kcb5c`), gated behind the editor-presence signal (Phase 2 `#kcb5b`) and an operator editor-less live repro (Phase 4). Plan: `tasks/agent-doc/plan-kcb5-editorless-cli-finalize-wedge.md`. Full suite (4836) + clippy green.

## 0.34.28

- **Plugin-side reconnect re-read: a stale editor buffer no longer reverts the binary's committed writes (`#yzer` / `#evmhplugin`, the plugin half of `#evmh`).** When the JB plugin was disconnected from IPC (supervisor down, plugin/cdylib reload) the binary may have committed control-plane content to disk/HEAD, leaving the open editor buffer stale. On the next `save_document` the stale buffer would overwrite HEAD — the `#postcommit-ipc-worktree-corruption` direction. The plugin now reconciles on IPC (re)connect: in `PatchWatcher.registerRoot`, after the socket listener starts, it walks every open `.md` session document under that root and asks the new binary FFI `agent_doc_reconnect_buffer_decision(root, file, buffer)` whether the buffer is stale. The decision is owned by the binary (pure `decide_reconnect_buffer` in `agent_doc_document_realtime::write_policy`): it re-reads disk only when the buffer is **provably** stale — it equals a recent prior commit of the file (`ffi_show_prior_blobs`) **and** disk equals clean `HEAD` (`ffi_show_head`). Otherwise it keeps the buffer, so genuine unsynced user edits are never clobbered (editor wins, per `#editorbufwin`). A `reread_disk` decision carries the disk content; the plugin applies it via `applyReconnectReread` (re-checks the live editor generation, `setText` + `saveDocument` to clear the dirty flag). Emits a `reconnect_buffer_decision decision=... #yzer` marker to `ops.log` for live verification. New tests: `decide_reconnect_buffer` unit coverage (in_sync / reread / keep) plus the `reconnect_buffer_sim_rereads_stale_then_keeps_user_edits` SimWorld scenario (re-read a prior-commit buffer, keep an offline-edited buffer). Binary half (`reset --from-current` converge seam) shipped in 0.34.27. Full suite (4832) + clippy green. **Live reconnect verification is operator-gated** (needs a real editor disconnect/reconnect).

## 0.34.27

- **`reset --from-current` resume-clear now routes through the listener-guarded converge seam (`#evmh` / `#cyh0`).** The default `reset --from-current` (without `--preserve-session`) clears the `resume` frontmatter pointer and rewrote the session document with a bare unguarded `std::fs::write`. When a live JB editor listener held the document open, that disk write diverged the editor buffer from disk and raised a `File Cache Conflict` — one of the recovery-path FCC triggers diagnosed in the agent-doc-bugs2 dogfooding session (the others — `apply_compacted_document` via `#w42v`, the post-commit worktree reconcile, and `write.rs` `atomic_write` — were already listener-guarded or are the intentional `--force-disk`/IPC-unavailable fallback). The resume-clear write now goes through `agent_doc_orchestration::write::converge_or_disk_write(..., "reset_resume_clear")`: a live editor listener converges the change through the buffer (no FCC), and with no listener it falls back to the same CLI disk write as before, so headless `reset` is byte-identical. `reset --from-current --preserve-session` already only rebuilt sidecars and never touched the document, so it was never a trigger. New regression test `reset::tests::from_current_routes_resume_clear_through_converge_seam` asserts the resume-clear write is source-labelled in `ops.log` (`reset_resume_clear_writeback ... transport=disk_fallback`), proving it routes through the seam rather than a bare write; existing reset tests confirm the headless path is unchanged. Full suite + clippy green. (The plugin-side reconnect "re-read disk/HEAD when the editor buffer is stale" half of `#evmh` remains separate Kotlin/FFI follow-up work.)

## 0.34.26

- **Operator-deleted structure the agent targeted is now surfaced in `agent:exchange` (`#hap7` / `#qdup`, deleted-structure rule).** Second half of the scoped-merge no-structural-duplication fix. The node-keyed `semantic_merge` already prevents the operator-reported queue-prompt *duplication* (a concurrent operator queue edit can no longer duplicate/reverse/drop adjacent structure — see regression tests `qdup_operator_queue_add_during_exchange_turn_no_duplication` and `qdup_one_changed_node_leaves_siblings_byte_identical`). The remaining gap was the plan's deleted-structure rule: when the operator **deletes** a node the agent's content this cycle targeted (an `OperatorDeletedAgentEditedNode` outcome), the deletion correctly stood (node never resurrected) but the dropped agent edit was only carried forward as a next-cycle ack — and the live-prompt-drift convergence scopes acks to the `exchange` active area, so a queue/backlog deletion ack could be silently dropped. Now `semantic_merge` records each such fact as an `exchange_notes` entry and injects a one-line blockquote note into the merged `agent:exchange` component (before a trailing boundary marker if present), so the operator sees the dropped agent edit **this** cycle, independent of the scoped ack carry-forward. The note is a blockquote (never a `### ` heading or `❯` prompt) so it cannot be misclassified as a response turn or user prompt by the convergence/drift gates; injection is idempotent (a note already present in the body is not re-added) and a no-op when no `exchange` component exists. The in-editor document remains the source of truth — the agent change is never merged back. New unit tests in `semantic_merge.rs` (`qdup_*`) plus SimWorld end-to-end coverage (`hap7_sim_operator_queue_add_during_exchange_turn_no_duplication`, `hap7_sim_operator_deleted_agent_targeted_node_noted_in_exchange`). Full suite + clippy green.

## 0.34.25

- **Answered free-text queue heads with a pasted code-fence log are now struck (`#ftstrike-fence`).** Operator-reported: answered free-text queue items (e.g. `JB Run Agent Doc on sampleportal.md did not submit` followed by a fenced route/console log) stayed unstruck in the queue forever, even though the response quoted and addressed them. Root cause: the position-independent answered-free-text strike (`#ftstrike`, `strike_answered_free_text_queue_heads`) matched a head by checking whether its *entire* normalized node text appeared inside the response's quoted-prompt blockquotes. For a head whose body is dominated by a pasted log, that whole-text key can never appear in a blockquote (nobody quotes the full log back), so `free_text_head_answered_by_response` always returned false and the head was never struck — it then fell behind newer heads and orphaned. Fix: match on the head's **prose prefix** (every line before the first ` ``` ` / `~~~` fence) via the new `free_text_head_match_prose`, so a code-fenced report strikes when its prose lead is quoted. The ≥4-significant-word guard and the blockquote-only requirement are preserved (a head that is all log, with an empty prose prefix, still never matches — no false strikes). Regression test `code_fenced_free_text_head_strikes_on_prose_lead_match`; full suite + clippy green.

## 0.34.24

- **Stale-binary recycle-yield: a self-draining `/loop` now yields one boundary so the supervisor can hot-reload onto a freshly-installed binary (`#wd40` / `#staleloop-recycle-restart`).** A continuously self-draining Claude Code `/loop` holds a fresh drain-owner lease AND keeps the harness `turn_active` back-to-back, so the route-owned supervisor never reaches its turn-boundary recycle and a freshly-installed binary never hot-reloads — the root of the recurring `content_ours` finalize drift + `#rt83` phantom-pin flood seen when dogfooding across a mid-session `cargo install`. Previously the operator had to manually `make install` + `agent-doc admin recycle` + end-turn to force the boundary. This automates it: when the supervisor idle-watch detects its own binary is stale AND a self-driving loop owns the drain AND a recycle WOULD fire at a boundary (not a bare `Detect`, and not after the Phase-3 kill+relaunch escalation is exhausted), it writes a short-TTL per-document recycle-yield request sidecar (`.agent-doc/recycle-yield/<hash>.json`, default 120s TTL, `AGENT_DOC_RECYCLE_YIELD_TTL_SECS` override). While that request is live, `queue_continuation::detect`, `preflight`, and `session-check` drop `queue_continuation_required` and surface `RECYCLE_YIELD_GUIDANCE` (an intentional, temporary yield — NOT a drained queue or a stop reason), so the in-session loop ends its turn cleanly; the idle boundary lets the `execve` recycle fire on its own, and the fresh (no-longer-stale) supervisor clears the request so the drain resumes on the new binary. Mid-turn `execve` stays out of scope — the yield is exactly what produces a clean boundary without a mid-write swap. New module `recycle_yield.rs` (request producer/reader/clear + pure freshness predicate) mirrors the `drain_owner` sidecar layout; pure policy `decisions::stale_drain_recycle_yield_requested` is unit-tested via truth table; the supervisor's own idle-watch drain uses `live_drainable_continuation_head` (not this), so it is unaffected and resumes after recycling. Full suite + clippy green.
- **Conventions: `#deploy-just-do-it` — agents execute every agent-doable release/deploy sub-step (version bump, `VERSIONS.md`, `make check`, commit, install + `lib-install`, push, `admin recycle`, tag, publish) without asking; only the live human eyeball is operator-gated, recorded as a non-blocking `[operator-verify]` follow-up.** Replaces the old "manual testing gate" that blocked publishing on operator confirmation. Documented in `AGENTS.md`.

## 0.34.23

- **Route trigger injections now emit a `dispatch_inject attempt=N` ops.log marker so a post-restart multi-inject regression is provable from logs (`#rdypoll` §D / img_52).** Operator-reported (2026-06-20): after restarting an agent-doc session, JB `Run Agent Doc` typed the `agent-doc <FILE>` trigger ~7 times into the harness composer with none submitted; on retry it worked, but "the restart state should not lag." The readiness gates that prevent the stacking landed in 0.34.21 (`#jbtsiftnosub` cold-start) and 0.34.22 (`#runexitrestart` restart-drain), but there was **no log marker proving how many times the trigger was actually injected** — so an operator who hit the duplicate stacking could not prove/disprove from `ops.log` whether a given dispatch re-typed. This adds a `dispatch_inject file=… pane=… harness=… transport=<direct_pane|supervisor_ipc> attempt=N` marker at both real injection funnels (`send_command_once_unchecked` direct-pane text+Enter, `dispatch_via_supervisor_ipc_with_mode` IPC inject) keyed off a process-global monotonic counter. A healthy dispatch logs `attempt=1` exactly once; a multi-inject regression (or a legitimate but visible `not_dispatched` full-trigger resend) shows `attempt=2`, `attempt=3`, … making the stacking class directly auditable. The route process is short-lived (one logical dispatch per `agent-doc route` invocation), so the monotonic counter cleanly answers "did this dispatch type the trigger more than once?" SimWorld coverage: `route_sim_restart_drain_waits_for_dispatch_ready_prompt_before_send` now also asserts the `dispatch_inject` marker is **absent** across all 7 not-ready restart ticks (`dispatch_injects == 0`) and present exactly once as `attempt=1` after `PromoteStartingPromptReady` — never `attempt=2` (a new `dispatch_injects` coverage counter mirrors the production marker through the model's accept-dispatch seam). Full suite + clippy green. **Live-verify (operator):** the two-pane restart repro (restart a session, JB `Run Agent Doc`, then check `ops.log` shows a single `dispatch_inject attempt=1`) and the `cargo install` / `admin recycle` deploy are operator-gated.

## 0.34.22

- **The supervisor idle-watch queue-drain now waits for the harness dispatch-ready prompt after a session RESTART, closing the restart variant of the JB `Run Agent Doc` no-submit-duplicate race (`#runexitrestart`).** Operator-reported (2026-06-20): after RESTARTING an agent-doc session, then JB `Run Agent Doc` on `sampleportal.md`, the `agent-doc <FILE>` trigger was typed ~7 times into the harness composer with none submitted. This is the RESTART sibling of the cold AUTO-START race `#jbtsiftnosub` fixed in 0.34.21: the route auto-start path (`route::startup`) and both existing-pane route paths (`dispatch_only_send_reopen` requires_ready_probe, `ensure_existing_pane_ready_for_dispatch`) already gate dispatch behind `wait_for_agent_ready_outcome` / `ready_prompt_candidate` (the strong `is_dispatch_ready_prompt_line` predicate), but the **supervisor idle-watch drain loop** (`idle_watch.rs`, the one looping site that can re-type each tick) gates on `idle_queue_prompt_visible`, which off the `actor_state == Ready` fast path falls back to the *weak* `child_output_prompt_visible` → `matches_prompt`. On a fresh restart the actor is `Starting`, the edge-triggered pty `terminal_screen` buffer can render a prompt *glyph* (matching `matches_prompt`) while the restarted composer is not yet submit-ready, so the per-idle-tick drain re-injected the trigger into a not-ready composer (Enter never submits) and each tick stacked another un-submitted copy — the operator's ~7 duplicates. The `#qflood2` pre-send dedup (`supervisor_pane_payload_already_pending`) cannot reliably catch a partially-rendered restarting composer, so it did not suppress the stacking. Fix: `idle_queue_prompt_visible` now, when the actor is NOT yet `Ready` but the weak pty-buffer signal is positive, re-verifies against a **fresh tmux capture** of the owned pane via the new `supervisor_pane_dispatch_ready` helper (mirroring `supervisor_pane_has_busy_cue`'s live-capture pattern) using the same canonical `route::ready_prompt_candidate` / `is_dispatch_ready_prompt_line` predicate the route and cold-start gates use. A fresh capture that proves a submit-ready empty composer dispatches; one that shows only a not-yet-ready glyph fails closed and defers the drain this tick (no trigger typed, nothing to re-stack); an unreadable/absent capture (`None`) conservatively falls back to the prior pty-buffer signal so a transient capture failure never permanently suppresses a legitimate drain. The `actor_state == Ready` fast path and the OpenCode/Codex idle-chrome paths are unchanged; `idle_queue_prompt_visible` has exactly one production caller (the idle-watch drain), bounding the blast radius. SimWorld coverage: `route_sim_restart_drain_waits_for_dispatch_ready_prompt_before_send` (new `DispatchIdleQueueDrainAfterRestart` command + `drain_into_restarting_pane_blocks` coverage) restarts a ready session to `Starting`, then asserts the idle-watch drain fails closed on **all 7** ticks (records `dispatch_into_restarting_pane`, `route_dispatch_acceptances == 0`, `go_drain_dispatches == 0` — no duplicate triggers), then dispatches exactly once after `PromoteStartingPromptReady`. Full suite + clippy green. **Live-verify (operator):** the two-pane restart repro (restart a session, JB `Run Agent Doc`) and the `cargo install` / `admin recycle` deploy are operator-gated.

## 0.34.21

- **Three merge/commit-core safety fixes that cascaded in a live degraded session: compaction overlay-CRDT staleness, reset queue-journal clear, and queue-consume head-divergence reconcile (`#editorbufwin` Fix A).** (1) **Compaction overlay-CRDT staleness** (`compact.rs`): a CRDT-mode `compact --commit` no longer leaves the overlay CRDT carrying the PRE-compaction (large) markdown. The early template-branch `save_document_crdt(file, &compact(&crdt_state), &content)` was the defect — it saved the overlay with the large `&content`, so later cycles re-projected (`load_overlay_crdt`→`to_markdown`) snapshot(large) > visible(small) and tripped `guard_no_stale_snapshot_reset_drift`'s "looks like a manual cleanup" refusal. That early stale save is removed; `apply_compacted_document(..., refresh_crdt=true)` is now the single authoritative CRDT writer and rebuilds the overlay from the COMPACTED text (fresh `CrdtDoc::from_text` / `OverlayCrdtDoc::from_markdown`, which also supersedes the old tombstone-GC step since fresh docs carry no tombstones). (2) **Reset queue-journal clear** (`reset.rs`): `reset --from-current [--preserve-session]` now clears the crash-durability queue journal (`queue_journal::clear`, mirroring the commit-time clear) after rebuilding the sidecars. The rebuilt snapshot/baseline IS the new durable queue baseline, so a pre-reset journal window (heads recorded while older prompts were live) is superseded — without this, answered+compacted heads would be re-inserted by `queue_journal::replay_missing` at the next `start` and resurface over the current queue. (3) **Queue-consume head divergence** (`write/queue_consume.rs`, `#editorbufwin` Fix A): the snapshot/content_ours head is the OLD head (the live user queue addition is deliberately NOT absorbed into content_ours), while the document head read fresh from disk is the user's live editor-buffer addition, so a benign live editor buffer made the head-equality check hard-bail EVERY cycle (the remaining-queue check below already tolerated this kind of divergence). The head check now reconciles — log `queue_consume_head_divergence_reconciled reason=live_buffer_addition_authoritative` and proceed using the DOCUMENT head as authoritative — but ONLY when the divergence is explained by recorded dropped-queue evidence (`cycle_state::dropped_queue_prompts`, written by the ipc write path); with no evidence it keeps the hard-bail as a corruption guard. content_ours/snapshot composition is untouched (the `ipc_live_prompt_drift_content_ours_ignores_unproven_live_queue_deletions` invariant still passes). Tests: `compact_advances_snapshot_and_crdt_so_next_preflight_does_not_refuse`, `preserve_session_clears_stale_queue_journal_so_compacted_heads_do_not_resurface`, `queue_consume_head_divergence_reconciles_with_dropped_queue_evidence` (+ negative `…_without_evidence_still_bails`). Full suite + clippy green. **Live (operator):** the `cargo install` / `admin recycle` deploy and the live zero-drift proof against a running route-owned supervisor remain operator-gated.

## Unreleased

- **Pre-run live-editor queue batches now have exact regression proof (`#qeditrace`).** Queue maintenance already reads the complete Lazily editor head and compare-and-swaps the whole document, but the original “only the first new head survives” report had no live multi-add reproduction. New embedded-relay coverage replaces stale queue rows with three unsaved heads and proves preflight preserves every addition in Lazily plus the recovery baseline without touching stale disk. If the head advances after planning, maintenance now records a durable `queue_maintenance_compare_and_swap_blocked ... recovery=retry_from_live_head` artifact and fails closed; SimWorld proves that stale first-add observation cannot mutate the batch and that retry converges all heads through editor, canonical, disk, and HEAD.

- **Busy `session clear` now queues one deferred clear and dedupes repeats (`#p6a0`).** A non-interrupting `agent-doc session clear` against a busy active auto-loop now records a single deferred clear for the supervisor's next proven idle boundary instead of asking the operator to retry. Repeated clears while that marker is pending report the already-deferred state and do not refresh the marker, extend cooldown, or inject another `/clear` into the active turn. Coverage adds queue-preemption, session-clear message, and SimWorld regressions.

- **Stale-supervisor self-recycle proof now covers the File Cache Conflict refusal loop (`#fccsup`).** Added a regression that ties the default-on queue-boundary recycle policy to the host-supervisor inode guard: a stale supervisor with a pending queue head must choose `RecycleImmediate`, and once `supervisor_binary_stale_self_recycled` maps the installed inode, the stale-supervisor `content_ours` refusal guard is no longer eligible. Also corrected stale code comments that still described supervisor auto-recycle as default-off.

- **Supervisor idle-queue submits now defer while an editor is actively typing the queue head (`#jbtypingguard`).** Operator-reported via JetBrains `Run Agent Doc`: a binary-owned auto continuation could add `/clear` plus `agent-doc tasks/...` while the operator was still typing in `agent:queue`, racing the manual JB route that submits the absolute-path `agent-doc /.../<FILE>` trigger and leaving both prompts unsubmitted. Root cause: preflight already honored the cross-process typing sidecar, but the long-lived supervisor idle-queue watcher only gated on route-in-flight, clear-settle, queue-edit leases, and pane idleness; it did not treat live editor typing as input ownership, and route-owned supervisors can hold a relative document path while JetBrains records typing against the absolute path. Fix: idle-watch checks both the current and resolved absolute document paths for the typing sidecar, logs `idle_queue_watch_skipped ... reason=editor_typing_active`, and the reset/drain decision policy now has explicit `SkipEditorTyping` outcomes. Coverage: pure reset/drain decision tests plus `idle_queue_typing_guard_checks_absolute_editor_path`.

- **Codex/Claude/OpenCode direct-pane routed dispatch now retries submit at least once per second while the `agent-doc <FILE>` trigger remains visibly drafted (`#jbcodexsubmit` / `#jbclaudesubmit`).** Operator-reported via JB `Run Agent Doc`: Codex routes were either not submitting or appeared very slow because the Enter retry loop waited a full 5s submit-acceptance window before nudging again. The acceptance window is now 1s, the default Enter retry cap is 30 attempts to preserve roughly 30s of recovery, and the cap remains env-tunable through `AGENT_DOC_DIRECT_PANE_MAX_ENTER_RESUBMITS`.

- **A stale-binary supervisor now auto-recycles during a continuously self-draining session by asking the in-session loop to yield one boundary (`#wd40` / `#staleloop-recycle-restart`).** The supervisor hot-reloads onto a freshly-installed binary only at a turn boundary (`prompt_visible && !turn_active`; see `supervisor_recycle_action`). A long in-session Claude Code `/loop` drain holds a fresh drain-owner lease AND keeps the harness `turn_active` back-to-back, so the supervisor never reaches that boundary — a freshly-installed binary never hot-reloads and the stale supervisor persists for the whole session (the root of the `content_ours` finalize drift + `#rt83` phantom-pin flood, since `#supselfheal` already notes a stale *binary* does not self-heal by lease expiry; it needed a manual `make install` + `admin recycle` + end-turn). Fix: when `idle_watch` detects its own binary is stale, a self-driving loop owns the drain, and a recycle WOULD fire at a boundary, it writes a short-TTL recycle-yield request sidecar (`.agent-doc/recycle-yield/<hash>.json`, new `recycle_yield` module, default 120s TTL, `AGENT_DOC_RECYCLE_YIELD_TTL_SECS` override). The attended in-session loop reads it at its next inter-item boundary — `queue_continuation::detect` returns no continuation, `session-check` prints `queue_recycle_yield=true` + the new `RECYCLE_YIELD_GUIDANCE`, and preflight drops `queue_continuation_required` with the same guidance — and yields one boundary instead of re-triggering. The resulting idle turn lets the `execve` recycle fire on its own; the fresh (no-longer-stale) supervisor clears the request and the drain resumes on the new binary (the loop may `agent-doc drain-claim <FILE> --release` to hand back immediately rather than wait for the lease TTL). The supervisor's OWN idle-watch drain uses `live_drainable_continuation_head` (not `detect`), so it is unaffected and resumes the drain after recycling. A bare `Detect` (auto-recycle opted out, no admin/wedge) does not request a yield (it would only stall the drain), and an exhausted Phase-3 kill+relaunch escalation does not yield-loop. New `ops.log`/session-log marker `supervisor_recycle_yield_requested` (`reason=stale_binary_drain action=signal_loop_yield`) proves the request live. Mid-turn `execve` stays OUT of scope — the supervisor owns the in-flight cycle CRDT/write-queue/IPC state (rebuilt fresh after re-exec); the yield is exactly what produces a clean boundary. New pure decision `stale_drain_recycle_yield_requested` (unit-tested truth table) + `recycle_yield` module unit tests + `detect_yields_when_supervisor_requests_recycle_yield` integration test; full suite + clippy green. **Live-supervisor-critical (operator):** the two-pane `make tmux-ci` self-recycle repro and the `cargo install` deploy are operator-gated. Plan: review item `#wd40`.

- **Auto-start route dispatch now waits for the harness dispatch-ready prompt before sending, closing the JB `Run Agent Doc` cold-start race (`#jbtsiftnosub`).** Operator-reported (2026-06-19): JB `Run Agent Doc` auto-started a fresh supervisor/tmux pane, typed the `agent-doc <FILE>` trigger into the Claude composer, but did NOT submit it. Root cause: a cold-start race distinct from the crashed-harness case (`#1vhn`/issue A). The "starting actor reroutes are prompt-gated" invariant already promotes a `starting` actor to `ready` only after a harness-specific dispatch-ready prompt is observed, but the **auto-start** path (`route::startup::auto_start_ext`) that creates a fresh pane did not hold the actual send behind that same gate: after `wait_for_agent_ready` proved a (possibly transient) dispatch-ready prompt while the Claude TUI was still coming up, the path went straight to `dispatch_routed_reopen` with no re-verify immediately before the send, so the trigger keystrokes could land in a not-yet-submit-ready composer and the Enter never registered as a submitted prompt. Fix: a new `reverify_auto_start_dispatch_ready` bounded-poll gate (`AUTO_START_DISPATCH_READY_REVERIFY_TIMEOUT`, 5s) runs immediately before the managed auto-start send; it re-captures the fresh pane and proceeds only when `ready_prompt_candidate` still proves a dispatch-ready harness prompt. If the bound elapses while the pane is still cold-starting it fails closed with claim/restart guidance and logs `dispatch_into_starting_pane` (`reason=harness_not_dispatch_ready_before_auto_start_send`); a pane that has dropped to a bare interactive shell during cold-start is distinguished and logged as `dispatch_into_shell` (the issue-A signature). The new `auto_start_dispatch_ready_block` helper classifies the pane state (`StartingPane` vs `DeadShell`) so the diagnostic distinguishes the cold-start race from the crashed-harness case from a normal dispatch. SimWorld coverage: `route_sim_auto_start_dispatch_waits_for_dispatch_ready_prompt_before_send` (new `DispatchAutoStartRoutePrompt` command + `auto_start_starting_pane_blocks` coverage) asserts that an auto-start dispatch into a still-`Starting` pane fails closed and records `dispatch_into_starting_pane`, then dispatches and proves submitted once the dispatch-ready prompt is observed. Full suite + clippy green. Plan: `tasks/agent-doc/plan-route-dispatch-into-crashed-harness.md` (Section C). **Live-verify (operator):** the two-pane cold-start repro (JB `Run Agent Doc` auto-starting a fresh pane) and the `cargo install` deploy are operator-gated.

- **Per-node semantic merge now preserves free-text / fenced queue heads, fixing the persistent `live_prompt_drift` editor-IPC convergence failure (`#qdup-freetext`).** Root cause of the recurring degraded session where **every** write fails `ack_mismatch` / `live_prompt_drift_after_preflight` (557 consecutive blocked events observed on `agent-doc-bugs2.md`, **zero** `live_prompt_drift_semantic_merged` successes): the node-keyed `semantic_merge` reconstructs a non-`exchange` component **only** from its `- ` bullet list items. A queue head that is multi-line *free text* — an operator-pasted console block (`:pushpin:` line + a fenced ```` ``` ```` block between `---` rules) — is not a bullet item, so `overlay::components` never parses it as an `Item`. `merge_components_into_body` then emitted only the bullet items and **dropped** every other inner line, so the merge silently lost the free-text head, tripped its own `dropped_queue_prompt_lines_after_content_ours` anti-data-loss gate, declined the merge **on every cycle**, and left every IPC write stuck on the blocked `content_ours` carry-forward path — surfacing to the operator as permanent buffer corruption / queue churn that "only an IDE file reload clears." Fix: a new `merge_nonexchange_inner` preserves operator non-bullet inner content (free-text heads, fenced code blocks, `---` separators, blanks) verbatim — tracking fenced spans so a `- ` inside a fence is not mistaken for a bullet — while still replacing the bullet-item region with the merged item set (placed at the first bullet position; appended after the prose when the component has no operator bullets). This generalizes the buffering already used for `exchange` heading-prose turns to all components, so a disjoint operator/agent edit around a free-text head now converges instead of blocking. Tests: `freetext_fenced_queue_head_survives_per_node_merge` (markdown-ast unit) and `smconv_preserves_freetext_fenced_queue_head_on_drift` (IPC convergence: the guard now adopts the merge instead of blocking, head preserved verbatim); FlowCore hot-path token budget updated (`ipc.rs` `guard_` 16→17, the new test's guard call); full suite (4807) + clippy green. Plan: `tasks/agent-doc/plan-scoped-crdt-merge-no-structural-duplication.md` (`#hap7`/`#qdup`).

- **Log timestamps are now human-readable ISO-8601 UTC (`#opslogts`).** Operator request: "ops.logs should have each entry contain a [readable] timestamp." Every operational log entry was prefixed with a bare Unix epoch (`[1781771180]`), which is illegible when reading the supervisor session log / `ops.log` to verify reported issues (e.g. correlating the `#tsiftmdcrash` SIGTERM to wall-clock time). New `agent_doc_core::log_time` module formats epochs as `YYYY-MM-DDTHH:MM:SSZ` and parses them back, with **no external date dependency** (Howard Hinnant's civil-date algorithms). All writers now emit ISO: `ops.log` (`ops_log`), the supervisor session log (`start::log_event`, `startup_miss`), cycles.jsonl (`iso_timestamp`), and the `/tmp` write-dedup / sync debug logs. Crucially, `parse_log_timestamp` is **backward-compatible** — it accepts both a bare epoch and ISO — so every timestamp **reader** keeps working across the switch: the staleness/accretion windows (`session_accretion`), startup-miss windows (`startup_miss`), and the `gate_verify` ops.log scanner (incl. the `s760_clear_decision_clear_true` verifier, which compares marker times to `set_at`). The helper lives in `agent-doc-core` so the core `gate_verify` scanner and the orchestration writers share one implementation. Tests: `log_time` known-vector + epoch/ISO round-trip (incl. leap day) + garbage-rejection, and the `ops.log` integration test now asserts an ISO bracket that round-trips. SPEC (`specs/supervisor.md`, `specs/07-core-commands.md`, `specs/07-closeout-commands.md`) updated.

- **Capability-proof give-up no longer SIGTERMs the live hosted harness child (`#tsiftmdcrash`) — root-fix for the "tsift.md turn crashed and killed the session while the tmux pane stayed alive" report.** Root cause, found in `tsift-v0.1.log`: the managed OpenCode session on pane `%78` was killed **twice** with `opencode_exit code=143` (143 = 128+15 = SIGTERM), each time at the same instant as `opencode_capability_proof status=failed attempts=3` ("opencode child network probe timed out after 45s"). The capability-proof thread's `GiveUp` branch called `shared.kill_child()` (`start.rs:1844`), SIGTERM-ing the **live interactive harness the operator was actively using** because a *separate background* `opencode run` network-probe child timed out (a false negative — the real TUI was "Thinking normally"). The supervisor owns that kill, so it stayed alive and the pane stayed active, then auto-restarted the child 2s later — exactly the operator's "crash that killed the session process while the pane stayed active" symptom. The kill was also redundant: the `Failed` gate already blocks **all** prompt dispatch via `capability_dispatch_blocker`, so no unsafe work can be auto-dispatched even with the child alive. Fix: the `GiveUp` branch no longer kills the child — it keeps the gate `Failed` (dispatch disabled), marks the actor `Blocked`, surfaces the diagnostic, and logs `<harness>_capability_proof_live_child_preserved reason=dispatch_gated_not_killed`. The operator's live session survives; they fix the environment / stop / restart to re-prove. Test: `failed_capability_proof_gate_blocks_dispatch_so_live_child_need_not_be_killed` (locks in that the `Failed` gate is itself the complete dispatch block that makes preserving the child safe). SPEC (`specs/codex-support.md`) + README updated. **Live-verify (operator):** on a managed OpenCode/Codex session, force a proof failure (e.g. network blip) and confirm the pane's harness stays alive with `…_capability_proof_live_child_preserved` in the session log and **no** `exit_code=143` kill.

- **Closeout now reaps dead-PID live-buffer sidecars, not just patch files (`#lbreap`).** The `#sqdrift` storm only happened because closed-IntelliJ orphan live-buffer sidecars accumulated unbounded (this doc had 187). `#fccreap` reaped dead-pid *patch* files at closeout but never the *live-buffer* sidecars, and `#sqdrift` reaps a dead peer only when a broadcast happens to touch it. New `reap_stale_jetbrains_live_buffers` runs in the post-commit closeout (beside `reap_stale_jetbrains_consumers`): it removes `.agent-doc/live-buffer/<stem>.jetbrains-<pid>-<uuid>` sidecars whose embedded pid is provably dead, never touching legacy no-editor-id or non-JetBrains sidecars. So orphans self-clear every cycle instead of piling up into a broadcast storm. Tests: `jetbrains_live_buffer_pid_parses_pid_from_sidecar_name`, `reap_removes_only_dead_pid_live_buffer_sidecars`, `reap_live_buffers_is_noop_on_missing_dir`; full suite (4802) + clippy green.

- **Realtime cross-editor broadcast no longer storms patches to dead editors (`#sqdrift` / `#fccreap2`) — root-fix for the recurring degraded session.** Root cause of the per-finalize `live_prompt_drift_after_preflight` + `postcommit_worktree_check match=false` degraded session: `realtime_model::broadcast_editor_change` built its peer set from **every** `live-buffer` sidecar with no liveness check, so a pile of closed-IntelliJ orphan sidecars (one per past window, accumulated over days) each became a broadcast target — and the broadcast was even being triggered with a **dead originator**. Observed live: 247 `realtime_broadcast_queued` events in a few minutes fanning out to ~22 dead `jetbrains-<pid>` consumers (all pids dead, no live IntelliJ), each (a) re-creating the dead-pid patch file the `#fccreap` reaper had just cleared and (b) merging against that dead editor's *divergent stale buffer* (merged_len 6300…73989), one of which then leaked into the finalize IPC-proof path as the drift candidate. Fix: `broadcast_editor_change` now liveness-filters the originator (a dead-pid origin skips the whole broadcast) and the peers (dead-pid peers are dropped **and** their orphan live-buffer sidecars reaped via `clear_live_buffer_for_editor`), so a dead editor is never a broadcast origin or target and the orphan sidecars self-heal. JetBrains ids carry the owning pid (`jetbrains-<pid>-<uuid>`); non-JetBrains ids (no embedded pid) are conservatively treated as live. Logs `realtime_broadcast_skipped reason=dead_origin_editor` and `realtime_broadcast_dead_peers_reaped count=<n>`. Tests: `editor_id_is_live_filters_dead_jetbrains_pids_only`, `broadcast_editor_change_skips_dead_origin`, `broadcast_editor_change_drops_and_reaps_dead_peer`; full suite (4799) + clippy green. (Separate from the stale-binary-supervisor self-heal `#supselfheal`, which the swapped-binary timing also exercised.)

- **Direct queue edits hold a queue-edit lease that preflight + the idle-watch defer to (`#sqedit-race` Phase 2).** The live-IPC-supervisor race on direct queue edits is the compounding of three concurrent queue writers (multiple plugin consumers → Phase 1 `#8bfz`, already shipped; preflight queue maintenance; supervisor idle-watch) observing a *torn intermediate* queue mid-edit and round-tripping it into corruption. Phase 2 adds the writer-side single-writer guarantee for the direct queue-edit commands: a new per-document `.agent-doc/queue-edit-owner/<hash>.json` lease (short self-healing TTL, default 15s; mirrors the `drain-owner` lease) is held for the whole `agent-doc queue prune-noise` / `agent-doc queue consume` read-modify-write via an RAII `QueueEditGuard` (released on drop, incl. early return / error). The two *other* concurrent queue writers now defer while a **different, live** process holds a fresh lease: `run_queue_maintenance` returns early without mutating (logs `queue_maintenance_deferred reason=queue_edit_lease holder_pid=<pid>`), and the supervisor idle-queue-watch skips the dispatch tick (logs `idle_queue_watch_drain_skipped reason=queue_edit_in_flight`). The short TTL makes this a brief yield, not a stall — the edit settles and the next preflight/tick proceeds normally on the clean queue. New `queue_edit_owner` module with the lease primitive, freshness predicate, foreign-holder detection (different-pid + fresh + live), and RAII guard. Tests: 5 `queue_edit_owner` unit tests + `run_queue_maintenance_defers_while_foreign_queue_edit_lease_held` (proves no mutation under a foreign lease, then resumes once cleared); FlowCore hot-path token budget updated (`maintenance.rs` `reason=` 3→4); full suite (4796) + clippy green. Remaining: Phase 3 (idempotent malformed-entry normalization — partly landed via `#qdup-bare-id`/`#qnoise-multiline-strike`/`#pushpinaccum`) and Phase 4 (operator-gated `make install` → `admin recycle` → live `prune-noise` no-reinjection proof). Plan: `tasks/agent-doc/plan-supervisor-direct-queue-edit-race.md`.

- **Routed dispatch detects a prompt that never landed and re-sends the full trigger (`#jbrundispatch` directive 2).** Operator directive on the "killed the pane + autostarted a new pane → Run Agent Doc stalled" report: *"the supervisor should detect if the prompt was not dispatched into the session, and send the prompt and submit the prompt."* Root cause: `poll_direct_pane_acceptance` treated an empty composer as a successful submit even when the trigger was **never** observed there — so a send that silently no-op'd into a not-ready pane (the pane-kill+restart case) was misreported as `Accepted`, and nothing re-dispatched. Fix: a new `not_dispatched` outcome — set only when the trigger was never seen in the composer AND the pane is sitting at an **idle dispatch-ready prompt** (`pane_idle_dispatch_ready`, reusing `is_dispatch_ready_prompt_line`). For an agent-doc trigger that starts a turn, a genuine submit leaves the pane *processing* (not idle), so empty+idle+never-seen reliably means non-dispatch and is safe from re-sending a real submit (which would double-run the agent). `send_command_unchecked` now re-sends the **full** trigger (text+Enter, not a bare Enter — there's no draft to submit) up to `direct_pane_max_enter_resubmits()` times until it lands, logging `route_redispatch_not_landed`, and reports a genuine `TimedOut` (not a false `Accepted`) if the budget exhausts. Tests: `pane_idle_dispatch_ready_distinguishes_non_dispatch_from_fast_submit`; dispatch suite (130) + `make check` green. Pairs with `#jbclaudesubmit` (retry-until-submitted) to close both halves of `#jbrundispatch`; both still want live verification of the actual failing pane state.

- **Routed-dispatch retries Enter until the trigger is submitted, with a higher + env-tunable budget (`#jbclaudesubmit`).** Operator directive on the "some JB `Run Agent Doc` ops don't submit to Claude Code" report: *"the supervisor should retry until the prompt is submitted."* The direct-pane submit path already re-sends a bare Enter while the routed trigger stays drafted in the composer (`send_direct_pane_enter_resubmit_until_stable`), exiting the moment the trigger is consumed — but the cap was a fixed `DIRECT_PANE_MAX_ENTER_RESUBMITS = 3`. Because Claude Code has no submit-proof hook (dispatch is accepted-only — text+Enter delivered without confirmation), a slow-to-focus composer could exhaust the 3-nudge budget before it consumed the Enter, leaving the trigger sitting unsent. Raised the default to 6 and made it env-tunable via `AGENT_DOC_DIRECT_PANE_MAX_ENTER_RESUBMITS` (`direct_pane_max_enter_resubmits()`), so the operator can crank "retry until submitted" without a rebuild during the live repro. The loop still exits immediately on submit, so the higher cap only costs wall-clock on a genuinely stuck pane. Tests: `direct_pane_enter_resubmit_is_bounded_while_trigger_remains_visible` updated to the tunable cap; full dispatch suite (129) + `make check` green. This is the "doesn't submit" half of `#jbrundispatch`; needs live verification (the pane-state where it failed) to confirm it closes the report vs needing a focus/readiness fix.

- **Preflight now collapses duplicate bare `[#id]` queue heads the mirror re-emits (`#qdup-bare-id`).** Operator-reported ("In sampleportal.md, I typed in queue items and agent-doc duplicated the queue items") and corroborated on this doc: `[#sqedit-race]` and `[#qpausemix-verify]` each appeared **twice** in the live queue. Root cause: the only id-dedup wired into preflight maintenance was the AST node-key dedup, which deliberately preserves occurrence-indexed duplicates, and the id-aware `dedup_live_prompts` was never wired in (preserving `do [#id]` duplicates is the deliberate `#queue-dedup-destroys-intentional-duplicates` invariant). So when the backlog→queue mirror / CRDT replay re-emitted a **bare** `[#id]` reference head (the pure mirror form, no `do`), nothing collapsed it. New `queue::dedup_bare_id_reference_heads` runs in `run_queue_maintenance` after the node-key dedup: it collapses duplicate **bare** `[#id]` / `#id` reference heads (pin markers stripped) to the first occurrence, while deliberately leaving `do [#id]` **directive** duplicates intact (intentional "run it twice" intent) and never touching free-text heads (incl. a directive citing an id with trailing text like `#id continue the drain`) or multiline blocks. Tests: `dedup_bare_id_reference_heads_collapses_mirror_duplicates`, `dedup_bare_id_reference_heads_noop_without_bare_duplicates`; the `preflight_preserves_intentional_duplicate_tracked_queue_prompt` invariant still green; full queue (481) + maintenance (67) suites + `make check` green. Backlog: `#sqedit-race` (the queue-edit-race hazard this is one facet of).

- **`queue prune-noise` now clears multiline/fenced pasted-evidence heads, not just bulleted noise (`#qnoise-multiline-strike`).** Operator-reported (sampleorders/agent-doc-bugs2): the queue kept showing duplicates of already-completed prompts that "no number of drains" cleared — "only a file reload in IDEA clears it." Root cause: `queue prune-noise` enumerated heads via the `markdown_ast` `item_nodes` overlay, which recognizes ONLY bulleted (`- …`) lines and skips fenced code, so operator-pasted `:round_pushpin:` console dumps — surfaced by `queue::parse` as multiline `---`-fenced `Prompt` heads or, for a bare ```` ``` ```` console paste, as a run of preserved `Freeform` lines — were invisible to the strike path and accumulated on disk forever (the editor convergence then faithfully re-pushed them). Fix: (1) new `queue::parse_spans` is the single byte-range-aware source of queue-head segmentation, and `prune_noise_queue_heads` excises multiline noise `Prompt` blocks AND pasted-evidence `Freeform` lines (new `queue::is_noise_freeform_line`, which preserves `---`/`~~~` separators and `re [#id]` references) by exact range, alongside the existing bulleted node-key strike; (2) the drainability classifier `is_drainable_queue_head_with_context` now demotes any **multi-line** head text (a console dump / `---`-wrapped multi-bullet paste) to noise — even when a line carries a stray `[#id]` (the `#5eq8`-in-a-console-dump false positive) — so the drain, the `queue_stale_noise_lines` counter, and `queue prune-noise` agree; a single-line `do [#id]` directive that merely happens to be `---`-wrapped stays drainable and is preserved. Validated against the live flooded doc: 61 noise entries excised (0 ``` fences / `:round_pushpin:` / `agent:boundary` left) with every id-backed directive — including the `---`-wrapped `#tsiftmdcrash` — preserved. Tests: `prune_noise_excises_multiline_fenced_paste_blocks_under_a_preset`; existing prune/queue suite (479) green. `make check` green. Backlog: `#prunenoise-live`.

- **Post-commit HEAD repair is now ack-gated under a live editor, and paused-queue preflight output names the reason (`#pcwcfailfix` / `#qpausemix`).** The `#pcwcdiskfree` listener-active path no longer claims editor-IPC reconciliation before `refresh_content` actually acks. If the editor refresh no-acks/errors, post-commit cleanup falls back to the authoritative `HEAD` disk write and logs `transport=disk_after_failed_editor_refresh`, preventing the corrupted working tree from re-seeding the next `live_prompt_drift` cycle. Separately, controller-paused queues now surface `queue_pause_reason` and pause-aware `queue_continuation_guidance`, so `queue_paused: true` beside `queue_continuation_required: true` is explicitly documented as "unattended supervisor auto-injection paused; attended loop still drains." Coverage: `postcommit_worktree_auto_reconcile_writes_disk_when_editor_refresh_fails`, `postcommit_worktree_auto_reconcile_skips_disk_write_with_active_listener`, `continuation_guidance_explains_controller_pause_reason`, and `run_queue_maintenance_controller_pause_surfaces_flag_without_stalling_continuation`. SPEC/README updated.

- **Done-id collection ignores prose citations (`#donemirrorreap`).** The preflight already-done-mirror reap was removing a gated `[/] [#fullboundary]` review item because `#fullboundary` is *cited in prose* inside the `#ftstrike` `agent:done` entry ("behind do `[#fullboundary]`"). `collect_agent_done_ids_with_root` scanned the whole done-component/archive text via `extract_pending_ids_from_text`, harvesting every bracketed id anywhere. New `agent-doc-core` `extract_done_item_own_ids` collects only each list-item's FIRST `[#id]` (its own identity, skipping checkbox markers and prose/continuation lines); done-id collection now uses it for both the inline `agent:done` component and the external `archive=` file. A `[#id]` cited in an item's description no longer marks that id done. Tests: `extract_done_item_own_ids_ignores_prose_citations`, `..._handles_checkbox_and_skips_prose_lines`; existing mirror-reap tests still green. `make check` green. SPEC updated.

- **Free-text queue heads are struck when answered, regardless of position (`#ftstrike`).** Operator-reported: "my free-text queue items are not immediately struck as if they are addressed." The leading-head consume only strikes a contiguous leading run and stops at an id-backed head, so a free-text report sitting behind an unfinished `do [#id]` head (e.g. behind `do [#fullboundary]`) was never struck even after the response addressed it. New closeout pass `strike_answered_free_text_queue_heads` (write.rs Phase 3c, after the leading-head consume): strikes every non-struck free-text head whose text the committed response answers, matched conservatively via `free_text_head_answered_by_response` — the head text (priority markers stripped, normalized to lowercase alphanumeric words, ≥4 significant words) must appear inside the response's `>` quoted-prompt blockquote region. A head merely mentioned in prose is NOT struck, so an unaddressed operator report is never silently dropped. Runs independent of the leading-head `queue_consumption_allowed` decision, strikes document + snapshot in sync (`consume_queue_nodes_by_key`), best-effort. Mirrors `strike_done_queue_head_prompts` for id-heads. 10 unit/adversarial tests (only-mentioned head not struck; short head not matched; head behind an id head selected; idempotent re-strike). `make check` green. SPEC updated. Plan: `tasks/agent-doc/plan-freetext-queue-strike-on-address.md`.

- **Convergence-gated inter-queue-item boundary Phase 1 — decision core + loud force-disk playback (`#fullboundary`).** Foundation for serializing the queue so item N+1 does not dispatch until item N proves a quiescent close (the root fix for the `content_ours` / `live_prompt_drift` / `postcommit_worktree_check match=false` / `inflight=5` `send_failed` drift family — the drain lease `#kp5z` serializes dispatch ownership but NOT editor convergence). New pure `convergence_gate` module: `ConvergenceFacts` (committed, editor_converged, inflight==0, actor_idle, elapsed/timeout) + `convergence_gate_decision` → `Dispatch` / `Defer { unmet }` / `ForceDiskFallback { unmet }` (no I/O, fully unit-tested). New `convergence_playback` module: `ConvergencePlayback` artifact written to `.agent-doc/playback/<doc-hash>/<cycle-id>.json` (ordered IPC attempt sequence + inflight, snapshot/baseline/HEAD hashes, candidate vs content_ours lengths/hashes, cycle/run/actor/supervisor identity, closeout state-machine transitions) + an ERROR-level `convergence_gate_force_disk_fallback severity=error … playback=<path>` ops-log line via `record_force_disk_fallback`. 16 unit tests; `make check` green. **Phase 2 (the remaining live-supervisor-critical wiring — call the gate inside the supervisor idle-queue-watch / drain inter-item dispatch path, and trigger the real `--force-disk` write + playback on a bounded timeout) needs a focused clean cycle with `make check` + `make tmux-ci` across two live panes**, which cannot run from the live driving session. SPEC updated. Plan: `tasks/agent-doc/plan-fullboundary-convergence-gate.md`.

- **Restart-agent Phase 1a/1b — frontmatter harness changes restart at the boundary (`#agentreloadrestart`).** Changing `agent:` in frontmatter (e.g. claude→opencode) is default-on restartable through the `agent_doc_agent_change_restart` knob (env `AGENT_DOC_AGENT_CHANGE_RESTART` > frontmatter > project config > default ON; resolver `resolve_agent_change_restart` / `agent_change_restart_enabled`). The supervisor idle-queue watch re-resolves the harness from current frontmatter each tick, logs `harness_change_detected`, gates with `agent_restart_boundary_gate`, requests a fresh restart only at a quiet dispatch-ready prompt, and the supervisor restart loop re-derives the launch spec before spawning the new harness with `agent_restart_performed`. Route/startup now refuses to cold-replace a healthy live authoritative actor solely because the document's `agent:` changed, logging `route_authoritative_actor_harness_mismatch_deferred` and deferring to the boundary restart path; stale replacement remains allowed for unhealthy supervisors or closed actors. Tests: `agent_change_restart_decision_policy`, `resolve_agent_change_restart_precedence`, `mismatched_authoritative_actor_can_be_replaced_only_when_not_live_authority`. Plan: `tasks/agent-doc/plan-restart-agent.md`.

- **An explicit operator `Run Agent Doc` now starts even on a paused queue (`#qpauserun`).** Operator-reported: JB `Run Agent Doc` "did not start. It should have started" — the controller dispatch RPC blocked it with `failed_stage=queue_paused`. A `paused` queue control governs *auto-draining the queue*, not whether the operator can run a cycle, so an explicit operator reopen must not be blocked by it (same split as `#qpausego`: a pause stops the unattended injector, not the attended action). The dispatch RPC now admits a dispatch whose `command_kind` is an explicit operator reopen (`managed_reopen` / `dispatch_only_reopen`, classified by `dispatch_command_kind_is_operator_reopen`) past a deliberate operator/admin pause — one-shot: the pause row stays, so unattended callers (`idle_queue_continuation` / `/loop`) remain blocked until `admin queue resume`. EXCEPTION: a stale-supervisor churn-stop pause (`#jbrestale`) still blocks every caller, so the route path restarts the stale supervisor and re-dispatches once instead of admitting a reopen against a stale supervisor. Coverage: `dispatch_operator_reopen_bypasses_paused_queue` (+ existing pause/marker tests updated to use an auto command_kind for the pause-block assertions).

- **Operator queue adds now survive a supervisor/pane crash+restart (`#qdurcrash`).** An operator adds an `agent:queue` item, the turn starts, the supervisor + tmux pane crash and restart, and the add was GONE — it lived only in the editor buffer / in-memory CRDT and the reloaded snapshot predated it. New crash-durable journal (`queue_journal.rs`, `.agent-doc/queue-journal/<doc-hash>.jsonl`, append-only + fsync): `record` (preflight queue maintenance) durably captures every operator queue prompt the binary observes; `replay_missing` + `merge_missing_into_content` (supervisor startup, `run_with_reap_policy`) re-insert journaled prompts absent from the reloaded document so the crash+restart replays the pending edit instead of dropping it; `clear` (on every `commit_success`) empties the journal once the queue state is durable in the snapshot, bounding the journal to operator additions observed since the last commit. **Additive + conservative:** it only ever re-adds missing prompts and never removes anything, so it does NOT re-wire the post-commit worktree reconcile that the `#fintol2`/`#pcwc` carry-forward invariant guards (full `make check` green, including those tests). A struck/consumed prompt is treated as present and never resurrected. Known gap (plugin-side, out of scope here): an add lost to a crash *before* any cycle observes it (pure editor-buffer, never flushed to disk/binary) cannot be journaled by the binary — that needs a plugin buffer-flush-on-edit. Coverage: `record_then_replay_recovers_a_lost_queue_add`, `replay_does_not_resurrect_a_consumed_item`, `record_is_idempotent_and_clear_empties_the_journal`, `merge_is_a_noop_without_a_queue_component`, `absent_journal_replays_nothing`. Plan: `tasks/agent-doc/plan-qdurcrash-queue-edit-crash-durability.md`.

- **Accepted `admin queue pause` now suppresses the unattended supervisor auto-injection on a `go`-mode queue, without stalling the attended in-session loop (`#qpausego`).** An accepted `agent-doc admin queue pause <FILE>` records a durable controller `queue_controls` row that the controller *dispatch* RPC already honored (`failed_stage=queue_paused`), but the supervisor idle-queue watch injects `agent-doc <FILE>` triggers straight into the pane — bypassing the dispatch RPC — so a `go`-mode auto-queue kept re-dispatching after an accepted pause (the unattended flood). The idle-watch now consults the new best-effort, read-only `queue_continuation::document_queue_controller_paused(file)` (resolves project root + canonical document id, reads the effective `queue_controls` state from `.agent-doc/state.db`; returns `false` — never paused — when no control-plane DB exists or a read errors, logging the error to stderr) and defers its drain (`queue_dispatch_skipped ... reason=queue_control_paused`) while the pause is active. `preflight` surfaces a new `queue_paused: bool` for visibility. **The pause deliberately does NOT drop `queue_continuation_required` / `queue_drainable_head_count` and does NOT short-circuit `queue_continuation::detect`:** the attended in-session `/loop` is the legitimate single-owner drain and keeps working real queue backlog (stalling it on a pause strands genuine drainable items — `queue: stop` frontmatter / `--- stop` fences are the in-session stop control). `admin queue resume` (state `resumed`) clears the flag; `drain` (`draining`) is not `paused`. Coverage: `document_queue_controller_paused_false_without_state_db`, `document_queue_controller_paused_reflects_paused_then_resumed`, `detect_still_continues_when_controller_paused`, `run_queue_maintenance_controller_pause_surfaces_flag_without_stalling_continuation`. Backlog: `#qpausego`.

- **Route no longer consumes/loses an uncommitted operator queue head on JB `Run Agent Doc` (`#qdispatchloss`).** Route selects an inactive `agent:queue` head from the live on-disk document, but the JetBrains/VS Code plugin can sync an *uncommitted* operator queue edit to disk before it reaches a git-committed snapshot. Dispatching that head moved a possibly half-typed line into the agent prompt and then lost it — the consume never landed in a committed snapshot, so the item disappeared and the turn stalled uncommitted (the no-crash sibling of `#qdurcrash`). `inactive_route_queue_head_in_content` now proves the candidate head is backed by the committed snapshot (`snapshot::load`) before surfacing it: a head absent from a present committed queue — or any head when the committed snapshot has no queue component — fails closed (returns `None`, logs `route_dispatch_uncommitted_head ... reason=head_not_in_committed_snapshot decision=defer`), so the activate/dispatch path no-ops and the operator's edit survives for the next cycle, which commits the queue edit first and dispatches it from the committed snapshot. Conservative by design: a missing/unreadable/unparseable snapshot allows the head (bootstrap escape hatch). Applies only to the inactive-activation path; active-queue continuation heads (`queue_active: true`) flow through `queue_continuation::live_continuation_head` and are unaffected. Coverage: `route_defers_uncommitted_queue_head_not_in_committed_snapshot`, `route_dispatches_committed_queue_head`, `route_queue_head_unbacked_when_committed_snapshot_has_no_queue`, `route_queue_head_backed_allows_when_no_committed_snapshot`. FlowCore `route.rs` `reason=` budget 8→9. Plan: `tasks/agent-doc/plan-uncommitted-queue-item-dispatch-loss.md`.

- **`#smsim` (semantic_merge Phase 5) — deterministic SimWorld coverage of the operator↔agent concurrent-edit matrix; all five semmerge phases now complete.** New `SimWorld::converge_semantic_merge` runs the production `semantic_merge_scoped` (the same `exchange`-active scoping the real `#smconv` `try_semantic_merge_convergence` applies) and four `semmerge_sim_*` scenarios + four coverage counters lock the merge/IPC data-loss family: node-disjoint auto-merge (agent strike + concurrent operator add both survive, no ack), same-node operator-wins + ack inside the active area, the identical conflict OUTSIDE the active area auto-resolving operator-wins with NO ack (`#smturnactive` gating), and operator-deleted-an-agent-edited-node keeping the deletion + raising the ack. Test-only. Plan: `tasks/agent-doc/plan-semantic-ast-merge.md`.

- **`#smqstrike` (semantic_merge Phase 3) verified complete + merged-tree coverage added.** The queue-consume strike already routes through the node tree by id across all three write paths — editor-IPC per-node `strike` patches (`build_ipc_node_patches_json`/`queue_consume_node_patches`), disk-path divergence reconcile (`queue_consume_reconciles_diverged_snapshot_instead_of_bailing`, the merged document wins so a drifted operator queue edit never aborts the strike), and the `#smconv` content_ours convergence where `semantic_merge` treats `struck` as a node-disjoint flag-edit. Added `smqstrike_struck_head_survives_concurrent_operator_queue_add` proving a struck head and a concurrent operator queue add both survive the merged tree with no ack. No behavior change (the contract landed incrementally with `#smconv`); test + docs only. Plan: `tasks/agent-doc/plan-semantic-ast-merge.md`.

- **Semantic-merge acks are carried into the next cycle's response as an acknowledgement turn (`#pk3f` / `#semmerge-ack-turn`, Phase 4).** `#smconv` (0.34.7) applies node-disjoint operator↔agent changes and operator-wins on same-node conflicts, then logged `semantic_merge_ack_pending` to `ops.log` for any non-applied agent change — but nothing carried that fact into the *agent's* next turn, so an operator-deleted-agent-edited-node / same-node-override / operator-revived-agent-deleted-node was silently won by the operator with no acknowledgement in the exchange. The convergence path (`write/ipc.rs`) now persists each `requires_ack` `AckRequest` to cycle_state via `record_semantic_merge_acks`; `start_preflight` carries un-surfaced acks forward exactly one cycle (driven by a `surfaced` flag, not a millisecond-collidable cycle-id compare) into the new `CycleState.pending_semantic_merge_acks`, and preflight surfaces them as `semantic_merge_acks` plus a companion `semantic_merge_ack_pending` warning so the existing "surface warnings" skill path drives the agent to acknowledge the non-applied change. The merged document is unchanged — operator content already won — so this only adds the courtesy acknowledgement; no content is created or lost. New stable `AckReason::token()` is the wire format cycle_state persists. Coverage: `ack_reason_tokens_are_stable` (markdown-ast); `record_semantic_merge_acks_tags_current_cycle_and_dedupes`, `start_preflight_carries_prior_cycle_acks_forward_exactly_once`, `semantic_merge_ack_recorded_after_carry_chains_to_next_cycle` (cycle_state); `preflight_output_semantic_merge_acks_roundtrip` (preflight). Plan: `tasks/agent-doc/plan-semantic-ast-merge.md`.

- **`semantic_merge` ack emission is scoped to the turn-active area so an unrelated operator edit no longer raises ack noise (`#msn6` / `#smturnactive`, Phase 6).** `#smconv` (0.34.7) applies node-disjoint changes and operator-wins on any same-node conflict regardless of turn scope, logging a `semantic_merge_ack_pending` for *every* same-node collision — including the common case where the operator edits the queue / a backlog item while the agent writes its response (the firsthand `queue: stop` + cross-head drift). `agent-doc-markdown-ast::semantic_merge` now takes a first-class turn-active node-set: new `ActiveNodes` (whole-component via `active_component` or node-granular via `with_node`) + `semantic_merge_scoped(base, ours, theirs, &active)`. The merged document and per-node outcomes are **identical** to `semantic_merge` (the operator still wins every same-node conflict — no content is ever lost or changed); only `requires_ack` is filtered: a conflict whose node is OUTSIDE the active area auto-resolves silently, while an in-area collision still raises its `AckRequest`. The convergence caller (`write/ipc.rs::try_semantic_merge_convergence`) marks the `exchange` component active (the turn-active area is the exchange tail), so operator drift in queue/backlog/frontmatter that collides with an agent edit no longer emits `semantic_merge_ack_pending` noise; only an exchange-response-area collision does. `semantic_merge` is unchanged (legacy all-active behavior preserved for every other caller). Coverage: `scoped_conflict_outside_active_area_drops_ack_but_keeps_operator_value`, `scoped_conflict_inside_active_area_keeps_ack`, `scoped_active_set_is_node_granular`, `scoped_empty_active_set_drops_all_acks`. Plan: `tasks/agent-doc/plan-semantic-ast-merge.md`.

- **Single live IntelliJ plugin consumer is elected per document so concurrent windows can't race patch application into File Cache Conflicts (`#8bfz` / `#fcconeowner`).** `#fccreap` (0.34.8) reaps *dead*-pid consumer patch files but does nothing about concurrent *live* ones: with N windows open on one workspace, each registers its own consumer id `jetbrains-<pid>-<uuid>` (`TypingTracker.kt`), watches `.agent-doc/patches/`, and applies + `saveDocument`s the same untargeted (broadcast) patch — N live writers racing into the File Cache Conflict / cross-buffer drift family. New `agent-doc-orchestration::plugin_owner` adds a per-document single-owner lease (`.agent-doc/plugin-owner/<hash>.json`, mirrors `drain_owner`) claimed atomically via `create_new` (O_EXCL) so two instances racing for an unowned/stale lease cannot both win. Ownership is sticky while the owner keeps applying (it refreshes the heartbeat on every patch event) and self-healing: a stale heartbeat OR a provably-dead owner pid (`kill(pid,0)`) hands ownership to the next live consumer, and explicit `release_plugin_owner` on dispose hands it over immediately without waiting out the 30s TTL (`AGENT_DOC_PLUGIN_OWNER_TTL_SECS` override). Exposed via FFI `agent_doc_plugin_owner_try_acquire` / `agent_doc_plugin_owner_release`; `PatchWatcher.processPatchFile` gates only **untargeted** (`editor_id == null`) patches behind the lease (editor-targeted patches already have a unique consumer and bypass it), and non-owners leave the patch file for the owner instance to apply + delete. Fail-open by construction: any IO/FFI error (or an older binary missing the symbol) returns "apply", so a single-instance setup is never worse off than before the lease. Coverage: `plugin_owner` unit tests (`first_consumer_wins_second_defers_while_owner_is_live`, `dead_owner_pid_hands_ownership_to_next_consumer`, `stale_heartbeat_hands_ownership_to_next_consumer`, `release_only_removes_own_lease`, `end_to_end_real_paths_elect_single_owner`). Operator-verify: needs a live multi-window IntelliJ test. Plugin 0.2.171.

- **Stale-binary supervisor self-heals from a wedged editor write or a failed re-exec instead of an indefinite wedge (`#supselfheal` Phases 2–5, `#supselfheal-rest`).** Completes the wedge plan on top of the Phase 1 `explicit_admin` override. `supervisor_recycle_action` now takes two more typed evidence inputs: `write_wedged` (Ph2, `#supselfheal-wedgetrigger`) and `reexec_failed` (Ph3, `#supselfheal-reexecescalate`), and gains a new `EscalateKillRelaunch` action. (1) **Wedge trigger:** the write/converge closeout derives a typed `write_wedged` fact from repeated `send_failed`/`no_ack` against a nominally-active JB listener (the `#fcc0e` de-wedge latch) — `write_wedged_from_ipc_failures` + the supervisor-facing `editor_ipc_write_wedged` reader — and logs `write_wedged_supervisor_recycle_requested` instead of looping silent refusals. A wedge against a stale binary overrides the default-OFF opt-out and recycles immediately at the turn boundary (it must never stay `Detect`, and never waits for an idle boundary that may never come). (2) **Re-exec escalation:** when the in-place `execve` recycle cannot start (deleted-inode `ENOENT` from a fresh `make install`, or another syscall error), the policy returns `EscalateKillRelaunch` and the idle watch escalates to a bounded (`MAX_REEXEC_ESCALATIONS`) kill+relaunch of the harness child — reusing the `#supkill-bg` drain-and-relaunch path — instead of looping `continue_current_binary` forever. (3) **Guidance:** the SKILL.md auto-loop note and `runbooks/commit.md` no longer claim a stale supervisor self-heals via drain-lease expiry; they document the wedge-triggered recycle / `admin recycle` / bounded kill+relaunch as the actual non-disruptive recovery for a stale **binary**. Coverage: pure decision-table tests for every new row (`supervisor_recycle_action_write_wedge_overrides_opt_out`, `supervisor_recycle_action_reexec_failure_escalates_to_kill_relaunch`, `reexec_escalation_bound_caps_retries`), converge classifier/reader tests, and SimWorld reproductions of the firsthand session (`wedged_opted_out_supervisor_recycles_on_write_wedge`, `failed_reexec_escalates_to_bounded_kill_relaunch`). Plan: `tasks/agent-doc/plan-stale-supervisor-wedge-no-selfheal.md`.

- **`supervisor_recycle_action` gains an `explicit_admin` override so `agent-doc admin recycle` can recycle a stale-binary supervisor (`#supselfheal` Phase 1 policy core, `#supselfheal-adminrecycle`).** The route-owned supervisor recycle policy (`start/decisions.rs::supervisor_recycle_action`) previously returned `Detect` (surface only, keep running the stale binary) whenever auto-recycle was opted OUT — so an explicit `admin recycle`, the gentle fix the closeout path itself recommends, had no policy path to actually clear a stale supervisor. The predicate now takes `explicit_admin`: an operator/agent `admin recycle` request overrides the default-OFF opt-out and returns `RecycleImmediate` for a stale supervisor at the next turn boundary, while still respecting `turn_boundary` (never drops a live turn) and staying a no-op when the binary is fresh. Exhaustively unit-tested (`supervisor_recycle_action_explicit_admin_overrides_opt_out`). The live `admin recycle` → route-owned-supervisor IPC adapter that flips this input to `true` (a non-disruptive supervisor-directed recycle that does not refuse busy panes) is the queued follow-up `#supselfheal-adminwire`; the idle-watch and SimWorld callers pass `false` until it lands, so current behavior is unchanged.

- **`parse_close_marker` accepts the standard `<!-- /agent:name -->` close-marker spelling so `Component.end_byte` spans the full component (`#gszq`/`#mdastclose`).** In `agent-doc-markdown-ast` `overlay.rs`, `parse_close_marker` only recognized the legacy `<!-- agent:/name -->` (`agent:`-then-slash) form. For the real spelling `<!-- /agent:name -->` (slash-then-`agent:name`) — the spelling every session document and the crate's own test fixture actually use — it returned `None`, so the component never matched its explicit close and was only *implicitly* closed by the next open marker (or EOF). `components()`/`items()` still parsed correctly via implicit-close (item parsing was unaffected), but `Component.end_byte` pointed just past the **open** marker instead of through the close line, so any consumer slicing a component by its `start_byte..end_byte` byte span got a truncated range. `parse_close_marker` now accepts both spellings (`/agent:name` and `agent:/name`); `end_byte` spans the full component. Coverage: `end_byte_spans_full_component_for_both_close_spellings`.

- **Closeout reaps stale dead-PID IntelliJ consumer patch files (`#fccreap`).** The JetBrains plugin registers a per-instance consumer id `jetbrains-<pid>-<uuid>` (`TypingTracker.kt`), and per-instance patch files `<doc_hash>.jetbrains-<pid>-<uuid>.json` land in `.agent-doc/patches/`. When an IntelliJ instance dies/restarts — or multiple windows are open on one workspace — those files **accumulated and were never reaped** (observed: 17+ dead-PID files up to 143 KB that regenerated after manual cleanup), bloating the patches dir and feeding the multi-instance IPC confusion behind the File Cache Conflict / cross-buffer drift family. `fire_post_commit` now best-effort reaps them (next to the existing `reap_local_model_leases` closeout reap): `reap_stale_jetbrains_consumers(project_root)` scans the patches dir, parses the pid from each `jetbrains-<pid>-<uuid>.json`, and removes only files whose pid is provably dead — Unix `kill(pid,0)` where `ESRCH` ⇒ dead/reap and `EPERM` ⇒ alive/keep, never the current pid, never a non-`jetbrains-` file (base `<hash>.json`/`.vscode` variants are skipped), and a no-op on non-Unix. Pure, injectable core (`jetbrains_consumer_pid`, `reap_stale_jetbrains_consumers_with`) so the decision is unit-tested without real processes; closeout never fails because a reap could not run. This is the durable defense against the multi-instance condition (operator-side mitigation is still collapsing to one IntelliJ window). Coverage: `jetbrains_consumer_pid_parses_pid_and_rejects_non_matching`, `reap_removes_only_dead_pid_consumer_files`, `reap_is_noop_on_empty_or_missing_dir`.

- **Live editor-buffer drift now node-merges instead of dropping the agent's response (`#smconv` / `#semmerge` Phase 2).** Root-cause fix for the `live_prompt_drift_after_preflight` → `content_ours`-adoption transition that dropped every agent response (new `### Re:` turn, queue strike, backlog edit) whenever the operator's editor buffer drifted from the agent baseline at closeout — the corruption that forced the manual `git checkout HEAD` / `reset --from-current` recovery dance. `write/ipc.rs::guard_ipc_snapshot_adoption_against_live_prompt_drift` now tries a node-keyed `agent_doc_markdown_ast::semantic_merge::semantic_merge(base, candidate, content_ours)` BEFORE the existing `#fintol2` line-merge / `content_ours` fail-close: when the operator and agent edited disjoint nodes (the common case — operator flips `queue: stop` + edits the queue while the agent strikes a *different* head and appends a `### Re:` turn) it applies BOTH change-sets in one clean commit. A conservative "safely applicable" gate requires the AST to apply (non-empty components on all three sides), a structurally-clean re-parse, and ZERO dropped agent prompt/queue/response content (`dropped_prompt_lines_after_content_ours` / `dropped_queue_prompt_lines_after_content_ours` empty + every new `### Re:` heading preserved) — otherwise it falls through to today's recorded-evidence `content_ours` path unchanged (fail-closed only when the merge can't be represented). Logs `live_prompt_drift_semantic_merged`; `requires_ack` outcomes (same-node operator-wins, operator-deleted-an-agent-node) are applied (operator-wins already encoded in `merged_doc`) and logged `semantic_merge_ack_pending` pending the Phase-4 ack-turn. Coverage: `smconv_merges_heading_prose_response_preserving_both_changesets`, `smconv_disjoint_drift_merges_both_change_sets`, `smconv_same_node_conflict_is_safe`, `smconv_declines_on_structurally_corrupt_ours_falls_through`. FlowCore budget bumped (`ipc.rs` `guard_` 12→16 test-call, `reason=` 17→18).
- **`semantic_merge` models `### Re:` heading-prose exchange turns as append-only nodes (`#semmerge-owner` heading-prose extension).** The shipped Phase-1 `semantic_merge` keyed exchange turns as list-item bullets (`- re [#id]`), but real session docs author turns as `### Re: <topic> — <model>` h3 heading-prose blocks the overlay never modeled as items — so a node-merge silently dropped the agent's response turn (the gap that made `#smconv` decline for every real session). `semantic_merge` now splits the `exchange` component into heading-keyed blocks (key normalized to ignore a trailing `(HEAD)` boundary annotation and `~~`-strike wrappers), and appends any `### Re:` block present in the agent's exchange but absent from the operator's — before the trailing `<!-- agent:boundary -->` marker, exactly one boundary preserved — as `AppliedAgentAdd` outcomes. Append-only by construction (no in-place prose merge). `overlay.rs` is unchanged; the heading split is local to `semantic_merge`. Coverage: `exchange_appends_agent_new_heading_prose_turn`, `exchange_head_marker_does_not_split_turn_identity`, `exchange_boundary_marker_preserved_and_new_turn_before_it`.

- **Shadow-open-backlog guard no longer hard-wedges on `agent:queue` `[#id]` heads (`#qheadsync` orphan-shadow).** `pending::detect_shadow_open_items` excluded tracked-work and `exchange` components from its scan but NOT `agent:queue`, so a queue head referencing an id absent from the live backlog — a reaped id's lingering `[#id]: note` head (e.g. `[#jbacceptwedge]: 0.2.170 installed`), or any `#mirrorall`-mirrored `do [#id]` whose item is reaped — was misclassified as an "open backlog item that exists only outside the live backlog" and made `enforce_no_shadow_open_backlog` (preflight repair step) `bail!` before the done-strike maintenance pass could clear it. That was the orphan-shadow wedge that forced the manual `Edit`+`reset --from-current`+`commit` dance after `--done` this session. Queue `[#id]`/`do [#id]` entries are legitimate references to backlog items, not shadow backlog items hiding in commented-out prose, so the queue component is now excluded from the scan like `exchange`/tracked-work already are (genuine shadow items in HTML-comment/non-component prose are still caught). This matters more post-`#mirrorall`, which deliberately places many `[#id]` heads in the queue. Coverage: `detect_shadow_open_items_ignores_agent_queue_id_heads` (reaped + mirrored queue heads not flagged; a commented-out shadow item still caught); existing shadow tests unchanged. This is the orphan-shadow half of the `#qheadsync` queue-head reconciliation; the answered-free-text auto-strike half remains.

- **Backlog→queue sync mirrors ALL queue-attr backlog items, including `[operator-verify]` (`#mirrorall`).** Operator: "The backlog items are not in the queue right now. They should be in the queue. We should have redundancy so it not only immediately adds the item into the queue once created, but also adds backlog items that were missed in previous turns." Previously `run_queue_maintenance` narrowed the sync source to the drainable subset (`partition_drainable_backlog_ids`), so `[operator-verify]` items were skipped from the queue entirely (the `#goqueuestall`/`#qcontdrain` anti-thrash rule). They are now mirrored into the queue as `do [#id]` heads so the queue is a complete worklist — an operator-verify head surfaces the operator instructions carried in the item text (which `#qheadsync`-era bolding renders as `**Operator action: …**`). Crucially `backlog_ids` is NOT narrowed: `head_is_drainable` still defers operator-verify ids via `deferred_backlog_ids`, so `queue_drainable_head_count` continues to exclude them and the in-session auto-drain loop is NOT re-armed by a mirrored operator-verify head. Because the per-preflight sync is idempotent (existing/struck ids are never re-added) and runs every cycle, it doubles as the **reconciliation sweep** the operator asked for: a backlog item missing from the queue (added before activation, or lost in a prior-turn drift like `#qheadsync`/`#733r` was) is re-mirrored on the next preflight, not just on creation. Phase 2 (required before a mirrored queue is resumed from pause): the supervisor idle-watch (`start/idle_watch.rs`) must apply the same drainability defer so operator-verify-only heads do not re-injection-thrash (`#rz3a`, previously DO-NOT-IMPLEMENT — the mirror-all decision reverses that gate); until it lands the mirrored queue stays operator-paused. Coverage: `run_queue_maintenance_mirrors_operator_verify_into_queue_but_keeps_it_nondrainable` (mirrors `[operator-verify]` into the queue AND asserts `drainable_head_count == 1`); existing `partition_drainable_backlog_ids_skips_only_operator_verify` (the pure partition is unchanged) and the queue-sync suite stay green.

- **`session-check` self-clears the `#queue-user-edit-overwrite` wedge by id (`#qheadsync`).** Operator: "You should automate the wedge clear." Observed on `agent-doc-bugs2.md`: after an IPC `content_ours` merge, `session-check` stayed `INTERRUPTED` indefinitely on dropped queue edits `[#qpauseux]`/`[#docdriftgrace]` even though both were preserved in HEAD as struck `~~do [#id]~~` heads from a prior cycle. No `reset` variant (`--preserve-session`, bare, `--force-disk`) cleared it because the only escape was a manual supervisor restart / patch surgery. Root cause: `check_dropped_queue_prompt_guard` proved preservation only by (a) normalized text identity — which cannot bridge the recorded bare `[#id]` form against HEAD's `do [#id]`/struck spelling — and (b) a consumed-check scoped to *this cycle's* resolved ids, while the strike happened earlier. The guard now also clears when the dropped prompt's `[#id]` is present in the committed/visible `agent:queue` in **any** form, including struck/consumed lines: new `queue_ids_including_struck` strips the strike wrapper before id extraction (`committed_queue_head_ids` deliberately skips struck lines for live-head accounting, so a loss-detection-only variant is required). A struck head visibly reached the document, so its id is preserved, not silently lost — the guard self-clears the stale `dropped_queue_prompts` marker instead of wedging the session. Genuinely lost ids (absent from the committed queue) still fail closed. Coverage: `session_check_clears_dropped_queue_marker_when_id_preserved_in_other_spelling`; existing `session_check_fails_closed_on_dropped_queue_edit` proves a real loss still interrupts. Plan: `tasks/agent-doc/plan-answered-queue-head-autostrike.md`.

- **Post-commit worktree reconcile no longer raw-writes the session doc behind a live editor (`#pcwcdiskfree`).** Operator: ":pushpin: JB `File Cache Conflict` still occurring. Please replace all direct file writes on the hot path." Root cause: `emit_postcommit_worktree_check` ran `std::fs::write(file, &head_doc)` *unconditionally* whenever `postcommit_worktree_lost_committed_content` returned true, then called `send_postcommit_editor_refresh` to push HEAD content through the editor IPC. The disk write was the recurring `File Cache Conflict` source — it fired behind IntelliJ's open buffer on every detected lost-committed-content drift, which during normal go-mode drains was usually just unsaved-editor divergence rather than real corruption. The reconcile is now listener-aware: with a JB (or VS Code) IPC listener active it **skips the disk write entirely** and lets `send_postcommit_editor_refresh` carry HEAD content back through `refresh_content` — the editor buffer becomes authoritative and the disk catches up on the IDE's next save (logged `postcommit_worktree_auto_reconciled ... transport=editor_ipc_skipped_disk_write`). With no listener (headless / CI), it still writes HEAD to disk authoritatively (logged `transport=disk`) so committed content is restored even when no editor is attached. Closes the last normal-operation session-doc disk write on the post-commit hot path; the remaining raw disk writes are the authoritative recovery/scaffold/migration paths that must hit disk even when the editor/IPC path is itself wedged. The `start_fake_listener` test helper now models `refresh_content` correctly (applies the message's `content` field instead of reading from disk), so the live-editor test asserts the editor IPC refresh, not the (now-removed) disk write. Coverage: `postcommit_worktree_auto_reconcile_skips_disk_write_with_active_listener`, `postcommit_worktree_auto_reconcile_writes_disk_without_listener`; existing `postcommit_worktree_auto_reconcile_refreshes_live_editor_buffer` and `postcommit_worktree_check_logs_match_false_for_real_corruption` continue to pass. FlowCore `reason=` token budget bumped 11 → 13 in `tests/test_cli.rs`.

- **Closeout now auto-reaps crashed-session GPU leases (`#kgleasereap`).** `tsift kg extract`'s cooperative GPU lease (`#kgleasewire`) previously needed a manual `tsift local-model lease reap --unload-empty` to reclaim pid-dead holders left by crashed extractor runs and unload the now-unreferenced model. The closeout (post-commit) hook now runs that reap automatically, mirroring the existing `tsift-memory` closeout-capture seam: it spawns `tsift local-model lease reap --unload-empty --json` under the resolved project root only when a `.tsift/gpu-lease.json` registry is present, and every failure mode (no project root, no registry, `tsift` not on PATH, non-zero exit) degrades to a logged stderr warning — closeout never fails because a reap could not run. This is safe during an active cycle because `#kgreflease` only reclaims pid-dead or TTL-expired holders, never a concurrent live extractor's lease. The closeout seam is used instead of the raw idle-tick so the reap stays off the queue-drain hot path. Pure `reap_command_args` builder (default `--unload-empty`, optional `--lease-file`/`--host` overrides) plus an explicit skip-without-project-root path. Coverage: `reap_command_args_defaults_to_unload_empty_without_optional_flags`, `reap_command_args_appends_lease_file_and_host_when_given`, `reap_skips_without_project_root_and_never_spawns`.

- **Remaining session-doc disk-write sites audited against a live editor (`#fccaudit`).** Extends `#fccqueue`: every `std::fs::write`/`atomic_write` call site that targets the session document is audited against an active JB editor IPC listener and either routed through the `#fcc0` converge gate or documented as editor-safe. Two normal-path gaps were routed through `converge_or_disk_write`: the `agent-doc write --status` component replace (`status_set`) and the post-commit ephemeral guard-marker strip (`strip_guard_markers`, removing `<!-- no-pending-capture -->` / `<!-- no-pending-done-guard -->`) — with a listener active they converge through editor IPC (no `File Cache Conflict`), with no listener they fall back to the same byte-identical disk write. Already-safe sites are documented: exchange compaction (`#w42v`), the post-commit boundary reposition (skips the working-tree write while a listener is active), and the `#pcwc` post-commit worktree reconcile (HEAD-authoritative repair of a tree that *lost* committed content, followed by an editor-buffer IPC refresh). Recovery/scaffold/migration writes (`claim` scaffold, `repair` orphan recovery, preflight migration/repair, `session-check` over-application remedy, `reset` resume-clear) stay authoritative must-hit-disk by design — they restore correctness when the editor/IPC path may itself be wedged. `(HEAD)` heading annotations and `agent:boundary` markers are deliberately out of scope (the working tree/editor preserve them). Coverage: `set_writes_status_to_disk_without_listener`; `strip_guard_markers` routes through the existing `converge_or_disk_write` gate tests.
- **Backlog→queue sync honors `not-before=YYYY-MM-DD` scheduling preconditions (`#backlog-not-before`).** Operator: "if items are gated or have preconditions that are not met such as a date in the future, do not add the backlog item into the queue." Gated `[/]` items were already excluded; this adds a date precondition. An open backlog/icebox item carrying a `not-before=YYYY-MM-DD` token is held out of the `queue`-attribute sync while the current UTC date is before that threshold (`pending::active_item_ids`, `active_item_priorities`, and `active_enqueue_item_ids` all exclude it — an explicit `:inbox_tray:`/`/enqueue` marker does not override an unmet date), and becomes eligible on/after the date. It stays a normal open `[ ]` entry the whole time (a soft schedule, not a `[/]` gate, never auto-gated). The token must start at a word boundary and parse as a strict `YYYY-MM-DD`; malformed values are ignored. Day math uses a proleptic-Gregorian `days_from_civil`, so no date crate was added. New: `pending::item_not_before_day` / `item_precondition_unmet` / `today_civil_day`. Coverage: `active_item_ids_holds_future_not_before_items`, `active_enqueue_item_ids_holds_future_not_before_items`, `item_not_before_day_parses_and_validates`, `item_precondition_unmet_compares_against_today`, `run_queue_maintenance_holds_future_not_before_backlog_item_out_of_queue`.
- **Preflight queue maintenance no longer raw-writes the session doc behind a live editor (`#fccqueue`).** Operator reported the IntelliJ `File Cache Conflict` dialog *still* fired during go-mode queue drains. Root cause: `run_queue_maintenance` persisted its mutations (queue body sync, opening-tag activation-token strip, `queue:` frontmatter state) with four unconditional `std::fs::write(file, …)` calls, bypassing the 08b write-authority routing the finalize/response path already uses — so every preflight queue-maintenance cycle touched disk behind the open editor buffer and tripped the conflict dialog. The four sites now route through a single `persist_queue_maintenance_doc` gate: with a JB editor IPC listener active it converges the queue shape through the editor (`converge_live_buffer_queue_shape` → plugin `setText` + `saveDocument`, no external-modification dialog) and skips the disk write, recording `write_authority action=routed surface=queue_maintenance` in `ops.log`; with no listener it writes to disk exactly as before (byte-identical non-IDE behavior). This brings queue maintenance to the same `#fcc0` converge-or-disk discipline the pending/review maintenance sites already use. The private `.agent-doc/` snapshot is still written directly (never open in the IDE). No plugin change required — the convergence patch handler already persists conflict-free. Coverage: `run_queue_maintenance_routes_through_ipc_without_disk_write_when_listener_active`; the existing no-listener `run_queue_maintenance_go_mode_appends_fresh_backlog_into_nondrained_queue` proves the disk-write path is unchanged without a listener.
- **Route ready-prompt barrier now accepts supervisor-proven `idle_pane_reconcile` transitions (`#monster60stimeout`).** A JB `Run Agent Doc` after a session-close/reopen could wait the full 60s route timeout even though the actor was already `ready` with `runtime_state=ready supervisor_health=healthy`, because the edge-triggered pty redraw missed re-emitting a prompt shape `ready_prompt_candidate` recognized and the fallback only matched `prompt_ready`/`dispatch_ready_prompt` reasons. The supervisor's idle-watch records `idle_pane_reconcile` only after `supervisor_pane_has_busy_cue == Some(false)` — direct pane evidence the pane is idle — so the barrier now accepts that current-generation transition as ready proof too, eliminating the 60s stall. Coverage: `transition_proves_ready_accepts_idle_pane_reconcile`, `transition_proves_ready_rejects_*`.
- **Same-cycle `--pending-add` closeout now populates active go-mode backlog queues (`#pendingaddqueuesync`).** `finalize` / `write --commit` apply pending-add mutations after preflight queue maintenance, so a captured follow-up could land in `agent:backlog priority queue` without a matching active queue head until another preflight happened to repair it. Closeout now appends ids recorded in `cycle_state.pending_added_ids` into active go/start queues whose backlog carries a recognized `queue` attribute, after current-head consumption and before commit. The helper is append-only to preserve the current runnable queue order, skips done/already-queued/operator-verify ids, updates the snapshot queue region, and leaves non-go persisted-active queues under the existing amplification guard. Coverage: `closeout_sync_appends_same_cycle_pending_add_in_go_mode`, `closeout_sync_holds_same_cycle_pending_add_without_go_mode`.
- **Managed Claude Code supervisors now keep routine stderr out of the foreground TUI too.** The earlier stderr-bleed fix redirected Codex and OpenCode supervisor diagnostics but left Claude attached to supervisor stderr, so stale-busy reconcile, restart, and hot-reload messages could still paint over Claude Code after `/clear` or recycle. Claude now participates in the same managed-TUI stderr redirection policy as Codex/OpenCode in both `agent-doc start` and `agent-doc run`: routine diagnostics go to `.agent-doc/logs/supervisor-stderr.log` / `run-stderr.log`, with verbose and non-managed stderr behavior unchanged. Coverage: `is_tui_harness`, `run_stderr_redirect_harnesses_include_claude_codex_and_opencode`.
- **Idle-queue Codex trigger dedupe now recognizes relative drafts before resubmitting.** The supervisor already avoided stacking an identical drain payload, but it compared the absolute `agent-doc /abs/.../tasks/sampleorders.md` trigger against the visible composer text literally. If Codex was already showing the equivalent relative draft `agent-doc tasks/sampleorders.md`, the idle watcher appended another trigger on each idle tick while the operator was typing in the queue. The pending-payload check now reuses route's relative/absolute draft equivalence before appending, so it presses the submit key once instead of flooding the composer. Coverage: `supervisor_pending_payload_matches_relative_codex_agent_doc_draft`.
- **Codex footer-only route readiness now accepts the shorter `Context N% use` suffix.** A JetBrains `Run Session Context` reroute after clear could still spend the full startup wait and fail as `latest run is still booting` when Codex rendered only `gpt-5.5 xhigh · ... · Context 0% use` instead of the previously-covered `Context N% used` form. The shared context-status predicate now accepts both suffixes, preserving the existing busy/protected prompt guards. Coverage: `ready_prompt_candidate_accepts_codex_context_use_footer_without_prompt` and `idle_chrome_only_output_accepts_codex_context_use_suffix`.
- **Codex idle-queue handoffs now honor opted-in context-threshold resets for ordinary heads.** The Codex Stop-hook could correctly log `codex_fresh_context_handoff` when `agent_doc_queue_context_reset`/`agent_doc_clear_threshold` required fresh context, but the supervisor receiver had been narrowed to clear only explicit `[clean-session]` heads, so the next ordinary queue head still dispatched into the old pane. The idle-queue watch now reuses the Codex reset reason at the safe idle boundary, sends `/clear`, waits for settle, then drains the ordinary head; route-in-flight, turn-active, pending-clear, and one-clear-per-head gates still apply. Coverage: `codex_opted_in_context_reset_dispatches_for_ordinary_head`.
- **Codex footer-only idle panes now satisfy route startup readiness after busy/protected checks.** JetBrains `Run Agent Doc` could spend the full startup wait on Codex panes whose bottom line was only `gpt-... · ... · Context N% used`, then fail as `latest run is still booting` even though there was no active-turn cue or drafted input. Route readiness now accepts bottom Codex idle status chrome the same way the status/clear path already did, while still rejecting background-terminal busy cues and hook-review/protected prompt states. Coverage: `ready_prompt_candidate_accepts_codex_footer_without_prompt` and `ready_prompt_candidate_rejects_codex_busy_footer_without_prompt`.
- **JetBrains Run Agent Doc now submits an existing relative-path draft instead of appending a duplicate absolute trigger.** The direct-pane pre-submit guard now treats a visible `agent-doc <relative-path>` draft as equivalent to the routed absolute-path trigger when the absolute target ends with the relative path, so a Codex pane already showing `agent-doc tasks/sampleorders.md` receives the submit key instead of `agent-doc /abs/.../tasks/sampleorders.md` being appended. The stale-scrollback guard still requires no later idle prompt. Coverage: `direct_pane_existing_draft_detection_matches_relative_codex_path`.
- **Codex background terminals now block route and idle-queue prompt injection (`#codexbgbusy`).** Codex can show an input prompt while a background terminal is still running; typing there queues a message instead of dispatching it. `HarnessConfig::dispatch_blocker_reason` now treats `Waiting for background terminal (... esc to interrupt)` as `active codex turn` evidence, so route readiness and supervisor idle-queue drains skip injection until the background task finishes. Coverage: `has_busy_cue_detects_codex_background_terminal_with_idle_prompt`.
- **Managed Codex supervisors now keep routine stderr out of the foreground TUI (`#codexstderrtui`).** Codex is now classified with OpenCode as a TUI harness for `agent-doc start`, so supervisor `eprintln!` diagnostics are redirected to `.agent-doc/logs/supervisor-stderr.log` instead of printing over the Codex screen after `/clear`, restart, stale-busy reconcile, or stale-binary hot-reload. Coverage: `is_tui_harness`.
- **IPC live-prompt drift no longer treats stale queue omissions as authoritative deletions (`#qdelipc`).** When socket ACK content, file-IPC ACK content, or file-read fallback content diverges after preflight, baseline `agent:queue` prompts missing from that candidate are now preserved in the agent-owned `content_ours` snapshot instead of being removed as if the user deleted them. The live-prompt-drift branch logs `queue_live_deletion_ignored ... reason=unproven_ipc_candidate_queue_deletion`, while the disjoint outside-edit tolerance refuses to forward-merge candidates that drop baseline queue prompts. Normal closeout queue consumption and done-id handling remain the deletion authority. Coverage: `ipc_live_prompt_drift_content_ours_ignores_unproven_live_queue_deletions`, `preserve_content_ours_over_live_queue_deletions_keeps_baseline_prompts`, and `fintol_queue_deletion_is_not_forward_merged_with_outside_edit`.
- **VS Code Run/Clear actions now match the JetBrains per-document command contract.** `Run Agent Doc` clicks dedupe behind the first alive plugin-spawned route process, `Clear Session Context` cancels a still-dispatching route process before invoking `agent-doc session clear <FILE>`, and `Run Agent Doc` clicks during an active clear queue until the clear and any selected clear-refusal recovery action finish. Added `editors/vscode/SPEC.md`, refreshed the VS Code README/session-command docs, and bumped the local VS Code package to `0.2.28`. Coverage: `editorCommandState.test.ts` plus the existing VS Code session UI command/refusal tests.
- **JetBrains Clear Session Context can preempt a stalled Run Agent Doc dispatch.** A Clear click while a plugin-spawned Run route process is still dispatching now cancels that route handle and proceeds through the shared `agent-doc session clear <FILE>` path instead of showing "blocked while Run Agent Doc is dispatching." Repeated Run clicks still dedupe behind the first alive route, and Run clicked during an active clear still queues until synchronous clear completion. Coverage: `normal clear preempts active run dispatch`, `run completion after preempting clear is ignored`, and `canceling active route removes and cancels current run`.
- **Active editor convergence failures no longer fall back to external file edits (`#fcc0-no-external-write`).** The shared editor-convergence gates now allow direct disk fallback only when no JetBrains/VS Code IPC listener is running. If a listener is active but component convergence has no delta, no ack-content proof, an ack mismatch, no terminal ack, or a send failure, the write logs `<source>_writeback ... transport=blocked reason=... action=refuse_external_disk_write` and fails closed instead of writing behind the editor and triggering File Cache Conflict dialogs. The live-prompt auto-recovery wedge also refuses its adopted-snapshot disk write under an active listener, logging `[jbstalecache] auto_recovery_disk_write_blocked ... reason=editor_ipc_unconfirmed`; no-listener recovery still writes as before. Coverage: `converge_document_or_disk_blocks_disk_fallback_with_active_listener_without_ack_content`, `converge_or_disk_write_blocks_plain_disk_fallback_with_active_listener_without_ack_content`, and `try_auto_recover_live_prompt_drift_blocks_disk_fallback_with_active_listener_without_ack_content`.
- **JetBrains Run Agent Doc polls visible drafts with bounded Enter retries.** Direct-pane route submit now keeps pressing the harness submit key up to three times while the exact routed trigger remains visibly drafted, stopping as soon as the trigger disappears or acceptance is observed. Each retry logs `route_submit_resubmit ... action=submit_key key=Enter result=... attempt=N`, so Codex non-submit reports can distinguish a missed first Enter from a trigger that stayed stuck through the bounded retry loop. Coverage: direct-pane retry-bound tests and updated proof-line assertions.
- **JB Clear Session Context no longer fails when only a stale actor pane can be captured.** Supervisor IPC `/clear` proof now verifies and retries Enter only when the live supervisor reports its actor pane; when the supervisor accepts the command but does not expose a pane id, `agent-doc` logs `session_clear_submit_verification_skipped ... reason=no_supervisor_actor_pane` instead of treating a default-tmux capture failure as proof that `/clear` was not submitted.
- **JetBrains Run Agent Doc no longer accepts a first empty Codex capture as submit proof.** Direct-pane route submit now requires empty input to remain stable before treating the prompt as accepted, so a delayed Codex composer draft can still become visible and receive the shared `Enter` submit-key retry. If Codex later reaches accepted-without-dispatch-start proof and the same routed prompt is visibly drafted, route sends one late `Enter` retry, logs `route_submit_late_resubmit ... cause=dispatch_start_unproven_prompt_visible`, and rechecks dispatch-start proof. Coverage: direct-pane acceptance state tests plus existing routed submit/resubmit proof tests.
- **JetBrains Run Agent Doc route attempts now have end-to-end diagnostic correlation (`#jbrouteattemptid`).** The JetBrains plugin passes its durable Run Agent Doc attempt id into `agent-doc route`, and the binary stamps that id on tmux input events, route submit observations/issues, route latency lines, route pane snapshots, and bounded Enter re-submit proof lines. This keeps the existing tmux-router/session reconciliation layer but makes a live "text typed but Enter not submitted" repro traceable from the click ledger in `.agent-doc/state/editor-route-attempts/` to the exact `tmux_text_enter` / `key=Enter` proof in `ops.log`. Coverage: JetBrains route ledger tests and input-diagnostic attempt-id formatting.
- **Active editor IPC failures no longer fall back to direct document writes.** CRDT stream/finalize, explicit IPC, streaming flush, sidecar-normalization, and IPC dedupe repair paths now fail closed when active socket/file IPC times out, lacks response proof, or cannot prove editor-owned visible repair: the pending response is retained, unconsumed file-IPC patches stay queued for the editor, diagnostics log `recovery=retry_without_disk_write`, and queue consumption/snapshot/CRDT/commit/direct document repair are skipped until a retry succeeds through the editor path. Explicit `--force-disk` remains the operator-controlled direct-write escape hatch.
- **`--force-disk` closeout now covers queue consumption, not just response placement.** A wedged active-listener recovery can place the response body directly but still fail if the follow-up queue-consume phase silently re-enters editor convergence. The strict write/finalize closeout now threads `force_disk` through queue consumption and done-id marking as one controlled escape hatch, so a forced recovery can reach a coherent write+consume+commit boundary. Coverage: `force_disk_closeout_queue_consume_bypasses_active_listener`.
- **Idle queue restart drains ordinary JetBrains heads as triggers unless an opted-in context reset is required.** Added a sampleorders regression proving a Codex idle-queue restart drain for an ordinary `Run Agent Doc` head sends `agent-doc <FILE>` with no `/clear` when no reset reason is active; explicit operator clears, `[clean-session]` heads, and opted-in context-threshold/accretion resets can still interleave a harness context reset.
- **JetBrains Run Agent Doc dispatch-only delivery is harness-neutral (`#jbrunparity`).** Dispatch-only Claude Code, Codex, and OpenCode reroutes now use one success policy: accepted shared tmux text+`Enter` delivery is successful and logs `proof=accepted proof_scope=accepted_only` when no stronger proof appears. Codex hook proof and OpenCode pane-state proof still upgrade telemetry to `dispatch_start`, but Codex hook tracking no longer suppresses accepted-delivery progress or gates success differently from the other harnesses. Coverage: `dispatch_only_progress_policy_is_harness_neutral`, `dispatch_only_submit_proof_gate_accepts_enter_delivery_for_all_harnesses`, `dispatch_only_proof_policy_accepts_enter_delivery_for_all_harnesses`, and the ignored live-tmux parity regression.
- **JetBrains Run Agent Doc now trusts the shared Enter submit path and avoids ungated threshold-clear churn (`#jbsimpleroute`).** Dispatch-only reroutes no longer fail the IDE action after tmux accepts the shared text+`Enter` submit merely because dispatch-start proof did not arrive inside the proof window; they complete as `proof=accepted proof_scope=accepted_only`, matching the simpler cross-harness delivery contract. The supervisor idle-queue watch does not clear ordinary queue heads unless the project/document opted into queue context reset and a reset reason is active; explicit operator clears and explicit `[clean-session]` heads remain clear sources. Coverage: dispatch-only accepted-delivery policy tests, `clean_session_head_forces_context_reset_policy`, and focused idle-queue context-reset tests.
- **Stale-supervisor freshness warnings no longer recommend destructive force commands.** The route-owned host supervisor stale-binary warning now points routine refreshes at `agent-doc admin recycle` (idle-boundary recycle) or normal `agent-doc session restart-supervisor <FILE>` (busy panes refuse), and explicitly keeps force/discard recovery scoped to genuinely wedged owners. Compaction stale-supervisor CRDT-interleaving messages were aligned with the same non-destructive guidance, preventing agents from copying stale-binary warnings into an active Codex turn and interrupting it. Coverage: `host_supervisor_stale_warning_message_uses_non_destructive_refresh` plus the controller stale-warning assertions.
- **Closed same-supervisor sessions now replace stale actor state immediately.** When a Codex/OpenCode child is closed and the same route-owned supervisor reports a newer session/generation for the same pane, controller register/heartbeat accepts the newer actor instead of rejecting it as stale. Stale `queue_paused` controls from `stale route-owned supervisor (pid N)` churn are cleared once a newer actor transition or a different live supervisor PID proves the pause was superseded, so JetBrains Run Agent Doc no longer stays blocked behind an already-answered archived head after reopening a session in the same supervisor. Coverage: `controller_supervisor_heartbeat_replaces_closed_same_supervisor_session`, `dispatch_clears_stale_supervisor_pause_that_predates_current_actor`, and the stale-supervisor queue-pause classifier tests.
- **Tmux submit parity is now documented and tested as text plus named `Enter` (`#jbtmuxenter`).** The shared submit profile test now asserts Codex, Claude, OpenCode, and unknown harnesses all build the same tmux command shape: `send-keys -t <pane> <text> Enter`, with trailing `\r`/`\n` stripped from the text. Specs/README now state that tmux paths must never use literal CR/LF submit bytes; the raw child-PTY fallback remains explicitly separate and its diagnostics were renamed to `raw_pty_*_enter_byte`.
- **Response recovery no longer turns assistant proof tails into user prompts (`#resprectail`).** Template repair now strips leaked `❯ ` prompt markers from assistant-owned proof/list lines anywhere inside a `### Re:` response block, including no-pending already-applied recovery, while preserving prompt-like quoted prose. Coverage: `strip_prompt_prefix_from_response_body_first_lines_strips_late_proof_lines` and `repair_without_pending_strips_response_body_prompt_prefixes`.
- **JetBrains Run/Clear actions now share a per-document state machine (`#jbrunclearstate`).** The plugin no longer cancels an alive `Run Agent Doc` route process on a repeated click; the first route process owns the submit/proof wait and later clicks are deduped with a durable `route_already_in_flight` / `route_process_already_in_flight` attempt stage. A Run click during an already-running normal clear queues the latest Run intent until the clear completes synchronously. A binary-deferred clear does not release that queued Run immediately. Coverage: `EditorCommandStateMachineTest` and `starting a route while one is alive keeps the first submitter`.
- **Supervisor context clears now survive JetBrains/Codex restart races (`#jbclearrestart`).** Idle-queue owned `/clear` submits now write a short-lived per-document `context-clear-in-flight` marker. A recycled supervisor treats that marker as authoritative, blocks drains until the clear has settled, and sends one shared Enter-profile submit key when the clear command is still visible in the composer. The marker is cleared after the same `CLEAR_COOLDOWN_RESUME_IDLE_TICKS` fresh-idle debounce used by the in-memory gate, so repeated `Run Agent Doc` restarts no longer stack `/clear` or strand a drafted clear after the watcher loses local state. Coverage: `context_clear_marker_is_active_until_cleared`, `context_clear_marker_ignores_stale_payloads`, `context_clear_marker_resubmits_visible_pending_clear_once`, and `context_clear_marker_blocks_until_settled_idle_prompt`.
- **JB `Run Agent Doc` now fences route submit from idle-queue `/clear` injection (`#jbrouteinflight`).** Route dispatch writes a short-lived per-document `route-in-flight` marker while the editor-triggered `agent-doc <FILE>` submit is awaiting acceptance/proof. The supervisor idle-queue watcher treats that marker as a first-class reset/drain skip reason, logs `idle_queue_watch_skipped ... reason=route_submit_in_flight`, and does not advance clear-settle debounce counters from the pre-submit composer. This prevents the observed `agent-doc <FILE><CR>/clear<CR>` concatenation where the route and idle supervisor wrote into the same Codex prompt window. Coverage: `route_submit_marker_is_active_until_guard_drops`, `route_submit_marker_ignores_stale_payloads`, `idle_queue_context_reset_waits_for_route_submit_to_finish`, `idle_queue_drain_waits_for_route_submit_to_finish`, and focused `route_submit`/`idle_queue` suites.
- **JB `Run Agent Doc` / idle-queue Codex submits use one shared text+Enter tmux operation (`#jbcodexcm`).** `Run Agent Doc`, `/clear`, and idle queue continuation now share `tmux send-keys -t <pane> <text> Enter`, and empty-text re-submit sends only `Enter`. Route, idle-queue, and session-clear one-shot resubmits use the same profile key and log `action=submit_key key=Enter`. The idle context-reset watcher also latches a clear-in-flight across in-place queue head edits, preventing a fresh `/clear` from being sent on every keystroke while an active queue prompt is still being typed. Coverage: `submit_profiles_keep_harness_submit_policy_in_one_place`, `routed_trigger_submit_diagnostic_names_codex_enter_key`, `context_reset_in_flight_dedupes_active_head_edits`, and the Enter-key raw-reader tmux test.
- **The tmux submit profile no longer carries a fake delivery choice.** The follow-up review correctly called out that the one-variant delivery enum made Codex support look more special than it is. `TmuxSubmitProfile` is now a single policy surface: it always emits `tmux_text_enter`, and `Enter` is the diagnostic submit-key label.
- **Workflow invariants now have an autofix planner (`#wfinvautofix`).** Added `agent-doc autofix <FILE>` to consume the doctor report and `workflow-invariant-catalog-v1`, plan invariant-keyed remediations, record `workflow_autofix:<invariant>:<hash>` proof markers in the append-only proof ledger, de-duplicate repeated symptoms by invariant id/fingerprint, and execute only the v1 whitelisted safe repairs under `--apply`. Operator/destructive/manual actions remain gated with exact commands or required proof. Coverage: `cargo test -p agent-doc-orchestration autofix`.
- **Workflow invariants now have a diagnostic doctor command (`#wfinvdoctor`).** Added `agent-doc doctor <FILE>` (alias `diagnose`) to evaluate `workflow-invariant-catalog-v1` against optional preflight/session-check JSON, live session-check inspection, cycle state, ops-log markers, controller freshness, git/snapshot state, parent gitlink drift, and editor sidecars. Each invariant reports `ok`, `recoverable`, `operator`, or `blocked` with exact repair commands or operator actions; missing required evidence is blocked with the command to gather it. Coverage: `cargo test -p agent-doc-orchestration doctor`.
- **Workflow invariants now have a machine-readable catalog (`#wfinvcatalog`).** Added `agent_doc_workflow::invariants` with stable ids, fact sources, ok predicates, disproof markers, severity, safe remediation, operator-gated remediation, and SimWorld/regression coverage for queue continuation, stale supervisor, closeout commit, editor convergence, generation redirect, and parent gitlink invariants. The catalog serializes as `workflow-invariant-catalog-v1` so doctor/autofix work can evaluate data instead of scraping prose.
- **Route/write UI outcomes now use a typed vocabulary (`#archuistates`).** Added the `ui-outcome-v1` user-facing outcome contract with stable tokens for `queued_behind_owner`, `recovered_and_retried`, `deferred_for_operator_proof`, `no_drainable_work`, `real_component_conflict`, and `blocked_with_exact_unblocker`. Route, session-check, controller dispatch-blocked proof payloads, and JetBrains route/write conflict surfaces now emit these fields while preserving legacy prose for compatibility.
- **Closeout recovery transition table coverage is explicit (`#smtransitiontests`).** `CloseoutRecoveryState::ALL` now drives pure decision-table tests for every recovery state, prompt-context priority, and stale-capture supersession proof. A proptest integration guard checks the same policy boundary across generated state/input combinations, and SimWorld now has an umbrella scenario covering queue edits during write fragmentation, stale compaction/full-content sources, stale/sidecar ACK repair, already-applied ACK recovery, and JB `Run Agent Doc` prompt-context queuing during an open closeout.
- **Closeout recovery mutations share one primitive (`#smrecoverymutate`).** `flow::closeout::apply_closeout_recovery_mutation` now owns replay-baseline refresh, sidecar rebuild/reset-from-visible, restore-from-HEAD, and stale-capture retirement mechanics. `capture::validate_replay`, `repair` stale-capture retirement, and metadata recovery now route through that primitive and log `closeout_recovery_mutation ... reason=...`, so queue-only replay, reset-from-visible, and stale-capture retire paths cannot drift in snapshot/CRDT/capture-state side effects.
- **Route consumes typed closeout recovery decisions (`#smrouteconsume`).** Routed/JB pre-dispatch closeout drains now carry `CloseoutRecoveryDecision` through the route boundary instead of surfacing raw capture or snapshot blocker strings. Existing active queue heads wait behind the unresolved closeout with a typed `closeout recovery ...` blocker, prompt-bearing reroutes queue behind the closeout, and terminal failures name the missing proof plus recommended recovery command.
- **Closeout recovery evidence is gathered through one typed API (`#smcloseoutevidence`).** `flow::closeout::gather_closeout_recovery_evidence` now collects the visible markdown hash, snapshot hash, active cycle phase, active capture state, response-body presence or supersession proof, queue-only drift proof, editor live-buffer/IPC degraded state, and controller/supervisor stale-binary warning in one read-only evidence record. `decide_closeout_recovery` now consumes that evidence for stale-capture supersession proof instead of requiring each caller to rediscover it. Coverage: `recovery_evidence_gathers_hash_cycle_capture_and_fresh_editor_state`, `recovery_evidence_proves_queue_only_drift`, and `recovery_evidence_reports_superseded_capture_heading`.
- **Closeout recovery now has a typed action decision boundary (`#smcloseoutdecision`).** `flow::closeout::CloseoutRecoveryDecision` maps the existing recovery classifier into `AlreadyCommitted`, `ReplaySafe`, `RetireStaleCapture`, `ResetSidecarsFromVisible`, `QueuePromptForAfterCloseout`, or `Blocked` outcomes, so route/JB recovery can consume policy-shaped decisions instead of interpreting low-level closeout errors. Route's closeout-block classifier now asks this boundary before queuing an operator prompt behind an unresolved closeout. Coverage: `recovery_decision_maps_states_to_typed_outcomes` plus the route closeout-block decision tests.
- **JB `Run Agent Doc` recovers legacy stale-supervisor queue pauses (`#jbrestale`).** Route now treats a markerless `failed_stage=queue_paused reason=#qchurn ... stale host supervisor pid<N> ...` dispatch error from an old route-owned supervisor the same as the newer `supervisor_restart_redirect` bail: restart the stale supervisor once, lift the pause, and retry instead of surfacing a hard JetBrains error.
- **Tmux submit now has a single shared profile (`#tmuxenter`).** The supervisor, idle queue, and direct-pane submit paths route through one profile-owned submit helper, so a post-restart `/clear` or `agent-doc <FILE>` draft cannot hide a missed submit key behind a successful text write. Current live-pane delivery is the single text+Enter tmux operation; route also preserves redacted pane-output snapshots under `.agent-doc/logs/route-submit/` when an existing draft, missed submit, or accepted-without-proof dispatch needs forensic evidence. Coverage: `submit_profiles_keep_harness_submit_policy_in_one_place`, `pending_payload_enter_resubmit_is_scoped_and_one_shot`, `route_pane_snapshot_preserves_redacted_terminal_capture`.
- **Captured response replay now tolerates queue-only live drift (`#queueeditcap`).** If a turn reaches `response_captured`, the operator edits only `agent:queue`, and the response has not yet crossed write/commit, `validate_replay` no longer deadlocks route/JB Run Agent Doc behind `captured response baseline no longer matches current document`. The replay guard now proves the snapshot still matches the capture and that replacing the live queue body with the snapshot queue body restores the document byte-for-byte, then refreshes only the capture file hash and lets the normal replay path apply the preserved response onto the queue-edited document. Non-queue drift and snapshot drift still fail closed. Coverage: `validate_replay_refreshes_baseline_for_queue_only_drift`.
- **Supervisor auto-install is now scoped to agent-doc dogfood session documents (`#supautoinstall-scope`).** The dogfood crate-root resolver no longer treats every document in a superproject containing `src/agent-doc` as eligible for build/install. Agent-doc sessions under `tasks/agent-doc/`, legacy agent-doc task docs, and docs inside the agent-doc source checkout can still auto-install; sibling project sessions such as `tasks/professional/sampleportal.md` and `tasks/software/lazily-rs.md` now resolve no auto-install crate root even if `AGENT_DOC_SUPERVISOR_AUTO_INSTALL` or config/frontmatter is truthy. Coverage: `dogfood_crate_root_rejects_unrelated_superproject_docs`.
- **Controller/admin status now exposes first-class binary freshness proof (`#freshnessstatus`).** `agent-doc controller status` and `agent-doc admin inspect --json` include a `freshness` object with installed binary identity, installed/running inode comparisons, stale/unknown/fresh classification, and operator guidance; plain `admin inspect` prints a compact `freshness=controller:<state>,supervisor:<state>` summary. Coverage: `controller_process_freshness_classifies_inode_identity`, `controller_status_reports_startup_binary_identity`, and `controller_queue_control_rejects_stale_generation_and_blocks_dispatch_when_paused`.
- **JB `Run Agent Doc` no longer silently retries Codex latest-run boot timeouts.** A dispatch-only `latest run is still booting ... (timed_out)` refusal has already spent the route ready-wait window, so the JetBrains plugin now surfaces the persisted route diagnostic immediately instead of re-running the 60s wait up to four times. Authoritative-actor startup failures remain retryable, and active-turn failures still show the still-running notification.
- **Between-turn supervisor handoffs now de-duplicate repeated `/clear` + `agent-doc <FILE>` requests (`#qdedup`).** The idle supervisor path now has a shared set-based planner for fresh-context handoff commands, emits the requested `between_turn_enqueue deduped=N kept=/clear,/agent-doc result=delivered` proof line when a clear-plus-drain sequence lands, and SimWorld proves repeated handoff requests buffer during an active turn then deliver one normalized command set at the idle boundary. Coverage: `between_turn_enqueue_plan_keeps_one_clear_and_one_trigger`, `between_turn_enqueue_plan_counts_concatenated_trigger_duplicate`, `qdedup_between_turn_enqueue_waits_for_idle_and_dedupes_command_set`.
- **The dogfood supervisor refresh stopgap runbook is retired (`#dfrefresh-retire`).** The stale manual make/install/restart fallback has been removed from the bundled runbooks, skill catalog, and installed harness mirrors now that dogfood supervisor auto-install plus stale-binary recycle cover the bootstrap path. The `tsift-memory 0.1.70` dependency is also resolved from crates.io instead of a sibling checkout so CI can build the updated dependency graph.
- **Major dependency constraints are upgraded through the current API surfaces (`#ar27-majors`).** Updated the held-back agent-doc dependency majors for `instruction-files`, `notify`, `portable-pty`, `pulldown-cmark`, `rusqlite`, `sha2`, `signal-hook`, `similar`, `toml`, `ureq`, `yrs`, and `zip`, with API migrations for `ureq 3` response/body handling, `sha2 0.11` digest formatting, and `portable-pty 0.9`'s `MasterPty::tty_name`. The `rusqlite 0.40` move is kept link-safe by aligning the first-party `tsift-memory` path dependency, and the old `generic-array 0.14.7` chain is removed by moving first-party `tagpath` to `sha2 0.11`. Verification: `cargo check -p agent-doc`, affected-crate cargo tests, and full `make check`.
- **JB Clear Session Context now retries drafted Codex/Claude `/clear` commands once (`#jbclearctxsubmit`).** The direct-pane clear path now polls the live pane after sending the harness clear command; if Codex or Claude still show the command in the active composer, it logs `session_clear_submit_observation ... issue=prompt_not_submitted`, sends one bare profile submit key, and records `session_clear_submit_resubmit ... result=accepted|still_visible|capture_failed`. Stale scrollback is guarded by treating a later prompt-prefix line as a newer composer, so an old `/clear` transcript does not trigger a stray submit key. Coverage: `clear_command_visible_detects_codex_active_composer`, `clear_command_visible_treats_empty_composer_as_submitted`, `clear_command_visible_ignores_stale_scrollback_before_idle_prompt`, `clear_direct_submit_retry_is_scoped_to_visible_codex_or_claude_drafts`, `clear_submit_proof_lines_report_prompt_issue_and_retry_outcome`.
- **MCP finalize close-after-capture recovery now has strict CLI regression coverage (`#codexmcpfinalize`).** The MCP finalize test suite now seeds the exact interrupted state left when `agent_doc_finalize` durably captures a response and the transport closes before the write/commit boundary. The next real `agent-doc preflight` must replay the capture, create exactly one closeout commit, clear the pending response, and leave `session-check` green without a manual reset. Coverage: `mcp_finalize_close_after_capture_recovers_on_next_preflight_once`.
- **Queue head removal diagnostics now name the removed head and proof source (`#samplequeuepreserve`).** The session-check queue provenance guards now log explicit proof for removed id-backed heads (`backlog_resolved_or_removed`, `cycle_lifecycle_outcome`, or `current_directive_target`) and removed free-text heads answered by committed response history. This keeps active-turn queue convergence auditable: preserved additions remain queued, authorized deletions name their source, and missing proof still fails closed. Coverage: `queue_head_removal_guard_logs_proof_source_for_authorized_id_removals`, `free_text_queue_head_guard_logs_response_proof_source_for_removed_head`.
- **Backlog priority queue sync now reports represented ids and has sampleorders coverage (`#samplebacklogqueuesync`).** `agent-doc queue sync` now explains active backlog ids that were skipped because an existing queue prompt already represents the same `#id` (for example `advance [#id]`, not only canonical `do [#id]`), and reports newly materialized ids. The CLI regression covers the sampleorders `queue: start` + `agent:queue ... priority go` + `agent:backlog priority queue` shape, commits the synced queue, and proves `session-check` stays clean afterward. Coverage: `test_queue_sync_materializes_priority_go_backlog_and_session_check_stays_clean_after_commit`.
- **Queue consume now refuses stale-position strikes on id-backed heads (`#qmisstrike-regression`).** The queue-consume planner now rechecks the live head before applying a positional free-text strike; if the head has drifted or been reordered onto an id-backed `do [#id]` item, it logs `queue_consume_refused_id_backed_head_without_explicit_signal` and leaves the item runnable unless an explicit `--done`/`--pending-gate`/`--pending-edit` id or pre-commit prompt/heading-target proof matched that same id. Coverage: `qmisstrike_regression_refuses_reordered_id_backed_head_without_explicit_signal`.
- **Supervisor hot-reload now has JB Run Agent Doc proof mapping (`#suprehotreload-agent`).** The live stale-binary recycle success path writes an `ops.log` proof marker, and SimWorld now models a JB `Run Agent Doc` cycle reaching the recycle boundary: success records a preserved-pane fresh-binary proof, while failed `execve` maps directly to `#recyclerestart-verify/#aazp/#4myd` instead of requiring live operator inspection. Coverage: `suprehotreload_agent_maps_jb_run_agent_doc_to_fresh_binary_proof`, `suprehotreload_agent_maps_reexec_failure_to_existing_operator_verify_buckets`.
- **Foreign-owned queue loss now has an agent-verifiable recovery audit (`#lazilyqrestore-agent`).** `agent-doc queue recover-lost <FILE>` reconstructs historical queue heads from the current snapshot/baseline, editor patch sidecars, and git history, then reports restore candidates only when a snapshot/baseline/sidecar-backed prompt is absent from the current queue and not accounted for by its `#id` in the document or done archive. Git-history-only prompts are reported separately as review context, and a zero-candidate report emits explicit `user_removal_or_completion_proof` evidence, so foreign-owned documents such as `tasks/software/lazily-rs.md` can be audited without taking over the live pane. Coverage: `recover_lost_queue_reports_patch_candidate`, `recover_lost_queue_emits_proof_when_id_is_accounted`, `recover_lost_queue_reads_git_history`.
- **Recyclerestart now has agent-verifiable kill-pane + sync-guard proof (`#recyclerestart-agent`).** SimWorld now models the post-install recycle path recording binary-promotion versus session re-clear/drain proof markers, plus a killed-pane `Sync Tmux Layout` path that defers only for a fresh holder and supersedes stale plugin-local guards through the production FFI sync-lock decision. Coverage: `recyclerestart_agent_verifies_kill_pane_sync_guard_and_reclear_proofs`.
- **Exchange compaction no longer recursively replays prior compact summaries or ordered-list response details.** The default compact digest now recognizes an existing compacted `### Session Summary`, carries forward only its compact metadata, and stops before the previous `Prior summary/context` payload. This prevents repeated compactions from duplicating archived response details or surfacing reversed ordered lists in the live exchange summary. Coverage: `exchange_compact_default_summary_does_not_replay_prior_compact_lists`.
- **Codex/Claude idle queue drains now submit already-drafted restart triggers instead of stalling.** When the supervisor idle-watch sees its drain payload or `/clear` already visible in the owned pane composer, it no longer treats that as handled for Codex/Claude. It sends one bare profile submit key keyed by the live queue head, logs `idle_queue_watch_resubmit ... action=submit_key key=Enter`, and keeps the retry one-shot. This closes the restart path where a Codex session came back with `agent-doc <FILE>` drafted but the queue stopped until the operator pressed submit. Coverage: `pending_payload_enter_resubmit_is_scoped_and_one_shot`, existing `idle_queue` filtered tests.
- **Realtime multi-editor broadcast now has headless targeted-delivery proof (`#rtwbcast-simproof`).** `SimEditor` now records per-editor live-buffer sidecars, consumes production targeted broadcast patch files, ignores non-target peer patches, ACK-deletes applied files, and proves JB + VS Code buffers converge without disk writes or conflict markers through `multi_editor_crdt_broadcast_converges_without_file_cache_conflict`. The production `compute_broadcast` seam also fast-paths rebroadcasts where the originator already contains a stale peer's base-relative deltas, preventing CRDT re-merge of already-converged agent-doc component markers. Coverage: `compute_broadcast_rebroadcast_preserves_component_boundaries`, `multi_editor_crdt_broadcast_converges_without_file_cache_conflict`.
- **Clean-session supervisor drains now leave ops-log proof for the forced fresh-context reset (`#cleandrainsup-agent`).** When the supervisor idle-watch owns a `[clean-session]` queue head, the `/clear` it sends before dispatch now emits `idle_queue_watch_context_reset` to `.agent-doc/logs/ops.log` with the document, harness, target, head hash, and `#cleandrainsup` reason. The existing `idle_queue_watch_drain` marker then proves the following dispatch, so the fresh-agent sequence can be verified locally without a live operator replay. Coverage: `clean_session_reset_ops_log_precedes_drain_submit`.
- **Compact Exchange now has agent-verifiable proof for committed response archival (`#compactdrift-agent`).** Added a git-backed fixture for `agent-doc compact <FILE> --component exchange --commit` where `HEAD` contains finalized `### Re:` response history, the compacted exchange converges through the editor-IPC component patch path, and the compact closeout commits without tripping the committed-historical `typed_component_drift` guard. The closeout spec now states that exchange-only archival of already-committed response history is not typed-component drift when non-exchange components are preserved. Coverage: `compact_with_commit_converges_committed_response_head_without_historical_drift_guard`.
- **Codex queue context-threshold clears now use real Codex session token counts (`#clearcodex`).** The Codex Stop-hook continuation path now locates the newest matching `~/.codex/sessions/**/rollout-*.jsonl` for the document project, reads the latest `token_count` event, and computes ctx% from `last_token_usage` plus `model_context_window`. When `agent_doc_queue_context_reset` is enabled and that pct crosses `agent_doc_clear_threshold`, the hook logs `[s760] ... pct=N clear=true`, records a `transcript context ... >= clear threshold ...` reason, and hands the queue continuation to the supervisor via `codex_fresh_context_handoff` instead of continuing inside the already-loaded pane. Missing/unreadable Codex token counts still fail safe with `pct=none clear=false`. Coverage: Codex JSONL parser/locator tests plus Stop-hook threshold handoff regression.
- **Spent prompt-preset queue pauses self-heal on dispatch instead of requiring manual queue edits (`#qpresetstrike`).** A durable controller `queue_paused` reason like `advance-review preset head is spent ... Operator can clear the '- #advance-review' line` is now revalidated against the live document before blocking JB Run Agent Doc / route dispatch. If the preset head is already absent, dispatch clears the stale pause and proceeds; if the registered preset token is still the live head, dispatch consumes it through the canonical queue consumer, clears the pause, and proceeds. Non-spent operator pauses still fail closed, and stale-supervisor pause recovery keeps its separate restart marker. Coverage: `dispatch_repairs_spent_preset_pause_when_head_is_absent`, `dispatch_repairs_spent_preset_pause_by_consuming_present_preset_head`, plus the existing queue-pause/stale-supervisor dispatch tests.
- **Codex Stop-hook fresh-context continuations now hand off to the supervisor instead of punting `/clear` to the operator (`#codexpcphandoff`).** When a Codex queue continuation needs fresh context because the exchange was compacted after the last tracked clear, the hook records the requested head, emits `codex_fresh_context_handoff ... result=queued supervisor=idle_queue_watch`, and allows the turn to close so the existing supervisor idle-watch can perform the clear/settle/dispatch sequence. Normal in-turn continuations still block with MCP/finalize guidance; only the fresh-context branch moves to CP/supervisor ownership. Coverage: `stop_marker_fallback_requires_clear_after_exchange_compaction`, `stop_tracked_state_hands_fresh_context_continuation_to_supervisor`.
- **JB `Run Agent Doc` no longer stacks repeated Codex reopen text when the same trigger is already drafted.** Before direct-pane dispatch appends `agent-doc <FILE>`, route now checks the recent composer for the exact target trigger. If the trigger is already visible for Codex or Claude, it sends one bare profile submit key and re-polls instead of appending another copy; if an idle prompt appears below the visible trigger, the line is treated as stale scrollback and normal dispatch proceeds. Coverage: `direct_pane_existing_draft_detection_enters_only_current_codex_draft`, `direct_pane_existing_draft_detection_handles_wrapped_codex_path`.
- **Codex idle queue drains now submit the bare `agent-doc <FILE>` reopen instead of a long owner-continuation prompt.** JetBrains `Run Agent Doc` and supervisor idle-queue continuation now share the same harness-native Codex entrypoint (`agent-doc tasks/...md`) instead of injecting a multi-line "Agent-doc active queue continuation" prompt into the TUI. Slash-command queue heads still submit literally, and the queue-continuation response contract stays in the installed harness instructions/runbooks rather than the editor/supervisor payload. Coverage: `idle_queue_drain_payload_uses_trigger_for_codex`.
- **`content_ours` adoption now refuses known-stale supervisors and repairs proven duplicate singleton component blocks (`#dupcontent2`).** The IPC adoption guards for `live_prompt_drift_after_preflight` and `prompt_duplication_in_ack_content` now fail closed when `stale_supervisor_warning_for_doc` classifies the serving controller/supervisor as `supervisor_binary_stale`, leaving the candidate snapshot in place and logging `content_ours_adoption_refused_stale_supervisor` plus an `ipc_proof_insufficient invariant=supervisor_binary_stale` breadcrumb. Separately, the IPC snapshot dedupe pipeline now repairs duplicate singleton components only when the pre-write/before document proves exactly one canonical block and the candidate contains that exact block plus injected duplicates; otherwise the existing structural corruption refusal remains the safety net. Coverage: stale-supervisor refusal tests for both adoption guards and `ipc_snapshot_dedupes_duplicate_singleton_component_from_before_content`.
- **Preset-bearing go queues no longer misclassify genuine prompt lines as stale noise (`#goqnoise`).** `queue_continuation` now treats a queue marker `preset="..."` as supplying the directive verb for each non-empty, non-fenced free-text `Prompt` line, so noun-phrase feature requests and terse lines like `deploy` remain drainable instead of producing `queue_continuation_required=false queue_stale_noise_lines=N`. Fenced console/evidence pastes still self-defer as noise even under a preset queue, and `deploy` is now recognized as a standalone directive verb for non-preset queues. Coverage exercises `detect`, `live_drainable_continuation_head`, `drainable_head_count`, and `queue_stale_noise_lines` on the live CPA/deploy repro shape. Plan: `tasks/agent-doc/plan-go-queue-noise-misclassifies-directive-heads.md`.
- **The controller self-watchdog now reaps a `Stable`-but-stranded handoff replacement, not just a `Preparing` one (`#stuckhandoff2` M1b — fixes the root IPC-drift cause).** Live-repro 2026-06-15: a wedged `controller serve … --handoff-state preparing` orphan survived ~31 minutes racing the IDE/ipc.sock buffer and corrupted a session doc mid-finalize (injected `❯ ` prompt glyphs, spliced duplicated/reordered response lines). ROOT CAUSE (confirmed in code): `promote_handoff` flips a handoff replacement straight to `handoff_state = Stable` + clears `handoff_started_at` the instant the client asks — `ControllerHandoffState::Promoted` is parsed/serialized but NEVER written as a production transition. A client that dies AFTER `promote_handoff` but BEFORE `std::fs::rename(temp_sock → public_sock)` (`handoff_stale_controller`) leaves a `Stable`-in-memory controller stranded on its `controller-handoff-*` temp socket. M1's `controller_self_watchdog_should_suicide` only fires on `Preparing`/`Promoted`, so it could not see the `Stable` orphan — `stale_preparing_controller_self_reaped` had ZERO occurrences ever, and only the slow `/proc`-cmdline gc/M5 sweep reaped these at 7–21 min. FIX (M1b, structural, `rpc.rs`): new `controller_handoff_replacement_is_stranded(handoff_temp_socket, launched_elapsed, threshold)` keyed off the launch socket, not in-memory state — a replacement launched on a temp `controller-handoff-*` socket whose path STILL EXISTS past the threshold proves the promote rename never completed (a healthy handoff removes it), so it self-reaps regardless of `handoff_state`. Wired into the serve-loop `WouldBlock` branch as `should_suicide || is_stranded`. `controller_self_watchdog_suicide` hardened with a generation-ownership guard so a stale stranded generation never clobbers a newer clean controller's shared on-disk record to `Failed` (it still exits to stop the buffer race). Net: a wedged orphan now self-clears within the 45s threshold regardless of when its client died, and the next bind promotes a clean controller — no manual `pkill` / `git checkout` recovery. Coverage: 6 new deterministic unit tests in `project_controller.rs` (stranded-when-temp-persists, not-stranded-after-rename, not-stranded-within-threshold, none-socket-never-stranded, suicide-marks-failed-for-stranded-`Stable`, suicide-preserves-superseded-generation). Plan: `tasks/agent-doc/plan-stuck-handoff-hardening.md`.
- **`agent-doc lib-install` now AUTO-recycles running controllers onto the freshly-installed binary instead of only printing the hint (`#autorecycle-on-install`, upgrades `#ctlrecycle` R4 from print-only to action).** Closes the recurring `supervisor_binary_stale` / `#fcc0` / `#no-mid-session-install` pain: after a `lib-install`, the JetBrains plugin hot-reloads the cdylib by mtime but already-running agent-doc controllers/supervisors keep serving the PRIOR binary until they recycle. R4 only PRINTED `run \`agent-doc admin recycle --all-projects\``; the operator (and dogfood loop) still had to run it by hand. `lib_install::run_paths` now calls `recycle_controllers_all_projects()` directly after a successful install, so every running controller is marked to recycle at its next idle boundary (the same idle-gated `recycle` RPC `admin recycle --all-projects` sends — it fires only at a turn / inter-queue-item boundary, never mid-turn, so triggering it from install is safe). Reports `[lib-install] auto-recycle: N controller(s) marked … M skipped`. Best-effort: a recycle error never fails the install — it logs a warning and falls back to the manual-recycle hint (no swallowed errors). Opt out with a falsey `AGENT_DOC_RECYCLE_ON_INSTALL` (`0`/`false`/`no`/`off`), which restores the print-only hint. Pairs with the supervisor self-recycle (`#supselfheal`) and auto-install (`#supautoinstall`) so a freshly-built agent-doc goes live everywhere without a manual recycle step. Coverage: `lib_install::tests::recycle_on_install_default_on_and_opt_out_is_falsey` (default-on + falsey opt-out resolution; the cross-project process recycle itself is not SimWorld-modelable). Plan: `tasks/agent-doc/plan-proactive-recycle-on-install.md`.
- **The free-text strike repair no longer mis-strikes the next open id-backed queue head (`#qmisstrike`).** Operator report (tsift.md) + live repro (agent-doc-bugs2.md): after a free-text head was consumed (or the operator manually deleted already-struck items), `finalize`'s strike repair struck the *next* still-open head — an id-backed `[#id]` head the response never answered — and `session-check`'s `#queue-clear-unrun-items` guard had to catch it and force a manual un-strike + `reset --from-current`. ROOT CAUSE: `repair::first_queue_head_is_free_text` (the guard gating `strike_recovered_free_text_queue_head` → `consume_queue_prompt_force_disk`, which strikes the leading head by POSITION) used a brittle `do [#` / `do #` prefix test. That test only recognized the `do `-prefixed spelling, so a bare `[#id]` or pin-prefixed `:pushpin: [#id]` / `:round_pushpin: [#id]` head (the spellings the operator's queues carry after manual edits or pin promotion) was mis-classified as *free text* and struck by position. FIX: delegate the guard to the authoritative `write::queue_head_is_free_text_prompt` classifier (which already resolves `#id`, `[#id]`, `do [#id]`, pinned, and preset spellings correctly via `topic_resolves_to_exact_id`), so the repair strike path agrees with the finalize path — an id-backed head is struck only via `--done` / `--pending-gate` / `queue consume`, never by the positional free-text heuristic. Coverage: `repair::first_queue_head_free_text_check_excludes_id_backed_head` (bare/pinned `[#id]` heads classify as not-free-text; a genuine no-`#id` head still strikes; inactive queue strikes nothing) + extended `write::queue_consume::free_text_queue_head_detection` (bare/pinned `[#id]` spellings). Plan: `tasks/agent-doc/plan-finalize-misstrike-next-head.md`.
- **Multi-phase auto-loop policy: routing a phase to review no longer terminates the go-mode drain (`#mphaseloop`).** Operator directive (2026-06-14): a go-mode drain must continuously advance a multi-phase task until it is DONE or legitimately blocked (clean-session / operator-verify / external outage). "Needs review" is NOT a terminal stop — when a phase needs human/external validation, it moves to `agent:review` as a gated `[/]` item and the drain KEEPS advancing the remaining phases/queue; only done/blocked terminate the drain. Codifies the `SKILL.md` `#drain-no-defer` + "complete over gate" policy into the binary closeout: at a successful commit that still owes a drainable queue continuation, when this cycle added an open `agent:review` item relative to the pre-commit HEAD (a phase routed to review rather than completed/blocked), the binary emits `drain_continue_after_review file=… next_head=… (#mphaseloop)` to `ops.log` — the proof that review-routing advanced to the next drainable head instead of stalling the queue. The continuation gate is unchanged (`queue_continuation_required = active && drainable_head_count > 0`); adding a review item never drops a still-drainable head out of the loop. New pure helper `queue_continuation::review_phase_routed(prior, current)` (open-`agent:review`-item delta, review-component-scoped so a growing backlog/queue is never misread as a routed phase). Coverage: `review_phase_routed_detects_added_open_review_item`, `review_phase_routed_counts_only_review_component`.
- **VS Code parity for the editor-save drift resolution (`#jbeditorsavedrift-vscode`).** Completes the follow-up flagged by `#jb-editor-save-resolves-drift` (below), which shipped the JetBrains/IntelliJ socket `save_document` path only. The binary's post-commit carry-forward flush (`flush_editor_buffer_to_clear_drift` in `git.rs`) was socket-only, so a VS Code-only session — which watches `.agent-doc/patches/` instead of running the IPC socket listener — never got the save request and the carry-forward drift recurred. Now the flush prefers the socket (JetBrains) and falls back to writing a `.agent-doc/patches/save-document.signal` file (JSON `{ file, patch_id }`) when no socket listener is active or the send returns no ack, mirroring the existing `vcs-refresh.signal` channel. The VS Code extension's `PatchWatcher` watches that signal, flushes the matching editor buffer to disk via `TextDocument.save()` (clearing the dirty flag), and writes the saved buffer to the ack-content sidecar (`.agent-doc/ack-content/<patch_id>.md`) — the VS Code mirror of the JB plugin's `saveDocumentViaDocument`. Proof line: `postcommit_editor_save_flushed … transport=socket|file_signal` (or `…_skipped` on failure). Spans the binary (`git.rs` file-signal fallback) + the VS Code extension (`extension.ts` `save-document.signal` watcher + new pure `saveSignal.ts` helpers). Coverage: `postcommit_carry_forward_superset_writes_file_signal_without_socket_listener` (Rust), `saveSignal.test.ts` `parseSaveDocumentSignal` / `ackContentSidecarPath` (TS). FlowCore `git.rs` `reason=` budget bumped 9→11 with audit. Live proof (operator-drive, needs a live VS Code window): type unsaved edits into a session doc, run a cycle, confirm the buffer flushes to disk without the same drift recurring.
- **A live-editor-buffer drift now resolves by asking the plugin to SAVE instead of stalling (`#jb-editor-save-resolves-drift`).** Operator directive (2026-06-14): "if you're talking about the JB editor buffer, have the plugin save the file to resolve the drift — we should not stall in this scenario." When `finalize`/`write` detects `live_prompt_drift_after_preflight` (the IntelliJ editor buffer holds unsaved edits ahead of disk, `content_ours_len > candidate_len`), the prior behavior adopted `content_ours` as a **next-cycle carry-forward** snapshot — which stalled the response AND left the editor dirty, so the next cycle re-detected the same drift and could raise a File Cache Conflict when the binary later wrote disk under the still-dirty buffer. Now, in the proven-socket `socket_ack_content` path, once the drift guard adopts `content_ours` the binary sends a new `save_document` IPC request to the live plugin (`ipc_socket::send_save_document`); the plugin runs `FileDocumentManager.saveDocument()` (flushing the buffer to disk AND clearing the editor's dirty flag) and writes the saved buffer to the ack-content sidecar, which the binary adopts as a clean **this-cycle** on-disk snapshot (`FileRead`) via `upgrade_live_prompt_drift_with_editor_save` — committing the response now instead of carrying it forward. Proof lines: `live_prompt_drift_editor_save_requested` → `live_prompt_drift_resolved_via_editor_save` (or `…_unreachable`/`…_no_sidecar` when no live editor answers, in which case it falls back to the prior carry-forward without regressing — never stalls harder). Spans the binary + the FFI cdylib (orchestration `send_save_document` + reconcile, shipped via `cargo build --release && agent-doc lib-install`) + the JB plugin (`save_document` handler in `PatchWatcher.handleSocketMessageV2` → `saveDocumentViaDocument`, plugin `0.2.164`). Coverage: `send_save_document_sends_typed_message_with_patch_id`, `reconcile_live_prompt_drift_via_editor_save_returns_saved_buffer`, `upgrade_live_prompt_drift_with_editor_save_flips_content_ours_to_filewrite`, `upgrade_live_prompt_drift_with_editor_save_no_listener_falls_back`. Live proof (operator-drive, needs a live IDE): type unsaved edits into a session doc, run a cycle, confirm ops.log shows `live_prompt_drift_resolved_via_editor_save` and the response commits without a stall or File Cache Conflict dialog. VS Code plugin parity is a follow-up (this ships the JB/IntelliJ path the operator hit).
- **The in-session `/loop` now drains `[clean-session]` heads in place — `queue_continuation_required` stays true while any non-`[operator-verify]` head remains (`#qcontdrain`). BREAKING CHANGE: clean-session is no longer deferred to the supervisor.** Operator directive (2026-06-14): "you should not have stalled. set queue_continuation_required=true." Supersedes the `DrainScope::Loop` clean-session deferral added by `#goqueuestall`/`#cleandrainsup`/`#freshgrant`: the in-loop agent used to defer `[clean-session]` heads under a live editor-IPC listener and stop, handing them to the supervisor idle-watch. But when the supervisor was itself stalled (the recurring live failure this fixes — `#recyclerestart`/`#suprecyclestall`), nobody drained them and the queue stranded. Now `queue_continuation::deferred_backlog_ids_with_ipc_scoped` and the go-mode backlog→queue sync (`partition_drainable_backlog_ids`) defer ONLY `[operator-verify]` (which genuinely needs a human) in **both** scopes; `[clean-session]` is always drainable, so the `/loop` drains it in the current session rather than stalling. The supervisor still force-`/clear`s before its own dispatches (`head_requires_clean_session`), and the `#freshgrant` short-TTL grant + `_live_ipc`/`DrainScope` plumbing are retained on the signatures but no longer gate the deferred set (follow-up: remove the now-dead grant machinery). `session-check`'s no-response active-head guard now interrupts on a committed-without-response clean-session head regardless of live IPC. SKILL.md auto-loop section + `specs/07-orchestration-commands.md` (`#goqstall2`) updated. Coverage: `deferred_backlog_ids_defers_only_operator_verify`, `loop_drains_clean_session_regardless_of_grant_or_ipc`, `loop_and_supervisor_both_drain_clean_session`, `partition_drainable_backlog_ids_skips_only_operator_verify`, `no_response_active_queue_head_interrupts_on_clean_session_head_regardless_of_ipc`. Live proof (operator-drive): with this build installed, leave only `[clean-session]` heads at an active go-mode queue under a live IDE and confirm preflight reports `queue_continuation_required: true` (the `/loop` drains them) instead of `false`.
- **A supervisor-cleared in-loop agent now DRAINS the `[clean-session]` head it was re-dispatched for, instead of re-deferring it (`#freshgrant`).** Completes the operator directive (2026-06-14): "redefine `[clean-session]` as a fresh agent session — if the loop restarted, it should run; if an idle-install + restart is needed, it should run without stalling." `#cleandrainsup` made the supervisor idle-watch force-`/clear` + re-dispatch a `[clean-session]` head to a freshly-cleared agent, but that freshly-cleared in-loop agent ran preflight in `DrainScope::Loop`, which still deferred `[clean-session]` under a live editor-IPC listener (`deferred_backlog_ids_with_ipc_scoped`) — so `queue_continuation_required` came back `false` and the fresh agent declined the very head it was cleared for, churning a no-op (`#qchurn`). Now: when the idle-watch sends the `#cleandrainsup` `/clear` for a clean-session head, it writes a short-TTL **fresh-context grant** (`write_clean_session_grant_for_head` → `.agent-doc/clean-session-grants/<hash>.json`, ops.log `clean_session_fresh_context_grant … (#freshgrant)`). The `DrainScope::Loop` deferral consults `active_clean_session_grant_ids` and does NOT defer a granted clean-session head — the freshly-cleared agent IS the clean session the tag asks for, so it drains that head. `[operator-verify]` stays deferred in both scopes regardless of any grant. The grant is bounded (`CLEAN_SESSION_GRANT_TTL_SECS = 600`): an un-drained grant expires and reverts to the deferral fail-safe so it can never enable in-loop clean-session drains in a later accreted session. Pairs with the already-landed auto-install + recycle (`#supautoinstall`/`#supselfheal`) so the binary change a clean-session item needs is installed + recycled without stalling the drain. Coverage: `loop_drains_granted_clean_session_under_live_ipc`, `clean_session_grant_roundtrips_filters_and_expires`. Live proof (operator-drive): with this build installed, leave a `[clean-session]` head at the active queue head under a live IDE, let the supervisor idle-watch fire `idle_queue_watch_context_reset … (#cleandrainsup)` + `clean_session_fresh_context_grant … (#freshgrant)`, and confirm the re-dispatched agent's preflight reports `queue_continuation_required: true` for that head (drains it) instead of `false`.
- **Exchange clear/compact keeps stale HEAD out of the durable merge base (`#clearexchstale`).** The exchange compaction/clear boundary now has explicit regression coverage for the stale-HEAD revival class: stale editor-cache/live-buffer proof still fails closed before any document or snapshot write, and a successful empty exchange replacement advances the snapshot to the cleared document so later preflight/repair paths cannot replay the archived pre-clear exchange. Coverage: `component_compact_rejects_stale_editor_cache_when_snapshot_is_stale`, `component_compact_empty_message_advances_snapshot_after_exchange_clear`. Plan: `tasks/agent-doc/plan-jb-clear-exchange-stale-head-revival.md`.
- **The supervisor auto-installs the agent-doc source after finalize, DEFAULT-ON in dogfood sessions (`#supautoinstall`/`#r18j`).** Closes the bootstrap gap behind `#supselfheal`/`#ctlrecycle`: a stale supervisor could self-recycle onto a freshly-installed binary, but nothing *installed* the binary after the operator edited agent-doc source — the dogfood loop still required a manual `cargo install`. The idle supervisor now runs an install rung after finalize (in the idle supervisor process, never the finalize client) that rebuilds + installs the agent-doc source, emitting `supervisor_auto_install_started` → `supervisor_auto_install_succeeded`, after which the existing `#supselfheal` staleness check fires `supervisor_binary_stale_self_recycled` and the session continues on the new build. The install rung precedes the `#ctlrecycle` recycle rung at the idle boundary. Dogfood-only — it never fires for non-dogfooding documents. Opt out with a falsey `AGENT_DOC_SUPERVISOR_AUTO_INSTALL` env / `agent_doc_supervisor_auto_install` frontmatter / `.agent-doc/config.toml` knob. Live proof (operator-drive, needs a live editor + supervisor restart): edit agent-doc source in a dogfood session, finalize, confirm the three ops.log lines and that the session keeps running on the new binary. Plan: `tasks/agent-doc/plan-supervisor-auto-install-after-finalize.md`.
- **The supervisor now self-drives `[clean-session]` queue heads instead of deadlocking under a live editor (`#cleandrainsup`).** Root-fixes the go-mode auto-loop stall the operator hit with the IDE open: a queue full of `[clean-session]` backlog items never drained. `queue_continuation::deferred_backlog_ids` deferred a head when `operator_verify_required || (clean_session_required && live_ipc)`, and **both** consumers shared that set — the in-session Claude Code `/loop` (`drainable_head_count` → `queue_continuation_required`) AND the supervisor idle-watch (`live_drainable_continuation_head`). With the IDE open (`live_ipc == true`) every `[clean-session]` head was deferred in both paths, so the loop stopped and the supervisor's `active_head` was `None` — nobody ever drained them, even though SKILL.md promised "the supervisor owns the `/clear` and re-dispatches the next item to a freshly-`/clear`ed agent." The drainability filter is now scoped (`DrainScope::Loop` vs `DrainScope::Supervisor`): the in-session loop still defers `[clean-session]` under a live IPC listener (it cannot give the head the fresh context the tag asks for, so it stops and hands off), but the supervisor idle-watch defers only `[operator-verify]` and DRAINS `[clean-session]` heads — force-`/clear`ing before each dispatch (`head_requires_clean_session` → `clean_session_head_forces_context_reset`, independent of the `agent_doc_queue_context_reset` opt-in) so each clean-session item runs in a genuinely fresh agent context. `[operator-verify]` stays deferred in both scopes (genuinely needs a human). SKILL.md auto-loop section updated to document that stopping on `queue_continuation_required == false` for clean-session heads hands them to the supervisor rather than abandoning them. Coverage: `deferred_scoped_supervisor_drains_clean_session_under_live_ipc`, `head_requires_clean_session_maps_head_id_to_tag`, `clean_session_head_forces_context_reset_policy`. Plan: `tasks/agent-doc/plan-cleandrainsup-supervisor-drains-clean-session.md`.
- **A stale route-owned supervisor now self-recycles onto the freshly-installed binary by default (`#supselfheal`). BREAKING CHANGE: supervisor auto-recycle defaults ON.** Automates the "harder recovery path." Previously a supervisor that went stale after a `cargo install` only logged `supervisor_binary_stale_detected` and kept re-filing File Cache Conflict / IPC-drift dialogs (`#fcc0`/`#ipcdrift`) until an operator manually ran `restart-supervisor` (which itself fails `generation N is closed` when the wedged controller can't cooperate), `interrupt-clear --force`, or `kill <pid> && agent-doc start`. The turn-boundary blue/green `execve` self-recycle machinery already existed (`#ctlrecycle` R3 / `#supkill-bg`) but was gated behind the opt-in `AGENT_DOC_SUPERVISOR_AUTO_RECYCLE` / frontmatter / project knob. `resolve_supervisor_auto_recycle` now defaults that resolution to ON, so a stale supervisor recognizes its own staleness (`process_binary_is_stale`) at the next turn / inter-queue-item boundary and replaces its own image with the fresh binary **in place**, preserving the live harness child + pane (zero-gap red/green — no dropped turn, no window without a supervisor). Safeguards retained: recycle fires only at a turn boundary (`prompt_visible && !turn_active`), never mid-turn; it is debounced at idle so a momentary lull never thrashes the child and fires immediately only at the deliberate inter-queue-item restart point; and a failed `execve` disables further attempts for the process lifetime (`#suprecyclestall`, never `process::exit`). Opt out with a falsey `AGENT_DOC_SUPERVISOR_AUTO_RECYCLE` env / frontmatter / `.agent-doc/config.toml` `agent_doc_supervisor_auto_recycle = false`. **Bootstrap caveat:** a supervisor ALREADY running the pre-`#supselfheal` binary cannot self-heal retroactively (old code lacks the default-on logic) — restart it once onto a `#supselfheal` build, then it is hands-off. Coverage: `stale_supervisor_self_recycles_at_turn_boundary_by_default`, `opted_out_document_clears_and_drains_without_auto_recycle`, updated `resolve_supervisor_auto_recycle_precedence` default-on assertions.
- **`start --route-owned` no longer wedges on a pane-move against an up-to-date controller actor (`#startgencas`).** Root-fixes the recurring live `Error: project controller command \`start_session\` failed: controller failed to start session actor` wall. `start/run.rs` computed the new ownership generation in two branches: when the launcher pane matched the registry it bumped (`next_generation` = `infer + 1`), but when the launcher pane differed from the registry's *stale* pane it handed the controller the **un-incremented** current generation via `infer_latest_generation`. The controller's `start_session` CAS unconditionally expects `start_generation - 1` as the prior generation, and `infer_latest_generation` returns `max(controller-actor gen, session-log gen)` — so once the controller actor caught up to the inferred latest (the normal healthy steady state) the no-bump value *equalled* the live generation and the CAS failed closed (`compare-and-swap failed: expected N-1, found N`), surfacing as the wrapped "failed to start session actor" error. A pane move IS an ownership transition, so `start` now always takes the `next_generation` (`infer + 1`) path and logs the `ownership_transition`, keeping the value handed to the controller aligned with the CAS contract regardless of registry-pane staleness. The fix lives in the `start` process (which computes the generation it sends), so it takes effect on the next `agent-doc start` without a controller restart. Coverage: `start_after_pane_move_against_up_to_date_actor_bumps_past_cas` (seeds an up-to-date actor at gen N, asserts the un-bumped start fails the CAS and the bumped start passes and re-asserts ownership on the new pane).
- **`start` no longer wedges forever once the session log runs ahead of the committed actor generation (`#startgenlogdrift`).** Follow-up to `#startgencas` — the same `controller failed to start session actor` wall, but a self-sustaining variant. `start/run.rs` writes the `session_start` / `ownership_transition` log lines with the *intended* generation BEFORE the controller commits the start, so any start that loses the controller CAS still appends its un-committed generation to the session log. `infer_latest_generation` returned `max(controller-actor gen, session-log gen)`, so one failed start left the log one ahead of the committed actor generation; the next start then inferred that inflated value, bumped past it, and the controller CAS rejected it again (`expected <log>, found <db>`) — appending a yet-higher generation and running the divergence away (observed live: actor stuck at gen 85 while the log climbed 86→98, every `start` failing). The committed controller actor record is the sole CAS authority, so `infer_latest_generation` now returns it directly whenever a record exists and consults the optimistic session log only as a bootstrap/legacy fallback before the control plane has any record for the document. This also self-heals an already-diverged session: the next `start` reads the committed generation, bumps once, and the CAS clears — no manual log/DB surgery. Coverage: `infer_latest_generation_ignores_optimistic_log_ahead_of_actor_record` (committed actor at gen N, session log inflated to N+10, asserts inference stays at N and the next generation is N+1).
- **Supervisor auto-recycle is now configurable per project AND per document (`#suprecyclecfg`).** The route-owned supervisor's `execve` hot-reload onto a freshly-installed binary (`#ctlrecycle` R3 / `#suprecyclequeue`) was opt-in only via the `AGENT_DOC_SUPERVISOR_AUTO_RECYCLE` env var and the project-config `agent_doc_supervisor_auto_recycle`. A per-document frontmatter `agent_doc_supervisor_auto_recycle: <bool>` now slots into the resolution between them, so a single document can explicitly opt in or out independent of the project default. Resolution precedence: env var (truthy/falsey force) → frontmatter → project config → built-in default (flipped to ON by `#supselfheal`, above). `resolve_supervisor_auto_recycle` takes the new frontmatter arg and `supervisor_auto_recycle_enabled` reads the doc's frontmatter; spec (`specs/06-config.md`) and the project-config doc comment list the full precedence. Coverage: extended `resolve_supervisor_auto_recycle_precedence`.
- **A benign in-flight dispatch coalesce reports deduped-success instead of exit-1 (`#qflood2`).** When `JB Run Agent Doc` (or any route dispatch) hits an identical dispatch for the same cycle already in flight, the controller correctly suppresses the re-send (never piling the trigger into the busy pane), but the route caller surfaced that suppression as `project controller command \`dispatch\` failed: dispatch coalesced … (#qflood)` — an exit-1 that read as an error and made the operator manually clear the queued prompt. The coalesce is a benign dedup (the requested work is already running), so it now reports success: the coalesce bail carries a stable `failed_stage=coalesced_in_flight` marker, `authorize_controller_dispatch` classifies it across the IPC boundary into a typed `RouteDispatchAuthorization::CoalescedDeduped` outcome, and every route dispatch site returns the already-running dispatch pane via `route_dispatch_deduped_pane` **without re-sending** (logged `route_dispatch_deduped reason=in_flight_coalesce`). The new enum makes the deduped case a compile-time-exhaustive match at all four send sites, so no path can fire a re-send on a coalesce (which would be the flood) and a coalesce can never dead-end as exit-1. The send-suppression (the actual flood guard) is unchanged; only its reporting changed, matching the SimWorld model that already returns deduped-success. The live multi-process collision repro stays operator-drive. Coverage: `qflood2_coalesce_marker_survives_ipc_wrapping`, extended `qflood_coalesces_busy_in_flight_redispatch_and_releases_on_ready`. Plan: `tasks/agent-doc/plan-queue-dispatch-flood.md`.
- **Proactive recycle-on-install: idle controllers/supervisors self-recycle onto a freshly-installed agent-doc (`#ctlrecycle`).** Root-fixes the recurring "already-running agent-doc keeps serving the OLD binary after `cargo install` until manually restarted" churn (the `#ctlstalebin` dispatch reject was only a per-dispatch backstop). Each long-lived process now compares its launch identity against the installed binary (`current_binary_identity` stats the install *path*, so it sees the new build while still running the old mapped inode) and recycles itself when idle, debounced (`AGENT_DOC_RECYCLE_IDLE_GRACE_SECS`, default 5s; fail-open on stat errors). **R1** — the controller serve-loop idle poll exits with `controller_self_recycled reason=stale_binary` once no dispatch is in flight for any document (`has_any_open_in_flight_dispatch`) and it is `Stable`; the next `connect_or_launch` relaunches the fresh binary (state is on disk). **R2** — `agent-doc admin recycle [--project-root R] [--all-projects] [--json]` marks running controllers to recycle at their next idle boundary (a `recycle` RPC, idle-gated so it never interrupts a turn) — deterministic for the release flow (`cargo install && agent-doc admin recycle --all-projects`). **R3** — the `start --route-owned` supervisor self-recycles from its idle-queue watch when idle + stale, **opt-in behind `AGENT_DOC_SUPERVISOR_AUTO_RECYCLE`** (it ends the live agent child, so default OFF logs `supervisor_binary_stale_detected` instead); chose clean `process_exit`-and-relaunch (`supervisor_binary_stale_self_recycled via=process_exit`) over re-exec for safety. **R4** — `agent-doc lib-install` prints the recycle hint. Coverage: `process_binary_is_stale_matches_and_differs`, `recycle_debounce_decision_requires_continuous_idle_grace`, `supervisor_stale_action_policy` (pure predicates + sentinel pattern; process recycle is not SimWorld-modelable). Plan: `tasks/agent-doc/plan-proactive-recycle-on-install.md`.
- **Stale-binary dispatch backstop recycles a running controller onto a freshly-installed agent-doc (`#ctlstalebin`, #stuckhandoff2 follow-up).** A controller whose own recorded `controller_binary` no longer matches the installed binary keeps serving OLD code. `connect_or_launch` already hands *cross-process* callers off to a fresh controller (the common `cargo install` recycle), but a dispatch that still reached the stale controller's `handle_dispatch` (in-process co-host, or a narrow handoff race) was admitted — letting an old-binary controller keep driving session writes until a manual restart (the operator's observed ~1h churn after a `cargo install`). `handle_dispatch` now refuses such a dispatch with a `controller_binary_stale` rejection receipt + `dispatch_refused_stale_binary` ops.log line, and `authorize_dispatch` retries the dispatch exactly once so the retry's `connect_or_launch` promotes the fresh binary through the two-phase handoff (`dispatch_retry_after_stale_binary`). Fail-open on any binary-stat error — a transiently-unreadable path never blocks a live dispatch. Coverage: `dispatch_refused_when_controller_binary_stale`. The in-process `start --route-owned` supervisor's own self-recycle (its whole process is the old binary) remains a separate follow-up.
- **Queue-consume editor IPC is node-keyed and generation-fenced (`#queueconsume-stale-fence`).** The shared editor convergence path now tags socket queue-consume payloads with both the raw baseline hash and a transient-marker-normalized baseline hash, and expresses the queue head completion as an exact markdown-AST `node_patches` strike instead of a broad legacy `queue` component replace. JetBrains enforces the generation fence against the live editor buffer before mutation, so a delayed socket/file patch from an earlier run is dropped once the queue has moved on while benign `(HEAD)` / boundary / guard / pipeline marker churn still passes. When no editor listener is running, the CLI falls back to the guarded disk write; when a listener is active and cannot apply/prove the node patch, it fails closed rather than replaying a stale queue replacement or writing behind the editor. Coverage: `queue_consume_editor_convergence_payload_is_node_keyed_and_fenced` and `PatchGenerationFenceTest` normalized-hash cases.
- **Finalize-tolerance + post-commit-repair groundwork; behavior-flip rungs held for operator confirmation (`#fintol`/`#pcwc`/`#rtwbcast`).** Three phases from `plan-clean-exchange-run.md` landed only their pure, seam-isolated, non-behavior-changing groundwork after the wiring was found to conflict with a deliberate shipped invariant:
  - `#fintol1` — `write::response_target_disjoint_from_user_edit(baseline, content_ours, candidate)`, a pure conflict-scope primitive proving (via a confined-outside-`exchange` check plus a conflict-free 3-way merge that preserves both sides) whether a concurrent user edit is disjoint from the response target. Unit coverage `write::tests::fintol_*` (disjoint queue edit / response-body-rewrite collision / new-exchange-prompt / no-edit). NOT wired into the commit path: today's gate already preserves a disjoint outside-`exchange` edit by carrying it forward UNCOMMITTED (the finalize succeeds) — a shipped invariant asserted by `finalize_preserves_late_comment_tail_edit_outside_exchange_uncommitted` (+6 integration tests). Forward-merging it into the same cycle's commit would flip commit-now vs carry-forward, so `#fintol2`/`#fintol3` are held for an operator decision against a live Phase-0 re-baseline.
  - `#pcwc` — post-commit worktree corruption now auto-repairs only the proven lost-committed-content class: when the working tree drops committed HEAD lines and adds no carry-forward user directive, `emit_postcommit_worktree_check` restores the file to HEAD and sends a guarded `refresh_content` socket message so a live JetBrains buffer stops writing the stale payload back. The editor applies that refresh only when the live buffer still matches the stale hash/length, preserving legitimate carry-forward supersets and concurrent user edits. Coverage: `postcommit_worktree_*`, `RefreshContentPreconditionTest`, and the hot-path token budget update for the new `reason=` logs.
  - `#rtwbcast` Option C — `realtime_model::compute_broadcast(base, originator, peer) -> BroadcastMerge { merged, originator_echo_suppressed }`, the pure MERGE-ONLY multi-editor convergence seam (no delivery). The SimWorld `multi_editor_crdt_broadcast_converges_without_file_cache_conflict` test now drives this production seam instead of an inline `merge_contents_crdt`. Coverage `realtime_model::tests::compute_broadcast_*`. Full multi-editor delivery (Options A/B — an `editor_id` FFI ABI bump, per-editor sidecars/patch files, both editor plugins, a two-live-IDE verify) remains an operator design decision.
- **Model-projected snapshot baseline — Rungs 2-4, now default (`#mps`).** Re-homes the merge baseline (the `--baseline-file` common ancestor the finalize merge uses) from the on-disk `.md` file onto the structured document model. **On by default**; opt out with `AGENT_DOC_MPS=0` (`false`/`no`/`off`) for the pure `.md` path. Defaulting on is non-regressing by construction while the `.md` remains the cross-check cache: the model projection is used only when it is byte-identical to the legacy `.md`, and the proven `.md` wins on any divergence. Rung 2 (pin): `preflight`'s `save_baseline_content` also persists the baseline as an overlay sidecar (`baselines/<hash>.overlay.yrs`) via `snapshot::save_baseline_model`, logging `mps_baseline_pin`. Rung 3 (flip): `write::read_explicit_baseline` sources the base by projecting that overlay (`snapshot::load_baseline_model`), logging `mps_baseline_resolve source=model|md_backstop|md_fallback` and, on disagreement, a loud `mps_baseline_divergence` (with first-differing byte) while preferring the `.md` backstop. Rung 4 (derive): the `.md` baseline is the derived cross-check cache, no longer the read authority — its removal (making the model standalone) is the remaining step, gated on production divergence logs staying clean. The baseline overlay is a separate sidecar from the crdt-runtime overlay so the cutover never perturbs the stream/crdt write merge, and is migrated on rename. Coverage: `snapshot::tests::mps_baseline_model_*` (round-trip, absent→None, md-backstop-on-divergence, projection-when-no-md, idempotent delete, first-diff-byte); the full 4317-test suite now runs through the model path. Verified end-to-end through the installed binary with no env set: preflight pins the overlay + emits `mps_baseline_pin`, finalize resolves `source=model diverged=false`, response merges and commits; `AGENT_DOC_MPS=off` writes only the `.md`. FlowCore hot-path token budget bumped +2 (`reason=no_model|model_error`) with audit.
- **Model-projected snapshot baseline — Rung 1 shadow instrument (`#mps`).** First, zero-behavior-change rung of the migration that re-homes the merge baseline from the on-disk `.md` snapshot onto the structured document model (`tasks/agent-doc/plan-model-projected-snapshot.md`, successor to the superseded `#snbc`). Adds `snapshot::overlay_projection_is_byte_stable` — a pure check that round-trips content through the same `OverlayCrdtDoc::from_markdown → encode_state → decode_state → to_markdown` pipeline the merge base (`crdt_merge_base_state`) uses — and an env-gated shadow probe wired at the central `snapshot::save` funnel (`AGENT_DOC_MPS_PROJECTION_PROBE=1`) that emits a grep-able `mps_projection_equiv ok|drift …` ops.log marker on real traffic without imposing overlay encode/decode cost on the default hot path. This proves the migration's load-bearing precondition (byte-stable projection; obstacle 2) before any cutover. Offline evals (`snapshot::tests::mps_projection_byte_stable_*`) confirm byte-stability for inline, template/queue, exchange-append, boundary-marker, empty, and unicode shapes. No merge outcome or persisted artifact changes; reads from the overlay remain shadow-only.
- **Deterministic SimWorld editor + tmux integration harness (`#swint`).** Added `SimEditor` to `src/sim_world.rs`: a deterministic editor-buffer actor that speaks the production durable live-buffer protocol (`debounce::record_live_buffer_digest_content`) and reads "current document" back through the production `realtime_model::resolve_current_doc` seam, so the File-Cache-Conflict / IPC-drift / queue-flood classes (previously live-IDE-only) are now regression tests. Slice 1 — `simeditor_unsaved_buffer_edit_resolves_to_editor_buffer_and_survives_commit` is the deterministic `#rtwverify` proof (unsaved buffer wins, emits the `realtime_doc_resolve authority=editor_buffer` ops.log marker, edit survives commit) plus `simeditor_save_then_close_falls_back_to_disk_authority`. Slice 2 — `simeditor_jb_and_vscode_buffer_authority_parity_with_kind_specific_conflict` (JetBrains/VS Code agree on read authority, differ only on the surfaced `CacheConflict` signal). Slice 3 — `multi_editor_crdt_broadcast_converges_without_file_cache_conflict` drives two emulated editors through `merge::merge_contents_crdt` (`#rtwbcast`). Slice 4 — `integrated_editor_edit_routes_drains_under_drain_owner_gate_and_broadcasts_back` connects the editor seam to the route/controller model and the public `drain_owner` lease (`#kp5z`). New production primitive `debounce::clear_live_buffer` models the editor-close lifecycle (clears the sidecar so the cycle falls back to disk); coverage `clear_live_buffer_removes_sidecar_and_is_idempotent`. Spec: `specs/12-deterministic-simulation.md` (`#swint` section).
- **Compact Exchange now converges through the editor instead of a direct disk write (`#w42v`).** `compact::apply_compacted_document` (the single replacement boundary for all compact paths) previously wrote the full compacted document to disk via `atomic_write_if_current_pub`, which diverged from an open JetBrains buffer and raised a `File Cache Conflict`. It now calls `write::try_compact_editor_converge` first: when a JB/VS Code IPC listener is active it sends component `op:replace` patches for the changed components (mirroring the `#q7jm` live_prompt_drift convergence — never `fullContent`) and verifies the editor ack matches the compacted target, logging `compact_writeback transport=editor_ipc`. The guarded direct write is now only the no-listener fallback, logging `compact_writeback transport=disk_fallback reason=no_listener` (or `reason=listener_degraded` only while no listener is accepting connections); active-listener no-delta/no-ack/mismatch/send failures log `transport=blocked` and fail closed. Spec updated in `07-closeout-commands.md`; coverage: `try_compact_editor_converge_falls_back_to_disk_without_listener`. Live JB verification (conflict gone) is operator-drive.
- **Dispatch-source observability for the queue-flood diagnosis (`#kp5z`).** `project_controller::authorize_dispatch` (the single production funnel every queue-continuation dispatch passes through) now logs `queue_dispatch_invoked file=… pane=… generation=… command_kind=… payload=…` on every invocation. Pure observability (no behavior change) so an operator flood repro reveals which caller re-invokes `dispatch` while the pane is mid-turn, pinning the source before a busy-gate/dedupe fix.

- **JB `Run Agent Doc` and `Clear Session Context` on Codex now use the shared tmux submit profile (`#jbcodexsubmit`).** The shared `sessions::send_submitted_text_for_harness` live-pane path sends Codex command text through the same profile as queue drains and clear retry. Current diagnostics report `tmux_text_enter` / `Enter`; the existing one-shot resubmit remains as bounded recovery if an older draft is already visible. Coverage: `submit_profiles_keep_harness_submit_policy_in_one_place`, `routed_trigger_submit_diagnostic_names_codex_enter_key`, and `simworld_jb_run_and_clear_share_codex_enter_submit_contract`.
- **JB Stale File Cache recovery now converges through editor IPC first (`#q7jm`).** The `live_prompt_drift_after_preflight` auto-recovery path no longer writes the adopted snapshot directly to disk while a JetBrains listener is active. It now sends component `op: "replace"` convergence patches through the editor, verifies ack-content against the adopted snapshot, and logs `transport=editor_ipc`; the direct disk write remains only as the no-listener `transport=disk_fallback` path. If the listener is active but editor convergence is unproven, recovery logs `[jbstalecache] auto_recovery_disk_write_blocked` and fails closed. JetBrains and VS Code patch appliers now honor explicit component `op` overrides ahead of marker `patch=append` / `mode=append`, so convergence can replace an append-mode exchange body without duplicating the response. Coverage: `try_auto_recover_live_prompt_drift_prefers_editor_ipc_when_listener_active`, `live_prompt_drift_convergence_patches_builds_replace_patch_for_exchange`, and editor-side `op` override wiring tests.
- **Codex auto-queue owner continuations report their harness submit mode.** The idle queue watch now logs `idle_queue_watch_drain` with the actual submit mode and file-scoped prompt hashes, while Codex Stop-hook queue blocks log `codex_stop_queue_continuation` proof for tracked-state and durable-marker sources. Codex live-pane deliveries use the shared text+Enter tmux submit path. This closes the drafted-but-not-submitted owner-continuation path from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Queue convergence IPC now carries the queue body (`#samplepcdrift`).** The
  preflight halt/drain convergence patch now sends the corrected `agent:queue`
  component body alongside `queue_auto` and canonical `queue:` frontmatter.
  This closes the live-editor gap where an open IntelliJ buffer could accept
  tag/frontmatter convergence yet keep stale queue lines and flush them back
  over the disk/snapshot repair, regenerating IPC drift on the next preflight.
- **Structured live-buffer-equals-disk marker carries proof fields
  (`#lvbremain`).** `visible_write_live_buffer_matches_disk` now logs
  `source`, `expected_len/hash`, `disk_len/hash`, `live_len/hash`, and
  `live_ts` when the editor live-buffer digest diverges from the merge base but
  exactly matches the current disk content. That makes the IntelliJ
  edit-during-finalize proof an anchored, self-contained `ops.log` line instead
  of a token that can be confused with `queue_diff_active_prompt_differs` prose.
  Regression `visible_write_reconcile_treats_editor_matching_disk_as_reconcilable_drift`
  now asserts the structured fields.
- **Structured `#s760` clear-decision gate verifier (`#ktw8`).** Added the
  built-in `s760_clear_decision_clear_true` ops-log verifier for destructive
  queue-turn `/clear` proof. It accepts only anchored
  `^[epoch] [s760] clear-decision ...` lines with `optIn=true`, numeric
  `pct >= threshold`, and `clear=true`; prose-only mentions inside
  `queue_diff_active_prompt_differs` remain pending, and structured false fires
  (`clear=true` below threshold or no-clear at/above threshold) fail closed.
  Regression coverage:
  `scan_s760_clear_decision_requires_anchored_clear_true_at_threshold`,
  `scan_s760_clear_decision_ignores_queue_diff_prose_only`,
  `scan_s760_clear_decision_fails_clear_true_below_threshold`,
  `scan_s760_clear_decision_fails_clear_false_at_threshold`,
  `gate_verify_s760_builtin_ignores_queue_diff_prose_only`, and
  `gate_verify_s760_builtin_auto_resolves_on_anchored_clear_true`.
- **08b cutover COMPLETE — removed the out-of-process writers and flipped all three authority
  defaults (removal rung).** This retires the gate ladders now that in-process hosting + ordered
  write queue + plugin read-only are the only behavior; **there is no rollback flag.**
  - **Write authority:** every editor-visible `.md` write routes through the session actor's single
    ordered write queue unconditionally. Removed `AGENT_DOC_WRITE_AUTHORITY`, the `WriteAuthorityGate`
    enum, `current_gate`, and the bare-`atomic_write` `off` bypass. `.agent-doc/` sidecar/snapshot
    writes and owner-thread-reentrant writes still take the raw path. The process-wide session-actor
    runtime lazily spawns a per-document owner thread on first write, so standalone `agent-doc write`
    serializes correctly without a running `start` session.
  - **Supervisor host:** `agent-doc start` always hosts the harness child through
    `supervisor::in_process::InProcessSupervisor`. Removed `AGENT_DOC_SUPERVISOR`,
    `supervisor_authority.rs`, and the out-of-process `session.wait()` host branch in `start.rs`. The
    external Unix-socket IPC boundary (CLI/editor callers) is unchanged.
  - **Plugin watch:** the JB plugin WatchService file-apply path is unconditionally read-only.
    Removed `AGENT_DOC_PLUGIN_WATCH` and the `active` path; `agent_doc_plugin_watch_readonly` always
    reports read-only (`watch_authority::plugin_watch_is_readonly`). The 0.2.159 plugin already calls
    this export, so no plugin rebuild is required — the cdylib refresh (`lib-install`) flips it live.
  - `make check` green; gate-flag unit/SimWorld tests rewritten to the unconditional end state.
  - **BREAKING CHANGE:** removes the `AGENT_DOC_WRITE_AUTHORITY` / `AGENT_DOC_SUPERVISOR` /
    `AGENT_DOC_PLUGIN_WATCH` rollback flags. Setting them now has no effect.
- **Demote the JetBrains plugin WatchService to read-only buffer reporting (`#dsqa` / `#pcp7` —
  08b cut-over residual phase 2).** New `AGENT_DOC_PLUGIN_WATCH` rollback flag
  (`watch_authority.rs`, mirroring the `#dav9` / `AGENT_DOC_SUPERVISOR` ladder):
  `active` (default = today's behavior) / `read-only` (demoted). At `read-only`, the JB
  plugin's autonomous NIO `WatchService` file-apply path no longer applies patches it
  observes under `.agent-doc/patches/` — the single controller-owned watcher (`#pcpc4`) plus
  the socket IPC command channel become the sole writer to the live editor buffer, killing the
  second-watcher race that produced `live_prompt_drift_after_preflight` /
  `ipc_socket_already_applied_live_buffer_diverged` even under in-process hosting (the `#dav9`
  swap moved *who hosts the child*, not *who writes the buffer*). The plugin reads the flag fresh
  via the new `agent_doc_plugin_watch_readonly` FFI export (the flag lives in the binary, so the
  operator opts in once on the `agent-doc` session and every IDE instance honors it without
  depending on the IDE's inherited environment); the export emits a structured
  `plugin_watch_readonly` `ops.log` marker so the cut-over is log-verifiable (`#q6js` /
  `#lvbremain`). **Default `active` is unchanged** — shipped users keep the WatchService applier
  until an operator opts in, and the gate fails safe back to `active` on an unrecognized value so
  a typo can never strand a live editor without an applier. The socket IPC apply path (the
  controller's writer arm into the editor) stays active in both modes. JB `pluginVersion`
  0.2.158 → 0.2.159. Coverage: `mode_parses_known_values_and_defaults_active`,
  `mode_unknown_value_falls_back_active`, `mode_is_readonly_only_at_demotion_rung`,
  `current_mode_reads_env_and_defaults_active`, `plugin_watch_readonly_default_active_returns_zero`.
  Remaining for `#q6js`/`#lvbremain`: the live operator drive (restart with
  `AGENT_DOC_PLUGIN_WATCH=read-only` + the 0.2.159 plugin installed, drive an edit-during-finalize
  cycle) so the agent can confirm zero-drift + the `visible_write_live_buffer_matches_disk` marker
  from `ops.log`.
- **Land the in-process supervisor hosting swap (`#dav9` / `pcpc5e1` authority rung).**
  `AGENT_DOC_SUPERVISOR=in-process` now actually hosts the harness child through
  `supervisor::in_process::InProcessSupervisor` instead of only logging the gate.
  `PtySession::take_child` hands the `portable-pty` child to the adapter, which
  reaps exits non-blockingly via `try_wait` (no reaper thread/channel), drives
  heartbeat + crash policy through its `tick` loop, and owns `kill`. `start.rs`'s
  `run_with_reap_policy` branches on `supervisor_hosts_in_process`: in-process
  mode hands off the child and drives the tick loop (honoring stop/restart/
  route-complete by killing once so the next tick reports the exit), while
  keeping the reader/writer/resize/auto-trigger/idle-watch plumbing and the
  Unix-socket IPC boundary; the outer restart loop still owns respawn (the
  adapter factory refuses an in-adapter respawn). **Default `off` is unchanged**
  — shipped users still block on `session.wait()` exactly as before; this is the
  dormant, flag-gated authority rung, not the live default flip. New coverage:
  `adopt_hosts_real_child_through_tick_loop`, `pty_supervised_child_reaps_real_exit_code`,
  `pty_supervised_child_kill_terminates_live_child`, `pty_supervised_child_monitor_rejects_stdin`,
  `kill_child_lets_tick_report_exit_without_halt`. Remaining: the live-gated
  default flip + out-of-process writer removal (`#q6js`), and the plugin
  WatchService demotion (`#dsqa`), both of which require live operator proof.
- **Fix UTF-8 char-boundary panic in closeout heuristics (`#heurpanic`).**
  `heuristics::has_quantified_remaining_work` computed a 30-byte look-back window
  start as a raw byte offset (`pos - 30`) and sliced `&lower[start..pos]`. When a
  response line placed a multibyte char (e.g. an em-dash `—`) so that `pos - 30`
  landed inside it, the slice panicked (`byte index … is not a char boundary`),
  crashing `finalize`/`session-check` (`rc=134`) on otherwise-valid responses and
  wedging closeout. The window start now rounds down with `floor_char_boundary`.
  Regression test `quantified_remaining_work_handles_multibyte_char_in_window`.
- **`#lvbremain` verification markers now reach `ops.log` (`#x9ds`).** Two of the
  marker emissions the `#lvbremain` ops.log scan expects were stderr-only and so
  always scanned 0. The cross-session-reject reject path (`claim.rs`) now also
  records its `[claim] cross-session-reject pane_id=.. pane_session=.. configured=..`
  marker to `ops.log` (it previously went only to stderr as the plugin-branch
  signal). The early-ack success path (`ipc_socket.rs`) now records a marker
  carrying the exact `early_ack_pending` predicate token to `ops.log` (the
  human stderr line "early-ack pending" is hyphenated and never contained the
  token); new testable `ipc_socket::early_ack_ops_marker`. The third marker,
  `run_clear_coalesce` (`#9adk`), is **not** a binary behavior — it is the
  JetBrains-plugin `InvocationCoalescer` (Run/Clear dedup) whose marker goes to
  `idea.log` and is operator-verified via the `#lvbatch` live batch, so there is
  no ops.log emission site to wire; the ops.log-scan expectation for it was a
  category error and is dropped. Tests `enforce_cross_session_claim_errors_on_reject`
  (asserts the marker reaches ops.log) and `early_ack_ops_marker_carries_predicate_token`.
- **Ops-proof auto-completion no longer reaps a same-cycle add (`#5b28` /
  `#opsproof-samecycle-add`).** A gated `agent:review` item (or `agent:backlog`
  item) added in the same `write`/`finalize` invocation via `--review-add` /
  `--pending-add` / `--pending-add-gated` could be opportunistically ops-proof
  auto-completed on the cycle it first appeared, when its text carried a
  completion marker plus a commit hash (e.g. a `#s760d` operator-verify gate that
  cites "Code shipped <commit>"). The existing `#opsproof-falsepos` guard compared
  against the on-disk snapshot, but in the finalize path the same invocation
  re-syncs that snapshot to include the new item, so the snapshot test could not
  tell a brand-new add from a pre-existing one. The add primitives now record the
  ids added this cycle in `cycle_state` (`pending_added_ids`), and the reap
  excludes them. `review_add` returns its assigned id; new
  `cycle_state::{record_pending_added_ids, pending_added_ids}`. Regression test
  `ops_proof_does_not_reap_same_cycle_added_gated_item`.
- **Transcript-token context-% pre-emptive `/clear` gate (`#s760c`).** The
  supervisor idle-queue watch now derives its pre-emptive `/clear` decision from
  the **real** harness context-usage % (cumulative session-transcript tokens ÷
  model context window, via the `#s760a`/`#s760b` `context_pct` source) instead of
  the exchange-size accretion heuristic. On each idle gap, when the default-off
  `agent_doc_queue_context_reset` opt-in is set, it locates the active Claude
  transcript (newest `*.jsonl` under `~/.claude/projects/<project-hash>/`), computes
  ctx%, emits the canonical `[s760] clear-decision optIn=.. threshold=.. pct=..
  clear=..` line to `ops.log`, and fires the tracked `/clear` only when
  `pct >= clear_threshold_for_doc` (`agent_doc_clear_threshold`, default 50). New
  pure, unit-tested `context_pct::{clear_decision, claude_projects_subdir,
  latest_claude_transcript}`. Fails safe per the destructive-`/clear` invariant: an
  unknown model, missing/empty transcript, or unsupported harness (Codex/OpenCode)
  yields `pct=None` and **never** clears; the compaction-after-clear safety case is
  preserved; everything stays behind the opt-in (default off). Operator live-verify
  of the destructive path on a throwaway opted-in doc remains `#s760d`. Plan:
  `tasks/agent-doc/plan-s760-transcript-ctx-clear.md`.
- **Opportunistic gated-review auto-verification (`#optverify`).** A gated `[/]`
  `agent:review` item can now carry a typed proof/disproof predicate so the binary
  auto-proves or auto-disproves it from `ops.log` markers during the normal
  preflight read — no dedicated live-verify session. Set it with
  `agent-doc backlog <FILE> set-verify <id> "verify=ops_log:<marker>;disproof=ops_log:<text>"`
  (or `--pending-set-verify id=<spec>` on `write`/`finalize`); the gate-set time
  is stamped automatically. The predicate persists inline as a
  `<!-- gate-verify verify=… disproof=… set_at=… -->` annotation (new pure
  `agent_doc_core::gate_verify` module). The scan is fail-safe: a proof marker at or
  after `set_at` with no disproof → `provable`; a disproof marker → `failed`
  (**disproof wins ties**); neither → `pending`; stale pre-`set_at` markers never
  count. Preflight surfaces a `gate_verify` result per predicate-bearing gated item
  and logs `optverify review=<id> status=<…>`. The `[/]→[x]` auto-transition is
  **opt-in and default off** — only when `agent_doc_gate_autoverify` is true
  (frontmatter, then project `.agent-doc/config.toml`) does a `provable` gate flip,
  staged to both the working tree and snapshot; otherwise the status is only
  surfaced and a human gate is never silently flipped. Spec:
  `specs/07-closeout-commands.md` (preflight section). Phases `#optv1`–`#optv4`.
- **Early-ack IPC activated (`#saevon`).** Flipped `EARLY_ACK_ENABLED false → true`
  in `ipc_socket.rs`: the sender now auto-tags live closeout `patch` messages with
  `early_ack: true`, so an early-ack-aware listener emits a `pending` ack the instant
  it receives the patch — before the blocking apply — decoupling the sender's liveness
  probe from plugin apply latency (root of the R2 false ack-timeout / degrade-vote
  class). The two-phase protocol was landed dormant and unit-tested in a prior cycle;
  this flip activates auto-injection. Skew-safe: older listeners ignore the unknown
  `early_ack` field and still get exactly the prior single terminal ack. Live
  verification (`#xkpf` / `#lvb-run`) greps `ops.log` for `[ipc-socket] early-ack
  pending emitted before apply` with a paired terminal ack and no `ack_timeout` /
  `false_success`. Test `early_ack_tagging_is_dormant_by_default` → renamed/inverted
  to `early_ack_tagging_is_active_for_patches`. `ipc_socket.rs`,
  `specs/process-topology.md`.

- **Consumed (struck) queue items are no longer recorded as dropped user edits
  (`#dropqueue-consumed-falsecount`).** `dropped_queue_prompt_lines_after_content_ours`
  counted only `content_ours` *active* queue prompts, so a queue item the user
  added this cycle that `content_ours` consumed (struck `~~…~~`) read as
  "dropped" — tripping the `#queue-user-edit-overwrite` guard with a false
  `session-check` INTERRUPTED on a correct closeout. The detector now counts
  consumed (`QueueEntry::Completed`) items toward coverage via
  `queue_prompt_texts_including_consumed`. This is the source-level counterpart
  to `#exch-intermix-falsedrop` (which fixed the auto-recovery gate). `write.rs`;
  regression test added.

- **`#exch-intermix` auto-recovery no longer fails closed on a false-positive
  dropped-prompt record (`#exch-intermix-falsedrop`).** `try_auto_recover_live_prompt_drift`
  bailed whenever the cycle recorded ANY `dropped_exchange_prompts` /
  `dropped_queue_prompts`. But that record compares the divergent IPC candidate
  against `content_ours`, so a queue item consumed (struck) this cycle is logged
  as "dropped" even though it survives in the adopted snapshot — stranding the
  response and forcing a manual `git checkout HEAD` / `reset --from-current` /
  `finalize --force-disk` recovery (hit live on the `#opsproof-falsepos`
  closeout: `dropped_queue_prompt_recorded count=3` blocked a snapshot that was
  the complete correct document). Recovery now bails only when a recorded
  dropped prompt is genuinely absent from the adopted snapshot; a consumed
  (struck) or preserved prompt still present no longer blocks it. The
  snapshot↔disk containment gate remains authoritative for current on-disk
  content. `write.rs`; unit + integration coverage added.

- **Ops-proof auto-completion no longer false-positives on cited dependency
  work or same-cycle adds (`#opsproof-falsepos`).** Preflight pending
  maintenance previously reaped an open backlog/review item as done whenever its
  text held a completion marker (`DONE`/`SHIPPED`/…) plus a commit hash and no
  blocker word — even when the marker only described already-landed *dependency*
  work and the item itself was still actionable. A freshly-added backlog item
  citing a prior commit could be auto-archived on the same cycle it was created.
  `classify_ops_proof_completion` now requires the marker to be an open item's
  own *leading status verb* (the status prefix before the first clause break);
  gated `[/]` items keep the marker-anywhere behavior. Pending maintenance also
  refuses to reap any item absent from the cycle-start snapshot (a brand-new
  same-cycle add). `preflight.rs`; SimWorld/unit coverage added.

- **08b document write-authority cutover gate ladder (`#pcpc5cut`, rungs
  `pcpc5a`–`pcpc5c`).** New `AGENT_DOC_WRITE_AUTHORITY` env gate routes the
  central `write::atomic_write` for editor-visible session documents up the 08b
  migration ladder (`specs/08b-single-process-control-plane.md`
  §"Document write and watch authority"): `off` (default — unchanged bare
  `atomic_write`, the rollback target), `shadow` (raw write unchanged, reports
  the would-route decision to `ops.log`), `dual-write`/`authority` (route the
  real write through the session actor's single ordered write queue,
  `write_queue::serialized_atomic_write`, serializing supervisor and
  agent-finalize writes at the root of the R6 self-race / exit-75 drift), and
  `removed` (reserved for supervisor/plugin-writer removal; routes like
  `authority` at this layer). A thread-local owner-scope re-entrancy guard
  (`write_authority::owner_scope_guard`, installed by
  `write_queue::run_serialized`) keeps the queue's inner `atomic_write` on the
  raw path so a routed write cannot deadlock the document's blocking mailbox.
  `.agent-doc/` sidecar writes are never routed. Default-off, opt-in, instant
  rollback by unsetting the env. The `removed` rung and the single-watcher /
  editor WatchService read-only demotion remain gated on live IntelliJ/Codex
  proof (review `#xkpf`).

- **VS Code reports full editor-buffer content to the live-buffer sidecar
  (`#f5d2` / review `#pcp6`).** The VS Code typing listener now calls
  `agent_doc_document_changed_digest_content` (full buffer text) instead of the
  len/hash-only `agent_doc_document_changed_digest`, matching the JetBrains
  `TypingTracker`. This completes the editor side of `#pcp6`: the binary FFI
  (`agent_doc_document_changed_digest_content`), content sidecar
  (`record_live_buffer_digest_content`), and `live_buffer_diverges_from_content`
  classifier already shipped, and the JB plugin already sent content — VS Code
  was the remaining digest-only reporter. With both editors sending content, a
  genuine unsaved editor edit (buffer ahead of disk) can be positively
  scope-classified by the reconcile guard instead of failing closed on the
  mtime heuristic. New `native.documentChangedDigestContent` binding + function.
  VS Code `0.2.25`. End-to-end live-editor verification of the classify-vs-
  fail-closed behavior remains gated on `#xkpf`.

- **Pre-emptive `/clear` decision predicate (groundwork) (`#s760` / review
  `#clear-opt-in-threshold`, phase 2).** Added the pure, unit-tested
  `ContextResetDecision.shouldClearBeforeDispatch(contextUsagePct, clearThreshold,
  optIn)` that pins the phase-2 semantics: clear before a Run Agent Doc dispatch
  only when the operator opted into `agent_doc_queue_context_reset` AND the live
  Claude Code context-usage % is known and at/above the configured
  `clear_threshold` (an unknown % or a 0 threshold never clears — fail-safe).
  This is **groundwork only and not yet wired**: the two genuinely live-gated
  pieces — reading the live context-usage % from the Claude Code pane, and
  triggering the clear in `SubmitAction`'s pre-dispatch path — plus their live
  in-IDE verification remain gated on `#xkpf`. JB plugin `0.2.158`.

- **JetBrains plugin coalesces rapid-fire Run Agent Doc / Clear invocations
  (`#9adk` / review `#console-input-accumulation`).** A per-document,
  per-action `InvocationCoalescer` guard skips a second invocation of the same
  action for the same document within a short window (default 750ms), so a
  rapidly re-fired action (auto-loop tick racing a manual click, or a double
  key-chord) can no longer stack a duplicate `/agent-doc` or `/clear` keystroke
  at the terminal layer below the route/queue dedup. `SubmitAction` keys on
  `run:<routeKey>` and `ClearSessionContextAction` on `clear:<routeKey>`, so a
  deliberate Run-then-Clear for the same doc is NOT coalesced — only same-kind
  rapid re-fires collapse. The coalesce decision is pure given `nowMillis`
  (unit-tested in `InvocationCoalescerTest`); live in-IDE behavioral
  verification of rapid-fire coalescing is gated on `#xkpf`. JB plugin
  `0.2.157`.

- **Early-ack IPC protocol landed (dormant) (`#ipc-early-ack` / `#saev`,
  Phase 2).** The socket IPC handshake now supports a two-phase ack:
  `ipc_socket::start_listener` emits a `pending` ack the instant it receives a
  patch that opted in (`"early_ack": true`) — before the blocking apply — and
  `send_message` reads acks in a loop, treating a `pending` ack as liveness-only
  (it keeps waiting for the terminal ack and never returns a false success).
  This decouples the sender's liveness probe from plugin apply latency (the R2
  false-timeout / degrade-vote race). New `AckClassification::Pending`;
  `message_requests_early_ack` / `early_ack_line` / `early_ack_tagged_message`
  helpers; the early ack is owned by the Rust transport, so no plugin/callback
  change is needed (ffi listener inherits it). Backward/skew-safe: senders that
  do not set `early_ack` get a single terminal ack exactly as before, and the
  read loop runs once. Activation is gated by `EARLY_ACK_ENABLED` (off — the
  sender does not yet tag patches) pending end-to-end verification under live
  typing load (`#xkpf`). Tests in `ipc_socket.rs`
  (`classify_ack_treats_pending_status_as_pending`,
  `send_message_handles_early_then_terminal_ack`, dormancy + flag-parse);
  spec `specs/process-topology.md` R2. Plan:
  `tasks/agent-doc/plan-ipc-transport-reliability.md`.

- **Editor plugins render a choice dialog on a cross-session claim reject
  (`#jb-claim-cross-session`, plugin half).** Building on the binary
  `cross-session-reject` marker, the JetBrains "Claim for Tmux Pane" action
  (`ClaimAction`, plugin 0.2.156) and the VS Code claim command
  (`crossSession` + `extension.ts`, 0.2.24) now parse the marker
  (`parseCrossSessionReject`) and prompt **Force Claim** (`claim --force`) /
  **Switch Project Session** (`session set <pane_session>` then re-claim) /
  **Cancel** instead of surfacing the raw exit-1. Unit coverage:
  `ClaimActionTest` (JB) and `crossSession.test.ts` (VS Code), each covering
  ordered/unordered/missing-field/no-marker parsing. Live click-through
  verification against a genuinely cross-session pane remains gated on a live
  IntelliJ/VS Code session (`#xkpf`). Plan:
  `tasks/agent-doc/plan-jb-claim-cross-session.md`.

- **Agent prompts now expose a stable prompt-cache prefix
  (`#pcache-boundary`).** Direct `run` prompts and Codex owner-pane queue
  continuations now render through a shared prompt-cache boundary helper: stable
  response contracts come before the boundary, while volatile diff, document,
  queue-head, status, and compaction/accretion context stays after it. Tests
  assert that queue heads, file paths, prompt-target sections, and accretion
  diagnostics remain below the boundary so prompt-cache fingerprints do not churn
  on ordinary session state changes.

- **`claim` emits a structured cross-session-reject marker for editor plugins
  (`#jb-claim-cross-session`).** On a cross-session **Reject** (target pane lives
  in a tmux session other than the configured project session, configured session
  alive, no `--force`), `claim` now prints a stable machine-readable line to
  stderr before the human bail: `[claim] cross-session-reject pane_id=<id>
  pane_session=<session> configured=<session>` (`CROSS_SESSION_REJECT_MARKER` +
  `cross_session_reject_marker` with stable field order). Editor plugins
  (JetBrains "Claim for Tmux Pane", VS Code claim command) can branch on this
  marker to render a Force claim / Switch project session / Cancel dialog instead
  of surfacing the raw exit-1 message. The human bail text is preserved unchanged
  for terminal use. Unit coverage in `claim.rs`
  (`cross_session_reject_marker_carries_stable_fields`); contract in
  `specs/07-session-tmux-commands.md`. The plugin choice dialog itself remains
  gated on live-IntelliJ/VS Code verification (backlog `#4wxr`).

- **A newly-canonical tmux session closes the superseded session
  (`#canonical-session-close`).** `agent-doc session set <name>` now closes the
  old session and prunes it from the model after the new session becomes
  canonical, instead of leaving registered panes spanning two tmux sessions
  (the recurring "session drift: registered panes span 2 tmux sessions" warning).
  After migrating the `agent-doc` + `stash` windows, `set` calls the new
  `resync::close_superseded_session(old)`, which closes the old session **only**
  when it is a pure agent-doc orphan: every remaining window is agent-doc-managed
  (`agent-doc`, `stash`, `stash-*`) and no pane runs a live agent process. A
  session holding any unmanaged user window or a live agent is preserved, so the
  cleanup never destroys unrelated work. New tmux helpers `list_window_names`,
  `list_session_panes`, and `kill_session` in `tmux-router`; live-tmux coverage in
  `resync.rs` (kills a pure orphan, preserves a user-window session, treats an
  already-gone session as closed); spec in `specs/07-session-tmux-commands.md`.

- **Overlay-as-merge-base is order-stable for the append case
  (`#ipc-drift-order-stable-merge`).** Verified the suspect overlay merge-base
  path (`5fd64b26`) cannot reverse a new `### Re:` response's lines or hoist it
  above the prior committed response when a foreign supervisor appends to the
  document tail mid-generation. The overlay source is used as the merge base
  only when its markdown projection is byte-identical to the cycle baseline
  (otherwise the merge falls back to the baseline text), so the derived base can
  never reorder committed exchange content. Added an end-to-end test
  (`overlay_merge_base_is_order_stable_for_exchange_append`) driving the real
  `crdt_merge_base_state` overlay path for an exchange-with-response document,
  fixed a stale client-id comment in `crdt::merge`, and documented the invariant
  in the document-format spec.
- **Concurrent-supervisor superproject write-back is serialized
  (`#ipc-drift-writeback-serialize`).** The git-dir-scoped commit lock already
  serializes the staging/commit critical section per resolved git dir; this is
  now verified for the cross-supervisor superproject case the drift report hit
  (a sampleorders submodule session's `submodule pointer` commit
  interleaving with another session's superproject commit). A submodule
  document's parent-gitlink update and a sibling superproject-root document's
  closeout contend on the same superproject lock, so two supervisors writing
  back to one superproject cannot interleave partial commits or strand a
  captured response outside `HEAD`. Added a deterministic concurrent test
  (`superproject_writeback_serializes_pointer_update_and_root_commit`) plus the
  closeout-spec invariant.
- **Clean CRDT merges reconcile foreign disk writes instead of stranding the
  response (`#ipc-drift-visbuf-reconcile`).** When the on-disk document diverges
  from the merge input but the live editor buffer does *not* (a foreign
  agent-doc supervisor appended mid-generation, not a pending user edit), the
  template/stream write paths now re-read the fresh disk content
  (`visible_write_disk_drift_reconcilable`), re-merge the captured response
  against it, and retry — bounded to a few attempts — rather than failing closed
  with "visible editor buffer differs" and leaving the captured response outside
  HEAD (the `stuck_captured_cycle` symptom). A genuine unsaved editor-buffer edit
  still fails closed. Only persistent drift past the attempt bound falls back to
  the old fail-closed behavior.
- **Route-owned queued reruns no longer write `agent:queue auto`.** Busy
  dispatch-only reroutes now create or update a plain `agent:queue`, strip a
  legacy `auto` attribute from any touched queue tag, and use `queue_active:
  true` / start fences for activation. Editor diagnostics accept both the new
  `active agent:queue` wording and older `agent:queue auto` output. Active
  session drift checks now ignore ordinary post-exchange scratch/comment edits
  after commit while still interrupting on real component edits. Tests:
  `route_enqueue_dispatch_prompt_creates_visible_plain_queue_and_snapshot`,
  `route_activates_existing_inactive_auto_queue_head_as_plain_queue_for_busy_deferral`,
  `route_enqueue_dispatch_prompt_supersedes_single_auto_queue_prompt`,
  `session_check_ignores_active_session_post_commit_comment_only_drift`, and
  JetBrains `TerminalUtilTest`.

- **Queue slash commands now run as commands after the current turn.** Active
  queue heads such as `/clear` or `/model sonnet` are classified in the managed
  idle-queue drain path, submitted literally to the owner pane at the next idle
  prompt, consumed from `agent:queue`, committed, and then the remaining queue
  resumes. The idle-queue watcher now also waits for the hook-owned turn-active
  marker to clear before submitting queued work, so a visually idle prompt cannot
  race ahead of the full Stop/idle boundary; queued `/clear` and context reset
  clears use the supervisor's gate-exempt clear submit path instead of generic
  prompt injection. Codex Stop-hook and `session-check --codex-final-gate`
  diagnostics now name these heads as queued slash commands instead of prompts to
  answer. Direct preflight, plan, and run synthetic active-queue-head paths now
  keep slash-only heads command-only as well: surrounding whitespace is trimmed
  for slash classification, no `preflight_started` response cycle opens, no
  prompt targets or repo actions are planned, and owner-pane self-invocation
  diagnostics tell Codex to let the supervisor submit the command instead of
  answering/finalizing it in `agent:exchange`. Tests:
  `parse_slash_commands_trims_surrounding_whitespace`,
  `preflight_does_not_open_cycle_from_active_queue_slash_command`,
  `build_plan_treats_active_queue_slash_command_as_command_handoff`,
  `active_queue_prompt_diff_ignores_slash_command_head`,
  `owned_pane_queue_handoff_diagnostic_uses_supervisor_for_slash_command`,
  `queue_command::tests::*`,
  `idle_queue_drain_waits_for_turn_status_idle_even_with_visible_prompt`,
  `idle_queue_context_reset_waits_for_turn_status_idle`,
  `auto_trigger_clear_command_bypasses_dispatch_gate_and_submits_enter`,
  `idle_queue_drain_payload_submits_literal_clear_command`,
  `idle_queue_drain_payload_submits_any_literal_slash_command`,
  `complete_idle_queue_slash_command_head_consumes_and_commits`,
  `stop_blocks_clean_closeout_when_auto_queue_has_clear_command`, and
  `stop_blocks_clean_closeout_when_auto_queue_has_generic_slash_command`.

- **Exchange slash commands now enter the same after-turn command path.**
Routed `agent:exchange` prompts whose pending text is a literal slash command
such as `/clear`, with or without a `❯` prompt prefix, are copied into
`agent:queue auto` as an unpinned literal command head, then the managed
idle-queue supervisor submits and consumes them after the current turn instead
of reopening agent-doc and answering them as prose. Tests:
`classify_prompt_bearing_changes_promotes_bare_slash_command_to_prompt_target`,
`route_enqueue_bare_exchange_slash_command_for_idle_drain`, and
`route_enqueue_exchange_slash_command_keeps_literal_head_for_idle_drain`.

- **Actor pane binding now recovers cross-document aliases.** Route/start actor
store writes atomically close and clear the displaced document's pane binding
before storing the incoming owner. This prevents editor navigation to files such
as `lazily-rs.md` from leaving that document pointed at the previous
`agent-doc-bugs2.md` pane after recovery. `session status` no longer treats a
closed actor's stale pane as live evidence, and `session doctor --repair` clears
old closed actor pane projections. Tests
`binding_a_pane_evicts_other_documents_bound_to_it`,
`sessions_projection_removes_displaced_cross_document_owner`, and
`closed_actor`.

- **Free-text queue consumption now requires exchange history.** Session-check
  no longer treats a sidecar response hash or binary consume marker as sufficient
  proof that a preflight free-text queue head was answered. If the head is gone
  from `agent:queue`, the committed `agent:exchange` must contain the response or
  queue-prompt echo, otherwise `#lr-queue-patchback-miss` fails closed. Tests:
  `free_text_queue_head_guard_fires_when_binary_consume_lacks_response` and
  `free_text_queue_head_guard_passes_with_committed_response_echo`.

- **Codex idle queue drain no longer reinvokes the owning pane.** The
  supervisor idle-queue watch now drains Codex `agent:queue auto` heads by
  injecting an in-owner-pane continuation instruction instead of the recursive
  `agent-doc <file>` trigger. Claude and OpenCode keep their configured trigger
  command. This keeps JetBrains `Run Agent Doc` prompts queued behind a busy
  Codex owner from stalling on the recursive-direct-invocation guard once the
  owner goes idle.

- **`agent-doc focus` is the fast pane handoff by default.** The default focus
  path now has the former editor fast-focus behavior: it selects an already
  visible pane immediately, defers stash surfacing to `sync --no-autostart`, and
  does not perform additive promotion work in the foreground. The previous
  synchronous promote-and-select behavior is still available for manual use as
  `agent-doc focus <file> --blocking` or `--synchronous`. Current JetBrains and
  VS Code tab selection now call plain `agent-doc focus <file>` with a short
  editor-side timeout and leave slow/missing-pane work to the debounced
  reconciler. This keeps navigation to documents such as `lazily-rs.md` from
  letting a long-running CLI focus attempt delay the UI handoff. Bumped the
  JetBrains plugin build version to `0.2.153` and the VS Code extension version
  to `0.2.23`.

- **Structured overlay CRDT is the merge-base authority
  (`#md-ast-crdt-merge-base`).** Template/CRDT merge paths now derive their
  text CRDT base from the structured `.overlay.yrs` sidecar when its markdown
  projection matches the active cycle baseline. If the overlay sidecar is
  absent, corrupt, or stale relative to the explicit baseline, the merge falls
  back to the baseline text and logs the fallback reason. Tests
  `crdt_merge_base_state_prefers_matching_overlay_projection` and
  `crdt_merge_base_state_falls_back_when_overlay_projection_is_stale`.

- **Safe-passive sync pre-locks the pane handoff.** `sync --no-autostart
  --focus <file>` now selects a live local actor projection before waiting on
  `.agent-doc/sync.lock`, so a contended reconcile no longer gates the visible
  pane switch. When no local actor record exists, sync tries skip-wait pane
  provisioning through nonblocking startup locks and defers stale/dead/blocked
  records to the existing locked guard path. Tests
  `safe_passive_sync_focuses_local_projection_when_sync_lock_is_contended` and
  `try_startup_lock_reports_busy_without_waiting`.

- **Session-isolated self install.** `agent-doc self-install` now installs the
current committed checkout from a temporary sibling git worktree, preserving
relative Cargo path dependencies like `../agent-kit` while avoiding dirty files
from concurrent sessions in the shared `src/agent-doc` checkout. It runs
`cargo install --path .`, builds the release cdylib, installs it with the
existing atomic `lib-install` path, and removes the worktree unless
`--keep-worktree` is set. Test
`isolated_worktree_uses_committed_head_not_dirty_checkout`.

- **JetBrains Run Agent Doc recognizes active-turn skip-wait refusals.** The
JetBrains route-failure classifier now maps the route core's
`pane is busy on an active ... turn` dispatch-only refusal to the immediate
still-running notification instead of the persistent route-failure path, so the
`#jb-run-agent-doc-busy-active-turn-stall` skip-wait fix is visible to IDE
users. Test `active-turn skip-wait route refusal is reported as still running
immediately`.

- **JetBrains File Cache Conflict keeps visual highlighting fresh.** The
JetBrains plugin now reschedules `VisualHighlighterManager` on the File Cache
Conflict pending, Cancel, accepted/reload, and deferred-patch-applied paths so
agent-doc markdown visual tokens are reapplied after the IDE resolves the
dialog. Test
`file cache conflict path refreshes visual highlighters`.

- **JetBrains cold-open tabs apply agent-doc visual highlighting.** The
JetBrains plugin now reschedules `VisualHighlighterManager` from
`FileEditorManagerListener` file-open and selection-change events so markdown
files loaded from disk without an existing IDE buffer receive agent-doc visual
tokens instead of falling back to plain theme markdown styling. Test
`cold opened markdown files refresh visual highlighters`.

- **Preflight/plan propose semantic completion matches.** The shared
`tsift-memory` session-memory path now exposes advisory semantic completion
candidates for open backlog/review items and free-text queue prompts that are
highly similar to done-state memory events. `preflight` emits
`semantic_completion_match` warnings and `plan` includes the same warning text;
the signal is proposal-only and does not mark work done. Tests
`semantic_completion_matches_done_archive_for_free_text_queue_prompt` and
`build_plan_warns_on_semantic_completion_match_for_free_text_queue`.

- **Preflight auto-completes deterministic ops-proof tracked work.** During
pending maintenance, active `agent:backlog` and `agent:review` items with
explicit completion markers plus commit or successful-CI proof are promoted to
done, removed from the active surface, archived, recorded in cycle state, and
logged as `auto_complete_ops_proof`. Blocker language such as partial,
remaining, reopened, deferred, false-closeout, or follow-up work keeps items
active. Test `pending_maintenance_auto_reaps_ops_proof_done_items`.

- **Preflight reaps stale active mirrors for archived done ids.** During pending
maintenance, active `agent:backlog` and `agent:review` items whose ids already
exist in inline `agent:done` or the configured external `agent:done archive=...`
are removed from the live tracked-work surface without appending duplicate done
archive entries. Queue maintenance continues to exclude those ids from
backlog-to-queue sync and now has explicit external-archive strike coverage.
Tests
`pending_maintenance_reaps_inline_done_backlog_and_review_mirrors`,
`pending_maintenance_reaps_external_done_archive_backlog_and_review_mirrors`,
and `run_queue_maintenance_excludes_external_archive_done_ids`.

- **Codex installs now register the agent-doc MCP server.** `agent-doc skill
install --harness codex` writes `[mcp_servers.agent-doc]` into
`.codex/config.toml` and the Codex Stop hook prefers the
`agent_doc_preflight` / `agent_doc_plan` / `agent_doc_finalize` /
  `agent_doc_session_check` continuation path when that server is configured,
  while preserving the existing in-pane CLI fallback for runs without MCP.

- **Session clear ignores its own drafted control command.** Codex protected
  prompt detection now treats `agent-doc session clear`, `interrupt-clear`,
  `stop`, and restart control lines as operator commands instead of unsaved
  drafted prompt text, so the clear command no longer blocks itself. Ordinary
  drafted prompt text remains protected. Tests
  `protected_prompt_input_reason_ignores_agent_doc_session_control_commands`
  and `protected_prompt_input_reason_keeps_agent_doc_non_control_text_protected`.

- **Runtime snapshots persist the structured markdown-overlay CRDT
  (`#md-ast-document-model`).** Template/CRDT write, stream, IPC fallback,
  socket-ack repair, compact, commit-refresh, reset/rebuild, and closeout
  recovery paths now save a structured `.agent-doc/crdt/<hash>.overlay.yrs`
  sidecar from the same markdown snapshot as the legacy text `.yrs` merge
  state. The overlay projection is now the preferred merge base when it matches
  the active cycle baseline, while the legacy state remains for older binaries
  and editor plugins. Tests
  `document_crdt_save_persists_legacy_and_overlay_state` and
  `ensure_initialized_migrates_after_move_with_existing_session`.

- **Preflight queue dedup is node-keyed, not text-keyed
  (`#md-ast-document-model`).** The active queue cleanup pass now delegates
  duplicate cleanup to the markdown-AST mutation layer and only removes duplicate
  durable queue node keys. Repeated `do [#id]` or free-text prompts remain
  executable queue intent instead of being collapsed by raw prompt text. Test
  `preflight_preserves_intentional_duplicate_tracked_queue_prompt`.

- **Icebox queue sync no longer auto-promotes parked work.** `agent:icebox`
  may still be sorted by priority and individual icebox items can still opt into
  queueing with per-item enqueue markers, but a component-level `queue`
  attribute on `agent:icebox` now warns and does not populate `agent:queue`.
  A drained queue plus drained backlog remains terminal until work is explicitly
  moved to backlog, queued manually, or marked for enqueue. Tests
  `run_queue_maintenance_does_not_sync_icebox_into_empty_queue` and
  `misplaced_component_attr_warning_flags_queue_sync_attr_on_icebox`.

- **No-response active-head guard checks only the current queue head.** A stale
  no-response/reap-only cycle no longer blocks session closeout just because
  later `do [#id]` queue items still exist in backlog while a free-text prompt
  sits ahead of them. The guard now compares recorded ids only with the first
  live queue prompt. Test
  `no_response_active_queue_head_passes_when_later_do_item_is_not_current_head`.

- **Pending shadow guard ignores exchange transcripts.** The shadow-backlog
  detector no longer treats checklist or ordered-list `[#id]` lines inside
  `agent:exchange` response history as live pending shadows. Completed items
  archived elsewhere, such as lazily-rs `#ipc1`, no longer block
  `session-check` just because an earlier response listed next steps.

- **Missing-response recovery trusts committed exchange bodies without capture
  metadata.** `session-check` now treats any committed `agent:exchange`
  `### Re:` body as sufficient proof that a missing-response closeout has been
  repaired, even when the stale queue-drain cycle lost `capture_id` /
  `response_sha256` metadata. This lets `agent-doc write --commit` recovery
  clear interrupted sessions like `tasks/software/lazily-rs.md` instead of
  re-failing forever. Test
  `committed_without_response_body_guard_passes_recovered_exchange_body_without_capture_metadata`.

- **Idle queue stale-busy recovery preserves same-head dedup.** The supervisor
  idle-queue watcher no longer clears `last_dispatched` while reconciling a
  stale busy actor over an idle pane. This stops a stuck active head from
  repeatedly injecting `agent-doc <FILE>` after each reconcile tick, while still
  allowing dispatch when the head drains or advances. Test
  `stale_busy_reconcile_preserves_already_dispatched_head_dedup`; spec
  `specs/supervisor.md`.

- **Completed queue items can be marked in place.** Explicit done IDs now mark
  matching `agent:queue` prompts completed even when the matching queue item is
  not part of the active contiguous head-consumption range. This preserves the
  current head while striking opportunistically completed queued work in both the
  document and snapshot. Tests
  `done_id_marks_later_queue_prompt_completed_without_consuming_head` and
  `done_id_marking_ignores_already_completed_queue_prompt`; spec
  `specs/07-orchestration-commands.md`.

- **Queue overwrite guard tracks free-text queue items.** The
  `#queue-user-edit-overwrite` detector now compares parsed `agent:queue`
  prompt entries by count instead of relying on prompt-target diff
  classification, so user-added free-text queue bullets adjacent to the current
  head are recorded before a `content_ours` IPC adoption can silently delete
  them. Tests
  `dropped_queue_prompt_lines_after_content_ours_captures_adjacent_free_text_items`,
  `..._empty_when_items_are_owned`, and `..._counts_duplicate_user_items`.

- **Backlog queue priority visibly annotates promotions.** A backlog
  component carrying both `queue` and `priority` now triggers queue priority /
  auto-DAG ordering for the synced `agent:queue` even when the queue marker
  itself has no `priority` token. Automatically promoted queue prompts are
  annotated with `:round_pushpin:`, while priority route dispatches are inserted
  with the operator `:pushpin:` marker and dedupe against bare equivalents.
  Tests `run_queue_maintenance_backlog_queue_priority_sorts_and_marks_promoted_item`
  and `route_enqueue_priority_dispatch_*`; specs `specs/pending-system.md`,
  `specs/07-orchestration-commands.md`.

- **Session memory retrieval (`#agent-doc-memgraphrag-retrieval`).**
  `agent-doc memory index/search` now indexes current session tracked work
  (`agent:backlog`, `agent:review`, `agent:icebox`, `agent:done` including
  repo-relative `.done.md` archives) plus live exchange response summaries into
  `.tsift/memory.db` through the shared `tsift-memory` library crate. Search
  combines persisted memory with the current document and ranks locally for
  already-tracked / already-fixed dedupe checks without embedding tsift's heavy
  codebase index in the per-cycle hot path. Tests `memory_cmd::*`; specs
  `SPEC.md`, `specs/07-core-commands.md`.

- **Per-item enqueue markers populate `agent:queue` (`#queue-enqueue-action`).**
  Open backlog/icebox/pending items containing `:inbox_tray:`, `/enqueue`, or a
  Markdown-decorated `enqueue` token such as `**enqueue**` now append `do [#id]`
  to `agent:queue` without requiring the whole component to carry a `queue`
  attribute. The path is idempotent, excludes gated/done/unmarked items, works in
  both preflight and `agent-doc queue sync`, and lets explicit markers bypass the
  plain active-loop fresh-item hold. Tests
  `active_enqueue_item_ids_returns_marked_open_items`,
  `run_queue_maintenance_enqueue_marker_populates_queue_without_backlog_attr`,
  `collect_backlog_queue_sync_reads_enqueue_markers_without_attr`, and
  `sync_accepts_enqueue_marker_without_queue_attr`. Spec
  `specs/07-orchestration-commands.md`.

- **Watch daemon emits node-keyed document events (`#md-ast-realtime-watcher`).**
  The markdown AST crate now exposes `events::diff_node_events`, producing
  insert, remove, replace, move, strike, and unstrike events keyed by the same
  semantic node ids used by mutations and IPC patches. The watch daemon seeds a
  per-file node snapshot for watched session documents and logs `document_node_events`
  JSON batches on subsequent file changes, giving realtime enqueue/follow-up work
  a stable node-keyed event stream. Tests
  `diff_node_events_reports_insert_with_anchors`,
  `diff_node_events_reports_strike_by_stable_node_key`,
  `diff_node_events_reports_reorder_without_text_matching`, and
  `update_node_snapshot_emits_node_keyed_events_after_seed`.

- **Markdown-AST IPC patches are node-addressed (`#md-ast-ipc-node-patches`).**
  Queue closeout now derives semantic occurrence node keys for live queue items
  and strikes non-draining queue heads through the AST mutation layer, so
  intentional duplicate prompt text is not consumed by text matching. IPC
  payloads retain legacy component `patches` while adding explicit `op` /
  `node_id` metadata and a `node_patches` array for item-level insert, strike,
  unstrike, replace, remove, and move operations. JetBrains and VS Code patch
  DTOs accept the new fields. Tests
  `queue_consume_uses_node_keys_to_preserve_duplicate_prompt_identity`,
  `build_ipc_node_patches_json_tracks_strike_and_insert_by_node_key`,
  `build_ipc_node_patches_json_tracks_reorder_without_text_matching`,
  `build_ipc_patches_json_seeded_boundary_is_stable_across_rebuilds`, and
  `parsePatchJson preserves node-addressed component patch fields`. Specs
  `specs/02-document-format.md`, `specs/07-orchestration-commands.md`.

- **Deprecated `queue_active:` frontmatter line no longer gets stuck in a
  document (`#queue-active-deprecated-line-stuck`).** `merge_queue_state`/`write`
  already drop the legacy `queue_active:` line when they re-serialize, but a doc
  whose hot path preserves frontmatter byte-precisely never re-serializes it, and
  the diff layer classifies any `queue_active:` line as managed state — so its
  removal reads as a no-op and is never committed. The legacy line stayed in the
  file forever even after the canonical `queue:` control took over (operators saw
  a persistent `queue_active: true` that "couldn't be removed"). Preflight repair
  now drops it once, byte-precisely, directly on disk + snapshot via
  `strip_deprecated_queue_active_line` — but ONLY when the canonical `queue:`
  control is present, so no queue state is lost. Idempotent; legacy-only docs
  (no `queue:`) keep their line. Tests
  `strip_deprecated_queue_active_line_drops_legacy_when_canonical_present` +
  `..._keeps_legacy_without_canonical`.

- **Session-check self-heals a late-IPC committed-response over-application
  (`#late-ipc-patch-response-uncommitted`).** When a wedged/slow IPC listener
  applies a stale queued patch after the cycle already committed, it re-adds a
  duplicate `### Re:` block to the working tree even though the real response is
  in HEAD — and session-check previously reported an unrecoverable interruption
  that stalled the `agent:queue` auto-loop. The mutating session-check
  entrypoints (`enforce_clean_closeout` on the `finalize` boundary,
  `run_with_options` for direct-exec `agent-doc session-check`) now restore the
  committed HEAD in place via `self_heal_late_ipc_overapplication` (logs
  `late_ipc_response_overapplication_self_healed`) instead of bailing — the same
  remediation `preflight` applies, taken only when `detect_late_ipc_response_overapplication`
  proves it safe. The read-only `inspect*` family stays non-mutating. Test
  `enforce_clean_closeout_self_heals_late_ipc_overapplication`. Spec
  `specs/07-closeout-commands.md`.

- **Queued IPC fallback patches carry a generation token for late-apply fencing
  (`#late-ipc-patch-duplicate-stall`).** A boundary-reposition IPC that times out
  queues a durable fallback patch file; a wedged/slow applier could apply it
  minutes late — after the cycle already committed — re-materializing a duplicate
  `### Re:` block and stalling the auto-queue. The write side already fences a
  fresh send for an already-committed cycle (`try_ipc`) and reposition-only
  patches already carry no response body, so `queue_file_ipc_reposition_boundary`
  now also tags the queued `.agent-doc/patches/<hash>.json` with `cycle_id` and a
  `baseline_hash` of the live doc it targets, giving the asynchronous applier the
  same generation token to drop a superseded patch. Test
  `queued_file_reposition_patch_carries_generation_token`. The plugin-side
  consumption of the token (`PatchWatcher` apply fence) and listener de-wedge are
  tracked follow-ups; spec `specs/07-closeout-commands.md`, plan
  `tasks/agent-doc/plan-late-ipc-patch-duplicate-stalls-queue.md`.

- **Free-text queue head consumed despite a prompt-prefix flip on an answered
  prompt (`#free-text-head-consume-genuine-not-struck`).** The answered-free-text-head
  decision (`cycle_answered_foreign_exchange_prompt`) diffs the *normalized
  snapshot* baseline against the *live* editor buffer, and the buffer preserves
  `❯` prompt prefixes on already-answered prompts that the snapshot normalized to
  the bare form. A pure `do x` → `❯ do x` prefix flip surfaced as an added
  `+❯ …` diff line and was wrongly read as a *new foreign* exchange prompt,
  blocking the free-text head strike and treadmilling the auto-loop (live repro:
  the axocoatl evaluation head answered by a committed `--stream` finalize yet
  never struck). Fix: a `❯` added line counts as foreign only when its
  normalized text is absent from the baseline entirely; a prefix flip on a
  prompt that already existed (in `❯ X` or bare `X` form) is no longer foreign.
  Genuinely new unrelated `❯` prompts still keep the head queued. Added
  `AGENT_DOC_DEBUG_QUEUE_CONSUME` env-gated instrumentation that logs each `❯`
  added line and its classification. Regression test
  `free_text_head_struck_despite_prompt_prefix_flip_on_answered_prompt`. Spec:
  `specs/07-closeout-commands.md`.

- **Retain-don't-reread runbook nudge in the shared SKILL source.** The
  `## Runbooks` section now instructs agents to read each runbook at most once
  per session and reuse the in-context copy instead of re-reading, re-opening
  only after a content change or compaction. Targets measured redundant runbook
  reads (heaviest on per-cycle shell harnesses such as Codex re-`cat`ing
  closeout runbooks every cycle); renders to all harness surfaces via
  `skill.rs`. Contract-safe (no per-harness divergence). Re-measure Codex's
  redundant-read rate before considering harness-level read-once enforcement.

- **No-change short-circuit detects committed no-response repair cycles
  (`#jb-codex-nochange-after-repair`).** When `agent-doc run` finds no
  document/snapshot diff but the latest cycle was a `Committed` no-response
  bookkeeping-only closeout (repair/reap following an abandoned recursive
  invocation or failed run), the classifier now returns an `Abnormal` verdict
  with a typed diagnostic naming the cycle id, last event, and recovery command,
  instead of plain "Nothing changed since last run". This prevents JB `Run Agent
  Doc` from showing a misleading "No changes were detected since the last run"
  after a repair cycle that followed a Codex recursive self-invocation
  abandonment. A committed cycle with a response body remains `Clean` regardless
  of bookkeeping. Spec: `specs/07-closeout-commands.md` (#jb-codex-nochange-after-repair).

- **Dedicated `blocked_in_interactive_substate` route guard reason (`#snrun`).**
  When a dispatch-only `Run Agent Doc` reopen is refused because the live pane is
  stuck in an interactive shell substate (`reverse-i-search` / history search)
  rather than a dispatch-ready composer, the fail-closed path now emits the
  dedicated `RoutedReopenGuardReason::BlockedInInteractiveSubstate`
  (`prompt_ready_barrier` FlowEvent) and a stage-specific error ("blocked in an
  interactive terminal substate") instead of the generic
  `dispatch_only_busy_actor_not_ready`. Pure helpers
  `routed_reopen::is_interactive_shell_substate_reason` /
  `dispatch_only_blocked_guard_reason`; regression
  `interactive_substate_gets_dedicated_guard_reason`. FlowCore hot-path token
  budget bumped (route.rs `guard_` 7→12, audited). The interactive-substate
  detection + multi-source dispatch-start proof were already implemented; this
  closes the remaining deterministic diagnostic refinement.
  Plan: `tasks/agent-doc/plan-run-agent-doc-snappy-auto-remediation.md`.

- **JB plugin logs the exact `setText` payload at debug level
  (`#jb-settext-payload-log`, plugin 0.2.145).** `PatchWatcher.kt` now logs the
  full `result` payload before `document.setText` on both the
  `applyPatch.component` and `repositionBoundary` paths, behind
  `LOG.isDebugEnabled` (only when `#com.github.btakita.agentdoc` debug logging is
  on). Previously the category logged only content hashes + lengths
  (`documentMutationDiagnosticUtil`), which was insufficient to capture the exact
  corrupting payload for the IPC-duplication family (`#ipcfullprompt-recur2`,
  `#wy0y`/`#6cmx`). Unblocks the Path B capture session.

- **BREAKING CHANGE: opt-in agent-doc documents (`#4a6p`).** A plain `.md` is no
  longer auto-converted into an agent-doc session. `route`, `run`, and `start`
  now fail closed before injecting `agent_doc_session:` frontmatter unless the
  document opts in via (1) any agent-doc-managed frontmatter field
  (`Frontmatter::has_agent_doc_marker()` — `agent_doc_*`, `session`, `agent`,
  `resume`, model overrides, `*_args`, `branch`, `queue_active`,
  `prompt_presets`, …), (2) a `[documents] include = [...]` glob in
  `.agent-doc/config.toml`, or (3) the `documents.auto_session_for_all_md = true`
  escape hatch (restores old behavior). Existing session docs already carry
  `agent_doc_session:`/`agent_doc_format:` so they are unaffected; only brand-new
  plain notes/README `.md` change behavior. The gate does **not** mutate the file
  when it refuses, and a malformed frontmatter block bypasses the gate so its own
  contextual YAML error surfaces. New pure predicate
  `project_config::is_agent_doc_document(rel_path, content, config)` + minimal
  zero-dep glob matcher `project_config::glob_match` (`*`/`?`/`**`); FFI
  `agent_doc_is_session_document(path)` for editor plugins to gate Run Agent Doc /
  SubmitAction. `agent-doc init`/`claim` remain explicit per-file opt-ins.
  Regressions: `documents_gate_tests::*` (core truth table + glob),
  `frontmatter_io::tests::gate_*` (fail-closed, file untouched, frontmatter +
  config-glob opt-in). Plan: `tasks/agent-doc/plan-opt-in-agent-doc-documents.md`.
  Follow-up (editor-side): wire the FFI gate into JB `SubmitAction.update` + the
  VS Code extension (needs a plugin version bump + live-verify).

- **Queue no longer re-mints completed `do [#id]` refs (`#ynra`).** The preflight
  backlog→queue sync now excludes ids already archived in `agent:done` before
  minting `do [#id]` prompts. Previously a lingering active backlog `[ ]` bullet
  whose id was also in `agent:done` was minted into the queue, struck by the
  done-strike pass that same cycle, then re-minted the next cycle — churning
  forever on a completed ref. `agent:done` ids are computed once up front and
  reused by both the sync filter and the strike pass. The done-strike pass also
  now reaps a resolved `do [#id]` **anywhere** in the queue (not only the head),
  so an already-orphaned completed ref behind a still-live head no longer lingers
  and trips the shadow-backlog guard. Regressions:
  `run_queue_maintenance_excludes_done_ids_from_backlog_sync`,
  `strike_done_queue_prompts_strikes_non_head_resolved_ref`.

- **Mutation-time identity-collision rejection (`#preset-item-id-collision-enforce`,
  part 1).** `agent-doc write --pending-add` / `--pending-add-after` /
  `--pending-add-before` / `--pending-add-back` / `--pending-add-to` now fail
  closed when given an **explicit** custom id (`id=<id>` or `[#id]`) that collides
  with a frontmatter `prompt_presets` key or an existing active
  backlog/review/icebox item id, so a new ambiguous `#id` is never written. Auto-id
  adds (no explicit prefix) are never blocked. Builds on the existing
  `detect_identity_collisions` registry (now factored through
  `document_active_identities` + `identity_collision_for_new_id`). The riskier
  dispatch-time halves (hard preflight/session-check block on a pre-existing
  collision; queue-generation refusal) remain intentionally deferred — the
  existing `preset_item_id_collision` preflight *warning* stays the dispatch-time
  signal — to avoid over-blocking live sessions with pre-existing collisions.
  Tests: `add_rejects_explicit_id_colliding_with_prompt_preset`,
  `add_rejects_explicit_id_colliding_with_active_item`,
  `add_allows_explicit_noncolliding_id`,
  `add_allows_auto_id_even_when_text_mentions_preset`,
  `identity_collision_for_new_id_reports_existing_sources`.

- **Empty pending/icebox bullets no longer get a phantom id
  (`#icebox-empty-item-phantom-id`).** A stray content-less bullet (`- [ ]`
  with no description and no continuation — e.g. an editor/IPC insertion before
  a component close marker) was being assigned a backfilled `[#hash]` id,
  producing a phantom tracked item whose "description disappeared" (observed:
  `- [ ] [#1k5y]` in an icebox). `pending::backfill` now **drops** content-less
  items instead of manufacturing an id for them, which both prevents the phantom
  and self-heals an already-cemented id-only empty item on the next maintenance
  pass. Items with empty header text but a real indented continuation are
  preserved. Regressions: `backfill_drops_content_less_empty_bullet`,
  `backfill_drops_id_only_empty_item_self_heal`,
  `backfill_keeps_empty_text_with_continuation`.

- **Managed capability-proof failure is recoverable (`#codex-capability-proof-unrecoverable`).**
  Two fixes so a transient network blip no longer permanently wedges a managed
  Codex/OpenCode session:
  - **Bounded re-prove.** The capability-proof thread now retries the
    network/SSH/writable-root probe with exponential back-off before committing
    the dispatch gate to `Failed`. Between attempts the gate stays `Pending`
    (gated but recoverable) and the session log records
    `..._capability_proof status=retry attempt=<n>/<max>`. Retry budget, base
    back-off, and probe timeout are configurable in frontmatter and
    `.agent-doc/config.toml` (`managed_proof_max_attempts`,
    `managed_proof_retry_backoff_secs`, `managed_proof_probe_timeout_secs`;
    defaults 3 / 2s / 45s, frontmatter wins). Pure helper `proof_retry_decision`
    has deterministic unit coverage.
  - **Gate-exempt operator recovery.** The supervisor IPC layer now gates only
    real prompt dispatch (`Inject`); the new `Clear` control method plus `Stop`
    and `Restart` bypass the capability gate. `agent-doc session clear` /
    `session interrupt-clear` deliver `/clear` through the gate-exempt `Clear`
    method, so they stop or clear a proof-`Failed` session without `kill -9`
    instead of failing with `prompt dispatch is disabled`. Auto-trigger /
    auto-queue dispatch stays gated.
  - Regressions: `proof_retry_decision_*`, `resolve_managed_proof_policy_*`,
    `ipc_method_gate_classification_only_gates_inject`,
    `handle_ipc_clear_bypasses_failed_capability_proof`,
    `handle_ipc_stop_bypasses_failed_capability_proof`.

- **Queue-audit partial-completion advisory (`#queue-audit-partial-completion`,
  WARN-only).** A queue-completion audit that reports the queue as "none
  complete" while citing several completed substeps — collapsing partial progress
  into all-or-none — now trips `check_queue_audit_partial_completion_guard`, which
  recommends classifying each row as complete / partially complete / not-started
  with the completed substeps and exact remaining condition. Conservative,
  WARN-only (never blocks closeout), suppressed by `<!-- no-queue-audit-guard -->`.
  Per the binary-vs-skill rule, per-row classification is response-contract
  guidance (skill/spec); the binary only flags the unambiguous collapse.
  Regressions: `queue_audit_guard_warns_when_none_complete_collapses_partial_progress`,
  `queue_audit_guard_quiet_when_partial_states_already_given`,
  `queue_audit_guard_quiet_when_not_about_queue`,
  `queue_audit_guard_quiet_without_extra_completion_evidence`,
  `queue_audit_guard_suppressed_by_marker`.
- **Auto-queue no longer strands live items when a new head is inserted
  (`#completed-queue-residue-regression` / `#queue-auto-no-continue`).**
  `detect_head_prompt_modified` compared only the first queue prompt's text, so
  inserting (or reordering) a new item ahead of the still-present in-flight head
  registered as an in-place `item_modified` edit. Preflight then halted the
  queue, stripped `auto`, set `queue_active: false`, and stranded every remaining
  live `do [#id]` as inactive residue — the auto-queue stopped instead of
  advancing. Now a head change only counts as a modification when the snapshot
  head prompt is genuinely gone from the current queue (edited in place or
  removed); a prepend/reorder of a still-present head is treated as a
  re-prioritization and the queue advances to the new head, staying active.
  Regressions: `head_prompt_modified_false_when_new_item_inserted_ahead_of_present_head`,
  `head_prompt_modified_false_on_reorder_promoting_existing_item`,
  `head_prompt_modified_true_when_head_text_edited_in_place`,
  `head_prompt_modified_false_when_head_unchanged`.
- **Gated-phase split advisory (`#gated-followup-split-enforcement`, WARN-only).**
  When a directed `do [#id]` cycle keeps a tracked item open (`--pending-edit` /
  `--review-edit` / `--pending-gate`) whose body enumerates multiple
  gated/remaining phases (the word "phase" + ≥2 parenthesized phase markers like
  `(2b)`/`(3)` framed by a gating signal) without breaking them into discrete
  child backlog IDs, `session-check`'s new `check_gated_phase_split_guard` warns
  to split each phase into its own child ID so deferred work stays independently
  trackable/queueable (sibling of `#blocked-closeout-followup-capture` and the
  SKILL "one backlog ID per actionable phase" rule). WARN-only — never blocks
  closeout — and suppressed by a `<!-- no-gated-phase-split-guard -->` marker.
  Regressions: `gated_phase_split_guard_warns_on_multi_phase_kept_open_item`,
  `gated_phase_split_guard_quiet_when_phases_already_split_into_child_ids`,
  `gated_phase_split_guard_quiet_for_single_phase_item`,
  `gated_phase_split_guard_suppressed_by_marker`,
  `gated_phase_split_guard_is_advisory_not_blocking`.
- **`preflight --probe` is a side-effect-free inspection mode
  (`#preflight-probe-side-effect-free`).** A diagnostic `agent-doc preflight`
  used to open a `preflight_started` cycle even when it was only inspecting
  state, leaving an open cycle that later wedged `session-check` (the empty-cycle
  churn from the recursive owner-pane diagnostic path, Proposed-Fix #4 of
  `#recursive-repair-state-drift`). `agent-doc preflight --probe <FILE>` now emits
  the same JSON but never opens a `preflight_started` cycle. The default
  response-bound preflight (and internal callers like `orchestrate`) keep opening
  the cycle that binds the upcoming response. Regressions:
  `preflight_probe_does_not_open_cycle_even_with_dispatchable_diff` (probe leaves
  no open cycle) and the existing
  `preflight_opens_cycle_from_active_queue_when_document_has_no_diff` (contrast:
  default path still opens it).
- **Supervisor idle-queue watch drains route-enqueued busy-queue heads
  (`#jb-run-agent-doc-busy-queue-dispatch-deadlock`).** When a busy-pane
  `Run Agent Doc` route appends a prompt to `agent:queue auto` and returns `Ok`,
  the drain was harness-delegated and a Claude session not running `/loop` had no
  guaranteed trigger, so the queued head could sit forever (operator-perceived
  "deadlock"). The supervisor now runs a long-lived idle-queue watch alongside
  the one-shot restart auto-trigger: on each busy→idle transition it drains a
  live `queue_active: true` ready head (shared
  `queue_continuation::live_continuation_head`) by injecting the harness trigger
  through the existing capability-gated `auto_trigger_inject_command` path. The
  drain decision is the pure, tested `idle_queue_drain_decision` — dispatch only
  when idle with a fresh head, never inject mid-turn (no-inject-into-active-turn),
  dedup a still-present head to avoid hot-looping, and clear the dedup once the
  head drains. Regressions: `idle_queue_drain_dispatches_when_idle_with_fresh_active_head`,
  `idle_queue_drain_skips_when_pane_busy_even_with_active_head`,
  `idle_queue_drain_skips_when_no_active_head`,
  `idle_queue_drain_dedups_already_dispatched_head`,
  `idle_queue_drain_fires_again_when_head_advances`. Live end-to-end verification
  on a real busy Codex/Claude pane stays operator-gated.
- **JetBrains Run Agent Doc retries busy actor wait timeouts.** When
  `agent-doc route --dispatch-only --wait-for-ready` waits behind an
  authoritative actor that is busy because another operator command is still
  draining, the CLI can fail with "dispatch-only route will not inject ... the
  authoritative actor is busy did not return to a dispatch-ready prompt". The
  JetBrains action now classifies that timeout as a retryable still-running
  route outcome instead of a persistent failure, so the existing retry loop can
  catch the actor when it becomes ready shortly after the first 60s wait.
  Regression: `dispatch-only busy actor wait timeout is retryable not
  persistent failure`. JetBrains plugin bumped to `0.2.142` and installed into
  the local IDEA 2026.1 profiles.
- **Binary-owned auto-queue continuation final gate (`#codex-auto-queue-stalled-final-gate`).**
  Codex auto-queue continuation previously depended on the `codex-stop` hook finding
  tracked in-memory session state, which the live failure (`sampleorders.md`
  `#seocat`→`#seopdp`) showed is too fragile — a clean document still owed a
  continuation but Codex sent a final answer. New shared `queue_continuation::detect`
  is the single source of truth (`queue_active` + `agent:queue auto` + active
  `resolve_activation` + a ready, unmodified head); `active_auto_queue_prompt` now
  delegates to it. Every clean binary closeout (`finalize` / `write --commit` /
  `repair` / already-committed no-op) reconciles a durable
  `.agent-doc/queue-continuations/<doc-hash>.json` marker (written when owed, cleared
  on drain / `auto` removal / `queue_active` false / head advance). `codex-stop` now
  consults that marker when no tracked session state exists — re-confirmed against the
  live document so a stale marker never forces a spurious block — and still blocks the
  final answer, failing closed on a repeated non-advancing head. `agent-doc session-check`
  surfaces `queue_continuation_required=…` / `next_queue_prompt=…`; the new strict
  `agent-doc session-check <FILE> --codex-final-gate` exits nonzero when continuation is
  required. New `queue_continuation` unit tests, two `codex_hook` marker-fallback tests,
  and a `codex_hook_integration` strict-gate test. Live verification still gated on
  `#codex-auto-queue-live-verify`.
- **Recursive direct-invocation guard abandons its empty cycle (`#recguard-abandon`).**
  When a Codex-backed `agent-doc <FILE>` runs inside the same tmux pane that already
  owns the document, the recursive-invocation guard fails fast with the existing
  `recursive direct invocation would deadlock` diagnostic — but it previously left the
  freshly-opened preflight cycle in `preflight_started`, so `session-check` reported an
  interruption and the owner session stayed wedged until a manual `agent-doc cancel`.
  `run.rs` now marks that empty cycle terminal (`Abandoned`) via a new
  `abandon_run_recursive_cycle` (the guard fires before any response capture, so nothing
  is lost); `session-check` accepts the terminal abandoned state automatically. Regression:
  `recursive_direct_invocation_abandoned_cycle_passes_session_check`.
- **Multi-retry / late-IPC response duplication hardened (`#finalize-retry-ipc-response-duplication`).**
  A closeout needing several finalize/write retries with the IDE IPC listener active and
  concurrent editing could leave a duplicated `### Re:` response block whose stale copy had
  its body lines wrongly prefixed with the `❯ ` user-prompt marker, fail-closing
  `session-check` until a manual `git checkout`. Fixes: `canonicalize_answered_prompt_prefixes`
  never `❯`-prefixes a prose block that butts directly against a preceding response heading
  (it's that response's body, not a user prelude); dedupe response-block normalization is
  now `❯`-insensitive and drops the corrupted copy of a duplicate pair; the late-IPC
  over-application / JB-cache replay detectors recognize the `❯`-corrupted-duplicate shape,
  so `preflight` restores clean HEAD automatically. Regressions in `dedupe`, `git`, and
  `session_check`.
- **Prompt-prefix dedup gap fixed (`#prompt-duplicated-while-typing`, partial).** The
  shared-core append dedup (`component::append_patch_already_present` via
  `normalize_append_patch_content`) stripped boundary / `(HEAD)` markers but not the
  `❯ ` user-prompt prefix. A synthesized boundary-aware exchange patch and the live
  editor buffer can differ by exactly that prefix, so the `contains` dedup missed and
  the prompt re-appended → the duplicate-while-typing report. `normalize_append_patch_content`
  now strips a leading `❯ `/`❯` prefix (`strip_user_prompt_prefix`), making dedup
  prefix-agnostic and symmetric (cannot collapse a distinct prompt — the glyph is
  presentation, not content). The JetBrains plugin loads the cdylib by path and
  hot-reloads on mtime change, so `agent-doc lib-install` ships this to the live editor
  with no plugin reinstall. Tests: `append_patch_already_present_ignores_user_prompt_prefix`,
  `append_patch_distinct_prompts_not_deduped`,
  `append_with_caret_does_not_duplicate_prefixed_prompt`. The structural buffer-snapshot
  race (synthesized patch carries the buffer at T1 while it advanced to T2) remains
  tracked. Plan: tasks/agent-doc/plan-prompt-duplicated-while-typing.md.

- **Queue head no longer struck on halt/refusal responses (`#queue-strike-on-halt`).**
  Consuming the active `agent:queue` head now requires an explicit completion
  signal. The CLI `finalize` / `write --commit` path requires a closeout flag —
  `--done`, `--pending-gate`, or `--pending-edit "<id>=…"` — naming the head id
  (or a genuine fresh operator prompt-target / `do queue` trigger); the old
  "`### Re:` heading mentions the head → consume" heuristic is removed, so a halt
  response that explains why the item should stay open no longer silently strikes
  it. The Codex Stop-hook auto-close path (no closeout CLI flags) still consumes
  from a heading but only on an exact topic match (`### Re: do [#id]`), never on a
  modified heading like `### Re: #id halt`. New `queue_head_has_explicit_completion_signal`
  in `write.rs`; `response_topic_matches_queue_head` narrowed to exact-match.
  Coverage: `explicit_signal_*` + `heading_topic_matches_head_exactly_only`
  (write.rs) and `halt_response_does_not_strike_queue_head_but_done_flag_does`
  (sim_world.rs). Spec: `07-orchestration-commands.md` + `SPEC.md`. Plan:
  `tasks/agent-doc/plan-queue-strike-on-halt-response.md`.

- **Queue/IPC buffer convergence seam (`#adoc-queue-ipc-buffer-divergence`,
  root cause #2).** Queue maintenance now converges a live route-owned editor
  buffer to the committed inactive queue shape after a halt/drain. Previously a
  content-only IPC patch could not change the `<!-- agent:queue auto -->`
  opening-tag attribute or the `queue_active:` frontmatter, so a live IDE buffer
  re-added `auto`/`queue_active: true` on its next flush and the snapshot/HEAD
  drift loop regenerated on every preflight. New `agent_doc_converge_queue_auto`
  FFI export (`agent-doc-core`, takes a C int for a stable JNA ABI) rewrites the
  queue opening-tag attribute; `ipc_socket::send_queue_convergence` carries the
  desired `queue_auto` state plus the `queue_active` frontmatter; preflight's
  `run_queue_maintenance` pushes the convergence through the listener after each
  halt/drain disk write (best-effort, non-fatal). JetBrains `PatchWatcher`
  parses `queue_auto` and applies it via `NativePatching.convergeQueueAuto` in
  both the Document-API and VFS apply paths (plugin `0.2.137`). Deterministic
  SimWorld repro: `queue_maintenance_converges_live_ipc_buffer_on_item_modified_halt`
  starts a simulated IPC listener, halts an active auto-queue, and asserts a
  single convergence message + idempotent follow-up. Plan:
  `tasks/agent-doc/plan-queue-ipc-drift.md`.

- **`agent-doc-core` v0.1.0 published to crates.io.** The pure document data
  layer (`#adcr` extraction: component parsing, frontmatter, template, CRDT,
  pending, diff classification, model tier, syntax, and the full pure C-ABI FFI
  surface) is now a standalone published crate. `publish = false` removed; all
  dependencies are crates.io crates (no path/git deps). Enables third-party FFI
  consumers and the editor-plugin slim-link target (link `agent-doc-core` —
  ~9.87s cold / 74 crates — instead of the full `agent-doc` orchestration crate
  — 129s / 266 crates). The `#k9e1`/`#epv5`/`#vb8h`/`#e130` FFI relocations
  moved all 15 pure FFI functions into `agent_doc_core::ffi` ahead of this.
- **Strict finalize appends no longer overwrite prior exchange responses when
  the explicit baseline is stale.** For template/CRDT append-mode exchange
  writes under `finalize` or strict `write --commit`, if the supplied
  `--baseline-file` is missing exchange content already committed in `HEAD`,
  the write path now applies the response on top of `HEAD` before producing
  `content_ours`, IPC snapshots, or commit-staged snapshots. This keeps
  back-to-back finalizes from dropping the previous `### Re:` block and logs
  `explicit_baseline_rebased_to_head` when the repair path is used. Regression:
  `finalize_stream_rebases_stale_exchange_baseline_to_head`. Closes
  `#finovrwr`.

- **OpenCode dispatch-only reroutes now have dispatch-start proof.** Route
  captures the OpenCode pane before submit, waits for the routed trigger to
  leave the composer, and accepts proof only when the pane leaves idle chrome
  within the OpenCode redraw budget. Proven OpenCode delivery now logs
  `proof=pane_state_changed proof_scope=dispatch_start`; accepted-only
  OpenCode delivery still fails closed.

- **Finalize now consumes answered queue-synthetic prompts.** When an active
  `agent:queue auto` head is the only prompt diff, `finalize` can now consume
  it after the response is written if the captured `### Re:` heading targets
  the queue head's id (for example `#spec-test-build-install-commit-push`).
  Unrelated baseline prompts still preserve the queue head.

- **Queued JetBrains Run Agent Doc reroutes now survive live prompt edits.**
  When `route --dispatch-only` queues a busy-actor rerun by saving
  `agent:queue auto` to the snapshot but `HEAD` still lacks that handoff, the
  next preflight auto-commits the route-owned queued snapshot before diffing.
  If the user edits the visible prompt meanwhile, that edit stays uncommitted
  in the working tree and becomes the fresh prompt diff instead of wedging the
  queue behind the generic `snapshot differs from HEAD` recovery hint.
  Repeating the editor action with updated prompt text now replaces the sole
  live route-owned `agent:queue auto` prompt instead of leaving stale wording
  queued behind the active turn.

- **Template exchange appends now keep response headings block-separated.**
  When a `<!-- patch:exchange -->` response starts with `### Re:`, the
  boundary-replacement and fallback append paths insert a blank line after
  non-empty prior exchange content. This prevents Markdown renderers from
  joining a new response heading to the previous paragraph when the prior
  response lacked a trailing blank line.

- **JetBrains Run Agent Doc now surfaces queued busy-actor reroutes.** When
  `agent-doc route --dispatch-only` accepts a prompt by adding it to
  `agent:queue auto` behind a busy authoritative actor, the IDE action now
  treats that output as a queued/still-running outcome instead of silent
  success. The notification keeps the route details copyable and tells the
  user the request is waiting for the active turn to drain.

- **Socket/file ACK-content sidecars can no longer commit duplicated user
  prompt text.** The write path now treats editor ACK content as a
  whole-buffer observation that still must pass response-aware prompt
  multiplicity checks before snapshot adoption. If the sidecar has extra
  user-prompt copies relative to the agent-owned `content_ours` response image,
  `agent-doc` logs `ipc_snapshot_adoption_blocked
  reason=prompt_duplication_in_ack_content`, saves `content_ours`, marks the
  cycle so commit staging cannot absorb the bad buffer, and repairs the visible
  duplicate through the guarded disk repair path. This closes the
  `tasks/professional/sampleportal.md` corruption shape where a narrow
  editor patch succeeded but the full-document ACK sidecar carried duplicated
  prompt text while the user was typing.

- **JetBrains Run Agent Doc retries transient dispatch-only Codex boot/busy
  refusals without masking protected input.** The IDE retry loop now recognizes
  the binary's `latest run is still booting` route refusal when the ready probe
  ended on `active codex turn` or `timed_out`, so fast repeated clicks do not
  strand behind a stale startup projection or still-running turn. Shell history
  search and other protected-input blockers remain terminal route failures.

- **JetBrains File Cache Conflict Cancel recovery is now pinned for the
  direct `write_applied` wedge.** The preflight regression suite now covers
  the exact Cancel-shaped closeout where the working tree and snapshot already
  contain the response but `HEAD` does not and the cycle is still
  `write_applied`. Preflight must classify that as
  `jb_cache_conflict_cancel` and close the missing commit boundary
  automatically, matching the already-covered committed-cycle variant and the
  JetBrains plugin Cancel contract.

- **Claude Code auto-loop guard no longer blocks on routine
  managed-component state edits.** The SKILL.md auto-loop rule previously
  fired only when `prompt_bearing_changes` was empty or exactly the
  queue-synthetic head prompt. In practice every meaningful queue cycle
  produces queue-activity toggles, queue item add/strike lines, or
  backlog/review/done item edits that preflight classified as
  `content_edit` / `prompt_target` and tripped the guard. Net effect: the
  auto-loop almost never fired for real queue work. Preflight now emits a
  new `user_intent_prompt_changes` field that filters the same change list
  through `diff::change_is_managed_state_only`, which recognises
  queue/backlog/review/done component-marker lines, `queue_active:`
  frontmatter flips, `- do ...` queue items (including struck `- ~do ...~`),
  and standard task-list items as managed state rather than user prompts.
  The SKILL.md auto-loop section now reads `user_intent_prompt_changes`
  instead of `prompt_bearing_changes` so routine session bookkeeping does
  not interrupt the queue drain. Real user prompts (free-text questions,
  imperative directives outside the managed components) still appear in
  `user_intent_prompt_changes` and continue to block. 7 new unit tests in
  `diff::tests::change_is_managed_state_only_*`. Plan: `#ccloopguard`.

- **JetBrains plugin (0.2.131) now emits `already_applied` socket-IPC acks
  via the new FFI v2 listener.** When the plugin's apply path detects that
  the incoming patch produces no structural change against the live editor
  buffer (response body already present from a prior socket retry, the
  in-process dedup cache, or the force-disk sentinel), it returns
  `2 → {"type":"ack","status":"error","reason":"already_applied"}` instead
  of `1 → status:ok`. The binary's `is_already_applied_error` gate then
  skips the file-IPC fallback that would otherwise stack a duplicate
  `### Re:` heading on top of the live buffer. New FFI export
  `agent_doc_start_ipc_listener_v2(project_root, callback)`; the v1 export
  remains for older plugins. JB plugin prefers v2 and falls back to v1 on
  binaries that don't export it (`UnsatisfiedLinkError` / `NoSuchMethodError`).
  Closes `#ipcpluginalready`.

- **File-IPC fallback hash-skips response patches that are already applied
  to the live buffer.** Defense-in-depth complement to the `already_applied`
  socket-IPC gate (and to `#ipcpluginalready` until every plugin emits the
  signal). In `try_ipc`, when the patches are response-bearing (contain at
  least one `### Re:` heading) and `apply_patches(current, patches)` is a
  structural no-op against the live file (boundary markers excluded), the
  file-IPC fallback short-circuits as success without writing the patch
  file. Non-response (prompt/component) patches still flow through the
  existing path so its no-ack guard for unacknowledged live-edit IPC stays
  authoritative. New test `try_ipc_file_fallback_skips_when_patches_already_applied_to_live_buffer`.
  Closes `#ipcfilehashskip`.

- **Test fixture migration: `agent:pending` → `agent:backlog`.** The
  `tagpath lint --dialect agent-doc` gate added in the prior release
  blocked the deprecated `agent:pending` component name. Migrated 31 sites
  in `tests/finalize_integration.rs` and 4 sites in `tests/run_integration.rs`
  to the canonical name; 30 previously-failing integration tests now pass.
  `tests/pending_integration.rs` keeps the legacy alias intentionally
  (those tests exercise the alias migration path). Closes `#ipclegacyfix`.

- **SimWorld regression coverage for the IPC corruption + duplicate response
  race when the user types into the post-`/agent:exchange` scratch comment
  during finalize.** New deterministic scenario in `src/sim_world.rs` exercises
  the `is_already_applied_error` gate: when socket IPC returns
  `{"type":"ack","status":"error","reason":"already_applied"}` after the plugin
  has applied the patch via a prior socket retry, the file-IPC fallback must be
  skipped so the response is not duplicated on top of the live buffer. Includes
  the counterfactual dedupe-recovery path to prove `dedupe_ipc_snapshot_content`
  still collapses the duplicate if the gate ever regresses. Also adds two
  integration-style tests for the `recover_empty_response_for_strict_closeout`
  wrapper in `src/write.rs` covering the full `agent-doc dedupe` →
  `agent-doc write --commit` (empty stdin) recovery path: the dedupe-only drift
  is committed through the binary path under strict closeout, and the
  non-strict path stays read-only. Closes Phases 1 and 5 of
  `tasks/agent-doc/plan-ipc-corruption-and-duplicate-during-typing.md`.

- **`finalize` / `write --commit` now invoke `tagpath lint --dialect agent-doc`
  before the snapshot/commit boundary.** Malformed session-document
  directives — for example `<!-- agent:done archive PATH -->` missing `=` —
  now fail closed at the lint gate with a structured error pointing at the
  rule, line, column, and fix hint, rather than crashing deep inside
  `finalize`. The gate is a library call against `tagpath`'s agent-doc
  dialect (no subprocess overhead). Mode resolution: CLI `--lint=off|warn|
  strict` > frontmatter `agent_doc_lint_dialect: off|warn|strict` >
  workspace `.agent-doc/config.toml` `[lint] dialect` > default (`warn`).
  Default behavior: errors block, warnings surface on stderr. `strict`
  promotes warnings to errors; `off` skips the gate (logged via `ops_log`
  for audit). New module: `src/lint_gate.rs`.

- **Newly activated auto queues no longer stall as modified in-flight work.**
  Preflight now treats a queue that was inactive in the snapshot and newly
  activated by the current document as operator-authored input, snapshots the
  activated queue body as the closeout baseline, and reserves
  `queue_halted: "item_modified"` for queues already active in the snapshot.
  This prevents intentional queue rewrites plus `auto` from stripping the
  auto attribute before the first queued item can run.

- **Full-document editor IPC is disabled end-to-end.** The binary no longer
  emits `fullContent` socket/file IPC payloads, even for no-component append
  fallbacks, repair redelivery, or operator mutations. Scope rejection still
  logs for template/component documents, committed-cycle cleanup still runs for
  stale fallback patches, and otherwise eligible paths log
  `full_content_ipc_disabled` before falling back to guarded disk/snapshot
  repair. JetBrains and VS Code now also reject or delete legacy/foreign
  `fullContent` payloads without applying whole-document editor replacements.
  Added a separate Mermaid reference for the repeated corruption chain. Bumped
  local plugin builds to JetBrains `0.2.129` and VS Code `0.2.21`.

- **Commit closeout repairs live prompt prefix duplicates before staging.**
  Snapshot-staged commit closeout now runs a narrow in-exchange prompt duplicate
  repair before staging, collapsing adjacent prefixed/raw copies of prompt text
  already represented in the snapshot. When the repaired file is only
  prompt-prefix-equivalent to the snapshot, the snapshot advances to that
  repaired content so queue-freeze and other prompt-only closeouts do not leave
  a duplicated prompt in the editor buffer or misclassify it as out-of-band
  drift.

- **Route/preflight now preserve visible post-exchange scratch comments.**
  Duplicate-prompt cleanup now treats the current visible document as ownership
  proof for ordinary HTML comments below `agent:exchange`, so route, preflight,
  and final closeout do not empty scratch comments the user typed before the
  mutation. Generated duplicate comment residue can still be scrubbed when it is
  absent from both the baseline/snapshot and the current file used for the
  write, while exact duplicate answered prompt tails inside `agent:exchange`
  remain auto-cleaned. The regression suite now includes a SimWorld/integration
  no-delete matrix across route cleanup, preflight recovery, direct write,
  IPC/plugin handoff, repair write-commit, compact exchange, and generated
  residue diagnostics.

- **Compact Exchange no longer emits full-document editor IPC.** Template
  exchange compaction now uses the visible idle + compare-and-swap direct-write
  guard even when an editor patch directory is present. This removes the
  `compact_exchange` fullContent path that could replace an active editor buffer
  immediately before the operator drafted the next prompt.

- **Queue closeout consumption now requires active-head proof.** Strict
  `finalize` / `write --commit` no longer consume an active queue head solely
  because the pre-response baseline and live document have no prompt diff. The
  closeout must see the exact queue-head prompt, a queue-synthetic run diff, or
  a matching `--done <id>`, so unrelated already-baselined prompts such as
  `#next-steps` cannot advance `agent:queue auto`.

- **Repair closeouts preserve auto queues unless the response targets the
  head.** Missing-response materialization through `write --commit` or the Codex
  Stop hook now leaves the active queue head and `auto` attribute intact unless
  the closeout carries explicit head proof, such as a matching `--done <id>` or
  a `### Re:` topic for the current queue item.

- **Codex child process launch retries transient executable-busy errors.**
  The Codex/OpenCode backend now retries child process spawn when Linux returns
  `ETXTBSY` for a just-written executable. This hardens the streaming resume
  retry tests and normal child launch path against CI filesystem races without
  masking other spawn failures.

- **Editor repair cleanup now distinguishes snapshot-only from redelivery.**
  The editor specs now make typed IPC repair decisions explicit: snapshot-only
  repair stays binary-owned, narrow `normalize_prefix_lines` + boundary
  reposition payloads stay on the normal patch path, and full-content redelivery
  is disabled in the first-party binary. VS Code and JetBrains tests now pin the
  narrow repair shape so pure-reposition shortcuts cannot absorb it.

- **File IPC sidecar-normalization fallback now has narrow repair coverage.**
  The file-IPC fallback path is covered by a regression proving prefix-only
  sidecar divergence queues a `patches: []` repair with `normalize_prefix_lines`,
  boundary repositioning, and stale-buffer proof. The closeout spec now calls
  out socket and file IPC as the same narrow-first contract and disables
  full-content redelivery.

- **The tsift.md duplicate-content IPC incident is now a named regression.** A
  focused fixture models stale duplicate-response repair planning while the
  visible `tasks/software/tsift.md` buffer receives a new prompt. The regression
  proves response-fallback full-document redelivery skips socket/file IPC,
  leaves the live buffer untouched, and logs the disabled/stale proof decision.

- **Codex Stop hooks now keep harness-native auto queues moving.** After a
  clean `finalize` / `session-check`, `codex-stop` now detects an active
  `agent:queue auto` with a ready next head prompt, blocks final-answer
  delivery, and tells Codex to invoke `agent-doc <FILE>` again in the same
  turn. The hook records the requested head and fails closed if a repeated
  continuation reaches Stop without the queue head advancing, preventing
  infinite hook loops.

- **Manual queue closeouts can drain explicit done-backed batches.** Strict
  `finalize` / `write --commit` closeouts now consume all contiguous active
  queue head prompts whose `do #id` items were resolved by repeated `--done`
  flags, while still stopping before the first unresolved prompt and proving the
  same queue range against the saved snapshot before mutation. This lets a
  harness-native response that handled a whole queued batch close the queue in
  one binary-owned commit instead of leaving later completed queue items behind.

- **Prompt-normalization overruns no longer force-commit.** The
  `MAX_NORMALIZE_USER_LINES` guard now logs `normalize_threshold_exceeded
  action=passthrough` and leaves the content unchanged for the typed
  repair/closeout path, removing the broad force-commit workaround that could
  absorb unrelated drift from inside prefix normalization.

- **Duplicate-prompt repair now has one write-path pipeline.** Closeout,
  content-ours normalization fallback, and IPC snapshot repair now share a
  canonical duplicate-prompt artifact repair that handles adjacent duplicate
  response blocks, answered prompt tails, post-exchange duplicate prompt
  comments, before-content prompt-line duplicates, and live prompt prefix
  variants in one audited pass. The aggregate
  `duplicate_prompt_artifact_repair` log records which artifact classes changed
  while preserving the existing narrow diagnostic markers.

- **IPC repair state is now a typed decision.** Sidecar-normalization fallback
  and duplicate-response IPC dedupe now resolve a single repair decision carrying
  the repaired snapshot content, snapshot source, disk-repair reason, bad editor
  buffer fingerprint, normalization targets, and explicit editor-redelivery flag
  before touching disk or sending editor repair IPC. Prefix-only sidecar
  divergence now tries a narrow `normalize_prefix_lines` + boundary-reposition
  patch before full-content repair, and repair/redelivery ops logs include patch
  ids, hashes, prefix counts, duplicate-prompt counts, and stale-proof skips.
  This keeps stale-editor redelivery, disk repair, and snapshot save behavior on
  one auditable branch.

- **Owned scratch comments survive duplicate prompt cleanup.** Closeout,
  preflight, and route duplicate-prompt cleanup now preserve post-exchange HTML
  comment lines that were already present in the pre-response baseline/snapshot
  or in the visible document used for the mutation. The scrub still removes
  generated duplicate prompt residue with no ownership proof and preserves the
  comment shell, but it no longer empties a user's parked scratch prompt such as
  the `tsift.md` `#next-steps` comment after the prompt is answered.

- **Answered prompt tails after the exchange boundary are scrubbed before redispatch.**
  Template normalization, preflight, and route cleanup now remove an exact raw
  prompt tail after the latest `agent:boundary` when that prompt block already
  has an assistant response earlier in `agent:exchange`. Preflight runs the
  cleanup before the commit step can reposition the boundary, preventing the
  already-answered prompt from reappearing as fresh prompt-bearing diff.

- **Mixed scratch comments preserve unrelated lines during duplicate cleanup.**
  When generated post-exchange HTML comment residue lacks ownership proof,
  cleanup removes only the duplicate prompt lines from multiline comments
  without applying a fuzzy whole-comment match that can erase unrelated
  scratch/log-triage text in the same comment. Added editor-visible and
  preflight regressions for the live `agent-doc-bugs2.md` mixed-comment shape.

- **Full-content replacements now bind to their computed source buffer.** Compact
  Exchange and other operator-owned whole-document replacements stamp editor IPC
  with the exact source buffer used to compute the replacement, not a late disk
  reread, and direct disk fallback uses the same visible-current compare-and-swap
  guard. Socket full-content ACKs are also rejected before snapshot save when the
  materialized document differs from the payload. This closes a live-typing race
  where a compact/full-content write could accept or persist content derived from
  an older buffer while the user was typing the next prompt.

- **Dispatch-only editor reroutes recover degraded authoritative panes.** When
  JetBrains `Run Agent Doc` finds an authoritative actor pane whose supervisor
  socket is missing or whose runtime actor state is absent, route now keeps that
  pane as the recovery target if it is still the current registered/live owner.
  The reroute records controller dispatch, logs
  `route_dispatch_only_authoritative_degraded_direct_pane`, and then uses the
  normal direct-pane readiness/blocker/proof gates before submitting, avoiding a
  first-open manual `agent-doc start <FILE>` rebind when the live pane is already
  dispatch-ready.

- **Freeform duplicate prompt residue now fails closed.** After the safe
  post-exchange HTML comment scrub runs, route, editor-visible normalization,
  final template reconciliation, and IPC snapshot dedupe reject remaining
  duplicate or near-duplicate prompt text in ordinary post-exchange Markdown
  outside tracked components. This keeps arbitrary manual Markdown edits from
  being silently committed or dispatched when there is no ownership proof for
  deleting or relocating the duplicate text.

- **Missed response materialization no longer closes as already committed.**
  IPC ACK/sidecar success now proves that the expected response body actually
  materialized before saving the snapshot, logging
  `ipc_materialization_missing_response` and falling back when the editor
  returns prompt-only or partial response content. `agent-doc commit` also
  refuses the `snapshot == HEAD` already-current no-op when an active captured
  response is absent from the staged snapshot, leaving the cycle recoverable
  through `agent-doc write --commit <FILE>`.

- **Preflight baseline capture is tied to the stable visible diff.** Preflight
  now waits for the shared editor typing indicator before any document-mutating
  recovery, commit, pending maintenance, or duplicate prompt residue cleanup.
  The emitted baseline is saved from the same stable visible content used for
  diff computation, preventing cleaned baselines from diverging from editor
  replayed prompt/comment content.

- **Generated post-exchange duplicate prompt comments are cleaned.** IPC
  snapshot dedupe and final template reconciliation remove ordinary HTML
  comment bodies after `agent:exchange` only when they duplicate or
  near-duplicate a prompt already present in the exchange and lack
  baseline/snapshot/current-visible ownership proof. Unrelated and visible
  scratch comments stay user-owned and remain outside `agent:exchange`.

- **Route pre-dispatch preserves visible scratch comments.** `agent-doc route`
  still removes exact duplicate answered prompt tails before sending a routed
  reopen, but ordinary post-exchange HTML comments already visible in the file
  are ownership-protected instead of being emptied as duplicate prompt residue.

- **Lower-agent job packet MVP.** `agent-doc plan` now emits deterministic
  lower-agent routing fields (`dispatch_candidate`, task class, risk,
  parallelism, model tier, context budgets, write scope, proof requirements,
  dispatch mode, and tsift context commands). New `agent-doc jobs
  create/list/status/collect` commands generate `agent-doc-job-packet-v1`
  markdown packets under `.agent-doc/jobs/<cycle>/`, expand compound `do`
  directives into one packet per target, derive target-specific write scopes
  from backlog path references, optionally write operation docs, attach tsift
  context and bounded graph acceptance evidence when available, and collect
  validated `agent-doc-worker-result-v1` envelopes for parent review without
  applying patches or bypassing finalize.

- **tsift dispatch-trace audit data now rides with graph-backed orchestration.**
  `agent-doc plan` / `orchestrate` now collect `dispatch-trace-v1` alongside
  graph-db evidence and conflict matrices, fail closed on missing projection
  hashes, worker feedback, replay/repair commands, or graph links, and attach
  that audit context to each normalized lower-agent job packet. Sequential/DAG
  child closeouts now append a hidden `worker_result` line with status, target
  id, touched files, tests, and follow-up ids before `finalize`, allowing the
  next tsift projection to connect worker outcomes back into graph evidence.

- **tsift conflict-matrix orchestration now carries the full planner contract.**
  `agent-doc plan` and orchestration prompts now preserve the
  `conflict-matrix-v1` context-pack, cached diff, impact, ranked candidate,
  conflict, worker prompt packet, token budget, semantic ranking fields, and
  normalized lower-agent job packet emitted from tsift. Graph-backed
  plan/orchestrate now rejects stale or underspecified envelopes before
  dispatch: evidence packets must be `graph-db-evidence-v1` with packet ids,
  projection hashes, replay commands, and repair commands; conflict matrices
  must be `conflict-matrix-v1`; worker packets must be
  `worker-prompt-packet-v1` with packet ids, projection hashes, token budgets,
  and explicit fail-closed prompt text. Parallel orchestration now blocks
  unless tsift explicitly reports `can_parallel=true` and
  `fail_closed=false`, so shared symbol/test risks cannot slip through just
  because they are not file-level fail-closed conflicts.

- **IPC duplicate-response detection now uses normalized response deltas.**
  IPC timeout fallbacks and ack-content normalization fallbacks compare the
  normalized `base -> content_ours` response insertion hunks against the
  current `agent:exchange` before adopting editor-applied content. Boundary
  churn, ordinary comments, and prompt-prefix-only normalization are ignored,
  but a single overlapping response body line is no longer treated as proof
  that the plugin applied the full response. This prevents both false adoption
  and CRDT replay of an already-visible editor response.

- **Pending-done guard now distinguishes kept-open pending mutations from completion.**
  Same-cycle `--pending-edit`, `--pending-gate`, `--pending-ungate`,
  `--pending-reorder`, and gate-type edits record a kept-open id ledger that
  suppresses missing-`--done` warnings for items intentionally left active or
  gated. The guard still scans response text for real completion signals,
  including `### Re: do [#id]` headings with later commit/push/verification
  evidence, so completed `do #id` batches no longer slip through just because
  ids only appeared in the response heading.

- **Mixed duplicate-scaffold closeouts now fail closed.**
  When a duplicated template scaffold lands between two `agent:exchange` close
  markers and strands live prompt text in that duplicated segment, the
  closeout normalizer now refuses automatic repair and logs a typed
  `flow::document_mutation` event with `reason=mixed_duplicate_scaffold_tail`;
  editor/FFI normalization also rejects the shape. Pure duplicated scaffold
  with no live text is still dropped automatically, but mixed live-typing
  content is preserved for explicit recovery instead of being reordered or
  duplicated during closeout.

- **Legacy full-content editor IPC proof remains diagnostic.** The binary keeps
  source-buffer proof helpers, but first-party CLI paths now skip `fullContent`
  emission by default and editor plugins no longer apply legacy/foreign
  whole-document replacements.
  Bumped local plugin builds to JetBrains `0.2.127` and VS Code `0.2.20`.

- **FlowCore now has an executable guard/proof regression gate.**
  Routed-reopen prompt-ready and dispatch-proof failure reasons now pass through
  `RoutedReopenGuardReason` instead of free-form strings from `route.rs`, and a
  source-token budget test flags unaudited new hot-path guard/proof/reason
  tokens before they can bypass the owning FlowCore enum/event.

- **Clear Session Context no longer treats the `agent-doc` wrapper process as
  blocking evidence by itself.** File-scoped `session clear` now blocks on
  protected prompt input or explicit busy cues such as an active Codex turn,
  hook-review prompt, or help screen, but proceeds for ordinary idle/status
  panes even when `pane_current_command=agent-doc`. JetBrains now parses legacy
  `active_agent_doc` clear refusals as typed busy-session warnings and exposes a
  standalone `Interrupt and Clear Session Context` action. Bumped the JetBrains
  plugin build version to `0.2.126`.

- **Template closeout uses one prompt reconciliation pass before visible writes.**
  Direct template/CRDT disk writes, IPC timeout fallbacks, and repair replays
  now run the same duplicate-prompt reconciliation that IPC snapshots use,
  before saving snapshots or replacing the document. The scanner is
  response-block aware, so prompt text quoted in assistant prose is preserved
  while duplicate live prompt copies are removed before closeout.

- **Editor IPC patches now prove the live buffer generation before mutation.**
  JetBrains and VS Code capture the editor buffer text plus generation after
  typing debounce and re-check that proof immediately before component append,
  socket IPC, and full-content repair writes. Stale generation mismatches now
  reject the editor mutation without ACK, and socket `status:error` acks are no
  longer treated as successful delivery. Bumped local plugin builds to JetBrains
  `0.2.125` and VS Code `0.2.19`.

- **Visible writes now prove the merged current document is still current.**
  Template/CRDT disk writes, IPC timeout fallbacks, and repair replays now
  re-read the session markdown after the active-typing guard and fail closed if
  the file changed after the response merge was computed. This keeps late
  scratch-comment or live exchange typing visible for the next cycle instead of
  committing a stale merge that can reintroduce duplicate/corrupted content.

- **FlowCore active-typing guard now blocks visible document writes.** Direct
  disk write paths consult `flow::document_mutation` before snapshot/document
  mutation and fail closed when the shared typing indicator never reaches idle.
  JetBrains and VS Code patch watchers now treat typing-debounce timeouts as
  no-mutation retry states instead of applying patches or boundary reposition
  while the user is still typing. Bumped the JetBrains plugin build version to
  `0.2.123`.

- **FlowCore owns the next closeout, mutation, and session-cycle slices.**
  `agent_doc_template::patchback` now parses and classifies template patchback shapes
  before visible writes across template, stream, IPC, and repair replay paths,
  including orchestrate-origin plain-response rejection; `flow::document_mutation`
  emits the file-scoped FlowEvent/ops-log adapter. `flow::closeout` owns
  the strict terminal transaction for commit, snapshot convergence, parent
  gitlink verification, session-check, and fallback-patch cleanup. `preflight`
  and `plan` now share `flow::session_cycle` prompt-target and finalize-command
  helpers so pending `--done` / cross-document add requirements come from one
  typed cycle contract.

- **Template patch sanitization moved into `agent-doc-template`.** Escaping
  agent component markers inside patch payloads is now owned by
  `agent_doc_template::sanitize`; orchestration calls that focused module
  directly and no longer defines write-local sanitization helpers.

- **Routed-reopen FlowCore owns the authoritative actor action slice.** The
  authoritative actor ready-wait facts, retry budgets, recovery hints,
  delivery-action classifier, and dispatch-start proof typing now live in
  `flow::routed_reopen`. `route.rs` maps tmux/supervisor/controller runtime
  facts into those pure helpers, then performs only the selected side effect.

- **Routed-reopen FlowCore owns the first route decision kernel.** Delivery mode,
  dispatch-start proof, degraded-authority refusal, runtime guard, and
  prompt-ready-barrier classifiers now live in `flow::routed_reopen`; `route.rs`
  maps supervisor/controller facts into those typed decisions and remains the
  tmux/supervisor/controller I/O coordinator. The large route test module was
  split out to `src/route/tests.rs` so live tmux fixtures no longer live inline
  in production routing code.

- **FlowCore mirror-mode typed events are in place.** Added the first `flow` module set for session-cycle, routed-reopen, closeout, document-mutation, operator-clear, and orchestration-batch ownership; ops summary now groups `flow_event` diagnostics by flow stage, and route/closeout/write paths emit initial mirror events for prompt-ready failures, commit closeout completion, and malformed patchback parse failures. The new flow map documents hot-path ownership and duplicated state checks for the next extraction phases.

- **Clear Session Context recognizes Codex's `Write tests for @filename` idle placeholder.** Operator status/clear readiness now treats the current dim Codex suggestion `› Write tests for @filename` as prompt-ready idle evidence, so an `agent-doc` wrapper pane with only that placeholder and the Codex model/cwd/context footer no longer stays classified as `alive-busy prompt_ready=false`. Real drafted input, queued drafts, shell search, active permission prompts, and panes showing `Working (... esc to interrupt)` still fail closed.

- **Codex Stop parent-pointer regression now accepts earlier strict-closeout blocks.** The Stop-hook submodule closeout regression now only requires stale parent gitlink drift when the response commit advanced inside the submodule and the parent-pointer commit is the failing layer. If strict closeout fails earlier before the submodule commit advances, the hook still blocks and preserves tracking, and the spec now states that no parent gitlink drift is required in that branch. This closes `#wnj2` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Clear Session Context treats an idle Codex footer below old transcript text as idle.** Operator status/clear evidence now accepts a bottom Codex model/cwd/context footer as `prompt_ready=true` even when previous assistant output remains visible above it, while drafted prompt input, queued composer state, and other busy cues still fail closed. Route dispatch still requires a real dispatch-ready prompt before injecting a reopen.

- **JetBrains Clear Session Context recognizes active-pane refusals.** The plugin now parses the binary's newer `session_clear refused ... pane ... is still active` output, including the generic `agent-doc command failed` wrapper, and shows the typed running-session warning with retry/status/interrupt/copy actions instead of surfacing the raw command failure. Bumped the JetBrains plugin build version to `0.2.122`.

- **JetBrains Clear Session Context typed warning was live-validated.** A live IDEA replay against `tasks/agent-doc/agent-doc-bugs2.md` now surfaces the typed running-session warning for an active `agent-doc` pane, including retry guidance, interrupt-clear recovery, and the latest pane output. The editor spec and regression suite now pin that observed warning shape.

- **JetBrains Clear Session Context keeps live-pane busy evidence authoritative.** A follow-up 0.2.122 replay showed the actor/controller projection can be `ready` while the direct Codex pane is still running the active `agent-doc` turn. The JetBrains refresh-retry readiness helper now has coverage that `alive-busy prompt_ready=false` does not retry clear from that state; the spec names waiting, refresh-after-idle, or explicit interrupt-clear as the valid operator choices.

- **Terminal user follow-ups no longer emit late closeout no-ops.** When the previous cycle is already committed and the working tree only contains a new user follow-up prompt, `agent-doc commit` now treats that state as prompt handoff instead of re-emitting `commit_noop` / `commit_already_current` lifecycle bookkeeping. Open recovery cycles can still close as already-current when needed, but idle post-finalize prompt typing no longer looks like another delayed closeout.

- **CI checks out sibling path dependencies.** Pull-request CI now clones `btakita/agent-kit` and `btakita/tmux-router` next to the `agent-doc` checkout before running `make check`, matching the local workspace layout required by the `../agent-kit` Cargo path dependency and the `Cargo.toml` tmux-router patch.

- **CI now names the tmux integration leg explicitly.** The GitHub Actions workflow labels the normal suite as `Run make check` and the live tmux sweep as `Run make tmux-ci`, with a visible `Running make tmux-ci` marker in the step log so reviewers can confirm the tmux leg executed.

- **Preflight now cleans duplicate prompt scratch comments before baseline capture.** When a submitted prompt is already present in `agent:exchange` and the same text remains in the ordinary HTML comment below `<!-- /agent:exchange -->`, preflight removes only that duplicate comment before the previous-cycle commit/baseline step. Unrelated scratch comments remain outside exchange, and the snapshot still excludes the live prompt. Added a focused preflight regression and updated the closeout spec.

- **Compact Exchange IPC is no longer blocked by committed response-cycle state.** The compact command now sends its full-document replacement through an operator-mutation IPC path, so JetBrains can apply Compact Exchange through the Document API even after the prior agent-doc response is already committed instead of falling back to a direct disk write and surfacing an external-file-change dialog. Added a regression for committed-cycle Compact Exchange IPC and refreshed the shared editor spec.

- **Clear Session Context recognizes the default Codex idle placeholder.** The operator status/clear readiness path now treats `› Ask Codex to do anything` plus the Codex model/cwd/context footer as an idle composer, so JetBrains Clear Session Context does not fail closed with `current_command=agent-doc prompt_ready=false` when the pane is only showing the default Codex placeholder/status UI. Drafted prompt input, queued drafts, and shell search still fail closed and point to `session interrupt-clear`.

- **Post-exchange hidden prompt duplicates are cleaned during closeout.** Final template reconciliation and IPC ack-content snapshot dedupe now remove ordinary HTML comments below `agent:exchange` only when the comment body duplicates or near-duplicates an exchange prompt, including already-answered prompt residue after boundary repositioning, while preserving unrelated scratch comments outside the exchange. Added focused write-path regressions and updated the closeout spec.

- **Starting actor route timeouts now coalesce per generation.** When repeated editor reroutes hit the same authoritative actor pane while that current generation is still booting, route now records one typed `route_authoritative_actor_starting_not_ready` timeout for that pane/generation and logs later retries as coalesced waits until the actor reaches ready, closed, or blocked. Added route-state regressions plus SimWorld coverage for the repeated-starting-timeout schedule. This closes `#rtbr` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Supervisor PTY filter diagnostics no longer print into managed prompts.** Child-output Kitty keyboard-mode preserve/drop traces are now opt-in via `AGENT_DOC_TMUX_INPUT_DIAG` / `AGENT_DOC_DEBUG_STDIN`, so normal Claude Code prompt editing and history search do not show `[agent-doc] tmux_input_event source=supervisor.pty_filter ...` lines in the managed pane. Route, queue, supervisor IPC, auto-trigger, tmux submit, and permission-prompt input diagnostics remain available at their normal input boundaries.

- **Drained auto queues now clean up already-completed residue on preflight.** If an `agent:queue auto` block has no remaining prompt entries because every item was already marked complete, preflight now clears the queue body, removes `auto`, syncs the snapshot, and leaves `queue_active: false` instead of preserving a completed queue run for later cycles.

- **Direct active-queue runs are explicitly single-step resumable.** Bare-path / `run` invocations now synthesize the active queue head when `queue_active: true` and the document has no diff, consume one queue prompt before strict closeout, and print a continuation diagnostic when an `agent:queue auto` block still has prompts remaining. Re-running the same command advances the next prompt instead of silently no-oping.

- **Prompt-prefix normalization now preserves committed ownership state through fallback closeouts.** The write path preserves HEAD prefix state when rebuilding `content_ours`, repairs stripped prompt prefixes in sidecar/content_ours fallback paths, and covers bare final prompt repair after merge/adoption. Focused regressions cover committed assistant lines staying unprefixed, committed user prompts staying prefixed, IPC sidecars that strip `❯ `, and final bare prompt repair. This closes `#pfxleak2`, `#bppfxstrip2`, and `#lastpfx` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Active queue prompts no longer get hidden behind empty document diffs.** When `queue_active: true` and the document matches its snapshot, `preflight` and `plan` now synthesize the queue head item as the prompt diff, so `agent-doc <FILE>` opens a real cycle instead of returning `no_changes=true`. Added regressions for the `#oobpmt` queue-resume shape and updated the git integration spec.

- **Preflight now warns on harness/document mismatch.** `agent-doc preflight` compares frontmatter `agent:` against the active Claude Code, Codex, or OpenCode harness, emits a structured `harness_mismatch` warning without blocking intentional handoffs, and the skill contract tells harnesses to surface it while keeping active-harness attribution and closeout behavior.

- **Direct template writes now strip safe progress chatter before exchange patchbacks.** When a direct `agent-doc <FILE>` / write closeout receives plain progress commentary followed by a valid `patch:exchange`, the write path now reuses the replay guard and applies only the sanitized patch body. Trailing, interstitial, transcript-shaped, or full-document unmatched content still fails closed instead of being appended into `agent:exchange`. This closes `#rspdigest` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **CRDT live prompt prefix-variant duplicates are repaired before closeout.** When live typing races with IPC/CRDT writes and leaves adjacent prompt lines where an earlier partial line is a prefix of the completed prompt, the write path now repairs only the live prompt tail after the last exchange boundary, preferring the longer line and leaving assistant prose untouched. IPC snapshot dedupe uses the same repair before saving or redelivering editor content. Added regressions for the observed OpenCode arrow-key prompt duplication shape and documented the plan in `tasks/agent-doc/plan-crdt-live-prompt-prefix-duplicate.md`.

- **CRDT closeout now fails closed on duplicate scaffold mixed with live user text.** Template normalization now runs the duplicate-scaffold repair path when CRDT/write merging creates a second `<!-- /agent:exchange -->` close marker with copied queue/backlog/done scaffold. Pure duplicated scaffold is repaired, but mixed scaffold plus live user text is rejected instead of being committed or silently dropping text. Added regressions for the observed `agent:exchange` live-typing corruption shape and documented the plan in `tasks/agent-doc/plan-crdt-duplicate-scaffold-closeout.md`.

- **Claude skill auto-update no longer defaults to context compaction.** Rendered Claude and shared instruction surfaces now use `agent-doc skill install --harness claude --reload restart` by default and reserve `--reload compact` plus `/compact` prompting for sessions that explicitly opt into `agent_doc_auto_compact` in frontmatter or project `.agent-doc/config.toml`. Updated the harness runbook and closeout docs so large-session/session-accretion signals stay advisory instead of triggering an implicit Claude compaction path. This addresses the latest `root.md` auto-compaction report in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Clear Session Context now ignores Codex dim placeholder text.** Protected-input detection for `agent-doc session clear <FILE>` now captures ANSI pane state and treats Codex faint placeholder text as idle chrome, so JetBrains clear no longer refuses with `reason=drafted_prompt_input` when the live pane only shows placeholder/status UI such as `gpt-5.5 high ... Context ... used`. Real non-dim typed prompt input, queued drafts, and shell search still fail closed and point to `session interrupt-clear`. This addresses the latest JetBrains Clear Session Context blocker in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Live-typing duplicate prompt repair now happens before IPC snapshots commit.** Socket and file IPC closeouts compare the post-apply exchange against the pre-write exchange and remove any extra copy of an already-present user prompt line, preferring the normalized `❯` prompt form. The write path logs `ipc_prompt_duplicate_repaired` before saving the snapshot, and `session clear` can now proceed on an unprotected live pane even when stale startup projection and fresh user prompt drift coexist. A named SimWorld regression now covers the JetBrains Clear Session Context sequence: stale starting actor clear, prompt-only document drift, dispatch-only reroute blocked until ready, and duplicate prompt repair before commit. This addresses the latest JetBrains `/clear` plus duplicate prompt report in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Answered closeout markers no longer keep committed cycles open.** `session-check` now treats active-session post-commit drift as closed when there is no unresolved prompt marker and the remaining differences are confined to answered exchange metadata (`❯`, `(HEAD)`, boundary ids) plus backlog metadata. Unrelated status/body edits still fail closed. Added regressions for the unfinished `agent-doc-bugs2` closeout shape and documented the follow-up plan in `tasks/agent-doc/plan-finish-closeout-after-answered-marker-drift.md`.

- **Sidecar normalization fallback now has direct repair diagnostics coverage.** A regression recreates the `#normfallback` shape from `tasks/agent-doc/agent-doc-bugs2.md`: the plugin ack-content sidecar strips a required prompt prefix, the binary rejects that primary snapshot with `reason=prefix_divergence`, repairs the snapshot and working tree from the normalized fallback, and records the `sidecar_normalization_fallback_repaired_working_tree` ops-log marker required by the closeout spec.

- **Stale preflight repair now has direct stale-checkpoint race coverage.** A regression now binds a partial-response checkpoint writer to an open `preflight_started` cycle, lets repair abandon that stale prompt-bearing cycle, and proves the original writer stops with `partial_response_checkpoint_stopped` instead of writing another checkpoint for the abandoned cycle. The backend spec names stale-preflight abandonment as part of the checkpoint stop contract for `#staleckpt` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Starting actor route waits now have deterministic prompt-barrier coverage.** The route wait decision is factored into a pure poll classifier and covered for the `starting -> busy -> ready` schedule: dispatch remains blocked through restart-bootstrap `busy` and through `ready` without prompt proof, then releases only when ready state, dispatch-ready prompt proof, and dispatch eligibility agree. The route spec now names that conjunctive gate for `#startroute` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Pending-only empty write closeouts cover completed items.** `write --commit` with empty stdin now has regression coverage for `--done` as well as `--pending-add`: it reaps and archives the completed item, commits the document, leaves the exchange untouched, and passes `session-check`. The closeout spec now names both add-only and done-only pending mutation shapes for the `#writeempty` contract in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Terminal closeout lifecycle updates are idempotent.** Repeated repair/replay/no-op bookkeeping after a cycle is already `Committed` no longer rewrites cycle-state, refreshes committed capture timestamps, or re-emits `capture_committed_after_replay`; late fallback rejection diagnostics now include the patch id. `agent-doc ops summary` also separates `commit_noop drift_kind=none` and protected-input clear refusals into expected-behavior buckets so routine no-op closeouts and fail-closed clear guards do not read as actionable bugs. This closes `#sp3q` and `#s8cs` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Agent-doc managed-cycle cleanup now avoids stale closeout work.** Orchestrate wraps clean plain template-mode child responses as explicit `patch:exchange` closeouts, avoiding the zero-template-patch write path; partial-response checkpoint writers stop once their original cycle commits, is abandoned, or is replaced; route-owned reap diagnostics now report `post_commit_user_follow_up` when the remaining dirty document content is a new user prompt; safe-passive sync uses live local actor projections before controller actor lookup; and repeated managed network child proof in one process reuses a same-command/args/environment success. This closes `#ds58`, `#djwb`, `#m2hx`, `#ha62`, and `#aymr` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Codex non-streaming patchback now filters progress chatter.** Direct Codex child response capture now selects the last `item.completed` `agent_message` before `turn.completed` as the durable response body instead of concatenating every assistant message from the JSONL stream. Multiple assistant messages without a final turn boundary now fail closed as ambiguous, preventing progress/status prose from being committed into template session documents. This closes `#codexverbosepb` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Codex required-SSH drift detection now requires live SSH evidence.** Command-execution parsing no longer treats arbitrary command output that merely mentions a required host plus an old `socket: Operation not permitted` as active SSH capability loss, so searches through `.agent-doc/captures` or logs cannot abort a resumed Codex run. Actual SSH commands still fail closed on bare EPERM output, and failure details now include the command string. This closes `#sshcapfalsecapture` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Codex hook-review route blockers now include recovery guidance.** Dispatch-only reroutes that see `route_dispatch_only_blocked reason=codex hook review prompt` now tell the operator to open `/hooks`, approve or disable the pending hook change, wait for the idle composer, and rerun the route/editor action instead of falling back to a generic idle-prompt hint. Updated route specs and regression coverage. This closes `#hookreviewroute` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Ops summaries now separate expected follow-up noise from anomalous drift.** `agent-doc ops summary` now buckets benign `post_commit_user_follow_up`, `post_commit_local_drift kind=user_follow_up`, and `commit_noop drift_kind=user_follow_up` events separately from working-tree drift/noop diagnostics. No-op closeouts now log their drift kind in `ops.log`, making routine user follow-up reruns distinguishable from real post-commit local edits. This closes `#opsnoisereduce` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Interrupt-clear timeouts now preserve final blocker evidence.** `agent-doc session interrupt-clear <FILE>` timeout logs and user-facing errors now report the final live-pane state, evidence source, prompt-ready value, current command, and recent pane tail after the protected clear discard path, instead of reducing the result to `outcome=timed_out` plus a loose last command. This closes `#interruptcleartimeout` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Backlog-section prompt patchback cleanup.** Template and CRDT closeout now remove newly-added raw prompt-target lines from `agent:backlog` / legacy `agent:pending` after the response is merged into `agent:exchange`, while preserving normal tracked backlog edits and pending state changes. This closes `#backlogorphan` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Sequential orchestration parent closeout now survives stale binary paths.** Parent-owned lifecycle commands in `agent-doc orchestrate --mode sequential --from-exchange` now resolve a launchable `agent-doc` binary before spawning `preflight`, `finalize`, or `session-check`, falling back when `current_exe()` points at a binary removed during local install work. Spawn failures include binary, cwd, and PATH-presence context, and regressions cover sanitized PATH and stale-current-exe resolution. This closes `#synchorchstop` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Sequential orchestration now freezes exchange task lists.** `agent-doc orchestrate --mode sequential --from-exchange` records the source markdown task list at parent start and rechecks it after each child closeout. If the live list is edited mid-run, the parent writes a deterministic interruption response, leaves remaining and newly added tasks open for the next explicit run, and exits before launching the next step instead of hanging. This closes `#orchmidrun` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Interrupt-and-clear now recovers Vim/Neovim prompts.** The explicit `agent-doc session interrupt-clear <FILE>` discard path now watches the managed pane after sending harness interrupt keys. If the interrupt opens Vim/Neovim, it sends one forced `:qa!` recovery before continuing the idle/closed wait; if the pane still does not settle, the timeout names the last observed command and gives an exact manual recovery action. Editor specs now keep that recovery in the binary-owned path. This closes `#clearinterruptvim` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Supervisor-to-tmux input now has raw end-to-end coverage.** The live tmux suite now includes a supervisor IPC test that drives the real tmux pane input path into a raw harness process and asserts the submitted prompt text, Enter delivery, arrow-key escape sequences, and final Enter bytes. This closes `#tmuxe2etests` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Tmux input paths now emit structured diagnostics.** Route, queue dispatch, supervisor IPC/auto-trigger injection, harness-aware tmux submits, stdin forwarding transforms, Kitty keyboard-mode preserve/drop decisions, and OpenCode permission-prompt key translations now emit `tmux_input_event` lines with source, destination, transform, key, byte count, and harness where known. Prompt text is represented by length plus SHA-256, giving regressions stable log assertions without leaking raw typed content. This closes `#opencodeinputdiag` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Route-owned reap no longer preserves panes for stale renderer tails.** The route-owned completion guard now trusts the supervisor actor's `ready` prompt state when deciding whether a committed one-shot pane can be reaped, while still preserving panes for explicit blocking prompt states such as queued drafts, permission prompts, hook-review prompts, history search, and clean-exit restart prompts. Managed PTY filtering also strips OSC title updates so transient title text such as `Working ... esc to interrupt` cannot enter prompt sampling. This closes `#ownedreapbusy` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Clear/restart now guard starting owned panes before tmux input.** Session operator clear/restart no longer trust controller acceptance alone while the actor record, matching or legacy session-scoped supervisor runtime, or matching supervisor lease still says `starting`. Clear now requires a dispatch-ready composer and a clean post-commit document hash before submitting `/clear`; restart allows either dispatch-ready composer evidence or the clean-exit restart prompt, but also fails closed on post-commit document drift. Refusals log `session_operator_starting_guard_refused`. This closes `#clearstartingrace` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Orchestrated template closeout now accepts clean plain child responses.** `write` no longer requires an explicit `patch:exchange` block for orchestrate-origin template responses when the child returned a single clean assistant body; the existing unmatched-content synthesis appends it to `agent:exchange`. Patch-bearing orchestrate responses still require `patch:exchange`, mixed patch/unmatched output still fails, and transcript-shaped, full-document, or multiple-response dumps are rejected before write. Updated orchestration and closeout specs with focused write-path regressions. This closes `#orchplainresp` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Late IPC fallback writers now stop at committed cycle state.** The write path now distinguishes a committed-cycle IPC skip from a consumed IPC patch, cleans stale fallback patch JSON with a claimed-patch sentinel, and avoids logging `ipc_write_consumed` / re-running already-current closeout work for a terminal cycle. Added regression coverage and documented the terminal IPC cycle guard. This closes `#latefallbackloop` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Direct pane submit telemetry no longer reports proven Codex reroutes as false timeouts.** Route now records direct tmux input acceptance latency separately from the later harness dispatch-start proof, waits to classify the direct-submit outcome until proof is known, and budgets the direct pane submit path around the full tmux/control-mode acceptance window plus capture-poll slack. If Codex proves the routed prompt was consumed after pane-input acceptance was not directly observable, ops logs now say `acceptance_unobserved_dispatch_proven` instead of `timed_out` / `over_budget`. Updated route regressions and session tmux specs. This closes `#directsubmitbudget` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Starting actor reroutes now refresh terminal lifecycle states immediately.** While route is waiting for a `starting` authoritative actor to become dispatch-ready, a supervisor refresh to `closed` or `blocked` now stops the wait and surfaces that terminal actor state instead of burning the startup-ready timeout and reporting stale `starting` state. Updated route specs and added SimWorld plus tmux-backed route coverage. This closes `#startreadytimeout` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **OpenCode live-pane submits now send real Return instead of newline.** Harness-aware tmux submissions use OpenCode's Kitty keyboard Return sequence for routed reopens, supervisor IPC injects, auto-triggers, and file-scoped `/clear`, so OpenCode panes whose TUI keymap distinguishes `return` from `ctrl+j` submit the prompt instead of inserting a blank line. Updated the session tmux spec and tmux-router coverage.

- **Completed work can now live in an explicit external done archive.** `agent:done archive=<repo-relative>.done.md` appends reaped backlog/icebox entries to the named markdown file instead of growing the session document, creates the archive when missing, rejects unsafe paths, suppresses duplicate retry entries, and lets preflight/session-check use archived IDs as dropped-history proof. Updated pending specs and runbook guidance. This closes `#donearchiveattr` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Clear Session Context no longer blocks ordinary active/status panes.** File-scoped `agent-doc session clear <FILE>` is treated as an explicit operator action again: direct `alive-busy` evidence alone no longer fails closed, so JetBrains/VS Code Clear Session Context does not get stuck behind Codex status/footer panes such as `gpt-5.5 high ... Context 60% used`. The remaining clear guard is scoped to protected prompt-input states such as permission prompts, queued drafts, shell search, or drafted user input; those refusals record `session_clear_protected_input_guard_refused` and point operators to `agent-doc session interrupt-clear <FILE>` for an intentional discard. This closes the latest Clear Session Context repro in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Editor sync guards no longer stay wedged after a dead-pane sync stalls.** JetBrains and VS Code now bound plugin-spawned layout-sync subprocesses. If one stalls while the binary is dealing with killed or stale tmux panes, the plugin terminates that subprocess, releases its local sync guard, and leaves the latest selection pending so a retry can run the binary recovery path instead of permanently showing `Sync deferred: another tmux layout sync is already running`. Updated shared editor specs and bumped local plugin builds to JetBrains `0.2.118` and VS Code `0.2.17`.

- **Managed OpenCode permission arrows no longer leak escape text.** While the supervisor sees an active OpenCode `Allow once` / `Allow always` / `Reject` permission prompt, legacy arrow-key escape sequences from stdin are translated to the prompt footer's Tab/BackTab selector controls before they reach OpenCode. Normal OpenCode prompt editing remains unchanged, and the regression covers the `^[[C` / `^[[D` leak shape from a live permission dialog.

- **Editor prompt answers now run from the owning session cwd.** `agent-doc prompt --all` entries include `cwd`, and the JetBrains/VS Code prompt UIs use that root when calling `prompt --answer` instead of assuming the current IDE workspace root. Failed answer submissions now clear the temporary suppression key so the still-active prompt can reappear. Added process-level JetBrains coverage for the prompt-answer command cwd and a live tmux integration regression proving OpenCode answers send Tab rather than a raw left/right arrow escape. Bumped local plugin builds to JetBrains `0.2.117` and VS Code `0.2.16`.

- **Editor prompt answers now use the `prompt --answer` positional contract.** JetBrains and VS Code prompt UIs accept flat `agent-doc prompt --all` entries with `selected`, keep the selected state in their prompt item model, and send the selected option's one-based position to `agent-doc prompt --answer` instead of forwarding the displayed TUI option number. Bumped the local-testing plugin builds to JetBrains `0.2.116` and VS Code `0.2.15`.

- **OpenCode permission prompt answers now use the actual TUI selector state.** `agent-doc prompt --answer` now captures OpenCode panes with ANSI attributes before parsing, so it can read the highlighted `Allow once` / `Allow always` / `Reject` option instead of falling back to option 0. OpenCode automation now moves with the prompt footer's Tab/BackTab selector contract rather than arrow keys, matching the live failure evidence where arrows leaked into the prompt as literal `^[[C` / `^[[D` text.

- **OpenCode permission prompts now preserve keyboard negotiation.** The OpenCode supervisor preserves OpenTUI's Kitty keyboard-mode sequences instead of stripping them with terminal query noise. The prompt-answer path relies on the prompt footer's Tab/BackTab selector contract and still accepts the `Allow always` follow-up confirmation prompt.

- **OpenCode dispatch-only startup probes now use the OpenCode redraw budget.** JetBrains `Run Agent Doc` can hit an OpenCode pane just after the controller has seen the idle splash but before the second startup-window guard catches the same prompt. Dispatch-only routing now gives OpenCode the longer harness-specific prompt/recovery budget instead of the short Codex-style boot probe, avoiding false `latest run is still booting` refusals after OpenCode is already accepting input.

- **OpenCode idle splash now promotes managed sessions to ready.** OpenCode 1.14 can render an idle composer as the splash chrome (`Ask anything...`, build-plan text, command/footer hints, cwd/version status) without a standalone `>` prompt or `context ... % used` footer. Shared harness readiness now treats that chrome-only splash as dispatch-ready, so start, route, and session status promote the actor instead of timing out with `route_authoritative_actor_starting_not_ready` after the capability proof succeeds.

- **Managed capability proof results now use tmux status messages.** Successful and failed Codex/OpenCode/Claude managed proof diagnostics still go to the session log, but `start` now surfaces the user-visible `[start] managed ... capability proof` line with `tmux display-message` targeted at the owned pane instead of writing it into the agent pane transcript. This keeps proof diagnostics from interfering with TUI prompt detection or the next agent input.

- **OpenCode proof output no longer strands startup in `starting`.** OpenCode prompt readiness now ignores supervisor capability-proof diagnostics and treats an otherwise chrome-only `context ... % used` footer as an idle composer. That lets route/start promote a proven OpenCode actor to `ready` and dispatch the trigger instead of timing out with `route_authoritative_actor_starting_not_ready` after `opencode_capability_proof status=proven`.

- **Strict closeout now reports slow commit phases and fails explicitly on stale parent gitlinks.** `finalize` / strict `write --commit` record a `closeout_latency` diagnostic when response durability crosses the closeout budget, with per-phase timings for commit retries, cycle-state checks, session-check, and cleanup. Submodule-hosted documents now fail closed after the bounded parent-pointer retry if the parent `HEAD:<submodule>` still differs from the submodule `HEAD`, naming `agent-doc commit <FILE>` as the idempotent recovery. This closes `#rspcmtdelay` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **JetBrains Run Agent Doc now forces the plain reopen prompt.** The JetBrains action calls `agent-doc route --dispatch-only --plain-trigger`, and route applies that flag by sending `agent-doc <FILE>` even when the document's normal harness trigger template is slash-command based. This keeps editor reruns from injecting `/agent-doc ...` into sessions such as `root.md` where the IDE action must send the plain Codex-compatible form. Bumped the JetBrains plugin build version to `0.2.114`.

- **Cross-harness JetBrains reruns can replace stale actor records.** Route now treats a stored harness mismatch as authoritative only when the old actor still has a healthy live supervisor and a non-closed state. Dead panes, closed actors, and unreachable supervisor records fall through to fresh start/rebind, so running JetBrains `Run Agent Doc` in Claude after closing a Codex session no longer fails on `bound to harness codex, not claude-code`. Updated route specs and added focused coverage for the live-vs-stale mismatch guard.

- **OpenCode managed sessions now prove required SSH before dispatch.** OpenCode startup now records `opencode_capability_proof` for SSH-gated documents, runs a bounded `opencode run --format json` child probe with isolated SSH options, and blocks auto-trigger, supervisor injection, managed route, and dispatch-only route until the current proof succeeds. OpenCode child probe failures such as `socket: Operation not permitted` now fail closed as managed-pane SSH capability denial instead of letting the agent discover the sandbox error mid-response. Route and session-status proof checks are harness-aware so OpenCode is not incorrectly held to Codex writable-root contracts. This closes `#opencodecapfail` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Post-commit follow-up prompts no longer look like missed patchback repair.** When `commit` sees `snapshot == HEAD` and the live file only adds a later user follow-up, it now logs a dedicated `post_commit_user_follow_up` marker and suppresses `prior_patchback_without_response_body` / `out_of_band_write` noise. The follow-up still remains uncommitted for the next response cycle, but ops diagnostics no longer imply a missing assistant response body. This closes `#codexpatchbodyloop` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **IPC timeout closeout deletes stale fallback patches.** The CRDT stream IPC timeout path now removes the queued `.agent-doc/patches/<hash>.json` file after its local write and git commit succeed, while still leaving the claimed-patch sentinel for any watcher that already observed the file. This prevents a late editor file-watcher pass from replaying the same response after the binary has already committed it. Added a child-process regression for the exit-75 timeout path. This closes `#ipc-timeout-dup` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Clear Session Context direct-pane delivery recognizes Codex idle placeholders.** File-scoped `agent-doc session clear <FILE>` uses the resolved direct pane or supervisor path after controller authorization and idle proof. Codex status now also recognizes the current `› Explain this codebase` idle placeholder as prompt-ready evidence. This closes the follow-up JetBrains Clear Session Context repro in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Clear Session Context no longer treats Codex status-only panes as busy.** File-scoped `session status` and `session clear` now classify Codex panes that show only model/cwd/context status chrome, with no prompt input or busy cue, as direct idle evidence. That lets operator clear override stale actor/supervisor busy projection while keeping route dispatch gated on a real dispatch-ready prompt. JetBrains also drops the unused response-status busy FFI surface, documents that Clear Session Context must always ask the binary instead of blocking on plugin-local busy state, and bumps the JetBrains plugin build version to `0.2.113`. This closes the latest JetBrains Clear Session Context stale-busy repro in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Dispatch-only Codex proof gating now explicitly covers ready actor reroutes.** The hook-visible Codex accepted-but-unproven guard already lives in the shared dispatch-only submit helper, so ready authoritative actors and startup-window reroutes both fail closed when pane acceptance never becomes routed submission proof. Added a non-tmux regression for the accepted-only gate and clarified the README/session specs. This closes `#4w5x` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **JetBrains Clear Session Context now recognizes wrapped protected-busy failures.** The plugin parser accepts the exact `agent-doc command failed (exit 1): Error: session_clear refused ... alive-busy` notification shape and less predictable pane-tail text, so the IDE shows the typed running-session warning with Refresh/Interrupt/Status/Copy actions instead of falling back to the generic command-failed error. Bumped the JetBrains plugin build version to `0.2.112`.

- **Base-index layout repair now runs during the active preflight.** When the pre-diff layout check finds the current tmux session missing window index `0`, preflight now removes the stale deferred-repair counter, runs `repair_layout` immediately, and rechecks layout before emitting JSON. If automatic repair cannot run, stderr names the explicit `agent-doc session doctor <FILE> --repair` action instead of silently waiting for a second detection. This closes `#baseindexrepair` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Dispatch-only proof scope is explicit across harnesses.** `route --dispatch-only` now logs both `proof` and `proof_scope` so Claude Code and OpenCode accepted pane delivery is labeled as accepted-only instead of being mistaken for Codex-style consumed/submitted dispatch-start proof. Codex keeps its hook-backed dispatch-start proof behavior when hooks are visible. Added route regressions and updated the session tmux spec. This closes `#clauderouteproof` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **JetBrains real markdown navigation now always validates the actor/supervisor path.** A true `selectionChanged` event still runs the guarded background `sync --no-autostart` reconciliation even when the visible/focused signature was already marked synchronized. The immediate focus fast path remains best-effort for existing panes, while the background sync owns the safe cold-start when a document like `tasks/software/corky.md` has no actor, preventing later `Clear Session Context` from surfacing `stage=missing_actor`. Bumped the JetBrains plugin build version to `0.2.110`.

- **JetBrains Clear Session Context now surfaces protected busy panes as a typed running-session result.** The CLI still fails closed when direct live-pane evidence says the pane is `alive-busy`, but the JetBrains plugin now parses that refusal and shows a warning with the pane id, current command, and latest pane tail plus Retry clear, Show status, and Copy details actions instead of a raw `agent-doc command failed` notification. Updated editor specs and added parser/message regressions.

- **Stale prompt-bearing preflight cycles are abandoned, not placeholder-closed.** If a pane dies after `preflight_started` before any response capture exists, and the live document still has an unresolved prompt target, `repair` now abandons the stale empty cycle after the bounded timeout instead of forcing a manual placeholder response. The prompt remains in the working document, so the next `preflight` opens a fresh cycle and handles it normally; recent empty cycles still fail closed to avoid stealing a live concurrent turn. Added cycle-state, repair, and preflight regressions. This closes `#preflight-started-recovery` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Prompt+response exchange drift now fails closed.** `session-check` now treats an uncommitted appended exchange chunk containing both a user prompt and a new assistant `### Re:` / `## Assistant` marker as uncommitted response drift instead of ignoring it as prompt-bearing local drift. Prompt-only tails still route through the prompt-tail guard. Added a regression for the SessionShare root `#rspcmt7` shape where the visible response closeout landed in `tasks/root.md` but the owning repo stayed dirty.

- **Clear Session Context now works after a closed actor generation.** The project controller still rejects `blocked` actors and non-clear commands for `closed` actors, but an explicit `session_clear` operator command now records an `operator_closed` acceptance so the CLI/editor can send `/clear` to the live harness context before the next run. Added a controller regression and updated the session tmux command spec. This closes the latest JetBrains `Clear Session Context` closed-generation repro in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Harness-agnostic uncommitted exchange drift detection.** `session_check.rs` now detects when a committed cycle has exchange-content changes in the working tree that differ from the committed snapshot, regardless of which harness (Codex, OpenCode, Claude Code) owns the session. Previously `detect_active_session_post_commit_drift` required Codex session tracking (`CODEX_THREAD_ID`) and silently returned `None` for all other harnesses, allowing uncommitted responses to pass all guards. The new `detect_uncommitted_exchange_drift` function checks snapshot vs working tree directly and fires as a fallback in all three committed-cycle branches. Added regression tests proving the guard catches exchange drift without Codex session state and does not fire for status-only drift. This closes `#rspcmt6` in `tasks/agent-doc/agent-doc-bugs2.md` and extends `tasks/agent-doc/plan-response-patchback-uncommitted.md` with the harness-agnostic drift evidence.

- **OpenCode CLI-only-output anti-pattern.** The OpenCode section of `runbooks/harness-invocation.md` now explicitly names the anti-pattern of outputting a response to the CLI without piping it through `agent-doc finalize` — response text visible in the console but absent from the session document is the same closeout violation as skipping finalize entirely. The shared Hot Path Digest in `SKILL.md` reinforces that the response does not exist until it crosses `finalize` or `write --commit`. Added regression tests proving session-check catches an OpenCode prompt-only exchange tail and the runbook section names the anti-pattern. This closes `#noexchopencode2` in `tasks/agent-doc/agent-doc-bugs2.md` and follows `tasks/agent-doc/plan-opencode-no-exchange-patchback.md`.

- **Direct-chat preset write-back invariant.** When a session-document preset (for example `#commit-push`) triggers repo work through a direct Codex chat turn, the turn is not complete until the response is written back with `agent-doc write --commit <FILE>` and `agent-doc session-check <FILE>` passes. The `harness-invocation.md` runbook now explicitly states this invariant. Added a regression test proving session-check catches a prompt-only exchange tail when a direct-chat preset completes repo work but writes no response patchback. This closes `#rspcmt5` in `tasks/agent-doc/agent-doc-bugs2.md` and extends `tasks/agent-doc/plan-response-patchback-uncommitted.md` with the direct-chat closeout invariant.

- **OpenCode direct-exec session-check guard.** The OpenCode harness runbook now requires `agent-doc session-check <FILE>` immediately after `finalize` and after manual `write --commit`, matching the existing Codex fail-closed contract. `runbooks/commit.md` and `README.md` now name both Codex and OpenCode for the direct-exec post-write guard. `session_check.rs` error messages no longer reference "the active Codex session" or "the Stop hook" exclusively — they use harness-agnostic language. This closes `#rspcmt4` in `tasks/agent-doc/agent-doc-bugs2.md` and extends `tasks/agent-doc/plan-response-patchback-uncommitted.md` with OpenCode-specific closeout evidence.

- **Closeout and starting-actor diagnostics now name the next command.** `agent-doc commit <FILE>` no longer lets the "already committed" no-op message sound like a full closeout when later user follow-up prompts remain; it now says to rerun `agent-doc <FILE>` or use `agent-doc write --commit <FILE>` for a missing response. Route's `starting` authoritative-actor failure now says to wait and rerun, and names `agent-doc start <FILE>` for stuck-owner recovery. This closes `#rspcmt3` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **OpenCode harness support.** `agent: opencode` now resolves to an OpenCode managed pane with `agent-doc <file>` trigger routing, `opencode_model` / `opencode_args` frontmatter and config aliases, and a minimal non-streaming `agent-doc run --agent opencode` backend that invokes `opencode run`. This supports OpenCode model IDs such as `zai/glm-5` via the same `--model` injection path.

- **`#agent-doc-bug` declaration chains now preserve backlog order.** `agent-doc plan` now expands multiple prompt-bearing `#agent-doc-bug` declarations into ordered expected add mutations for explicit backlog targets, and logs the declaration/final insertion order for multi-item batches. The first declared bug remains above later bugs unless the response explicitly documents an intentional priority override. This closes `#bugchainorder` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Standalone boundary setup no longer advances the commit snapshot.** `agent-doc boundary` still writes a transient marker into the working document and signals the editor, but it no longer updates the saved snapshot. That prevents the next preflight/commit from turning marker-only setup churn into a noisy boundary-only git commit.

- **Route no longer dispatches into `starting` authoritative actors.** Managed and dispatch-only reroutes now wait for a `starting` controller actor to refresh to `ready` before recording a dispatch attempt or sending tmux/supervisor input; if the actor stays `starting`, route fails closed with a state-gate diagnostic instead of creating an interrupted startup cycle. `busy` actors remain eligible for one supervisor-owned queued reopen. Added route regressions and updated the routing/session-actor specs. This closes `#startingdispatch` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Stale `starting` actor cleanup no longer trusts a live PID forever.** Normal `preflight`, `start`, `sync`, and `gc` cleanup now keep a one-hour-old `starting` actor only when the recorded supervisor PID is alive and its lease heartbeat is still fresh. A stuck `agent-doc start --route-owned` process with an old heartbeat is closed and projected from SQLite on the next normal cleanup pass. Added a regression for the live-PID/stale-heartbeat case and updated the session actor specs. This closes `#startgcleak` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Direct `agent-doc run` now stops when pre-commit repair consumes the whole diff.** If the initial diff only reflected an already-committed missed patchback and the pre-commit repair brings the snapshot back to `HEAD`, `run` rechecks the diff and fails before child-agent dispatch with an `agent-doc write --commit <FILE>` recovery hint. Added an integration regression proving a configured child agent is not invoked. This closes `#emptyrsprepair` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Agent-owned partial patchbacks can be adopted from empty strict repair writes.** `agent-doc repair` now adopts already-visible responses from interrupted `response_captured` / `write_applied` cycles even when no pending response artifact remains, and strict `write --commit` with empty stdin runs that adoption path before failing as an empty response. This closes `#partialpatchbackadopt` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Blocked-stop repair now replays guard-prefixed patch payloads.** The shared replay guard now accepts known closeout guard comments such as `<!-- no-pending-capture -->` around otherwise valid patch responses, while still blocking transcript/full-document dumps. `agent-doc repair` now writes the sanitized replayable payload returned by the guard, so patch bodies extracted from leading progress commentary are actually used instead of only classified. Added replay guard, repair, and Codex Stop-hook regressions. This closes `#blockedstopextract` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Stale `starting` actor cleanup now runs on normal paths, not just daily GC.** `preflight`, `start`, and `sync` now run the lightweight controller actor cleanup every cycle, closing one-hour-old `starting` records when no fresh supervisor heartbeat or live supervisor PID proves that generation is still booting. The full orphan-file GC remains on the `.agent-doc/gc.stamp` daily cadence. Added regressions for preflight with a fresh GC stamp and caller-specific actor transitions. This closes `#autogcstart` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Editor IPC prefix repair now repositions before normalization.** JetBrains and VS Code patch application now move the exchange boundary before applying `normalize_prefix_lines`, so prompts typed after the previous boundary marker are inside the user region seen by the ack-content sidecar. This should keep clean closeouts from repeatedly logging `sidecar_normalization_fallback reason=prefix_divergence`. Added editor regressions and updated the plugin IPC spec. This closes `#sidecarfallbackstill` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Tracked-work completion now uses `--done`.** `write` and `finalize` now expose `--done <id>` as the public flag for marking either `agent:backlog` or `agent:icebox` work complete. The old `--pending-done` spelling and the transitional `--backlog-done` spelling are accepted as deprecated aliases with warnings, while `plan` and recovery hints now emit `--done`. This closes the CLI rename request in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Prompt-only exchange tails now fail closed after closeout.** `session-check` now scans the live `agent:exchange` tail after otherwise-clean closed cycles and interrupts when it ends in a prompt-looking block with no later assistant response, even if that prompt already matches the committed snapshot. This catches direct Codex/manual turns like the May 10 `#vt-agent-deploy` patchback miss where implementation commits succeeded but the final response never landed in the session document. This closes `#rootpatchmiss` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **BREAKING CHANGE: completed backlog archives now use `agent:done`.** The completed/reaped archive component was renamed from `agent:backlog-done` to `agent:done`, and `agent:backlog-done` / `agent:pending-done` are no longer accepted as archive aliases by closeout, history replay, or pending resolution. `agent-doc migrate` rewrites both legacy tags to `agent:done`, and newly reaped items create or append to `agent:done`. This closes the follow-up archive rename request in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Cross-document backlog capture now has a binary-owned target path.** `write` and `finalize` accept `--pending-add-to <file> <text>` for explicit backlog targets, fail closed when the target file is missing or lacks a backlog component, and `plan` now surfaces those target files in `pending_mutations` / finalize hints. Closeout guards no longer let a current-document `--pending-add` bypass explicit target validation, preventing `#agent-doc-bug` items from landing in the wrong session document. This closes `#crossdocpend` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Prompt-prefix normalization now uses opt-in response-block exits.** The `content_ours` normalizer no longer leaves an inserted assistant response block just because a response sentence looks prompt-like, and target-based prefix repair must match an explicit `normalize_prefix_lines` target before it can resume after a `### Re:` block. This keeps assistant questions and preset-looking evidence lines bare while still repairing real follow-up prompts after a boundary or canonical prompt-target diff. This closes `#spfxnorm` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Direct `agent-doc run` waits now emit and persist heartbeats.** After preflight opens the response cycle, long non-streaming child-agent waits print `[run] heartbeat ...` progress every `AGENT_DOC_RUN_HEARTBEAT_SECS` seconds (default 30) and update the open cycle state's `updated_at` / `last_event` without advancing the phase. Timeout diagnostics still replace the heartbeat with the recoverable timeout event, but operators and Codex can now see phase/cycle progress while the child is legitimately still running. This closes `#runhb` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Compact Exchange now uses editor IPC before falling back to disk writes.** `agent-doc compact <file> --component exchange --commit` delivers its full-document replacement through the existing JetBrains/VS Code IPC watcher when available, so the active markdown buffer is mutated through the editor document API instead of triggering an external-file-change dialog. Added compact IPC regression coverage and refreshed the shared editor specs.

- **Sync layout memory now lives in the project controller store.** `agent-doc sync` imports legacy `.agent-doc/last_layout.json` once when `.agent-doc/state.db` has no layout row, then reads and writes the controller-backed `layout_states` table as the authoritative column-memory state. `last_layout.json` is still emitted for compatibility, but drifted JSON no longer overrides SQLite. This closes `#stateproj` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Submodule closeout now fails closed on stale parent gitlinks.** Strict `finalize` / `write --commit` and `session-check` now verify that a submodule-hosted document response is committed both in the submodule and through the parent repository submodule pointer. If the inner document commit succeeds but the parent pointer commit fails, closeout reports the missing parent layer and prescribes idempotent `agent-doc commit <file>` recovery. This closes `#rspcmt2` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Managed Codex capability proof now reports phase timings.** Successful `codex_capability_proof` events include `timings_ms` for host DNS, child network, required SSH, launcher writable-root checks, child writable-root checks, and total proof time, so slow `agent-doc start` runs show which capability phase is expensive. The Codex child probe prompts are also shorter while keeping the same shell checks and success markers. This closes `#caplat` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Prompt-prefix repair no longer treats prefixed response headings as prompt starts.** Prefix normalization now recognizes `❯ ### Re:` as an assistant response boundary, so a stale repair target list cannot cascade `❯ ` onto the response body, verification bullets, or commit evidence after a temporarily prefixed heading.

- **Direct `agent-doc <file>` invocation can no longer hang silently after opening preflight.** `run` now bounds the agent-child wait with `AGENT_DOC_RUN_AGENT_TIMEOUT_SECS` (default 1800s), records a recoverable `preflight_started` timeout event with cycle/pane/actor diagnostics on timeout, and rejects recursive Codex direct invocations from the same tmux pane that already owns the document before nesting another Codex child. `session-check` now surfaces those timeout events with concrete retry/restart guidance. This closes `#preflighthang` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Codex network-required sessions now prove network from inside a Codex child.** `codex_network_access: enabled` still clears inherited `CODEX_SANDBOX_NETWORK_DISABLED`, but managed `start` now also runs a bounded `codex exec --json` probe under the same launch args and requires a successful command-execution marker from DNS plus HTTPS checks. Failures distinguish host DNS, child DNS, sandbox/network denial, timeouts, and refused connections before route trusts or reuses the pane. This closes `#codexnonet` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Claimed IPC timeout patches are now durable skip signals.** When the CLI completes an IPC-timeout response by writing the document directly, `.agent-doc/claimed-patches/<patch_id>` now remains in place so every editor watcher pass skips the stale patch instead of only the first consumer. JetBrains also deletes the patch file on the inner EDT dedup path. This reduces post-closeout external edits that could replay the same response block and make later turns look duplicated. Bumped the JetBrains plugin build version to `0.2.106`.

- **Managed Codex panes now prove capabilities before reuse.** Codex `start` records a `codex_capability_proof` event after successful live network, isolated SSH, and writable-root probes whenever the document requests network access, `required_ssh_targets`, or extra `--add-dir` roots. Route no longer trusts a ready managed Codex actor without a current proof after the latest `session_start`; it restarts fresh once with the original launch contract before rerouting, and `session status` reports whether the proof is `proven`, `missing`, or `not_required`. This closes `#codexcapstale` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Managed reroutes keep supervisor-PID recovered panes on supervisor IPC.** When a registered pane no longer exposes the document path in child argv but the healthy supervisor PID still maps to that pane, normal route now treats supervisor IPC as the readiness boundary instead of downgrading an unrecognized prompt probe to a focus-only no-op. This restores the supervisor-PID fallback regression and updates the routing specs.

- **Safe-passive focus-only sync preserves already-visible focused siblings.** When an editor focus event supplies only the focused markdown file after a turn ends on another pane, sync now prefers the remembered or visible column that already owns that file before falling back to active tmux pane replacement. This keeps `docs.md`-style sibling panes selected in place instead of collapsing/replacing the old active pane. Added pure and tmux regressions and updated the session/tmux specs.

- **Safe-passive post-lock focus stays on the editor fast path.** `sync --no-autostart` now prefers the local actor projection for post-lock focus before issuing a controller actor-binding RPC, caches any controller fallback for the rest of the sync cycle, and keeps post-lock focus timing out of the broad `window_resolution` bucket. This targets the current `#syncbudgetstill` traces where one slow actor focus could both double-count as window resolution and trigger another controller lookup later in the same safe-passive sync.

- **IPC normalization fallback now respects concurrent non-exchange edits.** When a plugin sidecar strips a prompt prefix and the binary falls back to normalized `content_ours`, the fallback first merges the current disk content against the explicit pre-response baseline. Deleting a scratch HTML comment while the response is running now stays deleted instead of being restored by prefix repair. Added a regression and updated the closeout specs.

- **Safe-passive focus-only sync preserves visible splits without saved layout state.** If an editor event supplies only the focused markdown file and `.agent-doc/last_layout.json` is absent, sync now derives the sibling projection from registered panes already visible in the target `agent-doc` window before reconciling. This prevents post-turn editor sync from collapsing a visible split to one pane. Added a tmux regression and updated the session/tmux specs.

- **`agent-doc focus` no longer waits on the project controller RPC.** The editor immediate-focus path now selects a live local actor projection from `.agent-doc/session-actors.json`, then falls back to `sessions.json`, without launching or blocking on the controller actor-binding request. Background `sync --no-autostart` still owns slower reconciliation and projection repair. Added focused regressions and updated the focus/editor specs.

- **Editor document switches now attempt immediate focus before background reconciliation.** JetBrains and VS Code automatic tab sync issue a best-effort `agent-doc focus <file>` as soon as a markdown selection changes, then let the existing debounced `sync --no-autostart` reconciliation run in the background. Missing panes still fall through to reconciliation, while existing-pane handoffs feel snappy. Added VS Code command-arg coverage, updated the editor specs, and bumped the JetBrains plugin build version to `0.2.105`.

- **Automatic editor sync now skips superseded deferred retries.** If a rapid document switch leaves an older automatic sync running and that older process later reports a retryable preserved-layout or sync-lock-contention result, JetBrains and VS Code no longer schedule a delayed retry for that intermediate snapshot. The completed process is allowed to finish in the background, and only the latest selected document is replayed. Added plugin regressions, updated the shared editor specs, and bumped the JetBrains plugin build version to `0.2.104`.

- **Safe-passive sync now defers live stash-agent ownership proof on changed selections.** The first safe-passive cleanup pass after an editor selection/layout change still prunes stale registry entries, idle stash shells, and retained-dead non-stash panes, but it preserves live unregistered agent panes in stash instead of spending seconds proving whether each one is still owned. Full sync and explicit repair paths keep the deeper kill-or-preserve cleanup. Added a focused stash cleanup regression and updated the sync spec. This closes `#stashprunefast` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Project controller launch now falls back when `current_exe()` is stale after local installs.** Lazy controller startup no longer fails with bare `No such file or directory (os error 2)` when the running agent-doc process points at a binary path that was removed or replaced. Controller launch and bootstrap identity now prefer the live current executable, then fall back to the invoked command or `agent-doc` on `PATH`, and only then fail with a diagnostic that names the skipped stale path. Added focused resolver regressions and updated the controller specs. This closes `#syncbudget-regress` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Dispatch-only route now submits to healthy `starting` controller actors instead of refusing editor reruns.** If the controller and healthy supervisor still report `starting`, `route --dispatch-only` keeps the same direct-pane submit boundary as file-scoped `session clear` instead of focusing and dropping the rerun. When the pane is visibly dispatch-ready, route also promotes stale lifecycle state to `ready`, but split-pane submission no longer depends on that prompt probe. Added a four-pane tmux regression and updated the routing spec. This closes `#editorswitchctl` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Ordinary HTML comment bodies no longer count as prompt extensions.** The escaped-conversation/template repair scanners now ignore non-agent `<!-- ... -->` ranges the same way they already ignore code spans, so prompt-like scratch notes typed after `<!-- /agent:exchange -->` stay outside exchange instead of being moved into the live prompt tail. Session-check and write-path prompt-drift decisions also classify comment-stripped bodies. Added component, template, and session-check regressions for multiline HTML comment bodies.

- **`finalize --pending-done` now closes `do #id` turns in one pass.** Passing `--pending-done <id>` records a tracked-work mutation before closeout guards run, so pending-capture treats the item resolution as the required backlog outcome instead of demanding a second repair/finalize attempt. If preflight or repair already reaped the item into `agent:pending-done`, the flag is now an idempotent warning instead of a fatal missing-id error. Added focused write, pending, and finalize regressions and updated the closeout/pending specs. This closes `#finalize-do-cascade` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Editor document switches now focus through the controller actor before slow sync work.** Safe-passive `sync --no-autostart --focus <file>` resolves the focused markdown file through the live controller actor binding and selects that pane before waiting on `.agent-doc/sync.lock`, prune cleanup, ownership proof, or tmux-router reconciliation. A stale starting sibling session or contended sync can still defer layout reconciliation, but it no longer leaves tmux focus stuck on the wrong document. Added a tmux regression and updated the sync specs. This closes `#editorswitch` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Safe-passive sync now rate-limits repeated stash cleanup on unchanged layouts.** The common editor-selection path still prunes stale registry entries and retained-dead non-stash panes on every sync, but repeated `sync --no-autostart` runs with the same visible column/window mapping skip the expensive `prune_stash_windows` and `prune_stash_panes` work inside a short throttle window. Focus-only selection churn now logs near-zero stash cleanup subphases instead of spending the safe-passive budget rescanning orphaned stash panes. Added focused regressions and updated the sync spec. This closes `#syncprune` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Sync ownership proof now reuses per-cycle controller/live-owner facts.** A single sync run no longer re-queries the same document/session/pane actor binding and supervisor-backed live-owner proof across pre-reconcile ownership checks, synthetic tmux-router registry construction, and post-router registry projection. Added a regression for the per-cycle cache and updated the sync spec. This closes `#syncproof` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Cross-document sync no longer waits behind another document's closeout pane.** Manual `Sync Tmux Layout` and passive editor autosync still protect panes that own open `preflight_started`, `response_captured`, or `write_applied` cycles from DETACH, but a protected pane no longer turns a different requested document into a deferred no-op. Sync now attaches/focuses the requested pane immediately around the protected closeout owner, accepting temporary visible pane growth instead of blocking editor navigation. Updated tmux regressions and the sync specs for the `agent-doc-bugs2.md` repro.

- **Protected sync edge coverage now has deterministic SimWorld traces and fewer default-suite tmux variants.** Added named `#tmuxbudget` simulator traces for protected-layout handling, detachable-pane replacement, and preserve-layout focus handoff, plus simulator corpus coverage counters for sync protected/replacement/focus decisions. The default suite keeps safe-passive real-tmux smokes for pane/window movement, but duplicate manual protected-layout tmux variants are ignored behind the matching simulator traces and documented in the deterministic simulation spec.

- **Sync latency now names the expensive phase instead of hiding it in broad buckets.** Manual and passive sync emit `sync_lock_wait`, prune subphases, `controller_actor_lookup`, and `projection_refresh` alongside the existing window, prune, ownership, router, and safe-passive total timings. The live `#synclag` traces showed recent slow manual syncs spending 1.3-1.9s in prune while tmux-router stayed in the tens of milliseconds, so prune now reports registry, metadata-fetch, stash-window, stash-pane, and retained-dead cleanup subphases. Stash-pane cleanup also uses the already-fetched `pane_current_command` metadata instead of sleeping to resample every obvious foreign process.

- **Automatic editor tab sync now always uses passive sync instead of the focus shortcut.** The manual Sync Tmux Layout action already used `agent-doc sync --no-autostart`, which owns stash rescue, protected closeout handling, and safe replacement of detachable visible panes. The automatic VS Code and JetBrains tab-selection planners could still choose `agent-doc focus` for single-file handoffs, leaving editor navigation unable to reproduce manual sync's pane/focus result. Automatic tab sync now dispatches passive sync for every real selection/layout change, with updated plugin regressions and specs for `#autosync`.

- **Sync can now replace an unprotected visible pane even while another visible pane is protected by an open closeout.** The protected-layout guard no longer turns every hidden requested document into a no-op just because a different visible pane is mid-closeout. Manual `Sync Tmux Layout` and passive editor autosync now preserve the protected pane, displace an unprotected unwanted pane when one is available, and focus the requested pane. Added tmux-backed regressions and updated the session/tmux command spec.

- **Project controller IPC now fails closed around stalled clients.** Controller request and response reads have bounded timeouts, the server handles accepted clients independently so an idle socket cannot monopolize `.agent-doc/controller.sock`, and `status --ensure` releases its readiness stream before issuing the status RPC. Added regressions for response timeout and idle-client isolation, and updated the controller specs. This closes `#ctrlsock` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Project controller clients now invalidate stale controller binaries before RPC dispatch.** The controller bootstrap/status contract records the startup agent-doc binary path, version, size, and modified timestamp, and `connect_or_launch` compares that stamp against the caller before reusing an active socket. Missing or mismatched binary identity now triggers a controller shutdown and lazy relaunch, preventing local rebuilds or installs from leaving an old controller process that rejects newly-added RPCs such as `session_status` as unknown commands. Added focused controller identity regressions and updated the controller command spec. This closes `#ctrlreload` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Project Controller Phase E routes operator commands through the controller boundary.** `agent-doc session status/history/attach/restart/clear/doctor` now use controller-owned actor state for operator reads and command staging: status includes controller leases, recent command attempts, and projection drift; history prefers durable actor transitions; attach creates the manual handoff generation through controller IPC before refreshing `sessions.json` as a projection; restart and clear record an accepted or rejected operator stage before supervisor/tmux delivery. Added focused controller and clear-path regressions and updated the session actor/command specs. This closes `#pcops` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Project Controller Phase D moves actor-backed route/sync authority behind controller IPC.** Route and sync now request the document actor binding from the project controller before consulting supervisor-backed registry compatibility evidence, and route records controller `dispatch` attempts before managed or dispatch-only submits to the actor pane. Stale session, pane, or generation requests fail closed before input is sent; `session-actors.json`, session-log, registry-rebind, and process-tree evidence remain projection or repair diagnostics. Specs and controller regressions cover actor binding lookup, accepted dispatch attempts, and stale-generation rejection. This closes `#pcroutes` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Project Controller Phase C now routes supervisor lifecycle facts through controller IPC.** `agent-doc start` lazy-launches the project controller, records the starting actor generation through `start_session`, registers the supervisor pid/socket lease, and reports prompt-ready, busy dispatch, waiting-input, blocked, and closed transitions through controller-owned actor updates. Stale lifecycle reports now fail closed on session/pane/generation mismatch, supervisor leases keep runtime state current, and specs/tests cover the controller registration path. This closes `#pcsuper` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Claude streaming prompt writes now tolerate early child exit.** If the child exits before reading stdin, a `BrokenPipe` during prompt write is treated as normal subprocess termination so the streaming iterator can surface the real nonzero exit status and stderr diagnostics.

- **Project Controller Phase B now persists actor records through SQLite before emitting JSON projections.** `session_actor.rs` routes actor load/store through the controller state boundary, `project_controller.rs` owns `.agent-doc/state.db` tables for documents, transitions, leases, dispatch attempts, and projection diagnostics, and compatibility projections are emitted from committed state. Existing `sessions.json` entries are reconciled to the controller actor binding, while missing or failed projections record drift diagnostics without rolling back the authoritative actor transition. Added focused controller regressions and updated the session-actor/controller specs. This closes `#pcstore` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Explicit-baseline closeout now survives session document moves after preflight.** When a document is moved after `preflight`, rename migration can move `.agent-doc/baselines/<old-hash>.md` to the new hash before `finalize` reads the explicit `--baseline-file`. The write path now retries the migrated current-hash baseline, preserving the strict explicit-baseline contract instead of failing into a no-baseline fallback. Added a regression and updated the closeout/snapshot specs. This closes `#pathmove` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **JetBrains protected-layout sync warnings now identify the blocking pane.** The live `tasks/software/tsift.md` replay showed the backend was correctly preserving pane `%208` while its `preflight_started` closeout was open, but the JetBrains notification collapsed that into a generic "another pane" warning. `SyncLayoutAction` now parses the protected pane list from sync output and includes the pane id, phase, and document path in the visible warning, with editor spec/test coverage. Bumped the JetBrains plugin build version to `0.2.99`.

- **Prefixed assistant response labels no longer reopen committed cycles.** The prompt-target classifier now normalizes optional `❯`, list markers, and markdown emphasis before checking known assistant labels, so lines like `❯ **Verification:** ...` and `❯ **Commit / push:**` stay response prose while real prefixed follow-ups still start prompt runs. JetBrains prefix repair mirrors the same ordering, with Rust/session-check and editor regressions. Bumped the JetBrains plugin build version to `0.2.98`. This closes `#respfx` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Codex latest-prompt lookup now skips malformed hook-state entries.** `codex_hook.rs` no longer lets one unreadable or partially written session JSON hide a valid newer prompt for the same file, which keeps parallel hook-state churn from making `load_latest_prompt_for_file` return `None`. Added a direct regression and updated the shared spec.

- **IPC `content_ours` prefix fallbacks now repair the working tree before commit.** When plugin sidecar verification rejects a normalization result, `write.rs` still falls back to normalized `content_ours`, but it now writes that same repaired content back to disk before returning success. This prevents a later commit from capturing a plugin-stripped `❯ ` prompt prefix even though the snapshot was already repaired. The same closeout follow-through tightened the fresh-prompt classifier so stale prefix-repair target lists containing `Commit / push:` cannot prefix later assistant evidence labels. The closeout spec and regression coverage now assert snapshot preservation, working-tree preservation, and stale-target assistant-label suppression. This closes `#pfxcours` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Concurrent prompts added during explicit-baseline closeout now fail closed.** `write.rs` now classifies live disk drift against the pre-response baseline before the response is merged, so a prompt typed after preflight but before `finalize` cannot be mistaken as answered by the response that was already in progress. The committed snapshot stays at `content_ours`, `session-check` interrupts on the unresolved `prompt_target`, and the closeout spec plus integration coverage now encode the contract. This closes `#concprompt` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Streaming responses now leave durable partial checkpoints before final closeout.** `capture.rs` now maintains a `.partial.json` checkpoint ledger beside final response captures, saving the first non-empty streamed response and then changed partial output at most every 30 seconds without advancing the cycle to `response_captured`. Both `agent-doc stream` and CRDT orchestration streaming feed that checkpoint writer, with regressions proving the partial checkpoint survives before final closeout and remains diagnostic-only. This closes `#chkptcap` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **JetBrains protected-layout sync warnings are now deferred-sync UX instead of raw CLI diagnostics.** Manual `Sync Tmux Layout` still warns visibly when a protected visible pane is mid-closeout, but the notification is concise and the full CLI output stays in logs. Automatic tab sync now treats both preserve-layout markers (`[sync] sync preserved...` and `[sync] safe passive sync preserved...`) as deferred retry states unless the output includes `safe_passive_layout_preserved_reselected_focus`, covering the `agent-doc-bugs2.md` to `tasks/software/tsift.md` navigation repro. Bumped the JetBrains plugin build version to `0.2.97` and refreshed editor specs/tests for `#jbsyncwarn`.

- **Assistant response tails committed in `HEAD` now have explicit prompt-prefix regression coverage.** The `#pfxleak3` repro was a narrower variant of the `content_ours` fallback leak: a new prompt inserted directly below a prior assistant response tail could make the tail look like part of the prompt run. The closeout spec now states the `HEAD` prefix-state invariant explicitly, and the write-path regression proves the tail remains bare while only the new `do [#pfxleak3]...` prompt receives `❯ `.

- **Post-commit prompt-prefix repair no longer treats assistant `Commit / push:` evidence labels as prompt targets.** The `#pfxleak` closeout reproduced the remaining leak live: a historical bad `❯ Commit / push:` line in committed assistant content caused the IPC prefix-repair signal to add `❯ ` to a later assistant response label after the commit, tripping `session-check` as an unstarted prompt. The plain-response classifier now recognizes `Commit / push:` before the generic `commit ...` prompt heuristic, and both target extraction and prefix application refuse to propagate stale assistant-label targets. Added regressions for the target extractor, full-document prefix repair, and IPC patch-content normalization.

- **`content_ours` prompt-prefix normalization now preserves multi-line user prompts after stale inserted response blocks.** The `#pfxstrip2` repro showed a stale snapshot keeping the normalizer in agent-response mode long enough to skip ordinary `Please ...` prompt bodies, while a later preset-like prompt still received `❯ `. The write path now reopens a blank-separated fresh prompt run after an inserted response, prefixes every nonblank prompt line outside fences, and preserves already-committed `HEAD` prefix state bidirectionally so prefixed user prompt lines stay prefixed while prior agent response lines stay bare. Added regressions for the multi-line prompt strip and both HEAD prefix-state directions, and updated the closeout spec.

- **Closeout drift noise is narrowed after evaluating the Claude Code + Codex logs.** `session-check` no longer treats plain `content_edit` drift as an unstarted closeout after a committed cycle, so minor already-answered transcript edits do not force a second finalize. The `content_ours` prompt-prefix fallback now preserves unprefixed exchange lines already committed in `HEAD`, preventing prior agent response lines from gaining `❯ ` and needing a follow-up normalize commit. Template repair also keeps relocated live prompts out of the saved snapshot so preflight still sees them as user work.

- **Repeated no-op closeout churn is advisory again instead of an automatic compact handoff.** `plan.rs` no longer converts the repeated `commit_noop` subset of session-accretion into a mandatory `agent-doc compact ... --commit` command, so a document without an explicit compaction request continues normal repo work and closeout. The closeout spec, README, planning runbook, and regression test now state that session-accretion signals can suggest compaction but must not force it. This fixes the unwanted autocompaction reported in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Dispatch-only Codex accepted-but-unproven failures no longer print an optimistic fallback line first.** `route.rs` now suppresses the legacy "accepted but no proof" progress message when `Run Agent Doc` is in dispatch-only Codex mode and hook tracking is visible, leaving the final accepted-but-unproven error as the only user-facing outcome. Added route/plan regressions and refreshed the session-routing and JetBrains specs.

- **Sync layout cardinality and passive focus proof now share one visible projection contract.** Manual `agent-doc sync` preserves open closeout panes from DETACH while letting different requested documents attach and focus immediately around them. Preserve-layout focus handoffs for genuinely blocked files still print the `safe_passive_layout_preserved_reselected_focus` proof to command output, and the JetBrains automatic sync planner treats that proof as applied instead of retrying a selection that already focused the requested visible pane. Added tmux-backed and JetBrains planner regressions plus spec updates for `#syncfocuscard`.

- **Template repair now relocates prompt-only drift that lands between `agent:exchange` and markdown section breaks.** The latest `#oobprompt` repro in `tasks/agent-doc/agent-doc-bugs2.md` was narrower than the earlier escaped-response gap bug: `repair`/`preflight` already fixed `### Re:` or `## Assistant` tails stranded before later components, but a bare prompt target such as `do [#id]...` typed after `<!-- /agent:exchange -->` and before a plain `###` / `## Pending` section marker stayed outside exchange because the shared detector only keyed on escaped response headings. `template.rs` now isolates prompt-target blocks in that exchange-to-section gap, feeds them through the same guard/repair path, and leaves the structural separator outside the exchange. Added direct template regressions plus a repair-path normalization test. This closes `#oobprompt` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Required SSH prelaunch probes now isolate themselves from shared SSH socket state.** `agent::codex` was proving `required_ssh_targets` by running real `ssh <target> true` checks through the operator's normal SSH config, which meant ControlMaster/ControlPath multiplexing or forwarded-session side effects could leak out before the managed session even started. The probe path now forces isolated SSH flags (`ControlMaster=no`, `ControlPath=none`, `ClearAllForwardings=yes`, `PermitLocalCommand=no`) on both alias and direct-host checks, tightens the failure text to call out the isolated pre-launch probe scope, and adds unit coverage for the no-shared-socket contract. This closes `#sshcut` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Safe passive sync preserve-layout guards now keep tab focus moving across already-visible panes.** The latest `tasks/agent-doc/agent-doc-bugs2.md` regression came from the new preserve-layout exits in `sync --no-autostart`: when a blocked or protected missing file forced safe passive sync to skip tmux-router reconciliation, the command also skipped the final pane selection, so switching editor focus between already-visible docs could leave the `agent-doc` tmux window stuck on the old pane. `sync.rs` now reselects the requested pane before either preserve-layout return when that file is already visible, and added tmux-backed regressions for both the blocked-file and protected-pane guard paths. This closes the latest sync-focus regression in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Warn/block bounded context packs now expose a lightweight response TOC plus targeted retrieval commands.** Added `agent-doc response-toc` to enumerate current live `### Re:` sections alongside matching archived response sections for the same document, and `agent-doc response-fetch` to load exact live or archived sections with bounded neighbors. `prompt_context.rs` now includes that TOC in warn/block context packs and explicitly points agents at `response-fetch` for on-demand neighboring history instead of relying only on the fixed recent-turn window. Added unit + CLI regression coverage and updated the command/spec docs. This closes `#restoc` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Warn/block bounded context packs now anchor response history to the prompt's position in `exchange` instead of always replaying the newest `### Re:` turns.** `prompt_context.rs` now locates each prompt target inside the live exchange and includes the enclosing response block for inline prompt edits or the immediately previous response for tail follow-ups, while still falling back to the old recent-turn slice if no clean anchor can be found. Added regressions for both anchor shapes and updated the orchestration/README docs to match. This closes `#wv7g` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Dispatch-only Codex reroutes now fail once with a precise "accepted but unproven" reason instead of looking non-responsive.** `route.rs` now classifies Codex clean-exit restart prompts as an immediate dispatch blocker, routes healthy authoritative-actor `Run Agent Doc` submits through the same checked live-pane helper as other dispatch-only reroutes, and requires hook-visible Codex reroutes to produce bounded submission proof instead of silently succeeding on bare tmux acceptance alone. Added regressions for the restart-prompt blocker and the hook-visible authoritative dispatch-only failure shape, and updated the session-routing plus JetBrains specs. This closes `#fye2` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Resumed required-SSH Codex streams now discard stale prelude text before the fresh retry.** `agent::codex` no longer blocks required-SSH capability-drift recovery just because the resumed stream already emitted assistant text. For SSH-gated resumed streams it now buffers early agent chunks until the stream proves required SSH success or completes successfully, retries fresh once even after a stale prelude, and drops the buffered resumed prelude if that retry fires. Added streaming regressions for the exact "assistant prelude, then SSH failure" report plus the successful SSH release path, and updated the agent-backend spec and README. This closes `#sshprelude` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Codex Stop now records tool-only/auth-interrupted closeout misses instead of surfacing a generic empty-response block.** `codex_hook.rs` now saves a blocked-stop diagnostic even when `last_assistant_message` is empty, includes the tracked prompt in that artifact, and tells the operator that this often means Codex stopped after a tool-only/authentication step such as an MCP OAuth / `authenticate` flow before the final closeout was emitted. Updated the bundled skill, harness runbook, git-integration spec, and shared spec so MCP auth is explicitly a sub-step that still must end through `finalize` / `write --commit` plus `session-check`. This closes `#257p` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Session-accretion heuristics are now advisory only.** `plan.rs` no longer blocks normal turns on churn-heavy session metrics, and `preflight.rs` no longer auto-compacts exchanges at all, including documents that still carry legacy `auto_compact` frontmatter. The bounded recent-context pack stays in place for warn/block accretion prompts, but it no longer has a binary-enforced compact/block side effect. Updated regressions and command/docs text accordingly. This addresses the latest critical usability report in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Session-accretion turns now keep full documents intact and send bounded recent-turn context instead of auto-compacting mid-turn.** `preflight.rs` no longer auto-compacts template exchanges just because session-accretion heuristics tripped. `prompt_context.rs` now builds the warn/block response-context pack with prompt targets, session summary, backlog head, recent `### Re:` turns, and an explicit "ask for more previous turns if needed" instruction, so long sessions stay intact on disk while resumed prompts stay bounded. Added regressions for the no-auto-compact preflight path plus the richer recent-turn prompt pack. This closes `#ratecmp` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Preflight/plan now surface deterministic context-accretion signals without enforcing a hard stop.** Added `session_accretion.rs`, which summarizes per-document exchange growth, recent closeout churn, and restart-heavy reopen signals from the existing document/session logs without replaying full transcripts. `preflight` now emits a structured `session_accretion` advisory when those local heuristics trip, and the prompt-building path can still choose a bounded recent-context pack from that report, but `plan` no longer fails closed on the hard-stop tier. Added regressions for large exchanges, repeated no-op closeouts, restart-heavy churn with an active startup-miss, the preflight JSON surface, and the non-blocking plan contract. This closes `#ctxacc` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Sync reconcile now preserves panes whose documents still have an open closeout cycle.** `sync.rs` now re-enables tmux-router's DETACH protection only for panes whose registered document is still in `preflight_started`, `response_captured`, or `write_applied`, so layout reconciliation warns and leaves that pane visible instead of stashing it mid-closeout. Added regression coverage for both the open-cycle detector and the sync reconcile replay that keeps the in-flight pane visible. This closes `#busychk` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Stale empty `preflight_started` cycles now auto-close on the next preflight instead of trapping the document in manual recovery.** The `#staleflt` repro from `tasks/agent-doc/agent-doc-bugs2.md` showed a narrow crash window where a pane could die after `start_preflight()` but before any response capture existed, leaving later `preflight` runs with an open cycle that had no replay artifact and no exact hash proof. `repair.rs` now treats that shape as a bounded stale-empty-cycle case: if the cycle is still `preflight_started`, has no capture, shows no visible patchback, and is older than the timeout, it is closed as a no-op before the new preflight cycle opens. Added repair/preflight regressions plus spec/skill updates for the stale-empty timeout contract. This closes `#staleflt` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Required SSH metadata can now resolve from project config, and missing mappings fail closed before launch.** `frontmatter::parse_for_file()` now resolves effective SSH requirements from document frontmatter plus project-local `.agent-doc/config.toml` mappings (`[ssh.docs."<path>"]`, `[ssh.profiles.<name>]`), so known ops docs no longer bypass the required-SSH contract just because frontmatter omitted `required_ssh_targets`. `preflight`, `plan`, `run`, `start`, and `route` now consume the path-aware parse, and they stop immediately when a configured SSH-dependent document resolves no targets or references a missing profile. Added config/frontmatter/preflight regressions and the fresh-restart route guard needed to keep the suite green under the new path-aware parse. This closes `#sshmeta` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Phase-4 authoritative actor dispatch is now explicitly closed out in spec and regressions.** `route.rs` already switched normal reroutes onto the authoritative actor record and supervisor IPC in `312851e`, with later follow-ups covering harness aliasing and waiting-input recovery, but the phase item still lacked direct proof for the remaining hard-stop states. Added tmux-backed route regressions that prove `blocked` and `closed` authoritative actors fail closed without injecting a duplicate reopen into either the actor pane or a stale registered pane, and updated the session-actor contract to pin the full phase-4 state matrix. This closes `#sgown4` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **JetBrains passive tab sync now trusts the selection event target instead of a potentially stale `selectedTextEditor` snapshot.** The latest `tasks/agent-doc/agent-doc-bugs2.md` repro showed a narrower editor-side skip than the queued-replay race: when the user switched from `sampleportal.md` back to `agent-doc-bugs2.md`, JetBrains could enter `selectionChanged` with `event.newFile` already updated while `FileEditorManager.selectedTextEditor` still pointed at the previous file. That made the automatic snapshot/dedup planner think focus had not changed and suppress `agent-doc sync --no-autostart` entirely. `EditorTabSyncListener` now treats the event file as the authoritative active markdown target for automatic snapshots, falls back to the selected editor only when no event target exists, and adds a regression covering the stale-selected-editor shape. Bumped the JetBrains plugin build version to `0.2.94` and updated the plugin spec with the same callback contract.

- **Explicit `repair` now fails closed when only later prompt drift remains after committed historical patchback recovery.** The `#rprdrift` repro in `tasks/agent-doc/agent-doc-bugs2.md` exposed a bad explicit-repair downgrade: `repair::run()` could legitimately find no pending/capture artifact, but `agent-doc repair` then returned `No pending response found` even though `session-check` would still interrupt the same committed cycle after repairing the snapshot from `HEAD` and noticing later prompt-bearing drift. `repair()` now re-runs the closeout interruption check on no-op outcomes and surfaces that same failure instead of pretending the document is clean. Added a regression covering the exact committed-patchback-plus-follow-up-prompt shape and updated the closeout/spec docs to make the fail-closed contract explicit.

- **Completed backlog reap now removes malformed flush-left spill with the done parent item instead of orphaning it in backlog.** The latest `#mlreap` repro in `tasks/agent-doc/agent-doc-bugs2.md` showed `pending::reap_with_items()` only dropping the tracked `[x] [#id]` line while leaving adjacent flush-left command/diff transcript spill behind as generic backlog text. Reap now strips the leading non-structural text block that immediately trails a completed parent, preserves true structural separators such as headings/comments, and archives that spill with the removed item so preflight/repair/backlog reap no longer leave orphan prose behind. Added regression coverage in `pending.rs`, the backlog CLI integration, and preflight's live-prompt preservation path.

- **Codex `UserPromptSubmit` now finds the real `agent-doc <FILE>` line after injected prompt preambles.** The latest `#rspcmt` closeout miss showed a direct Codex `agent-doc ...` turn can arrive at the hook wrapped in AGENTS/instruction text, so the old "first non-empty line only" parser never tracked the target doc and the `Stop` hook had nothing to recover or block. `codex_hook.rs` now scans the prompt from the end, skips fenced placeholder examples like `agent-doc <FILE>`, and records the last real invocation line instead. Added hook regressions for prompt-preamble parsing and the resulting active-session post-commit drift recovery path.

- **Hash-prefixed pending ids now resolve on the actual mutation path, not just in closeout guards.** The `#9aep` tsift repro showed an inconsistency between agent-doc's backlog guards and its write-time pending mutations: `cycle_state` and `session-check` already normalized `#id`, but `pending_cmd::done()` and the lower-level pending ops still compared raw strings, so `agent-doc finalize --pending-done '#9aep'` failed with `id not found in backlog/icebox` even though the backlog item existed. The pending mutation layer now strips one optional leading `#` and lowercases ids across done/edit/gate/ungate/reorder/set-gate-type lookups, and added regressions for `op_done`, `write --pending-done '#id'`, and `finalize --pending-done '#id'`.

- **Safe passive sync now locks the exact VS Code mixed-root split replay into spec and tmux regression coverage.** `sync.rs` already preserved visible layout when a passive `--no-autostart` file stayed blocked, but the coverage was still generic. The latest `#vssplitreplay` closeout now names the concrete `tasks/agent-doc/agent-doc-bugs2.md` + `src/session-share/tasks/claudescore-3.md` split, proves that blocked sibling files do not stash either healthy visible pane, and records the same replay shape in the session/tmux spec so the visible mixed-root layout cannot silently collapse back into a new authoritative pane set.

- **Path-scoped manual repo commits now fail closed on staging drift in the installed instruction surface.** The bundled `SKILL.md`, `commit.md`, `harness-invocation.md`, `compound-task-steering.md`, `SPEC.md`, `README.md`, and git-integration spec now require agents to resolve the intended non-session path set first, stop immediately on any stage failure, verify the staged diff still matches the intended set, and commit only that validated set before `finalize` / `write --commit` closes the session document. Added regression coverage in `skill.rs` so future installs keep the stricter pathset-validation rule.

- **Skill/runbook commit ordering now explicitly keeps session docs off manual repo commits.** The bundled `SKILL.md`, `commit.md`, `harness-invocation.md`, `compound-task-steering.md`, and git-integration spec now state that compound `commit + push` work must exclude the active session document from any ordinary repo `git commit`, defer the session-doc closeout to `agent-doc finalize` / `write --commit`, and only push after that binary-owned closeout commit lands. Added regression coverage in `skill.rs` so future installs keep the stricter staging/order rule.

- **JetBrains automatic splitter replay now uses the latest captured event snapshot instead of re-sampling editor state after the previous sync finishes.** The latest `tasks/agent-doc/agent-doc-bugs2.md` repro showed one remaining race in rapid `Go to Next Splitter` sequences: the plugin could queue a replay correctly, then rebuild its command from a later background-thread view of `FileEditorManager` and land tmux on the first splitter hop instead of the final one. `EditorTabSyncListener` now snapshots the exact active file plus detected split layout on each selection event, replays the newest captured snapshot after an in-flight sync, and uses a column-aware visible signature so splitter identity survives replay dedup. Added JetBrains regression coverage for the column-aware replay signature, bumped the JetBrains plugin build version to `0.2.93`, and updated the shared plugin spec to require event-snapshot replay instead of live re-sampling.

- **Automatic editor sync now replays the latest queued selection/layout request instead of silently dropping it while another sync is running.** The latest `tasks/agent-doc/agent-doc-bugs2.md` repro was not primarily the 100 ms debounce delay itself. Both editor plugins could coalesce selection churn correctly, then lose the actual requested handoff because the automatic concurrency guard simply returned when a sync/layout command was already in flight. That meant selecting another visible agent doc during an active sync often did nothing until the user manually ran Sync. VS Code now recomputes tab-sync state from the live editor after each automatic run and immediately replays the newest queued request when generation changed mid-flight. JetBrains now does the same for tab-selection sync, and its layout-change detector also schedules one immediate replay when a newer automatic request lands during an in-flight layout reconcile. Added focused plugin regressions for the queued replay contract and updated the shared editor specs.

- **Bare session-document `write` no longer reports success after a synthetic/template `write_stream` leaves closeout open.** The historical BuildParty `dev.md` repro in `tasks/agent-doc/agent-doc-bugs2.md` showed a narrower closeout gap than the earlier generic missed-commit family: the CRDT/template write path had already preserved the response, capture, and synthetic `write_stream` state, but the command still looked successful until a later explicit `agent-doc commit` finally recorded `commit_success`. `write.rs` now keeps that response/capture state for recovery but immediately fails closed when a real session doc uses bare `agent-doc write` and the cycle remains open, so `response_captured` / `write_applied` can no longer masquerade as a completed turn. Added an integration regression that proves the bare stream write returns nonzero, preserves the synthetic `write_applied` evidence, and still lets a later explicit `agent-doc commit` finish the boundary.

- **Answered-prompt closeout canonicalization no longer rewrites prior assistant tail prose into fake `❯ ` prompts.** The latest `tasks/agent-doc/agent-doc-bugs2.md` repro exposed an over-greedy commit-time heuristic in `git.rs`: when a real answered prompt such as `do [#tailpatch]...` shared one contiguous block with the previous response tail, the closeout canonicalizer could prefix the whole block and commit assistant prose like `There were no actionable follow-up items to capture.` as if it were user input. The canonicalizer now starts at the first prompt-like line in that block and only prefixes from there onward, preserving multi-line prompt bodies without swallowing the assistant tail above them. Added a regression covering the exact mixed tail + `do [#...]` shape.

- **Manual `[x]` backlog completions now survive same-cycle history replay checks.** The latest `tasks/agent-doc/agent-doc-bugs2.md` repro exposed a second completed-backlog regression after the resurrection fail-closed work: preflight/repair could reap a user-edited `[x]` tracked item, but the backlog replay guard still only exempted ids recorded through `--pending-done`, so the just-reaped id looked "dropped from history" and could be restored from the older `[ ]` / `[/]` state. `cycle_state` now exposes a unified resolved-id set from both explicit `pending_done_ids` and same-cycle `reaped_pending_ids`, and the preflight/session-check history guards both consume that merged set. Added regressions for the manual `[x]` reap path in preflight and session-check.

- **Template closeout now guards and repairs escaped conversation in the gap between `agent:exchange` and later components.** The latest `tasks/agent-doc/agent-doc-bugs2.md` `#tailpatch` repro was not a total write-path bypass: the existing template guard only scanned after the final parsed component, so a prompt/response block that slipped between `<!-- /agent:exchange -->` and later sections such as the stray `###` marker or `agent:backlog` could still survive closeout even though the document remained parseable. `template.rs` now shares one outside-exchange range detector across the fail-closed guard, explicit repair, and manual-tail strip paths, so inter-component escaped conversation is blocked on normal write/finalize and recoverable through repair when the structure is safe. Added regressions for guard, repair, and strip on the exchange-to-backlog gap shape.

- **Normalization-divergence IPC fallbacks now re-apply prompt prefixes before saving snapshots.** When ack-content sidecar verification rejects editor output because a `normalize_prefix_lines` target is missing its `❯ ` prefix, both socket and file IPC fallback paths now run the target-based exchange prefix repair over `content_ours` after preserving any on-disk backlog mutations. This closes the `#bppfxstrip` shape where a sidecar-divergence fallback could still save a bare `do #...` prompt into the committed closeout baseline. Added regression coverage and updated the closeout/plugin specs.

- **Editor-side prompt-prefix repair now runs after exchange patch application, and pure-reposition fast paths no longer swallow normalization-only repairs.** The latest `tasks/agent-doc/agent-doc-bugs2.md` regression was an editor-plugin convergence bug rather than a snapshot-classification miss: the binary already emitted `normalize_prefix_lines`, but the JetBrains plugin applied that repair before later exchange/unmatched patches and could overwrite the fixed `❯ ` lines in the ack sidecar, while the VS Code reposition-only shortcut treated `patches: []` as a pure boundary move even when the payload still carried `normalize_prefix_lines`. JetBrains now normalizes the exchange user region after component/unmatched patch application and before boundary/head cleanup in both the Document and VFS paths, and the VS Code watcher now reserves its reposition-only debounce shortcut for truly empty boundary moves. Added a targeted VS Code regression for the patch-shape gate and refreshed the shared plugin spec for the pure-reposition contract.

- **Completed-backlog reap now fails closed if the same ids reappear in the live backlog or icebox before closeout.** The latest `tasks/agent-doc/agent-doc-bugs2.md` regression was not a one-sided snapshot bug: `repair`/`preflight` could reap user-marked `[x]` items correctly, but a stale local/editor rewrite could put those ids back into the live `agent:backlog` before the same cycle reached `git::commit()`. Because closeout treated that as generic post-commit local drift, HEAD stayed clean while the working tree resurrected the supposedly removed items and the next preflight had to reap them again. `cycle_state` now records ids reaped during the active cycle, `preflight.rs` and `repair.rs` publish those ids when they remove completed tracked work, and `git.rs` now blocks closeout if any of those ids reappear in the live backlog/icebox before commit. Added regression coverage for the new cycle-state ledger and the fail-closed commit guard.

- **Post-claim route sync now stays on the caller's tmux server, so isolated verification no longer mutates the live `agent-doc` window.** The latest `tasks/agent-doc/agent-doc-bugs2.md` pane-retention repro was not a normal editor sync failure: local verification was still calling `sync_after_claim(...)` with an injected `Tmux`, but the helper delegated to `sync::run(...)`, which silently jumped back to the default tmux server. In practice that meant a route/unit-test reconcile using dummy files like `file_a.md` / `file_b.md` could stash a visible sibling pane such as `src/session-share/tasks/buildparty-investor-demo/dev.md` out of the operator's real `agent-doc` window, after which a normal sync would merely rescue it back. `route.rs` now keeps that reconcile on `sync::run_with_tmux(...)`, and added a regression that proves the injected server's overflow pane is stashed locally instead of the default server being touched. Updated the sync-layout spec with the same invariant.

- **Dispatch-only live-pane reroutes no longer impose a second startup-ready gate that file-scoped clear never had, and tmux command submissions now route through one helper at the call sites.** The latest `tasks/agent-doc/agent-doc-bugs2.md` ops-log evidence showed that `session clear` was already succeeding via `delivery=direct_pane_submit`, but `route --dispatch-only` could still refuse the same pane with `still booting` because `dispatch_only_send_reopen(...)` ran an extra ready-probe loop before it was allowed to use that direct tmux submit path. `route.rs` now keeps the supervisor-IPC boot-window probe only for supervisor-owned reopen delivery; direct live-pane reroutes stay on the same single-submit tmux helper that clear already uses. I also rewired the remaining command-submit call sites in `route.rs`, `queue_dispatch.rs`, and `parallel.rs` to use `sessions::send_submitted_text(...)` instead of open-coded `tmux.send_keys(...)`, so tmux-bound command submission is centralized at the call site layer instead of only by convention. Added/updated tmux regressions for the starting-pane reroute behavior and refreshed the session/tmux docs.

- **Dispatch-only authoritative reroutes now stay on the live-pane tmux submit path even while the actor still reports `starting` or `busy`, and supervisor inject has a real socket-to-tmux regression.** The latest `tasks/agent-doc/agent-doc-bugs2.md` ops-log evidence showed the remaining mismatch clearly: file-scoped `session clear` was already using `delivery=direct_pane_submit`, but prompt-bearing `route --dispatch-only` after clear could still take the authoritative actor's optimistic supervisor-IPC queue path whenever the actor runtime still reported `starting`/`busy`. That kept `Run Agent Doc` on a different delivery boundary than the known-good clear path. `route.rs` now keeps dispatch-only authoritative reroutes on the same live-pane `send_submitted_text(...)` helper even in that short starting/busy window instead of queueing through supervisor IPC, and `start.rs` now has a socket-backed integration regression that drives a real supervisor IPC listener into an isolated tmux pane so the supervisor-owned submit boundary is covered beyond mocked writers.

- **Run/clear tmux submits now share one direct-pane helper, and file-scoped clear resolves the same live pane precedence as dispatch-only reroute.** The latest `tasks/agent-doc/agent-doc-bugs2.md` repro still left one structural mismatch: routed reopens, supervisor-owned injects, and file-scoped `session clear` were all supposed to share the same tmux submit boundary, but agent-doc still had multiple direct-pane wrappers and `session clear` only trusted the registry pane before dropping back to supervisor IPC. `sessions.rs` now owns the canonical live-pane submit helper used by route, start/supervisor inject, and `session clear`, and `session_actor_cmd.rs` now resolves direct-pane clear targets in authoritative-actor, live-owner, then registry order before it ever falls back to supervisor IPC. Added pane-selection regressions and updated the tmux session spec so `Clear Session Context` follows the same live-pane preference model as `Run Agent Doc`.

- **Shared tmux submit now pauses briefly before `Enter`, which fixes real Codex slash-command submits while preserving Claude behavior.** The latest `tasks/agent-doc/agent-doc-bugs2.md` investigation finally used isolated live harness panes instead of shell-loop stand-ins. That replay showed the current `tmux send-keys -l ... ; send-keys Enter` helper left `/clear` and `/help` drafted inside Codex even though the same path still worked in Claude. A 50 ms gap between the literal text injection and the submit key made the exact same Codex panes execute the slash command immediately. `tmux-router::Tmux::send_keys()` now uses that delayed submit contract for every live-pane command injection, `agent-doc` logs the mode as `tmux_literal_enter_delayed`, and tmux-router now carries a regression that fails if the submit helper stops leaving enough separation for managed TUIs that coalesce same-tick paste bursts.
- **Tmux-bound command submissions now go through one normalized text path and stop retrying synthetic `Enter` presses.** The latest `tasks/agent-doc/agent-doc-bugs2.md` repro showed that agent-doc was still carrying multiple overlapping newline/CR workarounds even after the live-pane submit boundary had been unified. `supervisor::ipc` now normalizes submitted command text once for tmux-bound injects, leaves raw `\r` encoding only for the direct PTY-writer fallback, and route/queue-dispatch no longer send follow-up `Enter` retries after the first tmux submit. That strips the accumulated defensive submit branches back to one literal-text-plus-Enter tmux path for `Run Agent Doc`, `session clear`, queue dispatch, and supervisor-owned reopen injects.

- **Live-pane reroutes and file-scoped clear now use one literal-text plus named `Enter` tmux submit path, and they log which delivery branch fired.** The latest `tasks/agent-doc/agent-doc-bugs2.md` repro still showed drafted `\n` behavior even after multiple carriage-return-focused fixes, which meant the remaining shared risk was the tmux submit primitive itself. `tmux-router::Tmux::send_keys()` now always batches literal text plus a named `Enter` instead of using the ASCII `send-keys -H ... 0d` fast path, so `Run Agent Doc` and `Clear Session Context` cross the same live-pane submit boundary as the known-good Claude clear flow. `route --dispatch-only` and file-scoped `session clear` now also write explicit ops-log markers with both the delivery path and submit mode so the next live replay can prove whether the command went direct to the pane or through supervisor IPC. Added tmux-router regression updates for the new submit contract and refreshed the session/tmux spec plus README.

- **Dispatch-only live-pane reroutes now always use the same direct pane submit boundary as `session clear`.** The latest `tasks/agent-doc/agent-doc-bugs2.md` repro showed one remaining split in the Enter handling model: healthy authoritative-actor reroutes already typed the bare reopen through the live pane, but plain registered-pane `route --dispatch-only` reroutes could still fall back to one-shot supervisor IPC injects. `route.rs` now keeps dispatch-only reopens on the resolved live pane's tmux input path for both authoritative and non-authoritative existing sessions, so `Run Agent Doc` and `Clear Session Context` share the same carriage-return submit boundary instead of diverging by route branch. Added a registered-pane dispatch-only regression and updated the tmux/session spec plus README to document the unified pane-submit rule.

- **Dispatch-only reroutes now keep using the authoritative pane even when supervisor state is missing, and they log that degraded branch explicitly.** The latest `tasks/agent-doc/agent-doc-bugs2.md` Claude repro showed a mismatch between two editor-adjacent flows: file-scoped `session clear` could still work because it only needed the live bound pane, while `route --dispatch-only` refused the authoritative-pane path as soon as supervisor IPC stopped reporting a healthy runtime/actor state and then fell back to stale registry heuristics that could send nothing. `route.rs` now keeps the strict supervisor gate for the normal authoritative IPC path, but dispatch-only reroutes may reuse the same authoritative pane directly when that pane is still the current registered/live-owner binding. The route path now writes explicit ops-log diagnostics for both the degraded authoritative fallback and the skipped-fallback shape so the next live replay shows exactly why editor reroute did or did not stay on the actor pane. Added a focused Claude tmux regression and updated the session/tmux spec.

- **Authoritative dispatch-only reroutes and file-scoped `session clear` now submit straight to the live pane when the current binary already owns that pane boundary.** The latest `tasks/agent-doc/agent-doc-bugs2.md` repro showed one stale-supervisor surface still left after the earlier Enter fixes: editor `Run Agent Doc` and file-scoped `agent-doc session clear <FILE>` could still relay through an already-running supervisor process even when the newer binary had already identified the authoritative pane and corrected the submit semantics. `route.rs` now sends `route --dispatch-only` reopens directly through the authoritative pane's tmux input path once the actor-owned pane is ready, and `session_actor_cmd.rs` now sends `/clear` directly to the authoritative pane when that pane is alive on the default tmux server, falling back to supervisor IPC inject only when no directly addressable authoritative pane is available. Added regressions for both direct-pane paths plus the default-server fallback, and updated the session/tmux spec to document the direct-pane boundary for editor reroutes and file-scoped clear.

- **Supervisor-owned reopen and clear injects now use the claimed pane's tmux input path instead of writing raw bytes directly into the child PTY.** The lingering `tasks/agent-doc/agent-doc-bugs2.md` Enter regression was deeper than newline normalization or stale plugin installs: authoritative route/session-clear/auto-trigger injects still wrote the submit payload straight to the managed child PTY, while the only path proven to behave like a real Enter in live tmux panes was the pane-input `send-keys` boundary. `start.rs` now keeps supervisor IPC as the authoritative control surface but re-delivers submitted input through the claimed pane's tmux key path, so `Run Agent Doc`, `Clear Session Context`, queue-dispatch, and auto-trigger all share one real terminal submit method. Added a start-level tmux regression that proves IPC inject now submits through the pane path rather than only a mocked PTY writer, and updated the routing/session specs plus README to document the tighter contract.

- **Dispatch-only Codex reroutes no longer turn a tracked `/clear` into an editor-side restart.** The latest `tasks/agent-doc/agent-doc-bugs2.md` repro showed that the shared Enter-submit fix had landed, but `route.rs` was still applying the older tracked-`/clear` fresh-restart policy before every `agent-doc route --dispatch-only` dispatch. That made `Run Agent Doc` restart Codex instead of sending the expected bare `agent-doc <FILE>` reopen into the live session. Dispatch-only reroutes now keep using the existing supervisor-owned submit path after `session clear`, while the managed non-dispatch route retains the tracked-`/clear` fresh-restart contract for explicit CLI recovery. Added a dispatch-only regression that proves the authoritative actor path still dispatches after a tracked clear without requesting restart, and updated the route/editor runbooks/specs so the editor contract and backend behavior match again.

- **Supervisor-owned command injection now shares one explicit Enter-style submit helper across clear, queue-dispatch, route, and auto-trigger paths.** The latest `Clear Session Context` repro in `tasks/agent-doc/agent-doc-bugs2.md` exposed that supervisor inject senders were still hand-assembling submit bytes in multiple places (`\n`, `\r`, or receiver-side normalization), even though tmux fallback already had a single batched text+Enter contract. `supervisor::ipc::submit_bytes()` now defines the canonical single-line submit payload, `session clear` and queued slash-command dispatch use it directly, route’s supervisor reopen helper delegates to it, and auto-trigger now emits the same Enter byte sequence instead of its own bespoke formatting. Added regression coverage for the shared helper plus exact injected bytes on the `session clear` and queue-dispatch paths, and updated the session/tmux spec so supervisor-owned command injection keeps one explicit Enter method instead of drifting across call sites.

- **Existing managed reroutes now stay on the supervisor-owned reopen path instead of falling back to direct tmux typing.** `route.rs` still uses tmux only to provision a fresh shell/supervisor, but once a managed Claude/Codex session exists the reopen path now goes through supervisor IPC for both managed reroutes and dispatch-only editor reroutes. That removes the remaining split-brain path where non-authoritative live panes could still receive direct `send-keys` reopen traffic, and it makes manual supervisor restarts resolve back onto the same socket-owned boundary instead of silently succeeding through pane typing. Dispatch-only still keeps its one-shot behavior, but the one shot is now a single supervisor inject. Updated the routing docs/README to make the supervisor-only reroute contract explicit.
- **Fallback tmux submits now use one byte-stream command plus carriage return instead of split text/Enter writes.** The latest `tasks/agent-doc/agent-doc-bugs2.md` repro still drafted `agent-doc …` into the managed Codex composer even after the supervisor IPC newline normalization fix, which meant the remaining failure surface was the non-authoritative tmux fallback path. That path still reused `tmux-router`'s literal-text send followed by a separate `Enter`, leaving a gap where managed panes could observe the reopen text without consuming it as one submit. `tmux-router::Tmux::send_keys()` now normalizes trailing line endings away and, for ASCII command payloads such as routed reopens and `/clear`, emits one `tmux send-keys -H ... 0d` command so the pane receives the full text plus carriage return as one stream. Non-ASCII payloads keep the old literal-text fallback. Added `tmux-router` regressions for the exact hex command shape and trailing-line-ending normalization, and updated the routing spec so non-supervisor submits keep the same explicit carriage-return contract as supervisor IPC.
- **Authoritative actor reroutes now queue one prompt-bearing reopen even while the supervisor still reports `starting` or `busy`.** The latest JetBrains `Run Agent Doc` repros in `tasks/buildparty-investor-demo/dev.md` and `tasks/agent-doc/agent-doc-bugs2.md` were failing earlier than the existing busy-pane optimism ladder: once route resolved a healthy authoritative actor, `route.rs` still hard-bailed on supervisor states `starting` and `busy` before it ever tried the existing optimistic dispatch behavior. That meant a live pane that was still accepting keystrokes could reject reroutes with `route will not inject a new trigger because the authoritative actor is busy` even though the supervisor IPC path was available. `route.rs` now allows one optimistic supervisor-IPC reopen for prompt-bearing reroutes while the authoritative actor is `starting` or `busy`, while keeping `waiting_input`, `blocked`, and `closed` fail-closed. Added authoritative-actor regressions for both the busy and still-starting cases, and updated the routing/session-actor specs to document the queue-first boundary.

- **Authoritative actor reroutes now compare canonical harness identities instead of raw supervisor binary labels.** The latest `tasks/buildparty-investor-demo/dev.md` JetBrains repro was not a stale actor record: the durable store correctly recorded `harness: claude-code`, but `route.rs` still compared that value against the live supervisor binary name `claude` and failed closed with `bound to harness claude-code, not claude`. Route now normalizes the live harness into the same canonical identity set used by `.agent-doc/session-actors.json` before validating the authoritative actor record, and added a focused regression proving a healthy Claude-owned actor remains dispatchable through the authoritative route path. Updated the routing/session-actor specs so `claude` vs `claude-code` stays an aliasing detail instead of a routing failure.

- **Supervisor IPC reroutes now normalize submit newlines to carriage return before writing to the managed PTY.** The latest JetBrains `Run Agent Doc` repro in `tasks/agent-doc/agent-doc-bugs2.md` was not just a stale-busy route policy issue: authoritative-actor reroutes and other supervisor IPC inject paths were still forwarding `...\n` verbatim, while the local auto-trigger path already used a carriage-return submit. In raw managed Codex/Claude sessions that let the routed reopen draft a literal newline into the composer instead of acting like Enter, which then left the actor stuck in `Busy` and caused follow-up reroutes to fail closed against the same pane. `start.rs` now normalizes supervisor-injected submit bytes (`\n` and `\r\n`) to `\r` before writing to the child PTY, and added regression coverage around both auto-trigger and IPC inject behavior. This closes the latest JB-plugin routed-submit failure from `tasks/agent-doc/agent-doc-bugs2.md`.

- **Phase-9 verification now locks the single-owner actor contract into both regressions and plugin diagnostics surfaces.** `session_actor.rs` now explicitly rejects stale generation/session updates in unit coverage, preserving the monotonic actor-store boundary after the phase-8 ownership cleanup. The editor specs now require plugin verification for exact `session status` display, actor-backed `session clear` wiring, and durable stage-specific route-dispatch failures. VS Code now mirrors the JetBrains durability expectation by writing routed dispatch failures into a dedicated output surface instead of only a transient toast, while JetBrains unit coverage now proves the session-status and `session clear` command wiring helpers directly. This closes `#sgown9` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Phase-8 now removes legacy owner election from the normal route/start/sync path.** `route.rs`, `start.rs`, and `sync.rs` now treat the authoritative actor record plus the supervisor-backed registered binding as the only normal-path ownership inputs. Latest-open session-log panes, `session_end origin=registry_rebind ... next_pane=...` successors, and generic same-file process-tree matches still surface as diagnostics and explicit repair signals, but they no longer let a stale pane silently reclaim authority or get re-registered during ordinary reroute/sync work. Passive sync now blocks on that legacy associated-pane evidence instead of auto-recovering it, and the route/start regressions now distinguish direct stale-registry state from authoritative actor-backed handoffs. This closes `#sgown8` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Phase-7 now keeps sync repair behind explicit repair commands instead of mutating tmux/session state on the normal path.** `sync.rs` no longer runs hidden `repair_layout(...)` passes or closeout replay when it notices a missing pane during ordinary sync. Instead, normal sync captures diagnostics, records the session-loss evidence, and fails closed with an explicit repair instruction whenever stash/window drift or an open `preflight_started` / `response_captured` / `write_applied` cycle would have required repair. The corresponding repair work now lives on explicit surfaces: `agent-doc repair <FILE>` still owns document-cycle recovery, and `agent-doc session doctor <FILE> --repair` now also runs the file-scoped layout/missing-pane repair helpers before re-reporting status. Added sync regressions for the new inspect-only boundary and updated the tmux/session-actor specs. This closes `#sgown7` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Forwarded `Ctrl+D` no longer has a committed-turn keepalive exception.** The old pane-retention hardening still left a committed-cycle `Ctrl+D` policy branch and closeout probe in `start.rs`, even though the user-facing contract had already moved back to "show the quit menu." `start.rs` now removes that lingering `ctrl_d_committed_cycle_restart_fresh` policy path entirely, so stdin-forwarded EOF/Ctrl-D always reaches the canonical `Enter`/`q` prompt, even immediately after a successful document cycle. The obsolete committed-cycle settle probe/tests are gone, and the README/spec/internal guidance now matches the actual behavior again. This closes the latest follow-up in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Forwarded Codex `Ctrl+C`/`Ctrl+D` now always surface the quit menu instead of silently chaining fresh restarts.** The latest `agent-doc-bugs2.md` repro was two separate policy bugs in `start.rs`: stdin-forwarded `Ctrl+C` was still classified through `CrashPolicy` as a transient non-zero exit before the quit-menu override could run, and stdin-forwarded `Ctrl+D` still short-circuited to `RestartFresh` whenever the previous run had already committed or had exited before surfacing a prompt. `start.rs` now treats a forwarded operator `Ctrl+C` as clean-exit policy input for supervisor bookkeeping, and any forwarded operator `Ctrl+D` or terminating `Ctrl+C` now routes to the canonical `Enter`/`q` prompt regardless of committed-cycle provenance. Only genuinely promptless clean exits without a forwarded operator key still auto-recover. Added start-level regression coverage and updated the Codex/supervisor docs. This closes the latest `Ctrl+C`/`Ctrl+D` restart loop in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Supervisor quit prompts now force a canonical local tty mode so Enter works in managed Claude/Codex sessions again.** The latest `agent-doc-bugs2.md` repro was not another restart-policy misclassification: the quit menu itself still used `read_line()` after restoring whatever stdin termios the parent harness originally gave `agent-doc`, and some managed binding sessions left that inherited tty raw-ish enough that `Enter` arrived as literal `^M` bytes instead of terminating the prompt read. `start.rs` now derives an explicit canonical prompt mode from the saved tty state before every restart/quit menu, re-enabling `ICANON`, `ECHO`, signal handling, and `ICRNL`/newline output for the local supervisor prompt without changing the raw child-forwarding path. Added a start-level regression around the prompt termios normalization and updated the supervisor/Codex docs. This closes the latest Enter-key quit-menu regression in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Editor popup numbering now reserves the primary digits for active document flow instead of low-frequency recovery actions.** JetBrains and VS Code now put `Compact Exchange` and `Restart Supervisor Process` in the primary numbered popup, while `Run with Junie` and `Force Claim for Tmux Pane` remain available from a non-numbered overflow path. The binary also exposes the explicit `agent-doc session restart-supervisor <FILE>` surface (with `session restart` kept as a compatible alias) so both plugins call a clearly named supervisor restart API instead of a vague session label.
- **Phase-6 actor operator commands and editor controls now route through one authoritative session surface.** `agent-doc session` still keeps the existing tmux-session pinning flow (`session`, `session set`, bare `session clear`), but it now also exposes actor-backed `status`, `history`, `attach`, `restart`, file-scoped `clear`, and `doctor` commands. Those commands read the durable actor record, session log, startup-miss marker, and supervisor IPC state instead of inventing separate tmux heuristics. JetBrains and VS Code now surface the same shared controls for Show Session Status, Restart Session, Clear Session Context, and Copy Session Diagnostics, keeping the operator UI aligned with the single-owner actor model.

- **Codex stdin-forwarded Ctrl+C now restores the supervisor quit menu instead of looking like a crash.** The current `agent-doc-bugs2.md` repro was not another generic restart-policy failure: `start.rs` already handled stdin-forwarded EOF/Ctrl-D on the clean-exit path, but a live pane `Ctrl+C` still arrived as `exit_kind=signal exit_signal="Interrupt"` and fell through `CrashPolicy` as a transient non-zero exit. That made the supervisor auto-restart after two seconds instead of offering the cooked-mode `Enter` / `q` choice. `start.rs` now tracks stdin-forwarded `Ctrl+C` explicitly, prompts only when that forwarded byte actually terminated the Codex child, and leaves route/plugin-injected interrupts on the existing automatic recovery path. Added start-level regression coverage for the new forwarded-interrupt classifier and quit-menu branch. This closes the latest `Ctrl+C` quit-menu regression in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Phase-1 single-owner session actor semantics are now pinned in spec and emitted in session logs.** `agent-doc` now documents a stable session-actor contract in `specs/08a-session-actor-contract.md` and starts writing monotonic ownership-generation provenance to `.agent-doc/logs/<session>.log`. Fresh `start` generations record `ownership_transition ... prior_generation=... new_generation=...`, and registry handoffs now include the same generation metadata on the transition, supersession, and `session_end origin=registry_rebind` lines. Legacy logs still infer generation count from repeated `session_start` events for compatibility, but new paths now emit explicit generation fields that later actor-store phases can consume without re-deriving ownership history from tmux heuristics.

- **Codex keepalive EOF/Ctrl-D once again restores the supervisor quit menu on the normal path.** The local `#ctrldmenu` regression in `tasks/agent-doc/agent-doc-bugs2.md` was caused by an over-broad keepalive hardening: `start.rs` treated every forwarded stdin EOF/Ctrl-D as `RestartFresh`, which removed the cooked-mode `Enter`/`q` decision path even when the child had already shown a real prompt and the operator was intentionally trying to quit. `start.rs` now only keeps the restart-fresh exception for the two existing fail-closed cases: child runs that already committed a document cycle, and fresh/fresh-restart runs that clean-exit before surfacing a prompt. Ordinary Codex keepalive Ctrl-D exits return to the quit menu again, while the remaining resume-failure prompt still treats prompt-time EOF as `restart fresh` rather than `quit`. Added start-level regression coverage for the restored split strategy. This closes `#ctrldmenu` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Dispatch-only Codex reroutes now follow same-file restart handoffs before surfacing a stale `still booting` error.** The busy-pane recovery path could already trigger a fresh supervisor restart, but the final `dispatch_only_send_reopen(...)` probe still treated the original pane as fixed and failed closed after the first 2s ready wait. In the live JetBrains `Run Agent Doc` repros for `tasks/claudescore-3.md` and `tasks/sampleorders.md`, that surfaced `dispatch-only codex reopen refused ... still booting` even when the supervisor had already rebound the same document session to a fresh pane or started a newer generation on the same pane moments later. `route.rs` now gives that boot-window timeout one bounded recovery pass: it watches the session log + same-file registry entry for a newer open generation, retries the same pane when a fresh start generation appears there, and follows an alive same-file successor pane when the supervisor hands the session off. Added route-level regression coverage for both the same-pane restart and same-file handoff decisions. This closes the latest JB-plugin `Run Agent Doc` false-refusal shape from `tasks/agent-doc/agent-doc-bugs2.md`.

- **Normal tmux turn paths now fail closed instead of killing panes or manufacturing duplicate stash fallbacks.** `start.rs` no longer auto-focuses, restarts, or supersedes another alive pane for the same document during ordinary `agent-doc start`; it now errors with explicit tmux inspect/capture/kill commands so the user chooses the winner manually. `route.rs` also dropped the "create then stash" fallback branches that could proliferate hidden duplicate panes when `split-window` failed or when an `agent-doc` window already existed without a safe registered anchor. On the sync side, ordinary missing-pane recovery now keeps dead panes retained for diagnostics instead of calling `tmux kill-pane(...)`; only explicit repair flows remain allowed to clean panes up destructively. Added regressions for the new start/route error surfaces and the retained-dead-pane sync path.

- **Dispatch-only reroutes now refuse to transiently rebind another file's pane before readiness checks finish.** `route.rs` was still calling `register_dispatch_target(...)` before it had proven the candidate pane was safe to reuse for the requested file. In the live `#jbpdrop` repro, that let `tasks/software/tsift.md` briefly emit `session_superseded old_pane=%177 new_pane=%169` even though `%169` was the authoritative `tasks/agent-doc/agent-doc-bugs2.md` Codex pane, creating exactly the post-success pane-theft churn the user observed. Route now validates that an existing dispatch target is either already registered for the requested file or currently unbound before any re-register happens, and it fails closed on cross-file reuse instead of emitting a temporary `registry_rebind` that later has to be undone. Added a regression that proves the original file keeps `%169` while the requesting file keeps `%177`.

- **Committed Codex keepalive restarts now discard inherited pre-prompt `Ctrl-D` bytes instead of letting the fresh successor quit itself.** The earlier pane-retention change correctly flipped committed `Ctrl-D` exits from `prompt_user` to `restart_fresh`, but the immediate successor run could still inherit the same raw `Ctrl-D` byte before it ever surfaced a prompt. In the live `sampleorders.md` repro that produced `ctrl_d_committed_cycle_restart_fresh`, then a second clean exit with `ctrl_d=true`, `ctrl_d_prompt_user`, and `user_quit_after_ctrl_d` on the successor pane. `start.rs` now suppresses stale pre-prompt `Ctrl-D` bytes only for that keepalive successor, while still forwarding fresh `Ctrl-D` normally once the child has shown a real prompt. Added start-level regression coverage for the byte-filter helper. This closes `#kpane` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Dispatch-only route now refuses to inject into a pane whose latest run is still in the fresh-start boot window.** `route --dispatch-only` intentionally skips the heavier ack/auto-fix machinery, but it was still treating any alive registered pane as immediately injectable. In the live `sampleorders.md` churn, that allowed a bare reopen to be sent to pane `%175` even though its latest session-log run was still just `codex_start mode=fresh` with no ready prompt yet, which made the follow-up route path look accepted right before later missing-pane churn rebound the owner to `%176`. Dispatch-only route now does one short ready probe when the latest open session-log run for that pane is still at its start event with no committed cycle yet; if the prompt never becomes dispatch-ready in that window, route fails closed instead of sending the reopen into a still-booting pane. Added a tmux-backed regression for the new guard. This closes `#sampleroute` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Live `registry_rebind` successors now remain authoritative even when PID/process-tree provenance drifts.** `sync.rs` already used `session_end origin=registry_rebind ... next_pane=...` to block passive cold-start only while the successor pane was still alive, but the main live-owner proof path still ignored that same tmux/session-log handoff evidence once the successor pane became the registered owner. If supervisor PID or process-tree matching changed after the handoff, sync could downgrade the still-live successor to `registered_pane_unowned` and start replacement/recovery churn again. Live-owner recovery now accepts an alive rebind successor before falling back to generic same-file process-tree matches, so pane continuity follows the tmux handoff itself instead of requiring stale PID identity to survive. Added regressions for direct live-owner reuse plus registered-pane proof on a rebind successor.

- **Passive sync now ignores stale `registry_rebind` closeouts once their successor pane is gone, while still honoring a live handoff pane.** `sync.rs` previously treated any latest `session_end origin=registry_rebind ...` as a permanent `--no-autostart` blocker, even after the recorded successor pane had died or drifted away. That stranded mixed-root documents like `src/sample-app/tasks/sampleorders.md` until a full autostart cycle recreated them, which in turn made later reconciles look like arbitrary pane replacement. Sync now recovers an alive rebind successor as an ownership proof source, and it only blocks passive cold-start while that successor pane is still alive and rooted to the same document. Added regressions for live-successor recovery plus stale-successor passive reopen.

- **VS Code split-layout sync now preserves editor groups instead of flattening every visible markdown tab into one tmux column.** The extension was still building `agent-doc sync --col a,b,c` for both manual sync and automatic tab-sync, even when the user had separate visible editor groups. In narrow shared `agent-doc` windows that let tmux-router reinterpret a side-by-side layout as one stacked column and stash a healthy running pane during passive reconciliation. The VS Code extension now emits one `--col` per visible editor group, keeps empty split placeholders so non-markdown side panes do not collapse column identity, and makes tab-sync dedup/signatures track column structure instead of just the flat file set. Added TypeScript regressions for split columns, placeholder columns, and split-with-one-markdown tab sync. This closes the latest `claudescore-3.md` passive-stash gap from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Codex now treats bare SSH `socket: Operation not permitted` output as required-SSH capability drift when the command context proves the target.** The previous resumed-session detector only matched transcript lines that already contained the required alias/host term, so a Codex `command_execution` event like `command: "ssh sampleorders-server true"` with `aggregated_output: "socket: Operation not permitted"` leaked through as a raw task failure and skipped the fresh-retry path. `agent/codex.rs` now inspects command-execution context: if the command itself proves SSH against a declared `required_ssh_targets` entry, bare socket EPERM output still triggers the existing one-time fresh retry and then fail-closed required-SSH error path. Added direct detector coverage plus blocking/streaming regressions, while keeping localhost/CDP EPERM on its separate capability-drift path. This closes `#ssheperm` in `tasks/agent-doc/agent-doc-bugs2.md`.
- **Committed routed Codex runs no longer close their tmux pane just because `Ctrl-D`/stdin EOF was forwarded during the child run.** The live `sampleorders.md` repro was no longer a sync/rebind ownership bug: pane `%166` completed `commit_success`, then `start.rs` saw `ctrl_d_forwarded`, dropped into the quit prompt path, and logged `user_quit_after_ctrl_d`, which closed the still-healthy claimed pane immediately after a successful document cycle. The supervisor now inspects the latest session-log run before applying the Ctrl-D clean-exit policy. If that run already recorded a committed `document_cycle`, Codex restarts fresh and keeps the pane attached instead of offering the quit prompt. Added session-log parsing coverage for committed-cycle detection plus start-level regression coverage for the new restart-fresh branch. This closes the latest `sampleorders.md` pane-drop from `tasks/agent-doc/agent-doc-bugs2.md`.
- **JetBrains route failures now stay copyable after the first notification moment.** `TerminalUtil.sendToTerminal()` was still collapsing `agent-doc route --dispatch-only` failures into a plain IDE error notification, which made startup-miss and pending-drift diagnostics effectively transient when the user launched `Run Agent Doc` from the plugin. JetBrains now persists the exact route output under `.agent-doc/state/editor-route-errors/`, marks the failure notification as important, and adds copy/open actions so the original binary-owned error remains available without paraphrasing. Added Kotlin unit coverage for the saved diagnostics path and exact-output persistence. This closes `#jberr` in `tasks/agent-doc/agent-doc-bugs2.md`.
- **Optional closeout sidecar reads now treat late `ENOENT` as absence instead of a hard failure.** `session-check` and the closeout helpers were still using `exists()`-then-`read()` for cycle-state, capture, startup-miss, ops-log, pre-response, and CRDT sidecars. Under full-suite tempdir churn, that left a narrow race where a sidecar could disappear after discovery but before the read, bubbling `No such file or directory (os error 2)` out of otherwise-valid closeout checks such as `session_check_skips_pending_done_warning_when_id_was_recorded`. Optional sidecar loads now read directly and downgrade only `NotFound` to `None`, preserving other I/O failures while eliminating the transient `ENOENT` flake. Added unit coverage for the shared optional-read helper. This advances `#sceno` in `tasks/agent-doc/agent-doc-bugs2.md`.
- **Sync now prefers the newest open session-log pane over stale same-file process-tree matches during live-owner recovery.** `sync.rs` already accepted generic `agent-doc`/harness argv matches as a fallback ownership hint, but it checked that process-tree evidence before the latest open session-log owner. In the live `sampleorders.md` reroute loops, that let an older pane that still had a same-file Codex process win back ownership immediately after a fresh replacement pane had already recorded the newest `session_start`, which in turn caused `registered_pane_missing` on the fresh pane and rebound the registry to the stale pane. Live-owner recovery now checks path provenance, supervisor identity, and the newest open session-log owner before generic process-tree matching, so a fresh pane that has already started the latest run stays authoritative unless stronger cross-file proof says otherwise. Added a tmux-backed regression for the stale-process-tree vs fresh-session-log conflict. This advances `#mrreap` in `tasks/agent-doc/agent-doc-bugs2.md`.
- **Fresh routed auto-starts now keep the fresh pane authoritative instead of handing dispatch back to an older same-session pane during boot.** `route.rs` was still re-reading `sessions.json` after the fresh-pane ready wait and would follow any concurrent same-session rebind back to an older pane, even when that rebind came from a layout/sync race rather than real ownership proof. In the live JetBrains `agent-doc-bugs2.md` repro this surfaced as `fresh_route_dispatch_handoff ... fresh_pane=%144 dispatch_pane=%127`, immediately superseding the new pane inside the same `agent-doc` window and making the completed run look like it had disappeared. Fresh-route dispatch now re-registers and uses the pane it just created unless that pane is cross-file invalid, so post-start geometry churn cannot steal the reroute away from the new pane. Added a regression that forces a competing registry rebind during boot and proves the fresh pane still receives the reopen and remains authoritative. This advances `#jbpdrop` in `tasks/agent-doc/agent-doc-bugs2.md`.
- **Split the command spec monolith into focused sibling specs and added a reusable split runbook.** `specs/07-commands.md` is now the stable command-spec index, while the normative detail moved into `specs/07-core-commands.md`, `specs/07-session-tmux-commands.md`, `specs/07-closeout-commands.md`, and `specs/07-orchestration-commands.md`. Added `runbooks/split-spec-files.md`, bundled it into installed harness runbooks, and documented the stable-index split rule plus the managed-vs-custom ownership boundary in `CLAUDE.md` / `README.md`.
- **Sync now fail-closes when an alive pane is still the latest open session-log owner, instead of fabricating `registered_pane_missing`.** `sync.rs` already refused to reuse an alive registered pane without live-owner proof, but it could still fall through to `repair_missing_registered_pane(...)` immediately afterward and synthesize pane loss even when the session log still showed that same pane as the newest open run. That was enough to orphan live `sampleorders.md`/mixed-root panes after a routed reopen or post-success restart window. Sync now treats that shape as a fail-closed ambiguity window, records explicit `registered_pane_open_session_log_owner ... action=fail_closed` provenance, and blocks replacement for the cycle instead of rebinding over the pane. Added regression coverage for the new session-log-owner guard.
- **Sync now fail-closes when an alive Codex pane still has drafted input, instead of logging synthetic pane loss and rebinding over it.** `sync.rs` already required live-owner proof before trusting an existing registered pane, but an alive pane that temporarily lost that proof could still fall through to `repair_missing_registered_pane(...)`, record synthetic `registered_pane_missing`, and provision a replacement even while the Codex composer still held live drafted input. Sync now reuses the shared harness prompt parser to detect protected Codex composer/search states, records explicit `registered_pane_protected ... action=fail_closed` provenance, and blocks replacement for that cycle instead of emitting `session_end origin=sync_missing_pane`. Added harness/sync regression coverage for drafted prompts, queue-state protection, and idle-placeholder non-matches. This advances `#prreap` in `tasks/agent-doc/agent-doc-bugs2.md`.
- **Route now derives its tmux session from the requested file/layout roots instead of only the launcher CWD.** `route.rs` now reuses the same root-aware session chooser as `sync`: a nested-repo `agent-doc route` without explicit window context honors the target file's own nearest `.agent-doc/config.toml` pin, and a mixed-root editor layout prefers the shared workspace-root pin over the focused child root. This prevents JetBrains `Run Agent Doc` from auto-starting nested documents into the wrong submodule session when the visible split already proves a shared workspace `agent-doc` window. Added route regressions for both the single nested-file and mixed-root layout cases, and updated the routing/editor specs.
- **Passive editor sync now favors a fast pane handoff before the heavier ownership-recovery machinery runs.** `sync.rs` now lets `agent-doc sync --no-autostart` reuse the latest matching session-log pane immediately, fall back to an alive registered pane rooted to the same document when no direct match exists, and cold-start a fresh pane right away when the document has no matching or exclusive registered owner. This removes the slow process-tree/supervisor scan from the common editor-selection path while keeping the heavier recovery logic for non-happy-path cases. Added a regression covering alive registered-pane reuse on the passive path.
- **Sync now refuses to treat an unrelated live pane as a document owner, and passive/fail-closed files no longer borrow spare panes during reconcile.** `sync.rs` now requires a registered pane to still prove live ownership before reusing it, so a merely alive pane cannot satisfy another same-root document just because the registry drifted. When recovery or `--no-autostart` intentionally leaves a managed file unresolved, agent-doc now tells `tmux-router` not to donate a same-column or spare visible pane to that file, preventing `tasks/software/tsift.md` and similar selections from reusing the old `agent-doc-bugs2.md` pane. Added regressions for unowned-alive pane rejection and the safe-passive no-alias path.
- **Safe passive mixed-root sync now preserves the existing visible tmux layout when a blocked file cannot be provisioned.** The earlier no-alias guard stopped `sync --no-autostart` from donating a spare pane to the blocked file, but the reconcile phase could still collapse the shared `agent-doc` window down to whichever foreign pane remained resolved, effectively making that foreign pane authoritative anyway. `sync.rs` now short-circuits before tmux-router reconciliation whenever passive sync leaves any visible file blocked, so the current live panes stay visible and the binary emits a warning instead of stashing the workspace pane out from under the user. Added regression coverage for the preserved-layout path and updated the session/tmux sync spec. This closes the remaining `#jbsubroot` mixed-root passive-sync replay from `tasks/agent-doc/agent-doc-bugs2.md`.
- **`agent-doc sync` now reuses the recorded layout by default and re-normalizes tmux windows after reconcile.** `sync.rs` now reads and writes `.agent-doc/last_layout.json` from the resolved sync scope instead of blindly anchoring it to the caller's CWD, and a no-`--col` `agent-doc sync` replays that saved layout as its default input. Sync also runs `repair_layout` again after `tmux_router::sync`, then pushes `agent-doc` back to index `0` and stash windows directly after it so post-reconcile pane mutations do not leave the tmux window order drifted. Added regressions for recorded-layout fallback, shared-root layout-state scoping, stash-window index normalization, and tmux-router overflow-stash discovery.
- **Windowless mixed-root sync now stays on the shared workspace tmux session, and stash rescue no longer leaks panes into the caller's current session.** `sync.rs` now derives its session pin from the visible document set's shared `.agent-doc` root before consulting the ambient tmux client, so alternating focus between workspace-root and child-root documents can no longer ping-pong the same layout between session `4` and session `1`. On the tmux side, `tmux-router` now targets the source pane's own session when breaking a stashed pane into a new window, closing the exact bug where rescuing a pane from session `4` stash could recreate `agent-doc` under the currently attached session `1`. Added regressions for the mixed-root windowless sync selection, the CWD-independent rescue path, and the tmux break-pane session-preservation contract.
- **Editor tab-sync now suppresses JetBrains/VS Code split-layout bounce-back.** In split editor layouts, JetBrains fires a spurious `selectionChanged` for the other split's file ~1 second after the user navigates to a file, causing the tmux pane focus to bounce back. Both plugins now track the pre-command focused file and suppress re-focus events that target that file within a 1.5-second settle window after a successful focus/sync command. Added `BounceBack` classification to both planners, regression tests for bounce-back suppression and expiry, and updated the session-routing spec.
- **Missing-pane sync recovery now fails closed when closeout replay itself needs manual repair.** `sync.rs` already tried `repair()` before starting a replacement pane, but a replay failure such as `pending/backlog patch changed non-list content` still only logged a warning and then fell through to auto-start. Sync now treats that shape as a deterministic repair-needed state: it records the missing-pane provenance, preserves the durable closeout capture, and skips replacement auto-start until the user repairs the document. Added a regression for the `response_captured` + unsupported backlog patch shape behind the latest `sampleorders.md` churn.
- **Busy-route progress logging no longer panics on Unicode prompt lines during live reroutes.** `route.rs` was trimming the "Still waiting for ..." tmux status line with a raw byte slice, which panicked as soon as a captured Codex prompt/status line included multibyte glyphs such as the ellipsis in `~/.../boost-clien…`. Route now truncates those diagnostics on char boundaries, and a regression locks the live `sampleorders.md` reroute shape that previously crashed in the busy-pane replay.
- **Passive `sync --no-autostart` can now cold-start only after it proves there is no live owner left.** The earlier editor-sync hardening correctly stopped passive tab/layout churn from replacing visible panes, but it also left `dev.md`, `claudescore-3.md`, and similar documents stranded after their last pane had already exited cleanly. `sync.rs` now distinguishes "do not replace a live or ambiguous owner" from "never start anything": safe passive sync still runs the full owner-recovery / startup-miss / recent-loss guards, but if no live owner survives and the latest session log is genuinely closed, it may provision a new pane so editor selection brings the document back into the `agent-doc` window. Passive sync still refuses that cold-start when the latest closeout is only `session_end origin=registry_rebind`, because that shape means a newer pane era may still own the document elsewhere. Added passive-autostart guard coverage plus updated command/editor specs.
- **`claim` now treats normalized registry keys as document identity instead of mistaking them for session UUIDs.** The session registry is keyed by canonical absolute file path, but `claim.rs` was still comparing that key directly to the current document's `agent_doc_session`. That made a document's own live pane look like a foreign claim and caused `Claim for Tmux Pane` on submodule-backed files such as `sampleorders.md` to provision a duplicate pane instead of reusing `%75`. `claim` now recognizes same-document ownership by canonical document identity, improves the conflicting-claim log label, and applies the same canonical matching when clearing stale claims. Added regressions for both normalized-registry-key and relative-entry-file shapes.
- **Codex Stop-hook captures now normalize safe backlog patches before durable replay.** Replayable template closeouts that include a `patch:backlog` block no longer persist the raw backlog patch into the pending/capture ledger. The capture path now applies the same safe backlog normalization used by the write pipeline first, strips the backlog patch from the stored response body, and leaves recovery to replay only the exchange-safe payload. This closes the latest `sampleorders.md` pane-loss shape where `sync_missing_pane_closeout_recovery` failed on `pending/backlog patch changed non-list content` and then replaced the running pane anyway.
- **Split-layout tab selection now stays on non-destructive sync instead of plain focus.** The earlier shared JetBrains/VS Code tab-sync contract still treated any unchanged visible markdown set as a pure `agent-doc focus <file>` move, which could leave a selected document stranded in `stash` and therefore missing from the visible `agent-doc` tmux window. Both plugins now keep multi-document visible layouts on `agent-doc sync --no-autostart ...` even when only the active tab changes, while single-document tab switches still use `focus`. Added Kotlin and TypeScript regressions plus updated editor specs.
- **Codex can now require and prove SSH capability before trusting resumed sessions.** Documents may declare `required_ssh_targets` in frontmatter, and the Codex backend now probes those SSH targets before launch. When a resumed Codex session later surfaces a target-specific SSH failure, agent-doc treats that as capability drift, retries once with fresh `codex exec`, and then fails closed if the required SSH capability still cannot be proven. Added frontmatter round-trip coverage plus Codex backend regressions for alias/config degradation and SSH-triggered fresh retry.
- **JetBrains and VS Code tab-selection sync now share the same non-destructive focus contract.** Both editor plugins now distinguish pure active-file changes from visible-layout changes: a tab switch with the same visible markdown set issues `agent-doc focus <file>`, while any visible-set change issues `agent-doc sync --no-autostart ...` instead of an autostart-capable sync. JetBrains no longer routes tab selection through provisioning sync from the focused root, and VS Code no longer treats every tab change as a layout sync. Added focused Kotlin and TypeScript regression coverage plus updated the JetBrains tab-sync spec.
- **Codex Stop-hook closeout now salvages valid template patchbacks even when `last_assistant_message` includes plain progress commentary ahead of the patch body.** The latest direct `agent-doc <FILE>` BuildParty repro still reached the Stop hook, but replay failed closed because the final assistant payload mixed two ordinary progress lines with a valid `patch:exchange` + `patch:backlog` closeout. `replay_guard.rs` now treats the narrow "plain prose prefix, then clean patch suffix" shape as recoverable by stripping the prefix and replaying only the patch body, while still blocking transcript markers, structured unmatched text, trailing/interstitial unmatched content, and full component dumps. Added replay-guard coverage plus a Codex Stop-hook regression that proves the sanitized patch replay commits cleanly without leaking the commentary into the document. This closes `#adinv2` from `tasks/agent-doc/agent-doc-bugs2.md`.
- **JetBrains cross-root sync now keeps workspace column memory even when focus moves onto an unmanaged nested-root markdown file.** The plugin had still chosen the focused file's nearest `.agent-doc` root as the `agent-doc sync` working directory, so focusing `src/agent-doc/specs/08-session-routing.md` while `tasks/agent-doc/agent-doc-bugs2.md` and `src/sample-app/tasks/sampleorders.md` shared the screen made sync read/write `src/agent-doc/.agent-doc/last_layout.json` instead of the workspace root state that remembered the left pane. JetBrains sync now uses the single visible root when all visible markdown files belong to one agent-doc root, but falls back to the workspace root `.agent-doc/` whenever the visible layout spans multiple roots. Added unit coverage for the single-root and cross-root root-selection cases, and updated the JetBrains/plugin specs.
- **Windowless sync now honors the live project tmux-session pin before inheriting the caller's attached session.** `sync.rs` had drifted from the documented session-resolution contract and was resolving its target session as `--window -> current session`, which let an attached session like `1` take over even while `.agent-doc/config.toml` still pinned a live session `0`. Sync now shares the same precedence contract as route: explicit window/session context first, then live project `tmux_session`, then current session, with harness fallback remaining route/start-only. Added route + sync regressions for the live-pin and dead-pin cases, and expanded the session-routing spec with an editor-to-tmux truth table covering `agent-doc`/`stash` outcomes.
- **JetBrains cross-root split reporting no longer drops the outer markdown pane when focus moves into a nested submodule.** The plugin was still filtering visible markdown files to the focused file's resolved root before building `sync` and routed layout hints, so a workspace-root + submodule split could oscillate between the correct two-column absolute layout and a one-column `sampleorders.md` report. That one-column report let `agent-doc sync` legitimately stash the other pane, which in turn fed later session-drift / stale-session cleanup noise. JetBrains now preserves all visible markdown files as absolute paths across both sync and route layout reporting, keeps empty columns for mixed splits, only rewrites submodule-local workspace-relative paths when needed, and bumps the plugin build version to `0.2.88`. Added unit coverage for cross-root sync normalization, visible-file collection, and routed layout arg generation.
- **Sync now treats the latest open session-log pane as fail-safe live-owner proof before replacement.** `sync.rs` no longer limits associated-pane recovery to argv/process-tree or supervisor-socket evidence. When a managed document's latest session log still shows an open pane, and that pane is still tmux-alive in the same project root, sync now accepts it as an ownership proof source, re-registers it through the shared associated-pane path, and only considers `registered_pane_missing` replacement after that fail-safe proof is exhausted. Added tmux-backed regressions for direct live-owner reuse and associated-pane recovery via session-log provenance. This advances `#ownergap` in `tasks/agent-doc/agent-doc-bugs2.md`.

- **Missing-pane sync recovery now reopens stranded closeouts before it starts another pane.** `sync.rs` already logged `registered_pane_missing` / dead-pane provenance, but it only self-healed stale `preflight_started` locks. If the owning pane disappeared after `response_captured` or `write_applied`, the durable capture could stay stranded until a later manual/preflight recovery. Sync now attempts the same binary-owned recovery path immediately on pane loss: `response_captured` replays through `repair` + strict closeout, `write_applied` finishes the missing commit boundary when the file/snapshot already prove the response landed, and the session log records explicit `sync_missing_pane_closeout_recovery_*` provenance before replacement starts. Added sync regressions for both the replayed-response and already-applied commit-boundary shapes. This advances `#jbcap` in `tasks/agent-doc/agent-doc-bugs2.md`.
- **JetBrains passive sync no longer reads `.agent-doc/sessions.json` or live tmux state to decide window/autostart policy.** The plugin now reports only absolute layout/focus file paths to `agent-doc sync`, preserves empty column placeholders for mixed markdown/non-markdown splits, and claim / force-claim no longer inject a plugin-chosen `--window`. This removes the Kotlin-side duplicate ownership heuristic so passive autostart, ambiguous-owner fail-closed behavior, cross-root tmux targeting, and remembered two-pane restoration live solely in the Rust binary. Added JetBrains unit coverage for absolute-path sync command generation and bumped the plugin build version to `0.2.87`.
- **Editor-driven tmux sync now has a non-destructive mode, and JetBrains startup no longer auto-runs `resync --fix`.** `agent-doc sync` now accepts `--no-autostart`, which keeps reconciliation/layout updates from auto-starting replacement sessions when pane ownership is uncertain. JetBrains automatic layout listeners, claim follow-up sync, and VS Code's editor-driven sync paths now use that mode so passive editor activity cannot replace a visible pane just because startup/restore briefly lost ownership proof. JetBrains project-open also switched from `agent-doc resync --fix` to a report-only `agent-doc resync` audit, shrinking plugin-triggered tmux close/replacement surface area down to explicit recovery paths such as duplicate/stash cleanup inside the CLI itself. Added JetBrains unit coverage for the non-autostart sync command and the non-destructive startup audit contract. This addresses the latest `#jbptrk` guidance in `tasks/agent-doc/agent-doc-bugs2.md`.
- **JetBrains repeat `Run Agent Doc` clicks now supersede stale plugin-spawned route processes instead of waiting behind them.** The editor `SubmitAction` already stopped inferring "already running" from local state, but a previous `agent-doc route --dispatch-only` process could still stay alive long enough that the next click felt blocked after a canceled Codex turn and `/clear`. `TerminalUtil.sendToTerminal()` now tracks one in-flight route process per document, terminates the stale process when the user reruns the action, suppresses stale-process failure noise, and immediately launches a fresh dispatch. Added a focused Kotlin unit test and bumped the JetBrains plugin version so the fix is installable. This closes the latest "second Run Agent Doc should immediately resend after `/clear`" report in `tasks/agent-doc/agent-doc-bugs2.md`.
- **JetBrains `Run Agent Doc` is now silent on success and progress instead of emitting any route-side UI hint.** The earlier cleanup removed the dedicated in-flight balloon, but the remaining success/progress hint path was still surfacing as a bottom-right IDE notification for some users. `TerminalUtil.sendToTerminal()` now only logs successful reroutes and reserves JetBrains notifications for real route failures, while the JetBrains editor spec/agent notes now describe the fire-and-forget contract explicitly. This closes the latest "remove the bottom right notification on `Run Agent Doc`" report in `tasks/agent-doc/agent-doc-bugs2.md`.
- **Prompt-bearing diff classification now suppresses stale-boundary raw-answer tails before `preflight`, `plan`, and write-path consumers ever see them.** The earlier `buildparty-investor-demo/repo.md` fix taught `session_check.rs` and routed cycle-ack gating to ignore a stale-boundary prompt that was already followed by plain assistant completion prose (`I updated ...`, follow-up bullets, etc.), but the lower-level `diff.rs` classifier still emitted that same tail as three fresh `prompt_target` blocks. That left `agent-doc preflight` / `agent-doc plan` falsely reopening completed work even after repair had already proven the tail was answered. The shared prompt-bearing classifier now drops answered prompt runs at the source, so `preflight`, `plan`, route/session-check, prompt-prefix normalization, and write-path snapshot decisions all agree on the same actionable tail. Added a regression with the exact `src/session-share/tasks/buildparty-investor-demo/repo.md` stale raw-answer shape.
- **Editor Run now has an explicit dispatch-only route mode instead of layering more busy-session heuristics on top of JetBrains / VS Code hotkeys.** `agent-doc route --dispatch-only` resolves the owning pane, sends the bare `agent-doc <FILE>` reopen, and returns without route-owned startup-miss gating, `/clear` relaunch policy, busy-pane recovery, or cycle-ack waiting. JetBrains `Run Agent Doc` now saves and dispatches immediately with that mode, and VS Code's Run action no longer blocks behind a plugin-local "Command already in progress" gate. Managed `agent-doc route` keeps the existing guarded behavior for CLI callers that still want binary-owned recovery. Added route regressions for dispatch-only busy-pane dispatch and timed-out bare reopen acceptance, and updated the editor specs / README.
- **JetBrains `Run Agent Doc` no longer self-blocks repeat reroutes, and live Codex reroutes now stay optimistic once the correct pane has accepted the bare reopen.** The JetBrains action no longer short-circuits on a stale local "route already in progress" flag, so cancel + `/clear` no longer gets trapped in the editor before the CLI runs. On the backend, `route.rs` still validates the target pane/file binding and still records startup-miss diagnostics, but once a live Codex pane for that file has accepted the bare `agent-doc <FILE>` reopen, missing routed submission proof or missing follow-up cycle-ack no longer fail-closes the reroute. The same optimistic rule now covers the alive-pane busy-session ladder after scoped fix / fresh restart / bounded interrupt recovery, while dead panes still fail closed. Added route regressions for missing-ack, same-cycle committed churn, and alive-busy timeout shapes, plus JetBrains plugin verification. This closes the latest `/clear` reroute blocker from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Routed cycle-ack gating now ignores stale-boundary prompt tails that already have raw assistant completion prose, not just formal `### Re:` blocks.** `session_check.rs` and `route.rs` already shared the "answered prompt below a stale boundary" detector, but it only recognized a later `### Re:` / `## Assistant` marker. The latest JetBrains `Run Agent Doc` failure for `src/session-share/tasks/buildparty-investor-demo/repo.md` hit the older raw-tail shape instead: the stale boundary was followed by the user prompt, then plain assistant completion prose (`I updated ...`) and bullets, so route kept waiting 30 seconds for a ghost `pending prompt_target`. The detector now also treats a narrow set of assistant-style completion lines and follow-up bullets as an answered tail, and new regressions prove both `session-check` and routed cycle-ack gating skip that raw-response shape while still keeping plain unanswered prompts actionable. This closes the latest JB reroute startup-miss false positive from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Busy same-document Codex reroutes now probe `C-g` before the generic interrupt closeout so reverse-history-search panes can recover instead of fail-closing.** `route.rs` already had the bounded same-document interrupt ladder (`Escape` + `C-c`) after scoped fix and fresh restart, but the latest JetBrains `agent-doc-bugs2.md` reroute still stranded the live pane in shell-history search and never reached a dispatch-ready prompt again. The busy-pane recovery path now sends one short `C-g` readiness probe first, immediately reuses the pane when that clears a latent `reverse-i-search` / history-search substate, and only falls back to the existing `Escape` + `C-c` sequence when the probe does not restore readiness. Added a tmux regression that requires `C-g` to recover the live pane and updated the route command spec. This closes the latest JB-plugin reroute failure from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Same-document routed busy-pane failures now preserve the final recovery stage instead of collapsing back to the stale pre-interrupt timeout.** `route.rs` already ran the scoped `agent-doc fix`, one bounded fresh supervisor restart, and one bounded Codex interrupt recovery for the `agent-doc-bugs2.md` `#selfrt` family, but the final fail-closed error still reused the older pre-interrupt `timeout` detail even when the last readiness check had already proven a more specific blocker like `reverse-i-search`. The interrupt recovery path now returns structured `ready / blocked / timed_out / skipped` outcomes, the final busy-session closeout surfaces that bounded-interrupt stage detail directly, and a regression covers the same-document shape where a healthy supervisor stays authoritative but the interrupted pane still lands in `interactive shell reverse-i-search`. This closes `#selfrt` from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Repair/Stop-hook closeout can now adopt a visible response patchback even after the replay payload is gone.** `repair.rs` now detects the narrow shape where the live document already contains a fresh `### Re:` / `## Assistant` block that the snapshot lacks, but no pending/capture artifact or replayable `last_assistant_message` survived. Instead of leaving that response as plain working-tree drift that still needs a separate human commit, repair synthesizes the visible response back through the existing already-applied dedup path, advances snapshot + `write_applied`, and lets the normal strict closeout helper commit it. Added repair and Codex Stop-hook regressions for the routed-no-ack / visible-response recovery shape. This closes `#8zjh` from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Template write/repair now recover the duplicated `agent:exchange` close-marker shape before failing on a stranded pending response.** `write.rs` no longer stops at the raw `closing marker <!-- /agent:exchange --> without matching open` parser error when a merged template document still has the real exchange opener plus a second escaped close marker after the response tail. The normalization path now detects that exact unmatched-close chain, uses `template.rs` to move the escaped response block back inside the real exchange component, drops the stray duplicate close, and only then re-runs the normal transcript/tail guard. `repair.rs` applies the same canonicalization when fixing no-pending template drift, so the May 1 `claudescore-3.md` `#xguard` family can finish through the binary-owned repair/write path instead of requiring manual response surgery. Added direct template and write-path regressions plus spec updates. This closes `#xguard` from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Routed Codex submission-proof gating now respects nearer `.codex` shadowing instead of assuming every workspace-root hook install is reachable from nested repos.** `route.rs` already learned to scan every tracked `.agent-doc` ancestor for `.codex/hooks.json`, but that was still too optimistic for child repos like `src/session-share` when a nearer `.codex` path existed as a file or hookless boundary. In that shape the live Codex pane never emits `UserPromptSubmit` state for the reroute, so waiting for hook-backed submission proof only creates a new false failure after tmux already accepted the bare reopen. Route now only requires hook-backed dispatch-start proof when the rerouted file can actually see that hook install on its own upward `.codex` walk, and a new regression covers the nested-shadowing case. This closes `#cs3intr` from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Fresh-restart routed cycle-ack retries now follow authoritative pane handoff before the second reopen, and they fail at the correct stage when the replacement pane never becomes dispatch-ready.** `route.rs` already had a one-shot fresh-restart retry after a live Codex reroute was accepted/consumed but never started a new document cycle, but that retry still waited on and resent into the original pane even if supervisor recovery had already moved the session to a replacement pane. The retry path now re-resolves the authoritative pane after the fresh restart, keeps the original resolved absolute reopen path for the second send, and surfaces a dispatch-readiness failure when the replacement pane stalls in a blocked shell substate instead of misreporting the outcome as another generic "no new cycle started" startup miss. Added a regression that forces the fresh-restart retry handoff to a replacement pane and updated the routing spec. This closes `#rbgap` from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Routed Codex submission proof now stays enabled across nested `agent-doc` roots when the workspace-level install owns the hooks.** `route.rs` used the nearest `.agent-doc` root to decide whether hook-backed dispatch-start proof was available, which silently disabled the stronger "submitted/consumed" stage for child repos like `src/session-share` and `src/sample-app` when only the workspace root had `.codex/hooks.json`. Route now scans every tracked `.agent-doc` ancestor for hook installation, matching `codex_hook.rs`'s cross-root state storage, so child-repo reroutes keep the explicit "accepted vs submitted vs consumed" partition instead of collapsing back to the weaker acceptance-only path. Added direct regressions for the nested-root positive and no-hook negative cases, and updated the routing spec. This advances `#rbgap` from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Resumed Codex backend turns now auto-discard stale local-browser/CDP capability drift and retry fresh once.** `agent/codex.rs` now watches resumed `codex exec resume <id>` responses for the specific local socket EPERM signature (`Operation not permitted` on `127.0.0.1:9222` / `localhost:9222`). When that appears before a real response lands, agent-doc treats the resume as poisoned capability inheritance, reruns the same prompt once through a fresh `codex exec`, and lets the fresh thread replace the saved `resume` id instead of trusting the stale one again. Added blocking and streaming regressions plus spec updates. This closes `#cxcdp` from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Fresh Codex sessions that die before showing a prompt now restart fresh instead of blindly resuming, and fresh-route startup-miss recovery no longer hands dispatch back to the pane it just replaced.** The remaining `claudescore-3.md` `/clear` reroute miss was not in `route.rs`'s path handling; it was in the supervisor clean-exit policy. A fresh/fresh-restart Codex child could exit `0` before ever surfacing an idle prompt, and `start.rs` would still treat that as a healthy clean exit and chain `--continue`, which later collapsed into `auto_trigger_timeout reason=no_prompt_after_30s`. The supervisor now tracks whether the current child ever exposed an idle prompt and treats a promptless clean exit on a fresh run as failed startup provenance, forcing a fresh restart instead of resume. Separately, when route has already deregistered a startup-miss pane and launched a fresh replacement, the post-ready handoff check now carries that replaced pane as explicit blocked provenance instead of relying only on the persisted startup-miss file that was just cleared. That prevents the fresh pane from handing the reopen straight back to the stale owner during startup. Added start/route regressions and updated the supervisor / command specs. This closes `#clrrt` from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Fresh-restart routed retries now preserve the original resolved reopen path instead of downgrading to `file.display()`.** `route.rs` already resolved routed triggers to an absolute `agent-doc <FILE>` path on the first send, but the one-shot fresh-restart retry after a missed cycle ack rebuilt the reopen from the caller path and could resend `agent-doc tasks/claudescore-3.md` into a `src/session-share` Codex pane. The retry path now reuses the same resolved absolute file path as the initial dispatch, and added regressions cover both the generic fresh-restart resend and the relative-document submodule shape. Updated the routing spec to make the retry-path invariant explicit. This closes the latest `/clear` reroute miss from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Component parsing no longer panics when the non-agent comment preview lands inside a multibyte glyph.** `component.rs` now bounds its fast-path `<!-- ... -->` preview on UTF-8 char boundaries before checking whether a comment is really an `agent:` marker. That keeps ordinary prose comments near `❯` and other multibyte text on the normal ignore path instead of panicking on a sliced preview, while preserving the existing structured errors for malformed real component nesting. Added a regression for the `#utf8p` repro shape and documented the valid-UTF-8 no-panic invariant in `SPEC.md`.
- **Busy same-document Codex reroutes now get one bounded interrupt recovery before the final fail-closed error.** `route.rs` still refuses to append a bare reopen into a genuinely non-idle pane, but after the normal scoped-fix and fresh-restart ladder is exhausted it now sends one interrupt sequence to the authoritative live Codex pane, waits for a real empty prompt again, and reruns the same bare `agent-doc <FILE>` reopen once before giving up. This keeps routed follow-ups from fail-closing just because the live pane was stranded in a shell substate or other stale busy UI after recovery, without dropping the existing multiline/drafted-composer safety checks. Added a route regression for the interrupt-recovery retry path and updated the routing spec. This closes the latest busy-pane reroute failure from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Routed Codex reruns now fail fast on interactive shell substates and accept broader submission proof for the same document.** `harness.rs` now classifies busy Codex panes by reason, including interactive terminal substates like `reverse-i-search`, so `route.rs` stops burning the full idle wait on panes that can never accept a reroute and immediately falls into the existing scoped-fix / bounded-restart path. On the post-send side, routed Codex proof no longer requires the hook store to echo the exact bare reopen text; any newer tracked prompt state for the same document now counts as submission proof, while an exact prompt match still records the stronger "consumed" stage. That lets route distinguish "drafted", "accepted but no submission proof", "submitted", and "consumed" without collapsing hook races back into false failures. Added harness/route regressions and updated the route command spec. This closes `#snrun` from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Routed Codex reopen now proves harness consumption before the later cycle-ack health check, and healthy busy-pane no-op fixes go straight to one fresh reroute.** `codex_hook.rs` now exposes the latest tracked prompt state for a document, and `route.rs` uses that `UserPromptSubmit` hook record as an explicit dispatch-start proof for bare `agent-doc <FILE>` reroutes when Codex hooks are installed. That means route can now fail with stage-specific diagnostics for "still drafted in tmux", "accepted but never consumed by Codex", or "consumed but no document cycle started" instead of collapsing those shapes into the same startup-miss timeout. In the same simplification pass, the no-op same-document busy-pane branch no longer injects into the still-busy pane and only later decides whether to restart; after one scoped fix, a still-healthy authoritative pane now gets one bounded fresh restart and final reroute. Added route regressions for the fresh-reroute path and updated the command spec. This advances `#runsm` from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Tracked Codex `/clear` reroutes now restart fresh before dispatch to preserve the original launch policy.** `codex_hook.rs` now exposes the latest tracked prompt for a document and flags an exact `/clear` as a capability-reset marker. Before `route.rs` reuses an otherwise healthy live Codex pane, it now checks that marker and forces one fresh supervisor restart before injecting the next `agent-doc <FILE>` reopen, so the original `codex_args`, writable roots, and network policy are reapplied instead of trusting post-clear resume inheritance. Added hook-level regression coverage for latest-prompt lookup plus a route regression that proves dispatch lands only in the fresh post-clear session. This closes `#clrpr` from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Codex busy-pane reroute now gets one fresh-session retry when command acceptance never clears after `/clear`.** `route.rs` already had a no-op same-document busy-pane recovery path for healthy supervisors, but it still fail-closed if the follow-up bare reopen stayed visibly drafted in the pane long enough for `send_command_checked` to time out. The busy same-document Codex branch now performs one bounded fresh supervisor restart, waits for the authoritative pane handoff/readiness, resends the reopen, and still requires the normal routed cycle ack before success. Added a regression that keeps the trigger visibly stuck in the old pane, forces the fresh restart handoff, and proves the routed reopen lands in the replacement pane. This closes the latest `/clear` reroute regression from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Direct `agent-doc run` now reuses the pending/backlog normalization gate from `write` and `repair`.** `run.rs` no longer rejects a valid template response just because it still contains a legacy `patch:backlog` block. The run path now normalizes backlog mutations before `replace:pending` enforcement and reuses the same real-response-body proof as the other write paths, so a normal `patch:exchange` + `patch:backlog` closeout no longer dies early on `replace:pending block forbidden`. Added a regression for the direct run template path and captured the remaining live validation as `tasks/agent-doc/plan-run-template-backlog-normalization-validation.md`.
- **Successful closeouts now repair transient live-file drift back to the committed blob instead of only cleaning the snapshot.** `git.rs` now reuses the same authoritative `HEAD` cleanup after a real git commit that it already used for `commit_already_current` no-op closeouts: if the working tree still differs from the just-committed document only by agent-owned closeout artifacts such as `(HEAD)` heading attribution or stale/fresh boundary churn, post-commit cleanup rewrites the live file back to committed `HEAD`, refreshes CRDT sidecars, and leaves the owning repo worktree clean. Added regression coverage for the real-commit path. This closes `#cs3turn` from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Codex idle-placeholder readiness is now structural instead of a three-string allowlist.** `harness.rs` now accepts the observed idle suggestion family by shape, including future variants like `› Explain this module in @filename`, as long as they still match the safe canned-placeholder form and target markers such as `@filename` or `my current changes`. This keeps routed Codex reopen triggers from fail-closing every time the composer suggestion text changes, while still rejecting real drafted user input and queue-only/busy panes. Added harness and route regression coverage and updated the routing spec. This closes `#cdxidle` from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Codex route readiness now recognizes the newer idle composer suggestion `› Improve documentation in @filename`.** Recent Codex panes can be fully idle while rendering that placeholder above the footer instead of a bare prompt glyph. `harness.rs` now treats it the same as the previously-known idle suggestions, so `route.rs` no longer misclassifies that pane as busy and fail-closes a valid reroute. Added harness regression coverage and updated the routing spec. This closes the latest live `src/session-share/tasks/claudescore-3.md` reroute miss recorded in `tasks/agent-doc/agent-doc-bugs2.md`.
- **Busy-pane supervisor restarts now wait for authoritative handoff before retrying route.** `route.rs` still allows a one-shot retry when a same-document pane is busy, the scoped fix made no changes, and the supervisor is only restartable, but it no longer re-probes the stale pane immediately after requesting that restart. Route now waits for the document's registered owner to move, and if a new pane takes over it waits for live-owner proof on that replacement before retrying dispatch. This keeps routed follow-ups from fail-closing against the old pane mid-shutdown or immediately re-restarting the fresh owner before its process-tree/file provenance settles. Added a regression that forces the old-pane-to-new-pane restart handoff and proves the routed trigger lands in the replacement pane.
- **Fresh routed auto-starts now follow same-session ownership handoffs instead of dispatching into the throwaway boot pane.** `route.rs` still creates and registers a fresh pane before launching `agent-doc start`, but after the ready wait it now re-reads the authoritative binding and, if startup reused an already-running pane for the same document session, dispatches the routed reopen into that recovered owner instead of the temporary new pane. This keeps route from surfacing a misleading busy/error path while the real Codex pane is already idle, and it avoids leaving the live follow-up tied to the wrong shell pane after a startup-time handoff. Added a regression that forces the registration to move to an existing owner during fresh boot and proves the trigger lands in the recovered pane.
- **No-pending repair now canonicalizes repeated prompt/response tails instead of only moving stale boundaries.** `repair.rs` now runs the full safe template normalization path even when there is no pending/captured response to replay, so a document that already shows a visible `### Re:` block next to a bare prompt target regains its required `❯ ` prefix during preflight/repair instead of fail-closing forever on the same typed-component-drift guard. Added a regression for the no-pending repeated-response shape that was still blocking routed reopen on `src/session-share/tasks/claudescore-2.md`, which closes the `#wcrp` repair gap in `tasks/agent-doc/agent-doc-bugs2.md`.
- **Parallel tmux full-suite routing/sync regressions now pin per-document registry roots instead of ambient `cwd`.** `route.rs` now looks up split-anchor panes from the target document's own `.agent-doc` project root, `sync.rs` writes synthetic tmux-router registries to an absolute path captured at creation time, and the cross-file split-anchor regression test now registers its anchor pane against an explicit base dir instead of ambient process state. This closes the remaining `#rtanch` full-suite flakes from `tasks/agent-doc/agent-doc-bugs2.md`, where parallel tests could make route miss an existing anchor pane or make tmux-router treat both cross-root panes as dead/missing simply because another test changed `cwd`. Added/strengthened full-suite regression coverage via the existing route and cross-root sync tests.
- **Manual `start` now clears dead stash registrations instead of fail-closing behind them forever.** `start.rs` still refuses to replace an alive pane when open startup-miss or session-log provenance says that pane may still own the document, but it now makes the stash-stranding case explicit: if the registered pane is alive only in a `stash` window, no live owner can be proven, the supervisor socket is gone, and both the startup-miss + session-log checks already show the old run is closed, `start` deregisters that stale stash binding and claims the current pane. Added stash-specific regression coverage and updated the start command spec. This closes the latest `agent-doc start ... still alive but no live owner was proven` repro from `tasks/agent-doc/agent-doc-bugs2.md`.
- **JetBrains `Run Agent Doc` now reaches tmux faster and stays visibly in-flight while route is working.** The plugin's explicit submit debounce dropped from 1500ms to 500ms, so a manual rerun is not held for an extra 1.5 seconds after the last keystroke before it even spawns `agent-doc route`. While the route subprocess is active, JetBrains now keeps an information notification open instead of relying only on a brief inline hint, then expires that notification and shows the usual success hint when route exits. This makes slow routed acks and tmux/session recovery windows visible from the IDE side while keeping final success lightweight. Bumped the JetBrains plugin build version to `0.2.82`.
- **Busy same-document reroutes with pending prompt drift now fail closed instead of reporting false success.** `route.rs` still focuses the authoritative pane and avoids force-restarting a healthy supervisor after a no-op scoped fix, but it no longer returns success for that shape. When the live Codex/Claude session is still busy and the document has unresolved prompt-bearing drift, route now emits a tmux display-message diagnostic and exits with the same busy-session error so JetBrains/CLI callers surface the blocked reroute instead of silently swallowing it. Added a regression that keeps the no-restart guarantee while requiring the fail-closed error path. This closes the latest `Run Agent Doc` false-success shape from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Full-suite verification is now explicitly fail-closed against "unrelated" or "flaky" waivers.** The bundled `agent-doc` skill and `SPEC.md` now state that a red project verification run must be treated as a real blocker even when the failing tests look outside the changed codepath. A turn must either fix the failing suite or report the concrete blocker and capture the follow-up in backlog before closeout. Added skill-bundling coverage so the installed Claude/Codex instructions keep that rule.

- **Direct local `cargo install` now resolves the sibling `agent-kit` crate without manual patch flags.** `src/agent-doc/Cargo.toml` now pins `agent-kit` with both `path = "../agent-kit"` and `version = "0.4.0"`, so `cargo install --path src/agent-doc --force` from the workspace root and `cargo install --path . --force` from `src/agent-doc` no longer fall back to the older crates.io copy that lacks `agent_kit::skill`. Added a regression test that locks this manifest contract.
- **`audit-docs` instruction discovery now prunes heavy skip dirs before descending.** `agent-kit::audit_common::find_instruction_files()` no longer uses raw recursive globbing for `src/**/...`, `.claude/**/...`, or `.agents/**/...` matches. It now walks those trees explicitly and stops descent as soon as a directory name matches `AuditConfig.skip_dirs`, so audit runs skip vendored/cache subtrees like `node_modules`, `.venv`, `target`, `.git`, `vendor`, `.next`, `dist`, and similar directories instead of traversing them first and filtering later. Added direct discovery coverage for skipped `src`, `.claude`, `.claude/skills/**/runbooks`, and `.agents` descendants.
- **Fresh routed auto-starts now rebind their own new pane immediately before the first guarded trigger dispatch.** `route.rs` still registers a fresh pane as soon as it is created so later route calls can discover it, but the first trigger send now re-checks that binding after the harness reaches its ready prompt and restores it when startup recovery cleared the temporary geometry-only entry during boot. The self-heal still fails closed if the pane was rebound to another document or a different pane already owns the same session, so cross-file dispatch protections stay intact. Added a route regression that clears the fresh-pane registration during the ready wait and proves the first `Run Agent Doc` attempt still succeeds instead of failing with `route dispatch target ... is not registered`.
- **No-op `commit_already_current` closeouts now refresh CRDT/editor sidecars when they rewrite live drift back to `HEAD`.** `git.rs` still closes transient-only `(HEAD)` / boundary churn as an already-committed no-op, but the cleanup path now also refreshes CRDT state from the committed document and emits the same editor/VCS refresh signal the plugin watches. The normal post-commit cleanup path also refreshes CRDT state after stripping guard-marker drift. This closes the `#6btt` parity gap from `src/session-share/tasks/claudescore-2.md`, where a no-op closeout could repair only the snapshot/on-disk file while stale CRDT or editor-visible state kept showing bare `compact exchange`, `(HEAD)` heading churn, or a newer boundary marker. Added regression coverage for the no-op CRDT + refresh-signal path.
- **Plain exchange-tail follow-ups now count as routed prompt work even without `?` or an imperative lead verb.** `diff.rs` now treats a non-artifact user block appended immediately before `<!-- /agent:exchange -->` as a `prompt_target`, so `session_check.rs` and `route.rs` no longer drop editor-added follow-ups like "When I run `Run Agent Doc` on this document...nothing happens..." just because they are plain prose below a stale boundary. This closes the latest `agent-doc-bugs2.md` JetBrains reroute shape where route focused the live pane as "already running" and injected nothing because it failed to see any pending prompt-bearing drift. Added direct classifier, session-check, and route regressions.
- **Codex reroutes now fail closed before dispatch if the reopen payload stops being the bare `agent-doc <FILE>` command.** `route.rs` now validates the final Codex `send-keys` payload right before injection and refuses any multiline or otherwise mutated payload instead of letting extra prompt/content text drift back into the composer and surface later as a misleading 30-second `no new document cycle started` startup-miss. Added direct guard coverage plus a live-child regression that keeps the `content_edit` reroute path on the same bare reopen contract. This hardens the `sampleorders.md` / `claudescore-3.md` failures recorded in `tasks/agent-doc/agent-doc-bugs2.md`.
- **`repair`/`preflight` now deterministically move stale boundaries past already-answered turns.** When a template document has no pending capture to replay but still shows a stale `agent:boundary` marker above a prompt/response pair that is already complete, `repair.rs` now treats that as safe template drift instead of just tolerating it. The repair path repositions the existing boundary marker to the true end of the completed turn, syncs the snapshot through the normal binary-owned path, and lets `preflight` commit the cleanup on the next cycle. Unanswered prompts below the boundary are still left in place and remain actionable. Added direct repair regressions for both the answered-turn repair and the unanswered-prompt no-op case. This closes the remaining deterministic-repair half of `#bdryc` from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Route now recognizes Codex's idle composer suggestion lines as dispatch-ready.** Recent Codex builds can render the empty composer as canned prompt text such as `› Run /review on my current changes` or `› Find and fix a bug in @filename` above the footer. `harness.rs` now treats those observed placeholder lines as idle chrome instead of drafted user input, so `route.rs` no longer times out on an otherwise ready pane just because the Codex UI is showing a suggestion. Real drafted text like `> agent-doc ...` or arbitrary freeform input is still rejected. Added harness and route regression coverage and updated the routing spec. This closes the latest `Run Agent Doc` failures from `tasks/agent-doc/agent-doc-bugs2.md`.
- **JetBrains now exposes a first-class `Fix Document` action for tracked markdown sessions.** The plugin adds `Fix Document` to the popup, Tools menu, editor context menu, and project view context menu, and it runs `agent-doc fix <FILE>` from the document's resolved agent-doc project root after saving buffers. This gives JB users an editor-native recovery path for the same deterministic repair flow the CLI already provides when `Run Agent Doc` surfaces a recoverable session/layout issue. Bumped the JetBrains plugin build version to `0.2.81`.
- **Busy live-pane reroutes now auto-apply the scoped fix path once before the final fail-closed error.** `route.rs` no longer surfaces the raw "not showing an idle prompt" failure on the first pass for a live same-document pane with unresolved prompt-bearing drift. Route now runs the same document-scoped repair path as `agent-doc fix <FILE>`, re-resolves the authoritative pane, and retries dispatch one time before failing closed. The follow-up behavior is now stricter: a no-op scoped fix no longer restarts an otherwise healthy same-document Codex supervisor into `resume --last`, because that could just resurrect the prior unrelated task and keep `Run Agent Doc` trapped in the same busy-pane loop. Healthy authoritative panes are still focused for visibility after the no-op fix, but the route now fails closed instead of reporting success while drift remains undispatched; only genuinely restartable supervisors remain eligible for the one-shot restart-and-retry path. Added regression coverage for both the healthy no-op-focus fail-closed path and the bounded fail-closed / retry cases, and updated the command spec. This closes the latest JetBrains `Run Agent Doc` repro from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Answered tails below a stale boundary no longer masquerade as new pending work.** `session_check.rs` now suppresses the oldest prompt-bearing change when the current exchange tail already contains that prompt below the last `agent:boundary` marker and a real `### Re:` / `## Assistant` block later in the same tail proves the turn was answered. `route.rs` picks up the same shared detector through its routed-cycle-ack gating, so reruns no longer wait 30 seconds for `pending prompt_target: ...` when the document already shows the completed response and only the closeout boundary/commit repair remains. Added direct regressions in both files and updated the command spec. This closes the `#bdryc` shape from `tasks/agent-doc/agent-doc-bugs2.md`.
- **Busy live-pane reroutes now only fail closed when there is real document drift to dispatch.** `route.rs` still refuses to inject `agent-doc <FILE>` into a non-idle Codex/Claude pane when the document has unresolved prompt-bearing changes, but a proven live pane with no pending prompt/content drift now counts as "already running": route focuses that pane and returns success instead of erroring out of the editor-trigger path. This closes the `tasks/software/corky.md` JetBrains repro from `tasks/agent-doc/agent-doc-bugs2.md`, where an already-active session blocked `agent-doc route` even though there was nothing new to send. Added a busy-pane regression that preserves the fail-closed behavior for real drift and updated the routing spec.
- **Routed Codex reopen now requires an empty composer and wrapped-trigger visibility.** `route.rs` no longer treats `› some drafted text` / `> some drafted text` as an idle dispatch target, so a live Codex pane must expose a truly empty composer before route injects `agent-doc <FILE>`. The send verification loop also now recognizes a wrapped absolute-path reopen line as still-pending input instead of declaring the command "accepted" just because the path split across multiple physical tmux lines. This closes the JB `claudescore-3.md` startup-miss shape from `tasks/agent-doc/agent-doc-bugs2.md`, where a routed reopen could be drafted into a live Codex composer, logged as accepted, and then fail closed 30 seconds later with no new document cycle. Added harness/route regression coverage for drafted Codex prompts and wrapped routed triggers, and updated the routing spec.
- **Route/session-check prompt-bearing drift detection now ignores frontmatter-only metadata edits.** `session_check.rs` now strips YAML frontmatter before classifying unresolved prompt-bearing changes, so harmless metadata churn such as `agent: codex` no longer surfaces as `content_edit` and forces `route` to wait 30 seconds for a cycle that never needed to start. `route.rs` picks up the same body-only behavior through its shared pending-change lookup, which closes the JB `claudescore-3.md` failure from `tasks/agent-doc/agent-doc-bugs2.md` where a routed Codex reopen could fail closed on `pending content_edit: agent: codex` despite there being no new user prompt in the document body. Added direct regression coverage in both `session_check.rs` and `route.rs`, and updated the backend/routing specs.
- **`replace:icebox` now parses as a real template patch instead of falling through to exchange.** `template.rs` now accepts `<!-- replace:icebox -->...<!-- /replace:icebox -->` alongside the existing `patch:icebox` form, so skill closeouts can rewrite `agent:icebox` through the binary-owned patch path without tripping the `0 template patches found` warning or dumping the list body into `exchange` as unmatched content. Updated the skill/runbook text and added parser + write regression coverage for the `#iceboxpatch` shape from `tasks/agent-doc/agent-doc-bugs5.md`.
- **Template exchange patchback now binds new responses to the oldest compatible unresolved prompt instead of blindly appending at the tail.** `template.rs` now inspects the prompt tail that lived below the previous boundary marker, matches pending ids referenced by the new `patch:exchange` response, and inserts the response immediately after the oldest matching unresolved prompt block. If the response would skip an older unresolved prompt in that tail, the write fails closed instead of silently reversing prompt/response chronology. This closes the `#pbord` shape from `src/sample-app/tasks/sampleorders.md`, where a newer `#wcx1` status reply could land ahead of an older unresolved `#wcup1` prompt and a later closeout would attach to the wrong turn. Added regression coverage for both anchored insertion and the skip-older fail-closed path.
- **Codex reroutes now keep the trigger payload to a bare `agent-doc <FILE>` reopen.** `route.rs` no longer appends the first unresolved prompt-bearing change onto routed Codex dispatches for closed-cycle retries. Live JB/plugin failures showed that the multiline payload could be consumed as ordinary Codex chat text, producing a conversational answer in-pane without ever starting the binary-owned document cycle, so route would correctly fail closed on the missing cycle ack. The route path now reopens only the document and relies on the session diff as the source of truth for pending work. Added regression coverage that rejects extra follow-up lines in the routed Codex payload, and updated the routing spec.
- **Preflight now fails closed on hidden uncommitted closeout drift instead of silently reporting `no_changes`.** `preflight.rs` now checks for out-of-band closeout state after repair/init but before pending maintenance or the generic commit path: a visible bypassed `### Re:` patchback or a snapshot that still differs from `HEAD` with no open/recoverable cycle now aborts preflight immediately. `session_check.rs` also names tracked side-effect files and prints the exact `agent-doc write --commit <FILE>` follow-through command in those failures. This closes the `#codcommit` shape from `src/session-share/tasks/claudescore.md`, where a Codex-side direct patchback plus `news/README.md` edits could leave the document looking answered while the binary-owned commit boundary never landed. Added regression coverage for both the hidden snapshot-ahead/no-diff preflight case and the side-effect-rich session-check diagnostic.
- **Template writes now fail closed when `patch:todo` would drop checklist items from an existing todo component.** `write.rs` counts Markdown checklist rows in the live `agent:todo` body and rejects any replacement patch whose new body contains fewer checklist items than the current component. This closes the `#ptdr` shape from `src/session-share/tasks/claudescore.md`, where a partial Phase 1 todo patch silently deleted the rest of the backlog sections because `agent:todo` still used full-replace semantics. Added regression coverage for destructive-subset rejection and same-size rewrites.
- **Later agent turns now carry forward standing document-level formatting requirements from earlier user prompts.** `prompt_contract.rs` now scans historical `❯ ...` prompt blocks for explicit structure directives such as "organize the backlog into a 2-level list" and surfaces them back into the run/stream/orchestrate agent prompts as active requirements. The prompt text also tells the responder to say so explicitly when its output contract prevents an exact match instead of silently flattening the structure. This closes the `#lvls` shape from `src/sample-app/tasks/sampleorders.md`, where follow-up bug-handling and transfer work could ignore an earlier backlog-organization requirement simply because it was no longer part of the latest diff. Added regression coverage in `prompt_contract.rs`, `run.rs`, `stream.rs`, and `orchestrate.rs`.
- **Normalization-divergence IPC fallback now preserves tracked backlog mutations.** When ack-content sidecar verification rejects the plugin snapshot because a required `❯ ` prompt prefix is missing, `write.rs` no longer saves raw `content_ours` by itself. Both IPC success paths now splice the current on-disk backlog/pending component back into that fallback snapshot first, so `finalize --stream --pending-add` cannot silently drop earlier pending mutations just because editor-side normalization diverged. Added regression coverage for the `#splpend` shape from `tasks/claudescore-3.md`.
- **Strict retry dedup now adopts already-present template responses before the no-edit fast path.** `write.rs` now checks for an already-visible response block before taking the `content_current == base` shortcut across the template closeout/replay paths, so a `finalize` / `write --commit` retry cannot append the same `### Re:` block a second time just because the current file already matches the retry baseline. The adopted-current path still re-runs exchange prompt-prefix normalization, which closes the `#duppb` shape from `tasks/agent-doc/agent-doc-bugs2.md` where a committed closeout-follow-up response could be replayed again with a visible `(HEAD)` copy. Added regression coverage for the same-base template retry shape.
- **Fail-closed sync recovery no longer rebinds an unrelated pane by column geometry alone.** When `sync.rs` skips auto-start for a document because a startup-miss marker or repeated recent `missing_pane` recovery window is still active, post-sync registration now refuses to mirror tmux-router's file→pane assignment unless that pane actually proves live ownership for the document. If a stale binding for that same pane was already present in the document's nearest `sessions.json`, sync prunes it instead of immediately writing the geometry-only assignment back. This closes the live `src/session-share/tasks/claudescore-3.md` shape from `tasks/agent-doc/agent-doc-bugs2.md`, where `%261` could keep being rebound to `claudescore-3.md` even after fail-closed recovery had intentionally refused to auto-start a fresh owner. Added regression coverage for the fail-closed geometry-only rebind path.
- **Synthetic tmux-router sync registry now drops ambiguous same-root duplicate pane claims before layout reconcile.** `sync.rs` now filters the per-run session-id registry it builds for tmux-router so one stale pane cannot stand in for both `src/session-share/tasks/claudescore.md` and `src/session-share/tasks/claudescore-3.md` during the same sync pass. A duplicate pane is kept only when exactly one claimant still proves live ownership (or, failing that, exactly one claimant uniquely matches the pane's project root); otherwise the duplicate pane is removed from the synthetic registry entirely so tmux-router must rehydrate a distinct pane instead of aliasing two visible documents onto one live pane. Added regression coverage for the ambiguous same-root child-repo shape and the unique-live-owner keep path.
- **Post-sync registration now fails closed when tmux-router aliases one pane onto multiple cross-root documents.** `sync.rs` now rejects duplicate file→pane assignments unless exactly one claimant matches the pane's own project root or already proves live ownership, and it prunes the losing stale registry binding instead of preserving a second cross-root alias. This closes the `agentic-harness-engineering.md` shape from `tasks/agent-doc/agent-doc-bugs2.md`, where `src/session-share/.agent-doc/sessions.json` could keep pointing at the root workspace pane `%151`, leaving the child document unable to start or sync because both registries claimed the same live pane. Added regression coverage for duplicate cross-root post-sync registration.

- **Committed cycle-state is now monotonic across later repair bookkeeping.** `cycle_state.rs` now refuses to downgrade an already-committed cycle back to `response_captured` or `write_applied` when a later repair/replay path touches the same cycle. `run.rs` now also opens a fresh `preflight_started` cycle after its pre-commit boundary so the current response closeout does not inherit that older committed state. This closes the `#stphk` shape from `tasks/agent-doc/agent-doc-bugs2.md`, where a post-commit `repair_applied` event could leave the cycle-state file open even though the capture ledger and session log already proved `commit_success`, causing the Codex Stop hook to loop on a fake unfinished turn. Added direct cycle-state regression coverage plus a repair replay test for the committed-then-replayed shape, and updated the command spec.

- **Post-commit boundary cleanup now repairs missing `❯ ` prompt prefixes in the working tree.** When IPC-side normalization verification falls back to `content_ours`, `git.rs` now compares the clean snapshot against the live document, restores any missing exchange user-region prefixes, and then repositions the boundary so the working tree catches up with the committed blob. `write.rs` also upgrades the live-listener path to send a zero-content IPC patch carrying `normalize_prefix_lines` plus `reposition_boundary` when that repair is needed, instead of sending a bare reposition signal that could only move the boundary. Added regression coverage for both the target extraction helper and the no-listener post-commit repair path.

- **Fresh-route cycle ack now survives initial supervisor restarts instead of failing 15s too early.** `route.rs` now gives fresh auto-starts the same longer start-ack budget as routed live-child dispatches (30s in production, 2s under tests), so the first `Run Agent Doc` attempt no longer fails closed when the initial pane immediately recycles through `sync_missing_pane`/startup recovery before the first `preflight_started` becomes visible. Added route-level regression coverage for a delayed fresh-start ack and logged the timeout budget on fresh-start ack success/miss paths.

- **Cross-root sync now feeds tmux-router a per-run session-id registry instead of the caller's root registry.** `sync.rs` now synthesizes a temporary tmux-router registry from each visible document's own nearest `.agent-doc/sessions.json` before reconcile. This closes the `agent-doc-bugs2.md` / `claudescore-3.md` regression where focus changes could invert left/right tmux ownership simply because tmux-router fell back to "spare pane" assignment after looking up both session ids in the wrong registry. Added isolated tmux coverage for the cross-root focus-stability repro and updated the sync command spec.

- **`start` now reuses alive session-log owners before fail-closing stale live-pane recovery.** `start.rs` now consults the owning session log's latest still-open pane as an extra provenance source whenever `sessions.json` points at an alive pane but current live-owner proof is missing. If that latest-open pane is still alive, `start` focuses and reuses it instead of falling straight into the "supervisor unavailable, no live owner proven" fail-closed path. This closes the `#asfc` repro from `tasks/agent-doc/agent-doc-bugs2.md`, where manual/editor-driven recovery could strand a healthy document behind stale registry state even though the session log still identified the last open pane. Added regression coverage for the session-log-owner reuse path and updated the start command spec.

- **Sync now reserves pane ownership per run so one live pane cannot satisfy two visible documents at once.** `sync.rs` now tracks pane ids already claimed earlier in the same reconciliation pass, treats later duplicate claimants as unresolved, and excludes those reserved panes from associated-pane recovery. This closes the `agent-doc-bugs2.md` mixed-root layout collapse where `tasks/agent-doc/agent-doc-bugs2.md` and `src/session-share/tasks/claudescore-3.md` could both believe `%75` was their live owner, causing `agent-doc sync` to collapse to a one-pane fast path instead of rehydrating the second column. Added regression coverage for same-run pane reservation conflicts and reserved associated-pane filtering.

- **Automatic prune now reaps stray retained-dead panes outside stash when another pane still owns the window.** `resync.rs` now kills unregistered `remain-on-exit` panes in non-stash windows during both automatic `prune()` and explicit `resync --fix` cleanup, but still preserves the last pane in a window for manual inspection. This closes the latent `Pane is dead` clutter reported from `tasks/agent-doc/agent-doc-bugs2.md`, where dead replacement remnants could survive indefinitely once the registry forgot them. Added regression coverage for the sibling-pane cleanup and last-pane safety guard, and updated the resync command spec.

- **JetBrains split-layout detection now follows screen position instead of focus-sensitive window order.** `LayoutDetector` now groups visible editor windows by their actual x/y bounds instead of assuming `FileEditorManagerEx.windows` is left-to-right stable. This closes the `agent-doc-bugs2.md` repro where selecting the right editor split inverted tmux pane placement, while still preserving empty columns when one split shows a non-markdown tab. Added unit coverage for reversed input order, vertical stacking, and empty-column preservation, and bumped the JetBrains plugin build version for local installs.

- **Recovered live-owner re-registration now preserves supervisor identity instead of downgrading back to transient CLI metadata.** `sync.rs`, `start.rs`, and `resync.rs` now re-register recovered panes through the owning tmux handle and restore authoritative `supervisor_pid + supervisor_instance_id` when that evidence is still available from the registry or supervisor socket. This closes the regression where a valid recovered pane was rewritten with the short-lived `route` / `sync` process PID and an empty instance id, causing the next provenance check to fall back to brittle heuristics and churn pane layout again. Added regression coverage for recovered associated panes, same-pane identity preservation without a live socket, cross-root sync registration, and the stale-live-owner route path.

- **Mixed-root editor sync now consults each document's own registry before rescuing or rebinding panes.** `sync.rs` no longer assumes the caller's current project root is authoritative for every visible markdown file. File resolution, stash rescue, associated-pane marking, path-provenance lookup, and post-sync `sessions.json` updates now all canonicalize the document path, resolve that document's nearest `.agent-doc` root, and read/write the registry there. This closes the cross-repo layout bug where `tasks/agent-doc/agent-doc-bugs2.md` could borrow `src/session-share/tasks/claudescore-3.md`'s live pane, leave a retained dead pane in the opposite slot, and accumulate duplicate path-keyed entries such as `src/session-share/src/session-share/...` in the child registry. Added regression coverage for cross-root registry resolution and per-root sync registration.

- **`sessions.json` is now path-keyed and live-owner proof is top-down by default.** `sessions.rs` now normalizes the registry around canonical absolute document-path keys, keeps `session_id` in the value, and records a `supervisor_instance_id` alongside the supervisor PID. `start.rs` stamps that supervisor identity into the registry and exposes it over IPC, while `sync.rs` now treats `pane + supervisor PID + supervisor instance id` as the primary ownership proof before falling back to tmux argv/process-tree heuristics. Added coverage across GC, route, startup-miss/session-check, and registry normalization paths, and updated the routing/supervisor command specs.

- **Active-session post-closeout drift now fails closed in `session-check` / Codex Stop recovery.** `session_check.rs` now refuses to report a committed cycle as clean when the current Codex session still owns that file and the live document changed again after the last committed closeout without reopening the binary-owned write/commit path. Instead of silently classifying that state as harmless post-commit drift, `session-check` interrupts so the Stop hook can recover from `last_assistant_message` or block the turn. Added regression coverage for both the direct `session-check` guard and the Stop-hook auto-close path, and updated the command spec.

- **Open session-log provenance now blocks halted-supervisor rebinds in `start`.** `start.rs` no longer treats `state="halted"` as sufficient authority to replace an alive registered pane when the session log still shows that same pane as the latest open run with no later child exit or `session_end`. In that stranded-owner shape, manual/editor-driven `start` now fails closed instead of emitting another `session_superseded ... origin=registry_rebind` pane era on top of unresolved in-flight work. Added regression coverage for the open-vs-closed session-log guard and updated the command spec.

- **Synthesized unmatched exchange patches now preserve visible `❯ ` prompt prefixes in JetBrains.** `write.rs` now applies `normalize_prefix_lines` when IPC has to synthesize an append-mode `exchange` patch from raw unmatched content, not just for explicit patch blocks. That closes the remaining JB-plugin shape where a prompt-bearing line such as `do #expatch. spec-test-build-install-commit-push` could still be saved visibly bare in the editor during uncommitted `(HEAD)` response state even though the Rust snapshot path already knew it should be prefixed. Added regression coverage for the synthesized-unmatched `#expatch` shape.

- **`agent-doc patch` now replaces component bodies by default, even on append-mode exchange docs.** The standalone `patch` subcommand no longer inherits a component's configured `patch=append` / `patch=prepend` mode as an implicit behavior change. Bare `agent-doc patch <FILE> exchange ...` now replaces the paired-marker body as the command synopsis promises, which fixes the `#expatch` repair path where exchange restores duplicated history instead of overwriting it. Intentional cumulative edits still exist behind the explicit `--mode append|prepend` escape hatch. Added CLI/unit regression coverage and updated the command spec.

- **Alive stale-owner panes no longer get silently replaced when `start` loses ownership proof.** `start.rs` now fails closed when a registered pane is still alive, no live owner can be proven for the document, and the supervisor socket is unavailable, instead of deregistering that pane and rebinding a fresh one. When `start` does intentionally replace an alive pane after an explicit halted/restart-failed determination, it now preserves the old registry entry until the new pane registers so the normal `session_superseded` / `session_end origin=registry_rebind` provenance is appended to the session log. Added regression coverage for the new fail-closed supervisor-health decision and updated the command spec.

- **Editor visual-token ranges now stay aligned after multibyte text.** `agent_doc_visual_tokens_json` now converts the shared scanner's internal UTF-8 byte ranges into UTF-16 document offsets before returning them to JetBrains and VS Code. This fixes the JB-plugin drift where highlights walked forward after emoji, smart punctuation, or other multibyte characters earlier in the document. Added FFI regression coverage and documented the editor-facing range contract.

- **Scratch-comment bodies now stay highlighted as comments across both editors.** The shared visual-token scanner now emits dedicated body ranges for ordinary HTML scratch comments (`<!-- ... -->`), not just the delimiter lines. JetBrains and VS Code consume that extra token so multiline scratch comments no longer fall back to raw Markdown parsing inside the comment body, which fixes the remaining JB-plugin "syntax error" rendering around commented examples and screenshot/image notes near the exchange closeout.

- **Editor overlays now mute agent-managed markdown bodies and normalize standalone bracket labels.** The shared visual-token scanner now emits agent-component body ranges plus standalone label tags such as `[recommended]`, excluding fenced/inline code, images, and checklist markers. JetBrains and VS Code both consume those new tokens so agent-managed blocks render with a muted background tint and bracket labels stop inheriting broken-link Markdown styling. This specifically cleans up the JB-plugin rendering issues where `agent:exchange`/backlog content stayed visually flat and tag-like labels looked like malformed references.

- **JetBrains plugin version bumped to `0.2.79` for the latest local-testing build.** Updated `editors/jetbrains/gradle.properties` so the next `buildPlugin` artifact and any local install/use of the bundled JB plugin carry a new patch version after the recent closeout-fix work.

- **JetBrains preserve-head cleanup now prefers committed answered prompt prefixes over stale editor buffers.** The JetBrains plugin's post-commit reposition comparator now treats already-answered `❯ ` prompt-prefix differences as the same committed content when the next meaningful exchange line is the matching `### Re:` block. That means the `preserve_head` boundary cleanup path will reuse the committed disk transcript instead of re-saving a stale unsaved editor buffer that only differs by boundary churn, model-attribution churn, or stripped historical prompt prefixes. Added regressions for the `#qprx` shape and for the unresolved-follow-up safety case where disk preference must still stay off.

- **Early Ctrl-D prompt EOFs no longer close freshly started Codex panes.** `start.rs` now treats a prompt-time stdin EOF as `restart fresh` instead of `quit` when Codex clean-exits immediately after a fresh pane start and the `Ctrl-D`/EOF prompt fires inside the early-start grace window. That closes the `sampleorders.md` rebind-churn shape where a transient tmux stash/rescue input race could look like `user_quit_after_ctrl_d`, close the claimed pane, and trigger `%546 -> %550 -> %552` replacement churn. Added start-level regression coverage and updated the start/supervisor specs.

- **Open preflight cycles with visible manual patchbacks now fail with an explicit follow-through message.** `session_check.rs` no longer reports a generic `preflight_started` interruption when the working tree already contains a fresh `### Re:` block that `HEAD` still does not prove. That shape now surfaces as a manual-repair / commit-boundary interruption with a concrete `agent-doc write --commit <FILE>` follow-through hint, so repaired-but-uncommitted session docs are easier to diagnose and cannot be mistaken for an ordinary stale preflight. Added regression coverage for the open-cycle manual-patchback path and updated the command spec.

- **Strict replay closeout now re-normalizes merged prompt prefixes and adopts already-present responses instead of duplicating them.** `write.rs` now re-runs exchange prompt-prefix normalization on the final merged template/CRDT document, not just on `content_ours`, so a concurrent bare `do #...` line cannot survive the merge and trip post-commit `session-check` after `finalize` already committed the response. When a manual `write --commit` / replay retry sees that the same response body is already present in the live document, the write path now adopts the current transcript and canonicalizes it instead of CRDT-merging the response a second time. `repair.rs` exposes the same normalized visible-response matcher for both recovery and write-time replay checks. Added regression coverage for the merged-prefix repair path and preserved the existing duplicate-replay tests.

- **Editor-return rebinds now preserve the canonical owner instead of churning fresh pane eras.** `start.rs` now treats a proved live owner as authoritative even if supervisor IPC is stale, and it fails closed when an alive registered pane still owns the active startup-miss marker instead of rebinding the document onto a fresh pane. `sync.rs` now clears startup-miss markers already superseded by a newer registered owner before auto-start decisions, and it skips auto-start entirely when the unresolved marker still belongs to an alive pane. This closes the `#rbret` shape where returning to an already-running document could cascade `%529 -> %533 -> %536 -> %540` registry rebinds and look like a tmux-pane crash even though the session log showed only `session_superseded` / `session_end origin=registry_rebind` provenance. Added regression coverage for superseded-marker clearing plus the new start/sync guards, and updated the command spec.

- **Session-log closeout parsing now honors metadata-bearing `session_end` events.** `startup_miss.rs` no longer treats only a bare literal `session_end` line as proof that the latest pane era closed. Session-log analysis now closes the latest run/session whenever the event token is `session_end`, even if recovery metadata follows (for example `session_end origin=registry_rebind ...` or `session_end origin=sync_missing_pane`). That keeps rebind and missing-pane recovery provenance from being misclassified as a still-open/crashed session in the remaining `#tmuxcrash` forensics path. Added regression coverage for metadata-bearing `session_end` parsing and updated the session-log spec.

- **Session logs now record document closeout phase transitions alongside harness/pane provenance.** `cycle_state.rs` now appends `document_cycle phase=... cycle=... event=...` entries to the owning `.agent-doc/logs/<session>.log` whenever a session document crosses `preflight_started`, `response_captured`, `write_applied`, or `committed`. That puts the document closeout boundary in the same timeline as `*_start`, `*_exit`, `supervisor_exit`, and dead-pane diagnostics, so `#tmuxcrash` forensics can distinguish true child death from an interrupted-but-already-committed closeout without reconstructing the boundary from separate state files. Added cycle-state regression coverage and updated the supervisor/session-log spec.

- **Stashed panes now keep dead-pane retention after `join-pane` moves.** Fresh panes provisioned for agent-doc sessions now enable tmux pane-local `remain-on-exit` instead of setting the option on the original window. That means a Claude/Codex pane moved into a stash window still retains `pane_dead_status` and visible tail output if the harness exits while stashed, closing the `#stshroe` path where sync had to auto-start a replacement because the old pane vanished before provenance could be captured. Updated the auto-start command spec and added a tmux-router regression that exits a pane only after it has been stashed.

- **Supervisor session logs now preserve child-exit provenance and shutdown reasons.** `start.rs` no longer flattens every harness exit into a bare `*_exit code=<n>` line. The session log now records `exit_kind`, signal name when applicable, and the rendered exit status text on both `*_exit` and `restart_eval`, and the supervisor now appends `supervisor_exit reason=...` immediately before the final `session_end`. This keeps true `#tmuxcrash` forensics distinguishable from ordinary clean exits or app-level nonzero exits without changing the existing startup-miss / recovery state machine. Added start-level regression coverage for signal and nonzero exit rendering, and updated the supervisor logging spec.

- **Cross-root stash pruning now preserves sibling-repo panes that still have live project-local ownership or supervisors.** `resync.rs` no longer decides stash-pane orphanhood only from the caller's current project root. Before killing an unregistered stash pane, prune now inspects the pane's own nearest project root, checks that root's `.agent-doc/sessions.json`, and consults that root's live supervisor sockets. This closes the sync churn where `src/session-share` panes were stashed out of the shared `agent-doc` tmux window, then incorrectly killed as "unregistered" by a root-workspace prune pass, which forced repeated `sync_missing_pane` auto-start loops for documents like `docs.md`, `claudescore.md`, and `claudescore-3.md`. Added regression coverage for the cross-root live-supervisor stash case and updated the command spec.

- **Repeated missing-pane recovery now fails closed before route/sync spawn more replacements.** `startup_miss.rs` now summarizes recent `supervisor_exit code=missing_pane` events from the session log, keyed by document session, and both `route.rs` and `sync.rs` consult that shared window before any blind auto-start. Once the same document records two unexpected pane-loss recoveries inside ten minutes, routed retries and editor-driven sync stop auto-provisioning fresh panes and surface a stable manual-recovery diagnostic instead of cascading more tmux churn over a repeated crash window. Added regression coverage for the shared detector plus the route/sync guard paths, and updated the command spec.

- **Session rebinds now close the prior pane era in the session log before switching panes.** `sessions.rs` now treats a same-UUID re-registration onto a different pane as a provenance boundary: before `sessions.json` overwrites the binding, it best-effort appends `session_superseded old_pane=... new_pane=...` and `session_end origin=registry_rebind ...` to the existing session log. That keeps crash/recovery forensics from showing an old pane as forever-open when `route`, `sync`, or `start` moved the document to a replacement pane. Added registry coverage for the rebind logging path and updated the command spec.

- **Halted supervisors now fail closed in route and get replaced fresh in manual start.** `start.rs` and `route.rs` no longer collapse supervisor state `halted` into the generic "restartable" bucket. Explicit `agent-doc start <FILE>` now treats a halted reused session as a crashed stale binding, deregisters it, and starts fresh instead of reviving the same halted loop in place. `route.rs` now refuses to auto-restart or auto-replace a registered pane whose supervisor already halted after repeated crashes, surfacing the pane id and restart count instead of cascading more automatic tmux churn over the same crash loop. Added regression coverage for the halted-health classifier, stale-start decision, and route fail-closed path.

- **Route no longer mistakes its own control plane for a live document owner.** `sync.rs` now narrows process-tree ownership proof so `agent-doc route <FILE>` / `claim <FILE>` utility invocations do not count as associated document panes; only the long-lived `agent-doc start <FILE>` supervisor path (plus harness-owned matches) can satisfy that proof. This closes a false duplicate-owner ambiguity found during a live tmux-backed Codex repro, where the control plane running `route` was reported alongside the real registered pane for the same document. Added regression coverage for owner-command classification.

- **Retained dead panes now preserve stashed-session crash provenance before replacement.** Fresh panes provisioned by `route.rs` now enable tmux pane-local `remain-on-exit`, and `tmux-router` now treats retained dead panes as dead rather than alive so route/sync do not accidentally reuse them. When `sync.rs` replaces a registered pane that has died, it now captures tmux's retained `pane_dead_status`, saves the last 80 lines of pane output under `.agent-doc/logs/dead-panes/`, records the open cycle phase plus capture path in the session log, and only then records the synthetic `supervisor_exit` / stale-preflight repair before replacement. `resync.rs` now also purges orphaned retained-dead stash panes once they are unregistered, so the new diagnostic preservation does not leak dead stash clutter forever. Added regression coverage in both `tmux-router` and `agent-doc` for retained-dead liveness, dead-pane provenance capture, and dead-stash purge cleanup.

- **Crash-recovery snapshot repair now heals committed answered-prompt prefix drift.** Historical snapshot self-heal no longer requires a new `### Re:` insertion when the only committed exchange difference is prompt-prefix normalization on an already-answered prompt (for example, stale `❯ do ...` vs committed bare `do ...` directly above the same response block). `commit` / `session-check` now compare snapshots with the same exchange-only normalization, repair the stale snapshot from committed `HEAD`, and stop misclassifying that drift as fresh unresolved prompt-bearing user work after crash recovery. Added regression coverage for the committed prefix-normalization path.

- **Nested backlog edits now replace stale child continuations and reassign duplicate child ids.** `pending.rs` now parses multiline `--pending-edit` payloads as a parent line plus continuation block, so editing a backlog item with a refreshed child sublist replaces the old nested content instead of appending the new lines on top of stale children. During nested-child canonicalization, existing duplicate child ids are now reassigned to fresh parent-prefixed ids, which lets damaged backlog sublists self-heal instead of preserving collisions forever. Added regression coverage in both `pending.rs` and `pending_cmd.rs`, and updated the pending command spec to document the stricter multiline-edit contract.

- **Sync now recovers supervisor-backed claimed panes before spawning a replacement.** `sync.rs` no longer relies only on argv/file-path matches when a managed document appears to have lost its pane. Before auto-starting, it now runs the shared associated-pane proof (`find_associated_panes`) so a still-alive supervisor-owned pane can be re-registered via supervisor child-PID fallback even after the foreground process tree stops mentioning the file. When that recovered pane is stashed, sync rescues it back into the `agent-doc` window; when multiple associated panes still remain, sync fails closed for that file instead of auto-starting another duplicate session. Added regression coverage for supervisor-backed associated-pane recovery and updated the sync command spec.

- **Startup-miss markers are now cleared when a newer registered pane has already taken over.** `startup_miss.rs` now detects the stale-marker shape where the persisted miss still points at an older pane, but `sessions.json` and the session log already prove a newer open start on a different registered pane for the same document. `route.rs` clears that stale marker before reuse/restart decisions, and `session_check.rs` now heals the same stale state instead of warning about a fake current crash. Added regression coverage for the cross-pane supersession path plus the post-commit session-check cleanup.

- **Nested backlog subtasks now get parent-prefixed ids and checkboxes automatically.** `pending.rs` backfill no longer leaves indented child bullets as anonymous prose when they look like subtask list items: it now canonicalizes them with checkboxes plus nested ids shaped like `[#parentid-abcd]`, using the owning flush-left parent item's id as the visible prefix. `pending_cmd.rs` now re-runs that canonicalization after granular edits/adds/state transitions so `--pending-edit` can add a sublist and get stable nested ids in the same cycle instead of waiting for a later preflight. Custom pending ids now accept hyphens to support the parent-prefixed child-id shape. Updated the pending spec/runbook text and added regression coverage for nested child-id backfill plus hyphenated id parsing.

- **Startup-miss reruns now treat later child restarts as fresh live-run provenance.** `startup_miss.rs` no longer reasons only from `session_start`; it now tracks the latest harness run boundary (`*_start` / `*_restart`) inside the owning supervisor session, so a pane that cleanly restarted the child is classified as open again instead of looking like a permanently closed or crashed pane. `route.rs` now clears retained startup-miss markers only when the same pane proves a newer open harness run after the miss, and its ops provenance now reports that latest run event directly. Added regression coverage for restarted-child session-log parsing and for the reroute helper path that must treat a later `fresh_restart` as superseding the old miss.

- **Routed startup-miss errors now surface the recorded timestamp and stop clearing unresolved live-pane misses.** `route.rs` now appends the persisted startup-miss timestamp to the fail-closed `no new document cycle started` error and to the tmux overlay diagnostic, so JetBrains/plugin error surfaces can point back to the exact recorded miss without hunting through logs. On reroute, a startup-miss marker is now cleared only when the same pane proves a newer open harness run after that miss; if the pane merely still owns the document but the session log shows a closed/timeout restart loop with no later run, route deregisters it and starts fresh instead of repeatedly reusing and re-clearing the broken pane. Added regression coverage for the closed-live-owner restart rule and for timestamped routed startup-miss failures, and updated the routing spec to document the stricter marker-retention contract.

- **Stash-loss recovery now preserves live supervisors and closes orphaned preflight cycles before replacement.** `resync.rs` no longer auto-purges an unregistered stash pane when that pane still hosts a live supervisor socket, so a temporarily unregistered stashed Codex/Claude session is preserved for later recovery instead of being silently killed as generic stash garbage. `sync.rs` now records explicit `supervisor_exit code=missing_pane` provenance in the owning session log and repairs a stale `preflight_started` cycle before auto-starting a replacement pane when a previously registered pane is truly gone. Added regression coverage for supervisor-backed stash preservation plus the missing-pane stale-preflight repair path, and updated the sync/resync command spec to document the stronger recovery contract.

- **Codex routed retries now re-submit the unresolved prompt body instead of a bare reopen.** `route.rs` now carries the first unresolved prompt-bearing change text alongside `agent-doc <FILE>` when re-dispatching into an already-live Codex pane on top of a closed cycle. That gives cancel/retry flows a fresh actionable message for the harness instead of a bare reopen that can be accepted by tmux yet produce no new document cycle. `session_check.rs` now exposes the first unresolved prompt-bearing change directly so route and session-check share the same classifier, and the routing spec documents the Codex retry payload contract. Added unit coverage for prompt-body normalization and Codex-only payload expansion.

- **Nested submodule gitdirs are now added to workspace-write harness roots.** `git.rs` now walks the current repo's `.git/modules/...` tree and exposes every nested child submodule gitdir alongside the existing submodule and superproject roots, so a session launched from `src/sample-app/tasks/...` can still commit inside `src/sample-app/src/sampleorders-dev` without tripping a misleading `index.lock` permission failure on the real gitdir under `.../.git/modules/...`. Added regression coverage in both `git.rs` and `agent/mod.rs`, and updated the config/command/git specs to document the deeper writable-root set.

- **`#agent-doc-bug` closeout now proves that the requested plans were actually created.** `prompt_contract.rs` now detects preset-expanded "create a plan" requirements, `preflight.rs` persists the required plan-reference count in cycle state, and `write.rs` / `session_check.rs` now fail closed when the response cites fewer existing plan files than the bug prompt described. This closes the chat-level bug-report gap where a response could enumerate backlog transfers but skip one or more required plan files. Added regression coverage for prompt-contract plan counting plus pre-commit/post-commit shortfall failures, and updated the skill/spec text to document the stricter contract.

- **Route now recognizes Claude's double-chevron composer chrome as idle.** `harness.rs` now treats lines like `⏵⏵ ... (shift+tab to cycle)` as a valid Claude prompt shape, and `route.rs` has regression coverage proving `wait_for_agent_ready()` no longer misclassifies that newer idle UI as a busy pane. This fixes routed `Run Agent Doc` failures where the pane was actually ready but route kept waiting for a bare `❯` / `⏵` line and then refused injection after 15 seconds.

- **Supervisor quit prompts now log the actual user decision and fail closed on ambiguous stdin.** `start.rs` now records whether a clean-exit / Ctrl-D / resume-failure prompt led to quit, EOF-quit, invalid input, or an explicit fresh restart, so session logs no longer jump from `ctrl_d_prompt_user` straight to another `codex_start` with no provenance. Prompt-time stdin EOF now exits the supervisor instead of being treated like an implicit restart, and stray non-empty input is rejected with a re-prompt instead of silently starting a fresh child. Added unit coverage for prompt-decision classification and input-summary logging.

- **Route/fix now treat duplicate document panes as a first-class recovery state.** `sync.rs` now enumerates every pane that still proves ownership of a document via process-tree or supervisor-PID evidence, `route.rs` only auto-picks a winner when that evidence is decisive (single owner overall, or single active-window owner with only stashed duplicates), and `resync.rs`/`fix` now re-register a unique winner before generic issue cleanup. Scoped `fix <FILE>` can also kill redundant unregistered stash panes once the winning pane is known. Ambiguous cases now fail closed with direct inspect/claim/kill commands instead of blindly reusing the first pane that happens to match.

- **Local tmux-router development is now first-class in agent-loop.** The workspace root now patches `tmux-router` to the sibling `src/tmux-router` checkout via `.cargo/config.toml`, the harness instruction surfaces (`AGENTS.md`, `SKILL.md`, `CLAUDE.md`) now tell Codex/Claude to treat that crate as a live development target when generic tmux behavior moves out of `agent-doc`, and `sessions.rs` / related helpers now delegate reusable session/key primitives to `tmux-router` instead of carrying their own shell-level copies.

- **Added `agent-doc fix` as the canonical session-repair surface, with document-scoped targeting.** `main.rs` now exposes a top-level `fix [FILE]` command, while `resync --fix [FILE]` routes through the same implementation. `resync.rs` now accepts an optional target document, limits dead-pane pruning and issue/fix application to matching registry entries for that file, and leaves unrelated stash/orphan cleanup untouched during scoped runs. Updated command metadata, CLI coverage, and `specs/07-commands.md`.

- **Preflight no longer swallows prompt-bearing status edits into step-2 OOB absorbs.** `git.rs` now rejects safe-status snapshot absorbs when the inserted status text contains prompt work, including preset-token leads like `#next-steps` and `#next-steps ...`, imperative directives, or other prompt-bearing lines. That keeps compact-follow-up status edits visible to `preflight` step 4 instead of letting step 2 commit them as prior-cycle out-of-band status churn and collapse the turn to `no_changes`. Added direct classifier coverage plus a preflight regression for the compacted-status repro, and updated the commit spec.

- **Startup-miss reruns now distinguish stranded sessions from real pane death.** `startup_miss.rs` now parses the owning session log for the latest live harness run in the session, `route.rs` logs that provenance and refuses to auto-start a replacement when the marked pane is still alive, the supervisor socket is gone, and no later child exit / `session_end` was ever recorded. `session_check.rs` now includes the same session-log detail in its startup-miss warning so the failure is visible as a stranded supervisor/startup-miss state instead of a generic tmux-pane crash. Added regression coverage for session-log parsing plus the new route fail-closed decision, and updated the routing spec.

- **`#agent-doc-bug` closeout now proves the full transferred bug set, not just target drift.** `prompt_contract.rs` now derives a minimum explicit transfer count from the prompt-bearing bug report itself, `preflight.rs` persists that count in cycle state, and `write.rs` / `session_check.rs` now fail closed when a target backlog changed but the response only enumerated a smaller set of transferred `[#id]` items than the bug prompt actually described. Existing promised-id enforcement still proves that every enumerated new id landed in the target backlog; the new guard blocks the earlier partial-transfer shape where only a subset of the reported bugs was captured. Added regression coverage for prompt-contract counting plus pre-commit/post-commit shortfall failures, and updated the command spec and transfer runbook to document the stricter `#agent-doc-bug` inventory contract.

- **Explicit backlog-target closeout now proves every newly promised `[#id]` landed.** `preflight.rs` now snapshots the baseline open-item ids for each prompt-contract target named by `Add to the backlog of ...`, and `write.rs` / `session_check.rs` now compare any new tracked-item ids listed in the response body against the live target backlog before allowing closeout. A target backlog merely changing is no longer sufficient when the response promises multiple new items: if some listed ids are still missing, `finalize` and `session-check` fail closed with the missing-id set. Added regression coverage for both pre-commit and post-commit enforcement, and updated the command spec plus transfer runbook to document the stronger contract.

- **Startup-miss diagnostics no longer get stranded in the harness input buffer.** `route.rs` now renders fresh-start and routed-trigger startup-miss notices through a tmux-owned `display-message` overlay instead of drafting `echo '...'` text into the pane input area. That keeps Codex/Claude panes visibly recoverable without making the session look hung behind an unsent composer line. Added route coverage for retry-command rendering and for the regression that no longer leaves drafted `echo` text in the pane, and updated the command spec to document the overlay contract.

- **Strict template / CRDT closeout now fails before IPC when the response has no real body.** `write.rs` now proves that a template-mode response contains at least one non-empty non-backlog/non-frontmatter patch or a non-empty unmatched body that can be synthesized into `exchange` / `output` before the strict closeout can proceed. Empty `patch:exchange` shells, frontmatter-only payloads, or normalization-only responses therefore fail before `ipc_write_consumed` / commit instead of silently consuming the turn as a zero-patch closeout. Added unit coverage for the proof helper plus finalize integration coverage for the strict CRDT reject path.

- **Shared docs now require an explicit security review before cross-document access.** `frontmatter.rs` adds `agent_doc_collaboration: shared` plus `agent_doc_security_review: <review-id>`, `extract.rs` now blocks cross-document `extract` / `transfer` when a shared source or target lacks that review marker, and `plan.rs` now blocks shared `do #id` work when the referenced backlog/icebox item points at another `.md` plan without the same review proof. Auto-created transfer targets inherit the source document's shared/review metadata. Updated the security spec, README, and pending-ops runbook, and added regression coverage for the new frontmatter, transfer guard, and plan blocker.

- **JetBrains/VS Code tab sync no longer suppresses the first opposite-pane selection, and the editor-side coalescing delay is now 100ms instead of 500ms.** The shared tab-sync planners were still carrying a 1.5s bounce-back filter that could classify a real left/right split selection as noise immediately after the prior sync, which matched the latest "first click to the other side does nothing" report. Both plugins now dispatch every real tab-selection state change, keep only exact-state dedup, and reduce the editor-side debounce to 100ms so visible split handoff stays low-latency. Added Kotlin and TypeScript regressions that prove an unchanged split still syncs on the first opposite-pane selection. Bumped the local-testing plugin builds to JetBrains `0.2.91` and VS Code `0.2.11`.

- **Editor plugins now visually distinguish agent-doc markdown structures from ordinary prose.** `syntax.rs` adds a shared Rust token scanner exposed through the new `agent_doc_visual_tokens_json` FFI export, and both editor plugins now consume that canonical range set instead of maintaining their own parsers. VS Code 0.2.10 adds live markdown decorations for agent component comments, patch comments, boundary markers, `### Re:` headings, `❯` prompts, tracked `[#id]` tags, and ordinary HTML scratch comments. JetBrains plugin 0.2.78 applies the same token stream through editor highlighters. Fenced/inline code examples are intentionally excluded so markup samples remain untouched. Updated the shared editor spec and editor integration guide to document the new highlighting contract.

- **Harness prompt intent now survives `no_changes` direct-entry turns.** `codex_hook.rs` now persists the last Codex `UserPromptSubmit` text alongside the tracked document, and `preflight.rs` / `plan.rs` now consume that harness prompt body (or an explicit `AGENT_DOC_HARNESS_PROMPT` override) when the document itself has no diff. The binary strips the leading `agent-doc <file>` invocation, synthesizes a prompt-bearing diff from the remaining chat text, and reuses the normal diff/prompt-contract pipeline so direct harness prompts such as `#agent-doc-bug`, `#code-review`, or `do #id ...` no longer collapse to `no_changes` / `No changes detected since the last snapshot.` simply because the user asked in chat instead of editing the document first. Added regression coverage for env-backed harness prompts, Codex-thread prompt lookup, preflight cycle opening, and plan output for preset-expanded backlog capture plus existing `do #id` resolution.

- **Backlog and icebox now support ordered parent items for explicit priority.** `pending.rs` now recognizes flush-left `1. ...` / `2. ...` parent entries alongside `- ...`, preserves them through backfill/edit/done/reap/transfer, and when any tracked item in a backlog or icebox uses ordered style the binary canonicalizes the whole tracked surface as a sequential ordered list in current item order. Granular mutations therefore keep numeric priority lists valid after adds, reorders, and selective transfers instead of treating them as inert prose or leaving stale ordinals behind. `pending_cmd.rs` also now lets legacy `remove` / `prune` helpers understand ordered parents. Updated the pending spec/runbook/transfer docs and added regression coverage for ordered parsing, renumbering, nested continuations, extraction, and legacy helper compatibility.

- **Backlog and icebox items now preserve nested indented lists as part of the parent task block.** `pending.rs` now treats only flush-left tracked parent lines as work entries and attaches following indented continuation lines to that parent item, so nested subtasks/dependencies survive backfill, edit, done, reap, reorder, shadow/history guards, and archive writes instead of being misparsed as standalone backlog entries. `extract.rs` now moves those nested blocks with their parent during selective `--items` transfers. Updated the pending spec/runbook text and added regression coverage for nested parsing, reorder, transfer, shadow detection, and archive preservation.

- **`do #id` closeout now treats icebox items as tracked work.** `session_check.rs` and `write.rs` now enforce missing-`--pending-done` against still-open ids from both `agent:backlog` / legacy `agent:pending` and `agent:icebox`. `pending_cmd.rs` now resolves `--pending-done <id>` in either tracked list surface, `preflight.rs` reaps completed icebox items through the same snapshot/archive closeout path as backlog items, and `plan.rs` emits `resolve_existing` for `do #id` directives that target icebox-only work. Updated the pending runbook/spec text and added regression coverage across pending mutation, precommit/session-check guards, plan output, and preflight maintenance.

- **Backlog and icebox headings are now preserved by granular mutations.** `pending.rs` now parses backlog bodies with non-item lines intact, so markdown headings and blank separators inside `agent:backlog` / `agent:icebox` survive backfill, reap, add, done, edit, clear, reorder, gate, and resolve operations instead of being dropped between the first and last bullet. `write.rs` also now normalizes accidental backlog replace-patches against the full non-item skeleton, which allows unchanged headers to survive the compatibility path while still rejecting real non-list edits. Updated the pending runbook/spec text and added unit + write-path regression coverage for header preservation.

- **Transfer now treats `agent:icebox` as a first-class tracked list surface.** `extract.rs` now accepts `--items` for `icebox` as well as `backlog`/legacy `pending`, resolves the backlog alias consistently during transfer lookups, auto-creates missing targets with the full status/exchange/queue/backlog/icebox scaffold, and when moving a non-list component also carries both backlog and icebox items into the target instead of only the backlog. Updated the transfer runbook/command spec and added regression coverage for auto-created target scaffolding plus selective icebox transfer.

- **Full exchange compaction now carries forward live backlog, queue, and icebox context by default.** `compact.rs` now replaces a full `exchange` compact with a default `### Session Summary` that includes the archive pointer plus concise state from the live `agent:backlog` / `agent:pending`, `agent:queue`, and `agent:icebox` components whenever no custom `--message` is supplied. The bundled compact-exchange runbook and command spec now also direct agents to treat those components as the canonical compaction inputs, with `prompt_presets` limited to optional summary-policy tuning. Added regression coverage for the default summary plus runbook assertions for the new context rules.

- **Invalid YAML frontmatter now surfaces a document-targeted startup error instead of raw parser noise or silent sync skips.** `frontmatter.rs` now wraps parse failures with the document path, parser message, and when serde_yaml reports a location, a compiler-style excerpt of the frontmatter with a caret at the reported line/column before the `--- ... ---` repair hint. `start.rs` and `route.rs` use that wrapper directly, so malformed frontmatter fails closed with actionable feedback. `sync.rs` now logs the same contextual warning during file resolution and auto-start, mirrors it into the document's `agent:status` component when present so editor-driven auto-start failures are visible even without a pane, and clears only that managed status note once the file parses again. Added regression coverage for the shared parse wrapper, sync status round-tripping, and sync-phase error context.

- **Strict queue closeout now proves both sides before advancing the queue.** `write.rs` no longer mutates the live queue before later strict closeout gates run, and queue consumption now computes the document + snapshot transforms fully before writing either one. Required closeouts therefore keep the head prompt in place when pending maintenance / pending guards reject the cycle, instead of partially advancing the queue in the working tree or snapshot before the commit boundary. Added finalize integration coverage for the rejected-closeout case and updated the queue consumption specs.

- **Route lazy-claim no longer commandeers the tmux session's current active pane.** `route.rs` now requires explicit pane provenance for Strategy 2 recovery after a dead registered pane: `find_target_pane()` only accepts an explicit pane override, still rejects already-claimed panes, and keeps the existing non-agent-process guard. When no explicit safe candidate exists, route falls through to auto-start instead of silently adopting an unrelated Codex/Claude pane from the same tmux session, repo, or nested registry. Updated the session-routing spec and added regression coverage for the explicit-only gate.

- **`claim` now rejects live cross-session tmux mismatches unless `--force` is explicit.** `claim::run()` no longer logs and proceeds when `cross_session_decision()` resolves to `Reject`. A pane in another healthy tmux session now aborts the claim with a concrete error telling the operator to switch sessions or pass `--force`; only stale configured sessions still auto-accept. Updated the claim/session-routing specs and added regression coverage for the fail-closed enforcement helper.

- **Legacy done backlog items now get ids before reap instead of disappearing silently.** `pending::reap_with_items()` no longer tolerates completed items with empty ids; it fails closed unless callers backfill first. `backlog reap` and stale-completed-item `repair` now canonicalize missing ids/checklists before removal, so legacy/manual `- [x]` lines are reaped and archived with stable references instead of being dropped without a trace. Added regression coverage for the pure helper, CLI backlog reap, and repair path.

- **Live-owner proof now recognizes pane-relative start paths as the same document.** `sync.rs` path matching no longer requires the running `agent-doc start <file>` argv to contain the exact registry string. When a submodule-hosted pane starts with a narrowed path like `tasks/docs.md`, root-level ownership proof for the same document now still matches the longer superproject form such as `src/session-share/tasks/docs.md` by normalized path-component suffix. This closes the false `NoLiveOwner` / stale-deregister shape that appeared in root `resync` output when the supervisor socket was unavailable and only the process-tree match remained. Added regression coverage for the submodule-relative and negative-path cases.

- **Missing commit-boundary recovery is now limited to exchange-only historical patchbacks.** `repair`, `preflight`, and `session-check` now share a narrow self-heal path for open `response_captured` / `write_applied` cycles and log-only write-complete/no-commit tails: when `HEAD` already proves the response landed as an exchange-only patchback, the snapshot/cycle/capture state is advanced to committed without synthesizing a new response write. Historical bypasses that also mutate typed components such as `status` / backlog / pending, or that still leave a bare prompt target in the repaired tail, now fail closed instead of being silently adopted. Added regression coverage across `git`, `repair`, `session-check`, and `preflight`.

- **Completed-backlog repair no longer lets preflight swallow a live prompt into `no_changes`.** When `repair` reaps stale `- [x]` backlog items with no pending response/capture, it now mirrors that reap into the snapshot surgically from the snapshot's backlog/archive components instead of re-saving the whole live document. That keeps prompt-bearing exchange edits visible for the next `preflight` diff and prevents the `#nodiffswallow` shape where a plain prompt inserted before `agent:boundary` could survive in the file but disappear behind `no_changes: true`. Added regression coverage for both the direct `repair` path and the full `preflight` closeout path.

- **Strict post-write closeout is now shared across `run`, `finalize`, `write --commit`, `repair`, and the Codex Stop-hook.** `write.rs` now exposes one binary-owned helper that runs `git::commit()`, requires the cycle state to be closed, retries once when the snapshot still differs from `HEAD`, and then enforces `session-check`. `run.rs`, `repair.rs`, and `codex_hook.rs` now use that same helper instead of weaker ad hoc `git::commit()` paths. This closes the `#patchregr` family where a response path could look successful after commit/no-op closeout without proving the same post-commit invariants as `finalize`. Added regression coverage for the already-committed-plus-later-prompt-drift shape.

- **Bare `compact exchange` directives now fail closed unless the binary compaction path is used.** `run.rs` now rejects a pending diff that contains a direct `compact exchange` request instead of sending a normal agent-response cycle. `write.rs` / `finalize` apply the same pre-write guard against unresolved compaction directives, and `plan.rs` now emits a `Compact` handoff with `agent-doc compact <file> --commit` instead of a misleading finalize placeholder. Added regression coverage for both the `run`/`write` guards and the new plan handoff.

- **Route now reuses or restarts the registered pane via supervisor health before spawning a fallback session.** When `route.rs` cannot prove live ownership from tmux process args or supervisor child PID, it now still queries the registered pane's supervisor socket before treating that pane as stale. Healthy supervisors are reused in place; reachable halted/degraded supervisors get a `restart` IPC and keep the same pane; only unreachable/missing sockets fall through to deregister + fresh auto-start. This closes the duplicate-session shape where a supervisor was still alive but sitting at the Ctrl-D restart prompt. Added route coverage for both the fresh-start decision helper and the registered-pane restart path.

- **Explicit-baseline writes now keep concurrent user edits out of the next snapshot baseline.** `write.rs` now persists `content_ours` instead of merged disk `final_content` only when an explicit `--baseline-file` was supplied and the live file diverged during the response merge. That keeps user edits pasted while `finalize` is completing visible in the next diff instead of silently absorbing them into the snapshot. Non-baseline writes still persist the final merged disk state as before. Added regression coverage for both paths.

- **Stale startup-miss markers no longer spawn duplicate fallback sessions.** On rerun, `route.rs` now checks whether the pane named by a persisted startup-miss marker has since resumed proving live ownership of the document. If it has, route clears the stale marker and reuses that pane instead of deregistering it and auto-starting a second session. Added a route regression for the fresh-start decision helper and updated `specs/07-commands.md`.

- **`resync --fix` now preserves active bound panes even when session/window cleanup heuristics disagree.** `resync.rs` now requires the live-owner proof to resolve back to the registered pane itself; if another pane owns the file, the registration is treated as stale `NoLiveOwner`. When the registered pane does still prove live ownership, `WrongSession` / `WrongWindow` fix paths preserve that active bound session instead of killing or stashing it based only on foreground-command or layout heuristics. Updated `specs/07-commands.md`.

- **Stabilized the remaining tmux readiness/full-suite regressions.** The `route` prompt-readiness tests now wait for an actual idle shell before injecting their mock agent, and the `resync` wrong-window tests no longer depend on process-global cwd or fixed sleeps for pane/window relationships. This keeps the parallel `cargo test` suite deterministic without changing runtime command behavior.

- **`start` no longer relocates the launcher pane before deciding to reuse a live owner.** The wrong-session auto-relocation now runs only on the fresh-start path after `start` has already ruled out successful reuse/restart of an existing live owner pane. This closes the cross-session bug where invoking `agent-doc start <file>` from another tmux session could `join-pane` the transient launcher into the project session, making the caller's original window disappear or look like a crash even though the command was about to reuse a different pane. Added a regression test that proves the reuse focus path keeps the launcher pane in its original session.

- **Snapshot-committed guard catches response patchbacks that were never committed.** `session-check` now verifies that the current snapshot matches `git show HEAD:<file>` in the owning git root after a committed cycle. If the snapshot differs from HEAD, the response patchback is visible but was never committed — `session-check` exits `1` with a specific diagnostic. `finalize` retries the commit once before handing off to `session-check` when it detects this mismatch. Additionally, `commit` now updates the parent submodule pointer even during no-op (`commit_already_current`) cycles when the pointer is stale. Added `verify_snapshot_committed()` and `is_submodule_pointer_stale()` to `git.rs`, the snapshot-committed guard to `session_check.rs`, retry logic to `write.rs`, and 6 regression tests.

- **Backlog-replay guard detects open items silently dropped from recent history.** `preflight` and `session-check` now compare the current document's backlog against the pre-cycle baseline (`.agent-doc/baselines/`, falling back to `git show HEAD`). Open items present in the baseline but completely absent from the current document — not in live backlog, not in icebox, not in shadow/commented sections, and not in the cycle's `pending_done_ids` — fail closed. This prevents the bug where open backlog items disappear during a response cycle with no shadow copy to trigger the existing shadow guard. Added `detect_dropped_from_history()` detector in `pending.rs`, guards in both `session_check.rs` and `preflight.rs`, and 8 regression tests.

- **Codex Ctrl-D now shows quit menu instead of auto-restarting fresh.** `restart_continue_exit_strategy()` now routes `ctrl_d_forwarded` to `PromptUser` so the user sees "Press Enter to restart fresh, or 'q' to exit" instead of an automatic fresh restart. The supervisor log records `ctrl_d_prompt_user` / `user_quit_after_ctrl_d` for the new path. The `RestartFresh` handler no longer contains a dead Ctrl-D branch. Updated supervisor spec and regression tests to match.

- **Startup-miss tracking makes fresh-start failures visible instead of looking like dead panes.** When a fresh-start or routed-trigger cycle acknowledgment times out, `route.rs` now records a startup-miss marker at `.agent-doc/state/startup-miss/<doc-hash>.json` and echoes a diagnostic into the pane so the user sees "startup-miss: ..." instead of an unexplained idle shell. On rerun, route detects the marker on the registered pane, deregisters it, and auto-starts fresh instead of reusing a pane that never started a document cycle. Successful acknowledgment clears the marker. `session-check` reports a warning when a startup-miss marker exists. Added `startup_miss` module with persistence/load/clear/detection, 4 unit tests, 4 route-level integration tests, and updated `specs/07-commands.md`.

- **`start` reuse now probes supervisor health before switching focus.** When `start` finds a live owner pane, it queries the supervisor IPC `state` method. Healthy sessions get focus-switched as before. Unhealthy sessions (halted/degraded/not-running) get a `restart` IPC command; if that fails, the stale registration is cleared and a fresh supervisor starts in the current pane. Panes with unreachable or missing supervisor sockets are deregistered and replaced. This closes the case where `agent-doc start <file>` silently switched to a stuck or dead session.
- **Cross-session `start` reuse now switches the current tmux client before focusing.** When the live owner pane is in another tmux session, `start` now uses a current-client focus path that switches to the target session first, then selects the window and pane. This closes the false "switching focus" success case where the reuse path proved a live owner but left the user in the old tmux session.

- **Successful duplicate `start` reuse no longer prints shared `[sync]` probe diagnostics.** `start.rs` now uses a quiet live-owner lookup when it is only deciding whether to reuse an already-running pane, so the happy path emits only the start-level reuse/focus messages. `route` and `resync` keep the richer `[sync]` owner-proof logging they use for recovery and diagnostics.

- **Duplicate live `start` now reuses the existing pane instead of erroring.** `start.rs` now excludes the current transient `agent-doc start <file>` pane when probing for live owners, focuses any already-running owner it proves, and re-registers to that pane when the registry was stale. If the registry points at a different alive pane but no live owner can still be proven, `start` now clears that stale binding and proceeds in the current pane instead of failing closed forever. Added start-level regression coverage for reuse, stale-alive clearing, and same-pane/dead-pane cases.

- **`resync` now shares route's live-owner proof and stale-owner recovery.** `sync.rs` now exposes a shared ownership probe that first scans tmux process trees for the document path and then falls back to the per-session supervisor PID. `resync.rs` reports alive-but-unowned registrations as `NoLiveOwner`, `resync --fix` deregisters them without killing the pane, and `route.rs` now clears that same stale binding before continuing with lazy-claim / auto-start recovery instead of failing closed immediately.

- **Stash cleanup no longer preserves every unregistered agent pane by default.** During `resync --fix`, unregistered `agent-doc` / `codex` / `claude` panes in stash are now kept only when the shared live-owner proof still ties them to some registered document. Otherwise they are purged as orphaned agent panes. Added regressions for stale-owner detection, lazy-claim recovery, and stash cleanup.

- **Codex Ctrl-D clean exits now prompt the user instead of silently resuming.** `start.rs` treats stdin EOF/Ctrl-D on a clean Codex exit as a prompt path (Enter to restart fresh / q to exit) so the user can choose to quit the supervisor cleanly. Single failed resume handoffs stay on the fresh-restart path before escalating to a prompt after repeated failures. Added start-level regression coverage for the exit-strategy split and updated the supervisor/Codex support docs to match.

- **Live-pane route ownership now falls back to supervisor PID before declaring ambiguity.** `route.rs` still prefers a tmux process-tree match on the document path, but when a registered pane is alive and the long-lived `agent-doc` supervisor no longer exposes that file path in argv, route now queries the per-session supervisor socket for the live child PID and maps that PID back to the owning tmux pane. This closes the JetBrains/IDE reroute shape where a live `agent-doc` pane was refused as "ambiguous" even though the supervisor still owned the document session. Added route regression coverage for recovering the live pane via supervisor PID when argv loses the file path.

- **Failed fresh-route cleanup no longer kills the new live pane.** When route creates and registers a new pane for a document but later fails closed because fresh-start acknowledgment was not observed, `route.rs` now preserves that pane if it is still the live registered owner instead of cleaning it up as an orphan. This keeps `fresh_route_start_missing` / `fresh_route_trigger_missing` from surfacing to the user as a tmux pane crash. Added route coverage for both preserving the registered owner and still cleaning up truly unregistered panes.

- **Resume auto-trigger cancellation now cuts through the shared child-pty writer path.** Supervisor shutdown now flips both the auto-trigger stop flag and the stdin->pty writer stop path before joining either thread, the auto-trigger waits for the shared writer mutex interruptibly, and Unix child-pty writes now poll in short intervals so cancellation can break backpressure instead of hanging behind `stdin->pty`. Added regression coverage for cancelling while the writer lock is busy and updated the supervisor spec to document the shutdown ordering.

- **Resume auto-trigger now proves the prompt from current child PTY output.** The restart watcher no longer decides readiness from `tmux capture-pane` history. It now watches the filtered output emitted by the current resumed child and only injects once the latest non-empty line is a harness prompt, so stale visible prompts left in tmux scrollback cannot trigger an early resume command. Added regression coverage for latest-line prompt detection and updated the supervisor spec/module contract to match.

- **Resume auto-trigger now injects through the child pty instead of pane stdin.** The restart watcher still waits for a visible harness prompt via `tmux capture-pane`, but once the prompt appears it now writes the trigger command directly through the supervisor-owned child pty writer instead of `tmux send-keys`. That closes the `#rvinjectrace` window where a stale watcher could inject into the supervisor restart prompt or a later replacement process after the resumed child died during the trigger handoff. Added regression coverage for carriage-return injection, late cancellation before write, and closed-writer failure during the trigger window.

- **Historical snapshot repair now adopts committed `HEAD` before later local drift.** When `session-check` or `commit` sees that `HEAD` already contains a previously bypassed assistant response, snapshot repair no longer requires the live worktree to be exactly `HEAD` or `HEAD` plus an exchange-only prompt follow-up. It now advances the snapshot to the committed `HEAD` state for any later local drift that does not introduce a newer `### Re:` / `## Assistant` block beyond `HEAD`, then reclassifies the remaining user edits normally. This closes the stale-snapshot/manual-commit `#pbc2` shape where a structurally valid committed response was still misreported as a direct patchback bypass. Added regressions for both `session-check` and `commit` on the committed-head-plus-local-status-edit case.

- **`agent-doc backlog` is now the canonical backlog CLI, with `agent-doc pending` retained as a deprecated alias.** The top-level backlog management subcommand now lives under `agent-doc backlog ...`; invoking the legacy `agent-doc pending ...` spelling still works for compatibility but emits a deprecation warning directing callers to the canonical name. Updated autocomplete command metadata and integration coverage for both the canonical and deprecated spellings.

- **Completed backlog reap now fails closed when persistence is incomplete.** Preflight no longer downgrades reap-persistence problems to a warning: if it removes `- [x]` backlog items from the working tree but cannot verify the same reap in the staged snapshot, the cycle stops before commit instead of silently letting completed items survive. `session-check` now also fails closed when a supposedly clean committed document still contains stale completed backlog items from an older cycle. Added regression coverage for the happy path, the missing-snapshot-backlog failure, and the post-commit closeout guard.

## 0.33.16

- **Pending add/backlog normalization now fail closed on malformed leading id prefixes.** Active `--pending-add` parsing still accepts canonical `id=<custom> ...` and compatibility `[#custom] ...`, but it now rejects bare `[#]` placeholders, empty `id=` prefixes, and stacked leading prefixes like `[#a] [#b] ...` or `id=a [#b] ...`. The accidental `replace:pending` / `patch:pending` normalization path still repairs a lone legacy `- [ ] [#] ...` line into a generated id, but it now blocks the stacked-prefix shape before any malformed prefix text can persist into backlog content. Added unit coverage for the add-time parser and write-path regression coverage for normalize-vs-reject behavior.

- **Submodule sessions now expose the parent working tree to workspace-write harnesses.** `append_workspace_access_args` no longer limits submodule-hosted Claude/Codex sessions to external git metadata dirs. Fresh launches now also add the superproject working tree as an extra writable root, so a session started in `src/session-share` can still patch parent-repo docs such as shared backlog files without misreporting them as outside the writable root. Existing Codex resume behavior is unchanged: `exec resume` still strips `--add-dir` because the resumed thread inherits those writable roots from the original exec. Added regression coverage for both the computed workspace-access dirs and the actual appended Codex args.

- **Already-committed closeout now blocks bypassed response patchbacks.** When the staged snapshot already matches `HEAD` but the working tree contains a likely direct assistant patchback (`### Re:` / `## Assistant`) with no newer `agent-doc` cycle, `git::commit` now fails closed instead of classifying that state as ordinary post-commit working-tree drift and returning `commit_already_current`. This closes the `#pbypass1` shape where a session doc could show a restored response but stop at "Nothing has been committed," leaving the patchback outside the binary-owned commit boundary. Added regression coverage for the committed-HEAD plus bypassed-response case.

- **Session closeout now fails before commit when completed backlog items omit `--pending-done`.** `write`/`finalize` gained a pre-commit pending-done gate that compares the active response capture against still-open backlog ids and blocks commit when a response clearly completes `#id` but the cycle recorded no matching `--pending-done <id>`. Session documents now default `pending_done_guard` to `strict` unless frontmatter or project config downgrades it, while non-session docs keep the old warn default. Added unit coverage for default/recorded/warn/suppressed paths plus integration coverage proving `finalize` leaves `HEAD` unchanged when the gate trips.

- **Blank `--window` sync scope now fails safe instead of reconciling the whole tmux server.** `sync.rs` now normalizes empty/whitespace-only window overrides to "unset" before repair, auto-start scoping, and `tmux_router::sync`, and `route.rs` ignores blank `context_session` overrides the same way. This closes the tmux-instability path where a JetBrains/plugin sync passed an empty window id, producing `target_window=` / `session=""` reconcile state that detached unrelated live panes into stash and triggered follow-on duplicate starts. Added regression coverage for blank sync/window scope normalization.

- **Stash rescue no longer swaps a live pane out of view.** `route.rs` and `sync.rs` now rescue stashed session panes back into the `agent-doc` window with guarded `join-pane`, placing them on the requested left/right edge instead of preferring `swap-pane`. This closes the remaining `claudescore-3.md` tmux swap/recovery bug where a recovered pane could displace another live pane into stash and only appear to "heal" on a later reroute. Added route/sync regressions that prove the existing visible pane stays in the `agent-doc` window during rescue.

- **Duplicate live `start` now fails closed before spawning a second pane.** `agent-doc start` now checks whether the document session UUID is already registered to another alive tmux pane and refuses to launch a duplicate live harness in the new pane when it is. This closes the `corky.md` restart failure class where the same session id was repeatedly started on `%194/%196/%197/%198/%199`, destabilizing other active panes instead of reusing the already-live session. Added start-level regression coverage for alive/same-pane/dead-pane cases.

- **Already-present recovery closeouts now advance the snapshot before commit.** When a reopened repair/Stop cycle finds that the live document already contains the assistant response but the snapshot still lags behind, `repair` now advances the snapshot and `write_applied` phase before the commit boundary runs. That closes the Codex direct-patch bypass shape where the response was visible in the document, but the later commit path downgraded the turn to post-commit local drift and left it unowned. Added regression coverage for the committed-cycle + direct-patch + already-applied recovery path.

- **Boundary-artifact-only preflight now stays cycle-free.** `preflight` no longer opens `preflight_started` on pure agent-owned `(HEAD)` / boundary churn in template docs. It classifies that shape first, collapses it back to `no_changes` / already-committed closeout, and prevents that transient drift from leaking a stale user-visible lock. Added regression coverage for the exact clean-snapshot plus transient-`(HEAD)` shape that previously surfaced as `cycle started but no write/commit followed`.

- **`compact exchange` write-back now replaces `agent:exchange` for that turn.** When the user-added diff explicitly starts with a direct `compact exchange` directive, template/CRDT write paths now override the normal append mode for `agent:exchange` and force replacement semantics instead. That closes the failure where repeated compaction requests kept appending new checkpoint summaries over older `### Re:` history instead of collapsing the component to one compacted checkpoint. Added directive-detection, template apply, and repair/write regression coverage for both patch-based and raw-response closeouts.

- **Route start-ack now rejects same-cycle committed churn.** `route.rs` no longer treats mutations to an already-committed baseline cycle as proof that a new document cycle started. When a routed or fresh trigger is dispatched against prompt-bearing drift on top of a closed cycle, acknowledgment now requires a genuinely newer cycle id; same-cycle `commit_already_current` updates fail closed instead of logging a false `route_cycle_start_acknowledged`. Added regression coverage for the exact same-cycle false-ack shape.

- **Route/sync now fail closed instead of inventing fallback tmux sessions or force-moving live stash panes.** `route.rs` no longer rewrites `config.toml` when a configured `tmux_session` is dead, refuses auto-start into an implicit dead fallback session like `"claude"`/`"codex"`, and re-registers an already-running pane for the same file before lazy-claim/auto-start. `sync.rs` now preserves stashed panes that belong to another live tmux session instead of moving them across sessions during rescue. Successful replacement paths also preserve prior stash panes unless there is explicit provenance for cleanup. Added regression coverage for dead implicit fallback refusal and non-destructive stash replacement.

- **Live-pane reroutes now require real cycle acknowledgment for pending prompt drift.** `route.rs` now applies the same fail-closed start-ack rule to dispatches into an already-running pane when the document already has unresolved `prompt_target` / `content_edit` drift on top of a closed cycle. A consumed routed trigger no longer counts as success by itself; route waits for a newer per-document cycle state and fails closed if none appears. Added route coverage for both the acknowledged and missing-ack live-pane shapes.

- **Post-commit stale-buffer guard for `codex (HEAD)` drift.** JetBrains post-commit boundary reposition now prefers the just-committed on-disk document when the open buffer differs only by agent-owned `### Re:` heading attribution and/or boundary churn. That prevents the stale-buffer failure where a successful patchback commit was immediately re-dirtied to `codex (HEAD)` with a newer boundary marker. Added JetBrains regression coverage for the prefer-disk decision and Rust closeout coverage that repairs historical heading-attribution drift back to clean `HEAD`.

- **`session-check` now catches startup-miss prompt drift.** When a session document already has unresolved prompt-bearing user edits (`prompt_target` / `content_edit`) relative to its snapshot, but no newer `agent-doc` cycle ever started, `session-check` now fails closed instead of reporting the stale committed state or `no cycle state or ops.log — ok`. The Codex Stop hook inherits that signal and can auto-close the missed-start case from `last_assistant_message` through the normal repair/write/commit path. Added `session_check` and Codex hook regression coverage.

- **Session-document `write --commit` now fails closed.** `write --commit` still behaves as a best-effort helper for non-session docs and `--pending-only`, but when it is writing a response into a real session document (`agent_doc_session` / legacy `session`) it now upgrades to the same strict closeout contract as `finalize`: reject non-git docs before mutation, fail the command on commit failure, and only return success once the cycle reaches `committed`. Added CLI integration coverage for gitless/session, git-backed/session, and non-session best-effort behavior.

- **Normalize accidental pending patches before capture/replay.** When a response still contains a single list-shaped `replace:pending` / `patch:pending` block, the write path now translates it into granular pending mutations before durable capture instead of capturing first and then failing on `replace:pending block forbidden`. That closes the `response_captured` orphan path behind `#pendops`. `repair` replays the same historical capture shape through the same normalization path, while unsupported pending/backlog patch shapes still fail closed before capture. Added live-write and repair regression coverage.

- **Fresh Codex start now requires real cycle acknowledgment.** `route.rs` no longer treats a consumed `agent-doc <file>` trigger as sufficient proof that a fresh pane started successfully. After trigger injection, route now waits for a new per-document cycle state (`preflight_started` or later) before declaring success, logs `fresh_route_start_acknowledged` / `fresh_route_start_missing`, and fails closed if the file never enters a real cycle. Added route unit coverage for fresh-cycle, fast-commit, and timeout shapes. Specs updated to document the stronger startup contract.

- **Fix Codex submodule handoff.** `codex exec resume` does not accept `--add-dir`, but `append_resume_args` was passing it through from `base_args`. The Codex backend now strips `--add-dir` (both `--add-dir <DIR>` and `--add-dir=<DIR>` forms) from resume args. Resumed sessions inherit writable roots from the original `exec`, so stripping is correct behavior. Specs updated to document backend-specific handling.

- **Pending-capture guard now catches single unresolved bug/follow-up prose.** The recommendation heuristic no longer requires a numbered batch when the response clearly identifies a current issue as still needing follow-up (for example, "still hitting the older ... bug that X was meant to close"). Strict `finalize` now blocks those uncaptured single-item responses before commit, and `session-check` warns on the same shape post-commit. Added regression coverage for unresolved-vs-resolved bug prose.

## 0.33.15

- **Supervisor model injection from frontmatter.** `start.rs` now injects `--model` from `claude_model` / `codex_model` / `model` frontmatter when the freeform args (`claude_args`, `agent_args`, etc.) don't already contain `--model`. Precedence: harness-specific field (`claude_model` for Claude, `codex_model` for Codex) > generic `model` field.

- **Pre-commit pending capture gate in `finalize`.** When `pending_capture_guard: strict`, `finalize` scans the response for uncaptured recommendations before committing. If recommendation-like items are detected without `--pending-add` flags, finalize exits non-zero before the commit step.

- **`plan` emits `ExpectAdd` pending mutations.** When prompt targets contain backlog/recommendation signals ("tasks", "todo", "backlog", "what's next", "recommendations", "next steps", "action items"), `plan` emits an `expect_add` entry in `pending_mutations`. Tells the skill that finalize should include `--pending-add` flags for actionable items in the response.

- **Post-preflight planning command.** `agent-doc plan <FILE>` emits a structured planning/dispatch record with `prompt_targets`, `repo_actions`, `required_commands`, `pending_mutations`, `handoff`, and `blockers`.

## 0.33.14

- **Inline guard marker stripping.** `strip_guard_markers` now removes `<!-- no-pending-capture -->` and `<!-- no-pending-done-guard -->` from within content lines (not just standalone lines where the entire trimmed line equals the marker). Trailing whitespace is trimmed after removal. Previously, inline markers like `**Bold text** <!-- no-pending-capture -->` survived into committed blobs.

- **Rename `agent:pending` → `agent:backlog`.** The component is now canonically `<!-- agent:backlog -->` with `agent:pending` accepted as a backward-compatible alias. `patch=replace` attribute on backlog/pending tags is deprecated and auto-stripped. Added `agent:icebox` component to template scaffold for parked items.

- **`agent-doc migrate` command.** New subcommand for deprecated component name/attribute migrations (e.g., `pending` → `backlog`).

- **Per-harness model override.** Frontmatter `claude_model` and `codex_model` fields allow different model selections per harness, resolved through the existing tier/config precedence chain.

- **Snapshot auto-migration on document rename.** State files (snapshots, baselines, captures, CRDT) now follow when a document path changes, preventing orphaned state after renames.

- **Pane eviction guard.** `route.rs` now skips tmux pane eviction when an agent process is still active, preventing mid-response pane recycling.

- **Route trigger path resolution.** Trigger paths are now resolved to absolute paths, preventing submodule CWD misrouting when the working directory differs from the document's repo root.

- **Pending-capture heuristic fix.** Detects unconditional follow-up patterns that were false-positive-triggering the recommendation batch guard.

- **Queue component (Phase 1–3).** Parser, data model, template scaffold, preflight integration, trigger resolution, consumption, dispatch, and halt detection for `<!-- agent:queue -->` orchestration.

- **Prompt preset expansion in orchestrate.** Frontmatter `prompt_presets` are now resolved during orchestrate task expansion, and `--plan` flag previews expanded prompts without execution.

- **Post-preflight planning command.** `agent-doc plan <FILE>` emits a structured planning record (prompt targets, repo actions, required commands, pending mutations, blockers, handoff) for the skill to execute against.

- **Compound task steering runbook.** Bundled guidance for normalizing multi-clause directives into explicit sequential steps.

- **Orchestrate synonym dispatch runbook.** Natural-language phrasing like "run these in order" maps to `orchestrate --mode sequential|parallel|dag`.

- **Orphaned supervisor socket GC.** Stale supervisor sockets are cleaned up automatically.

- **IPC snapshot integrity validation.** `start` now validates snapshot integrity before launching the IPC listener.

- **Code formatting cleanup.** Applied rustfmt across 8 source files.

## 0.33.13

- **Workspace-write submodule sessions now auto-add external gitdirs.** When a session document lives in a git submodule, the harness launch path and fresh-agent backends now append `--add-dir` entries for the submodule's external gitdir under the superproject `.git/modules/...` tree plus the superproject `.git` used by parent-pointer updates. That keeps normal workspace-write Claude/Codex sessions from tripping permission failures on submodule commits while preserving the existing arg-precedence chains. Added regression coverage for external-gitdir discovery and for Claude streaming preserving extra `--add-dir` args when switching to `stream-json`.

- **`agent-doc orchestrate` now executes real DAG batches.** The shared orchestration surface still resolves task batches from repeated `--task`, `--from-file`, and `--from-exchange`, but `--mode dag` now parses optional `[id=... after=...]` metadata, falls back to the first `#token` in each prompt as the node id, validates duplicate/missing/cyclic dependencies, and runs the resulting graph in deterministic topological order through the same per-step `inject -> preflight -> fresh agent -> finalize -> session-check` lifecycle. This gives same-document fan-in semantics without pretending concurrent patchback is safe. Added unit coverage for DAG metadata parsing, unknown-dependency and cycle failures, and topological execution order.

- **Legacy `parallel` now routes through the orchestrate dispatcher.** `agent-doc parallel` remains available, but it now forwards its explicit task list into the same `orchestrate --mode parallel` routing layer used by the newer command surface instead of bypassing orchestration entirely. This keeps task normalization and mode dispatch in one place while preserving the existing parallel backend and its empty-task compatibility behavior. Added coverage for shared parallel dispatch and the legacy compatibility path.

- **Compound single-line task steering is now bundled into the skill surface.** The installed skill/runbook now explicitly tells agents to normalize directives like `do #ntoc. Add to today's news. commit + push` into explicit sequential or dependency-ordered steps before execution instead of treating them as one opaque prose task. The command spec now documents that this remains skill-side steering, not binary-owned free-form parsing, and regression coverage locks the new bundled runbook into the installed harness content.

- **Pending ordering guidance now covers late additions from an existing ordered batch.** The bundled skill and `pending-ops.md` runbook still treat front insertion as the default, but now document the exception for follow-on steps: if Step 1 / Step 2 are already captured and you later promote Step 3, add it with a canonical custom id and reorder it into place adjacent to its predecessor in the same cycle instead of prepending it above earlier steps. Added regression checks for the new bundled guidance so the skill surface keeps the `#9pw9`-style placement rule.

- **Skill auto-update now targets the active harness explicitly.** Installed instruction content now renders `agent-doc-version` from `CARGO_PKG_VERSION` instead of inheriting a stale literal from the source template, Codex environment detection now recognizes live Codex shell vars like `CODEX_THREAD_ID` / `CODEX_CI`, and the rendered auto-update step now uses harness-specific install/reload commands (`--harness claude --reload compact` for Claude Code, `--harness codex --reload restart` for Codex). Added regression coverage for the new detection signals plus rendered Codex/Claude auto-update content.

- **Prompt-prefix enforcement now reuses the prompt-bearing classifier.** `write.rs` now treats prompt-prefix targets as a shared binary invariant derived from `diff.rs`'s canonical `prompt_target` classifier instead of relying only on a separate line-shape heuristic, and `session-check` now reports bare prompt-target lines when a bypassed `### Re:`/`## Assistant` patchback left the transcript uncanonicalized. Added unit coverage for prompt-prefix target extraction and the new `session_check` failure shape.

- **Pending-capture guard in `session-check`.** Committed response captures are now scanned for recommendation-like batches (priority labels, numbered action lists, recommendation headers, imperative follow-ups) when the cycle recorded no `--pending-add` / `--pending-add-gated`. Default mode warns on stderr; `pending_capture_guard: strict` or project `[guards] pending_capture = "strict"` upgrades the condition to a nonzero `session-check`, and `<!-- no-pending-capture -->` suppresses the guard for intentional skips. Added heuristic unit coverage plus `session_check` coverage for warn, strict, suppression, and frontmatter-overrides-project precedence.

- **Unified prompt-bearing change classifier.** The diff/prompt contract no longer splits explicit `required response targets` from `inline_annotations`. `diff.rs` now classifies ordered user-authored changes as `prompt_target`, `content_edit`, `recovery_artifact`, or `boundary_artifact`, prompt builders render that typed section directly, and preflight surfaces the canonical list as `prompt_bearing_changes` while keeping `inline_annotations` as a compatibility projection. Added regression coverage for inline prompt promotion, inline correction classification, and response-artifact detection.

- **Committed captures no longer trigger repeat recovery dedup on later preflights.** `repair` now ignores terminal durable-capture states (`committed`, `discarded`) unless there is still a pending response file to reconcile, so routine `preflight` runs stop emitting the "`Response already present in document`" self-heal message after a cycle has already closed cleanly. Added regression coverage for the committed-capture/no-pending shape.

- **Post-commit editor refresh now reuses the committed boundary ID.** Standalone IPC `reposition` messages can carry the exact exchange `boundary_id`, and both editor helpers now preserve that marker instead of minting a new one after `commit()`. This closes the boundary-only dirty-worktree shape where the response was already committed but the editor saved a fresh marker afterward. Added Rust, JetBrains, and VS Code regression coverage for explicit-ID repositioning.

- **Imperative detection now recognizes natural-language pending tasks.** The executable-directive guard no longer stops at hard-coded `do #id` / `run tests` phrases: pending-item prose that starts with an imperative verb (for example `[#n8q4] Fix the cross-repo ...`) is now classified as executable intent too. That means status-only replies like "I'm starting now" are rejected for those diffs instead of letting actionable pending text be misread as non-directive continuation prose. Added unit coverage for diff extraction and finalize integration coverage for the pending-item shape.

- **Delayed recovery patchbacks now keep provenance.** Durable capture records now retain lifecycle timestamps like `replayed_at` and `committed_at`, and `ops.log` emits `capture_committed_after_replay` when a response only reaches the commit boundary after recovery replay. This preserves the distinction between "same-turn patchback succeeded" and "the response was written back later during recovery/closeout" for forensic analysis and user-facing explanations.

- **`commit` now explains post-commit local drift explicitly.** When the stripped snapshot already matches `HEAD` but the working tree still has later local edits, `agent-doc commit` now classifies that state as post-commit local drift, logs whether it was a user follow-up or broader working-tree edits, and closes the cycle without mislabeling the state as a generic out-of-band patchback warning. Added regression coverage for both the safe follow-up and later-local-edit shapes.

- **Stale snapshots can no longer rewind already-committed responses on no-op closeout.** If the snapshot lags behind a response that is already in `HEAD`, and the working tree only adds a new user follow-up on top of that committed state, `agent-doc commit` now repairs the snapshot up to `HEAD` before the `HEAD`-current no-op path runs. This prevents a later closeout from staging the old snapshot blob and momentarily rewinding the document before recovery re-adds the response. Added regression coverage for the exact stale-snapshot + follow-up shape.

- **Relative submodule doc resolution no longer falls through to outer-repo shadows.** When `agent-doc` is invoked from inside a submodule with a relative document path like `tasks/sampleorders.md`, path resolution now prefers the caller's existing cwd-local file before consulting the superproject root. This fixes the case where `commit` / `show_head` / related git paths could silently target an outer-repo document with the same relative path, leaving the intended submodule doc uncommitted even though the closeout logged success. Added regression coverage for the shadowed-path shape.

- **Executable-directive backstop in `run` + `finalize`.** The binary now inspects the pending user diff for imperative document directives (`do #id`, `run tests`, `build + install`, `commit + push`, and approval words like `go`) and rejects status-only/meta-only replies unless they include either concrete execution evidence or a concrete blocker. Added unit coverage for directive extraction + response classification and finalize integration coverage for the reject path.

- **Codex closeout contract hardened.** `agent-doc finalize` is now the strict happy path for normal session responses, Codex/direct-exec instructions require an immediate `agent-doc session-check <FILE>` after `finalize` or `write --commit`, and the installed Codex `Stop` hook can auto-close a pending response cycle from `last_assistant_message` before failing closed. Added CLI/integration coverage for the `finalize + session-check` path and the real Codex hook flow.

- **Codex hook state now survives root / turn drift.** The repo-local `UserPromptSubmit` / `Stop` bridge now mirrors active-session state across nested `.agent-doc` roots and still inspects the tracked document on later `Stop` events in the same Codex session, so a closeout cannot be skipped just because the harness CWD moved between the superproject and a submodule or because the next `Stop` arrives with a newer turn id. Added regression coverage for the nested-root replay path.

- **Interrupted-cycle + historical-drift repair.** `preflight` now fails closed on unrecoverable `preflight_started` cycles instead of snapshot-committing over newer live content, while `commit` / `session-check` can narrowly repair already-committed historical `### Re:` drift when `HEAD` proves the response is no longer out-of-band.

- **Bare-path compatibility restored.** `agent-doc <FILE>` once again aliases to `agent-doc run <FILE>`, keeping older wrappers working while the explicit subcommand form remains canonical.

- **Boundary cleanup invariants locked.** Boundary/head-marker cleanup is now regression-covered across the Rust path plus both editor helpers so stale boundary IDs and duplicate visible `(HEAD)` churn do not survive reposition.

- **Repo-scoped commit closeout serialization.** `git::commit()` now keys its advisory closeout lock by the resolved git dir / submodule git dir, blocks for the short critical section instead of proceeding unlocked, and retries the full stage+commit transaction when `index.lock` contention hits `update-index`, `git add`, or `git commit`. Added regression coverage for a staged `index.lock` retry and two different docs contending on closeout in the same repo.

- **`repair` now closes git-backed recovery in one command.** `agent-doc repair` (legacy alias: `recover`) no longer stops after replaying or deduping a pending response; when recovery work happened inside git it now immediately runs the normal commit boundary so repaired assistant content does not remain uncommitted until a later `preflight`. Added regression coverage for both replayed and already-applied repair paths.

## 0.33.12

- **Codex agent backend (Phase 1).** New `agent/codex.rs` implements `Agent` + `StreamingAgent` for the OpenAI Codex CLI. Parses Codex JSONL event stream (`thread.started`, `item.completed`, `turn.completed`). Session resume via `codex exec resume <id>`, fork via `codex exec resume --last`. Registered in `agent::resolve("codex")`. 11 unit tests covering event parsing, session ID propagation, and stream iterator behavior.

## 0.33.11

- **Fix: lib-install uses atomic rename to prevent mmap corruption.** `install_versioned()` in `lib_install.rs` previously used `std::fs::copy(source, &dst)` which overwrites the versioned `.so` in place (same inode). On same-version reinstall during development, this corrupted IDEA's live mmap of the `.so`, triggering a crash. Now copies to a temp file then calls `rename()` — atomic on POSIX, creates a new inode so existing mmaps stay valid. 1 new test: `same_version_reinstall_creates_new_inode`.

## 0.33.10

- **Fix: Component parser peek guard for non-agent HTML comments.** `parse()` in `component.rs` previously consumed any `<!-- ... -->` sequence in document content, causing the close-comment search to eat the next `<!-- /agent:name -->` marker. Now peeks 20 bytes after `<!--` and skips non-agent sequences (advances 1 byte) rather than consuming them. Fixes "unclosed component" errors when pending items contain literal `<!-- ` in their text. 5 new tests.

- **Fix: CRDT stale-base detection uses prefix+suffix.** `merge()` in `crdt.rs` previously only checked `common_prefix_len` to decide if the base was stale. Template documents have structural content (frontmatter, component markers, pending sections) at both ends — a short exchange meant only the prefix went uncounted, causing valid bases to be classified as stale and triggering duplicate-user-prompt bugs. Now computes `ours_shared = (prefix + suffix).min(base_len)` and uses that ratio for the 50% threshold.

- **Cleanup: Remove IPC degraded mode.** `is_ipc_degraded`, `mark_ipc_degraded`, and `clear_ipc_degraded` removed from `write.rs`. The ack-content sidecar mechanism (v0.33.x) made the degraded marker obsolete — sidecar ACK is authoritative; disk fallback handles the timeout path. Replaced with `cleanup_legacy_ipc_degraded` that removes any stale `.agent-doc/ipc-degraded` marker left by older installs.

- **JB plugin 0.2.71: writeAckContent fires on all patch paths.** Previously `writeAckContent` was only called from the VFS patch path; the two exchange-level patch paths omitted it. Now all three paths (WriteCommandAction exchange, VFS exchange, boundary-reposition) call `writeAckContent`, ensuring the ack-content sidecar always fires regardless of which code path processes the patch.

- **Fix: Makefile `test` target unsets git hook env vars.** `make test` now runs `env -u GIT_DIR -u GIT_INDEX_FILE -u GIT_WORK_TREE cargo test`. When the pre-commit hook calls `make precommit`, git sets `GIT_DIR` to the outer repo — all temp-repo tests in the suite inherited this and routed their git subcommands to the wrong repo, causing 24+ test failures during commit. The `env -u` strips the hook vars before cargo test, restoring correct isolation.

## 0.33.9

- **Fix: CommitLock uses try_lock_exclusive to prevent indefinite hang.** `CommitLock::acquire` (git.rs) previously called `fs2::lock_exclusive()` which blocks indefinitely when another process holds the lock. In the IPC-sidecar-timeout fallback path (exit 75), the write to disk succeeded but `git::commit` blocked at the flock — causing the skill process to hang. Changed to `try_lock_exclusive()`: returns `None` immediately when contended, proceeding unlocked. Git's own `index.lock` retry loop (3 attempts with exponential backoff) handles serialization at the git layer.

## 0.33.8

- **Rename debounce (#qam7).** `agent-doc sync --rename` writes a 5s debounce marker (`.agent-doc/rename-debounce/<hash>.marker`) for the focused file; subsequent auto-start checks skip files with active markers. Prevents spurious pane creation when `FileRenameListener` (JB) or `onDidRenameFiles` (VS Code) triggers sync for a file with no alive pane. Both editor plugins now pass `--rename` on file rename/move events. JB plugin 0.2.70, VS Code extension 0.2.7.
- **Auto-start pane ID logging.** `route::provision_pane` now returns `Result<String>` (the new pane ID). Sync logs `[sync] auto-started %XX for <file>` per pane; when >1 pane starts in a single call, a batch summary is printed. Both messages written to `/tmp/agent-doc-sync.log`.
- **Tests + spec.** 5 new tests: 3 rename debounce unit tests, 2 batch summary formatting tests. Spec, contracts, and evals added for both features in `sync.rs`.

## 0.33.7

- **Boundary reposition CAS guard (JB plugin 0.2.68 + VS Code extension).** `repositionBoundaryViaDocument()` in `PatchWatcher.kt` and `repositionBoundaryWithDebounce()` in `extension.ts` now verify the document content is unchanged between the `document.text` read and `document.setText()` / `WorkspaceEdit.apply()`. If the user typed between `await_idle` timeout expiry and the write dispatch, the reposition is silently skipped rather than overwriting the new keystrokes. Adds `repositionBoundaryToEndUtil` / `findCodeBlockRangesUtil` as internal top-level functions (JB) and `repositionBoundaryToEnd` as a vscode-free module (VS Code) for unit testability. New: `RepositionBoundaryTest.kt` (7 cases) and `reposition.test.ts` (5 cases).

- **Skip working-tree boundary reposition when IPC available.** `reposition_boundary_in_snapshot()` in `git.rs` now checks for `.agent-doc/patches/` before touching the working tree. When the IDE plugin is installed (IPC path), the CLI skips the disk-level read-modify-write entirely and relies on the IPC reposition signal — eliminating the TOCTOU race where concurrent user typing could produce duplicate boundary markers in the committed state. New regression tests: `reposition_skips_working_tree_when_ipc_available` and `reposition_updates_working_tree_when_no_ipc`.

## 0.33.6

- **Inline annotation surfacing.** Preflight JSON added `inline_annotations: Vec<String>` as the original surface for user additions (`[user+]`/`[user~]`) inside agent response blocks. In later versions this becomes the compatibility projection of the broader `prompt_bearing_changes` contract.

- **False positive fixes for `inline_annotations`.** Two exclusion rules eliminate boundary artifacts: (1) `[user~]` lines where the only change is appending ` (HEAD)` to a heading are skipped — these are binary reposition artifacts. (2) `[agent]` lines that are component tags (`<!-- ... -->`), section headers (`# ...`), or blank are excluded from the "substantive agent lines after" check — end-of-exchange user input followed only by structural markers is now correctly classified as regular input, not inline annotations.

## 0.33.5

- **FFI library hot-reload (JNA + koffi).** Fixes SIGSEGV crash (PC=0x0) when `cargo install` overwrote `libagent_doc.so` while IDEA held it mmap'd via JNA. Both plugins now stat the `.so` on every `get()` / `ensureLoaded()` call; if mtime changed, they force `Native.unregister` + reload (JNA) or `koffi.unload` + reload (VS Code). One `stat(2)`/`statSync()` per FFI dispatch — negligible overhead. Race window narrows to sub-microsecond.

- **Versioned cdylib install.** `cargo install` / `make install` now writes `libagent_doc-<version>.so` and atomically updates the `libagent_doc.so` symlink via `ln -sfn` + `rename(2)`. The old inode stays alive in any running editor's mmap — editor restarts pick up the new version. Backward-compatible: `agent-doc lib-path` still returns `libagent_doc.so` (now a symlink). Legacy installs (regular file) are upgraded to the symlink layout on first install.

- **Lockfile-tracked GC (`agent-doc gc-libs`).** On JNA/koffi load, plugins write `<so-path>.lock` containing their PID; on clean exit (JVM shutdown hook / VS Code `deactivate()`), they remove the lock. `agent-doc gc-libs` walks all `libagent_doc-*.so` siblings: keeps the current symlink target and any .so whose `.lock` has a live `/proc/<pid>`; unlinks stale .so files and orphaned locks. Triggered on load, on install, and manually. Crash-safe: stale locks from SIGKILL'd processes are cleaned on next sweep.

- **Post-reload version sanity check (JB + VS Code).** After each native library (re)load, both plugins now call `agent_doc_version()` and log `[native] loaded libagent_doc v{version} from {path}` on success. Warns on null return or exception (ABI mismatch). Helps diagnose cases where a reload brings in an incompatible .so.

## 0.33.4

- **SKILL.md § 1b: pending promotion heuristic.** Agents now have an explicit rule: if a response ends with a numbered list of distinct, actionable recommendations and pending is empty (or the user asked for backlog/tasks), each recommendation must be added via `--pending-add` in the same write. Prevents actionable items from being silently lost as prose-only responses.

## 0.33.3

- **IPC sidecar timeout: fall back to disk write instead of claiming success.** `try_ipc()` previously returned `success: true` when the socket acknowledged but the sidecar ack timed out, causing the caller to skip the disk write path. If the plugin didn't actually apply the content, the response was silently lost. Fixed: sidecar timeout now returns `success: false`, so the caller falls through to the CRDT disk write path — the reliable fallback that always works.

- **IPC fallback patch file pre-write.** The disk patch file is now pre-written before socket send (overwriting any stale content) and cleaned on confirmed sidecar success. On sidecar timeout, the file is left for file watcher recovery as an additional safety net. `patch_id` deduplication prevents double-apply.

- **IDE buffer stale fix (JB plugin 0.2.64).** `repositionBoundaryViaDocument()` in `PatchWatcher.kt` now calls `reloadFromDisk(document)` after VFS refresh so the buffer picks up the CRDT-merged content before the boundary is repositioned. Previously the handler read the pre-merge buffer, repositioned the stale content, and wrote it back — burying the agent's response.

- **Runbook: agent-proposed forward actions must be `--pending-add`ed.** `runbooks/pending-ops.md` now requires any response ending with a forward-looking question ("Ready to X?", "Should we A or B?", "Shall I capture Y?") to add each concrete next-step option to `agent:pending` in the same cycle, so the proposal survives user non-reply.

## 0.33.2

- **`agent_doc_resolve_project_path` FFI export.** Editor plugins can now resolve a file's nearest agent-doc project root (the ancestor containing `.agent-doc/`) and the path relative to that root. Fixes a JetBrains plugin bug where `Run Agent Doc` on a file inside a submodule (e.g. `src/session-share/tasks/foo.md`) passed the full monorepo-relative path to the submodule's Claude session, producing `file not found`. Plugins now pass the submodule-relative path (`tasks/foo.md`) and use the submodule root as CWD.

- **IPC timeout path: CRDT merge instead of atomic_write.** The exit(75) fallback now uses the same CRDT merge as the normal disk write path, preserving all concurrent changes (user edits, pending mutations, structural modifications) — not just the `agent:pending` component. Falls back to `splice_pending_component` only if CRDT merge itself fails.

- **Recovery dedup fix.** `is_already_applied()` now checks each fingerprint line individually instead of joining them into a single substring. Fixes false negatives caused by blank-line separation between paragraphs and `(HEAD)` boundary suffixes on headings, which prevented the joined fingerprint from matching.

- **5 new tests** covering nested-submodule resolution, no-ancestor fallback, file-in-root, and recovery dedup with blank lines/boundary markers.

## 0.33.1

- **Pending parse fix: bare `[#]` placeholder accumulation.** `parse_item_line` now strips `[#]` markers instead of prepend-on-backfill, preventing placeholder accumulation across cycles.

- **Pending dedup on `--pending-add`.** `op_add` checks for identical text before appending, preventing duplicate items when the same add is retried.

- **Content-shrink guard for `--stream` writes.** `check_exchange_shrink_guard()` in `write.rs` refuses writes when new exchange content is < 10% of existing length (and existing > 100 bytes). Prevents accidental truncation from malformed heredocs or trivial payloads. Fires in both IPC and disk fallback paths. Overridable with `--force`.

- **9 new tests** for pending parse fixes and shrink guard (5 shrink guard + 4 pending).

## 0.33.0

- **Typed gate markers (`[/release]`, `[/deploy]`, `[/code-review]`, etc.):** Parser recognizes typed gates alongside plain `[/]`. Gate types are alphanumeric with hyphens/underscores, case-insensitive, stored lowercase. State machine: `[/release]` is a refinement of `[/]`; gate type is metadata on `Gated` state, cleared when resolved to `[x]`. Untyped `[/]` items are never touched by `resolve-gate`.

- **Per-file gate commands** (`agent-doc pending <FILE>`): `resolve-gate <type>` finds all `[/<type>]` items and flips to `[x]`. `set-gate-type <id> <type>` transitions `[/]` → `[/release]` (errors if not gated).

- **Project-wide `resolve-gate` command** (`agent-doc resolve-gate <type>`): Scans all `.md` files under project root (or `--scope <dir>`) for items with matching typed gates. Designed for hook integration:
  ```jsonc
  { "match": "cargo publish", "run": "agent-doc resolve-gate release" }
  { "match": "git push",      "run": "agent-doc resolve-gate deploy" }
  ```

- **Write command gate flags:** `--pending-resolve-gate <type>` and `--pending-set-gate-type id=type` for atomic pending+response cycles.

- **`--pending-add-gated` flag:** Add items pre-gated as `[/]` instead of `[ ]`. Available on both `write` and `notify` commands.

- **`--pending-only` flag:** Skip stdin reading and exchange synthesis — only apply pending mutations. Requires at least one `--pending-*` flag; incompatible with `--template`/`--stream`/`--ipc`.

- **`--status` flag on `write`:** Replace the `agent:status` component content inline during a write operation, same pattern as pending ops.

- **`status` submodule (`status_cmd.rs`):** New module for status component manipulation.

- **Notify with pending:** `agent-doc notify` gains `--pending-add`, `--pending-add-gated`, and `--no-create-pending` flags. Message is now optional when `--pending-add` is used.

- **`session clear` subcommand:** Clear the configured tmux session, returning to auto-detect mode.

- **Supervisor PTY module (`supervisor/pty.rs`):** New 526-line module for PTY-based process spawning and management within the supervisor architecture.

- **Start.rs expansion:** Major rework (+627 lines) for improved tmux detection, session routing, and supervisor integration.

- **Debounce simplification:** Removed redundant debounce logic in favor of the consolidated approach.

- **Tests:** 20 new typed-gate tests (parse, render, roundtrip, resolve, set-gate-type, scan, case insensitivity, edge cases). All 1111 tests pass, clippy clean.

## 0.32.5

- **Route idle gate tightened for busy Codex panes; bulk stash prune now reaps orphaned unregistered agent panes:** `route.rs` no longer treats every visible Codex prompt glyph as an idle routed-dispatch target. `wait_for_agent_ready()` now requires two consecutive idle-prompt samples and rejects captures that still show an active permission prompt or the Codex `tab to queue message` footer, which is a queue-only busy state rather than a true idle prompt. This closes the failure mode where route logged `codex ready after 0.0s`, injected `agent-doc <file>` into a live pane, then timed out with `no new document cycle started` because Codex had only queued the message. Tests: new `harness::has_busy_cue_*` coverage plus `route::wait_for_agent_ready_rejects_codex_queue_message_footer`. In the same pass, `resync.rs` bulk stash cleanup now matches the stricter single-pane cleanup behavior: unregistered stash panes running `agent-doc`/`claude`/`codex` are killed automatically unless live-owner proof still ties them to a registered document. This prevents repeated reroute attempts from piling up "unregistered — skipping kill (may be rescuable)" orphan panes in stash. Tests: new `resync::purge_unregistered_stash_panes_bulk_kills_unregistered_agent_without_live_owner`.
- **Fix submodule auto-start `file not found` (route.rs `rewrite_start_path`):** When the spawned tmux pane's `cwd` is narrowed to a submodule root (by `git::resolve_pane_cwd`), the `agent-doc start <path>` send-keys invocation now rewrites the caller-supplied super-root-relative `file_path` to be relative to that narrowed `cwd` before composition. Previously a path like `src/session-share/tasks/foo.md` was passed verbatim to a pane already `cd`'d into `src/session-share`, producing `Error: file not found: src/session-share/tasks/foo.md` and blocking auto-claim + auto-start on every submodule-hosted document. Fix lives at a single funnel (`auto_start_in_session`) and also feeds `send_command`'s `/agent-doc <path>` slash command for the same reason. Pure helper `rewrite_start_path(file, cwd, original) -> String` canonicalizes both sides, strips the cwd prefix, and falls back to `original` on any failure (preserves behavior for non-submodule docs, ghost paths, and files outside cwd). Tests: 4 new unit tests (`rewrite_start_path_narrows_to_submodule_relative`, `rewrite_start_path_no_op_when_file_under_cwd_with_same_prefix`, `rewrite_start_path_falls_back_when_canonicalize_fails`, `rewrite_start_path_falls_back_when_file_not_under_cwd`) plus full `route::` suite (43 passing). Forward-compatible with the supervisor track (#jg0d/#b486/#40ct/#vnp0/#6ae3/#zp02/#f7d5) — when `PtySpawnConfig.args` lands, the same helper feeds path rewriting at the new spawn funnel.
- **Binary strips trailing bare `❯` lines from exchange writes (`template::strip_trailing_caret_lines` in `apply_patches_with_overrides`):** The post-patch boundary marker `<!-- agent:boundary:... -->` lands directly after agent content, so a trailing `❯` on its own line becomes a phantom prompt-glyph row above the boundary on every cycle. Agent discipline is the wrong layer — this is now a code-enforced invariant. New pure helper `strip_trailing_caret_lines(content)` collapses all trailing lines whose trim is exactly `❯`; called on `patch.content` when `patch.name == "exchange"` and on unmatched content when it routes to `exchange`/`output` (including the auto-created-exchange path). Non-exchange components are untouched — `❯` in `notes`, `pending`, or user-authored content like `❯ follow-up` is preserved. Tests: 8 new (`strip_trailing_caret_removes_bare_prompt_line`, `_removes_multiple_trailing_lines`, `_preserves_mid_content_caret`, `_preserves_caret_with_text`, `_handles_no_trailing_newline`, `_noop_when_no_caret`, `apply_patches_strips_trailing_caret_from_exchange`, `apply_patches_preserves_caret_in_non_exchange`). Full `template::` suite: 64 passing. See [runbooks/code-enforced-directives.md](runbooks/code-enforced-directives.md).
- **SKILL.md audit + prune (293 → 112 lines, ~62% cut):** Delegated rarely-consulted workflow detail to runbooks to keep the hot-path instructions tight. New runbooks bundled via `include_str!` in `src/skill.rs::BUNDLED_RUNBOOKS` and installed to `.claude/skills/agent-doc/runbooks/` on `agent-doc skill install`: `model-tier-gate.md` (precedence chain, `required_tier` gate, `model_switch` ack — was SKILL §0c), `streaming-checkpoints.md` (when/how to flush, baseline re-save pattern — was a §1 sub-section), `document-format.md` (frontmatter fields, inline vs template mode, `<!-- agent:name -->` component conventions + inline attributes + snapshot storage — was §Document Format + §Snapshot Storage), and `code-enforced-directives.md` (promoted from project-local into the bundled set). Removed from SKILL.md: the `❯`-rule paragraph (now binary-enforced, see above), the verbose preflight JSON schema code block (the agent parses the real output), the duplicated baseline/write-back instructions between §2a and §2b, the per-mode split between append and template (unified into a single write-back block), and `## Snapshot Storage`. Preserved verbatim (hot-path on every cycle): invocation + subcommand detection, preflight call + `no_changes`/`claims`/`baseline_file` handling, slash-command dispatch via `Skill` tool, `### Re:` header rule + model attribution, pending granular-ops 3-line summary, `--stream` write-back + immediate `agent-doc commit`, and the `IMPORTANT: Do NOT use Edit tool` guard. Memory cleanup: `feedback_no_trailing_prompt_glyph.md` deleted from `~/.claude/projects/-home-brian-work-btakita-agent-loop/memory/` and its `MEMORY.md` index line removed — the rule is now a binary invariant, not a per-agent memory.

## 0.32.4

- **Pending gated-state `[/]` (#pf01, #mgdw, #h1j2, #q90h, #sx35):** New `PendingState::Gated` variant for pending items that are code-complete but awaiting an external gate (release, telemetry, field validation). Rendered as `- [/] [#id] text` in the pending component. Never auto-reaped — only `- [x]` items are reaped by preflight. Spec: `src/agent-doc/specs/pending-system.md` — includes the full state-transition matrix (§4), lifecycle diagram, and reaper rules. State machine: `Open → Gated` via `gate`, `Gated → Open` via `ungate`, `Open|Gated → Done` via `mark-done`. Illegal transitions (`ungate` from `Open`/`Done`, `gate` from `Done`) return errors; idempotent transitions (`Gate` on `Gated`, `MarkDone` on `Done`) are no-ops. Parser: `pending::parse_item_line` accepts `[ ]` / `[/]` / `[x]` / `[X]`; `PendingItem::render` round-trips. CLI: `agent-doc write --pending-gate <id>` and `--pending-ungate <id>` flags on the `write` subcommand, combinable with `--pending-add` / `--pending-done` / `--pending-edit` / `--pending-reorder` in a single call (gate/ungate run before done so `--pending-gate X --pending-done X` promotes through `Open → Gated → Done` atomically). Preflight: emits `pending_gated_count: N` in the JSON output when at least one item is gated (omitted when zero to keep happy-path output compact), alongside the existing `pending_reordered` signal. Reaper: preflight's reap pass skips `Gated` items unchanged. Tests: `tests/pending_integration.rs` covers parser round-trip for `[/]`, all valid/invalid state transitions, reaper respects `Gated`, CLI flag integration (`write_pending_gate_open_to_gated`, `write_pending_gate_idempotent_on_gated`, `write_pending_gate_done_errors`, `write_pending_gate_then_done_in_one_call`, `preflight_emits_pending_gated_count`, `preflight_omits_pending_gated_count_when_zero`). Rationale: previously, long-lived release-gated tasks had no lexical distinction from active work — they either sat in `[ ]` and competed for attention, or got prematurely `[x]`-marked and reaped before the gate actually cleared. The `[/]` character was chosen for visual distinctness from `[ ]`/`[x]` and because it's already in GFM-task-list parser tolerance ranges across common editors.

- **Rename `patch:pending` → `replace:pending` (#25ag):** The full-replacement block syntax for the `pending` component is renamed from `<!-- patch:pending -->...<!-- /patch:pending -->` to `<!-- replace:pending -->...<!-- /replace:pending -->`. The `replace:` prefix signals full-replacement semantics explicitly (all other `patch:<name>` blocks are component-scoped patches; pending uniquely replaces the whole list). Corresponding renames: `--allow-patch-pending` → `--allow-replace-pending` (CLI flag), `AGENT_DOC_ALLOW_PATCH_PENDING` → `AGENT_DOC_ALLOW_REPLACE_PENDING` (env var). Dual-accept migration: the deprecated `patch:pending` form, `--allow-patch-pending` flag (via clap alias), and legacy env var all continue to work for one release. The parser emits a stderr deprecation warning on every `patch:pending` block so callers can find and update their usage. The default-reject gate applies to both forms — enforcement recognizes `name == "pending"` regardless of which prefix opened the block. Rationale: the `replace:` prefix is a higher-signal warning to human readers that this block clobbers a list the user is actively editing, reducing the silent-data-loss failure mode that `patch:` understates. Tests: `write_rejects_replace_pending_block`, `write_rejects_legacy_patch_pending_block` (covers deprecation warning), `write_allows_replace_pending_with_escape_hatch`, `write_allows_legacy_patch_pending_with_legacy_flag`, `write_allows_replace_pending_with_legacy_env_var`, `write_rejects_replace_pending_via_library_default`. **Next release removes dual-accept:** `patch:pending` will become a hard error; update any remaining call sites now.

## 0.32.3

- **Fix: Submodule-aware git commit routing** — Files inside git submodules (`src/sample-app/tasks/*.md`, `src/session-share/tasks/*.md`) previously caused `fatal: Pathspec '...' is in submodule '...'` errors during `agent-doc commit` (preflight sweep and session-final commits). Root cause: parent-level git operations tried to stage submodule-relative paths directly in the parent index. Fix: Added `narrow_to_submodule(super_root, file) -> (PathBuf, bool)` which detects submodule boundaries. When a file is in a submodule, all git staging/commit ops (`hash-object`, `update-index`, `commit`) run inside the submodule's repo with submodule-relative paths. After commit succeeds, `update_parent_submodule_pointer()` updates the parent's submodule pointer in a separate partial commit. Tests: `narrow_to_submodule_returns_super_root_for_non_submodule_file`, `commit_in_submodule_routes_through_submodule_repo` (integration test with actual `git submodule add` sandboxing). Live verification: Two separate submodule documents (`src/session-share/tasks/claudescore.md`, `src/sample-app/tasks/sampleorders.md`) now commit cleanly with zero `fatal:` lines.

- **Feature: `out_of_band_write` always-on forensic logging** — Added unconditional log emission when a file's on-disk size diverges from the last snapshot, regardless of threshold. Previously, only divergences >100 bytes emitted human warnings; now all out-of-band writes emit a structured ops.log entry: `out_of_band_write file=<path> drift=<bytes> snap_len=<N> file_len=<N>`. This enables downstream analysis (aggregation, correlation with concurrent operations, drift pattern classification) without requiring the safety rail to trip (which only fires at catastrophic thresholds). Helps root-cause the recurring 135-byte snapshot-vs-file gaps observed in sampleorders and other in-flight sessions.

- **Feature: Safety rail with forensic logging in `normalize_user_prompts_in_exchange`** — When a user's added content (between snapshots) contains escaped newlines or other encodings that decompose during normalization, the normalization logic could diverge from the user's source. Added: (1) `normalize_threshold_exceeded` detection when decomposition deltas exceed a configurable threshold (default 500 bytes), (2) forensic logging of applied normalization counts and byte deltas, (3) automatic git commit with diagnostic context if threshold trips. Log schema: `normalize_user_prompts snap_len=<N> base_len=<N> applied=<count>` (fires on every write, no threshold), plus `normalize_threshold_exceeded file=... delta=... snap_len=... base_len=...` (fires if `delta > threshold`). Enables early detection of corruption patterns in heterogeneous editor environments (mixed CRLF, smart quotes, etc.). See ops.log for real-world drift data.

## 0.32.2

- **Feature: `env` frontmatter for per-document environment configuration:** Documents can now declare environment variables in YAML frontmatter that apply to all Bash tool calls and Claude spawns within that session. Syntax:
  ```yaml
  env:
    OPENROUTER_API_KEY: "$(passage btak/OPENROUTER_API_KEY)"
    ANTHROPIC_BASE_URL: "https://openrouter.ai/api"
    ANTHROPIC_AUTH_TOKEN: "$OPENROUTER_API_KEY"
    ANTHROPIC_MODEL: "qwen/qwen3.6-plus"
  ```
- **Shell expansion support:** Environment variable values support shell expansion (`$(command)`, `$VAR`, `${VAR}`). Cross-references work (later vars can reference earlier ones). Values are expanded at runtime; expanded secrets never appear in JSON output or logs.
- **Coverage across all paths:** Env vars apply to:
  - Interactive Claude sessions started via `agent-doc start <FILE>` (via `cmd.env()` on spawned process)
  - Non-streaming submits via `agent-doc run` (via `Claude::with_env()`)
  - Streaming submits via `agent-doc stream` (via `StreamingAgent::send_streaming()`)
  - Parallel fan-out (via unexpanded shell exports in tmux send-keys, so target shell handles expansion safely)
  - `/agent-doc` skill in existing sessions (preflight JSON returns unexpanded values; skill runs `export` in Bash)
- **Preflight JSON field:** `"env": {"KEY": "unexpanded_shell_expr"}` — skill exports these unexpanded so secret expansion happens inside the Bash call, never in JSON output.
- **New module `src/env.rs`:** 
  - `expand_values(env)` — expands all vars through the shell (used by start/run/stream paths)
  - `shell_export_prefix(env)` — builds `export K="V" && ...` string with unexpanded values (used by parallel path)
- **Tests added:** 42 existing tests + 8 new env tests covering plain values, shell expansion, cross-references, empty env, and safe quoting in send-keys commands. All 72 tests passing.
- **SKILL.md step 0c2:** Skill now exports env vars from preflight JSON into the shell before tool calls.

## 0.32.1

- **Fix: CRDT state not refreshed after `agent-doc compact`:** When a template-mode document with CRDT write strategy ran `compact`, the binary correctly rewrote the file and snapshot on disk, but the CRDT state in `.agent-doc/crdt/<hash>.yrs` was stale. On the next `agent-doc write` or `stream`, the 3-way merge loaded the stale CRDT (containing pre-compact exchange AND pre-compact pending), causing non-target components (like `agent:pending`) to be clobbered by old CRDT view of pending items. Fix: After `run_component_compact` or `run_component_compact_partial`, when `is_crdt`, refresh CRDT state by creating a new `CrdtDoc` from the post-compact content and saving it to `.agent-doc/crdt/<hash>.yrs`. This resets the CRDT to a fresh state, discarding pre-compact history (appropriate since compact is a "new epoch" operation).
- **Runbook hardened:** `.claude/skills/agent-doc/runbooks/compact-exchange.md` now explicitly forbids mutations to non-target components. Added Safety Invariants section and pre/post verification steps using git snapshots.
- **Tests added:** `crdt_compact_preserves_pending_with_state_refresh` (verifies fix), `compact_preserves_boundary_marker` (tests ❯ preservation in non-target component), `compact_working_tree_consistency` (disk/snapshot consistency).

## 0.32.0

- **Fix: Submodule-aware patch routing:** `try_ipc()` and `try_ipc_full_content()` in `write.rs` now use `git::resolve_to_git_root()` to detect submodule context. When a session document lives inside a git submodule, IPC patches are routed to the **superproject's** `.agent-doc/patches/` directory instead of the submodule's local `.agent-doc/patches/`. Previously, patches written to submodule documents (e.g. `src/session-share/tasks/claudescore.md`) would land in `<submodule>/.agent-doc/patches/` where the JetBrains plugin (which only watches the parent repo) never saw them. The fix falls back to `find_project_root()` if git resolution fails, preserving backward compatibility for non-git and non-submodule cases.
- **Tests added:** `try_ipc_routes_to_superproject_when_available` (creates a real git submodule structure and verifies patches route to parent), `try_ipc_falls_back_to_find_project_root_when_not_in_git` (fallback behavior), and `test_submodule_write_patches_dir_structure` (integration-level directory layout validation).

- **Feature: Harness-agnostic model tier selection:** New `model_tier` module defines a `Tier` enum (`auto | low | med | high`) and composes an `effective_tier` from four sources, highest precedence first:
  1. Inline `/model <x>` command in the diff (stripped from downstream diff/classifier)
  2. `<!-- agent:model -->` component content
  3. `agent_doc_model_tier` frontmatter field
  4. Diff heuristic (`suggested_tier`) based on `diff_type` + document path
- **Config: `[model.tiers.<harness>]` maps** let users customize tier→model mappings per harness (`claude-code`, `codex`, `default`). Built-in defaults: claude-code → haiku/sonnet/opus, codex → gpt-4o-mini/gpt-4o/o3.
- **Harness detection:** `detect_harness()` checks `CLAUDE_CODE_SESSION` / `CLAUDECODE` / `CODEX_SESSION` env vars and returns `claude-code | codex | default`.
- **Preflight JSON additions:** `effective_tier`, `required_tier`, `suggested_tier`, `model_switch`, `model_switch_tier` fields.
- **Diff scanner strips `/model` lines:** `scan_model_switch` runs before classification, so downstream classifier/slash-command parser never see `/model`.
- **SKILL.md step 0c (Model tier gate):** Documents how skills should read `effective_tier` / `required_tier` and either proceed, acknowledge a `/model` switch, or ask the user to `/model` before re-invoking.
- **Frontmatter field:** `agent_doc_model_tier: low | med | high | auto` on session documents.
- **Tests added:** 48 tests in `model_tier.rs` covering tier parse/resolve, harness detection, component read, scanner guards (code fence, blockquote), heuristic path boosts, composition precedence, and JSON serialization.

## 0.31.31

- **Fix: Commit-reliability — snapshot committed even on IPC timeout exit(75):** `write.rs` now saves snapshot + calls `git::commit` before `process::exit(75)`, so agent responses are preserved even when the IDE plugin doesn't ACK the patch in time.
- **Fix: Commit-reliability — commit before `result?` propagation:** `main.rs` reordered to run commit before `result?`, ensuring partial writes that saved a snapshot are always tracked in git.
- **Fix: Commit-reliability — retry on git index.lock contention:** `git.rs` retries `git commit` up to 3× with exponential backoff (100/200/400ms) when concurrent sessions cause lock contention.
- **Fix: Commit-reliability — `agent_doc_commit` FFI export:** `ffi.rs` exports `agent_doc_commit(file_path)` for IDE plugins to call after applying a patch. `NativeLib.kt` + `PatchWatcher.kt` updated to call it on the Document API path.
- **Fix: Commit-reliability — preflight cross-document sweep:** `preflight.rs` scans all tracked docs in the same project at the start of each cycle and commits any doc where the snapshot is newer than the file (missed commit backstop).
- **Fix: `project_config_path()` CWD-sensitivity:** Walks up from CWD to find `.agent-doc/` instead of always using a bare relative path. Prevents wrong-config reads when subcommands run from a subdirectory (e.g., submodule CWD drift). Falls back to CWD for uninitialized projects.
- **Tests added:** `commit_retry_logic_handles_index_lock_error`, `commit_succeeds_when_no_lock_contention` (Fix 3); `agent_doc_commit_returns_false_for_null`, `ffi_git_commit_commits_staged_file` (Fix 4); `preflight_sweep_commits_other_tracked_docs` (Fix 5).
- **Fix: `(HEAD)` marker incorrectly applied to bash comments inside fenced code blocks:** The old ad-hoc fence tracker (`is_fence_marker`) toggled `in_fence` on every line starting with 3+ backticks — including `` ```bash `` which per CommonMark can only OPEN a fence, not close one. When a `` ``` `` plain fence contained inner `` ```bash `` lines (e.g., terminal output referencing a bash command), the state inverted, causing `# On the server — run once` inside a subsequent `` ```bash `` block to appear "outside" the fence and receive a `(HEAD)` marker it must not have. Fix: replace the ad-hoc `is_fence_marker` / `in_fence` toggling in `strip_head_markers` and all four code paths in `add_head_marker` (step 1 cleanup, step 2 heading collection, step 3 HEAD heading counting, re-application loop) with CommonMark-compliant code block detection via `pulldown-cmark`. A closing fence cannot have an info string — `pulldown-cmark` correctly handles this. The re-application path also now filters out any `# comment (HEAD)` lines in git HEAD that are themselves inside a code block, preventing propagation of the baked-in bad marker across commits.
- **Test added:** `add_head_marker_bash_comment_inside_plain_fence` — exercises the specific failure path: a plain `` ``` `` fence containing a `` ```bash `` line, followed by a real heading, followed by a `` ```bash `` fence with a `# comment` line.

## 0.31.30

- **Fix: `❯ ` prefix applied to `agent:pending` patches (regression in v0.31.29):** `normalize_patch_content` was called on all IPC patches, not just exchange patches. When `normalize_prefix_lines` contained a line that also appeared verbatim in the `agent:pending` patch content, that line incorrectly received the `❯ ` prefix. Fix: gate `normalize_patch_content` on `is_append_mode_component(&p.name)` at both the primary IPC write path and the IPC timeout fallback in `write.rs`. Replace-mode components (`pending`, `status`, etc.) now always pass patch content through unchanged.
- **Test added:** `normalize_prefix_lines_skipped_for_replace_mode_components` — verifies that `agent:pending` content is not normalized.

## 0.31.29

- **`agent-doc write --commit` flag:** Runs `git::commit` immediately after a successful write. Eliminates the separate `agent-doc commit` step — the final write in the SKILL.md skill now uses `--commit`. Silently skips commit if the document is not inside a git repo (`git rev-parse --is-inside-work-tree` guard). Streaming checkpoint writes do not use `--commit`; only the final write does.
- **`git::is_in_git_repo` helper:** New `pub(crate)` function that checks whether a file path is inside a git repository.
- **SKILL.md updated:** Step 2a/2b final writes now use `--commit`; step 3 updated to reflect merged write+commit.

## 0.31.28

- **`start.rs` auto-relocate:** When claiming a pane from a terminal in a different tmux session than the project expects, automatically relocates the pane to the correct session before registration (was warn-only). Falls back to warn-only if no anchor pane exists in the expected session.
- **`relocate_if_wrong_session` helper + 3 tests:** Extracted guard into a testable `pub(crate)` function; 3 `IsolatedTmux`-based tests cover noop, cross-session success, and no-anchor fallback.

## 0.31.27

- **`pane_policy` module (tmux-router 0.3.10):** New `PaneMoveOp` + `CrossSession` enum as a mandatory gateway for all pane movement. `CrossSession::Deny` by default; `CrossSession::Allow { reason }` for intentional cross-session relocations. All 7 `join_pane` call sites in agent-doc migrated to use `PaneMoveOp`.
- **Guard `start.rs` registration:** When claiming a pane, warns if `$TMUX_PANE`'s session ≠ `project_tmux_session()` — prevents silent session drift on claim.
- **Guard `resolve_target_session` auto-update (route.rs):** No longer overwrites `tmux_session` config when a previously-configured session is dead. Only writes config when no session was previously set. Prevents session 1 from silently overwriting session 0.
- **Fix `resync.rs` WrongSession detection:** `detect_issues` now falls back to `config::project_tmux_session()` when `frontmatter.tmux_session` is absent. Panes in a wrong session are flagged even without per-document session frontmatter. `apply_fixes_to_registry` uses `PaneMoveOp::allow_cross_session("relocate WrongSession pane to project session")` to move them.

## 0.31.26

- **Fix: orphan repair dedup guard (repair.rs):** `repair::run` now reads the document before applying a pending response and checks if the content is already present using a 3-line fingerprint. If already applied (e.g., IPC path wrote the content but `clear_pending` was never called due to exit 75), the pending file is removed without re-applying. Prevents ghost-reappearance of previous responses. New test: `recover_skips_duplicate_apply`.

## 0.31.25

- **`preflight` diff-only always (preflight.rs):** `document` field is always `null` — the full document is never sent automatically. Use `agent-doc read <FILE>` to fetch on demand.
- **BREAKING CHANGE: `--diff-only` and `--with-document` flags removed from `preflight`:** Both flags removed. Diff-only is now unconditional. Any callers using either flag must remove it.
- **`agent-doc read <FILE> [--component <name>]` (read.rs):** New subcommand to fetch the full document or a single named component's body on demand. Use on the first cycle when the document is not yet in context.
- **Stash window pane check removed (preflight.rs):** `check_layout` no longer flags panes in `stash*` windows as layout issues. Stash windows hold intentional backgrounded sessions.
- **Fix: `collapsible_if` in `git.rs` (CI):** Nested `if` at line 410 collapsed to satisfy Rust 1.94.1 clippy.

## 0.31.24

- **Fix: `~~~` tilde fences protected from `❯ ` prefix normalization (write.rs):** `normalize_user_prompts_in_exchange` previously only tracked `` ``` `` (backtick) fences. Lines inside `~~~` fenced regions could incorrectly enter `user_added` and receive a `❯ ` prefix. Fixed by extracting `fence_open`/`fence_close` helpers that handle both `` ` `` and `~` fence chars with proper length tracking (matching `diff.rs`'s `fence_char`/`fence_len` approach). New test: `normalize_user_prompts_tilde_fence_interior_skipped`.

## 0.31.23

- **Fix: `❯ ` prefix normalization via IPC `fullContent` (write.rs):** When `normalize_prefix_lines` is non-empty, `try_ipc` now also sends `fullContent = content_ours` in the IPC payload (both socket and file paths). The plugin's `fullContent` path replaces the entire document, guaranteeing `❯ ` prefixes reach the editor file even when targeted string replacement fails.
- **Fix: boundary regex in `findBoundaryInComponent` + `repositionBoundaryToEnd` (PatchWatcher.kt v0.2.51):** Pattern updated from `[a-f0-9-]+` to `[a-z0-9][a-z0-9:-]*` so summary-style boundary IDs (e.g. `a0cfeb34:agent-doc-bugs`) are correctly matched.
- **Fix: boundary stripping regex in VSCode extension (extension.ts v0.2.4):** `[a-f0-9]+` → `[a-z0-9][a-z0-9:-]*` in boundary marker strip-before-replace path.
- **Regression test:** `normalize_user_prompts_restores_prefix_lost_in_file` — verifies snapshot `❯ do` is restored when editor file has bare `do`.
- **`agent-doc compact --tag <name>` (compact.rs):** Creates a lightweight git tag at HEAD before compaction as a pre-compact checkpoint. Without `--tag`, auto-generates `agent-doc/<doc-name>/pre-compact-N`. Use `--tag skip` to disable. Tagging failure is a warning, not an error.
- **`agent-doc log <FILE>` (history.rs):** Annotated git log for a session document. Walks `git log`, loads all `agent-doc/<name>/pre-compact-*` tags, and annotates matching commits in the output table (COMMIT, DATE, SUBJECT, TAG columns).
- **`agent-doc show <FILE> [--back N | --at N | --tag <name>]` (history.rs):** Shows document content at a specific point in git history. `--back N` maps to `HEAD~N`; `--at N` selects the Nth commit in log order (0 = newest); `--tag <name>` resolves the tag to its commit.
- **`agent-doc diff <FILE> --from <ref> [--to <ref>]` (history.rs):** Shows a unified diff of the document between two git refs. `--to` defaults to `HEAD`. Without `--from`, falls back to the existing live diff behavior.

## 0.31.22

- **Fix: quoted strings skip `❯ ` prefix normalization (write.rs):** `normalize_user_prompts_in_exchange` now excludes lines starting with `"` from `❯ ` prefix tagging. Previously, user-written quoted strings (e.g., `"Merge conflict with external write"`) were incorrectly tagged as terminal prompts. New test: `normalize_user_prompts_quoted_string_skipped`.

## 0.31.21

- **Fix overeager `❯ ` prefix on agent response lines (write.rs):** `normalize_user_prompts_in_exchange` now takes a `baseline` parameter. User-added lines are identified by diffing `snapshot → baseline` (not `snapshot → content_ours user_region`). After `apply_patches_with_overrides`, the boundary moves to the end of exchange — so content_ours' "user region" incorrectly included agent response lines. The fix diffs against baseline (pre-agent state), ensuring only genuine user additions get `❯ `. New regression test: `normalize_user_prompts_agent_response_not_prefixed`.

## 0.31.20

- **`❯ ` prefix normalization for exchange user prompts (write.rs):** After each agent cycle, new user-typed lines in `patch=append` exchange components are prefixed with `❯ ` to visually distinguish user input from agent responses. Implemented via `similar` diff of snapshot vs `content_ours`; only Insert lines before the boundary marker are prefixed. `normalize_user_prompts_in_exchange()` and `extract_normalization_targets()` added. 6 tests.
- **IPC-side prefix normalization (write.rs + PatchWatcher.kt v0.2.49):** `try_ipc` passes `normalize_prefix_lines: Option<&[String]>` in the IPC payload. JetBrains plugin applies `normalizeExchangePrefixes()` targeting only the user region (before `<!-- agent:boundary:UUID -->`) via targeted text replacement. Both Document API and VFS paths updated.
- **SKILL.md rule: never echo user input in patch:exchange (SKILL.md):** For `patch=append` exchange components, the patch must contain only new agent response content — echoing user input creates duplicates.

## 0.31.19

- **AGENT_PROCESSES guard on wrong-session recovery (route.rs):** `is_agent_process()` helper added. Wrong-session recovery path now skips `stash_pane`+`rescue_from_stash` for panes running non-agent processes (corky, shells, etc.) — falls through to auto-start instead. Prevents corky/foreign panes from being dragged across tmux sessions.
- **AGENT_PROCESSES guard on lazy claim Strategy 2 (route.rs):** `find_target_pane()` result is now gated by `is_agent_process()` — panes running non-agent processes are not claimed. Prevents corky from being registered as the owner of a document pane.
- **`resync --fix --session <target>` (resync.rs + main.rs):** `WrongSession` fix now supports `--session <name>` to relocate panes via `join-pane` instead of killing them. `apply_fixes_to_registry` takes `relocate_session: Option<&str>`. Falls back to deregister if no active pane found in target session.

## 0.31.18

- **Partial compact `--keep N` (compact.rs):** `agent-doc compact <FILE> --keep N` archives only exchanges older than the last N `### Re:` sections, preserving recent context. `parse_topic_sections()` helper added; 4 new tests.
- **Slash command dispatch from diff (diff.rs + preflight.rs):** `parse_slash_commands(diff)` extracts slash commands from user-added lines; preflight returns them in `slash_commands[]`; the SKILL executes each before responding. Guards: code fences, blockquotes, non-added/removed lines excluded.
- **Dedupe stale patch cleanup (dedupe.rs):** After removing duplicate blocks, deletes `.agent-doc/patches/<hash>.json` to prevent `processPendingPatches()` from re-applying removed content on next plugin startup.
- **JB plugin startup dedup guard (PatchWatcher.kt v0.2.48):** Before applying a pending patch file, compares snapshot mtime against patch file mtime. If snapshot is newer, the patch was already applied — deletes stale file and skips. Replaces the incorrect boundary-ID check from v0.2.47.
- **Cross-session pane swap fix (route.rs + sync.rs):** `rescue_from_stash()` now checks pane session before swap; uses `join-pane` for cross-session panes. Session-drift detection added to `check_layout()` in preflight.
- **PromptPoller FFI CRDT merge (editors/jetbrains):** FFI-based CRDT merge, fix unnecessary reload, preserve edits on conflict.
- **SPEC.md §7.26 + §7.28 updated:** preflight JSON now documents `slash_commands[]`; dedupe documents stale patch file cleanup.

## 0.31.17

- **CRDT duplicate bug fix (write.rs):** When boundary-synthesis consumed unmatched content into a patch, the IPC payload also sent the same content as `"unmatched"` — the plugin applied both, producing duplicates. Fixed by clearing `effective_unmatched` to `""` when synthesis occurred, on both socket and file IPC paths.
- **Write-time dedup (write.rs):** `build_ipc_patches_json` now checks if the unmatched content already exists in the target component before synthesizing a patch. Skips synthesis if a match is found, making writes idempotent.
- **SKILL.md demoted (SKILL.md):** `<!-- patch:exchange -->` wrapper is now "preferred, not required" — the binary correctly handles both wrapped and raw content paths.
- **3 new tests (write.rs):** `synthesis_dedup_skips_when_content_already_present`, `synthesis_proceeds_when_content_is_new`, `effective_unmatched_cleared_when_synthesis_consumes_content`.

## 0.31.16

- **Extreme drift snapshot re-sync (git.rs):** When `commit()` detects file is >5x larger than snapshot (typical of file move/rename), automatically re-syncs snapshot from file content. Prevents the drift loop that caused "externally saved" dialogs and lost keystrokes after renaming files.
- **Claim auto-scaffold (claim.rs):** Empty `.md` files get the full template (UUID + format + crdt + components) when claimed. Previously only wrote `agent_doc_session`, causing scaffolding to skip (no format detected).

## 0.31.15

- **Transfer auto-init (extract.rs):** `agent-doc transfer` auto-creates the target file in template mode if it doesn't exist. Creates parent dirs, generates UUID session, copies agent name from source. Always defaults to template format.
- **Write silent-drop warnings (write.rs):** `run_stream` warns when file has no template components but receives unmatched content. `try_ipc` logs `ipc_unmatched_content_dropped` to ops.log. Improved ops.log to include `ipc_patches` count alongside original `patches` count.
- **Investigation runbook:** New `runbooks/investigate-behavior.md` for debugging agent-doc behavior (ops.log, git history, affected files, common failure patterns).

## 0.31.14

- **Binding invariant enforcement (claim.rs):** When target pane is already claimed by another document, `claim` now provisions a new pane instead of erroring. Enforces SPEC §8.5: "never commandeer another document's pane."
- **Sync auto-scaffold (sync.rs):** Empty `.md` files in editor layout are automatically scaffolded with template frontmatter + status/exchange/pending components. Scaffold is saved as snapshot and committed to git immediately.
- **Transfer pending merge (extract.rs):** `agent-doc transfer` now automatically transfers the `pending` component alongside the named component. Source pending is cleared after merge.
- **SPEC.md updates:** §7.10 (claim provisions on occupied pane), §8.5 (empty file auto-scaffold in initialization step).
- **Tests:** 6 sync scaffold tests (positive + negative), 2 pending merge tests. 458 total.
- **Runbook:** `code-enforced-directives.md` — behavioral invariants enforced by binary, not agent instructions.

## 0.31.13

- **Diff-type classification (P1)**: `classify_diff()` classifies user diffs into 7 types (Approval, SimpleQuestion, BoundaryArtifact, Annotation, StructuralChange, MultiTopic, ContentAddition). Wired into preflight JSON as `diff_type` + `diff_type_reason`. 13 tests.
- **Annotated diff format (P3)**: `annotate_diff()` transforms unified diffs into `[agent]`/`[user+]`/`[user-]`/`[user~]` format. Wired into preflight JSON as `annotated_diff`. 5 tests.
- **Content-source annotation sidecar (P4)**: New `agent-doc annotate` command generates `.agent-doc/annotations/<hash>.json` mapping each line to agent/user source. SHA256 cache invalidation. GC integration. 6 tests.
- **Reproducible operation logs (P5)**: New `.agent-doc/logs/cycles.jsonl` with structured JSONL entries (op, file, timestamp, commit_hash, snapshot_hash, file_hash). Wired into all write paths + git commit. 2 tests.
- **Post-preflight eval diffs (P2)**: Moved `strip_comments` to `component.rs` (shared between binary and eval-runner). eval-runner preprocesses diffs with comment stripping.
- **Transfer-source metadata**: `PatchBlock` now supports `attrs` field. `<!-- patch:name key=value -->` attributes parsed and preserved. 3 tests.
- **JB plugin Gson migration**: Replaced hand-rolled JSON parser with `com.google.gson.JsonParser`. Fixes `\\n` unescape ordering bug. Plugin v0.2.44.
- **SKILL.md enhancements**: Diff-type routing (0b), multi-topic `---` separators (0c), process discipline clarification.
- **Domain ontology**: Interaction Model section in README.md (Directive, Cycle, Diff, Annotation). `directive.md` kernel node.
- **Module-harness**: New `ontology-references` runbook for cross-referencing domain ontology in module specs.

## 0.31.12

- **Refactor `ensure_initialized()`**: Split into 3 focused functions: `ensure_session_uuid()`, `ensure_snapshot()`, `ensure_git_tracked()`. Composite `ensure_initialized()` calls all three.
- **Rename `auto_start_no_wait()` → `provision_pane()`**: Aligns with domain ontology (Provisioning = creating a new pane + starting Claude).
- **Tests**: 8 new tests for ensure_session_uuid (3), ensure_snapshot (2), ensure_initialized (1), plus 2 helpers.

## 0.31.11

- **Sync auto-initialization**: `ensure_initialized()` now called in sync's `resolve_file`. Files with `agent_doc_format` but no session UUID get one assigned automatically on editor navigation. Fixes: files created by skills (granola import) are no longer invisible to sync.
- **Binding invariant spec**: SPEC.md section 8.5 documents the pane lifecycle invariant — document drives pane resolution, never commandeers another document's pane.
- **Domain ontology**: README.md now has Document Lifecycle, Pane Lifecycle, and Integration Layer ontology tables (Binding, Reconciliation, Provisioning, Initialization).
- **Module docs**: sync.rs, claim.rs, snapshot.rs, route.rs updated with ontology terminology.

## 0.31.10

- **Auto-init for new documents**: `ensure_initialized()` in `snapshot.rs` — claim and preflight now auto-create snapshot + git baseline for files entering agent-doc. No more untracked files after import.
- **Cross-process typing detection**: FFI exports `agent_doc_is_typing_via_file` and `agent_doc_await_idle_via_file` for CLI tools running in separate processes. `is_idle` and `await_idle` now bridge to file-based indicator when untracked in-process.
- **Diff stability fix**: `wait_for_stable_content` counter now tracks consecutive stable reads across outer iterations (was resetting within each pass).
- **IPC error propagation**: `ipc_socket::send_message` now returns proper errors instead of swallowing connection/timeout failures as `Ok(None)`.
- **Template patch boundary fix**: Improved boundary marker handling in `apply_patches_with_overrides`.
- **CI/build**: `make release` target, idempotent release workflows, version-sync check in `make check`.

## 0.31.9

- **Transfer-extract runbook**: New bundled runbook for cross-file content moves (`agent-doc transfer`/`extract`). Installed via `skill install`.
- **Compact-exchange runbook update**: Added note about preserving unanswered user input during compaction.
- **SKILL.md Runbooks section**: Added runbook links to SKILL.md so the skill knows about transfer/extract/compact procedures.
- **Housekeeping**: Gitignore `.cargo/config.toml`, resolve clippy warnings, remove accidentally committed files.

## 0.31.8

- **CI fix**: Removed `path = "../tmux-router"` override from Cargo.toml. CI runners don't have the local submodule; uses crates.io dependency exclusively.

## 0.31.7

- **Stash-bounce fix**: Removed `return_stashed_panes_bulk()` from automatic `prune()` path. Active panes now stay in stash until the reconciler explicitly needs them, eliminating the stash→return→stash loop that caused visible pane bouncing.
- **Sync file lock**: Added `flock` on `.agent-doc/sync.lock` to serialize concurrent sync calls. Prevents race conditions when rapid tab switches fire overlapping syncs.
- **Route sync removal**: Removed redundant `sync::run_layout_only` from Route command dispatch and `sync_after_claim` from route.rs. The JB plugin's `EditorTabSyncListener` is now the sole authority for layout sync.
- **Diagnostic checkpoints**: Added checkpoint logging in sync (`post-repair`, `post-prune`, `pre-tmux_router`) to pinpoint pane state at key transitions.

## 0.31.6

- **Debounce fix**: Default mtime debounce increased from 500ms to 2000ms. Configurable per-document via `agent_doc_debounce` frontmatter field.
- **Structured logging**: Added `tracing` + `tracing-subscriber` + `tracing-appender`. Set `AGENT_DOC_LOG=debug` to log to `.agent-doc/logs/debug.log.<date>`. Zero overhead when unset.
- **Pre-response cleanup bug**: `clear_pending()` now deletes pre-response snapshots after successful writes. Previously accumulated indefinitely.
- **Lock file cleanup bug**: `SnapshotLock::Drop` now deletes the lock file (not just unlocks). CRDT lock acquisition cleans stale locks (>1 hour old).
- **`agent-doc gc` subcommand**: Garbage-collects orphaned files in `.agent-doc/` directories. Supports `--dry-run` and `--root` flags.
- **Auto-GC on preflight**: Runs GC once per day via `.agent-doc/gc.stamp` timestamp check.
- **Cleanup runbook**: New `runbooks/cleanup.md` documenting `.agent-doc/` directory structure and cleanup rules.
- **Tracing instrumentation**: `tracing::debug!` at key decision points in sync, route, layout, and resync modules.
- **Source annotations for extract/transfer**: `agent-doc extract` and `agent-doc transfer` now wrap content with `[EXTRACT from ...]` or `[TRANSFER from ...]` blockquote annotations including timestamp.
- **Post-sync session health check**: After every sync, verifies the tmux session still exists. Logs `CRITICAL` if session was destroyed.
- **Route cleanup on failure**: When route fails, only panes that the current route attempt itself created are eligible for cleanup before the error propagates. Concurrent panes from sibling documents in the same tmux window are no longer treated as orphaned cleanup candidates.

## 0.31.5

- **Commit on claim**: `agent-doc claim` now commits the file after saving the initial snapshot. Ensures the first prompt appears as a diff against a committed baseline.
- **Auto-setup untracked files**: Preflight auto-adds untracked files to git (snapshot + `git add`), so `/agent-doc` works on new files without claiming first.
- **VCS refresh after commit**: `agent-doc commit` writes a VCS refresh signal file, prompting IDEs to update their git status display.
- **Preflight `--diff-only` flag**: Omits the full document from preflight JSON output, reducing token usage by ~80% on subsequent cycles.
- **Skill-bundled runbooks**: `agent-doc skill install` now installs runbooks alongside SKILL.md at `.claude/skills/agent-doc/runbooks/`. First runbook: `compact-exchange.md`.
- **JetBrains prompt button truncation**: maxLabelLen reduced from 45 to 25 characters.
- **Debounce module**: New `src/debounce.rs` for reusable debounce logic.

## 0.31.4

- **IPC reposition simplified**: Removed file-based IPC fallback from `try_ipc_reposition_boundary`. Boundary reposition now uses socket IPC exclusively (through FFI listener callback). Non-fatal on failure.
- **Inline `max_lines=N` attribute**: Component tags support `max_lines=N` to trim content to the last N lines after patching. Precedence: inline attr > `components.toml` > unlimited. Example: `<!-- agent:exchange patch=append max_lines=50 -->`.
- **Boundary-stripping in watch hash**: `hash_content()` strips boundary markers before hashing, preventing reactive-mode feedback loops where boundary repositions trigger infinite re-runs.
- **Console component scaffolding**: `agent-doc claim` now scaffolds a `<!-- agent:console -->` component for template-mode documents.
- **HEAD marker cleanup**: `git.rs` strips stray `(HEAD)` markers from working tree after commit (defensive cleanup).
- **StreamConfig max_lines**: `agent_doc_stream.max_lines` frontmatter field limits console capture lines (default: 50).
- **Tests**: 612 total. New: 4 `max_lines_*` tests in template.rs.
- **Docs**: SPEC.md, README.md, CLAUDE.md updated for max_lines and socket-only IPC.

## 0.31.3

- **Claim snapshot fix**: `agent-doc claim` now saves the initial snapshot with empty exchange content. Existing user text in the exchange becomes a diff on the next run, preventing unresponded prompts from being absorbed into the baseline.
- **Tests**: 608 total. New: `strip_exchange_content_removes_user_text`, `strip_exchange_content_preserves_no_exchange`.

## 0.31.2

- **`agent-doc dedupe`**: New command removes consecutive duplicate response blocks. Ignores boundary markers in comparison. Used to fix duplicate responses caused by watch daemon race conditions.
- **Write-origin tracing**: `--origin` flag on `agent-doc write` logs the write source (skill/watch/stream) to ops.log. Aids diagnosis when snapshot drift occurs.
- **Commit drift warning**: Warns when `file_len - snap_len > 100` bytes, indicating a possible out-of-band write that bypassed the snapshot pipeline.
- **Watch daemon busy guard**: Skips files with active agent-doc operations (`is_busy()` check), preventing the watch daemon from generating duplicate responses when competing with the skill.
- **PatchWatcher EDT fix**: Patch computation moved outside `WriteCommandAction`. No-op patches skip the write action entirely, eliminating EDT blocking and typing lag.
- **ClaimAction claim+sync**: `Ctrl+Shift+Alt+C` now calls `agent-doc claim` on the focused file before syncing, handling unclaimed/empty files.
- **Single-char truncation fix**: Single characters are treated as potentially truncated in `looks_truncated()`, requiring 1.5s stability check. Prevents partial typing (e.g., "S" from "Save as a draft.") from triggering premature runs.
- **SKILL.md**: All write examples include `--origin skill`. Version 0.31.2.
- **JetBrains plugin**: Version 0.2.40.
- **Tests**: 606 total. New: `truncated_single_chars`, `dedupe_*` (4 tests).
- **Docs**: SPEC.md §7.22 (--origin), §7.23 (busy guard), §7.28 (dedupe). CLAUDE.md module layout.

## 0.31.1

- **Declarative layout sync**: Navigating to a file in a split editor now creates a tmux pane automatically. Files with session UUIDs are always treated as Registered by sync, even without a registry entry (reverses 0.31.0 Unmanaged guard). Auto-start phase also no longer requires registry entries.
- **ClaimAction simplified**: JetBrains ClaimAction (Ctrl+Shift+Alt+C) now delegates entirely to SyncLayoutAction — removed 200+ lines of position detection, pane ID extraction, and independent auto-start logic.
- **Claim registry protection**: `agent-doc claim` refuses to overwrite an existing live claim without `--force`, preventing silent pane corruption from fallback position detection.
- **HEAD marker duplicate fix**: `add_head_marker` uses occurrence counting instead of substring matching, correctly marking new headings even when the same heading text exists earlier in the document.
- **Busy guard removed**: EditorTabSyncListener no longer blocks sync when any visible file has an active session. The binary's own concurrency guards (startup locks, registry locks) are sufficient.
- **Build stamp**: New `build.rs` embeds a build timestamp. On sync, the binary compares against `.agent-doc/build.stamp` and clears stale startup locks on new build detection.
- **Plugin binary resolution fix**: EditorTabSyncListener and SyncLayoutAction now pass `basePath` to `resolveAgentDoc()`, correctly resolving `.bin/agent-doc` instead of falling through to `~/.cargo/bin/agent-doc`.
- **JetBrains plugin**: Version 0.2.38. Requires uninstall→restart→install→restart (structural class changes).
- **Tests**: 602 total. New: `add_head_marker_duplicate_heading_text`.
- **Docs**: SPEC.md §7.10 (claim protection), §7.15 (occurrence counting), §7.20 (UUID-always-registered, build stamp). Ontology claim.md updated.

## 0.31.0

- **`agent-doc session` CLI**: Show/set configured tmux session with pane migration (`session_cmd.rs`).
- **Stash pane safety**: `purge_unregistered_stash_panes` no longer kills agent processes (agent-doc, claude, node) in stash — only idle shells. Prevents loss of active Claude sessions when registry goes stale.
- **Session resolution consolidation**: `resolve_target_session()` extracts duplicated session-targeting logic from route.rs into a single function. Config.toml is the source of truth; claim/route no longer auto-overwrite it.
- **Stale UUID handling**: Files with frontmatter session UUID but no registry entry are treated as Unmanaged by sync — prevents auto-starting sessions for unclaimed files.
- **Unused variable cleanup**: Fixed 8 warnings across route.rs and template.rs.
- **Docs**: SPEC.md §7.27 (session command), CLAUDE.md module layout updated.
- **Tests**: 601 total, 1 new (`purge_preserves_unregistered_agent_process_in_stash`).

## 0.30.1

- **FFI `agent_doc_is_idle`**: Non-blocking typing check for editor plugins to query idle state before boundary reposition.
- **JetBrains plugin typing debounce**: Boundary reposition deferred until typing stops, using FFI idle check.
- **VS Code koffi FFI bindings**: `native.ts` with koffi-based native bindings for the shared FFI library.
- **VS Code reposition boundary handling**: Boundary reposition with typing debounce via FFI idle check.
- **tmux_session config drift fix**: `route.rs` follows pane session, `claim.rs` updates config to match.
- **2 new FFI tests**: Coverage for `agent_doc_is_idle` and related FFI surface.
- **Dependencies**: `tmux-router` v0.3.8.

## 0.30.0

- **Stale baseline guard (component-aware)**: `is_stale_baseline()` now parses components and only checks append-mode (exchange, findings). Replace-mode components (status, pending) are skipped. Falls back to prefix check for inline docs. 11 new tests.
- **Busy pane guard**: `SyncOptions.protect_pane` callback in tmux-router DETACH phase + `layout.rs`. Prevents stashing panes with active agent-doc/claude sessions during layout changes.
- **Auto-start startup lock**: `.agent-doc/starting/<hash>.lock` with 5s TTL prevents double-spawn when sync fires twice in quick succession.
- **Bug 2A fix**: IPC snapshot save failure after successful write is now non-fatal with warning. Commit auto-recovers via divergence detection.
- **Bug 2B fix**: Removed commit-time divergence detection that was eating user edits into the snapshot.
- **Hook system**: `agent-doc hook fire/poll/listen/gc` CLI. Cross-session event coordination via `agent-kit` hooks (v0.3). `post_write` and `post_commit` events fired from write + commit paths.
- **HookTransport trait**: Abstract delivery mechanism with `FileTransport`, `SocketTransport`, `ChainTransport` implementations.
- **Ops logging tests**: 2 new tests for `.agent-doc/logs/ops.log`.
- **Dependencies**: `agent-kit` v0.3 (hooks feature), `tmux-router` v0.3.7 (SyncOptions).
- **Docs**: SPEC.md §6.6/§7.9/§7.20/§9.5, README.md key features, CLAUDE.md module layout.
- **Tests**: 595 total (16 new), 0 failures.

## 0.29.0

- **Links frontmatter**: Renamed `related_docs` → `links` (backward-compat alias). URL links (`http://`/`https://`) are fetched via `ureq`, converted HTML→markdown via `htmd` (stripping script/style/nav/footer), cached in `.agent-doc/links_cache/`, and diffed on each preflight. Non-HTML content passes through unchanged.
- **Session logging**: Persistent logs at `.agent-doc/logs/<session-uuid>.log` with timestamped events for session start, claude start/restart/exit, user quit, and session end.
- **Auto-trigger on restart**: After `--continue` restart, background thread sends `/agent-doc <file>` via `tmux send-keys` after 5s delay to re-trigger the skill workflow.
- **Security documentation**: README.md top-level security notice + detailed Security section. SPEC.md Section 10 with threat model, known risks, and recommendations.
- **New dependency**: `htmd` v0.5.3 (HTML-to-markdown, ~13 new crates from html5ever ecosystem, no HTTP server).
- **Tests**: 7 new tests for URL detection, HTML conversion, boilerplate stripping, cache paths. 361 total, 0 failures.

## 0.28.3

- **Write dedup boundary fix**: Strip `<!-- agent:boundary:XXXXXXXX -->` markers before dedup comparison. Boundary marker IDs change on each write, causing false negatives in the dedup check (content appeared different when only the boundary ID changed).

## 0.28.2

- **Write dedup**: All 4 write paths (`run`, `run_template`, `run_stream` disk, `run_stream` IPC) skip the write when merged content is identical to the current file. Dedup events logged to `/tmp/agent-doc-write-dedup.log` with backtrace.
- **Pane ownership verification**: `verify_pane_ownership()` called at entry of `run`, `run_template`, `run_stream`. Rejects writes when a different tmux pane owns the session (lenient — passes silently when not in tmux or pane is indeterminate).
- **Column memory**: `.agent-doc/last_layout.json` saves column→agent-doc mapping (carried from v0.28.1, now documented).

## 0.28.1

- **Column memory**: `.agent-doc/last_layout.json` saves column→agent-doc mapping. When a column has no agent doc, sync substitutes the last known agent doc from the state file. Preserves 2 tmux panes when one column switches to a non-agent file.

## 0.28.0

- **Empty col_args filtering**: `sync` now filters out empty strings from `col_args` before processing. Fixes phantom empty columns sent by the JetBrains plugin during rapid editor split changes.
- **Sync debug logging**: Added `/tmp/agent-doc-sync.log` trace logging at key sync decision points (col_args, repair_layout, auto-start, pre/post tmux_router::sync pane counts).
- **Post-auto_start stash removed**: The explicit stash after auto-start is no longer needed — `tmux_router::sync` always runs the full reconcile path (no early exits), so excess panes are stashed during the DETACH phase.
- **tmux-router v0.3.6**: Early exits removed from `sync` — the full reconcile path now runs for 0, 1, or 2+ resolved panes uniformly. Previous early exits for `resolved < 2` bypassed the DETACH phase, leaving orphaned panes from previous layouts visible.
- **JetBrains plugin v0.2.36**: Filter empty columns in SyncLayoutAction.kt

## 0.27.9

- **tmux-router v0.3.5**: Updated dependency — trace logging at key sync decision points + early-exit stash removal (preserves previous-column panes)

## 0.27.8

- **tmux-router v0.3.4**: Updated dependency — early-exit stash now derives session from pane via `pane_session()` instead of dead `doc_tmux_session` path
- **VERSIONS.md backfill**: Added entries for v0.23.2 through v0.26.6

## 0.27.7

- **Sync path column-aware split**: `auto_start_no_wait` now accepts `col_args` and computes `split_before` via `is_first_column()`. Previously hardcoded `split_before = false`, causing new panes to always split alongside the rightmost pane regardless of column position. The sync path (editor tab switches) now matches the route path behavior.

## 0.27.6

- **Bold-text pseudo-header fallback for `(HEAD)` marker**: `add_head_marker()` in `git.rs` now falls back to bold-text lines (`**...**`) when no markdown headings are found in new content. `strip_head_markers()` also handles stripping `(HEAD)` from bold-text lines.
- **SKILL.md header format guidance**: Added "Response header format (template mode)" section instructing agents to use `### Re:` headers. Bold-text pseudo-headers are supported as a fallback but real headings are preferred for outline visibility and sub-section nesting.

## 0.27.5

- **Column-aware split target**: `auto_start_in_session` picks the split target based on column position — first pane (leftmost) for left-column files, last pane (rightmost) for right-column files. Fixes 3-pane layout bug where new panes split the wrong existing pane.
- **Early-exit stash**: Before the `resolved < 2` early return in `tmux-router::sync`, excess panes in the agent-doc window are now stashed. Previously, old panes from previous layouts stayed visible when only one file resolved.
- **tmux-router v0.3.3**: Published with the early-exit stash fix.

## 0.27.4

- **Rescue stashed panes in sync**: `sync.rs` now rescues stashed panes back to the agent-doc window via swap-pane/join-pane before falling back to auto-start. Preserves Claude session context across editor tab switches.

## 0.27.3

- **Revert auto-kill**: Reverts v0.27.2 auto-kill of idle stashed Claude sessions. The `❯` prompt is the normal state of a stashed session waiting to be rescued — not an orphan indicator.

## 0.27.2

- **Auto-kill idle stashed Claude sessions**: Added auto-cleanup in `return_stashed_panes_bulk()` for stashed panes running agent-doc/claude at the `❯` prompt with no return target. (Reverted in v0.27.3 — too aggressive, killed active sessions.)

## 0.27.1

- **Fix "externally modified" popup**: Removed stale boundary disk write that caused spurious file modification notifications in editors.

## 0.27.0

- **Fix stash rescue deregistration**: Fixed pane deregistration during stash rescue operations.
- **Socket IPC**: Added `ipc_socket` module using Unix domain sockets via the `interprocess` crate for direct binary-to-plugin communication.
- **Bulk resync**: `return_stashed_panes_bulk()` for batch stash rescue operations.

## 0.26.6

- **FFI sync lock/debounce**: Added `agent_doc_sync_try_lock`/`unlock` FFI exports for cross-editor concurrency control. Added `agent_doc_sync_bump`/`check_generation` for cross-editor event coalescing.
- **Layout debounce fix**: `LayoutChangeDetector` uses generation counter instead of spawning concurrent threads per event.
- **JetBrains plugin v0.2.35**: Uses FFI sync primitives with local fallback.

## 0.26.5

- **Skip no-op IPC reposition**: IPC reposition signal skipped when boundary position is unchanged, eliminating ~64% of no-op PatchWatcher operations.
- **Handle inotify overflow**: PatchWatcher scans for missed files on inotify OVERFLOW events.
- **CI: crates.io-only dependencies**: All path dependencies (instruction-files, tmux-router, agent-kit, module-harness, existence) replaced with crates.io versions in CI workflows.

## 0.26.4

- **Prompt detection for Claude Code v2.1+**: Support numbered list format (`N. label`) in prompt option parsing alongside bracket format (`[N] label`).
- **Auto-start PromptPoller**: Plugin auto-starts PromptPoller on project open.
- **JetBrains plugin v0.2.32**: PromptPoller auto-start, `.bin/` path resolution, diagnostic logging.

## 0.26.3

- **Sync no longer auto-inits frontmatter**: Sync returns `Unmanaged` for files without session UUIDs; only `claim` adds frontmatter now.
- **Plugin mixed-layout sync**: Uses focus-only when non-`.md` files are in editor splits, preventing stashing.
- **JetBrains plugin v0.2.25**: Alt+Space popup, removed ActionPromoter (frees Alt+Enter for native JetBrains intentions).

## 0.26.2

- **Route single exit point**: Refactored route to `resolve_or_create_pane()` eliminating propagation bugs. `sync_after_claim` now runs on ALL route paths.
- **Response status signals**: File-based status signals (`.agent-doc/status/<hash>`) for cross-process visibility. FFI: `set_status`/`get_status`/`is_busy` for in-process plugin checks.
- **Auto-init unclaimed files in sync**: Sync writes session UUID for unclaimed files.
- **`agent_doc_version()` FFI export**: Runtime version tracking for plugins.
- **JetBrains plugin v0.2.24**: `is_busy()` guard in `EditorTabSyncListener` + `TerminalUtil`.

## 0.26.1

- **Sync layout authority**: `sync_after_claim` uses editor-provided `col_args`, preventing 3-pane layout regression on file switch.
- **Clippy fixes**: `doc_lazy_continuation` fixes in sync.rs, upgrade.rs. Unused variable fix in tmux-router `break_pane_to_stash`.
- **SPEC.md updates**: Added sections on project config, IPC write verification, and sync layout authority.

## 0.26.0

- **Kill pane safety**: `kill_pane` refuses to destroy a session's last window (tmux-router v0.3.0).
- **IPC verification**: Content verification catches partial plugin application failures. `--force-disk` cleans stale patches to prevent double-writes.
- **Module harness context**: All 53+ modules annotated with Spec/Contracts/Evals doc comments (468 named evals, 68% coverage).
- **Existence-lang ontology**: 9 domain terms defined (Document, Session, Component, Boundary, Snapshot, Patch, Exchange, Route, Claim). Dev dependencies: existence v0.4.0, module-harness v0.2.0.
- **README rewrite**: Concise GitHub-facing guide.

## 0.25.15

- **Sync layout repair**: Added `repair_layout()` to fix window index mismatches (agent-doc window not at index 0). Sync tests added for repair skip and move scenarios.
- **Blank line collapse on tmux_session strip**: Collapsing 3+ consecutive newlines to 2 when stripping deprecated `tmux_session` frontmatter field.

## 0.25.14

- **Sync pane repair**: Window index repair, pane state reconciliation, effective window tracking.
- **Resync enhancements**: Enhanced dead pane detection and session validation.
- **Route improvements**: Improved command routing logic.

## 0.25.13

- **Install script**: Rewritten `install.sh` with platform detection and improved install paths.
- **Homebrew formula**: Added `Formula/agent-doc.rb` for macOS/Linux Homebrew installation.
- **Deprecate `tmux_session` frontmatter**: Sync strips the field on encounter instead of repairing it. Route `auto_start` no longer attempts repair.

## 0.25.12

- **Sync swap-pane atomic reconcile**: `context_session` overrides frontmatter `tmux_session`, auto-repairs on mismatch.
- **Visible-window split**: New panes split in the visible agent-doc window instead of stash.
- **Resync report-only in sync**: `resync --fix` disabled in sync path to preserve cross-session panes.
- **tmux-router v0.2.9**: Swap-pane atomic transitions.

## 0.25.11

- **Tmux-router swap-pane atomic transitions**: Pane moves use `swap-pane` for flicker-free layout changes. CI fix for path dependencies (agent-kit, tmux-router).

## 0.25.10

- **Preflight mtime debounce**: 500ms idle gate before computing diff.
- **Unified diff context**: Diff output uses unified format with 5-line context radius.
- **Route `--debounce` flag**: Opt-in mtime polling for coalescing rapid editor triggers.
- **`is_tracked` FFI export**: For editor plugins to check file tracking status.
- **Sync no-wait auto-start**: `auto_start_no_wait` for non-blocking session creation during sync.
- **JetBrains plugin v0.2.21**: Sync logging improvements.

## 0.25.9

- **`is_tracked()` FFI export**: Conservative debounce on untracked files (fallback to local tracking).
- **Untracked file debounce fix**: Untracked files no longer bypass debounce.
- **JetBrains plugin v0.2.20**: `is_tracked` binding + FFI logging tags.

## 0.25.8

- **Preflight debounce**: Mtime-based 500ms idle gate before computing diff.
- **Unified diff context**: Switch diff output to unified format with 5-line context radius.
- **Route `--debounce`**: New flag for opt-in mtime polling to coalesce rapid editor triggers.
- **Truncation detection fix**: Smarter dot handling for domain fragments in `looks_truncated`.

## 0.25.7

- **Rename `submit` to `run`**: `submit.rs` renamed to `run.rs`; all internal "submit" terminology updated to "run".
- **FFI debounce module**: `document_changed()` + `await_idle()` FFI exports for editor-side debounce.
- **Route sync fix**: Route calls `sync::run_layout_only()` to prevent auto-start race conditions.
- **JetBrains plugin v0.2.19**: FFI debounce, conditional typing wait, layout-only sync.

## 0.25.6

- **Route `--col`/`--focus` args**: Declarative layout sync from the route command. Plugin `sendToTerminal` passes editor layout in a single CLI call.
- **Layout change detection**: `LayoutChangeDetector` using `ContainerListener` with 5s fallback poll in the JetBrains plugin.
- **EDT-safe threading**: Plugin uses `invokeLater` for Swing reads, background thread for CLI calls.
- **JetBrains plugin v0.2.17**.

## 0.25.5

- **FFI boundary reposition**: Export `agent_doc_reposition_boundary_to_end()` for plugin use.
- **Boundary ID summaries**: 8-char hex IDs with optional `:summary` suffix (filename stem). `new_boundary_id_with_summary()` wired into all write paths.
- **Snapshot boundary cleanup**: Commit path uses `remove_all_boundaries()`. Working tree cleaned via `clean_stale_boundaries_in_working_tree()` on commit.
- **JetBrains plugin v0.2.14**: FFI-first reposition with Kotlin fallback.

## 0.25.4

- **Boundary accumulation fix**: Plugin `repositionBoundaryToEnd` removes ALL boundaries, not just the last one.
- **Short boundary IDs**: 8 hex chars instead of full UUID (centralized in `lib.rs`).
- **Autoclaim pruning**: Validate file existence, prune stale entries on rename/delete.
- **Sync stale pane detection**: Detect alive panes with non-existent registered files (rename), kill stale pane and auto-start new session.

## 0.25.3

- **Fix IPC boundary reposition for prompt ordering**: All IPC write paths call `reposition_boundary_to_end()` before extracting boundary IDs. Previously the stale boundary position caused responses to appear before the prompt.

## 0.25.2

- **Fix skill install superproject root resolution**: Added `resolve_root()` to detect git superproject when CWD is in a submodule. `skill install`/`check` now writes to the project root, not the submodule's `.claude/skills/`.

## 0.25.1

- **IPC boundary reposition from commit**: After committing, send an IPC reposition signal to the plugin so it moves the boundary marker to end-of-exchange in its Document buffer. Avoids writing to the working tree (which would lose user keystrokes).

## 0.25.0

- **`agent-doc preflight` command**: Consolidated pre-agent command (recover + commit + claims + diff + document read) returning JSON for skill consumption.
- **Boundary reposition fix**: Snapshot-only reposition prevents losing user input; no working tree writes during reposition.
- **CRDT merge simplification**: Removed `reorder_agent_before_human()`, deterministic client IDs.
- **Pulldown-cmark outline**: CommonMark-compliant heading parser for outline.
- **Plugin boundary reposition via IPC**: `reposition_boundary: true` flag in IPC payloads.
- **Stash window routing**: Target largest pane, overflow to stash windows.
- **JetBrains plugin v0.2.12**: Plugin-side boundary reposition.

## 0.24.4

- **Deterministic boundary re-insertion in `apply_patches`**: Binary handles boundary re-insertion after checkpoint writes, removing the need for SKILL.md to manually re-insert boundaries.

## 0.24.3

- **Context session for auto_start**: Pass context session to `auto_start` to prevent routing to the wrong tmux session. Post-sync resync for consistency.

## 0.24.2

- **SKILL.md step 3b**: Added mandatory pending updates check each cycle.
- **`plugin install --local`**: Install JetBrains/VS Code plugins from local build directory.
- **JetBrains plugin v0.2.10**: `resync --fix` on startup.
- **JetBrains plugin v0.2.9**: VCS refresh signal fix (ENTRY_MODIFY event).

## 0.24.1

- **SKILL.md heredoc examples**: Updated bundled SKILL.md with heredoc examples for the write command.

## 0.24.0

- **`agent-doc install` command**: System-level setup that checks prerequisites (tmux, claude) and detects/installs editor plugins.
- **`agent-doc init` project mode**: No-arg `init` now initializes a project (creates `.agent-doc/` directory structure, installs SKILL.md) instead of requiring a file argument.
- **SKILL.md content tests**: CLI integration tests for skill install/check content verification.
- **Sync pane guard**: Pre-sync alive pane check prevents duplicate session creation.

## 0.23.3

- **Cross-platform sync pane guard**: `find_alive_pane_for_file()` uses `ps(1)` instead of `/proc` for Linux+macOS compatibility. Pre-sync auto-start checks alive panes before creating duplicates.
- **Clippy fixes**: Fix `collapsible_if` warnings in template.rs, git.rs, terminal.rs. Suppress `dead_code` warnings for library-only boundary functions.

## 0.23.2

- **Explicit patch boundary-aware insertion**: `apply_patches_with_overrides()` checks for boundary markers when applying explicit patch blocks in append mode, not just unmatched content. Prevents boundary markers from accumulating as orphans.
- **Version bump**: Includes all v0.23.1 fixes (IPC snapshot, HEAD marker cleanup, boundary insertion).

## 0.23.1

- **Boundary-aware insertion for unmatched content**: `apply_patches_with_overrides()` now uses boundary-aware insertion for both explicit append-mode patches and unmatched content routed to `exchange`/`output`. Previously only explicit patches used boundary markers; unmatched content used plain append.
- **IPC snapshot correctness**: `try_ipc()` now accepts a `content_ours` parameter (baseline + response, without user concurrent edits). On IPC success the snapshot is saved from `content_ours` instead of re-reading the current file, preventing user edits typed after the boundary from being absorbed into the snapshot.
- **IPC synthesized exchange patch**: When no explicit patches exist but unmatched content targets `exchange`/`output` and a boundary marker is present, `try_ipc()` synthesizes a boundary-aware component patch so the plugin inserts at the correct position.
- **`boundary.insert()` cleans stale markers**: Before inserting a new boundary marker, `insert()` strips all existing boundary markers from the document. Prevents orphaned markers accumulating across interrupted sessions.
- **`boundary::find_boundary_id_in_component()`**: New public function. Scans a pre-parsed `Component` for any boundary marker UUID, skipping matches inside code blocks. Used by `template.rs` and external callers without re-parsing components.
- **Post-commit working tree cleanup**: After `git.commit()` succeeds, `strip_head_markers()` is applied to both the snapshot and the working tree file. Ensures `(HEAD)` markers never appear in the editor — they exist only in the committed version (creating the blue gutter diff).

## 0.23.0

- **Boundary marker for response ordering**: New `agent-doc boundary <FILE>` command inserts `<!-- agent:boundary:UUID -->` at the end of append-mode component content. The marker acts as a physical anchor — responses are inserted at the marker position, ensuring correct ordering when the user types while a response is being generated. Replaces the fragile caret-offset approach.
- **Boundary-aware FFI**: New `agent_doc_apply_patch_with_boundary()` C ABI export. JetBrains plugin (`NativeLib.kt`, `PatchWatcher.kt`) uses boundary markers with priority over caret-aware insertion.
- **Component parser: boundary marker exclusion**: `<!-- agent:boundary:* -->` comments are now skipped by the component parser (no longer cause "invalid component name" errors).
- **IPC boundary_id**: All IPC patch JSON payloads include `boundary_id` when a boundary marker is present in the target component.
- **SKILL.md: boundary marker step**: Updated bundled SKILL.md to call `agent-doc boundary <FILE>` after reading the document (step 1b).
- **Claim auto-start**: JetBrains plugin "Claim for Tmux Pane" action now auto-starts the agent session after successful claim.
- **JetBrains plugin v0.2.8**: Boundary-aware patching + claim auto-start.

## 0.22.2

- **SKILL.md: immediate commit after write**: Updated bundled SKILL.md to call `agent-doc commit` right after `agent-doc write`, replacing the old "Do NOT commit after writing" instruction. All sessions get the new behavior after `agent-doc skill install`.
- **Plugin default modes**: `exchange` and `findings` components now default to `append` mode in the JetBrains plugin (matching the Rust binary's `default_mode()`), so `<!-- agent:exchange -->` works without explicit `patch=append`.

## 0.22.1

- **Any-level HEAD markers**: `(HEAD)` marker now matches any heading level (`#`–`######`), not just `###`. Only root-level (shallowest) headings in the agent's appended content are marked.
- **Multi-heading markers**: When the agent response has multiple sections, ALL new root headings get `(HEAD)` markers (comparing snapshot vs git HEAD).
- **VCS refresh signal**: After `agent-doc commit`, writes `vcs-refresh.signal` to `.agent-doc/patches/`. Plugin watches for this and triggers `VcsDirtyScopeManager.markEverythingDirty()` + VFS refresh so git gutter updates immediately.
- **JetBrains plugin v0.2.7**: VCS refresh signal handling, cursor-aware FFI, VFS refresh before dirty scope.

## 0.22.0

- **`agent-doc terminal` subcommand**: Cross-platform terminal launch from editor plugins. Config-first (no hard-coded terminal list): `[terminal] command` in `config.toml` with `{tmux_command}` placeholder. Fallback to `$TERMINAL` env var. Detects stale frontmatter sessions and scans registry for live panes.
- **Selective commit**: `agent-doc commit` stages only the snapshot content via `git hash-object` + `git update-index`, leaving user edits in the working tree as uncommitted. Agent response → committed (no gutter). User input → uncommitted (green gutter).
- **HEAD marker**: Committed version of the last `### ` heading gets ` (HEAD)` suffix, creating a single modified-line gutter as a visual boundary and navigation point.
- **First-submit snapshot fix**: When no snapshot exists and git HEAD content matches the current file, treat as first submit (entire file is the diff) instead of "no changes detected".
- **Cursor-aware FFI**: `agent_doc_apply_patch_with_caret()` in shared library — inserts append-mode patches before the cursor position. `Component::append_with_caret()` in `component.rs`. JNA binding in `NativeLib.kt`.
- **JetBrains plugin v0.2.7**: Cursor-aware append ordering via native FFI with Kotlin fallback. Captures caret offset from `TextEditor` before `WriteCommandAction`.

## 0.21.0

- **`agent-doc parallel` subcommand**: Fan-out parallel Claude sessions across isolated git worktrees. Each subtask gets its own worktree and tmux pane. Results collected as markdown with diffs. `--no-worktree` for read-only tasks.
- **CRDT post-merge reorder**: Agent content ordered before human content at append boundary using Yrs per-character attribution (`Text::diff` with `YChange::identity`).
- **README**: Added parallel fan-out documentation section.

## 0.20.3

- **`agent-doc claims` subcommand**: Read, print, and truncate `.agent-doc/claims.log` in a single binary call. Replaces the shell one-liner (`cat + truncate`) that was prone to zombie process accumulation when the Bash tool auto-backgrounded it.

## 0.20.2

- **Fix: numeric session name ambiguity** (tmux-router v0.2.8): `new_window()` now appends `:` to session name (`-t "0:"` instead of `-t "0"`). Without the colon, tmux interprets numeric names as window indices, creating windows in the wrong session. Root cause of persistent session 1 bleedover bug.

## 0.20.1

- **Session affinity enforcement**: Route and auto_start bail with error instead of falling back to `current_tmux_session()` when `tmux_session` is set in frontmatter. Prevents pane creation in wrong tmux session.

## 0.20.0

- **CRDT conservative dedup** (#15): Post-merge pass removes identical adjacent text blocks.
- **CRDT frontmatter patches** (#16): `patch:frontmatter` now applied on disk write path (was IPC-only).
- **Binary-vs-agent responsibility** documented in CLAUDE.md.

## 0.19.0

- **ExecutionMode in config.toml**: `execution_mode = "hybrid|parallel|sequential"` in global config.
- **TmuxBatch**: Command batching in tmux-router v0.2.7 — reduces flicker via `\;` separator. `select_pane()` uses batch (2 → 1 invocation).

## 0.18.1

- **Revert Gson**: Hand-written JSON parser restored in JetBrains plugin (Gson causes ClassNotFoundException).
- **H2 scaffolding**: `claim` scaffolds h2 headers before components for IDE code folding.
- **SKILL.md**: Canonical pattern documented — h2 header before every component.

## 0.18.0

- **`agent-doc undo`**: Restore document to pre-response state (one-deep).
- **`agent-doc extract`**: Move last exchange entry between documents.
- **`agent-doc transfer`**: Move entire component content between documents.
- **Pre-response snapshots**: Saved before every write for undo support.

## 0.17.30

- **Immutable session binding**: `claim` refuses to overwrite `tmux_session` unless `--force`. Prevents cross-session pane swapping.

## 0.17.29

- **JNA FFI integration**: `NativeLib.kt` JNA bindings for JetBrains plugin with Kotlin fallback.
- **`agent_doc_merge_frontmatter()`**: New FFI export for frontmatter patching.
- **`agent-doc lib-path`**: Print path to shared library for plugin discovery.
- **VS Code prepend mode**: Fixed missing `prepend` case in `applyComponentPatch()`.

## 0.17.28

- **Validate tmux_session before routing**: Guard against routing to a non-existent tmux session.

## 0.17.27

- **Plugin code-block fix**: JetBrains and VS Code plugins skip component tags inside fenced code blocks. JB plugin 0.2.4, VSCode 0.2.2.

## 0.17.26

- **PLUGIN-SPEC docs update**: Document recent plugin features in PLUGIN-SPEC.

## 0.17.25

- **Stash else-branch fix**: Fix else-branch stash logic. Use `diff --wait` for truncation detection.

## 0.17.24

- **Pulldown-cmark for code range detection**: Replace hand-rolled code span/fence parser with `pulldown-cmark` in component parser. Stash overflow panes instead of creating new windows.

## 0.17.23

- **Stash overflow fix**: Overflow panes stashed instead of creating new tmux windows.

## 0.17.22

- **UTF-8 corruption fix**: Sanitize component tags in response content before writing to prevent UTF-8 corruption in `sanitize_component_tags`.

## 0.17.21

- **Indented fenced code blocks**: Component parser skips markers inside indented fenced code blocks. Scaffold `agent:pending` in claim for template documents.

## 0.17.20

- **BREAKING CHANGE: Rename `mode` to `patch`** for inline component attributes (`patch=append|replace`). `mode=` accepted as backward-compatible alias.

## 0.17.19

- **Split-window in auto_start**: Use `split-window` instead of `new-window` for auto-started Claude sessions. Resync tests added.

## 0.17.18

- **Resync `--fix` enhancements**: Detect wrong-session panes and wrong-process registrations. Renamed `--dangerously-set-permissions` to `--dangerously-skip-permissions`.

## 0.17.17

- **Parse fix**: `parse_option_line` matches `[N]` bracket format only. Fix `find_registered_pane_in_session` lookup.

## 0.17.16

- **Cursor editor support**: Add Cursor as a supported editor. `claude_args` frontmatter field for custom CLI arguments. Tmux session routing fix. VS Code extension bumped to v0.2.1.

## 0.17.15

- **Route/sync improvements**: Routing and sync refinements for multi-session workflows.

## 0.17.14

- **Plugin IPC fix**: VS Code IPC parity with JetBrains. History command improvements. Documentation updates.

## 0.17.13

- **Fix exchange append mode**: Remove hardcoded replace override in `run_stream`, allowing exchange component to use its configured patch mode.

## 0.17.12

- **Inline component attributes**: `<!-- agent:name mode=append -->` — patch mode configurable directly on the component tag.

## 0.17.11

- **History command**: `agent-doc history` shows exchange version history from git with restore support. IPC-priority writes with `--force-disk` flag to bypass.

## 0.17.10

- **Default component scaffolding**: Auto-scaffold missing components on claim. Append-mode exchange default. Route flash notification via `tmux display-message`.

## 0.17.9

- **Fix CRDT character interleaving**: Switch to line-level diffs to prevent character-level interleaving artifacts.

## 0.17.8

- **Template parser code block awareness**: Component markers inside fenced code blocks are now skipped by the template parser.

## 0.17.7

- **Fix CWD drift**: Recover and claim commands no longer drift from the project root working directory.

## 0.17.6

- **Documentation update**: Align docs with IPC-first write architecture from v0.17.5.

## 0.17.5

- **IPC-first writes**: All write paths (`run`, `stream`, `write`) try IPC to the IDE plugin via `.agent-doc/patches/` before falling back to disk. Exit code 75 on IPC timeout.

## 0.17.4

- **Tmux pane orientation fix**: Arrange files side-by-side (horizontal split) instead of stacking vertically.

## 0.17.3

- **Fix CRDT character-level interleaving bug**: Resolve text corruption caused by character-level merge conflicts in CRDT state.

## 0.17.2

- **Fix CRDT shared prefix duplication bug**: Prevent duplicate content when CRDT documents share a common prefix.

## 0.17.1

- **Fix stream snapshot**: Use replace mode for exchange component in stream snapshot writes.

## 0.17.0

- **BREAKING CHANGE: `agent_doc_format`/`agent_doc_write` split**: Replace `agent_doc_mode` with separate format (`inline`|`template`) and write strategy (`disk`|`crdt`) fields. IPC write path for IDE plugins. Layout fix.

## 0.16.1

- **Native compact for template/stream mode**: `agent-doc compact` now works natively with template and stream mode documents.

## 0.16.0

- **Reactive stream mode**: CRDT-mode documents get zero-debounce reactive file-watching from the watch daemon. Truncation detection and CRDT stale base fix.

## 0.15.1

- **Patch release**: Version bump and minor fixes.

## 0.15.0

- **CRDT-based stream mode**: Real-time streaming output with CRDT conflict-free merge (`agent-doc stream`). Chain-of-thought support with optional `thinking_target` routing. Deferred commit workflow. Snapshot resolution prefers snapshot file over git.

## 0.14.9

- **Multi-backtick code span support**: `find_code_ranges` handles multi-backtick code spans (e.g., ` `` ` and ` ``` `).

## 0.14.8

- **Code-range awareness for strip_comments**: Fix `<!-- -->` stripping inside code spans and fenced blocks. Stash window purge for orphaned idle shells.

## 0.14.7

- **Bidirectional convert**: `agent-doc convert` works in both directions (inline <-> template). Autoclaim sync improvements.

## 0.14.6

- **Auto-sync on lazy claim**: Automatically sync tmux layout after lazy claim in route. Plugin autocomplete fixes for JetBrains.

## 0.14.5

- **`agent-doc commands` subcommand**: List available commands. Plugin autocomplete for JetBrains/VS Code. Remove auto-prune (moved to resync). Purge orphaned claude/stash tmux windows in resync.

## 0.14.4

- **Claim pane focus**: Focus the claimed pane after `agent-doc claim`. `convert` handles documents with pre-set template mode.

## 0.14.3

- **Autoclaim pane refresh**: Refresh pane info during autoclaim. Template missing-component recovery on write.

## 0.14.2

- **Skill reload via `--reload` flag**: Compact and restart skill installation in a single command.

## 0.14.1

- **SKILL.md workflow fix**: Move git commit to after write step in the skill workflow to prevent committing stale content.

## 0.14.0

- **Route focus fix + claim defaults to template mode**: New documents claimed via `agent-doc claim` default to template format. `agent-doc mode` CLI command for inspecting/changing document mode.

## 0.13.3

- **Bump tmux-router to v0.2.4**: Fix spare pane handling in tmux-router dependency.

## 0.13.2

- **Sync registers claims**: `agent-doc sync` registers claims for previously unregistered files in the layout.

## 0.13.1

- **Sync updates registry file paths**: Fix autoclaim file path tracking when sync moves files between panes.

## 0.13.0

- **Autoclaim + git-based snapshot fallback**: Automatic claim on route when no claim exists. Fall back to git for snapshot when snapshot file is missing.

## 0.12.2

- **Exchange component defaults to append mode**: The `exchange` component uses append patch mode by default instead of replace.

## 0.12.1

- **Lazy claim fallback**: `agent-doc claim` without `--pane` falls back to the active tmux pane.

## 0.12.0

- **`agent-doc convert` command**: Convert between inline and template document formats. Lazy claim support. `agent-doc compact` for git history squashing. Exchange component as default template target.

## 0.11.2

- **Strip trailing `## User` heading**: Also strip trailing `## User` heading from agent responses (complement to v0.11.1).

## 0.11.1

- **Strip duplicate `## Assistant` heading**: Remove duplicate `## Assistant` heading from agent responses when already present in the document.

## 0.11.0

- **Append-friendly merge strategy**: Improved 3-way merge strategy optimized for append-style document workflows.

## 0.10.1

- **Bundle template-mode instructions in SKILL.md**: SKILL.md now includes template-mode workflow instructions for the Claude Code skill.

## 0.10.0

- **BREAKING CHANGE: Rename `response_mode` to `agent_doc_mode`**: Frontmatter field renamed with backward-compatible aliases.

## 0.9.10

- **Code-span parser fix**: Component parser skips markers inside fenced code blocks and inline backticks. Template input/output component support.

## 0.9.9

- **Template mode + compaction recovery**: New template mode for in-place response documents using `<!-- agent:name -->` components. Durable pending response store for crash recovery during compaction.

## 0.9.8

- **Relocate advisory locks**: Move document advisory locks from project root to `.agent-doc/locks/`.

## 0.9.7

- **`agent-doc write` command**: Atomic response write-back command for use by the Claude Code skill.

## 0.9.6

- **Race condition mitigations**: Stale snapshot recovery, atomic file writes, and various race condition fixes.

## 0.9.5

- **Advisory file locking**: Lock the session registry during writes. Stale claim auto-pruning.

## 0.9.4

- **Bump tmux-router to v0.2**: Update tmux-router dependency.

## 0.9.3

- **Bump tmux-router to v0.1.3**: Fix stash window handling in tmux-router.

## 0.9.2

- **`agent-doc plugin install` CLI**: Install editor plugins from GitHub Releases. VS Code extension reaches feature parity with JetBrains.

## 0.9.1

- **Stash window resize fix**: Bump tmux-router to v0.1.2 to fix stash window resize issues.

## 0.9.0

- **Dashboard-as-document**: Component-based documents with `<!-- agent:name -->` markers, `agent-doc patch` for programmatic updates, `agent-doc watch` daemon for auto-submit on file change.

## 0.8.1

- **Auto-prune registry**: Prune dead session entries before route/sync/claim operations.

## 0.8.0

- **Tmux-router integration**: Wire `tmux-router` as a dependency for pane management. Fix `route` auto_start bug.

## 0.7.2

- **Attach-first reconciliation**: Sync uses attach-first strategy with auto-register for untracked panes. Column-positional focus. Tmux session affinity.

## 0.7.1

- **Additive reconciliation**: Convergent reconciliation loop (max 3 attempts) with deferred eviction and reorder phase. Nuclear rebuild fallback.

## 0.7.0

- **Snapshot-diff sync architecture**: Rewrite sync to use snapshot-based diffing for tmux layout reconciliation. Dead window handling and column inversion fix.

## 0.6.6

- **`--focus` on sync**: `agent-doc sync` accepts `--focus` flag. Inline hint notification at cursor position in JetBrains plugin.

## 0.6.5

- **Always use `sync --col`**: Single-file sync uses column mode. Break out unwanted panes. Plugin notification balloon for detected layout.

## 0.6.4

- **Sync window filtering + layout equalization**: Filter sync to target window only. Equalize pane sizes after layout.

## 0.6.3

- **LayoutDetector fix**: Skip non-splitter Container children in JetBrains plugin 3-column layout detection.

## 0.6.2

- **Fire-and-forget Junie bridge**: Junie bridge script resolved automatically. Plugin clipboard handoff for non-tmux editors.

## 0.6.1

- **Junie agent backend**: Add Junie as an agent backend with JetBrains plugin action support.

## 0.6.0

- **`agent-doc sync` command**: 2D columnar tmux layout synced to editor split arrangement. Dynamic pane groups.

## 0.5.6

- **Commit message includes doc name**: `agent-doc commit` message format now includes the document filename. `agent-doc outline` command for markdown section structure with token counts.

## 0.5.5

- **Window-scoped routing**: Route commands scoped to tmux window (not just session). `--pane`/`--window` flags. Layout safeguards. JetBrains plugin self-disabling Alt+Enter popup (removes ActionPromoter).

## 0.5.4

- **Positional claim**: `agent-doc claim <file>` accepts file as positional argument. Editor plugin improvements and SPEC updates.

## 0.5.3

- **Bundled SKILL.md with absolute snapshot paths**: Snapshot paths use absolute paths for reliability. Resync subcommand and claims log documentation.

## 0.5.2

- **Claim notifications + resync + plugin popup**: Notification on claim. `agent-doc resync` validates sessions.json and removes dead panes. JetBrains and VS Code editor plugins added.

## 0.5.1

- **Windows build fix**: Cfg-gate unix-only exec in `start.rs` for cross-platform compilation.

## 0.5.0

- **`agent-doc focus` and `agent-doc layout`**: Focus a tmux pane for a session document. Layout arranges tmux panes to mirror editor split arrangement.

## 0.4.4

- **Rename SPECS.md to SPEC.md**: Standardize specification filename.

## 0.4.3

- **Commit CWD fix**: Fix working directory for `agent-doc commit`. SKILL.md prohibition rules.

## 0.4.2

- **SPEC.md gaps filled**: Document comment stripping as skill-level behavior (§4), `--root DIR` flag for audit-docs (§7.6), `agent-doc-version` frontmatter field for auto-update detection (§7.12), and startup version check (`warn_if_outdated`).
- **Flaky test fix**: Skill tests no longer use `std::env::set_current_dir`. Refactored `install`/`check` to accept an explicit root path (`install_at`/`check_at`), eliminating CWD races in parallel test execution.
- **CLAUDE.md module layout updated**: Added `claim.rs`, `prompt.rs`, `skill.rs`, `upgrade.rs` to the documented module layout.

## 0.4.1

- **SKILL.md: comment stripping for diff**: Strip HTML comments (`<!-- ... -->`) and link reference comments (`[//]: # (...)`) before comparing snapshot vs current content. Comments are a user scratchpad and no longer trigger agent responses.
- **SKILL.md: auto-update check**: New `agent-doc-version` frontmatter field enables pre-flight version comparison. If the installed binary is newer, `agent-doc skill install` runs automatically before proceeding.
- **PromptPanel: JDialog to JLayeredPane overlay**: Replace `JDialog` popup with a `JLayeredPane` overlay in the JetBrains plugin, eliminating window-manager popup leaks.

## 0.4.0

- **`agent-doc claim <file>`**: New subcommand — claim a document for the current tmux pane. Reads session UUID from frontmatter + `$TMUX_PANE`, updates `sessions.json`. Last-call-wins semantics. Also invokable as `/agent-doc claim <file>` via the Claude Code skill.
- **`agent-doc skill install`**: Install the bundled SKILL.md to `.claude/skills/agent-doc/SKILL.md` in the current project. The skill content is embedded in the binary via `include_str!`, ensuring version sync.
- **`agent-doc skill check`**: Compare installed skill vs bundled version. Exit 0 if up to date, exit 1 if outdated or missing.
- **SKILL.md updated**: Fixed stale `$()` pattern → `agent-doc commit <FILE>`. Added `/agent-doc claim` support.
- **SPEC.md expanded**: Added §7.7–7.13 (all commands), §8 Session Routing with use case table (U1–U11), §8.3 Claim Semantics.

## 0.3.0

- **Multi-session prompt polling**: `agent-doc prompt --all` polls all live sessions in one call, returns JSON array. `SessionEntry` now includes a `file` field for document path (backward-compatible).
- **`agent-doc commit <file>`**: New subcommand — `git add -f` + commit with internally-generated timestamp. Replaces shell `$()` substitution in IDE/skill workflows.
- **Prompt detection**: `agent-doc prompt` subcommand added in v0.2.0 (unreleased).
- **send-keys fix**: Literal text (`-l`) + separate Enter, `new-window -a` append flag (unreleased since v0.2.0).

## 0.1.4

- **`agent-doc upgrade` self-update**: Downloads prebuilt binary from GitHub Releases as the primary upgrade strategy. Falls back to `cargo install`, then `pip install --upgrade`, then manual instructions including `curl | sh`.

## 0.1.3

- **Upgrade check**: Queries crates.io for latest version with a 24h cache. Prints a one-line stderr warning on startup if outdated.
- **`agent-doc upgrade`**: New subcommand tries `cargo install` then `pip install --upgrade`, or prints manual instructions.

## 0.1.2

- **Language-agnostic audit-docs**: Replace Cargo.toml-only root detection with 3-pass strategy (project markers → .git → CWD fallback). Scan 28 file extensions across 6 source dirs instead of .rs only.
- **--root CLI flag**: Override auto-detection of project root for audit-docs.
- **Test coverage**: Add unit tests for frontmatter, snapshot, and diff modules.

## 0.1.0

Initial release.

- **Interactive document sessions**: Edit a markdown document, run an AI agent, response appended back into the document.
- **Session continuity**: YAML frontmatter tracks session ID, agent backend, and model. Fork from current session on first run, resume on subsequent.
- **Diff-based runs**: Only changed content is sent as a diff, with the full document for context. Double-run guard via snapshots.
- **Merge-safe writes**: 3-way merge via `git merge-file` if the file is edited during agent response. Conflict markers written on merge failure.
- **Git integration**: Pre-commit user changes before agent call, leave agent response uncommitted for editor diff gutters. `-b` flag for auto-branch, `--no-git` to skip.
- **Agent backends**: Agent-agnostic core. Claude backend included. Custom backends configurable via `~/.config/agent-doc/config.toml`.
- **Commands**: `run`, `init`, `diff`, `reset`, `clean`, `audit-docs`.
- **Editor integration**: JetBrains External Tool, VS Code task, Vim/Neovim mapping.
- **Backlog-required review closeout is now fail-closed.** Preflight now persists a cycle-scoped "requires backlog capture" contract derived from prompt targets plus recursive frontmatter `prompt_presets` expansion (for example `#code-review` chaining into `#follow-up-backlog`). `plan` now emits `expect_add` for those preset-driven review prompts, and `finalize` / `session-check` now fail when such a cycle records no backlog mutations unless the response explicitly states that there were no actionable follow-up items to capture. Added regressions for preset-expanded plan detection plus pre-commit/post-commit enforcement and the explicit-no-follow-ups escape hatch.
- **Pre-prompt Codex `Ctrl-D` exits now restart fresh instead of stalling reroutes behind the supervisor quit prompt.** `start.rs` now treats a forwarded `Ctrl-D`/stdin EOF on a fresh or fresh-restart Codex child that never surfaced an idle prompt as failed startup provenance, so the supervisor restarts fresh automatically instead of prompting for quit/restart. The successor run also suppresses only the stale inherited pre-prompt `Ctrl-D` byte until a real prompt appears. This closes the `sampleorders.md` `%179` shape from `tasks/agent-doc/agent-doc-bugs2.md`, where dispatch-only reroutes kept failing with `still booting` while the live pane sat behind `ctrl_d=true ... action=prompt_user`. Added restart-strategy regression coverage and updated the Codex/supervisor spec text.
- **Live Codex reroutes now get one fresh-supervisor retry before route records a startup-miss.** `route.rs` still requires a real document-cycle ack after injecting `agent-doc <FILE>` into a ready live pane, but when that ack never arrives on a still-live Codex session route no longer fails closed immediately. It now asks the supervisor for a one-shot **fresh** restart of that same pane, waits for the restarted Codex prompt to become dispatch-ready again, and resends the same bare reopen exactly once before falling back to the existing startup-miss error. This closes the cancel + `/clear` shape from `tasks/agent-doc/agent-doc-bugs2.md`, where the pane was alive and apparently idle but the stale conversation state would absorb the routed reopen without ever starting a new document cycle. Added regression coverage and updated the routing spec.
- **Same-document Codex reroutes no longer fail closed purely on a missed idle-prompt heuristic after a no-op scoped fix.** `route.rs` still waits for the pane to look dispatch-ready first, but when the registered pane still authoritatively owns the document, the scoped fix makes no changes, and the supervisor is healthy, route now retries the bare `agent-doc <FILE>` reopen once and requires the usual cycle-start acknowledgment before success. This removes the false-negative `sampleorders.md` / busy-pane route failure where Codex was effectively idle but prompt detection never stabilized, without dropping the fail-closed startup-miss proof if the reopen still does not start a cycle.
- **Repair/write normalization now preserves legacy alias tags in existing backlog items.** The pending/backlog compatibility path still rejects genuinely new duplicate custom-id prefixes, but it no longer fails replay just because an already-existing backlog line begins its free-form text with a secondary reference tag such as `[#ss01]` or `[#wpmem]`. That closes the `sampleorders.md` repair-blocked shape where an orphaned `<!-- patch:backlog -->` replay warned about legacy backlog syntax and then died on `duplicate leading custom id prefix` even though the live document itself had no `patch:backlog` block. Added normalization regressions for both the preserved existing-alias case and the still-rejected new-item case.
- **Dispatch-only editor reroutes now stop after one bare reopen and fail closed on explicit shell blockers.** `route --dispatch-only` still resolves the authoritative pane and sends the literal `agent-doc <FILE>` reopen, but it no longer reuses the managed route's Enter-retry acceptance loop. That means editor hotkeys will not keep pressing Enter for 5 seconds when the reopen text remains visible in pane scrollback, which could previously accept stray shell state and launch commands like `nvim`. Dispatch-only also now checks the current pane capture for explicit interactive blockers such as `reverse-i-search`, shell history search, queued drafts, or active permission prompts and refuses to inject anything else into those states. Added route regressions covering both the no-extra-Enter contract and the reverse-i-search fail-closed guard. This addresses the latest JetBrains `agent-doc-bugs2.md` unexpected `nvim` launch report.
