Documentation README
This is the entry point for agent-doc documentation, specs, and product-planning material.
Start Here
- README - project overview, install commands, quick start, architecture summary, editor setup, and security model.
- Introduction - concise user-facing introduction.
- Quick Start - shortest path from a markdown file to an agent-doc cycle.
- Installation - install paths for the CLI and editor integrations.
- Configuration - project and document configuration.
Guides
- Commands - user-facing command guide.
- Document Format - session frontmatter and component layout.
- Components - named component patching model.
- Dashboard-as-Document - dashboard workflow.
- Run Flow - normal edit, diff, response, write, and commit flow.
- Editor Integration - JetBrains, VS Code, and other editor integration notes.
- Agent Backends - Claude, Codex, OpenCode, and direct harness behavior.
Reference
- Specification - includes the root functional specification.
- Flow Map - FlowCore ownership map and typed event migration plan.
- Active Turn Lifecycle And Replay Paths
- generated diagrams for active turns, route readiness, prompt ownership, stale cache/conflict replay, and late fallback replay.
- IPC - editor IPC architecture and fallback behavior.
- Full-Document IPC Corruption Chain
- separate Mermaid logic chain for repeated full-document IPC corruption and the end-to-end disabled path.
- Prompt Duplicate Closeout Repair
- Mermaid process diagram for the disabled full-content IPC path and duplicate-prompt closeout repair.
- Race Condition Analysis - concurrency hazards and mitigations.
- Reactive Stream - reactive stream notes.
- Changelog - version history.
Specs
The canonical spec entry point is SPEC.md. Split specs live under
specs/ for focused review:
- Overview
- Document Format
- Snapshot System
- Diff Computation
- Agent Backend
- Config
- Commands
- Core Commands
- Closeout Commands
- Orchestration Commands
- Session Tmux Commands
- Session Routing
- Session Actor Contract
- Git Integration
- Security
- Debounce
- Deterministic Simulation
- State Backbone
- Pending System
- Supervisor
- Codex Support
Development
Examples And Ontology
PRDs
No PRD or product-requirements documents are currently present in this repo. When PRDs are added, link them from this section and keep this README as the documentation entry point.
agent-doc
Alpha software. Expect breaking changes between minor versions. See the changelog for migration notes.
agent-doc turns any markdown document into an interactive session with an AI agent. Edit the document, press a hotkey, and the tool diffs your changes, sends them to the agent, and writes the response back into the document. The document is the UI.
Why documents?
Terminal prompts are ephemeral. You type, the agent responds, the context scrolls away. Documents are persistent — you can reorganize, delete noise, annotate inline, and curate the conversation as a living artifact. The agent sees your edits as diffs, so every change carries intent.
How it works
- You edit a markdown document with
## User/## Assistantblocks - You run via hotkey or CLI — agent-doc computes a diff since the last run
- The agent responds — the response is appended as a new
## Assistantblock - Your editor reloads — the document now contains the full conversation
The diff-based approach means you can also edit previous responses, add prompt-bearing inline edits or corrections, delete noise, and restructure the document freely. The agent sees exactly what changed.
Features
- Session continuity — YAML frontmatter tracks session ID for multi-turn conversations
- Merge-safe writes — 3-way merge if you edit during an agent response
- Git integration — auto-commit for diff gutter visibility in your editor
- Agent-agnostic — Claude backend included, custom backends configurable
- Editor integration — JetBrains, VS Code, Vim/Neovim via hotkey
Tech stack
- Language: Rust (2021 edition)
- CLI:
clap(derive macros) - Diffing:
similarcrate (pure Rust) - Serialization:
serde+serde_yaml/serde_json/toml - Hashing:
sha2for snapshot paths
Installation
pip / pipx (all platforms)
pip install agent-doc
# or
pipx install agent-doc
This installs a prebuilt wheel with the compiled binary — no Rust toolchain needed.
Shell installer (Linux & macOS)
curl -sSf https://raw.githubusercontent.com/btakita/agent-doc/main/install.sh | sh
This downloads a prebuilt binary to ~/.local/bin/agent-doc. Use --system to install to /usr/local/bin instead (requires sudo).
From source
cargo install --path .
Windows
pip install agent-doc is the easiest option. Alternatively, download .zip from GitHub Releases or build from source with cargo install --path ..
Quick Start
Create a session document
agent-doc init session.md "My Topic"
This creates a markdown file with YAML frontmatter and a ## User block ready for your first message.
Write your first message
Open session.md in your editor and write under ## User:
---
session: null
agent: null
model: null
branch: null
---
# Session: My Topic
## User
Explain how TCP three-way handshake works.
Run the agent
agent-doc run session.md
The tool computes a diff, sends it to the agent, and appends the response as a ## Assistant block. Your editor reloads the file with the response.
Continue the conversation
Add a new ## User block below the assistant's response, write your follow-up, and run again:
agent-doc run session.md
Preview before running
agent-doc diff session.md # see what changed since last run
agent-doc run session.md --dry-run # preview the prompt without sending
Basic workflow
agent-doc init session.md "Topic" # scaffold a session doc
# edit session.md in your editor
agent-doc run session.md # diff, send, append response
# edit again, add follow-up
agent-doc run session.md # next turn
agent-doc clean session.md # squash git history when done
Configuration
Config file
Location: ~/.config/agent-doc/config.toml
default_agent = "claude"
[agents.claude]
command = "claude"
args = ["-p", "--output-format", "json"]
result_path = ".result"
session_path = ".session_id"
[agents.codex]
command = "codex"
args = ["--prompt"]
result_path = ".output"
session_path = ".id"
[agents.opencode]
command = "opencode"
args = ["run"]
Fields
| Field | Description |
|---|---|
default_agent | Agent backend used when not specified elsewhere |
[agents.NAME] | Agent backend configuration |
command | Executable name or path |
args | Arguments passed before the prompt |
result_path | JSON path to extract the response text |
session_path | JSON path to extract the session ID |
Resolution order
The agent backend is resolved in this order:
--agentCLI flagagent:field in document frontmatterdefault_agentin config- Fallback:
"claude"
Per-document overrides
Set agent and model in the document's YAML frontmatter:
---
session: null
agent: opencode
opencode_model: zai/glm-5
---
These override the config file for that specific document.
Environment Variables
Runtime Tuning
| Variable | Default | Description |
|---|---|---|
AGENT_DOC_LOG | — | Structured log filter (e.g. debug, agent_doc::preflight=debug) |
AGENT_DOC_RUN_AGENT_TIMEOUT_SECS | 1800 | Max agent run time before timeout (30 min) |
AGENT_DOC_RUN_HEARTBEAT_SECS | 30 | Run heartbeat interval |
AGENT_DOC_QUEUE_MAX_ITERATIONS_HARD_CAP | 50 | Hard cap on auto-loop queue iterations |
AGENT_DOC_TSIFT_BIN | — | Override tsift binary path |
AGENT_DOC_TSIFT_GRAPH_TIMEOUT_SECS | — | Tsift graph query timeout |
Harness Detection
These are read by detect_harness() to identify the active agent harness:
| Variable | Harness |
|---|---|
CLAUDE_CODE_SESSION / CLAUDE_CODE / CLAUDECODE | Claude Code |
CODEX_SESSION / CODEX_THREAD_ID / CODEX_CLI / CODEX | Codex |
OPENCODE_CLIENT / OPENCODE | OpenCode |
Harness Arg Overrides
| Variable | Description |
|---|---|
AGENT_DOC_CLAUDE_ARGS | Claude CLI args (lowest precedence, below frontmatter + config) |
Vision
| Variable | Description |
|---|---|
AGENT_DOC_VISION_PROVIDER | Vision provider override (e.g. openai) |
AGENT_DOC_VISION_API_KEY | Vision API key |
AGENT_DOC_VISION_MODEL | Vision model override |
AGENT_DOC_VISION_ENDPOINT | Vision endpoint override |
Testing / Debug
| Variable | Description |
|---|---|
AGENT_DOC_NO_AUTOSTART | Prevent auto-start of new agent panes |
AGENT_DOC_ROUTE_BIN | Override agent-doc binary for route dispatch |
AGENT_DOC_DEBUG_FILTER | Debug filter for supervisor start |
AGENT_DOC_DEBUG_STDIN | Debug stdin for supervisor start |
AGENT_DOC_ALLOW_REPLACE_PENDING | Allow replacing pending items |
AGENT_DOC_HARNESS_PROMPT | Override harness prompt detection |
Probe Markers
Set by child processes to confirm capability proofs:
| Variable | Description |
|---|---|
AGENT_DOC_NETWORK_PROBE_OK | Codex child network probe success |
AGENT_DOC_WRITABLE_ROOT_PROBE_OK | Codex child writable root probe |
AGENT_DOC_OPENCODE_SSH_PROBE_OK | OpenCode child SSH probe |
Project Config
Location: .agent-doc/config.toml (relative to project root).
| Field | Description |
|---|---|
tmux_session | Tmux session name bound to this project |
agent_doc_auto_compact | Line threshold for automatic compaction opt-in |
documents.include | Project-relative globs for session document opt-in |
documents.auto_session_for_all_md | Legacy escape hatch (default false) |
SSH Config
[ssh.profiles.production]
targets = ["host1", "host2"]
[ssh.docs."ops/deploy.md"]
profile = "production"
Component Config
Inline component attributes override defaults:
<!-- agent:exchange patch=append max_lines=50 -->
Commands
All commands are available through the agent-doc CLI.
run
agent-doc run <FILE> [-b] [--agent NAME] [--model MODEL] [--dry-run] [--no-git]
Diff, send to agent, append response. The core command.
| Flag | Description |
|---|---|
-b | Auto-create branch agent-doc/<filename> on first run |
--agent NAME | Override agent backend |
--model MODEL | Override model |
--dry-run | Preview diff and prompt size without sending |
--no-git | Skip git operations (branch, commit) |
Flow:
- Compute diff from snapshot
- Build prompt (diff + full document)
- Pre-commit user changes (unless
--no-git) - Send to agent
- Append response as
## Assistantblock - 3-way merge if file was edited during response
- Save snapshot and close out through the commit boundary. Successful runs commit the response in the same cycle and clean agent-owned post-commit drift back to
HEAD; only genuine later local edits remain uncommitted.
init
agent-doc init <FILE> [TITLE] [--agent NAME]
Scaffold a new session document with YAML frontmatter and a ## User block. Fails if the file already exists.
diff
agent-doc diff <FILE>
Preview the unified diff that would be sent on the next run. Useful for checking what changed before running.
reset
agent-doc reset <FILE>
agent-doc reset --from-current <FILE>
Clear the session ID from frontmatter and delete the snapshot/CRDT sidecars. Use --from-current after manually cleaning a corrupted document; it rebuilds the snapshot and CRDT state from the visible markdown so old sidecars cannot resurrect stale content.
clean
agent-doc clean <FILE>
Squash all agent-doc: git commits for the file into a single commit. Useful for cleaning up history after a long session.
audit-docs
agent-doc audit-docs [--root DIR]
Audit instruction files (CLAUDE.md, AGENTS.md, README.md, SKILL.md) against the codebase:
- Referenced file paths exist on disk
- Combined line budget under 1000 lines
- Staleness detection (docs older than source)
- Actionable content checks
- Generated agent-doc instruction surfaces match the running binary. From a submodule checkout, the default release audit checks the superproject install root; explicit
--root DIRchecks generated surfaces under that root exactly.
route
agent-doc route <FILE>
Route a /agent-doc command to the correct tmux pane. Looks up the session UUID from frontmatter, finds the pane in sessions.json, and sends the command via the managed supervisor or tmux submit path. If no live owner exists, route auto-starts a route-owned pane and reaps it after the new document cycle commits; a startup miss records diagnostics and kills the just-created idle pane.
start
agent-doc start <FILE>
Start the configured harness in the current tmux pane and register the session. Ensures a session UUID exists in frontmatter, registers the pane in sessions.json, then runs the harness under the supervisor restart loop. Route-created panes use an internal one-shot mode so successful fresh cycles close the pane automatically; manually started panes remain persistent.
If the YAML frontmatter is malformed, start now fails with a file-targeted error that includes a compiler-style frontmatter excerpt with a caret at the reported line/column, then tells you to fix the --- ... --- block before retrying. The sync/auto-start path also mirrors the same warning into the document's agent:status component when present, so editor-driven auto-start failures are visible even when no tmux pane appears.
claim
agent-doc claim <FILE>
Claim a document for the current tmux pane. Reads the session UUID from frontmatter and $TMUX_PANE, then updates sessions.json. Unlike start, does not launch Claude — use this when you're already inside a Claude session.
Last-call-wins: a subsequent claim for the same file overrides the previous pane mapping. Multiple files can be claimed for the same pane.
Also available as a Claude Code skill: /agent-doc claim <FILE>.
prompt
agent-doc prompt <FILE>
agent-doc prompt --all
agent-doc prompt --answer N <FILE>
Detect permission prompts from a Claude Code session by capturing tmux pane content.
| Flag | Description |
|---|---|
| (none) | Detect prompts for a single file |
--all | Poll all live sessions, return JSON array |
--answer N | Answer prompt by selecting option N (1-based) |
commit
agent-doc commit <FILE>
Git add + commit with an auto-generated agent-doc: YYYY-MM-DD HH:MM:SS timestamp message.
compact
agent-doc compact <FILE> [--component exchange] [--message TEXT] [--keep N] [--commit]
Archive old exchange/component content and rewrite the document atomically. Use --commit to close out through the binary-owned agent-doc commit path so editor VCS refresh signaling also runs.
skill
agent-doc skill install
agent-doc skill check
Manage the Claude Code skill definition.
| Subcommand | Description |
|---|---|
install | Write the bundled SKILL.md to .claude/skills/agent-doc/SKILL.md. Idempotent. |
check | Compare installed skill vs bundled version. Exit 0 if up to date, exit 1 if outdated. |
The skill content is embedded in the binary at build time. After agent-doc upgrade, run agent-doc skill install in each project to update the skill definition.
patch
agent-doc patch <FILE> <COMPONENT> [CONTENT]
Replace content in a named component. Components are bounded regions marked with <!-- agent:name -->...<!-- /agent:name -->.
| Argument | Description |
|---|---|
FILE | Path to the document |
COMPONENT | Component name (e.g., status, log) |
CONTENT | Replacement content (reads from stdin if omitted) |
Behavior depends on .agent-doc/components.toml config:
| Config | Default | Description |
|---|---|---|
mode | replace | replace, append, or prepend |
timestamp | false | Auto-prefix with ISO timestamp |
max_entries | 0 | Trim entries in append/prepend (0 = unlimited) |
pre_patch | none | Shell hook: transform content (stdin → stdout) |
post_patch | none | Shell hook: fire-and-forget after write |
See Components for full configuration and hook documentation.
watch
agent-doc watch [--stop] [--status] [--debounce MS] [--max-cycles N]
Watch session files for changes and auto-submit.
| Flag | Default | Description |
|---|---|---|
--stop | Stop the running watch daemon | |
--status | Show daemon status | |
--debounce | 500 | Debounce delay in milliseconds |
--max-cycles | 3 | Max agent-triggered cycles per file before pausing |
The daemon watches all claimed files (from sessions.json), debounces per-file, and triggers agent-doc run on changes. PID stored in .agent-doc/watch.pid.
Loop prevention: bounded cycles (default 3) and convergence detection (stop if agent response matches previous). See Dashboard-as-Document for the full workflow.
memory
agent-doc memory index <FILE> [--db PATH] [--json]
agent-doc memory search <FILE> --query TEXT [--db PATH] [--limit N] [--json] [--rebuild]
Index and search session memory from agent:backlog, agent:review, agent:done (including archive= files), agent:icebox, and live exchange ### Re: sections. The default store is <project>/.tsift/memory.db, using the shared tsift-memory library crate rather than shelling out to the tsift CLI.
upgrade
agent-doc upgrade
Check GitHub Releases for the latest version and upgrade. Tries the prebuilt GitHub binary first, then pip install --upgrade.
Global flags
agent-doc --version # Print version
agent-doc --help # Show help
Document Format
Structure
Session documents are markdown files with YAML frontmatter:
---
session: 05304d74-90f1-46a1-8a79-55736341b193
agent: claude
model: null
branch: null
---
# Session: Topic Name
## User
Your question or instruction here.
## Assistant
(agent writes here)
## User
Follow-up. You can also annotate inline:
> What about edge cases?
Frontmatter fields
| Field | Required | Default | Description |
|---|---|---|---|
session | no | (generated on first run) | Session ID for continuity |
agent | no | claude | Agent backend to use |
model | no | (agent default) | Model override |
branch | no | (none) | Git branch for session commits |
All fields are optional and default to null.
Frontmatter parsing
Delimited by ---\n at the start of the file and a closing \n---\n. If frontmatter is absent, all fields default to null and the entire content is treated as the body.
Interaction modes
Append mode
Structured ## User / ## Assistant blocks. Each run appends a new assistant response.
Inline mode
Annotations anywhere — blockquotes, edits to previous responses, comments in the body. The diff captures what changed; the agent addresses inline edits alongside new ## User content.
Both modes work simultaneously because the run sends a diff, not a parsed structure.
Components
Documents can contain components — named, re-renderable regions marked with HTML comment pairs:
<!-- agent:status -->
| Field | Value |
|-------|-------|
| build | passing |
<!-- /agent:status -->
Components are updated via agent-doc patch or by agents/scripts. Their content can be replaced, appended to, or prepended to based on configuration in .agent-doc/components.toml.
Regular HTML comments (<!-- like this -->) remain a private scratchpad — they're stripped during diff and never trigger responses. Component markers look like comments but are structural and preserved.
See the Components guide for full details.
History rewriting
Delete anything from the document. On next run, the diff shows deletions and the agent sees the cleaned-up document as ground truth. This lets you:
- Remove irrelevant exchanges
- Consolidate scattered notes
- Restructure the conversation
- Correct earlier context
Components
Components are bounded, named, re-renderable regions in a document — similar to web components or React components. They provide a way for agents and scripts to update specific sections of a document without touching the rest.
Syntax
Components use paired HTML comment markers:
<!-- agent:status -->
| Field | Value |
|-------|-------|
| build | passing |
<!-- /agent:status -->
The opening marker <!-- agent:NAME --> and closing marker <!-- /agent:NAME --> define the component boundary. Everything between the markers is the component's content.
Why paired markers?
A single marker (<!-- agent:x -->) has no boundary — after the first render inserts content, the next render can't distinguish the marker from rendered data. Paired markers create an unambiguous boundary. The closing marker makes re-rendering idempotent: agent-doc patch always knows exactly what to replace.
Naming rules
Component names must match [a-zA-Z0-9][a-zA-Z0-9-]* — start with alphanumeric, followed by alphanumerics or hyphens.
Valid: status, build-log, session2
Invalid: -start, _name, with spaces
Nesting
Components can nest. Inner components are parsed independently:
<!-- agent:dashboard -->
# System Overview
<!-- agent:status -->
All systems operational.
<!-- /agent:status -->
<!-- agent:metrics -->
CPU: 42%
<!-- /agent:metrics -->
<!-- /agent:dashboard -->
Patching status or metrics only affects that inner component. Patching dashboard replaces everything between its markers (including the inner components).
Patching components
The agent-doc patch command replaces a component's content:
# Replace from argument
agent-doc patch dashboard.md status "build: failing"
# Replace from stdin
echo "build: passing" | agent-doc patch dashboard.md status
# Replace from a script
curl -s https://api.example.com/status | agent-doc patch dashboard.md status
The markers are preserved — only the content between them changes.
Component configuration
Configure component behavior in .agent-doc/components.toml at the project root:
[log]
mode = "append"
timestamp = true
max_entries = 100
[status]
mode = "replace" # default
[metrics]
pre_patch = "scripts/validate-metrics.sh"
post_patch = "scripts/notify-update.sh"
Modes
| Mode | Behavior |
|---|---|
replace | Full content replacement (default) |
append | New content added at the bottom of existing content |
prepend | New content added at the top of existing content |
Options
| Option | Type | Default | Description |
|---|---|---|---|
mode | string | "replace" | Patch mode |
timestamp | bool | false | Auto-prefix entries with ISO timestamp |
max_entries | int | 0 | Auto-trim old entries in append/prepend modes (0 = unlimited) |
pre_patch | string | none | Shell command to transform content before patching |
post_patch | string | none | Shell command to run after patching (fire-and-forget) |
Shell hooks
Hooks let you transform content or trigger side effects when a component is patched.
pre_patch
Runs before the content is written. Receives the new content on stdin, outputs transformed content on stdout:
[status]
pre_patch = "scripts/validate-status.sh"
#!/bin/bash
# scripts/validate-status.sh
# Transform content before it's written to the component
# Read incoming content from stdin
content=$(cat)
# Validate or transform
if echo "$content" | jq . > /dev/null 2>&1; then
# Valid JSON — format it as a markdown table
echo "$content" | jq -r 'to_entries[] | "| \(.key) | \(.value) |"'
else
# Pass through unchanged
echo "$content"
fi
Environment variables available:
COMPONENT— component name (e.g.,status)FILE— path to the document being patched
If the hook exits non-zero, the patch is aborted.
post_patch
Runs after the content is written. Fire-and-forget — output is inherited (prints to terminal), exit code is logged but doesn't affect the patch:
[metrics]
post_patch = "scripts/notify-update.sh"
#!/bin/bash
# scripts/notify-update.sh
echo "Component '$COMPONENT' updated in $FILE"
# Could trigger a webhook, send a notification, etc.
Components vs comments
Regular HTML comments are a user scratchpad — they're stripped during diff comparison and never trigger agent responses:
<!-- This is a regular comment — invisible to the agent -->
Component markers look like comments but are structural:
<!-- agent:status -->
This content is managed by the agent or scripts
<!-- /agent:status -->
The diff engine preserves component markers while stripping regular comments. This means:
- Adding/removing regular comments does not trigger a response
- Changing content inside a component does trigger a response
- The markers themselves are never modified by
patch
Dashboard-as-Document
A dashboard is a markdown document with agent-maintained components that display live data. Instead of a separate dashboard UI, the document is the dashboard — editable in any text editor, version-controlled with git, and updated by scripts or agents via agent-doc patch.
Quick start
1. Create the dashboard document
---
session: null
---
# Project Dashboard
## Status
<!-- agent:status -->
| Service | State |
|---------|-------|
| api | unknown |
| worker | unknown |
<!-- /agent:status -->
## Recent Activity
<!-- agent:log -->
<!-- /agent:log -->
2. Configure components
Create .agent-doc/components.toml:
[status]
mode = "replace"
[log]
mode = "append"
timestamp = true
max_entries = 50
3. Update components from scripts
# Update the status table
agent-doc patch dashboard.md status "$(cat <<'EOF'
| Service | State |
|---------|-------|
| api | healthy |
| worker | healthy |
EOF
)"
# Append to the log
agent-doc patch dashboard.md log "Deployment completed successfully"
The status component gets replaced entirely. The log component appends with a timestamp:
<!-- agent:log -->
[2026-03-04T18:30:00Z] Deployment completed successfully
<!-- /agent:log -->
4. Auto-update with watch
Start the watch daemon to auto-submit when the dashboard changes:
agent-doc watch
Now when external scripts update components via patch, the watch daemon detects the file change and can trigger agent-doc run to let the agent respond to the new data.
End-to-end flow
External script agent-doc Agent
| | |
|-- patch status ----------->| |
| |-- (file changed) --->|
| | (watch detects) |
| |-- run (diff+send) -->|
| | |-- responds
| |<-- patch log --------|
| | (agent updates) |
- An external script calls
agent-doc patchto update a component - The watch daemon detects the file change
- Watch triggers
agent-doc runwhich diffs and sends to the agent - The agent sees the change ("status went from unknown to healthy") and can respond — updating the log, adding analysis, or patching other components
Dashboard with multiple components
A real-world dashboard might look like:
---
session: null
---
# Build Monitor
<!-- agent:summary -->
**Last updated:** never
<!-- /agent:summary -->
## Build Status
<!-- agent:builds -->
No builds yet.
<!-- /agent:builds -->
## Test Results
<!-- agent:tests -->
No test results.
<!-- /agent:tests -->
## Activity Log
<!-- agent:log -->
<!-- /agent:log -->
With .agent-doc/components.toml:
[summary]
mode = "replace"
[builds]
mode = "replace"
post_patch = "scripts/check-failures.sh"
[tests]
mode = "replace"
[log]
mode = "append"
timestamp = true
max_entries = 200
Update from CI:
# After a build completes
agent-doc patch monitor.md builds "$(./scripts/format-builds.sh)"
# After tests run
agent-doc patch monitor.md tests "$(./scripts/format-tests.sh)"
# Log the event
agent-doc patch monitor.md log "Build #${BUILD_ID} completed: ${STATUS}"
User interaction with dashboards
Dashboards are still documents — users can write in them. Add a ## User block or annotate inline. The agent responds to the diff like any session document.
## User
Why did build #42 fail? Can you analyze the test results component?
The agent sees the full dashboard (all components) plus the user's question, and can respond in context.
Loop prevention
When the watch daemon is running, a patch can trigger a run, which might patch again, creating a cycle. Watch prevents unbounded loops:
- Bounded cycles (default 3): After 3 consecutive agent-triggered re-submits with no external change, watch pauses that file
- Convergence detection: If the agent's response produces the same content as last time (hash match), the cycle stops
- Configurable:
agent-doc watch --max-cycles 5 --debounce 1000
Tips
- Reference other files: Dashboards can reference other documents — use relative paths from the project root
- Inline annotations: Edit within component content to ask questions — the diff captures your edits
- Comments are private:
<!-- regular comments -->are never sent to the agent. Use them for notes. - Snapshots reset on rename: Moving a file resets the diff baseline (snapshots are keyed by canonical path)
- Git integration:
agent-doc commit dashboard.mdcommits the current state with a timestamp
Run Flow
Overview
┌──────────┐ hotkey ┌────────────┐ diff + prompt ┌───────┐
│ Editor │ ──────> │ agent-doc │ ──────────────> │ Agent │
│ │ │ │ <────────────── │ API │
│ reload │ <────── │ write+snap │ └───────┘
└──────────┘ │ git commit │
└────────────┘
Step by step
- Read document and load snapshot (last-known state from previous run)
- Compute diff — if empty, exit early (double-run guard)
- Pre-commit user's changes via
git add -f+git commit(baseline for diff gutters) - Send diff + full document to agent, resuming session if one exists
- Build response — original content + session ID update +
## Assistantblock +## Userblock - Check for concurrent edits — re-read the file
- Merge if needed — 3-way merge via
git merge-fileif file changed during agent response - Write merged content back to file
- Save snapshot and close out — the response is committed in the same cycle, then post-commit cleanup brings the live document, snapshot, and editor-facing state back in line with committed
HEAD. Only genuine later local edits remain uncommitted.
Session continuity
- Empty
session:— forks from the most recent agent session (inherits context) session: <uuid>— resumes that specific session- Delete
session:value — next run starts fresh
Merge-safe writes
If you edit the document while the agent is responding:
- Clean merge (edits in different regions) — merged automatically. Message: "Merge successful — user edits preserved."
- Conflict (edits in the same region as the response) — conflict markers written to the file with labels
agent-response,original,your-edits. Message: "WARNING: Merge conflicts detected."
The merge uses git merge-file -p --diff3, which handles edge cases (whitespace, encoding, partial overlaps) better than a custom implementation.
Git integration
| Flag | Behavior |
|---|---|
-b | Auto-create branch agent-doc/<filename> on first run |
| (none) | Pre-commit user changes to current branch |
--no-git | Skip git entirely |
The closeout flow still commits the user's baseline before generating a response, but a successful response turn now also crosses the binary-owned commit boundary before it exits. Agent-owned (HEAD) / boundary churn is cleaned up in the same cycle instead of being left behind for the next run. Only real follow-up edits after that closeout remain as uncommitted local changes.
Cleanup: agent-doc clean <file> squashes all session commits into one.
Editor Integration
agent-doc is designed to be triggered from your editor with a single hotkey.
Both official editor plugins also add lightweight visual distinction for agent-doc structures in markdown: component comments, patch comments, boundary markers, ### Re: headings, ❯ prompts, tracked [#id] tags, and plain HTML scratch comments plus their bodies are highlighted directly in the editor, while fenced code examples are left alone. JetBrains component bodies and markdown emphasis inherit the editor's normal text color; underscores inside identifier words such as foo_bar_baz are not treated as emphasis delimiters. The JetBrains plugin reapplies those visual tokens when markdown files are opened or selected cold from disk, before the document has been edited in the IDE buffer.
JetBrains (IntelliJ, WebStorm, etc.)
Settings > Tools > External Tools > Add:
| Field | Value |
|---|---|
| Program | agent-doc |
| Arguments | run $FilePath$ |
| Working directory | $ProjectFileDir$ |
Assign a keyboard shortcut (e.g. Ctrl+Shift+S). The External Tool shows output in the Run panel — progress messages, merge status, and errors all appear there.
VS Code
Add a task to .vscode/tasks.json:
{
"label": "agent-doc run",
"type": "shell",
"command": "agent-doc run ${file}",
"group": "build",
"presentation": {
"reveal": "silent",
"panel": "shared"
}
}
Bind to a keybinding in keybindings.json:
{
"key": "ctrl+shift+s",
"command": "workbench.action.tasks.runTask",
"args": "agent-doc run"
}
Vim / Neovim
nnoremap <leader>as :!agent-doc run %<CR>:e<CR>
The :e<CR> reloads the file after the response is written.
General tips
- Don't edit during run — the merge-safe flow handles it, but it's simpler to wait for the progress indicator to finish.
- Auto-reload — JetBrains and VS Code auto-reload files changed on disk. Vim needs the
:ereload. - Diff gutters — after run, your editor shows diff gutters for everything the agent added (because agent responses are left uncommitted).
Agent Backends
agent-doc has an agent-agnostic core. Only the "send prompt, get response" step varies per backend.
Claude (default)
The built-in Claude backend runs:
claude -p --output-format json --permission-mode acceptEdits
Session handling:
- First run:
--continue --fork-session(inherits context from the most recent session) - Subsequent runs:
--resume <session_id>(continues the specific session)
The backend removes the CLAUDECODE environment variable to prevent nested session conflicts.
Custom backends
Configure in ~/.config/agent-doc/config.toml:
[agents.codex]
command = "codex"
args = ["--prompt"]
result_path = ".output"
session_path = ".id"
| Field | Description |
|---|---|
command | Executable name or path |
args | Arguments passed before the prompt |
result_path | JSON path to extract the response text from output |
session_path | JSON path to extract the session ID from output |
Backend contract
Each agent backend implements: take a prompt string, return (response_text, session_id).
The prompt includes the diff and full document. The backend handles CLI invocation, JSON parsing, and session flags.
Per-document override
Set agent: in the document's YAML frontmatter to use a specific backend for that document:
---
agent: codex
model: gpt-4
---
Or override per-invocation:
agent-doc run session.md --agent codex --model gpt-4
Building
Developer setup
git clone https://github.com/btakita/agent-doc.git
cd agent-doc
make release # build + symlink to .bin/agent-doc
Make targets
make build # Debug build
make release # Release build + symlink to .bin/agent-doc
make test # Run tests
make clippy # Lint
make check # Lint + test
make precommit # Full pre-commit checks (lint + test + audit-docs)
make install # Install to ~/.cargo/bin
make init-python # Set up Python venv with maturin
make wheel # Build wheel and install into venv
.gitignore
The following are gitignored:
target/
.bin/
.agent-doc/
.venv/
CLAUDE.local.md
.idea/
Release build
The release profile optimizes for binary size and performance:
[profile.release]
opt-level = 3
lto = "fat"
codegen-units = 1
panic = "abort"
strip = true
Conventions
Code style
- Use
clapderive for CLI argument parsing - Use
serdederive for all data types - Use
serde_yamlfor frontmatter parsing - Use
similarcrate for diffing (pure Rust, no shelldiffdependency) - Use
serde_jsonfor agent response parsing - Use
std::process::Commandfor git operations (notgit2) - Use
toml + serdefor config file parsing - No async — sequential per-run
- Use
anyhowfor application errors
Instruction files
CLAUDE.mdis the primary instruction file- Personal overrides:
CLAUDE.local.md(gitignored) - Actionable over informational. Instruction files contain the minimum needed to generate correct code. Reference material belongs in
README.md. - Update with the code. When a change affects patterns, conventions, or module boundaries, update instruction files as part of the same change.
Documentation maintenance
- Generated diagrams, architecture notes, and workflow references belong under
docs/, not in session documents only. - Update affected docs in the same change when implementation, workflow, or architecture behavior changes.
- When adding, renaming, or removing docs pages, update
docs/README.mdanddocs/SUMMARY.mdin the same change. - Keep specs normative. Reference docs may explain behavior and failure modes, but must point back to specs or source when behavior is enforced there.
Version management
- Never bump versions automatically — the user will bump versions explicitly.
- Commits that include a version change should include the version number in the commit message.
- Use
BREAKING CHANGE:prefix in VERSIONS.md entries for incompatible changes. - Update
SPEC.mdwhen agent-doc functionality changes (commands, formats, algorithms).
Workflow
Follow a research, plan, implement cycle:
- Research — Read the relevant code deeply.
- Plan — Write a detailed implementation plan.
- Implement — Execute the plan. Run
make checkcontinuously. - Precommit — Run
make precommitbefore committing.
FFI Relocation Pattern
How to move an FFI function from the main agent-doc cdylib crate into a
focused crate such as agent-doc-ffi without dropping its symbol from the
shipped libagent_doc.{so,dylib,dll} export table that the editor plugins
(JetBrains, VS Code) link against.
This is the "pure FFI relocation" pattern proven across #k9e1 / #epv5 /
#vb8h / #e130 (POC: agent_doc_free_string + agent_doc_free_state moved
to a lower crate and verified still exported) and tracked under #yybc.
The problem
Editor plugins load a single libagent_doc.{so,dylib,dll} and resolve
agent_doc_* symbols at runtime via JNA / FFI. There is exactly one cdylib
— the agent-doc crate. When an #[no_mangle] pub extern "C" function is moved
into a dependency crate, it is no longer defined in the cdylib's own
compilation unit. A plain Rust re-export is enough for Rust call sites, but the
static linker will strip the re-exported symbol from the cdylib's dynamic
export table because nothing in the cdylib crate references it — the editor
plugin then fails to find it.
The pattern
- Move the function into
agent_doc_ffior another focused crate, keeping its#[no_mangle] pub extern "C"signature unchanged. - Re-export from the main crate's
src/ffi.rs:#![allow(unused)] fn main() { pub use agent_doc_ffi::*; } - Force-link the symbols so the linker keeps them in the cdylib export
table. Add a never-called reference function in
src/ffi.rsthat names each relocated symbol with its exactextern "C"type, and reference it fromlib.rsso it lands in the cdylib's compilation unit:
Every relocated symbol must appear in this function. Adding a function to core's FFI without adding its reference here will silently drop it from the export table.#![allow(unused)] fn main() { /// Never called — the symbol references just need to exist in the main /// crate's compilation unit so the linker keeps them in the cdylib export /// table. #[allow(dead_code)] fn force_link_ffi_symbols() { use agent_doc_ffi::{agent_doc_free_string, /* ...all relocated fns... */}; let _: unsafe extern "C" fn(*const c_char) = agent_doc_free_string; // one typed reference per relocated symbol } }
Verification (mandatory on every FFI-touching relocation)
cargo build --release
nm -D target/release/libagent_doc.so | grep -c ' T agent_doc_'
nm -D target/release/libagent_doc.so | grep ' T agent_doc_' # spot-check names
The T (text/exported) count must not drop after the move. Run this gate on
every wave that relocates FFI bodies — it is part of the orchestration
extraction acceptance criteria (see
the crate decomposition PRD).
Why one cdylib
Editor plugins ship and link a single shared library. Splitting the FFI surface
across multiple cdylibs would force plugin changes and multi-library loading.
Keeping ffi.rs and the cdylib in the agent-doc shell crate — with relocated
bodies re-exported and force-linked from focused crates — preserves the
single-library contract while still letting the heavy logic live in lower
crates.
agent-doc Functional Specification
-
Hot-path authority is singular and named consistently across the binary and editor plugins: Lazily current owns the live document, and
state.dbowns intent/lifecycle coordination. Normal execution must not read, write, import, scan, or replay filesystem live-buffer, patch-inbox, queue-journal, queue-delete, continuation, capture, cycle, turn-scope, editor-op, transport-health, hook-session, or cooldown state. Snapshot/CRDT files are permitted only as cold recovery projections after the authority is unavailable; they never vote in a live merge. There is no compatibility fallback to retired sidecars. -
The document write state machine is
IntentCaptured -> CanonicalApplied -> ReplicaAccepted -> ReplicaVisible -> DiskProjected -> Committed. A transition may advance only one edge after its matching proof, duplicate/reordered events are idempotent, endpoint churn is a self-loop, and no recovery command may skip an edge or reconstruct intent from a projection. Queue deletion uses the same rule: the exact Lazily authority shape is the compare-and-swap base, so a missing historical queue item is deletion—not an input to union/replay. -
A controller recycle invalidates relay membership even when an editor still holds a cached client. Forced editor refresh must retire that client and issue a fresh registration. Every deferred document write, including an explicitly authorized
--force-diskprojection, retains its full base and target in Lazily state so reconnect restores or component-merges the editor buffer without consulting Git HEAD. Compact Exchange must compose active captured/deferred response lineage before archiving. -
Captured-response recovery must persist both the response and its complete editor-visible baseline in Lazily
ResponseCapturedstate; hashes alone are not replay authority. A partially materialized response may be reconciled only from added nonblank lines whose multiplicities are proven by the captured baseline and response, with at least two matching lines. The reconciled target must pass through document/editor authority, retain any unrelated operator text, and then replay and commit the complete response once. A legacy hash-only capture may consult a byte-hash-matching GitHEADonly as a historical baseline anchor and must fortify Lazily state with the recovered content; it must never restore the working tree toHEAD. Template-mode raw captures must become explicit exchange patches before strict replay. An open captured cycle may cross a recovered commit boundary only when the captured response is materially present inHEAD.
Language-independent specification for the agent-doc interactive document session tool. This document captures the exact behavior a port must reproduce.
Individual specs are in specs/. This file is the index.
Notable invariants:
- Real-time document authority is operator-first. The editor-visible document
state owns every operator-authored change, including ordinary non-prompt text,
queue/backlog edits, frontmatter, comments, whitespace, and partial words.
content_ours, snapshots, and legacy editor-content receipts are merge candidates only; they must never authorize a recovery, IPC patch, harness hook, or disk write that drops operator-visible text. Snapshots are durable backup/audit state, not hot-path authority; legacy snapshot-derived candidates must be narrowed and merged into the latest source-of-truth document. The document realtime state machine, source-authority projection, editor/disk epochs, owner leases, and in-flight apply facts belong toagent-doc-document-realtimeand are lazily-rs-backed state, not turn-local sidecars. The realtime parse state projection is also lazily-backed: parse issues surface as realtime editor diagnostics and preflight repair is a crash/retry backstop, not the hot-path way to make live documents parse.agent-doc-documentremains the pure document model/projection crate. CRDT merge and document realtime apply/verify do not commit; the document turn lifecycle owns commit decisions. An attached-document write waits for typing quiescence, rebases the agent target over the latest canonical CRDT cut, and applies backpressure while any prior delivery frontier lacks visible-replica acknowledgement. Repeated intent is coalesced to the latest target; an accepted CRDT replacement is never replayed through legacy editor IPC, and disk is only a post-acknowledgement projection of exact canonical text. Bounded convergence failure retains the change and fails closed instead of issuing a competing disk write. An editor-visible write acknowledgement is durable authority only when its Lazily event carries the full acknowledged content; hashes validate and index that content but cannot reconstruct it. A legacy hash-onlyalready_appliedreceipt gets one bounded live-buffer publication attempt that upgrades the same patch fact. If no editor replica can publish, the full target is retained as a Lazily deferred-write intent and the cycle fails promptly without file IPC or a disk projection. An empty delivery target set is never convergence for an editor-owned write. Editor-selected tmux focus is likewise latest-wins per project. A stale or pruned actor projection cannot hide a still-running document owner when the latest open session log, live pane, and exact process-tree document binding all agree; incomplete or cross-document evidence fails closed without repairing the document or ownership projections. The normal preflight boundary also recognizes the narrowly provable legacy corruption shape where a structurally complete session document is repeated byte-for-byte two (or a power-of-two number of) times. It automatically coalesces that replay to one projection through the same CRDT/ACK path before parsing, diffing, or dispatch; the agent must never run a document-repair workflow for this transport artifact. Non-identical content is never eligible. Editor delivery must target theeditor_idfrom the newest live reliable-sync registration when an owner lease exists; untargeted file-IPC fallback is not delivery proof for an editor-owned document. See Real-Time Workflow Authority and Turn Lifecycle Authority. Invariant: parse state projection drives editor diagnostics; preflight repair is not the live parse hot path. agent-doc commitremains snapshot-selective. It may repair narrowly-classified missed agent-owned drift before staging, but it must not absorb free-form user prompts from the working tree. Already-committed historical response drift may repair the snapshot only when the working tree matchesHEADmodulo transient boundary /(HEAD)markers. On an already-current no-op closeout, a stale agent-owned exchange collapse (committed### Re:heading missing from the working exchange, with only committed exchange lines remaining and a duplicated queue-prompt blockquote proving the missing response id) is restored fromHEAD; independent local queue/backlog/prompt drift outsideagent:exchangeremains visible and uncommitted.- When the snapshot already matches
HEAD, post-commit local drift classification must reuse the canonical prompt-bearing diff classifier. Queue/backlog directive edits that preflight would surface as prompt targets areuser_follow_updrift, not anomalousworking_tree_edits; inline content corrections remain working-tree edits. agent-doc prompt --allmust normalize OpenCode horizontal permission prompts for any external prompt consumer. When the OpenCode pane exposes option controls but no explicit← ...question line, the reported question is the neutralPermission required; earlier shell command text, including ANSI-literalprintfprompt fixtures, must never become a user-facing prompt label. First-party JetBrains and VS Code plugins do not consume this polling surface.agent-doc memory index/searchmust use the sharedtsift-memorylibrary crate to index/search agent-doc session surfaces (agent:backlog,agent:review,agent:done,agent:icebox, and liveagent:exchangeresponses) in.tsift/memory.db. It must not invoke or embed the heavy tsift CLI codebase index on the per-cycle hot path.preflightandplanmay reuse that shared library path to emit advisorysemantic_completion_matchcandidates when open backlog/review items or free-text queue prompts are highly similar to done-state memory events; those advisories are proposals only and do not mark work done without a deterministic closeout path.- Managed supervisor sessions own both the child PTY and a terminal-state model for that PTY. Filtered child output must be fed into an
alacritty_terminalviewport, and readiness/help/protected-prompt checks must prefer that current screen text before falling back to the raw byte ring, so cursor rewrites and line clears are not interpreted as append-only tmux scrollback. In managed OpenCode sessions, the supervisor stdin forwarder must translate legacy arrow-key escape sequences to Tab/BackTab only while the current child output is an OpenCode horizontal permission prompt (Allow once/Allow always/Reject). Normal OpenCode composer arrow editing must pass through unchanged, and permission-prompt arrows must not leak literal^[[C/^[[Dtext into the TUI. - When
agent-doc commitsees aHEAD-current snapshot plus a later user follow-up prompt, it must leave that prompt uncommitted for the next response cycle and logpost_commit_user_follow_up; that safe follow-up shape must not be reported asprior_patchback_without_response_bodyorout_of_band_write. If the persisted cycle is already terminal, this is a prompt handoff only: it must not re-emitcommit_noop/commit_already_current, rewrite committed cycle state, or describe the prompt handoff as another closeout. - Extreme snapshot/file drift does not relax that rule for tracked documents. Wholesale snapshot re-sync from the live file is reserved for bootstrap scaffold snapshots on files with no
HEADentry yet; tracked documents stay selective so unanswered prompts cannot be committed during preflight. - Post-commit cleanup keeps the committed blob, snapshot, and user-facing document state in the same clean boundary shape. Transient
(HEAD)/ boundary-only churn must be collapsed after commit instead of being left as post-success working-tree dirtiness, and that cleanup must preserve comment-only user notes that live outside the committed response. - If post-commit cleanup proves the working tree lost committed
HEADcontent and added no carry-forward user directive,agent-docmust restore the file to the committedHEADblob and push that committed content to a live JetBrains editor buffer through a guardedrefresh_contentsocket message. The editor may apply the refresh only when the live buffer still matches the stale content hash/length; any changed buffer must reject the refresh so legitimate carry-forward edits are not clobbered. With a live editor listener, the working-tree disk write may be skipped only after that editor refresh is acked; a no-ack/error refresh must fall back to the authoritativeHEADdisk write and logtransport=disk_after_failed_editor_refresh. - Post-commit cleanup must also fail closed against stale editor buffers that resurrect completed queue prompts. If
HEADcontains a completedagent:queuerow and the working/editor buffer contains an active row with the same prompt text, cleanup must restore the committed completed row before any generic editor-buffer flush can persist the stale active copy. This applies to pinned/id-backed queue heads and answered free-text rows, preserves unrelated new queue work, and is directional: a newly struck editor-owned row whenHEADstill has it active remains editor-owned queue state. - Queue-head closeout consumption must require proof for the exact active head. Id-backed queue heads require an explicit same-id closeout signal such as
--done,--backlog-gate,--review-resolve, or--backlog-edit; free-text heads require an explicit current-response target such as the> **Queue prompt:**echo for that head. Generic repair/no-op responses and unrelated exchange answers must keep the head runnable. - Before that cleanup runs, newly patchbacked
### Re:headings in append-mode exchange content must surface as transient(HEAD)markers in the working tree/editor buffer so the user can distinguish the fresh uncommitted response from already-committed transcript history. The write path, FFI/editor patch paths, and fallback patchers must all preserve that temporary marker until the post-commit cleanup removes it. - Final responses and their complete editor-visible baselines are durably captured as
ResponseCapturedfacts instate.dbbefore canonical application. Interrupted-cycle replay uses only that ledger intent and rebases its semantic cell on Lazily current; recovery projections and Git history cannot synthesize or supersede the response. - Cycle-state lifecycle transitions append stable, idempotent state-backbone facts and advance the formal write phase only after matching proof. No JSON cycle/capture file participates in normal execution.
- Git closeout appends a
CommitObservedfact carrying the exactHEADSHA after a successful real commit or already-current no-op closeout. session-checkandpreflightread the state-backbone projection as the only open/terminal lifecycle authority. Projection-only open cycles fail closed; missing/corrupt ledger state is an explicit recovery condition, never permission to import a sidecar.agent-doc preflightmust surfacewarnings[].code = "stuck_captured_cycle"when persisted cycle state sayscommittedbut the matching durable capture's materialized response body is absent fromHEAD. Detection must match the active cycle/capture id and response hash, ignore discarded captures, tolerate template patch materialization and prompt-prefix stripping, and stay quiet whenHEADalready contains the response.- Every document turn stage that can observe the LIVE controller/supervisor — preflight, generation, stream, finalize/write, commit, compact, session-check, and closeout evidence — must detect when its recorded launch
controller_binaryno longer matchescurrent_binary_identityand write an idempotent per-document recycle request. The request is mandatory for proven staleness even when proactive auto-recycle is disabled, checkpoints CRDT state first, and is honored only at the supervisor's safe idle boundary so it cannot sever an open closeout. Preflight still emitswarnings[].code = "supervisor_binary_stale"and session-check emits[session-check] WARNING:, but no stage may insert an operator-facing stale marker into document content. Detection remains fail-open when status or identity evidence is unavailable. - Quiescent idle-queue observation must be lazily invalidated. With live editor authority, the supervisor compares a compact canonical CRDT state vector plus replica liveness/convergence state before materializing canonical markdown; with disk authority or an intentionally suppressed controller probe, it compares file metadata. Full-text hashing, serialization, and queue parsing run only when that revision changes, when the compact probe fails, or on a bounded safety reconciliation. Installed-binary staleness remains a prompt local probe outside this throttle.
- A reliably-open editor authority with zero registered relay replicas is also stale at every observable turn stage, even when the supervisor binary inode is current. The complete write target remains in CRDT/Lazily state, agent-doc requests the same owner-scoped safe recycle automatically, and recovery retries the binary-owned finalize transaction after reconnect; it must not require response repair or an implicit force-disk write.
- Stale-controller replacement must be directional. The caller stamps its full binary identity on controller RPC requests; a replacement shutdown is accepted only when that identity proves a newer release or a newer same-version build timestamp than the controller bootstrap identity. An older caller must adopt the newer live controller rather than tear it down. This proof applies before every stage-level recycle request so a stale controller cannot refuse a genuinely newer install merely because its own process-local executable still matches its bootstrap record.
- Supervisor recycle in-flight state is a lazily-backed project-controller projection, not a
.agent-doc/marker authority. Recycle starts publishStateFact::SupervisorRecycleStartedand settle paths publishStateFact::SupervisorRecycleSettled; callers inspectSupervisorRecyclePhase::{InFlight,Settled}through controller RPC helpers. Route/direct/dispatch-only callers must wait throughwait_for_supervisor_recycle_settle_for_filebefore injecting prompt text so triggers are not dropped across supervisorexecve. The old recycle-in-flight marker sidecar is retired. - Installing the cdylib must send the shared
reload_libraryintent to every live editor registration after the versioned symlink swap and controller/supervisor auto-recycle. The controller enumerates Lazily registrations, deduplicates by project/PID/editor identity, and sends through the PID-scoped endpoint. The operation is best-effort and reports projects, endpoints, deliveries, and failures; a disconnected editor reloads lazily on its next native call. JetBrains and VS Code implement the same intent name and re-register open replicas after reload. No filesystem broadcast or compatibility watcher exists. Running controllers/supervisors still recycle onto the new binary at their idle boundary. - Every stale supervisor/controller recycle request must be coupled centrally with an
ack_recovery_force_refresheditor event, regardless of which turn stage observed the stale binary, route, or zero-replica authority. JetBrains and VS Code must treat that event, and every native-library reload broadcast, as a forced replacement of cached open-document forwarders so the retained response can resume against the replacement controller without an operator repair or editor restart. - The relay must retain recognized editor replica identity metadata with membership and, when a replacement registers, retire prior memberships only when their encoded editor process is provably dead. This pruning occurs on registration and update-driven reattachment rather than idle polling, precedes replacement bootstrap/convergence, and must never infer liveness or ownership for opaque identities. A crashed or restarted editor therefore cannot leave an unreachable pending-delivery member blocking
write --commit, while independent live replicas remain protected. - The binary must detect a stale editor plugin and warn (
#stale-plugin-detect). Each plugin reports its release version in its live Lazily registration; preflight compares that registration against the build-time expected JetBrains/VS Code version and emits one non-blockingstale_pluginwarning per distinct kind+version. No live-buffer file is written or consulted. Version comparison is dotted-numeric with leadingvand prerelease/build suffixes ignored; unparseable versions and source-absent packaged builds fail open. - Supervisor recycle/restart decisions must not sever an open agent-doc closeout cycle. While a cycle is open, stale auto-recycle, explicit admin recycle, editor write-wedge recycle, and failed-reexec escalation all defer. If the cycle never closes past the bounded deferral threshold, the forced recycle preserves the open durable checkpoint rather than abandoning it; the fresh supervisor boot then adopts a surviving child or re-dispatches the interrupted turn exactly once. Stalled-cycle abandonment may clear only old, no-IPC cycles at a true harness turn boundary, never a long active turn mid-closeout.
- A managed supervisor owns one document and must never use a process-global harness-history selector to replace its child. Claude
--continue, Codexresume --last, and OpenCode--continuecan select another document's most-recent conversation even while CP actor/registry ownership remains correct. Replacement launches therefore start fresh and re-submit the owning document's trigger unless a future implementation has durable, exact document-to-harness-session proof. - Binary installation is an atomic promotion and ordered handoff boundary. Local install paths must build a complete executable, copy it to a same-directory staging path, preserve executable permissions, and rename it over the installed binary; they must never unlink the live path before the replacement is complete. Background supervisor auto-install must defer while the source checkout is dirty, suppress nested generic recycle-on-install fanout, durably mark route-owned supervisors before controllers, and issue exactly one fleet recycle wave. Live child PTYs remain owned across the in-place supervisor exec; a failed handoff must keep the prior process/binary serving rather than reap the pane.
- Recycle-yield for self-driving queue loops uses the same controller projection, not a sidecar file. The supervisor idle watch records
stale_binary_drainorstate_flush_drainas aSupervisorRecycleStartedreason; preflight, session-check, and queue continuation detection suppress in-session looping whilesupervisor_recycle_yield_pending_for_filereports the projection in flight, and they surfacequeue_recycle_yield=true/RECYCLE_YIELD_GUIDANCEso the harness yields one boundary and restarts on the fresh supervisor. Clearing a recycle-yield settles only those yield reasons and must not clear real admin/auto-install in-flight recycle state. - Queue drain-stall continuation-pending state is a lazily-backed project-controller projection, not a
.agent-doc/marker authority. A clean closeout that still requires queue continuation publishesStateFact::QueueDrainStallContinuationRecorded; the next preflight classifies#qstallguardfacts and publishesStateFact::QueueDrainStallContinuationClearedafter every reconciliation outcome. Supervisor idle-watch drain progress also clears the same projection so an actively progressing supervisor drain cannot false-firequeue_stall_detectedin the next agent preflight. - Streaming agent paths must also save durable partial-response checkpoints while generation is still in progress. The first non-empty partial response and then changed partial output at most once every 30 seconds are appended as cycle-scoped recovery facts in
state.dbfor diagnostics/manual recovery, without advancing the cycle toresponse_captured. Once the persisted cycle changes, commits, or is abandoned, the writer must stop instead of updating the retired cycle's ledger projection. - Partial model output must never be written into the authoritative session document. Streaming and orchestration buffer generation outside the document, may update only the
state.dbrecovery ledger, and submit exactly one complete final response to the write+commit boundary. Bare/non-committingagent-doc writeon a session document fails before stdin, capture creation, document mutation, queue consumption, or lifecycle advancement. - Outside fenced code examples, a session exchange may contain at most one
agent:boundarymarker and it must occupy a standalone line. Final placement removes every prior boundary before appending the singleton at exchange end; duplicate or inline markers fail the mandatory integrity gate. Explicit repair may normalize historical partial-patchback artifacts, but ordinary preflight/finalize/compact/session-check must never launder them into a new baseline. - Every repair mutation must compare-and-swap against the exact realtime authoritative document image from which the repair was computed. After the write, repair must prove both the realtime authority and the disk projection are byte-identical to the intended repaired image before it may save a snapshot, commit, or report success. A concurrent/stale projection mismatch fails closed and must never be merged back as operator content, because doing so can resurrect fragmented response bodies and duplicate boundaries. If the CAS target was durably retained but the editor owner has zero registered replicas, explicit repair may use the audited force-disk authority to project only that exact retained target; ordinary writes remain deferred, and repair must preserve force-disk reconnect lineage after exact disk proof so a stale editor buffer cannot overwrite the repair when it returns.
- Multi-stage repair must settle every superseded deferred target after exact authority/disk proof and retain at most the newest reconnect target, using the original editor cut as its content-bearing merge base. Settling the newest delivery must not reveal an older intermediate marker, boundary, or fragmented response image.
- When a newer response target already equals live CP authority, it supersedes any older deferred response target instead of merging that stale assistant tail back into the document. Reconnect lineage rebases on the exact superseded target: an unchanged failed-ACK editor cut receives the newer target directly, while later operator prompt nodes are preserved through semantic response-tail reconciliation with one terminal boundary.
- Every deferred-write composition and settled-operator rebase must canonicalize boundary control state from the newest target branch: outside fenced examples the result retains exactly that branch's singleton
agent:boundarymarker at exchange end. A forced editor refresh is not a reconnect merge or delivery operation: it captures the exact visible editor bytes, validates them, and register/swaps the replacement replica from that same baseline under a generation fence. It never preinstalls or saves a deferred target. Retained mutations replay afterward through the ordered document-cell/CRDT path, so a refresh cannot overwrite a prompt or resurrect deleted queue state. - Terminal
session-checksuccess additionally requires byte-for-byte equality between current canonical document authority and disk. Any mismatch fails closed, records both hashes, and requests stale-editor-replica supervisor recovery before a caller can report the response committed. When those surfaces agree on content that differs from committedHEADonly by transient agent-doc markers, session-check must automatically clear all superseded deferred intents, compare-and-swap the exact committed bytes through editor authority, and proveHEAD == authority == disk; substantive post-commit operator edits are never eligible for this self-heal. - Response placement, answered queue-head removal, backlog/review/done mutations, snapshot publication, and commit form one final-response transaction. A validation or convergence failure leaves none of those effects authoritative.
AlreadyAppliedcan advance that transaction only when the visible document contains the exact complete final payload expected by the same transaction; a response prefix, partial checkpoint, prior-cycle body, or heading-only match is insufficient. - Every appended response must cross a commit boundary in the document turn lifecycle unless the user explicitly asks to leave it uncommitted. The normal happy path is
agent-doc finalize <file>; the documented repair path for an already-present prompt isagent-doc write --commit <file>. CRDT merge/realtime convergence may provide the verified document state for closeout, but it must not run the commit as a side effect. - Strict
write --commitwith empty stdin may only commit when it first recovers an agent-doc-owned visible response or applies explicit backlog/review/icebox mutations. A live editor/CRDT projection is harness-neutral authority for this recovery: even when cycle state is alreadyCommittedand the snapshot/HEADimage is compacted or stale, a response delta proven in the editor buffer must be adopted into the snapshot and carried through the normal commit/session-check boundary exactly once. The binary must never recommend restoring the editor buffer toHEADfor that state. Empty stdin with no recoverable response and no tracked-work mutation must fail before commit so live prompt drift remains unresolved for the next cycle. - Once a closeout cycle reaches
Committed, a delayed editor intent for that cycle is rejected by its state-ledger phase and generation fence. No alternate delivery transport exists. Active delivery timeout retains the same intent instate.db, performs no direct write for an attached document, and resumes from the recorded phase. Terminal lifecycle updates are idempotent. - Explicit
--force-diskcloseout is the operator-controlled direct-write escape hatch for stale/wedged editor listeners. When used with strictwrite --commit/finalize, the force flag must apply to the whole binary-owned closeout path: response placement, pending-maintenance reap, queue consumption, done-id marking, and free-text queue strike. Ordinary preflight/route maintenance still fails closed under active listeners unless the caller explicitly selected this force-disk recovery path, and force-disk maintenance writes must remain attributable inops.log. A live editor buffer that diverges from disk because the operator has unsaved edits is a valid state, not a wedge (#unsaved-buffer-divergence-valid): theretry after typing stops/visible_write_deferred_live_buffer_changed/live_prompt_driftdeferral is the guard working as designed. Harnesses must not chainmake install+agent-doc admin recycle+--force-diskto force a closeout through it, and must escalate to the stale-binary recycle path only when the drift is actually traced to astale_install/ recycle-yield projection; once the response already reachedcommittedinHEADwithsession-checkOK, state-only leftovers left in the working tree reconcile on save and must not be force-disked. See runbooks/commit.md#unsaved-buffer-divergence-valid. - Every
agent:exchangewrite emitsexchange_write_diagnosticwith the transaction id, writer identity, named intent, expected/current Lazily generations, affected semantic nodes, before/after canonical hashes, rebase/conflict result, and receipt phases. All template, queue, compact, normalization, and repair mutations use node/component-scoped socket intents;fullContent, file IPC, degraded attached-editor disk fallback, and compatibility payloads do not exist. The plugin validates target-node preconditions immediately before apply, and the state machine advances only from typed Lazily accepted/visible receipts. Duplicate-prompt repair is response-block aware and operates on the verified canonical result before disk projection; it never adopts post-preflight operator scratch text into an agent-owned snapshot candidate. agent-doc routedefaults to a bounded 500ms quiescence gate before it inserts a missing session id, scrubs duplicate prompt comments, or submits a managed/dispatch-only reopen. The route gate must observe both filesystem mtime idle and the shared editor typing indicator idle for the debounce window; if either remains active through the bounded wait, route fails closed before document mutation or pane input. Editor integrations may pass--debounce 0only after they have saved the focused document themselves; the JetBrainsRun Agent Docpath uses that focused-save fast path so dispatch is not delayed by a second debounce.- Direct-pane route submit acceptance must not treat a single empty pane capture as proof that the harness received the prompt. Empty captures must remain stable before acceptance, visible drafted triggers still get a bounded submit-key retry, and Codex accepted-without-dispatch-start proof must attempt one late Enter retry when the same routed prompt is visibly sitting in the input. Once the full trigger transport succeeds, absence of dispatch-start proof never authorizes another full-payload send; post-send recovery may send only a bare submit key while the exact routed draft remains visibly present.
- When
route --dispatch-onlyqueues a prompt-bearing editor rerun behind a busy actor inagent:queue, the rerun must be inserted ahead of queued active-loop items (priority preempt,#jb-run-preempt-autoloop-priority): a manualRun Agent Docpreempts the loop instead of landing at the tail. Route-owned queue writes must never addauto; when they touch a legacyagent:queue autotag they stripautowhile preserving other attributes. A bare slash-command line such as/clearin an exchange diff is prompt-bearing even without a❯prefix, and route must short-circuit it intoagent:queuefor the idle supervisor instead of submitting it as a normal agent-doc turn. The priority insert lands after any leading queue directive (preset / start fence) and must not supersede a lone active-loop prompt, so the queued item is preserved (manual prompt first, then the queued item) rather than replaced. If an explicit editor run finds no new prompt-bearing diff but the document already has a startable inactive queue head (queue_active: true, a start fence, or legacyauto), route must promote that head toqueue_active: true, sync the snapshot, and let the supervisor idle-queue watch drain it after the busy pane returns to idle. The idle-queue watch must treat the hook-owned turn-active marker as authoritative over renderer prompt heuristics: queued work, slash-command heads, and automatic context-reset clears may run only after the full turn reaches the Stop/idle boundary. Queued/clear//newheads and context reset clears must use the supervisor's clear submit path, not generic prompt dispatch, so they are recovery controls and remain gate-exempt while still submitting Enter to the owner pane. A non-interruptingsession clearon a busy active auto-queue must publish exactly one deferredQueueContextClearDeferredprojection for the supervisor's next proven idle boundary; repeat clears while that deferred projection is pending must report "already deferred" and must not refresh the projection or inject another/clearinto the active turn. After the idle-watch submits the deferred clear, it promotes the projection toQueueContextClearStarteduntil the cleared pane settles. A synthetic active queue head that consists only of a slash command, ignoring surrounding whitespace, is command-only for preflight, plan, and direct run: it must not openpreflight_started, must not become prompt targets/repo actions, and must stay live inagent:queuefor supervisor idle submission. - A managed owned pane must not remain indefinitely split between authoritative ready state and stale busy renderer state. When actor state, supervisor actor state, and controller lease are all
readybut the live pane probe still reportsalive-busy/prompt_ready=falsefrom a recoverable stale queued-draft cue, route-owned completion and the idle-queue watch debounce that ready/busy conflict for the same four-poll window used by stale-busy idle repair, emitowned_pane_ready_busy_conflict, and then let queued work proceed. Genuine active-turn, permission, hook-review, shell-search, help, and clean-exit blockers remain protected.agent-doc session statusmust make the ready/busy conflict explicit and prefer bounded reconcile/clear guidance over pane restart. - A plain
route --dispatch-onlyreopen with no prompt-bearing work (an editorRun Agent Doc) against abusyauthoritative actor must fail closed with the busy-not-ready diagnostic after focusing the pane, not return focus-only success — otherwise it reports a routed run to the editor caller while injecting nothing, leaving the operator with no feedback after the full ready-wait timeout (#jb-run-agent-doc-command-route-miss). The editor must classify that busy-not-ready outcome as a "session still running" notification surfaced immediately; the narrow exception is the already-startable inactive queue-head promotion described above. Only a still-bootingstartingpane stays a silent retry. - Once route admission classifies
managed_reopenordispatch_only_reopenas an explicit operator reopen, a stale same-generation in-flight dispatch receipt must not coalesce that controller dispatch. Automatic/non-operator re-dispatches retain durable in-flight backpressure, and the JetBrains action layer may still coalesce duplicate clicks while the same boundededitor_routerequest is alive; those are separate dedupe boundaries. - Template write paths must fail closed when a response contains patch/replace markers outside code blocks but no closed patch blocks parse. Such malformed patchbacks must log
template_patchback_parse_shapeandtemplate_patchback_malformed_rejectedwith response hash, marker count, patch count, exchange patch count, unmatched length, and source before returning an error; they must never be synthesized intoagent:exchangeas raw unmatched content. Socket IPC attempts must also logipc_socket_attemptplus lazily visible-write receipt hashes/lengths so corruption reports can distinguish malformed payload parsing, editor projection drift, and stale fallback writers. - When an
agent-docturn also includes ordinary repocommit + push, manual git commits must exclude the active session document. Agents may commit the non-session repo files first, but the session document still closes throughagent-doc finalize <file>/write --commit <file>, and the push happens after that binary-owned closeout commit lands. - Narrowed/path-scoped manual repo commits must fail closed on staging drift. The agent must resolve the exact intended non-session path set first, run stage commands only for that set, stop immediately if any stage step fails, prove the staged diff still matches the intended set before
git commit, and use an explicit pathspec commit or equivalent isolated-index strategy so unrelated pre-staged entries cannot leak into the commit. session-checkmust warn on likely partial manual staging closeouts: if the latest repo commit (or dirty submodule repo surfaced by the owning worktree) changes relevant source/test paths and tracked dirty or staged companion changes remain with overlapping changed string literals, the closeout is suspect because local verification may have run against the dirty worktree while CI sees only the committed tree.agent-doc finalize <file>is the binary-owned happy path for session responses: it must fail before mutating non-git documents, run the normal write pipeline, invoke commit, and refuse success unless the cycle closes incommitted. If the active prompt is a synthesized activeagent:queuehead and the captured response heading targets that queue item or its#id, finalize consumes the queue head even when the visible document had no queue-head diff; unrelated baseline prompts must not consume it.- A semantic response-cell closeout must not commit ahead of visible convergence.
ResponseCellAddedproves durable idempotence only; finalize must apply normal typing/ACK backpressure, materialize the acknowledged canonical cut to disk even when an attached authority currently has zero live relay members, and only then snapshot and commit. Materialization proof is semantic across transient(HEAD)and boundary annotations. Before adding the latest complete response, the operation anchors at the last unchanged committed response inHEAD, removes only later uncommitted assistant-response nodes, preserves all operator prompts, and emits one terminal boundary; interrupted partial/full variants must not accumulate or require repair. The committed blob and working-tree projection must therefore begin closeout byte-identical instead of relying on later repair. - Inter-queue-item dispatch is convergence-gated (
#fullboundary): item N+1 must not dispatch until item N proves a quiescent close — cyclecommitted, the live editor buffer proven converged ==HEADvia lazily editor receipt (not merely a git commit + fire-and-forget VCS-refresh), this document's editor-IPC inflight count drained to 0, and the authoritative actor idle (dispatch-ready prompt, no active turn). The decision core is the pureconvergence_gate::convergence_gate_decision(ConvergenceFacts)returningDispatch(all four proofs),Defer { unmet }(not yet quiescent, within the bounded timeout), orForceDiskFallback { unmet }(bounded timeout exceeded while unconverged). When the boundary trips the bounded timeout (editor IPC wedged/dead), a--force-diskwrite is permitted to avoid a permanent stall, but it is an ERROR condition: the closeout must emit an ERROR-levelconvergence_gate_force_disk_fallback severity=error … playback=<path>ops-log line and persist a replayable operation-playback artifact (convergence_playback::ConvergencePlayback) to.agent-doc/playback/<doc-hash>/<cycle-id>.jsoncapturing the ordered IPC attempt sequence (patch_id/transport/receipt/inflight), snapshot/baseline/HEAD hashes, candidate vscontent_ourslengths/hashes, cycle/run/actor/supervisor identity, and the closeout state-machine transitions, so a recovering agent can root-cause the wedge without guessing. The live supervisor inter-item dispatch (idle-queue watch / drain) is the consumer of this gate and composes with the drain-owner lease (#kp5z, dispatch ownership) andqueue_continuationdrainability. - The
live_prompt_drifteditor-convergence recovery is component-merge-aware (#qpcwcmerge). After the editor applies the convergence patches, the recovered editor buffer is accepted even when it is NOT byte-identical tocontent_ours, provided every divergence lives strictly INSIDE a component OTHER than the agent's response component (exchange) — the editor's live queue/backlog/review/status and any plugin-defined component, while the response component, the document's non-component regions (preamble/frontmatter/interstitial), and the component structure all match (normalized). In that case the editor buffer is the correct merged state (the operator's live queue + same-cycle auto-strikes + the response), so committing it makesHEADequal the editor buffer and eliminates the recurring#pcwcpost-commit worktree drift instead of falling back to thecontent_oursdisk write that drops the editor's components (editor-wins,#queue-user-edit-overwrite). The discriminator is AST-structure-driven and component-name-agnostic (it keys offcomponent::parse, never an allowlist), so a plugin that defines a new component is reconciled identically to the built-in queue. Conservative: any out-of-response divergence, a structural component add/remove, or a parse failure fails closed to the existing block/content_ourspath. preflightIPC-truncation recovery may ask the active editor to flush its visible buffer through typedsave_document, but only through the matching PID-scoped editor socket and with a Lazily receipt. JetBrains and VS Code implement the same intent; neither uses a file signal. If save proof is absent or the published buffer no longer preserves the committed exchange, recovery fails closed with the operator-visible Lazily cut authoritative.- Component patch and recovery writes may target an editor only when its live Lazily registration, editor identity, PID, and PID-scoped endpoint agree. With no live registration the document is detached and may project to disk; disagreement fails closed before payload delivery, leaving captured intent retryable.
- The editor-sync-barrier timeout flush asks the owning editor to republish through the read-only
observe_lazily_currentintent. It uses the same PID-scoped socket and intent name in every plugin; there is no file-signal or pluginless fallback. - When preflight/current-document resolution observes a live editor owner but the CRDT relay model is missing or not yet converged, it must attempt bounded
ensure_document_model(file)before returning an agent-facing failure. That ensure step may request the same read-onlyobserve_lazily_currentmessage and then re-observe relay state; the editor-side publish handler must treat that request as both a visible-buffer content report and a CRDT relay registration/refresh for the open document, so an alive editor owner cannot continue to reporteditor_attached_model_missingmerely because the buffer has not been edited. Authority-bearing publish must recreate stale cached editor forwarders so a CP/controller recycle, socket handoff, or dropped relay registration cannot leave the editor thinking it is attached while the relay has no replica. The publish handler must not block the socket receipt waiting for CRDT CP/native alignment that can only make progress after the receipt returns; it should accept/schedule the replica refresh and let the binary's bounded ensure step observe convergence. The ensure step must not seed a successful editor-authoritative read from disk. If the bounded attempt fails, the final error names document-model startup/reconciliation failure with recovery guidance and says disk remained non-authoritative, rather than surfacing the raw "no registered replica" relay observation as the normal prompt contract. - The
#yzerreconnect-reread is editor-agnostic and lives in the binary FFI (agent_doc_reconnect_buffer_decision, owning the disk==HEAD vs buffer==prior-commit staleness decision); both the JetBrains plugin (reconcileStaleBuffersOnReconnect) and the VS Code plugin (PatchWatcher.reconcileStaleBuffersOnReconnect, run on activation) are thin callers that only re-read disk into a buffer the FFI proves stale, so genuine unsynced user edits are never clobbered (editor wins,#editorbufwin). The two plugins must stay behavior-matched through that shared FFI. - Operator-authored queue prompt order is position-locked by stable identity, not by mutating visible prompt text. During preflight, queue prompts absent from the snapshot and not appended by backlog sync form an operator-authored identity set; priority and auto-DAG queue sorting anchors those identities like operator pins while leaving the line marker-free, including id-backed
do [#id]heads. Binary-synced backlog mirrors still use append-stable ordering, explicit operator pins still anchor normally, and dependency edges may still outrank an anchor when holding the slot would violate the edge (#qauthorderpin). - Free-text queue heads are struck when answered, regardless of position (
#ftstrike). The normal leading-head consume only strikes a contiguous leading run and stops at an id-backed head, so a free-text report sitting BEHIND an unfinisheddo [#id]head was never struck even after the response addressed it. On closeout, after the leading-head consume, a position-independent pass (strike_answered_free_text_queue_heads) strikes every non-struck free-text head whose text the committed response answers — matched conservatively by requiring the head text (priority-markers stripped, normalized to lowercase alphanumeric words) to appear inside the response's>quoted-prompt blockquote region, with a minimum four-significant-word guard. A head merely mentioned in prose (not quoted as a prompt) is NOT struck, so an unaddressed operator report is never silently dropped. The pass is also conservative about in-flight operator edits (#qstrikeexplain): a free-text head is struck only when it was already present in the stable pre-turn baseline (the preflight baseline). A head that first appeared in the live buffer during this turn — a line the operator is still typing — is never same-cycle struck even if it fuzzy-matches a quoted prompt; it defers to the cycle that actually answers it (editor-wins, consistent with#queue-user-edit-overwrite). A missing baseline (rare; preflight writes it each cycle) skips the gate, preserving legacy strike behavior. The pass runs independent of the leading-headqueue_consumption_alloweddecision, strikes the document and snapshot in sync, and is best-effort (a missed strike never fails an otherwise-clean closeout). This mirrors howstrike_done_queue_head_promptsstrikes id-backed heads regardless of position. - Short free-text heads such as
deploycount as answered only when the response contains an explicit labeled> **Queue prompt:**echo for that exact head (#qheadresidue), never from a bare prose mention.session-checkmust fail closed when an active free-text queue row is still present even though exchange history contains that labeled echo, because the row is completed queue residue and would re-run stale work. - Queue convergence also auto-strikes a LIVE free-text queue head when it is already complete OR a backlog item already addresses it, even if no exchange answer echoes it (
#qftbklgstrike). During preflight queue maintenance, after the#qheadresidueexchange-answer strike, a deterministic (no-LLM) lexical scorer (memory_cmd::semantic_queue_strike_matches, the strike sibling of the existingsemantic_completion_matchwarning) scores every non-struck free-text head (no#id) against BOTH the completedagent:donearchive and the activeagent:backlogitems. A head that clears the conservativeQUEUE_STRIKE_THRESHOLD(1.6 — strictly above the warning's 0.8 floor, set above the scorer's+1.0substring-contains bonus so only a near-restatement of a tracked item can reach it) is struck in place and annotated: a done match renders- ~~<original> — auto-struck: completed by #<id> (#qftbklgstrike)~~, a backlog match renders- ~~<original> — auto-struck: tracked by backlog #<id> (#qftbklgstrike)~~(the reason is baked INSIDE the strikethrough so the line round-trips as a stableCompletedentry). The head is struck, never deleted. The same committed-snapshot in-flight-edit gate as#qheadresidueapplies, so an operator line convergence just added is never struck; id-backed heads are skipped (they have their own done-strike); the operator-authored position-lock (#qauthorder/#queue-operator-pin-position-lock) and existing#ftstrikeexchange-answer strike are preserved. The conservative threshold is the false-strike safety margin: an unrelated operator prompt that is not a near restatement of a tracked item can never reach 1.6, so an unanswered operator prompt is never silently buried. - Queue parser/drainability must treat operator prose followed by a
---separator as one multiline free-text prompt (#qfreetext-sep). In a preset-bearing queue, a prose bug report followed by fenced diagnostics is drainable work: the preset supplies the directive and the prose lead is the object. Pure log/evidence blocks with no prose lead remain predicate-proven noise and may be removed byqueue prune-noise; prose+diagnostic reports must instead be answered and then struck/consumed by closeout. - Paused-queue supervisor fallback must not use a supervisor-owned drain-owner lease as proof that a self-driving in-session loop owns the drain (
#qstallguard-failsafe-lease). The transactional drain-owner row gates only real/loopowners;owner=supervisor-failsaferows are ignored by the pause gate, stale-recycle-yield gate, and stall classifier. - Done-id collection uses each item's OWN id, never prose citations (
#donemirrorreap). The preflight already-done-mirror reap removes an activeagent:backlog/agent:reviewitem whose id appears inagent:done(or the externalarchive=file). That done-id set must be collected per item viaextract_done_item_own_ids— the FIRST[#id]on each list-item line — NOT via a whole-text scan that harvests every bracketed id. Otherwise a[#other]cited inside one done entry's prose (e.g. "behind do[#fullboundary]" inside the#ftstrikeentry) is wrongly treated as done and falsely reaps a still-open#otherreview/backlog mirror. An item's identity is its leading id, never a citation in its description. agent-doc mcp serveis a stdio MCP transport into the same binary-owned document APIs. It exposes read/preflight/plan/session-check/finalize tools for Codex or other MCP clients, but finalize must still call the normal strict write/commit/session-check machinery instead of creating a harness-specific patchback path.agent-doc <file>andagent-doc run <file>are the same mode-aware entrypoint. Document mode comes from frontmatter viaresolve_mode(), with template as the default when no explicit format is present.- After
runpre-commits user edits and opens its responsepreflight_startedcycle, the agent child wait is bounded byAGENT_DOC_RUN_AGENT_TIMEOUT_SECS(default 1800s). While waiting,runemits parent-visible heartbeat stderr everyAGENT_DOC_RUN_HEARTBEAT_SECS(default 30s) and records the heartbeat as open-cycle progress by updating the cycle state'supdated_atandlast_event; that gives Codex/direct harnesses phase/cycle evidence that a long child wait is still progressing. Whenrunis invoked with terminal stderr inside a tmux pane owned by a Codex/OpenCode parent harness, routine run/diff/commit stderr is redirected to.agent-doc/logs/run-stderr.logunless verbose input diagnostics are enabled, so progress output cannot paint over the foreground TUI after a restart. If the child times out, or if a Codex harness tries to runagent-doc <file>from the same tmux pane that already owns that document, the command must fail closed, leave the cycle inpreflight_startedwith a timeout/blockinglast_event, and surface cycle id, phase, pane, actor generation/state, and retry/restart guidance. - If the pre-commit repair for
runcloses an already-committed missed patchback and the post-repair diff is empty,runmust fail before child-agent dispatch. The diagnostic must say no new assistant response body was supplied and nameagent-doc write --commit <FILE>as the missed-response repair path. agent-doc start <file>must preserve the one-live-pane-per-session invariant by failing closed when another alive pane is already bound to that document. Normalstartmay not reuse, restart, supersede, or silently replace that pane. The error must include concrete tmux inspect/capture/kill commands so the user can decide which pane to keep.--forceremains the explicit escape hatch for deliberate repair work.- Route-created
startpanes carry an explicit lifecycle policy. Editor-origin Run Agent Doc requests and editor-layout/focus reconciliation create interactivekeep-alivedocument sessions so a successful commit cannot be misclassified as a crash and later recreated solely because the selected document became unfocusable. Controller/watchdog recovery retainsautoone-shot behavior: after a new cycle reachescommitted, it may stop and reap only when the supervisor actor is in stablereadyprompt state or direct live-pane evidence shows the child is back at an idle harness prompt, and the document has no liveness signals. Explicit blocking prompt states such as queued drafts, permission prompts, hook-review prompts, history search, and clean-exit restart prompts keep the pane alive. If a one-shot fresh trigger is accepted but no cycle starts, route must record startup-miss provenance and reap the just-created pane instead of preserving an idle registered owner indefinitely. - Route must re-check legacy associated-pane evidence immediately before normal-path auto-start. If stale registered-pane cleanup clears the old binding and only then a live associated pane becomes provable from session-log /
registry_rebind/ same-file evidence, the binary must fail closed with explicit claim/repair guidance instead of cold-starting around that ambiguity or silently promoting the legacy pane back to authority. - Startup-miss supersession follows the current registered file owner, not the stale marker's original
session_id. Once a later pane/session is registered for the same document,start,route,sync, andsession-checkmay clear the old pane'sstartup_missmarker only when the current owner's session log proves a newersession_starton that registered pane. agent-doc gcprunes stale operational artifacts without hiding fresh diagnostics: Codex blocked-stop payload records age out after seven days, andstartingactor records older than one hour close unless a live supervisor PID still has a fresh supervisor heartbeat for that generation. A live but non-heartbeatingagent-doc startprocess is not enough to pinstartingindefinitely. Actor cleanup mutates the controller SQLite store transactionally. The lightweight stale-startingactor cleanup also runs every normalpreflight,start, andsynccycle; the full orphan-file GC is bounded by a coordination throttle in.agent-doc/state.db.agent-doc run <file>must advance the cycle towrite_appliedonce the final response (and anyresumeupdate) is on disk, before attempting the post-write commit. In git-backed runs, it must then pass through the same strict post-write closeout helper used byfinalize,write --commit,repair, and the Codex Stop-hook: commit, prove the cycle is closed, retry once if the snapshot still differs fromHEAD, and finish withsession-check. Preflight/recover then finish from the recordedwrite_appliedstate instead of a staleresponse_capturedphase.- Codex Stop-hook, direct template writes, and
repairreplay must validate captured assistant payload shape before writing whenever an exchange patch is mixed with unmatched text. Transcript-shaped or full-document component dumps fail closed into diagnostics, while known replay guard comments such as<!-- no-pending-capture -->may wrap an otherwise valid patch response. Safe plain progress commentary before the first patch is stripped, and the sanitized patch payload is what the write path applies. - The per-session log at
.agent-doc/logs/<session>.logmust capture document closeout boundaries in the same timeline as harness/supervisor events. When a session document crossespreflight_started,response_captured,write_applied,committed, orabandoned, the correspondingdocument_cycle phase=... cycle=... event=...entry must be appended to that session log so crash forensics do not have to infer the closeout boundary from separate state files. agent-doc preflighttreatspreflight_started,response_captured, andwrite_appliedas open cycle states. It auto-attempts recovery+commit forresponse_captured/write_applied; a stalepreflight_startedlock auto-clears whenrecovercan prove the recorded snapshot/file hashes still match exactly, when safe historicalHEADproof shows the patchback is already committed, or when an otherwise-emptypreflight_startedcycle has no capture and has been stale past the bounded timeout. If the JetBrains File Cache Conflict Cancel branch leaves the response already visible in the working tree and snapshot whileHEADlacks it, preflight must classify bothwrite_appliedand already-markedcommittedvariants asjb_cache_conflict_canceland close the missing commit boundary instead of requiring manualagent-doc write --commit. Ifroute --dispatch-onlyleaves a busy-actor queued dispatch in the snapshot (queue_active: true,agent:queue) whileHEADlacks it, preflight must commit that route-owned snapshot before diffing; any later visible prompt edit remains uncommitted for the fresh cycle. If that stale empty cycle still has unresolved prompt-bearing drift,recovermarks the cycleabandonedinstead of committing a placeholder response, leaving the prompt in the live document for the next fresh preflight cycle. Recent empty prompt-bearing cycles still fail closed so active concurrent work is not stolen. Outside those cases,preflight_startedstill only auto-closes whenrecoverreplays a pending/captured response first. If neither path applies, preflight fails closed before diffing. Pure agent-owned boundary churn ((HEAD)attribution and boundary-id-only diffs) is normalized back tono_changes/ already-committed closeout instead of opening a new user-visible cycle, so transient post-commit markers do not leak a stalepreflight_startedlock. It emits non-blockingwarnings[]; when frontmatteragent:is set and differs from the detected active harness after alias normalization, preflight emitscode: "harness_mismatch"so the skill can surface the mismatch while still using active-harness attribution and closeout behavior. It also emits the tier/attribution contract the skill consumes:effective_tier,required_tier,suggested_tier,model_switch,model_switch_tier,agent_model, andsession_accretionwhen local exchange/log heuristics detect churn-heavy growth or restart-heavy reopen patterns. Those heuristics are advisory only; preflight does not auto-compact the document.- Inline prompt-preset references are prompt-preset requests, not only standalone
preset <name>directives.preflight.prompt_presets_requested, backlog-capture guards, andplanclassification must recognize user prompt text such asPlease analyze failed orders and bot traffic. #next-steps, while ignoring YAMLprompt_presets:definition lines. Stale empty preflight cycles and post-compact follow-up prompts that contain#next-stepsmust reopen a fresh actionableprompt_targetcycle with backlog capture still required; step-2 commit recovery must classify that drift aspost_commit_user_follow_upinstead of absorbing it into the snapshot or HEAD. agent-doc planmay surface accretion guidance through normal prompt context. Exchange-size accretion and repeatedcommit_noopcloseout churn remain advisory; the plan must not suppress normal repo work/finalize or require a compact/restart handoff unless the prompt or document explicitly requests compaction. Claude skill auto-update also defaults to restart, not/compact; compact reload requires explicitagent_doc_auto_compactopt-in in document frontmatter or project.agent-doc/config.toml. After a real Codex skill-version update, the installed Codex instructions must re-read the installed skill completely and continue the active turn in place; they must not request a supervisor restart or stop the turn (#codex-skill-reload-in-place).agent-doc planmust recognize bothdo #idanddo [#id]directives as executable repo actions. When the id matches an open backlog or icebox item, the emitted normal finalize command must carry--done <id>so the binary-owned closeout records the resolved tracked-work item in the same cycle.- If an ancestor
.tsift/graph.dbis materialized,agent-doc planandagent-doc orchestrateshould try to collect graph evidence for queueddo #id/do [#id]work before dispatch:graph-db status,graph-db refresh, per-targetgraph-db evidence,conflict-matrix, anddispatch-trace. Any tsift collection failure, including stale/fail-closed graph freshness, non-current status, missing evidence targets, command timeout, bad JSON, or missing graph orchestration contract fields, is advisory at the agent-doc turn boundary:planmust emit a soft warning plusmanual_packet_only: true,orchestratemust warn and continue without graph evidence, andjobs createmust still be able to write parent-reviewed manual packets. When graph evidence is successfully collected, evidence packets must begraph-db-evidence-v1withpacket_id,projection_hash,replay_commands, andrepair_commands; conflict matrices must beconflict-matrix-v1; dispatch traces must bedispatch-trace-v1with projection freshness/hashes, evidence packet ids, worker feedback, graph nodes/edges, replay commands, and repair commands; worker prompt packets must beworker-prompt-packet-v1withpacket_id,projection_hash,token_budget, and explicit fail-closed prompt text. A successfully collected conflict matrix can still block parallel dispatch when it explicitly reports unsafe ownership (can_parallel=falseorfail_closed=true). Prompt targets and orchestration job prompts with available evidence must carry compact graph handles/evidence packet ids plus a normalized lower-agent job packet preserving owned files, read-only context, forbidden files, expected tests, expansion commands, token budgets, fail-closed instructions, and dispatch-trace audit context; child prompt context must not be injected into the session document itself. Successful graph-backed orchestrate child closeouts must append a tsift-projectableworker_resultline with status, target id, touched files, tests, and follow-up ids beforefinalizeso later graph projections can close the feedback loop without scraping CLI output. agent-doc planmust also emit lower-agent routing fields for job-packet workflows:dispatch_candidate,task_class,risk,parallelizable, parent/model tier, context and job-packet token budgets,write_scope,required_proof,dispatch_mode,manual_packet_only,warnings, andtsift_context.agent_doc_dispatch: offdisables the candidate flag;agent_doc_dispatch: autorecords opt-in for future automatic dispatch. These fields are structural hints for packet generation and never replace parent review or verification.agent-doc jobs create <FILE>writesagent-doc-job-packet-v1markdown packets under.agent-doc/jobs/<cycle>/, expands compounddo [#a] [#b]directives into one packet per target, derives target-specificwrite_scopefrom explicit path references in backlog text, optionally writes an operation note, and records tsift context sidecars whentsift status --json/tsift context-pack --jsonsucceed. When a tsift graph projection is usable, create dogfoods graph-db evidence, conflict-matrix, dispatch-trace, worker prompt packet, worker_result feedback, replay, and repair command contracts before attaching graph acceptance evidence; whenmanual_packet_onlyis true, the job index and packet body must preserve the warning and omit graph acceptance evidence so parent review remains explicit.agent-doc jobs list/status/collectinspect generated packets andagent-doc-worker-result-v1result envelopes only; collection validates required changed path, command/test, touched-file, expected-test, follow-up, proof, and attention fields but does not apply patches, resolve backlog items, or bypass the normal parent-ownedfinalizecloseout.agent-doc orchestrate <FILE> --mode dag --from-queueowns the auto-DAG queue path. It reads all active queue prompts plus queue-level presets, expands compounddo [#a] [#b]items into per-target nodes, parses explicit dependency metadata and natural dependency phrases, computes deterministic antichain batches, persistsagent-doc-auto-dag-schedule-v1JSON under.agent-doc/schedules/, and writes schedule-backed job packets under.agent-doc/jobs/<schedule-id>/before dispatch.--resume-schedule <ID>must reload the durable schedule, skip complete nodes, preserve attempt counts, and fail closed if a blocked/failed node would otherwise unblock downstream work. When graph evidence is present, auto-DAG treats stale graph status, missing target evidence, ambiguous ownership packets, or unsafe conflict-matrix antichain dispatch as blockers before launch. Recent ops-log/session-review families are scheduler input: prompt-budget/cache-resend gates to compact-first, restart-loop gates to restart/repair-first, and repeated noop-closeout gates to fixture-fix-first. The schedule record must preserve the guard action, replay commands, repair commands, node state, and attempt count for interrupted run recovery.- Repeated
--backlog-addflags in a singlewriteorfinalizecommand must preserve caller order as a batch at the top of the backlog: the first backlog-add flag becomes the first new backlog item, followed by later backlog-add flags, followed by the pre-existing backlog. Legacy--pending-addremains an alias. finalize/write --commitmust expose--no-followups(with--no-pending-captureas an alias) as the first-class declaration that the response creates no actionable follow-up work. The runtime must encode the declaration into captured response evidence before pending-capture guards run, preserve it across idempotent closeout retries, and strip its transient marker from visible and committed document text.- A
#next-stepscloseout that captures multiple follow-up backlog items must preserve declaration/priority order through the same repeated--backlog-addpath, and any generatedTop backlog item: #id.status sentence must name the first inserted live backlog item. --backlog-add-to <file> <text>is the binary-owned path for explicit cross-document backlog capture. Legacy--pending-add-toremains an alias. It must fail when the target file is missing or lacks anagent:backlog/legacyagent:pendingcomponent, andagent-doc planmust expose explicit backlog target files so skills do not satisfy a target-specific prompt by adding to the current document. Closeout guards must still check explicit targets even when the current document recorded unrelated tracked-work mutations. When a prompt chain contains multiple#agent-doc-bugdeclarations,agent-doc planmust preserve declaration order in its expected add mutations and emitted placeholder flags; intentional priority overrides must be visible in response text or explicit mutation metadata, not produced by accidental LIFO insertion.--icebox-add,--icebox-add-after,--icebox-add-before, and--icebox-add-backare granular tracked-work mutations for parked work inagent:icebox. They use the same stable-id, checkbox, collision, same-cycle metadata, and operator-preserving writeback rules as backlog adds, but they must not mirror new items into runnableagent:queue.agent-doc boundaryprepares a transient working-tree insertion marker and may signal the editor, but it must not update the saved snapshot or create a git commit. A later preflight/commit may normalize marker-only working-tree churn as already-committed drift, but a standalone boundary marker is never the committed snapshot basis.- Full and partial exchange compact with a trailing unresolved prompt after the boundary must preserve that prompt in the live editor/working tree while leaving it out of the saved snapshot/commit. The compact closeout may archive old response sections and commit the compacted history, but the next
agent-doccycle must still see the prompt as live user drift. Compact therefore carries distinct live and committed targets through the transaction: editor save/relay convergence uses the live target, Git stages and verifies the committed target, and an already-converged live relay must never be reset to the committed-only projection. A stale zero-editor relay fallback may be repaired, but it must be repaired to the live target. - When warn/block session-accretion prompt packing is active, the bounded exchange context must anchor each prompt target to its actual
exchangeposition: include the enclosing### Re:block for inline prompt edits, or the immediately previous### Re:block for tail follow-ups. The pack must also expose a lightweight live+archive response TOC plus a targeted retrieval surface (response-fetch) so agents can request exact neighboring sections on demand instead of relying only on a fixed recent-turn slice. If no clean anchor can be found, the pack may still fall back to the latest bounded recent-turn slice. - When
syncdiscovers that a previously registered pane disappeared while the document's cycle state is stillresponse_capturedorwrite_applied, it must attempt the same binary-owned recovery path immediately instead of only logging pane loss.response_capturedmust replay from durable capture through the normal write/commit closeout, andwrite_appliedmust finish the missing commit boundary if the live file/snapshot already prove the response landed. If either recovery attempt fails, the failure must be logged alongside the pane-loss provenance and the durable capture must remain available for laterpreflight/repair. - During tmux layout reconciliation,
syncmust not evict an unwanted pane whose owning document still has an openpreflight_started,response_captured, orwrite_appliedcycle. In that shape the binary warns and preserves the pane instead of stashing it mid-closeout. finalize --done <id>is a tracked-work resolution and must satisfy pending-capture closeout requirements fordo #idturns in the same way as adding new backlog work. If the item was already reaped into canonicalagent:done, into the repo-relative external.done.mdarchive named byagent:done archive=..., or recorded as resolved in the active cycle, the flag is an idempotent no-op warning, not a fatal "id not found" error. Legacyagent:backlog-doneandagent:pending-donemarkdown components are migration inputs only, not accepted completed archives at runtime; runagent-doc migrateto rewrite them.- During preflight maintenance, active
agent:backlogoragent:reviewmirror items whose ids already appear in inlineagent:doneor the configured externalagent:done archive=...are stale and must be removed from the active tracked-work surface without writing duplicate done archive entries. Active backlog/review items may also be auto-completed when their item text carries an explicit completion marker (DONE,SHIPPED,IMPLEMENTED,COMPLETE, orCOMPLETED) plus deterministic commit or successful-CI proof, but blocker language such as partial, remaining, reopened, deferred, false-closeout, or follow-up work keeps the item active. That auto-completion must not fire on a false positive where the marker only describes already-landed dependency work: for an open (non-gated) item the completion marker must be the item's own leading status verb (the status prefix before the first clause break), not a marker buried in a cited-dependency clause such as "the predicate already shipped in abc1234"; gated[/]items keep the marker-anywhere behavior because the agent deliberately code-completed them. Auto-completion must also never reap an item on the same cycle it is added — a brand-new add is absent from the cycle-start snapshot and must be closed explicitly rather than archived the moment it appears. Queue maintenance must also treat those ids as resolved: backlog-to-queue sync excludes them, and existing live queue prompts for them are struck before dispatch. agent:reviewis the canonical review-pending tracked-work component.--backlog-gate <id>moves backlog work into review as[/];--backlog-ungate <id>moves review work back to backlog as[ ]; legacy--pending-gate/--pending-ungateremain aliases.--done <id>accepts backlog, review, or icebox sources.review_done_guarddefaults tooff; withwarnorstrict/error, direct--doneoutside review is surfaced or blocked.agent-doc migratemoves legacy[/]backlog items intoagent:review, and preflight warns withlegacy_gated_in_backloguntil that migration lands.auto_done: truein frontmatter, or[guards] auto_done = truein.agent-doc/config.toml, opts a document into automatic tracked-work resolution for clear completion signals. Explicit inline prompts such asmark #id done,#id done,done #id,complete #id, andresolved #idmust makeagent-doc planemit aResolveExisting/--done <id>mutation even withoutauto_done; a baredone/complete/resolvedprompt may auto-resolve only whenauto_doneis enabled and exactly one openagent:reviewitem is the contextual target. Whenauto_doneresolves a response-completion guard hit duringfinalize/write --commit, it must record the same cycle-state pending-done ids as an explicit--done. If the target still lives inagent:backlog, the auto path gates it intoagent:reviewbefore marking it done.- Generated status text that includes
Top backlog item: #id.must stay consistent with the liveagent:backloghead. Pending maintenance, reap repair, explicit backlog reap, and exchange compaction must reconcile that sentence to the current open backlog head or clear it toNo open backlog items.when the backlog is empty; unrelated free-form status text remains user-owned. - Editor-triggered
syncserialization must be bounded. A stuck or orphaned prior full sync may delay a later sync only up to the configured contention budget; safe-passive editor sync may wait only thesync_lock_waitlatency budget. Immediate editor focus is handled by the separate Project Controller focus command; sync itself does not move focus before acquiring this lock. On safe-passive contention, the binary must emit a visible[sync] safe_passive_sync_lock_contention_retry ... phase=sync_lock_wait ... status=over_budget ... coalesced=skipped_stalemarker and return without further auto-start, tmux reconciliation, or post-lock hidden-pane focus changes so the editor can retry the latest superseding selection instead of waiting the full lock budget. If contention is caused by stale orphanedagent-doc syncprocesses that still hold the same lock file, sync must reap those lock owners and retry acquiring the lock rather than requiring manual process cleanup. - Editor plugins must treat that safe-passive contention marker as deferred, not applied: the stale command must leave the last-applied selection state unchanged, and only the newest superseding tab/layout request may be retried. Editor-side guards must not self-contend by acquiring their local sync guard and then calling a helper that reacquires it before starting the CLI process.
- Editor-selected document focus is operator-first while its latest-wins focus command crosses the Project Controller. The editor plugin must hold a short focus-intent lease that suppresses tmux-to-editor recall of the previously active pane, acknowledge the intended pane without reopening the editor, and restore normal tmux-to-editor following after a bounded expiry if the command never converges. This prevents the two directions from creating a self-sustaining cross-document focus loop.
- Controller-owned safe-passive sync must use the live authoritative SQLite actor row as local ownership proof, allowing an exact-visible editor selection to swap an already-running hidden document pane into view. A standalone safe-passive CLI remains forbidden from issuing a nested Project Controller actor-binding RPC; it may proceed only from non-RPC ownership evidence.
- Editor tab selection performs its immediate focus handoff through the separate latest-wins Project Controller
focus_document_panecommand. The following safe-passive sync must not steal focus before acquiring its sync lock; it owns guarded exact-visible reconciliation, including the hidden-pane swap after controller-local actor proof. - Safe passive sync must also fail safe before attach-first reconciliation would expand the visible
agent-docwindow around those protected panes. If a requested file is not currently visible and satisfying it would require attaching another pane while some already-visible unwanted pane is still protected by an open cycle,sync --no-autostartpreserves the current visible layout and warns instead of creating a visible 3+/4+ pane mix it already knows cannot detach cleanly. That preserve-layout fast path must still reselect the requested pane when the focused file is already visible in the kept window. - Session-check must not treat plain
content_editdrift as proof that an already-committed response bypassed closeout. Only new prompt targets, response patchback markers, hidden snapshot-vs-HEAD closeout drift, or open cycle states should force another closeout boundary. - Session-check must also fail closed when the live
agent:exchangetail itself ends in a prompt-looking block with no later assistant response, even when that prompt already matches the committed snapshot. This catches direct/manual closeouts where implementation commits completed but the final response patchback never landed. - Explicit-baseline
finalize/write --commitmust classify concurrent live-file drift against the pre-response baseline. A prompt added after preflight but before write-back remains outside the committed closeout snapshot, and the required post-commitsession-checkmust interrupt on that unresolvedprompt_target. - For strict template/CRDT append-mode
agent:exchangewrites, an explicit baseline that is missing exchange content already committed inHEADis stale for patch application.finalize/ strictwrite --commitmust apply the response on top ofHEADbefore producingcontent_ours, IPC snapshots, or commit-staged snapshots, while still leaving later live user drift for the next cycle. The repair path must logexplicit_baseline_rebased_to_head. - Explicit-baseline closeout must survive session-document path moves after preflight. If the supplied
.agent-doc/baselines/<old-hash>.mdpath is missing because rename migration moved it to the current document hash,finalize/write --commitreads that migrated baseline before failing or considering any fallback. - Cold snapshot drift never overrides Lazily current text. If a snapshot is corrupt or stale, closeout continues from the
state.dbtransaction and Lazily semantic baseline, then regenerates or quarantines the cold projection after verified convergence.reset --from-currentrebuilds only cold recovery state; no CRDT, capture, pending, baseline, or live-buffer file is imported. - Exchange prompt-prefix normalization must preserve exchange prompt-prefix state already committed in
HEADin both directions: previously prefixed user-prompt lines must keep❯, and previously unprefixed agent response lines must stay bare. Acontent_oursfallback may add❯to newly typed prompts, including every nonblank line in a blank-separated multi-line prompt run after a stale inserted response block, but it must not add that prefix to prior agent response lines simply because the snapshot/baseline pair is stale. Inserted assistant response blocks stay assistant-owned until an explicit❯line, component/boundary marker, or canonical prompt-target diff opens a new prompt run; prompt-shaped assistant prose such as questions is not enough by itself. When IPC sidecar verification rejects a plugin-side normalization result and computes acontent_oursfallback, that fallback must first merge current disk edits against the explicit pre-response baseline, then deliver the normalized repair through narrow editor IPC. If editor delivery is skipped or unproven, the write must fail closed before saving a snapshot, CRDT state, commit, or direct working-tree repair. Concurrent user edits outsideagent:exchange, including deletions of scratch HTML comments, must remain editor-owned and must not be restored by normalization retry. Post-commit prefix repair must not propagate historical bad prefixes from assistant evidence labels such asCommit / push:into later assistant responses, and patch/content prefix application must still refuse to prefix those assistant labels if a stale target list includes them, even when a historical stale target list containsCommit / push:and a later response has a fresh bareCommit / push:label. A temporarily prefixed response heading such as❯ ### Re:is still an assistant response boundary; prefix repair must not treat matching response-body lines after that heading as prompt targets. Prompt-prefix overrun protection logs and passes through when more thanMAX_NORMALIZE_USER_LINESprefixes would be applied; it must not force-commit from inside normalization now that typed repair decisions own disk/snapshot/editor recovery. - Sync live-owner recovery must rank file-specific evidence by freshness: path/supervisor provenance first, then the latest open session-log owner, and only then generic same-file process-tree matches. An older pane that still has a same-file harness process must not steal authority back from a newer pane that already recorded the latest
session_start. - Sync must also treat the latest alive
session_end origin=registry_rebind ... next_pane=...successor as authoritative live-owner evidence before generic same-file process-tree fallback. A pane handoff proven by tmux/session-log provenance must not fail just because the supervisor PID or foreground process tree changed afterward. - When
route --dispatch-onlyreuses a live pane through direct tmux input, startup-window reroutes must first observe a harness-specific dispatch-ready prompt in that pane. A Codex status/footer line such asgpt-5.5 high · ... · Context ...is not a prompt by itself. OpenCode startup-window prompt probes must use the longer OpenCode redraw budget, including the same harness-specific recovery wait used for starting actors, because the idle splash can become dispatch-ready after the short Codex-style boot probe. During that startup prompt-ready wait, route owns the pane input window and must publish controller-backedRouteSubmitStarted/RouteSubmitSettledfacts before any supervisor idle-queue context reset can enqueue or submit/clear//new; pendingQueueContextClearDeferred/QueueContextClearStartedprojections and orphan visible clear drafts must wait while the route-submit projection is pending. Accepted-without-dispatch-start proof recordsRouteSubmitBlockedfor the bounded blocked window. Dispatch proof must separate pane-input acceptance from dispatch-start evidence. Accepted shared text+Enterdelivery is a successful dispatch-only result for Claude Code, Codex, and OpenCode, and logsproof=accepted proof_scope=accepted_onlywhen dispatch-start proof is absent. Codex hook proof and OpenCode pane-state proof are still logged as strongerdispatch_startevidence when available, but missing stronger proof does not make the editor action fail. Editor-triggered JetBrains routes must propagate the durable Run Agent Doc attempt id into the binary process, and route/tmux diagnostics must include that id on the input-delivery and proof lines so a live click can be correlated across.agent-doc/state/editor-route-attempts/,ops.log, and preserved route-submit pane snapshots without replacing tmux-router reconciliation. - Claude readiness treats a bare
⧉ <label>line as an attached-artifact composer chip and continues scanning for the real idle❯/permissions prompt;<label>is arbitrary session content and must never be hard-coded. Only the complete picker shape (Enter to openplus aclaude.ai/code/artifact/...URL) is an operator-owned dispatch blocker. - Safe passive mixed-root sync must preserve the current visible
agent-docwindow layout whenever any visible file stays blocked undersync --no-autostart. In that shape the binary warns and skips tmux-router reconciliation instead of collapsing the remaining foreign pane set into a new authoritative layout, but it still reselects an already-visible requested focus pane inside the preserved window. - Full/manual
agent-doc syncis the binary repair path behind editorSync Tmux Layout: before reconciling the editor pane projection, it must invoke the same file-scoped doctor repair used byagent-doc session doctor <FILE> --repairwhen a focused/session document is available. That repair must close recoverablejb_cache_conflict_cancelcommit-boundary drift (visible document and snapshot already contain the response whileHEADdoes not) before pane liveness can short-circuit, and must normalize the inferred tmux session to0:agent-doc,1:stash, and adjacent overflowN:stashwindows, renamingstash-*aliases back tostash, even when the editor supplied a stalestashwindow target or sync starts while the active window is namedstash. Passivesync --no-autostartremains non-destructive and does not run that repair step. - Safe passive editor sync must optimize for the fast pane handoff path before it owns the bounded sync lock when a local projection or no-record provisioning miss is cheap, then converge through the existing locked path. Safe-passive prune may still remove stale registry rows and retained dead non-stash panes, but it must skip expensive stash-window and stash-pane cleanup before tmux-router reconciliation so extra visible panes are detached before orphaned stash scans spend the selection budget. If no live actor binding exists after the pre-lock handoff, reuse the latest matching pane when one is known, otherwise fall back to an alive exclusive registered pane rooted to the same document, and only then cold-start a new pane. The slower supervisor/process-tree recovery still applies to non-passive paths and non-happy-path recovery.
- Automatic editor syncs that have a complete visible markdown projection must pass
--exact-visiblewith--no-autostart; the binary must then treat a single--colas authoritative and skip remembered focus-only sibling expansion, so stale panes do not reappear after the editor switches away from a document. resync --fixorphan-agent cleanup must prove a pane is unowned across project roots before killing it. A non-stash agent pane that is absent from the current registry but registered in its pane-local project root, still proves a live owner there, or hosts a live supervisor from that root is preserved instead of being reaped as a parent-project orphan.- Safe passive focus-only editor sync must not collapse a multi-column
agent-doctmux layout to one pane. When a single focused markdown column arrives while the last recorded layout has sibling columns, sync should first prefer the remembered or currently visible column that already owns the focused document; only true replacements should infer the editor side from the currently active tmux pane in the targetagent-docwindow. It must replace only that column with the focused document and keep the sibling columns visible until a full editor-layout sync supersedes it. If no recorded layout exists, sync must derive the same sibling projection from the registered panes currently visible in the targetagent-docwindow before reconciling. agent-doc preflightmust also fail closed before diffing when the live snapshot/file pair already looks like an uncommitted assistant closeout with no open/recoverable cycle left to explain it. That includes a visible bypassed### Re:block and the hiddensnapshot != HEADshape where the file now matches the snapshot but tracked side-effect edits are still only in the working tree. The failure must nameagent-doc write --commit <FILE>as the repair path.agent-doc commit <FILE>may close an already-committed historical response as a no-op while leaving later user follow-up prompt edits for the next turn only when an open/recoverable cycle still needs that terminal transition. If the most recent cycle is already terminal, a later user follow-up prompt is not a closeout at all; the command must return without lifecycle no-op bookkeeping and say to rerunagent-doc <FILE>or pipe the missing response throughagent-doc write --commit <FILE>.- When the document diff is empty, direct harness prompt text or an active queue head item may still open a real cycle. The binary strips the leading
agent-doc <file>invocation from the current harness prompt (Codex hook state or explicitAGENT_DOC_HARNESS_PROMPT) or reads the activeagent:queuehead prompt, synthesizes an in-memory added-lines diff from that body, and feeds it through the normal prompt-target / prompt-preset / backlog-contract classifiers. Bareagent-doc <file>with no trailing body and no active queue still remainsno_changes. - Consuming (striking) the active
agent:queuehead requires an explicit completion signal fordo [#id]directive heads, never an inferred one (#queue-strike-on-halt); synthetic/preset heads have an exact-id exception (#queue-head-consume-on-topic-id-regression). On the CLIfinalize/write --commitpath, consuming ado [#id]head requires a closeout flag —--done <id>,--backlog-gate <id>, or--backlog-edit "<id>=..."— that names the head id, or a genuine fresh operator prompt-target /do queuetrigger in the cycle diff. A### Re:heading that merely names ado [#id]head is not a completion signal, because a halt/refusal response names the head to explain why it is not being done. For a synthetic/preset head (a natural-language prompt carrying a trailing#presetid, not a baredo [#id]directive), the CLI path additionally consumes when the response heading topic resolves to exactly the head id (### Re: #spec-test-build-install-commit-push). An operator-pinned bare id head ([#id]/#id, with or without a:pushpin:/:round_pushpin:priority pin) whose id names a trackedagent:backlog/agent:reviewitem is an id-backed directive, not a synthetic/preset head: it follows the same explicit-flag rule and must not be struck by a### Re: #idlog-check/halt heading (#zwn5), so an operator-drive live-verify item the agent can only log-check stays pinned for an explicit close. A registered prompt-preset id is not a tracked backlog/review item, so it remains synthetic and still consumes on a matching heading. The Codex Stop-hook auto-close path, which has no closeout CLI flags, consumes from a response heading on an exact topic match to the head prompt (### Re: do [#id]) or a topic that resolves to exactly the head id (### Re: #idfor headdo #id) — never on a heading with trailing modifiers such as### Re: #id halt/### Re: #id deferred. - When recovery reopens a closeout from durable capture or Codex
last_assistant_message, anAlreadyApplieddedup outcome is still allowed to advance the snapshot andwrite_appliedphase when the live document already contains the response but the snapshot does not. That keeps the subsequent commit boundary binary-owned instead of downgrading the turn to post-commit local drift. - The same adopt-current closeout path must also work when no pending/capture artifact survives but the live document already contains a fresh visible response block absent from the snapshot.
repairand the Codex Stop hook may synthesize that visible response back into the normal dedup/write-applied/commit pipeline, but they must not leave the response as plain working-tree drift that still needs a separate manual commit. - Explicit
agent-doc repairmust also fail closed when the no-pending recovery pass still leaves asession-checkinterruption behind. A committed historical patchback may self-heal the snapshot fromHEAD, but if later prompt-bearing user drift remains after that repair,repairmust surface the same interruption instead of reportingNo pending response found. - Fresh route-triggered starts must not treat prompt detection or pane-input consumption as sufficient success. After the initial trigger injection, the route path must observe a new per-document cycle state for that file before considering the start successful; otherwise it fails closed as a missed startup rather than silently idling. Harness-readiness detection should key off the real harness prompt shapes, not a generic shell
>echo. - When route has already created a fresh pane for a document, a concurrent same-session registry rebind is not authority by itself. Fresh-start dispatch must stay on the new pane unless the new pane is invalid for that file, so layout/sync churn cannot supersede the just-created pane out of the visible
agent-docwindow. - When a Codex child exits after a stdin-forwarded
Ctrl-D/stdin EOF or a terminating stdin-forwardedCtrl+C, the supervisor must always route that clean exit through the restart-or-quit prompt, even if the run already recorded a committeddocument_cycle. Operator quit keys must never auto-restart Codex directly. - When a fresh/fresh-restart Codex child clean-exits before it ever surfaces an idle prompt and no forwarded operator quit key was observed, the supervisor must restart fresh instead of prompting the user. That exit is failed startup provenance, not an intentional quit.
- When a routed Codex fresh-restart retry never regains a dispatch-ready prompt after the supervisor bounce, the binary must keep the optimistic fallback explicit: record the resulting
startup_misson the original routed pane, preserve the canonical absolute document path for later recovery, and avoid silently redirecting that retry through a replacement pane that never proved ready. - Route dispatches into an already-running pane must enforce the same fail-closed contract whenever the document already has unresolved prompt-bearing drift on top of a closed cycle. A consumed routed trigger is not enough; route must observe a newer per-document cycle state for that file before returning success, or fail closed instead of silently re-entering the stale pane.
- When the baseline cycle is already
committed, route/fresh-start acknowledgment must require a genuinely newer cycle id for that document. Same-cyclecommit_already_current/ other committed-state mutations do not count as proof that a new closeout cycle started. - Template writes and
compact exchangemust keep conversation content insideagent:exchange: a safe escaped## User/## Assistant/### Re:tail is repaired automatically before snapshot/write, and the same repair path must also pull prompt-target blocks back inside exchange when they were typed after<!-- /agent:exchange -->but before sibling markdown section breaks such as###/## Pending. Ambiguous mixed trailing structure still fails closed instead of being committed malformed. Comment-only or scratch-note content without escaped conversation headings stays outsideagent:exchange; ordinary multiline HTML comment bodies (<!-- ... -->), including transiently unterminated comment tails while the user is typing, are ignored by escaped-tail scanners and prompt-bearing drift checks even when the comment text looks like a prompt. During final template reconciliation, complete post-exchange HTML comments remain user-owned scratch containers; duplicate or near-duplicate prompt text inside those comments is scrubbed while preserving the shell only when the duplicate line was not already present in the pre-response baseline/snapshot. After that safe scrub, any remaining duplicate prompt residue in freeform post-exchange Markdown outside tracked components is an invalid closeout/route state and must fail closed instead of being silently committed or dispatched. - Template exchange appends must keep response headings block-separated from the previous response body. When the appended
agent:exchangecontent starts with### Re:, both boundary replacement and fallback append paths must insert a blank line after non-empty prior exchange content so Markdown renderers never join the new H3 heading to the previous paragraph. - Template/CRDT closeout must also clean up answered raw prompt-target lines that were typed inside
agent:backlog/ legacyagent:pending: after the response is merged intoagent:exchange, only the newly-added prompt-target lines are removed from the backlog component. Normal tracked backlog additions and backlog/review state changes remain part of the closeout. - That template-write repair path must also handle duplicated-close failure shapes: if a merged document still contains the real
<!-- agent:exchange ... -->opener but later hits a second<!-- /agent:exchange -->after safe escaped response content, the binary must move that escaped content back inside the real exchange block and drop the stray duplicate close before the final guard/commit boundary. If the text between close markers is only a duplicated template scaffold, that scaffold is dropped. If the duplicated scaffold is mixed with live prompt text, closeout fails closed with a typedflow::document_mutationevent, and editor-visible normalization also rejects the shape, instead of moving or deduplicating that live text automatically. Ambiguous duplicate-close suffixes still fail closed. - When the user-added diff explicitly requests
compact exchange, template/CRDT write-back must forceagent:exchangereplacement semantics for that turn instead of inheriting the component's normal append mode. A compaction checkpoint summary must replace the superseded exchange body, not layer on top of it. agent-doc compact <file> --component exchange --commitcomputes against Lazily current state and submits one expected-current canonical intent. An attached document is updated only through its PID-scoped editor endpoint; a detached document may project directly to disk. Reposition is the sharedrepositionintent, never a queued file patch. VCS refresh is observational and cannot invalidate an already-durable compact commit.- Reconciliation distinguishes operator edits from remote CRDT application using Lazily operation provenance in the same document lineage. There is no live-buffer digest sidecar and no older-plugin compatibility branch; a plugin lacking the required registration, intent, and receipt capabilities is rejected as incompatible.
- A bare
compact exchangedirective in the current diff must not complete through the normal response write path.run,write, andfinalizefail closed with a binary-compaction handoff (agent-doc compact <file> --commit) unless the turn is already using that compaction path. - The Codex/OpenCode/direct-exec instruction path must run
agent-doc session-check <file>after final response persistence (finalizeor manualwrite --commit) and fail closed when the check reports an open cycle, unresolved prompt-bearing user edits with no newer cycle start, or a likely direct assistant patchback that bypassed the binary write path. The only self-heal exception is already-committed historical snapshot drift proven byHEAD. - Closeout lifecycle, capture, prompt, and coordination state is read from
state.db, not optional files. Logs are diagnostic and snapshot/CRDT files are recovery projections; their absence cannot create lifecycle proof. An unavailable ledger fails closed. - Harness-native
agent-docentrypoints (/agent-doc <FILE>in Claude Code,agent-doc <FILE>in Codex/OpenCode/direct-exec, and equivalent direct entry forms in other harnesses) must be treated as the start of the binary-owned response cycle rather than as a generic document-editing request. A turn started that way must not be reported successful until it crossesfinalizeorwrite --commit, and "not committed" is only valid when the user explicitly asked to leave it uncommitted. - When
session-checkfails on an uncommitted closeout (snapshot != HEAD, committed-cycle exchange drift with a newly appended assistant response marker, or a direct response patchback with no matching cycle), the diagnostic must include any tracked side-effect files and the explicit follow-through commandagent-doc write --commit <FILE>. - Session-document closeout owns backlog consistency too: when a response clearly completes an existing
#idbacklog item, the same closeout cycle must record the matching--done <id>mutation or fail before commit. The done guard must scan the response body, including### Re: do [#id]headings with later completion evidence, but it must exclude ids explicitly kept open by same-cycle backlog mutations such as--backlog-edit,--backlog-gate,--backlog-ungate,--backlog-reorder, or gate-type edits. Warn-only post-commit stale-backlog states are not acceptable as the default for real session docs. - When the active prompt contract requests backlog capture directly or through prompt-preset expansion (for example
#code-reviewchaining into#follow-up-backlog), the same closeout cycle must either record backlog mutations, explicitly state that there were no actionable follow-up items to capture, or fail before commit. Findings-only review prose must not silently commit against an unchanged backlog. - The pending-capture heuristic (
heuristics.rs) must detect unconditional follow-up work — quantified remaining items (e.g., "18 remaining"), outstanding work, unfinished tasks, and unresolved current bugs/issues that the response frames as still needing follow-up — even when the response frames continuation as mutually exclusive options or blocked-with-choices. A single high-confidence unconditional follow-up indicator (confidence >= 0.7) is sufficient to trigger the guard at count=1; the count >= 2 threshold only applies to lower-confidence patterns. - The Codex install surface writes repo-local
.codex/hooks.jsonand.codex/config.toml;UserPromptSubmit/Stopsession tracking is stored in each relevant projectstate.db, never mirrored into ambient ancestor roots or filesystem session files.UserPromptSubmitresolves the real harness-native invocation despite injected preambles. Latest-prompt lookup skips malformed ledger rows.Stopmay finish an open cycle only from a validated single-assistant closeout; transcript-shaped or empty tool-only payloads remain diagnostic blockers and cannot count as response completion. - Non-streaming Codex child runs must persist only the final assistant closeout for the active turn. If
codex exec --jsonemits multipleitem.completedagent_messageevents,agent-docselects the last assistant message beforeturn.completedas the durable response body; if multiple assistant messages arrive without aturn.completedboundary, the run fails closed instead of committing progress/status chatter as a transcript-shaped patchback. - The shared instruction surface must treat MCP auth / OAuth tool flows as sub-steps inside the current
agent-docturn. A browser/authenticate step may pause the turn, but it does not satisfy the response boundary; the same turn still must finish throughfinalize/write --commitplussession-check, or fail closed. - When that replayable Codex Stop payload contains a safe
patch:backlogmutation, the backlog normalization must run before the payload is saved to the pending/capture ledger. Recovery must replay only the stripped exchange-safe payload, not the raw backlog patch, so later closeout recovery cannot fail just because the live backlog has interleaved section headers or other preserved non-item structure. - The instruction surface must also preserve response-ordering: requested implementation / verification / build-install work completes before final response persistence, and once
finalize/write --commitreturns, onlysession-check, failure recovery, and final reporting remain for that turn. - The shared instruction surface must treat imperative user edits inside the document as executable directives for the underlying repo work. That includes explicit command-like lines (
do #id,run tests,build + install,commit + push) and actionable pending-item prose that begins with an imperative verb (for example[#n8q4] Fix the cross-repo ...). For path-scopedcommit + pushwork it must also require an explicit fail-closed staging contract: resolve the intended non-session path set, stop on any stage failure, verify the staged diff still matches that set, and commit only that validated set. It must not require the user to repeat those directives in chat, and it must not append "starting/continuing" status prose when the requested work has not actually happened. The binaryrun/write/finalizepath must reject status-only or meta-only replies to those directive diffs unless the response contains either concrete execution evidence or a concrete blocker. - The project controller surface owns route/start/sync authority.
agent-doc controller status --ensurelazily launches one project-local controller through.agent-doc/controller.sock, guarded by.agent-doc/locks/controller-launch.lock. Bootstrap epoch, launch mode, generation, handoff state, prior PID, and binary identity live transactionally in the.agent-doc/state.dbcontroller_bootstraprow; there is no controller-state compatibility file. Controller launch skips a stale removed executable and falls back to the invoked command oragent-doconPATH. RPC reads are bounded, an idle client cannot block unrelated clients, and stale-binary replacement promotes a private-socket generation before reaping verified same-project duplicates. Start, route, sync, and controller-backed admin operations use typed generation-CAS requests and receipts. Queue pause/drain and backpressure are durable SQLite state; explicit operator reopen bypasses a deliberate auto-drain pause once, while stale-supervisor churn-stop still forces recovery first. - A quiescent supervisor must not replay the full idle reconciliation pipeline on every 500 ms watch tick. Once queue state has been observed and the actor is ready, the tick retains only local installed-binary and historical-zombie liveness probes; pane inspection, full CRDT text/controller reads, and stable blocked-head retries are throttled to a five-second liveness pass. Busy work, in-flight clear settling, and installed-binary staleness bypass the throttle immediately. A stale supervisor must
execveonto the installed binary at the first checkpoint with no supervisor IPC receipt handler in flight, regardless of harness turn/prompt stage or an open durable agent-doc cycle; the live harness child and cycle checkpoint survive the exec. - A detached controller is scoped to the exact project-root directory incarnation that requested it. On Unix, startup retains an open handle to the original directory until its final incarnation check so deletion cannot make that inode immediately reusable by a same-path replacement. If the directory disappears or is replaced between spawn, bootstrap creation, and socket publication, startup fails closed and removes its socket instead of recreating a deleted temporary root. Supervisors that survive an in-place
execvemust also reap already-exited historicalagent-doccontroller children without consuming the live harness child's exit status. - The project controller actor store writes
.agent-doc/state.dbas the sole persistence boundary. Thedocuments,actor_transitions, and registry tables carry session actor and pane-binding state; no actor or registry JSON projection participates in runtime resolution. Sync column memory lives only inlayout_statesrows; there is no legacy layout import or compatibility file projection. - The project controller Phase C supervisor path is controller-owned:
agent-doc startmust lazy-launch/connect to the project controller, register the starting actor generation and supervisor lease, and report prompt-ready, busy dispatch, waiting-input, blocked, and closed lifecycle facts through controller IPC. Lifecycle reports include session id, pane id, and generation; stale reports fail closed before mutating the authoritative actor record. Whensession_idandpane_idboth match the current actor record, a staleexpected_generationis allowed through: the generation bump was from a same-owner rebinding (compact/restart/route), and the transition proceeds using the current generation rather than rejecting the legitimate owner. Supervisor state probes include the actor session id, pane id, and generation sosession status/doctorcan refresh the controller lease heartbeat andruntime_statefrom a live matching supervisor without adding a duplicate actor transition. Route readiness waits for astartingauthoritative actor must stop immediately when the supervisor refreshes that actor toclosedorblocked; those terminal states are the actionable diagnostic and must not be reported as a startup-ready timeout. agent-doc session status <FILE>must include direct live-pane evidence beside projected actor/controller state: pane id, evidence source, pane current command, recent meaningful output tail, and analive-idle/alive-busy/closed-clean/projection-staleclassification. If direct evidence isalive-idlewhile actor, supervisor, or controller lease projection still saysbusy, the session operator path reconciles the actor/lease back toreadybefore printing status or clearing. For Codex operator status/clear, direct evidence that contains only Codex status/footer chrome, such as the model/cwd/context line, and no prompt input or busy cue isalive-idle; a bottom Codex model/cwd/context footer is also idle evidence even when older visible transcript text remains above it, as long as there is no protected composer state or busy cue. Codex idle placeholder prompts such as› Ask Codex to do anything,› Explain this codebase, and safe generated placeholders ending inin @filename,for @filename, oron my current changesare also prompt-ready evidence when the pane does not show an activeWorking (... esc to interrupt)cue. Route dispatch still requires a real dispatch-ready prompt before injecting a reopen; a Codex footer alone never proves dispatch readiness. File-scopedsession restart-supervisor <FILE>must fail closed when direct pane evidence isalive-busyor when the actor is stillstartingand the document changed after the last committed cycle; those refusals must point operators to--force. File-scopedsession restart-supervisor <FILE> --forceis the explicit discard path and may interrupt a busy live pane or bypass the starting-actor guard before requesting the supervisor restart. File-scopedsession clear <FILE>is an explicit operator clear request and must not run the starting-actor readiness guard: it must not fail solely because a pane is classifiedalive-busy, becausepane_current_commandis an agent wrapper such asagent-doc, because the actor projection still saysstarting, or because the document has prompt-only drift after the last committed cycle. Clear may refuse protected prompt-input states such as a permission prompt, queued draft, shell search, drafted user input, or clean-exit restart prompt, and may block explicit busy cues such as an active Codex turn, hook-review prompt, or help/usage screen; those refusals must point operators toagent-doc session interrupt-clear <FILE>for the explicit discard path. Codex placeholder text rendered dim by the TUI is idle chrome, not drafted user input, even when the plain text is not in the built-in placeholder phrase allow-list. Ifsession interrupt-cleartimes out after that explicit discard path, its ops event and user-facing error must report the final blocking live-pane state, evidence source, prompt-ready value, current command, and recent pane tail instead of collapsing to a generic timeout.- The project controller Phase D route path is controller-owned: before route submits a managed or dispatch-only reopen to an actor-owned pane, it must record a controller
dispatchattempt for the current session id, pane id, generation, and command kind. Stale session, pane, or generation requests fail closed before tmux/supervisor input is submitted. Astartingactor is not dispatchable until it refreshes toready; route may promote a healthy starting actor toreadyonly after the current generation's live pane shows a harness-specific dispatch-ready prompt, and if it remainsstartingafter the bounded route wait, route must fail closed before tmux or supervisor input is submitted. If the same pane/generation later shows dispatch-ready prompt proof after route has marked it blocked forstarting_actor_timeout, route may clear that timeout and promote the actor toreadybefore submitting. Startup wait failures must persist a single typed diagnostic with elapsed time, pane id, generation, actor state, supervisor health/runtime state, prompt-ready status, and last lifecycle transition so the editor route error points at the exact failed phase. The current-generation ready-prompt barrier must also accept anidle_pane_reconciletransition as ready proof (#monster60stimeout): the supervisor's idle-watch only records that reason aftersupervisor_pane_has_busy_cuereturnsSome(false)(direct pane evidence that the pane is not busy), so the route must not wait the full 60s timeout when the edge-triggered pty redraw missed re-emitting a recognized prompt shape but the supervisor already proved the pane idle. - When a document explicitly changes
agent:to another harness, a healthy authoritative actor record for the old harness must not hard-fail futureRun Agent Docroutes with "bound to harness X, not Y" (#actor-switch-rebind). Route treats that explicit frontmatter harness switch as a stale binding. For an old-harness actor that is not a live authority (closed/blocked, or its pane is no longer dispatch-ready), route falls back to the normal create/rebind path for the newly resolved harness. For an old-harness actor that is still a healthy, non-closed live authority, route must NOT cold-replace the live pane — that is the#agentreloadrestartkill-guard (mismatched_authoritative_actor_can_be_replacedreturns false for a live authority). Instead route logsroute_authoritative_actor_harness_mismatch_deferred action=defer_to_boundary_restartand defers the harness switch to the supervisor idle-watch boundary restart (agent_change_restart_decision→Restartonce the pane isprompt_visible && !turn_active), which respawns the newly resolved harness (agent_restart_performed old=<X> new=<Y>). Because that deferral dead-ends when the supervisor is paused/stale, the pane never reaches a dispatch-ready boundary, oragent_change_restartis disabled, the defer bail must be operator-actionable (#actorswitchdefer): it surfaces the supervisor health / queue-paused / pane-not-ready / restart-disabled state and theagent-doc session restart-supervisor <FILE>(or--force) single-shot recovery. Without an explicit document harness change, a healthy ready actor from another harness remains fail-closed. - Resumed agent prompts must restate the changed exchange tail as ordered user-authored prompt-bearing changes. The canonical subtypes are
prompt_target,content_edit,recovery_artifact, andboundary_artifact. The turn-completeness contract is oldest-first: do not anchor only on the newest question, do not treat the turn as complete until eachprompt_targetitem is answered or explicitly grouped into one response, and treatcontent_edititems as source-of-truth corrections while artifact items route to normalization/repair instead of ordinary conversation. - Prompt-cache replay metadata is part of direct-run prompt assembly. Only durable response contracts, harness-neutral instructions, turn-payload reading rules, cache-control metadata, and provider replay-key material may live above the cache boundary. File paths, queue heads, diffs, current document excerpts, status text, prompt-bearing change sections, compaction/accretion diagnostics, bounded context packs, session ids, and recovery markers must stay in the volatile suffix after the boundary. The provider cache key must include the agent-doc key version, routing-affinity hash, and stable-prefix SHA-256; the volatile suffix must never contribute to that key. The stable prefix SHA-256, provider cache key, cache-control policy, and routing affinity must remain identical across consecutive resumed agent-doc turns whose changes are only volatile session churn such as no-op closeout diagnostics, status edits, queue-head/list churn, boundary markers, diffs, or bounded context packs. Durable response-contract/instruction changes must change the stable-prefix fingerprint and provider cache key, even when the routing affinity is held constant.
- Prompt-cache session-cost diagnostics must make cache misses explainable without raw JSON analysis. Given two cost samples, miss ranking compares stable-prefix fingerprint, adapter state, routing affinity, cached-input token delta, and creation-token spike, then reports ranked causes with the highest-impact invalidation cause first. When no prior sample is available, the diagnostic must still print the current fingerprint, adapter state, routing affinity, unknown token deltas, and an explicit
baseline_requiredrank state so later history persistence can feed the same ranking path. The token/performance gate persists prompt-cache effectiveness samples as JSONL records keyed by provider, harness, and real transcript id (for example Codex/OpenAI and Claude/Anthropic transcripts). Trend checks must compare the current sample with the latest matching workload history and fail with an operator-readable summary that includes provider, harness, transcript id, previous observation time, cached-input delta, creation-token spike, thresholds, and the same ranked miss causes used by the session-cost diagnostic. - Prompt-prefix preservation is part of that same contract: append-mode exchange canonicalization must derive required
❯transcript prefixes from the prompt-bearing classifier (not a separate ad hoc heuristic). That invariant still applies whenrepairdedups an already-present template response:AlreadyAppliedis a canonicalization-capable outcome, not proof that transcript normalization can be skipped. Prefix repair must remain structurally scoped to prompt runs: once a### Re:/❯ ### Re:/## Assistantresponse block starts, assistant prose and verification/list bullets must not become❯targets merely because their trimmed text matches anormalize_prefix_linesentry; targeted repair may leave the response block only when the line matches the explicit target set and also starts a prompt run, or when a boundary/component marker has already closed the response.session-checkmust fail closed when a bypassed response leaves bare prompt-target lines in the changed exchange tail, and when prompt-bearing user edits exist without any newer cycle start at all. - The prompt-bearing classifier itself must suppress stale-boundary prompt runs that are already visibly answered by a later response in the same changed tail, including raw assistant completion prose without a formal
### Re:heading.preflight,plan, routed cycle-ack gating, prompt-prefix normalization, and write-path snapshot decisions must all consume that same filtered actionable list instead of re-deriving their own stale-tail heuristics. - Unstarted exchange-prompt detection must not be snapshot-gated on a fresh session (
#codex-exchange-prompt-no-dispatch). The queue path activates independently of any cycle snapshot, so a queue write dispatchesRun Agent Doceven before the first snapshot exists; the exchange path keys off the snapshot↔file diff. When no snapshot exists yet,first_unstarted_prompt_bearing_changemust fall back to the committedHEADblob (then to an empty baseline for untracked docs) so a freshly typed exchange tail prompt is still detected and routed, instead of leaving exchange writes inert while the same text inagent:queuestarts a turn. The HEAD fallback stays exchange-scoped — queue components remain stripped from this diff and keep their own activation path. - Component/comment parsing must never panic on valid UTF-8 document content. Ordinary HTML comments near multibyte glyphs such as
❯must either parse normally or be ignored as prose; malformed agent markers still surface as structured parse errors instead of byte-boundary crashes. - The state backbone is the Cycle State Machine plus typed event-sourced projections, not one global FSM for every subsystem. The cycle FSM owns only turn closeout phases. Queue, document, transport, supervisor, route, and proof state must be represented as typed append-only facts reduced into deterministic projections, with live mutation guarded by owner actors, epochs, and leases. Local FSMs are allowed for small closed subdomains, coroutines may express linear protocols, and behavior-tree-style logic may be used only as policy over projections; GOAP/MPC-style planning is not the durable state model for correctness-critical closeout.
- FlowCore is the typed ownership layer for the direct session cycle, routed reopen, closeout, document mutation, operator clear, and orchestration batch hot paths. The first phase is mirror-mode: existing commands still execute behavior, but high-risk branches emit
flow_eventops lines withflow,stage,outcome, and tokenizedreason, andagent-doc ops summarygroups failures by flow stage. The same summary must also emit ranked bug clusters that correlate closeout/captured-response drift, route/start replay gaps, Codex warning storms, SQLite counts, cross-harness markers, session-review guardrails, and working-tree drift by file/session/cycle/thread keys. Closeout/document-mutation guard reasons, including pre-write/pre-commit pending capture blocks, patchback parse shapes, repair recovery boundaries, committed-cycle late fallback rejection, and disabled full-content decisions, must use FlowCore enums or document-mutation decision helpers instead of adding more free-form proof strings. New tactical fixes in these hot paths should prefer FlowCore enums and pure decision helpers over adding more free-form proof strings. - The FlowCore regression gate must remain executable, not only documented. Routed-reopen prompt-ready and dispatch-start failure reasons are represented by
RoutedReopenGuardReason, and the hot-path source-token budget test must fail when route/write/preflight/session-check/orchestrate/git/repair add unauditedguard_,proof=,proof_scope=,reason=,flow_reason=, oraccepted_onlytokens. A failing budget is resolved by promoting the new branch into the owning FlowCore enum/event or by updating the budget with the FlowCore audit complete. - Agent harnesses, not git hooks, own full-suite verification after changes. The shared instruction surface must require explicit full-project verification before final response persistence whenever code, tests, build logic, or instruction surfaces changed.
- Test-bearing harness turns must review the live tmux CI leg with
gh run list --workflow CI --limit 1. The expected local reproduction command ismake tmux-ciwhen the CI leg fails after runner startup; any failure fix should include deterministic SimWorld coverage for the regression class when feasible, so the default suite can catch modeled failures before relying on live tmux. Queued or in-progress CI runs are recorded but do not block turn closeout unless the user explicitly asks the agent to wait. Empty-step Actions jobs with no logs because GitHub never allocated a runner, such as billing/spending-limit exhaustion, are external CI-start blockers rather than tmux/code regressions; closeout should record the CI annotation and the local verification evidence. - A failing full-suite verification run must not be waived off as "unrelated" or "flaky". If the suite is red, the turn must either fix the failing tests or report a concrete blocker and capture the follow-up work in backlog before closeout.
make testshould use a parallel test runner when available.cargo-nextestis preferred because it runs test binaries concurrently while preserving Cargo's integration-test environment, with a doctest pass afterward. Plaincargo test --all-targetsremains the fallback when the parallel runner is unavailable.CycleContext/ActorContextare the lazily-backed read cache boundary for per-run global configuration and transactional document/project registry snapshots. Long-lived actors must explicitly invalidate those slots on controller events. Read-only route/sync/resync/gc lookups may reuse those snapshots for a single logical operation, but every registry read-modify-write path must still execute through the state-store transaction boundary.- Harness-specific launch controls are explicit:
agent_argsis generic,claude_argsapplies only to Claude,codex_argsapplies only to Codex,opencode_argsapplies only to OpenCode, andcodex_network_accesscontrols whether agent-doc preserves, removes, or forcesCODEX_SANDBOX_NETWORK_DISABLEDfor Codex child sessions.claude_model: opus,/model opus, and the built-in Claude Code high tier are deferred to Claude Code: agent-doc emits--model opusso Claude Code resolves its current latest opus, and attribution is self-stamped by the running agent instead of a pinned version (agent-doc stores no concrete opus version, so launch and attribution can never lag a release). Explicit non-alias model ids (e.g.claude-opus-4-8) pass through unchanged.opencode_model/opencode_argsallow managed OpenCode panes to launch asopencode --model <provider/model>with OpenCode-specific flags, while directagent-doc run --agent opencodeusesopencode run. Forcodex_network_access: enabled, managed Codex startup must prove host DNS plus a boundedcodex exec --jsonchild DNS/HTTPS command under the same launch args before route may trust the pane as network-capable; managed OpenCode startup uses a boundedopencode run --format jsonchild probe for network checks. Managed OpenCode startup also recordsopencode_capability_prooffor documents withrequired_ssh_targets, and that proof must run an isolated SSH check inside the OpenCode child before auto-trigger, supervisor injection, managed route, or dispatch-only route may submit prompt work. Successful proof events must include per-phasetimings_msfor network, SSH, writable-root, and total proof time. Successful and failed managed proof summaries must be surfaced as tmux status messages throughdisplay-messageon the owned pane, not written into the child pane transcript; the full proof event remains in the session log. For OpenCode, an otherwise chrome-onlycontext ... % usedstatus footer counts as an idle composer even when no standalone>prompt line is captured after proof completion. When Codex writable roots are required, successful proof events must also include a normalizedwritable_root_contractfingerprint, and route/session-status may treat a Codex proof as current only when that fingerprint matches the active document's resolved--add-dirset after the latestsession_start. When a Claude/Codex document lives in a submodule, harness launches must also auto-add any writable roots outside the submodule that the session lifecycle may need: the superproject working tree for parent-repo document patchbacks plus the external git metadata directories (.git/modules/...for the submodule and the superproject.gitfor pointer updates). If a resumed Codexexec resume <id>turn reports the local-browser/CDP EPERM signature (Operation not permittedon127.0.0.1:9222/localhost:9222), agent-doc must treat that as stale resumed-session capability drift, retry once with a freshcodex exec, and let the successful fresh thread replace the savedresumeid instead of trusting the poisoned resume state again. Becausecodex exec resumecannot accept new--add-dirroots, a direct resumed Codex backend turn whose current launch args require writable roots must fresh-start with the full root set instead of attempting resume. Required SSH metadata may come from document frontmatter (required_ssh_targetsorrequired_ssh_profile) or from project-local.agent-doc/config.tomlmappings for known ops docs. If a configured SSH-dependent document cannot resolve any targets, preflight/startup must fail closed before launch. Those pre-launch SSH capability probes must run in isolated mode (ControlMaster=no,ControlPath=none,ClearAllForwardings=yes,PermitLocalCommand=no) so they cannot create or reuse shared SSH multiplexing state that would affect later operator shells. When a document resolves required SSH targets, that same resumed-session recovery must also treat baresocket: Operation not permittedcommand output as required-SSH capability drift when thecommand_executionevent proves SSH command context for one of the required targets, while still excluding the localhost/CDP drift family and historical capture/log grep output from the SSH path. For resumed required-SSH streaming turns, early assistant chunks from the resumed session must stay buffered until required SSH is proven safe or the turn completes successfully, so a fresh retry can discard stale prelude text instead of leaking it into the final response. A transient capability-proof failure must not permanently disable the session: the proof retries with bounded exponential back-off (configurable viamanaged_proof_max_attempts,managed_proof_retry_backoff_secs, andmanaged_proof_probe_timeout_secsin frontmatter or.agent-doc/config.toml; defaults 3 / 2s / 45s), keeping the gatePendingbetween attempts and only committing toFailedafter the budget is exhausted. Operator recovery is gate-exempt: only real prompt dispatch (Inject, auto-trigger, auto-queue) is gated, while the supervisor control methods (Clear,Stop,Restart) and read-only methods (State,Pid) bypass the gate, soagent-doc session clear/session interrupt-clearcan stop or clear a proof-Failedsession withoutkill -9(#codex-capability-proof-unrecoverable). ### Re:response headers must use the resolved model short name for attribution (for examplegpt-5,opus-4-7), never the harness label (codex,claude).- Bundled skill/install content is part of the external contract: Claude/Codex/OpenCode hot-path instructions must render from one shared source surface, with differences limited to harness-specific invocation wording and frontmatter description. Harness installs must write trigger-scoped managed skill files (
.claude/skills/agent-doc/SKILL.md,.codex/skills/agent-doc/SKILL.md,.opencode/skills/agent-doc/SKILL.md) and must retire old generated rootAGENTS.md/.codex/AGENTS.mdcopies instead of refreshing them as always-on mirrors.audit-docsmust fail generated managed skill surfaces that carry agent-doc managed frontmatter but no longer match the running binary, and must fail retired generatedAGENTS.mdsurfaces untilagent-doc skill installmigrates them away. Without an explicit--root, submodule-local audits check the git superproject install root used by normal release installs; explicit--root DIRchecks generated surfaces underDIRexactly, including tracked submodule-local managed artifacts. Custom root instruction files that do not look agent-doc-managed remain user-owned. Filesystem mtime freshness is advisory for agent-doc audits; source-only timestamp changes may be reported, but generated instruction surfaces are release-blocking only when the rendered content differs. The shared Claude/Codex/OpenCode manual-repair instructions must distinguish adding a missing user prompt from repairing a missed assistant response, useagent-doc write --commit <file>for the missed-response path, and not stop after bareagent-doc write. The repo-local Codex plugin manifest must stay schema-valid: it must not pointskillsat Claude install paths such as.claude/skills; if it exposes Codex skills, the manifest path must resolve to a real plugin-root./skills/tree. - Reusable authoring runbooks and OKF concept bundles shipped by
skill installare part of that same contract. Ifrunbooks/split-spec-files.mdorokf/index.mdis referenced from the shared instruction surfaces, it must be bundled into installed harness resources and its ownership rule must stay harness-agnostic across agent-doc-managed surfaces while leaving custom root instruction files opt-in unless they still match the generated baseline. - Route readiness/trigger acceptance is a binary responsibility: pane prompt detection must be robust to shell startup noise and must wait for actual prompt state rather than treating echoed command text as readiness.
- Route progress/status diagnostics must also be binary-safe: when
route.rstrims captured tmux lines for stderr/status output, it must preserve UTF-8 char boundaries so multibyte glyphs in prompt/status lines cannot panic the reroute path. - Route trigger commands must use absolute file paths (resolved against the invoker's CWD) to prevent submodule CWD-dependent misrouting. That invariant applies to every routed reopen attempt, including post-restart retries after a missed cycle ack; once route has resolved the canonical path, later retries must not fall back to
file.display()or other caller-relative renderings. When a tmux pane's CWD is narrowed to a submodule root and the same relative path exists in both the main repo and the submodule, a relative path would resolve to the wrong file.
| # | File | Description |
|---|---|---|
| 1 | Overview | What agent-doc does and how sessions work |
| 2 | Document Format | Frontmatter fields, components, and template structure |
| 3 | Snapshot System | Snapshot storage, lifecycle, and diff baseline |
| 4 | Diff Computation | Line-level unified diff and comment stripping |
| 5 | Agent Backend | Agent trait, resolution order, Claude backend |
| 6 | Config | Global/project config, IPC, document state model |
| 7 | Commands | Command-spec index with split sibling specs for core, tmux/session, closeout, and orchestration behavior |
| 8 | Session Routing | Registry, claim semantics, stash routing, binding invariant |
| 9 | Git Integration | Commit/branch/squash and hook system |
| 10 | Security | Threat model, known risks, recommendations |
| 11 | Debounce | Debounce system gaps, limitations, and improvements |
| 12 | Deterministic Simulation Testing | Fast seeded workflow simulation for closeout edge cases |
| 13 | State Backbone | Cycle FSM boundary, typed event ledger, projections, and actor ownership |
| 14 | Real-Time Workflow Authority | Operator-first source-of-truth and live mutation invariants |
| 15 | Turn Lifecycle Authority | Turn/closeout state machine, realtime handoff, and commit ownership |
| 16 | Codex Support | Harness-specific differences for Codex vs Claude Code |
Command sub-specs:
Session-routing supplements:
State Backbone
ResponseCaptured is content-bearing recovery authority: it retains the full response body and the full editor-visible baseline used at capture time, in addition to their hashes. This lets repair distinguish partial response fragments from operator text without consulting the working tree as authority. Legacy hash-only facts can be upgraded by a new revisioned fact after an independently hash-matching baseline is found. DocumentWriteDeferred retains the resulting reconciled target when no editor replica can publish it; only matching convergence clears that intent.
agent-doc keeps the Cycle State Machine as the global lifecycle authority for
one response turn. Other state must not be folded into that same finite state
machine. Document convergence, queue ownership, editor transport, route
readiness, supervisor health, and proof collection each have independent axes
that would make one global FSM brittle and hard to replay.
The durable backbone is:
- A typed, append-only event ledger.
- Deterministic projections derived from that ledger.
- Single-owner actors for live mutable surfaces.
- Local FSMs only for small closed subdomains.
Cycle FSM Scope
The Cycle State Machine owns only the turn lifecycle:
preflight_startedresponse_capturedwrite_appliedcommitted- interrupted or abandoned recovery states
The cycle FSM is the closeout gate. A response is not complete until the cycle
reaches committed and session-check can prove there is no unresolved prompt
or pending write boundary for that turn.
The cycle FSM must not directly encode queue ordering, editor receipt wire shapes, supervisor restart policy, route prompt readiness, or proof-specific transport facts. Those are inputs to the cycle closeout decision, not cycle phases.
Event Ledger
Every meaningful state transition should have a typed fact that can be replayed. Examples:
PreflightStartedBaselineSavedQueueHeadSelectedQueueHeadCompletedQueueContextClearDeferredQueueContextClearStartedQueueContextClearSettledQueueDrainStallContinuationRecordedQueueDrainStallContinuationClearedResponseCapturedWriteAppliedEditorPatchAppliedEditorPatchRejectedIpcProofInsufficientVisibleWriteCommitCandidateObservedDocumentWriteDeferredDocumentWriteConvergedCommitObservedSessionCheckPassedCycleAbandonedAgentRestartPerformedCapabilityProofObservedActorGenerationChangedSupervisorRecycleStartedSupervisorRecycleSettledRouteSubmitStartedRouteSubmitSettledRouteSubmitBlocked
Events must carry stable ids where available: document hash, session id, cycle
id, actor generation, patch id, queue node key, backlog id, and causation id.
The event log is append-only. Corrections are new events that supersede earlier
facts by projection rules; they are not in-place mutation of old facts.
Content-bearing visible-write receipts include the model revision in their
stable event identity so a fresh editor publication can supersede a legacy
hash-only fact for the same patch without losing idempotence. A visible-write
hash is validation metadata, not recoverable content authority.
DocumentWriteDeferred is likewise content-bearing: it retains both
expected_content and target_content. A successor intent must be based on the
prior target or component-compose the prior and new targets over the retained
base. Matching editor convergence alone retires the intent; a force-disk
projection or commit does not erase reconnect authority.
Its cause is a DocumentWriteDeferredReason enum in facts and projections, not
free text. CRDT convergence phases and replica wake-event kinds follow the same
rule: stable snake-case strings are serialization/log tokens at the boundary,
while state transitions and dispatch use exhaustive variants internally.
Implementation: agent-doc-state-backbone/src/lib.rs defines
StateEvent, StateFact, and EventLedger. The ledger deduplicates event ids
during projection replay so duplicate delivery stays idempotent, while
causation_id preserves the chain from prompt, queue head, IPC patch, route
dispatch, or proof marker to the emitted fact.
Projections
Current state is derived by deterministic reducers over the event ledger and the document components. Required projections include:
| Projection | Owns |
|---|---|
| Document projection | current component bodies, boundaries, snapshot relation, prompt-bearing tail, editor attachment, CRDT relay model/replica status, document-model ensure outcome |
| Queue projection | active head, struck/completed heads, drainability, backlog-to-queue sync, context-clear phase |
| Closeout projection | latest cycle phase, captured response materialization, pending write boundary |
| Transport projection | PID-scoped endpoint, named binary intent, expected generation/hash, editor accepted/visible/rejected facts, retry/backoff state |
| Supervisor projection | actor state, child pid, harness, capability proof, restart/recycle epoch |
| Route projection | authoritative pane, readiness, dispatch authorization, route-submit phase, dispatch proof |
| Proof projection | ops-log markers, typed verify/disproof predicates, semantic completion advisories |
Reducers must be idempotent and replay-testable. If two modules need the same answer, they should consume the same projection instead of recomputing from free-form logs or partial document text.
The document projection retains the full target content for an admitted write
that cannot reach a live editor replica. Only DocumentWriteConverged for the
matching intent clears it. Unrelated convergence facts must not erase pending
work, and a zero-member relay acknowledgement must not manufacture delivery or
disk authority.
Implementation: StateBackboneProjection reduces the event ledger into
document, queue, closeout, transport, supervisor, route, and proof projections.
The closeout projection delegates phase advancement to the existing
CyclePhaseMachine; the other projections use their own small state machines
for closed subdomains.
agent-doc-cycle-state-io appends PreflightStarted, ResponseCaptured,
WriteApplied, CommitObserved, and CycleAbandoned facts with stable event
ids after accepted lifecycle transitions. A transition must not be reported as
successful if the matching state.db fact cannot be recorded. Preflight,
session-check, routing, repair, and closeout all reduce the same closeout
projection for open/terminal authority; no filesystem cycle record is read or
replayed.
The git closeout layer also appends CommitObserved with the exact HEAD SHA
after a successful real commit or an already-current no-op closeout. That fact
is the authoritative commit identity.
Editor Projection Bridge
Editor integrations must treat the FFI state backbone as the shared foundation. JetBrains, VS Code, and later editors should:
- bind
agent_doc_state_projectionandagent_doc_record_state_eventwhen the native library exposes them; - compute the canonical document hash with the same canonical-path SHA-256 key used by snapshots;
- report editor transport observations using the shared
EditorIntentand lifecycle vocabulary (IntentCaptured,CanonicalApplied,ReplicaAccepted,ReplicaVisible,DiskProjected,Committed); - report route dispatch observations as route-owner generation plus readiness/proof events instead of relying only on plugin-local booleans; and
- render route, transport, and proof status from
DocumentStateProjectionslices where status/log output needs current state.
The editor bridge may keep small in-process counters for owner generations, but it must not implement a second route, IPC, or proof FSM. If an installed plugin or native library cannot publish lazily transport receipts, it is incompatible: install/reload must surface a version error instead of falling back to ACK-shaped proof.
Package-level bridge parity is explicit:
lazily-ktandlazily-jsown reusable state-projection clients and pure helper contracts for canonical document hashing,StateEventJSON, projection summary rendering, and pointer/free lifecycle.- JetBrains keeps a plugin-local canonical bridge instead of depending directly
on
lazily-kt, because the plugin build is constrained by the IntelliJ Kotlin/JBR toolchain whilelazily-ktis a standalone Kotlin/JVM package. - VS Code keeps a plugin-local canonical bridge instead of importing
@lazily/jsat runtime, because the extension is CommonJS-packaged while@lazily/jsis ESM. VS Code tests must compare its pure helpers against the package helpers so this duplicate adapter cannot silently drift. - Rust owns the authoritative
ProjectionSummarycompact string. Editor compact-summary helpers must keep matchingroute=<readiness> pane=<pane> transport=<intent>:<phase> proof_markers=<count>, and cross-language coverage must drive both plugins through queued/retry/accepted/visible transitions plus route started/proven/blocked events.
State Wire (lazily-spec snapshot/delta)
#lazilystatesync2 exposes the projection as a reactive lazily-spec wire graph
so plugins mirror it into a lazily-kt / lazily-js graph and apply deltas instead
of re-rendering the full snapshot on every event.
Implementation: agent-doc-orchestration/src/state_wire.rs maps
DocumentStateProjection onto the src/lazily-spec/schemas/{snapshot,delta}.json
envelope. The existing agent_doc_state_projection FFI (full
DocumentStateProjection JSON) stays as the cold round-trip path and is
unchanged.
FFI surface
agent_doc_state_subscribe(document_hash, last_epoch) -> JSON— returns a lazily-spec message with a"type"discriminator:"snapshot"whenlast_epoch == 0(cold read) or the document has no accepted events yet — a full graph image the mirror applies once."delta"when0 < last_epoch < current_epoch— orderedopsthe mirror applies verbatim to converge fromlast_epochto current."delta"with emptyopswhen the caller is already current.
type_tag vocabulary
Each projection node maps to one lazily-spec node with a stable type_tag.
slot_id = fnv1a(document_hash, type_tag, entity_key) so Rust/Kotlin/JS address
the same node without a central allocator (FNV-1a is re-implemented identically
across the three languages — no platform Hasher drift).
| type_tag | entity_key | source |
|---|---|---|
agent_doc.document.baseline | document_hash | BaselineProjection |
agent_doc.queue | document_hash | QueueProjection (singleton) |
agent_doc.queue.head | node_key | QueueHeadProjection |
agent_doc.closeout.cycle | cycle_id | CloseoutProjection |
agent_doc.transport.patch | patch_id | TransportPatchProjection |
agent_doc.supervisor.owner | owner name | OwnerProjection |
agent_doc.route | document_hash | RouteProjection (singleton, including RouteSubmitProjection) |
agent_doc.proof.marker | marker | ProofMarkerProjection |
Node payloads are base64(serde_json(struct)).
While a cycle is open, its closeout payload may include realtime_steering.
RealtimeSteeringObserved facts replace that aggregate using canonical CRDT
content-hash event identity, so warm subscribers receive an ordinary cell_set
delta. The payload carries the primary steering kind, total directive count,
preview, and full ordered verbatim aggregate. PreflightStarted, commit, and
abandonment clear the field at cycle boundaries.
Derivation edges
The snapshot/delta carry dependency edges so a plugin mirror can invalidate/recompute only a derived subtree instead of re-rendering the whole projection:
closeout.cycle → document.baselinequeue.head → closeout.cycletransport.patch → closeout.cycleroute → supervisor.owner[route_dispatch]transport.patch → supervisor.owner[editor_ipc_bridge]
Epoch + idempotence
- lazily-spec
epoch= per-document monotonic counter = number of accepted (deduped)StateEvents targeting the document (EventLedger::document_epoch). A re-emit/replay does not bump the epoch. - The projection is a pure fold of deduped events, so delta application is
deterministic and idempotent — a re-emit yields a no-op (empty) delta. This is
the property
#queuestatemachine/#qdedupsyncbuild on. - The ledger is append-only within a process lifetime, so any
last_epoch <= current_epochis satisfiable without a resync. Deltas may span multiple epochs (epoch > base_epoch + 1); the orderedopsconverge identically to a fresh snapshot.
The type_tag table is the in-repo producer half of the wire vocabulary. The
canonical schema pin (lazily-spec/schemas/agent-doc-state.json + a conformance
snapshot/delta pair) is #lazilyspecpin, a sibling lazily-spec change.
Actor Ownership
Live mutable surfaces have exactly one current owner:
- document writer
- editor IPC bridge
- route/dispatch actor
- supervisor/child process actor
- queue orchestrator
Actors communicate by events, leases, epochs, and explicit proofs. A stale actor may report facts, but projections must reject reports whose generation or epoch does not match the current owner. This is why restart and capability proof code uses epochs rather than trusting any later-arriving thread result.
Implementation: owner reports carry a StateOwner and generation. The backbone
projection records rejected stale events instead of applying late reports from
an old editor IPC bridge, route dispatcher, supervisor, queue orchestrator, or
document writer.
Where Other Models Fit
| Model | Fit | Use |
|---|---|---|
| FSM | Strong local fit | small closed state sets: cycle phase, transport connection, proof gate, recycle lifecycle |
| Behavior tree | Policy helper only | inspectable recovery/dispatch priority and fallback logic that reads projections and emits commands |
| GOAP | Poor durable-state fit | avoid for correctness-critical closeout; planning machinery is harder to replay than reducers |
| Coroutine | Good protocol expression | linear handshakes such as writeback, startup proof wait, or clean-exit restart; persist checkpoints as events |
| Event-driven | Strong backbone fit when typed | append-only events, causation ids, idempotent reducers, replay tests |
| MPC | Not a fit | agent-doc state is discrete workflow convergence, not continuous control optimization |
Implemented local FSMs:
| FSM | Domain | Purpose |
|---|---|---|
CyclePhaseMachine | closeout | existing turn lifecycle authority |
QueueHeadMachine | queue | pending, selected, deferred, completed head lifecycle |
QueueContextClearMachine | queue | deferred operator clear, explicit context-clear in-flight, and settled window |
QueueDrainStallMachine | queue | one-shot continuation-pending stall signal versus reconciled/cleared |
TransportPatchMachine | transport | queued IPC patch, applied/rejected receipt, insufficient proof, retry, force-disk fallback |
ActorLifecycleMachine | supervisor/owner | starting, ready, busy, waiting-input, restarting, stale, closed |
SupervisorRecycleMachine | supervisor/recycle | in-flight versus settled supervisor recycle gates |
RouteReadinessMachine | route | pane observed through dispatch proof |
RouteSubmitMachine | route | idle, in-flight, and bounded blocked submit windows |
ProofGateMachine | proof | marker observed versus disproved |
Regression Rule
New state-bearing behavior must answer two questions in the same change:
- Which owner emits the typed event?
- Which projection reduces it into current state?
Adding another tactical reason=..., proof=..., ad hoc boolean, or log-only
string in a hot path is not enough unless the change also explains why that fact
is intentionally not part of the state backbone.
Flow Map
This reference records the first FlowCore ownership map. It is an inventory,
not a behavior change plan: command modules still execute the current hot path,
while src/flow provides typed state, pure decisions, and mirror-mode
operational events. The broader durable-state contract is defined in
State Backbone: keep the Cycle State
Machine scoped to response-turn closeout, then derive queue/document/transport/
supervisor/proof state from typed events and deterministic projections.
Owning Flows
| Flow | Owns | Current hot-path modules |
|---|---|---|
session_cycle | Direct invocation lifecycle: preflight, plan, execution, patchback, commit, session-check | preflight.rs, plan.rs, run.rs, write.rs, git.rs, session_check.rs |
routed_reopen | Actor binding, readiness barrier, dispatch authorization, submit, dispatch proof | route.rs, start.rs, project_controller.rs, session_actor.rs, supervisor/* |
closeout | Terminal response durability, pre-write/pre-commit guards, retryable repair boundaries, and commit/session-check guard | write.rs, git.rs, repair.rs, session_check.rs, cycle_state.rs |
document_mutation | Patchback shape parsing, component mutation, prompt ownership normalization, duplicate repair, and late fallback mutation refusal | write.rs, template.rs, merge.rs, pending.rs, repair.rs |
operator_clear | Clear Session Context and interrupt-clear guard decisions | session_cmd.rs, editor clear actions, route.rs readiness helpers |
orchestration_batch | Queue freeze, child dispatch, child patchback normalization, batch stop/resume | orchestrate.rs, queue.rs, queue_dispatch.rs, plan.rs |
Duplicated State Checks
The recurring bug classes map to a single proposed owner:
| Bug class | Current duplication | Flow owner |
|---|---|---|
| Starting/busy actor dispatch into an unready pane | route.rs readiness loops, controller actor state, supervisor runtime probes | routed_reopen |
| Accepted-only dispatch proof described as submitted/consumed | route log strings, ops summary classification, harness-specific proof checks | routed_reopen |
| Late editor-intent retry after committed cycle | write-pipeline terminal checks, state.db closeout projection, git no-op closeout | closeout |
Malformed or plain template patchback escaping agent:exchange | write.rs, orchestrate.rs, repair.rs, template parser callers | document_mutation |
| Prompt prefix or duplicate prompt repair before projection trust | Final template reconciliation, typed editor intent, Lazily rebase, post-commit repair, session-check | document_mutation |
| Pending capture or done guard ambiguity | write.rs, session_check.rs, plan.rs, pending.rs | session_cycle |
| Clear Session Context racing active panes | editor actions, session_cmd.rs, route readiness classification | operator_clear |
| Queue item mutation between children | queue.rs halted-state detection, orchestrate.rs child loop, child finalize | orchestration_batch |
Stringly Typed Proof Fields To Promote
These fields currently cross modules as strings and should converge on FlowCore enums as extraction proceeds:
| Field | Proposed enum |
|---|---|
proof, proof_scope | DispatchProof |
| routed reopen prompt-ready / dispatch-start failure reasons | RoutedReopenGuardReason |
actor_state, runtime_state, supervisor_health | routed reopen actor facts |
drift_kind, basis, reason=already_current, pre-write/pre-commit guard names | CloseoutGuardReason and closeout terminal-state reason |
markers, patches, exchange_patches, unmatched_len | PatchbackShape and document-mutation parse event |
source, write_mode, patch_id, cycle_id | DocumentMutationKind and closeout ids |
queue_halted, item_modified, child task labels | orchestration batch outcome |
| protected/busy/idle clear states | operator clear input state |
Mirror-Mode Event Contract
ops.log may now contain flow_event records:
flow_event file=<path> flow=<flow> stage=<stage> outcome=<outcome> reason=<token>
agent-doc ops summary groups these events by flow stage and outcome. It keeps
named buckets for common failures, and falls back to a generic
flow <flow> <stage> <outcome> bucket for newly added typed events so a
FlowCore emitter cannot disappear into an undifferentiated tactical-log bucket.
The mirror-mode emitters now cover routed reopen prompt-ready and dispatch-proof
failures, document-mutation patchback parse and visible-write guard outcomes,
document-mutation template-structure repair/fail-closed outcomes, strict
closeout guard blocks, committed-cycle late-fallback rejection, repair recovery
boundaries, orchestration child patchback normalization, operator clear guards,
and closeout commit completion. Later phases should replace tactical log parsing
with flow events rather than adding more free-form log strings.
Regression Gate
The hot-path regression gate has two parts:
- Routed-reopen prompt-ready and dispatch-proof failures use
RoutedReopenGuardReason, soroute.rscannot pass arbitrary failure reason strings into FlowCore events. tests/test_cli.rs::flowcore_hot_path_guard_and_proof_tokens_are_budgetedbudgets existingguard_,proof=,proof_scope=,reason=,flow_reason=, andaccepted_onlytokens in route/write/preflight/ session-check/orchestrate/git/repair files. A budget change is the audit point: new tactical guard/proof tokens must first move into the owning FlowCore enum/event, or the test expectation must be updated with that audit complete.
Phase Boundaries
Phase 1 adds the vocabulary, pure decision helpers, mirror-mode events, and
ops-summary grouping. The routed-reopen extraction has started: delivery mode,
dispatch-start proof, degraded-authority refusal, runtime guard, event
construction, and prompt-ready-barrier classifiers now live in
flow::routed_reopen, while route.rs keeps tmux, supervisor IPC, and
controller I/O. Route, closeout, document mutation, operator clear, and
orchestration extraction should continue by moving one pure decision at a time
behind the new types, then deleting the corresponding tactical branch from the
legacy module after equivalent deterministic coverage exists.
Active Turn Lifecycle And Replay Paths
This reference stores the generated diagrams for active agent-doc turns, routed dispatch readiness, post-baseline prompt ownership, JetBrains stale cache/conflict replay, and late fallback patch replay.
The implementation source of truth remains the Rust code and split specs. Keep this page current when the implementation, workflow, or architecture changes in ways that alter these lifecycle boundaries.
Active Turn Lifecycle
agent-doc owns the document lifecycle: preflight, baseline selection, diff
classification, response merge, snapshot/capture state, commit, and
session-check. Claude, Codex, or OpenCode owns the live reasoning/tool turn
between plan and finalize.
sequenceDiagram
autonumber
participant User
participant Doc as session markdown document
participant AD as agent-doc CLI
participant Snap as snapshots and captures
participant Harness as Claude or Codex session
participant Tools as repo tools and shell
participant Git as git commit/session-check
User->>Doc: edit prompt or queue directive
User->>AD: invoke agent-doc <file>
AD->>Snap: preflight reads last snapshot/baseline
AD->>Doc: diff user edits against snapshot
AD->>AD: classify prompt, claims, queue, model tier
AD->>Harness: expose prompt targets and execution contract
Harness->>Doc: read current document context
Harness->>Tools: perform requested repo work when needed
Tools-->>Harness: command output and verification evidence
Harness->>AD: finalize response patch with baseline
AD->>Doc: merge response into exchange
AD->>Snap: update snapshot/capture state
AD->>Git: commit session document writeback
Git->>AD: post-commit session-check result
AD-->>Harness: closeout success or recovery interruption
Harness-->>User: console summary after committed closeout
A response that only appears in the console is not complete. It becomes part of
the managed turn only after finalize writes it into the document and the
binary-owned commit/session-check boundary passes.
Dispatch Readiness Block
Dispatch-only routing can reuse an authoritative actor only when the live pane proves it can consume input for the same generation. An idle-looking console is not enough proof.
flowchart LR
A["User invokes Run Agent Doc"] --> B["route resolves document target"]
B --> C["authoritative actor store owns a pane for the document"]
C --> D["dispatch-only route checks for a prompt-ready live pane"]
D --> E["pane does not prove harness-specific readiness"]
E --> F["wait for the same generation to return ready"]
F --> G{"ready prompt observed?"}
G -->|"yes"| H["inject trigger into the owned pane"]
G -->|"no"| I["fail closed: do not inject into a busy or unproven actor"]
This guard prevents a second trigger from being mixed into an active or unproven turn. Future queue-first dispatch should route repeated dispatches through a single document lease and a durable queue instead of directly trying another pane injection.
Prompt Ownership After Baseline
Whole-buffer editor ACK content is an observation, not authority. If the ACK contains user-owned prompt text created after preflight, agent-doc must preserve that prompt for the next cycle and must not absorb it into the committed snapshot for the current response.
flowchart TD
A["preflight baseline"] --> B["user prompt entity P1"]
B --> C["agent builds response entity R1"]
C --> D["editor ACK returns whole buffer"]
B --> E["user keeps typing prompt entity P2 after baseline"]
E --> D
D --> F{"ACK contains post-baseline user entity?"}
F -->|"no"| G["adopt response snapshot"]
F -->|"yes"| H["block ACK adoption for user-owned region"]
H --> I["commit R1 from agent-owned response image"]
H --> J["leave P2 visible for next preflight"]
F -->|"duplicate P1/P2 detected"| K["repair duplicate artifact or fail closed"]
The durable model should distinguish user prompt entities from agent response entities. Component ranges, content hashes, owner fields, and cycle ids give the write path enough evidence to preserve post-baseline prompt text without duplicating older prompts.
JetBrains Stale Cache Conflict Replay
The intended replay exists because JetBrains may hold an older cached buffer after disk changed underneath it. If the user accepts that cached editor buffer hours later, agent-doc should treat the next managed cycle as delayed replay classification, not as permission to trust the whole buffer.
flowchart TD
A["agent-doc commits document at HEAD"] --> B["editor still has older cached buffer"]
B --> C["JetBrains File Cache Conflict appears"]
C --> D{"operator choice"}
D -->|"reload from disk"| E["safe: editor matches HEAD"]
D -->|"accept cached editor buffer"| F["stale replay into working tree"]
F --> G{"dedupe/replay classifier matches HEAD?"}
G -->|"yes"| H["repair only through proven editor/disk-owned path"]
G -->|"no"| I["fail closed: preserve drift for investigation"]
G -->|"classifier misses duplicate"| J["bad path: duplicate exchange or old prompt can persist"]
agent-doc may warn, disable stale patch application, and remove its own stale sidecars after timeout. It must not dismiss the IDE dialog by choosing disk or cached content for the user unless the editor exposes an explicit cancel-agent-patch operation that preserves the user's content choice.
Late Fallback Patch Replay
Fallback patch replay exists so an IPC timeout can still deliver a response if the socket path failed while the cycle is still open. After the same cycle is committed, every fallback for that cycle is stale.
flowchart TD
A["finalize sends socket IPC patch for cycle C"] --> B{"socket ACK before timeout?"}
B -->|"yes"| C["response visible and committed"]
B -->|"no"| D["write fallback patch file"]
D --> E{"cycle C still open?"}
E -->|"yes"| F["file watcher may apply fallback"]
F --> G["closeout commits cycle C"]
E -->|"no, already committed"| H["reject stale fallback and clean up"]
C --> I["delayed watcher sees old fallback"]
I --> J{"cycle-state guard present?"}
J -->|"yes"| H
J -->|"no"| K["bad path: stale response patch re-dirties document"]
Cycle id, patch id, capture hashes, and terminal closeout state should reject stale fallback files before they can mutate the editor buffer, snapshot, or working tree.
Elimination Boundary
Full-document IPC corruption is eliminated by construction only for first-party paths that obey these invariants:
- template and component documents use component patches or fail-closed retry;
- first-party senders do not emit
fullContentpayloads for managed documents; - first-party editor plugins reject or delete legacy
fullContentpayloads; - ACK/file-read content cannot become snapshot authority when it contains post-baseline prompt drift absent from the agent-owned response image;
- committed-cycle fallback patches are rejected by cycle state before visible mutation;
- duplicate prompt repair remains defense in depth, not the primary guarantee.
External editors, stale caches, or foreign tools can still write arbitrary whole-file content. agent-doc can detect, refuse, repair bounded duplicate shapes, or fail closed in those cases, but it cannot make external writes impossible.
Editor intent transport
Purpose
Agent-doc connects the Rust controller to editor plugins without external-file reloads, cursor displacement, or whole-buffer replacement. The transport carries typed intent and proof; Lazily carries the live document value.
Authority model
operator keystrokes
|
v
editor buffer <-> Lazily current
^ |
| typed intent | current value + causal receipts
| v
PID-scoped socket <-> project controller <-> state.db
|
v
native editor save -> disk projection -> git commit
- Lazily current is the only live document authority while an editor replica is attached.
state.dbowns durable intent and closeout phase.- Disk is the persistence/commit projection.
- Recovery snapshots are cold audit/projection material, never a live input.
Intent envelope
{
"type": "apply_canonical",
"intent_id": "uuid",
"cycle_id": "cycle-...",
"file": "/absolute/path/session.md",
"expected_generation": 42,
"expected_current_hash": "sha256",
"mutation": {
"node_patches": []
}
}
The accepted intent names are defined once as EditorIntent and mirrored
verbatim by Rust, JetBrains, and VS Code. Mutations are node-keyed or
component-keyed operations with expected source proof; replacement content is
not a transport operation.
Receipt envelope
{
"intent_id": "uuid",
"cycle_id": "cycle-...",
"editor_id": "member-id",
"phase": "replica_visible",
"generation": 43,
"current_hash": "sha256",
"causal_frontier": "..."
}
The controller validates identity, generation, hash, and causal frontier before advancing its monotonic state machine:
IntentCaptured -> CanonicalApplied -> ReplicaAccepted -> ReplicaVisible
-> DiskProjected -> Committed
Retries resume the same intent from the recorded state. Receipt replay is idempotent; stale or future-generation receipts are rejected.
Concurrency and rebase
Immediately before an editor mutation, the plugin rechecks Lazily current and the native editor generation. A mismatch means the operator changed the buffer. The plugin performs no mutation and returns the newer proof; the controller rebases the same narrow agent intent on that current value.
This rule preserves unsaved prompts and queue deletions. It also prevents an old response delivery from duplicating boundary/component markers after reconnect.
Focus neutrality
The target document must already be open. Background transport may not open a file, choose a tab, move focus, scroll, or alter layout. A missing open target is a typed rejection, not permission to activate the document.
Failure behavior
Timeouts, disconnects, plugin crashes, and editor restarts leave the durable intent at its last proven phase. The keyed controller worker retries with bounded backoff after replica registration. No filesystem inbox is scanned and no disk fallback is attempted for an attached document.
An ABI or capability mismatch fails closed and asks for the matching plugin and native library. There is no compatibility transport on the live path.
Verification
SimWorld exercises all crash points, receipt reorderings, concurrent operator edits, editor disappearance/reconnect, and duplicate retry delivery. Adapter tests additionally prove generation rechecks, focus neutrality, exact receipt shape, and the absence of alternate attached-document transports.
Full-Document IPC Corruption Chain
This reference records the repeated live failure chain where whole-document editor IPC could race user typing, leave duplicated prompt text, and then repeat on the next visible edit. The mitigation is intentionally wide: full-document IPC is disabled at both the binary sender and editor-plugin receiver.
Logic Chain
flowchart TD
typing["User types a prompt in agent:exchange"]
staleState["Snapshot, sidecar, or patch state was computed before the latest keystrokes"]
fullPayload{"A fullContent payload exists?\nold sender, stale file patch, or foreign tool"}
oldReceiver["Old editor plugin accepts fullContent after source proof"]
wholeReplace["Editor replaces the whole document buffer"]
clobber["Live prompt bytes are overwritten, duplicated, or reinserted from stale content"]
promptVariant["Prompt-prefix normalization sees adjacent raw/prefixed prompt variants"]
duplicateRepair{"Repair catches every duplicate before closeout?"}
committedClean["Snapshot and committed blob stay clean"]
residue["Duplicate prompt residue remains visible or becomes new drift"]
nextTyping["User keeps typing the same prompt"]
repeat["The stale full-document path can fire again and repeat the sequence"]
disabledBinary["New binary: try_ipc_full_content logs disabled and returns false before socket/file payload construction"]
guardedDisk["Caller uses narrow component patching or fails closed for retry"]
disabledPlugin["New editor plugins: delete file-watch fullContent patches and reject socket fullContent payloads"]
adoptionGuard["New binary: ACK/file-read snapshot adoption rejects live prompt drift after preflight"]
noReplace["No whole-buffer editor replacement occurs"]
promptHandoff["Live prompt remains visible and outside the committed snapshot"]
narrowPatch["Only component patches, prefix normalization patches, reposition patches, or retry-only failure remain"]
typing --> staleState --> fullPayload
fullPayload -- "yes, before this fix" --> oldReceiver --> wholeReplace --> clobber --> promptVariant --> duplicateRepair
duplicateRepair -- "yes" --> committedClean
duplicateRepair -- "no" --> residue --> nextTyping --> repeat --> fullPayload
fullPayload -- "yes, after this fix" --> disabledPlugin --> noReplace --> narrowPatch
staleState --> adoptionGuard --> promptHandoff --> narrowPatch
staleState --> disabledBinary --> guardedDisk --> narrowPatch
fullPayload -- "no" --> narrowPatch
Disabled Path
- The Rust binary keeps the committed-cycle cleanup guard, then returns
falsefrom full-document IPC paths before constructing socket or file payloads. - JetBrains and VS Code plugins no longer treat source-buffer proof as
permission to apply
fullContent. - File-watch payloads containing
fullContentare logged and deleted as disabled stale/foreign patches so they cannot retry indefinitely. - Socket payloads containing
fullContentfail closed. - Socket/file ACK content and file-read IPC fallback content are not adopted as
the committed snapshot when they contain prompt-bearing exchange drift that
appeared after preflight and is absent from the agent-owned response image;
the binary logs
stage=ipc_snapshot_adoptionwithreason=live_prompt_drift_after_preflightand keeps that prompt for the next cycle. - Duplicate prompt repair remains as defense in depth, but correctness no longer depends on it catching corruption caused by a whole-buffer editor replacement.
Document Node-Merge Architecture
How agent-doc merges concurrent edits (agent writes vs. live editor keystrokes) without splicing content across unrelated regions of the document.
Companion read: Full-Document IPC Corruption Chain describes the failure mode this architecture exists to eliminate.
Maintenance: This document tracks the merge engine in
agent-doc-merge/src/lib.rs. When the merge logic changes (merge,merge_by_component,segment_into_cells, or the roadmap phases below), update this file in the same change.
The problem: whole-document blob merge
The original merge (crdt::merge) treats the entire document as one Yrs Text blob.
Every write / finalize / convergence threw the whole file into a single three-way merge
(base, ours, theirs). Because the merge had no notion of document structure, a queue
keystroke and an agent exchange write competed inside the same merge even though they
touch unrelated regions — and text could land in the wrong region.
Live repro: agent console output (● Supervisor is now fresh …) got merged into the
agent:queue component as a fenced block while the operator was typing a prompt. The
stale-base-detection and dedup_adjacent_blocks hacks were band-aids over duplicates that
blob-merging itself produced.
The node model
A node is an isolated merge unit. The document is segmented into nodes, and each node merges only against its own prior state — two nodes never share a merge, so content can never splice from one into another.
The coarsest level of this model is per-component: each <!-- agent:* --> block
(exchange, queue, backlog, review, done, …) is a node, with the text between
components ("interstitials") paired positionally. The roadmap generalizes the same idea
recursively down to individual list items and ### Re: blocks.
Code vs. prose terminology: the concept is called a node. The current code symbol is
Cell(segment_into_cells,Cell::Component,Cell::Interstitial) — a symbol rename to match the prose is tracked separately. Read "cell" in the code as "node."
Current implementation — merge_by_component
Shipped as the anti-corruption rung (#qnodemerge1). Entry point:
crdt::merge_by_component(base_state, ours_text, theirs_text). Both FFI merge entry points
route through it.
- Whole-document replay gate — canonicalize an exact or monotonic duplicate projection
on either side before any tree/CRDT reconciliation. If one side contains two structurally
complete but divergent projections, reject the merge: the ordinary whole-document fallback
is not allowed to concatenate an ambiguous replay. Monotonic comparison uses the shared
code-block-aware
(HEAD)normalization and exchange-scoped prompt-prefix normalization; the retained projection remains byte-verbatim. - Short-circuit — if
ours == theirs, return as-is. - Segment both
oursandtheirsinto nodes viasegment_into_cells. If either fails to segment, fall back to the whole-docmerge(logged). - Inline-mode guard — if neither side has any components (a component-less / inline
document), delegate to the legacy whole-doc
mergewith the original state, preserving exact prior behavior. - Structural-divergence guard — if the set or order of component names differs between
oursandtheirs, a per-node pairing is unsound, so fall back to the whole-docmerge(logged), subject to the complete-document replay gate above. Structural reconciliation across differing node sets is the job of the recursive phase (#qnodemerge3), not this rung. - Per-node base alignment — decode the base state once, segment it, and build a
name → contentmap (base_by_name) plus a positional list of interstitial base slots. Each node resolves its own base: components by name (so theexchangecommitted-response guard sees its real base), interstitials by position. - Per-cell merge — first project aligned components into keyed children and compose each child against its own base with component ownership. Duplicate prompt keys receive stable occurrence ordinals: the operator-owned side's exact multiplicity wins, so intentional duplicates survive while retained-agent-only replay copies do not multiply.
- Component-local leaf fallback — if one aligned component changes splittability (most commonly a keyed response append racing a body-only live exchange), run the legacy leaf merge for that component only. The mismatch must not demote unrelated queue/backlog components to a flat CRDT merge with the original base.
- Recombine in document order.
The leaf merge is still the whole-doc merge, applied to one component's text at a time.
The leaf merge — merge
Three-way CRDT merge over text using three Yrs actors (base, ours, theirs): apply each
side's diff-from-base, merge the updates, return the conflict-free result. Two safeguards
matter for correctness:
- Stale-base detection — if the base text shares too little with both sides (checked via
common prefix and suffix, since template documents bookend the exchange with structural
frontmatter / markers / pending sections), the base is treated as stale and
oursis used as the base to prevent duplicate insertions. - Committed-response preservation (
#ipc-crdt-response-drift) — committed### Re:blocks are append-only history. The merge captures committed response headings from the original base before stale-base advancement can rewrite it, so a stale or divergenttheirscan never delete a committed response out of the merged result. Boundary markers (<!-- agent:boundary:… -->) and working-tree-only(HEAD)annotations are treated as transient and never count as new content.
Durable per-node base — MultiNodeState (#qnodemerge2)
merge_by_component derives each node's base by decoding one whole-doc state and slicing it
by name — so the persisted base (<hash>.yrs) is still a single blob whose Yrs clock is shared
across every node. MultiNodeState makes the base durable per node:
- One Yrs state per node, one file.
MultiNodeState::from_textsegments the document into top-level nodes (components + interstitials) and encodes each node's text as its own Yrs state.encode/decoderound-trip the whole set into a single self-describing container (MAGIC | version | count | [name, state]…) persisted at.agent-doc/crdt/<hash>.nodes.yrs. - Deterministic encoding. Node states use a fixed Yrs client id (
encode_text_deterministic) so identical text always re-encodes to byte-identical bytes — an untouched node's base is provably unchanged across a cycle, and there are no spurious sidecar rewrites. The base client id is irrelevant to the leafmerge(which reads only the base text), so this is safe. - Independent advance.
MultiNodeState::merge(base, ours, theirs)runs the same per-node reconciliation asmerge_by_component(sharedmerge_aligned_nodes), but resolves each node's base from its own persisted state and returns a freshMultiNodeStatewhere only changed nodes advanced. Structural divergence / component-less docs fall back to the whole-docmerge, same safety net as the component rung. - Migration & GC.
snapshot::multinode_crdt_statereads the.nodes.yrssidecar, lazily migrating a legacy whole-doc<hash>.yrs(decode → split) when the sidecar is absent.save_document_crdtrebuilds and rewrites the sidecar every cycle (so compaction, which routes through it, GCs per node),delete_crdtremoves it, and the rename migration carries it with the document. The legacy<hash>.yrsand<hash>.overlay.yrsare still written for back-compat.
Recursive reconciliation — reconcile_component (#qnodemerge3)
The Phase 1/2 layers stop at the top-level node (component + interstitial) and run the leaf
text merge on each whole component. Phase 3 drills the same keyed reconciliation one
level deeper, inside any component whose body is a sequence of keyed children:
- List components (
queue/backlog/review/done): each- …markdown item is a child, keyed by its durable#id(the identity the strike paths already use) or, for a free-text item, its normalized text (strike markers / pin glyphs / checkbox stripped, so a strike keys the same). Continuation lines attach to the preceding item; leading text is a reserved preamble child. exchange: each### Re:block is a child keyed by its heading (minus the working-tree-only(HEAD)). A prompt typed into the exchange attaches to the block it falls within, so it reconciles within that block and never cross-splices into another.
reconcile_component_body matches ours/theirs/base children by key (React-VDOM style):
- Matched, different → leaf
mergeagainst that child's own base. Two edits to different keyed children land in separate sub-trees and can never contend — editing queue item B while item A is the running head leaves A byte-identical, and an interleaved exchange prompt converges alongside a freshly appended### Re:block with zero cross-block splice. - Key on one side only → a pure insert (key absent from base — kept, so a concurrent user
queue addition is never dropped) or a delete (key in base, the other side unchanged — honored
for list items; never for committed
exchangeblocks, the per-block#ipc-crdt-response-driftguard). A modify-vs-delete conflict keeps the surviving content. - Order is
order_union: ours' order is the spine, theirs-only inserts woven in after their nearest placed theirs-predecessor — deterministic for the common append/insert-on-one-side cases.
Segmentation is lossless (concat(children) == body) and the whole path is fail-safe: an
unsplittable component, malformed marker framing, ambiguous (duplicate) keys, or any leaf merge
error falls the component back to the flat whole-component merge (current behavior), so Phase 3
strictly narrows contention without widening the corruption surface. Because the engine lives in
the shared merge_aligned_nodes, both merge_by_component (whole-doc base) and
MultiNodeState::merge (per-node base) get the recursion.
Op-capture / evented reflection — merge_with_editor_ops (#qnodemerge4)
Every layer above still reconstructs the theirs (editor) side of a merge from a Myers text
diff (compute_edit_ops). A Myers diff returns a minimal edit script — not necessarily the
edit the user actually performed — so two same-region edits can be mis-attributed and duplicate
(the #hap7/#qdup corruption family). Phase 4 removes that guess for the editor side by
replaying the editor's real operations.
EditorOp(crdt.rs): an absolute-offset editor mutation in byte units —Insert { offset, text }/Delete { offset, len }— matching the editor'sDocumentListener.documentChanged/onDidChangeTextDocumentevents. A replacement is captured as aDeletethen anInsertat the same offset. Ops are recorded and replayed in editor order, each offset absolute against the buffer after all prior ops.serde-serializable for the capture sidecar.replay_editor_ops(base, ops): reconstructs the editor's final text by applying the ops in sequence, returningNoneon any out-of-bounds or non-char-boundary offset.merge_with_editor_ops(base_state, ours, theirs, theirs_ops): same contract asmerge, but whentheirs_opsis supplied it feeds the editor's exact ops into thetheirsCRDT side (apply_editor_ops) instead of the diff-guess (apply_ops).
Safety gate (the acceptance invariant "ops replay equals editor-observed state"). The captured
ops are trusted only when replay_editor_ops(base_text, ops) == theirs_text against the resolved
merge base (after stale-base / shared-prefix advancement). If the ops were captured against a
divergent base (advanced base, a missed event), replay won't match and the merge transparently
falls back to the diff-guess — never worse than today. A conservative offset-safety guard also
restricts op-replay to ASCII base/theirs (Yrs index semantics for non-ASCII are not asserted
here); per-component merge means a unicode glyph in queue never disables op-replay for ASCII
exchange prose. merge delegates to merge_with_editor_ops(.., None), so the existing path is
byte-identical until ops are supplied.
Status. The op-capture consumer and supply side are shipped: a per-document op-capture sidecar
(record/load/clear/GC), FFI ingestion (agent_doc_record_editor_op), live merge_contents_crdt
consume+clear wiring, and thin JetBrains DocumentListener / VS Code onDidChangeTextDocument
reporters. Realtime editor-to-editor convergence now delivers the merged result with node-keyed IPC
patches (plus legacy component fallback), so peer buffers can apply targeted node inserts/replaces
under the same ACK proof used by CP-to-editor writes. The zero-duplication live eyeball (a
concurrent live edit + agent write) remains [operator-verify].
Roadmap — recursive AST-node merge
merge_by_component is the component-level (coarsest) rung. The full model applies node
isolation to the entire document AST, reconciled by durable node identity the way a React
Virtual DOM keys its children.
| Phase | What it adds |
|---|---|
#qnodemerge1 ✅ shipped | Component-scoped merge (merge_by_component) — the anti-corruption rung above. |
#qnodemerge2 ✅ shipped | Per-node CRDT state persistence — MultiNodeState (crdt.rs) persists one independent Yrs state per top-level node into a single structured container (<hash>.nodes.yrs), the per-component successor to the whole-doc <hash>.yrs. Each node carries its own stable base across cycles (an untouched node re-encodes byte-identically; a changed node's base advances on its own). Migrates a legacy whole-doc <hash>.yrs lazily, rebuilds (GCs) per node every save/compaction, and follows the document across renames. See Durable per-node base below. |
#qnodemerge3 ✅ shipped | Recursive AST-node reconciliation — reconcile_component (crdt.rs) drills the keyed reconciliation inside each component via the shared merge_aligned_nodes per-node path. Queue/backlog/review/done items are keyed by their durable #id (or normalized text); ### Re: blocks are keyed by heading. Children are matched by key (React-VDOM style): a matched-but-different child runs the leaf text merge against its own base child, a key-on-one-side child is a pure insert (kept) or delete (honored only on a clean delete-vs-unchanged; never for committed exchange blocks). The whole-component text merge stays the leaf and the fallback (unsplittable component, malformed framing, or ambiguous/duplicate keys). See Recursive reconciliation below. |
#qnodemerge4 🟡 core shipped | Op-capture / evented reflection (highest-leverage accuracy lever) — feed real editor operations (DocumentListener.documentChanged / onDidChangeTextDocument) into the per-node model instead of reconstructing edits from a text diff, removing the diff-guess. The merge consumer is shipped: EditorOp + replay_editor_ops + merge_with_editor_ops (crdt.rs) replay the editor's exact ops into the theirs CRDT side behind a replay-exactness safety gate. Remaining (#qnodemerge4wire): the supply side — capture-sidecar persistence, the FFI ingestion entry point, and the thin plugin DocumentListener / onDidChangeTextDocument reporters that record ops live. See Op-capture / evented reflection below. |
#qnodemerge5 | Surface true conflicts, never fabricate — for a genuine concurrent edit to the same leaf node (information-theoretically underdetermined), present both versions for operator resolution instead of silently auto-merging text neither side wrote. |
Accuracy ordering is the dependency order: 2 → 3 → 4, with 5 sequenceable any time after
3. The structural layer (keyed reconciliation) gets the document into the right shape; the
accuracy comes from feeding it real ops (4) on a correct per-node base (2/3) and
refusing to guess on genuine conflicts (5).
Prompt Duplicate Closeout Repair
This reference records the process used to prevent duplicate prompt corruption and unsafe full-document IPC closeout. It focuses on the failure mode where a template session document has already passed through IPC/write handling, but the working tree still contains an adjacent normalized/raw copy of the same prompt before commit closeout.
Process
flowchart TD
userEdit["User edits agent:exchange"]
preflight["preflight captures stable baseline"]
response["Agent response parsed as component patch"]
ipcScope{"Template or agent:* component document?"}
componentPatch["Use component patch or synthesized exchange patch"]
fullContentReject["Reject fullContent IPC for template/component scope"]
sourceProof{"Would caller need fullContent IPC?"}
fullContentDisabled["Return false; fullContent IPC disabled"]
receiverDisabled["Editor plugins reject/delete any fullContent payload"]
guardedDisk["Guarded disk/snapshot fallback"]
editorApply["Editor/socket/file IPC applies patch"]
sidecar["ACK sidecar provides materialized content"]
writeRepair["Write path duplicate-prompt repair"]
snapshot["Save repaired snapshot"]
commitStart["commit closeout reloads snapshot and working tree"]
responseDedupe["Deduplicate adjacent assistant response blocks"]
promptRepair["Commit pre-stage prompt repair against snapshot"]
prefixVariant{"Adjacent prompt-prefix variant?"}
collapse["Keep normalized prompt, remove raw duplicate"]
equivalent{"Repaired file prompt-prefix-equivalent to snapshot?"}
updateSnapshot["Update snapshot to repaired clean content"]
preserveSnapshot["Keep snapshot; do not absorb arbitrary drift"]
driftMeasure["Measure drift from repaired file length"]
stage["Stage snapshot blob"]
commit["git commit plus session-check"]
failClosed["Fail closed on unresolved prompt residue or unsafe drift"]
userEdit --> preflight --> response --> ipcScope
ipcScope -- "yes" --> componentPatch --> fullContentReject --> editorApply
ipcScope -- "no" --> sourceProof
sourceProof -- "yes" --> fullContentDisabled --> receiverDisabled --> guardedDisk --> writeRepair
sourceProof -- "no" --> guardedDisk
editorApply --> sidecar --> writeRepair --> snapshot --> commitStart
commitStart --> responseDedupe --> promptRepair --> prefixVariant
prefixVariant -- "yes" --> collapse --> equivalent
prefixVariant -- "no" --> equivalent
equivalent -- "yes" --> updateSnapshot --> driftMeasure
equivalent -- "no" --> preserveSnapshot --> driftMeasure
driftMeasure --> stage --> commit
promptRepair --> failClosed
Key Invariants
- Template documents and documents with
agent:*components must use component patches or fail closed for retry when editor repair cannot be proven. They must not use full-document IPC for normal response writes. - Whole-document IPC is disabled in the first-party binary. Any normal response
path that would emit
fullContentlogsfull_content_ipc_disabled, returnsfalse, and cannot authorize a direct disk/snapshot repair without separate editor-owned proof. - Editor plugins also reject or delete legacy/foreign
fullContentpayloads without applying them. Source-buffer proof is diagnostic-only. - Write paths that touch
agent:exchangerun duplicate-prompt repair before snapshot trust. - Commit closeout repeats the safe prompt repair subset before staging. This closes failures that appear after IPC has already consumed the response.
- The commit repair is snapshot-bounded: it can remove prompt duplicates already represented by the snapshot and adjacent normalized/raw prompt variants, but it cannot absorb arbitrary user edits into the snapshot.
- Drift diagnostics use the repaired working-tree length so a fixed duplicate is not reported as unresolved out-of-band drift.
Covered Failure
The named regression shape is:
snapshot:
prompt
working tree:
agent prompt prefix + prompt
prompt
Before the fix, closeout could leave this as positive drift and still stage the
snapshot path. The repaired path keeps one normalized prompt, repairs the
working tree, updates the snapshot only when prompt-prefix-equivalent, and logs
commit_pre_stage_prompt_duplicate_repaired.
Regression Tests
write::tests::commit_prompt_repair_dedupes_exact_prefixed_raw_prompt_copygit::tests::commit_repairs_prompt_prefix_duplicate_drift_before_staging
The wider guard surface remains covered by the duplicate prompt suite plus
regressions that assert no fullContent payload is emitted.
Versions
agent-doc is alpha software. Expect breaking changes between minor versions.
Use BREAKING CHANGE: prefix in version entries to flag incompatible changes.
Unreleased
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-backoffforever, 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 zeroremote-drain/transport.pullDeliveryentries 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'srequestRemoteDraingated 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 toack_recovery_force_refreshafterCRDT_ACK_FORCE_REFRESH_AFTER_MS(2000ms) — the only path that called the backoff-bypassingrequestUrgentRemoteDrain. Measured againstops.log: compactwait_mssat at 2646–8695ms with a ~4.5s floor that did not scale withupdate_bytes(46KB→6007ms, 130KB→2646ms), confirming fixed overhead rather than payload cost. Every controller push now drains urgently;request_full_stateremains 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 Docdispatch after a single unproductive drain request (#crdtpushdrain).await_idle_with_max_wait_and_effectslatched its urgent CRDT delivery drain behind a one-shoturgent_drain_requestedflag. A single urgent drain can legitimately apply nothing —drainRemoteUpdatesForreturns 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 gatedrequestRemoteDrain, which an idle document's escalated no-op backoff suppresses. Route then spent its remaining budget polling a frontier nobody would pull and failed withroute 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 atmax_wait. -
A successful urgent drain now resets the escalated no-op backoff counter (
#crdtpushdrain).requestUrgentRemoteDrainnever clearedconsecutiveNoOpReschedules, 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_harnesspreviously updated only the in-memory identity used by the tmux submit profile; nothing wrote the recordroutereads, andtransition_state_*carries the stored harness forward on every later lifecycle transition. A codex→claude switch therefore left the record readingcodexforever, soRun Agent Dockept deferring to a boundary restart that had already run. The supervisor now also writes the switched harness throughagent_doc_session_actor_io::set_record_harness_direct, preserving pane/generation/state. - Route's harness-mismatch guard normalizes both sides (
#actorharnessnormcompare).expected_harnesswas 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 actorbusywhile 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 reportedSent, 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_identityclaimed IPCstate"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 inagent-doc-session-actor-io, and end-to-end by the SimWorld scenarioroute_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 reportedstored_harness=codex expected_harness=claude-codemarker.
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-diskintegration tests were removed from the generic--ignoredsweep, 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_LOCKEDinitialization failures against the same authoritative database for the existing bounded timeout. Controller startup and simultaneous status probes no longer racePRAGMA journal_mode=WAL; corruption and every non-lock error still fail closed without replacing or quarantiningstate.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_writepaths, 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_writewhose 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:boundaryexchange 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_capturedandpending_response_clearedrows 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 reachcontroller.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 -> Committedis 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.dbfails 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 separatereliable_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-checkrepairs the exactresponse_captured/write_appliedsplit 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 recommendwrite --commitin 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-checknow 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 terminalDonestate from a genuinely reopenedOpen/Gatedcopy. 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 JetBrainsRun Agent Docfinds the earlier❯/permissions composer and dispatches normally. The actual online-artifact picker (Enter to openplus 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-Nregistrations 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-checkrecognize 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
DocumentWriteConvergedimmediately 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, leavingsession-checkwith 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-checknow 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-targetsession-checkreopened 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 Docrequest 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 withEsclets 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-checkpreviously 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 openplusclaude.ai/code/artifactmodal as a typed, operator-owned blocker. Prompt-bearing JetBrainsRun Agent Docrequests queue durably behind it and resume after the operator pressesEsc; agent-doc never sendsEscorEnter, 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.mdshowed canonical Lazily authority was structurally valid with one boundary atsession-checkentry; 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.
CrdtLineageFencenow 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.logbefore submitting the pane command, then render an exact shell-level2>>redirection. Argument parsing, admission failures, and every other pre-SupervisorStderrRedirectdiagnostic 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
CrdtLineageFenceTLC 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-checkpointwrites cumulative, complete### Re:cells into Lazily without queue/backlog mutation or commit; later checkpoints replace the prior uncommitted tail.respondis now the primary binary-owned exact-once turn-resolution command, withfinalizeretained 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--doneis 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-buffersnapshots 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-checkclears 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.mdcannot switch an operator away from another document. - The formal and executable recovery models now separate delivery ACK, native editor save, and commit.
CrdtLineageFence.tlaand 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.mdshowed 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-checknow 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-checknow 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.tlaexhaustively 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 RustSimWorldmodel explores all reachable action schedules in normal tests. JetBrains plugin0.2.268carries 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: stopsurvive 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 Nplans 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
stopnow dominates conflicting stalegostate.
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-routerexposes 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, recordingnetwork_probe=unrestricted_shell_dns_https. There is no Codex sandbox in that mode for a nestedcodex execprocess 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_childevent, and otherwise runs the bounded probe normally. This prevents repeated 45-second Codex proof timeouts and retries from turning a healthy adopted sessionblockedafter 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-checkrepairs 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.tlanow 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-checknow 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 donewithout masking unrelated future-work recommendations. - PlusCal/TLA+ covers the reconnect settlement.
CloseoutChurn.tlaproves 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-checkreports binary-owned continuation and explicitly forbids recapture, another finalize/write, or force-disk recovery. - PlusCal/TLA+ covers the post-proof race.
CloseoutChurn.tlanow 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.tlachecks 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
VirtualFilemodification stamp beforesaveDocument, 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 installnow 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-installedupdates 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-librefreshes 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
WriteAppliedcapture 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-promptnow 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_requiredno longer records or prints a manualwrite --commitrecovery command, and terminal authority/disk divergence now schedules replica settlement while declaringsession-checkstatus-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.mdis 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.
PendingExternalDiskDecisionis 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.yrsmodel 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_backpressureevent 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-dirand fails clearly instead of blocking ambiguous non-interactive jobs; PyPI publishing builds a zig-backedmanylinux_2_17wheel; 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 Doccontinues 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 Doccan 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 anagent-docwindow, 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 livesampleorders.mdrefusal where the nestedsample-appregistry was stale while the shared window's%14/%15panes 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
ResponseCapturedfacts, 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 returningactor_pane_not_visibleonce 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/Documentstate inside a bounded read action, then perform logging and asynchronous CRDT re-registration after releasing it. This removes theThreadingAssertionsexception 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_revisionnever 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,
execvepreserves 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 byexecvewhile 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 requireagent-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 inwrite_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-dochappens 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.117heading for the preceding boundary-convergence release is restored to0.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 advancesHEADwith 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
HEADand 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-checknow 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-checknow recognizes the exactHEADtarget 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
HEADonly by agent-doc transient markers,session-checknow clears every obsolete deferred intent, CAS-restores the exact committed bytes through editor authority, and provesHEAD == authority == diskbefore success. This removes leakedno-pending-capturemarkers 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-checknow 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:boundarymarkers 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
AlreadyAppliedrecovery. - 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:boundarymarkers 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_authorityare surfaced directly. Live-prompt recovery now uses the typedIpcLivePromptDriftStateenum; 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
DocumentWriteDeferredcauses 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-diskwrites retain safe editor-reconnect lineage. Before disk changes, the binary records the complete pre-write base and target in a content-bearingDocumentWriteDeferredfact. 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_appliedrecovery 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 GitHEADrestore. If an editor-owned document has no registered replica, the binary retains the complete intended target in a LazilyDocumentWriteDeferredfact, 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 Exchangepreserves 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 Docoperator reopens no longer disappear behind stale dispatch backpressure. The production Project Controller now classifiesmanaged_reopen/dispatch_only_reopenwith 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 thewaiting_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-
committedcycle whose snapshot/HEADimage was compacted while JetBrains still held the expanded response. The recoveredAlreadyAppliedresponse now crosses the ordinary converged commit/session-check boundary exactly once; the binary does not restore the editor buffer toHEAD, 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, emptywrite --commit, and the single recovery commit.
0.34.104
- JetBrains
File Cache Conflictrecovery now preserves editor authority. Socketalready_appliedrecovery 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.252and VS Code0.2.50return 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-rsDeadlineCore-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 vscodenow requires the VSIX version to exactly matcheditors/vscode/package.jsoninstead 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-alivepolicy, while controller recovery keeps bounded one-shotautoreaping. 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 JetBrains0.2.250/ VS Code0.2.48use lazily-kt/jsDebounceCoreKeepLatest 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, oropencode --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%83remained registered tosampleportal.mdbut CP recycle launched Claude with--continue, attached sessioncb1415be…, 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, andself-installbuild a complete executable and atomically rename it over the installed path; controller/supervisor exec calls therefore never observe theNo such file or directorywindow produced bycargo install --force. Background auto-install now waits for a clean committed checkout, suppresseslib-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_stateprojection 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%87feedback loop. JetBrains plugin0.2.249carries 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
ResponseCellAddedstate 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 throughrestart-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. OneinvokeLatermutation is admitted per EDT turn, so a blocked UI cannot grow an unbounded apply FIFO or leave the replica worker synchronously parked ininvokeAndWait; no-op retry backoff is scheduled and coalesced instead of parking that worker for up to 30 seconds. Plugin0.2.247consumes lazily-kt0.28.0for 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 asSummarize recent commitsno longer block JetBrainsRun 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-sessioncontinues 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 followingwrite --commitno 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
ResponseCellAddedas the realtime backbone'sWriteAppliedreceipt. 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 repeatedRun Agent Docclicks and recovers stale boot-state routing. A timed-out startup classification receives one bounded retry, while an authoritativeReadyactor 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). Thecommit_documenthandler 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 selectivehash-object/update-indextransaction itself was fast. - JetBrains plugin (
0.2.245) shows⟳ agent-doc: Compacting Exchangein 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, andmake version-syncfails if a new package omits that boundary. The release target publishes only the PyPI distribution; GitHub Releases remain the primary binary channel, whiletmux-routerkeeps its independent public release lifecycle. Installation docs no longer advertisecargo install agent-doc, andagent-doc upgradediscovers 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 realagent-doc skill install --harness codex --reload restartupdate, the installed Codex instructions immediately requestagent-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-
Enterretries 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.244packages 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, whoseTextCrdt::insert_strprojects visible order once and whose newdelete_rangetombstones a precomputed range. The native editor replica no longer rebuilds the full origin tree for every inserted or deleted character; JetBrains plugin0.2.243carries 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 withlive 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-planeP4). 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=0rollback 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.openFilesand VS Code enumeratesworkspace.textDocuments; both liveness graphs suppress a duplicate open callback so startup seeding emits exactly one durable fact. JetBrains plugin0.2.242and VS Code extension0.2.47include the matching startup path. Agent-doc now consumes lazily Rust0.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 incrementalTextOpdeltas 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-independentOutbox<S>;agent-doc-sqlitenow 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 plugin0.2.241includes 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, soCompact Exchange/write --commit/finalizeused 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_outcomecallscommit_document_via_controller: for a headless document it returnsOk(None)(the CLI commits locally as before); for an editor-attached document with a reachable controller it sends a newcommit_documentRPC. The controller handler (handle_commit_document_rpc) flushes live editor ops into the canonical (commit_barrier_for_file), then runs the commit via theProjectControllerRuntimeEffects::commit_documentport (the binary wires it toagent-doc-commit-io, which depends onagent-doc-controller-io, so the controller cannot call it directly). ACONTROLLER_COMMIT_IN_PROGRESSscope 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-diskand 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 onagent-doc-bugs2.md: with an older plugin whose CRDT replica register failed, the editor held a phantom lease (live_editors == 0yet 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_authoritativethen 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 (newRelayHub::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), andverify_compact_head_landedstill 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 failedregister()logged only a bare[crdt-replica] register failedWARN with no reason, so a live wedge (controller socket unavailable,ok=false, oversized bootstrap round-trip, or a nativereplica_openABI/bootstrap rejection) was undiagnosable fromidea.log. The transport now records the specificsend/register error (socket_unavailable,controller_error,missing_data,missing_client_id) andregister()logs it, distinguishing a transport-null failure from anative.openrejection;NativeReplicaNode.opennow logs its throwable at WARN. Aligns with the "NEVER swallow errors" convention.
0.34.84
ProvenPanecapability 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 aProvenPanenewtype whose only constructors are provenance-gated (from_recorded_owner, which runsrecorded_owner_pane_is_safe_target, andfrom_verified_live_ownerfor panes the sync resolver already proved).send_clear_to_panenow takes&ProvenPane, so a caller cannot submit into an unchecked pane without a compile error. This is the prototype scope (direct-submit path); rollingProvenPaneout 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_reusetests.
0.34.83
session clear/ interrupt / resubmit no longer send input into a foreign pane that reused ours (#stale-actor-pane-collisionpart 2).resolve_direct_submit_panereturned the authoritative-actor (or registry) pane as a direct-input target onpane_alivealone — 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, asession clear/ interrupt / resubmit would have injectedCtrl-C//clear/ prompt text into that foreign session's pane. The direct-submit resolver now gates the authoritative-actor and registry panes throughrecorded_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. Thefind_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 sharedevidence_pane_is_foreign_reusedecision 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, andlive_pane_evidence_for_panereported italive-idlefromauthoritative_actorbecause the pane was alive and showed a claude idle prompt — sosession status, sync, and focus all treated a foreign pane as our live actor (observed: runningSync Tmux Layoutonsampleportal.mdaround an IDE-restart / harness-switch window leftagent-doc-bugs2.mdpointing 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 asprojection-staleso 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 fromcodextoclaude, the in-loop restart path (run.rs) spawned a fresh harness (agent_restart_performed) and updated its localharnessbinding, butSupervisorShared.harness_binarywas an immutableStringfixed at construction. The IPCstateresponse — 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 windowroutesawstored_harness=codexvsexpected_harness=claude-codeand emittedroute_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_binaryis now aMutex<String>withcurrent_harness()/set_current_harness(); the fresh-spawn branch callsset_current_harness(&new_harness)so IPCstatereports 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 emitssync --focus <file> --exact-visible --no-autostart. Under that projectionskip_autostart_diagnosticsforcedregistered_live_ownerfalse for every file, so each one was markedblocked_unresolvedand the layout-preservation guard early-returned before thetmux_routerreconcile ever ran — a focused document whose owned pane was merely parked off-screen (a stash window / another window) never got swapped into view (observed: focusingsampleportal.mdleft another document's pane in the visible column with no auto-sync).agent-doc-sync-io::syncnow 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 ruleexact_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: SimWorldsync_sim_exact_visible_focus_swaps_offscreen_owned_pane_into_view+sync_sim_exact_visible_focus_preserves_layout_when_owner_unproven(shared decision rule) andexact_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 hadsampleportal.mdopen, the controller/supervisor recycled (#statedbgc), the hub was rebuilt from the durable.yrsprojection with zero live replicas while the plugin-owner lease survived — soeditor_attached()still reported "live".Compact Exchangethen taggedpre-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), buttry_editor_convergediscarded that signal and kept trying to converge through the phantom. It now short-circuits: whenapply_canonical_replace_if_attachedreturns disk authority (None) and the recovery authority proveslive_editors == 0 && delivery_convergedunder a held lease, it logs…_writeback transport=disk_stale_lease reason=zero_live_replica_disk_authority action=demote_phantom_editorand takes the guarded detached disk write instead of refusing.refuse_unproven_editor_delivery/try_detached_disk_writedemote the same stale lease (aCurrent{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_updateinstead of hard-failing "not registered" (phantom-editor prevention). Root cause of the phantom above: after a controller recycle the in-processhub_registryrestarts 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 shippingreplica_updates (on edits and on thepublish_live_bufferrecovery path viaensureEditorText), butrelay_replica_update_for_filerejected them withreplica {id} is not registered, leavinglive_editors == 0forever until the doc was re-opened. Since the caller already provededitor_attached()and passes the editor's stableidentity, the relay now re-registers the dropped replica (seeded from the recovered canonical) before relaying: it logscrdt_replica_reattach_on_update … recovery=reregister_dropped_replica, integrates the editor's update idempotently, and restoreslive_editorsto 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). Whenfinalizecaptured a response but failed before recording the--backlog-addmutations, the cycle stranded inresponse_capturedwithrequires_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-erroredprevious cycle is still response_captured after recovery/commit— forcing a manualmvof 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'sstate_is_openreplay guard then skips it), loggingpreflight_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 Exchangehandles a live-editor CRDT drift cleanly instead of exit-1 (#compactcrdtretry). When the operator kept editing during compaction, the CP converge was refused withrecovery=retry_crdt_mergeand compact hard-failed with the raw error.apply_compacted_documentnow converges throughconverge_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.dbself-heals on open instead of wedging every caller (#statedbgc). Live 2026-07-10:.agent-doc/state.dbhad grown to 4.3 GB with no valid SQLite header, so everyagent-doc preflighthard-failed withError: 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, butopen_state_dbpropagated 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 corruptstate.dbplus its-wal/-shmsiblings aside asstate.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 thecrash_recovery_markersgrowth 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 Docfound 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 probestmux.pane_alivefor the selected winner and every redundant candidate: when none are alive it logsroute_associated_pane_dead_auto_reclaim … recovery=fall_through_normal_routeand 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 Docsurvives a corrupt IntelliJ Local History store (#jblocalhistcrash).SubmitActionforce-saves the active document before dispatch; IntelliJ's Local History VFS storage can throw an internaljava.lang.AssertionError(AbstractRecordsTable.createNewRecord) from that save when its store is corrupt, which aborted the whole action. The forcedsaveDocumentis now wrapped: a platformThrowableis 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 onagent-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). Everyfinalize/compactCP write then CAS-refused withexpected_hash=… current_hash=… recovery=retry_crdt_mergebecause the frozen canonical could never match the disk-derivedexpected_current. That wedged the cycle intoresponse_capturedand churned preflight repair (each repair replayed the captured response and re-refused against the same stale hashes, forcing manualmv-aside recovery). The read path already resolvedauthority=diskon 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_filenow mirrors the read path: when a hub exists buthub.live_count() == 0, there is no live editor buffer to protect, so it logscrdt_cp_write_disk_authority_stale_lease … recovery=disk_authority_no_live_replicaand 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#cpcwritemissingreplicadurable-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 onsitscape.md: JBRun Agent Docon a document with no tmux pane created the pane, typedagent-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_outcomenow takes atrigger_pending_in_composerfact and returns a newStrandedTriggerResubmitoutcome;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 outcomeroute.rs/startup.rsresubmit the stranded draft once with a bare harnessEnterand re-check for a document-cycle ack: the session is kept only if the resubmit acknowledges, otherwise route records aFreshStartstartup-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#restartstderrbleed2tracks 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 acceptBlockedactors instead of hard-rejecting them (#clear-blocked-actor). ABlockedactor (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 sawoperator commandsession_clearrejected for ...: generation N is blockedand had no binary-owned path out. The project controller'shandle_operator_commandnow treatsBlockedthe same asClosedfor recovery commands:session_clear/session_interrupt_clearreset the session context andsession_restartsupersedes the blocked generation (blue/green#supkill-bg). Non-recovery commands (normal dispatch) still reject on aBlockedactor. Thesession_restartsupersede redirect log now recordsaction=supersede_blocked_actor(orsupersede_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-hourdocument_model_controller_lookup_error/controller_crdt_current_text_read_unavailabletimeout storm. The watch now records controller degradation (set whenobserve_live_editor_authority's controller RPC fails, read via the newcontroller_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-
execverecycles fixed (#supfdleak). The route-owned supervisor self-execves on binary hot-reload, andexecvedoes not runDropimpls — so every non-FD_CLOEXEChelper fd survived each recycle and accumulated as orphans until the process hitToo many open files (os error 24). Live telemetry showed supervisors holding 287–415 fds (267/dev/ptmx, 45supervisor-stderr.log, ~45 stop-signal pipe pairs) spanning days of recycles, which then surfaced as the recurring[supervisor::ipc] accept error: Too many open filesand the multi-hour controller-lookup timeout storm (the wedged supervisor could no longer accept IPC). All pty-master dups now go through a new atomicdup_cloexec(F_DUPFD_CLOEXEC): the shared inject writer's write fd (dup_write_fd), the resize handle, andAdoptedMaster::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 theSupervisorStderrRedirectsaved-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 (
#6b5hwrite-path parity). The read path already demoted zero-live relay text to disk authority (553904e8), and the--force-diskauthority gate already honoredlive_editors: 0, but the normal (non-force-disk) write gate only checkedpatches_dir.exists()— so a pure-CLI session with no live editor buffer wedged for ~8s on socket→file-IPCno_acktimeouts 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 seesCurrent { live_editors: 0, delivery_converged: true }, skips straight to the disk write — disk is authority when no editor owns the document. - JB plugin
CrdtReplicaManagerno longer busy-spins when the CP controller socket is unavailable (#crdt-drain-backoff). A zero-update drain cycle rescheduled immediately viarequestRemoteDrain(reason = "rescheduled")with no delay; against a missingcontroller.sockthis 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). Previouslysupervisor_recycle_actiongated 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 ranadmin recycleby hand. A latched wedge now escalates immediately, bypassing the boundary and cycle-open gates, and is guarded once-per-episode by arecycle_attemptedflag 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
execverecycle 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 viaChildLaunchPlan. arm_stale_ctrl_d_suppressionuntil the prompt is seen; an intentional operator Ctrl+D at a live prompt still reaches the child. -
make testcan 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 onset -e. -
Editor turn-state status now reads the Project Controller lazily projection, not sidecars. JetBrains and VS Code call the Project Controller
state_subscribeendpoint, 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 showsagent-doc: Project Controller disconnectedinstead of silently falling back. -
JetBrains claim/run recovery now preserves the editor-selected document under active editor authority races.
Claim for Tmux Panekeeps 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 failedsitscape.mdclaim cannot recall the editor to the stalehaiven.mdpane. Route-owned queued dispatch writes now retry a bounded CRDT merge on Project Controller relayrecovery=retry_crdt_mergehash 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-ioreads and writes.agent-doc/state.dbthroughtmux-router, while actor authority stays in the SQLitedocumentstable and tmux-router metadata is isolated inregistry_entries. The oldsessions.json/session-actors.jsonsupport 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-routerdefaults 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 Docnow routes through the lazily command/RPC message plane (command-plane-v1,#lzmsgpcp) by default. The project controller serves a neweditor_command_submitendpoint beside the classiceditor_route(shadow mode): it decodes anagent-doc.editor_route.v1CommandSubmit, dispatches the existing route path unchanged, and returns a foldedCommandProjectionwith progress events plus a terminal causal receipt (appliedon success,rejectedon failure). Both the JetBrains and VS Code plugins send this envelope and resolve the action only on the terminal receipt — a transport ACK oraccepted/startedprogress never completes the command. SetAGENT_DOC_COMMAND_PLANE=0to fall back to the classiceditor_routerequest. Terminal semantics, idempotency, generation guards, and reconnect projection are owned by lazily (lazily-specmessage-passing.json+lazily-rscommandmodule); agent-doc owns the payload schemas (editors/command-payloads.md).
0.34.67
-
Claim/sync now prefer the current live
agent-doctmux session before a configured project pin.Claim for Tmux Paneaccepts panes in the operator's current session without requiring a temporary.agent-doc/config.tomledit, whileroute/syncfollow the current session only when it already contains anagent-docwindow. The configuredtmux_sessionremains 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_bufferremains read-only, butsend_publish_live_buffernow 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 controllercrdt_current_textRPC 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_receiptandsend_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 Contextuses the same bounded Enter-resubmit state machine as direct-pane route dispatch. Clear already detected a visible/clearor/newdraft 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 byAGENT_DOC_DIRECT_PANE_MAX_ENTER_RESUBMITS, and logs eachsession_clear_submit_resubmitwithattemptandmax_attempts. -
Editor plugins leave debounce ownership to the CP/binary. JetBrains and VS Code
Run Agent Docno longer forceagent-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 inagent-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-iosubmodule test helper configures a local test author before committing submodule additions, so CI runners without global git identity no longer failexternal_git_dirs_for_submodule_include_nested_submodule_gitdirs.
0.34.66
- JetBrains
Compact Exchangeno longer leaves an uncommitted summary (#jb-compact-editor-buffer-flush). The editor-IPCop:replaceconvergence 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--commitselective 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 --commitnow asks the live editor to flush its buffer to disk (the samesave_documentIPC preflight uses forlive_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_authoritativestill verifies HEAD landed the compacted content and fails closed otherwise. No plugin change: thesave_documenthandler 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 inagent-doc-controller; pure git path/output interpretation lives in the newagent-doc-gitcrate; harness name normalization lives inagent-doc-harness; and ownership generation/event formatting lives inagent-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 inagent-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 inagent-doc-queue, leaving orchestration to provide marker storage and stale-file cleanup. Codex transcriptsession_meta.cwdparsing now lives inagent-doc-model-tier, leaving orchestration to handle only transcript file discovery and reads. Drain-stall continuation-pending marker construction now lives inagent-doc-turn, with orchestration retaining only marker path, clock, JSON, and file IO. Transcript-content context percentage policy now also lives inagent-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 inagent-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 inagent-doc-queue, while orchestration keeps marker path resolution, read/write/remove effects, and idle-watch delivery. Editor column split classification now lives inagent-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 inagent-doc-fs; route startup only supplies the document path, resolved cwd, and original CLI path. Supervisor reexec candidate ordering now lives inagent-doc-supervisor; orchestration only gathers current-exe/install-path facts before attempting the platformexecve. -
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 inagent-doc-prompt-contract; harness output prompt visibility now lives inagent-doc-harness; OpenCode permission prompt stdin normalization now lives inagent-doc-turn-executor-tmux; and the direct-pane retry budget parser now lives inagent-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-harnesscrate. Orchestration imports that crate directly and no longer exposes aharnessfacade module. The same cleanup removes compatibility surfaces that had no greenfield consumers: preflight JSON now emitsuser_intent_prompt_changesonly,patch:pendingand its--allow-patch-pending/AGENT_DOC_ALLOW_PATCH_PENDINGescape hatches are rejected, and tracked-work completion accepts only--donerather than the old--pending-done/--backlog-donespellings. -
Deprecated tracked-work aliases removed from active APIs. The
backlogandiceboxCLI surfaces now exposereapas the only completed-item removal command; the oldprunesubcommand is rejected. Preflight JSON now emits onlybacklog_reorderedandbacklog_gated_count, dropping the legacypending_*aliases.agent-doc-templatealso imports boundary-id helpers directly fromagent-doc-elementinstead 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-resumeinterlock 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 --alltimer, 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 callsprompt --answerfrom 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 plugin0.2.202and VS Code extension0.2.36include 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 plugin0.2.201and VS Code extension0.2.35include the editor-side changes. -
Socket
already_appliednow requires response materialization proof. The already-applied IPC path no longer savescontent_oursas 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_driftrepair 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_appliedretries 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 plugin0.2.200also publishes the same synced-buffer proof through the editor FFI when it writes ack-content. -
Socket
already_appliedcloseout 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>.mdsidecar before returningalready_appliedor deleting a stale file-IPC patch. The Rustalready_appliedbranch 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 plugin0.2.199includes 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 inagent_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 inagent_doc_work_graph::schedule. The rootauto_dagmodule 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 callsagent_doc_turn_executor::capturedirectly 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_contextnow callsagent_doc_workflow::session_cycledirectly 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 inagent_doc_document::status_projection.status_cmdremains 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 inagent_doc_workflow::session_cycle. Orchestration keeps onlyFlowEventadaptation for session-cycle events and imports the focused API directly. -
Release publish contract fix for
#suprestassoc. Supersedes the unpublished0.34.64crate attempt by depending onlazily 0.13.1, which publishes theCellTree/SemTree/TextCrdt/ reconcile API thatagent-doc-corealready used through the local path dependency. The behavior change remains therestart-supervisordocument-scoped registry lookup described in0.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 inagent_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 inagent_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 inagent_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 inagent_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 inagent_doc_controller::operator_clear. Orchestration keeps only theFlowEvent/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 inagent_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 inagent_doc_turn::closeout_guard. Orchestration keeps onlyFlowEvent/ops-log formatting and no longer owns a duplicate closeout state vocabulary. -
Agent streaming output parsing moved to
agent-doc-turn-executor. The sharedStreamChunktype, Claudestream-jsonline parser, and Codex JSONL line parser now live inagent_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 inagent_doc_template::patchback.flow::document_mutationkeeps 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 inagent_doc_template::patchback.flow::orchestration_batchkeeps 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 inagent_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 inagent_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 inagent_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 inagent_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 aflow::workflow_statemodule. -
Workflow invariant catalog moved to
agent-doc-workflow. The stable invariant ids, fact-source vocabulary, remediation actions, catalog builder, and JSON serialization now live inagent_doc_workflow::invariants. Orchestration doctor/autofix commands consume that focused catalog directly and no longer own aflow::workflow_invariantsmodule. -
Append response heading normalization moved to
agent-doc-turn. The helper that strips echoed## Assistant/ trailing## Userheadings before append writes now lives inagent_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 forworth revisiting,revisit later,follow-up needed, andfuture worknow lives inagent_doc_turn::heuristics; the write path only emits the warning when no--pending-addwas 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 callsagent_doc_queuedirectly. -
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 inagent_doc_queue::queue_responseandagent_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 inagent_doc_turn::response_text; orchestration keeps only diff inspection, ops-log emission, and rejection formatting. -
Cycle phase label policy moved to
agent-doc-turn. Canonicalpreflight_started/response_captured/write_applied/committed/abandonedrendering now lives onagent_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 inagent_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 inagent_doc_turn::exchange_tail. Session-check keeps only file/context adapters and guard message formatting. -
Closeout metadata-drift authority moved to
agent-doc-turn. TheQueueMetadataDrift/RecoveryProjectionVisibleDriftauthoritative-side classifier now lives inagent_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 inagent_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 inagent_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 callagent_doc_diffdirectly 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 afteragent: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 callagent_doc_lease::timestamp_is_freshdirectly 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_freshwrappers 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 inagent_doc_turn_executor::capability_proof. The supervisor start path gathers frontmatter/config facts and calls the focused API directly, whileagent::modkeeps 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 inagent_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 inagent_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. Thecodex resumerestart argument transformation now lives inagent_doc_turn_executor::codex_launch, including sandbox flag translation, conflicting sandbox rejection, missing-value diagnostics, and--add-dirstripping for resume.harness.rskeeps 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, Codextoken_countpercentage parsing, and the clear/no-clear diagnostic decision now live inagent_doc_model_tier::context_usage. Orchestration'scontext_pctmodule 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 Codeopusattribution 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. TheCrossSessionDecisionenum, structured reject marker, stale-session/force decision, and foreign-supervisor lease guard now live inagent_doc_controller::claim. Theclaimorchestration 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 inagent_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#pcp3aconcurrent-finalize drain retry decision now lives inagent_doc_controller::dispatchbeside the other dispatch admission helpers.route.rsstill 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 inagent_doc_controller::dispatch.route.rsmaps 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 inagent_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 inagent_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 inagent_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 inagent_doc_controller::dispatch. Route keeps only its mode flags and tracker/tmux effects. -
Direct-pane resubmit proof-line rendering moved to
agent-doc-controller. Theroute_submit_resubmitresult 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 trivialrouted_trigger_payloadwrapper 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 inagent_doc_controller::dispatch.route/dispatch.rsstill 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 inagent_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 inagent_doc_controller::dispatch. Route still applies the operator--wait-for-readyoverride and performs tmux polling, but the harness/test-mode timeout table is no longer owned byflow::routed_reopenor route-local wrappers. -
Starting-timeout recovery policy moved to
agent-doc-controller. The durablestarting_actor_timeoutreason, blocked-actor facts, and prompt-proof recovery classifier now live inagent_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 inagent_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 inagent_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 inagent_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 inagent_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 inagent_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 inagent_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 inagent_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 structuredroute_latencylog formatter now live inagent_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 inagent_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#jbrunautobugbacklog item template, symptom dedupe marker, route-submit issue marker, and dispatch proof evidence fields now live inagent_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 inagent_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 inagent_doc_controller::dispatch. Route modules pass harness/live-child/test-mode facts directly and no longer wrap the budget functions throughflow::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 inagent_doc_controller::dispatch.flow::routed_reopenis 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 inagent_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 inagent_doc_tmux_commands.sessionsremains the effect adapter that executes tmux commands, while route/start/idle-watch import the focused command policy directly instead of routing throughsessions. -
Route submit diagnostics no longer use an orchestration wrapper. Route dispatch now calls
agent_doc_tmux_commands::tmux_submit_transform_for_harnessandtmux_submit_key_for_harnessdirectly for text-submit diagnostics. The localrouted_trigger_submit_diagnosticfacade is deleted and covered by the tmux command boundary guard. -
Submit-text normalization no longer routes through orchestration. The supervisor IPC
normalize_submit_texthelper and routerouted_trigger_submit_payloadwrapper are deleted. Raw PTY, supervisor IPC, queue dispatch, route, and session-clear callers now consumeagent_doc_tmux_commands::submitted_text_without_trailing_line_endingsdirectly, 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.sessionsonly querieslist-panesand callsagent_doc_tmux::select_pane_by_positiondirectly, 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 importsagent_doc_tmux::pane_current_command_is_bare_shelldirectly 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 inagent_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 inagent_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-guardgrace window and timestamp classifier now live inagent_doc_supervisor::configbeside 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 inagent_doc_supervisor::crash_policybeside 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 inagent_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 inagent_doc_supervisor::run_loop.start/run.rskeeps 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 inagent_doc_turn::heuristicsbeside pending-capture recommendation detection. Session-check and pre-commit guards import the focused turn API directly, and boundary coverage preventsprompt_contractfrom re-owning the phrase policy. -
Reaped directive response-loss detection moved to
agent-doc-turn. The pure detector that decides whether a reapeddo #idqueue head lost its### Re:response now lives inagent_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 inagent_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 inagent_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 inagent_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-statenow live inagent_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 inagent_doc_turn::closeout_signal. Session-check and pre-commit pending checks call the focused turn API directly, and the oldsession_check::response_text_for_guardsroute 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 inagent_doc_queue::queue_command, andsession_check::response_guardscalls 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_guardskeeps 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 inagent_doc_supervisor::idle_reconcile.startand 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 inagent_doc_supervisor::selfkill. Orchestration keeps only sentinel files,/procinspection, 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 purewrite_wedgedclassifier that turns repeated active-listener IPC write refusals into supervisor recycle evidence now lives besidesupervisor_recycle_actioninagent_doc_supervisor::lifecycle.write::convergekeeps 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-queuenow exposes onlyoperator_authored_prompt_identitiesfor the operator-added prompt identity path; the unusedannotate_manual_queue_additionsshim 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.focusimportsagent_doc_tmux::decide_focus_panedirectly 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::ownershipdirectly, 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. Deletedagent-doc-orchestration/src/merge_control_state_machine.rsand 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.
preflightimportsclassify_stall,StallFacts, andStallVerdictdirectly fromagent_doc_turn::drain_stall;agent-doc-orchestration::drain_stallnow owns only the one-shot continuation marker sidecar IO. Added a boundary guard to keep the pure classifier inagent-doc-turn. -
Log timestamp helpers no longer route through
ops_log. Orchestration log writers and readers callagent_doc_log_time::{format_log_timestamp, parse_log_timestamp}directly, whileops_logkeeps 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::rpcfacades. The RPC adapter importsagent_doc_controller::dispatchprivately, and route authorization calls the focused coalesced-dispatch classifier directly. Added a boundary guard so pure dispatch admission helpers stay owned byagent-doc-controller. -
Cycle phase vocabulary no longer routes through
cycle_state. Orchestration sidecar code and CLI helpers importagent_doc_turn::CyclePhasedirectly, whilecycle_statekeeps only durable cycle sidecar persistence and transition application. Added a source-wide guard against the oldcycle_state::CyclePhasefacade 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 callproject_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
snapshotor orchestration. New focused crateagent-doc-fsowns project-root discovery and optional file reads;snapshot, orchestration, and CLI callers importagent_doc_fsdirectly. 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, andActorLastTransitionare imported directly fromagent_doc_sqlite::state_store;session_actornow 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 fromagent_doc_sqlite::state_store;project_controllerkeeps 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 importTmux,IsolatedTmux,PaneMoveOp,Registry,RegistryEntry, andRegistryLockfromtmux_routerdirectly, whilesessionskeeps 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.ResizeWatcherandquery_terminal_sizenow live in the focused process-effect crate; orchestration's supervisor module no longer owns or re-exportssupervisor::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-awarecontinuation_guidancebuilder now live beside the pure queue continuation drainability policy; preflight and session-check importagent_doc_queue::queue_continuationdirectly, 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_cmdremains only the file-IO/write adapter, and the CLI constructs filters fromagent_doc_element_reviewdirectly. 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_stagingremains the git/file adapter and callsagent_doc_diffdirectly. 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#idtarget extraction now lives inagent-doc-queue::queue_directive, backed by ordered tracked-work#idscanning inagent-doc-element-backlog. Session-check, preflight, and controller callers import the focused queue API directly instead of routing throughsession_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_signalnow owns response-heading completion detection plus explicit/plain done-signal parsing, reusing the tracked-work id scanner fromagent-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_policynow 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 oldsupervisor::stateorchestration 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 callagent_doc_supervisor::configandagent_doc_controller::recycledirectly. 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::dispatchnow owns the typed stale-queue recovery record and binary proof vocabulary, route authorization calls the focused classifier directly, andproject_controller::rpcno 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::confignow owns the auto-install retry decision and route-owned host supervisor inode staleness predicate.project_controller::rpccalls 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_linedirectly. Boundary coverage prevents the local forwarding helpers from returning.
0.34.64
#suprestassoc—restart-supervisorregistry 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 restartingsampleportal.mdcannot adopt a staleagent-doc-bugs2.mdpane/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.mdrouter points concept/vocabulary lookups atokf/index.md, while branch procedures remain in runbooks and current-state packs remain inpreflight/plan/tsiftoutput.agent-doc skill installnow 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, orsend_failed), it now recordsblocked_closeout.kind=editor_convergence_requiredon the open cycle with the source, reason, optional patch id,retry_without_disk_write, and a copyable recovery command.session-checkreports 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: focusedsession-checkregression plus the existing editorless-socket no-ACK write-path test. -
#tmuxsynccrash—repair_layoutno 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=0forcedensure_stash_window, then later syncs bounced through destructivemove-window/swap-window/join-pane/resize-windowchurn 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#tmuxsynccrashguards for passive JB supersede reruns and per-session destructive repair throttling. Coverage: pure rescue-phase regression plus an ignored live-tmux no-op regression formake 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_eventsis an append-only SQLite ledger keyed by idempotentevent_id,StateFact::FileWatchChangeObservedreduces into the document projection, and controller startup rebuilds the in-memoryStateBackboneProjectionfrom that ledger. The legacywatch.rsdebounce loop no longer callsrun_with_contextdirectly; it hands the settled event todocument_watcher::route_legacy_submit, which serializes the submit throughSessionOpKind::FileWatchwhile 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 / agoactivation to theagent:queuemarker 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 withours_open/ours_closeunconditionally, so an operator marker edit ontheirs(the live editor / disk side) was always discarded. Fix: a newreconcile_marker_operator_authoritativehelper 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 legacypostcommit_worktree_lost_committed_content/send_postcommit_editor_refreshtower (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-corruptionsource 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-pathreason=token budget forgit.rsdrops 20 → 7 accordingly (the surviving non-revertingreason=annotations are re-audited intests/test_cli.rs). Coverage replaces the auto-reconcile tests withpostcommit_worktree_observe_only_never_reverts_lost_contentandpostcommit_worktree_preserves_carry_forward_superset. Together with#qmarkerauththis closes the operator's "agent-doc deletes attributes added toagent: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 anagent:queuefree-text head- Compact exchange should commit the compacted contentmid-turn; the nextfinalizeaborted with "barecompact exchangedirective 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 withcompact 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 (-/*/+/orderedN.) via the newtrimmed_is_markdown_list_itemhelper (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." Previouslystrike_answered_free_text_queue_heads(viaanswered_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_responsesearches only the response's blockquote region), so a head answered in plain prose was never struck and the go-mode loop churned no-op#qchurncycles 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. Newhead_carries_in_progress_markerhelper detects the🚧marker (cosmetic-marker-aware via the existingstrip_priority_markers/display_queue_prompt_textpath);answered_free_text_head_node_keysnow strikes a head when it is the drain-target marker head OR the response prose-matches it (the secondary#ftstrikesignal that still strikes non-marker free-text heads answered at any position). Safety preserved: the#qstrikeexplainPhase-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_actorsonly TRANSITIONSStartingactors toClosed; nothing ever removed records alreadyClosed, so the controller actor store (documentstable) andadmin listgrew without bound (operator observed 251 deadsession=session-clearrows in one project). Newprune_dead_actors(project_controller.rs) hard-removes a record when ALL hold: state isClosed,last_transition.timestampis older thanDEAD_ACTOR_PRUNE_AFTER(1h, matching the stale-Startingwindow), 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 newdelete_actor_document_tx(state_store.rs) that removes thedocuments+actor_transitions+supervisor_leasesrows for one document_id in a single transaction. Wired intogc.rs(default-on, dry-run honored) soagent-doc gccleans 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 legacysession-actors.jsonprojection DIRECTLY from the post-delete db rather than viaemit_actor_projection/load_actor_store, because those re-runmigrate_legacy_actor_projection, which — once the prune empties thedocumentstable — 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; theagent-doc-core/agent-doc-orchestration/agent-doc-sqlite/agent-doc-markdown-astsub-crate versions and the inter-crate dependencyversion =refs stayed at 0.34.56, whichtest_manifest_uses_publishable_dependency_contractflags (it would breakcargo 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/clearsaw the stale pre-compact snapshot (33119 bytes) against the compacted visible file (19123 bytes) and (a) trippedguard_no_stale_snapshot_reset_drift's "looks like a manual cleanup" refusal, forcing a manualreset --from-current+commitrecovery, and (b) the preflightrepairprompt-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 liveturn_scope(absent after/clear). It consults the existing binary-authoredrecent_exchange_compaction_timestampmarker (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 deterministicreset --from-currentguidance (safety rail preserved).- New
line_is_binary_authored_compact_summary(agent-doc-core/src/diff.rs), mirroringline_is_binary_authored_ipc_proof_diagnostic: recognizes the binary-authored Session Summary shapes (tolerating a❯an earlier mis-classification already applied) and is wired intois_recovery_artifact_line,classify_prompt_bearing_block(a block that is entirely summary content →RecoveryArtifact, neverPromptTarget), 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#crdtauth6seam 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 checkgreen (5434 tests).
0.34.56
#midturnresumebPhase B reconciled onto main — active turn-resume on a fresh recycle boot. Grafts themidturn-recycle-resumebranch (boot_resume_action) onto current main, layered with the already-landed#suprecyclespinstalled-cycle-resolve (0.34.51) +#recycledeadlockbreak (0.34.52) it was originally cut before. Newboot_resume_action(start/decisions.rs) decides, on a fresh supervisor image: re-dispatch the genuinely-interrupted turn from the#durablerecyclecheckpoint (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 instart/run.rs; idempotency via a newrecycle_resume_consumedcycle-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 clearscycle_openfor 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 thesupervisor_recycle_actiondecision; the operator-restart reexec gate keeps plaincycle_open(it has its own#recycledeadlockpath) and the post-decisionabandon_wedged_cycle_for_recyclecalls keepcycle_openso 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 preservedopen_stalled_resolves_only_abandoned_older_turns, plus SimWorldnever_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 throughfinalize(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 FALSEINTERRUPTED: 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 logssession_check_patchback_tolerated_head_committedand 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-effortsubmodule_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 classifieroverlay_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-freefrom_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 (randomDoc::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: newop_capture::has_pending_editor_opsis 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, →OverlayAheadPreservedwith 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 queueddo [#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#syncbarriernor#crdtsvdomhad been implemented. Root cause: the free-text/id-backed classifier (queue_head_is_free_text_prompt+ the per-entryqueue_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: newtopic_resolves_to_only_id_directivesrecognizes a head composed entirely ofdo+ one or more[#id]/#idtokens (any count) as id-backed regardless of id count; it returnsNone(→ 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); fullqueue_consumesuite (55 tests) green including theqmisstrikereordered-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 dogfoodingagent-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 rungit stash/reset/checkoutto recover. The preflight layout guard (enforce_no_uncommitted_closeout_drift,preflight.rs) previously bailed on thisSnapshotDiffersFromHeadshape 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 viaeditor_buffer_preserved_head_exchange(write/converge.rs) that the flushed buffer did not drop HEAD's committedexchangeresponse, 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 (#crdtsvdomstate-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): afinalizefailed the editor-IPC proof on a stale binary (supervisor_binary_stale→missing_response_probe→recovery=retry_without_disk_write), leaving the cycle inresponse_captured. The operator/agent then ranagent-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) returnedDeferCycleOpenfor any open cycle, winning overexplicit_admin/write_wedged/reexec_failed. That#midturn-recycle-resumeinterlock 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 andDeferCycleOpenfires ~2/sec indefinitely. Fix (Part A): thecycle_opendefer no longer wins whenstale && (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-resumeand#wd40fresh-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, loggedsupervisor_recycle_wedged_cycle_abandoned … reason=restart_interrupted_task_on_fresh_binary) before theexecve/ kill+relaunch, so the fresh supervisor sees a clean slate and re-dispatches the still-unansweredagent:queuehead that drove the interrupted turn instead of inheriting an un-committable cycle that would immediately re-wedge. The#suprecyclespinstalled-cycle resolver remains the backstop. Coverage:supervisor_recycle_action_defers_while_agent_doc_cycle_openextended 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-boundaryagent-doc admin recycleor normalagent-doc session restart-supervisor <FILE>. README aligned;host_supervisor_stale_warning_message_uses_non_destructive_refreshstill pins the no---force/ no-interrupt-clearnon-destructive guidance.
0.34.51
- Supervisor recycle spin-loop on a stalled cycle fixed (
#suprecyclespin/#midturnresumebPhase B). Live production repro (alex-lee.md supervisor pid 999966):supervisor_recycle_deferred_cycle_openfired ~2/sec forever and the stale supervisor never hot-reloaded onto the installed binary. Root cause: the#midturn-recycle-resumeinterlock (supervisor_recycle_actionDeferCycleOpenarm) correctly refuses toexecve-recycle while an agent-doc cycle is open — but thecycle_openfact at the idle-watch call site (start/idle_watch.rs) was computed purely fromCycleState::is_open(), which staystrueindefinitely 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 harnessturn_boundarywith no IPC ack connection in flight and untouched pastSTALLED_CYCLE_RESOLVE_SECS(45s) is now resolved — force-closed toabandonedviamark_abandonedso the gate clears and theexecverecycle reaches its boundary — instead of deferred. A genuinely live cycle is never touched: eachpreflight → finalizephase ticksupdated_atand 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 helperCycleState::open_stalled(inflight, now_secs, deadline_secs)with unit coverageopen_stalled_resolves_only_abandoned_older_turnspinning 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): typingmodel: opus/claude_model: opusinto frontmatter during a concurrent finalize, plus intermittent 1-character deletes while typing. This is data loss, distinct from the#qbasehashmemotyping-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 calledrebuild_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'sours=response andtheirs=live buffer, so both survive with no old-content replay); only a genuinely behind overlay is rebuilt to baseline, and that discard now logsoverlay_discarded_bytes file=… overlay_len=… baseline_len=… first_diff_offset=…so the loss is never silent (newcrdt_merge_base_overlay_ahead_preservedmarker +OverlayAheadPreservedsource). New#crdtedgetests8-case matrix insnapshot::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 fromops.log: every editorDocumentEvent(JBTypingTracker.reportEditorOp, and the VS Code equivalent) calls theagent_doc_document_base_hashFFI →op_capture::current_base_hash, which rebuilt the full-document CRDT merge base (crdt_merge_base_state→CrdtDoc::from_textover 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 atlen=26650, 98,922 merges in one session acrosssampleportal,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_hashnow 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 evalcurrent_base_hash_is_memoized_until_base_changespins 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). WhenRun Agent Dochits 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 withRestart Supervisor and resume, not-ready panes showInterrupt and restart, and healthy deferred switches offerRestart Supervisor; all variants keepShow statusandCopy details. Coverage pins the liveclaude-code -> codexpaused-queue output plus the--forcenot-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:PatchWatcherran a content-bearingVirtualFile.refresh(false, false)unconditionally at the start of every apply/reconcile path (applyPatch,repositionBoundaryViaDocument,applyReconnectReread, the socketrefresh_contenthandler) before checking whether the in-memoryDocumentwas unsaved. IntelliJ'sFileDocumentManagerImplarms 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-basedhasPendingMemoryDiskConflictdeferral ran too late. All four apply-time refresh sites are now gated byshouldRefreshVfsBeforeApplyUtil(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 viasetText). The dominant remaining trigger was inPromptPoller: itsautoSaveTrackedFiles/refreshTrackedFilesloop ran a content-bearingVirtualFile.refresh(false, false)on the 1.5s poll timer regardless of any agent activity —autoSaveTrackedFileseven refreshed specifically in theisDocumentUnsavedbranch — 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 sameshouldRefreshVfsBeforeApplyUtil(documentUnsaved)predicate; for an unsaved buffer the refresh is skipped and disk divergence is detected by comparing theDocumentagainst the VFS-cached disk bytes (theBulkFileListenerstill delivers real external writes viaVFileContentChangeEvent, andmergeOrReloadreconciles via the Document API). The Rust post-commit reconcile paths (#pcwc/#pcwcdiskfree/#pcwcwarn/#pzjyingit.rs) were already IPC-first (trysend_postcommit_editor_refreshbefore anystd::fs::writewhen a listener is active), so the trigger was plugin-side. The Rust--force-diskoperator escape hatch is unaffected.RefreshBeforeApplyConflictTestpins the pure decision util plus the invariant that no apply-time refresh inPatchWatcherand no poll-timer refresh inPromptPollerremains ungated. - Multiline phantom-pin flood dedup (
#rt83qflood).dedup_free_text_headscollapses duplicate binary-injected free-text queue pins beyond their snapshot-authored multiplicity, butfree_text_dedup_keyearly-returnedNonefor 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 JBRun Agent Docharness-switch report re-injected every preflight, repeatedly blocking clean closeout with#qheadresiduesession-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 evalsdedup_free_text_heads_collapses_multiline_phantom_pin_floodanddedup_free_text_heads_preserves_distinct_multiline_pins. - OpenCode auto-loop self-drive (
#ocdrainstallPart A): the installed OpenCode skill now mandates in-turn drain self-drive — after a provenwrite --commitcloseout withqueue_continuation_required=trueandqueue_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). Thestart.rsauto-trigger thread used to log a provisionalno_prompt_after_30stimeout 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 30sAUTO_TRIGGER_TIMEOUTis now a hard deadline: on expiry without a dispatch-ready prompt the thread fails closed viarecord_session_startup_miss— recording astartup_missmarker against the owned pane and surfacing an actionablesession did not become dispatch-ready in Nsdiagnostic 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 boundedroute.rswait_for_ready/fresh_route_start_missingstartup-miss path so neither the route nor start side can hang. New unit evalauto_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-sqlitecrate versions and the root dependency pins from0.34.46to0.34.48, satisfying thetest_manifest_uses_publishable_dependency_contractexact-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). Theipc_proof_insufficientnote appended during interrupted-cycle recovery (e.g. a queue-consumesocket_ack_contentACK mismatch onlive_prompt_drift_after_preflight, or amissing_response_probepatch consumed without the response body) now opens with a### Re:response heading. The prompt-bearing diff classifier therefore treats it as a binary-authoredRecoveryArtifactinstead of a userPromptTarget, 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, neverdirect_write_fallback). New regressionipc_dogfood_note_is_recovery_artifact_not_prompt_bearingpins 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 aProjectionSummary/ 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 authoritativeDocumentStateProjectionfields and compact route/transport/proof summaries.
0.34.44
-
Package-level lazily state-projection parity is explicit (
#lzpkgwire).lazily-ktandlazily-jsnow expose pure bridge helpers for canonical document hashing,StateEventJSON, 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-bugand route-filed#jbrunautobugbacklog 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 planemits--pending-add-tohints 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 Docnow invokesagent-doc route --dispatch-only --plain-trigger --debounce 0 --wait-for-ready 120with 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 --commitnow run the stale snapshot/CRDT reset-drift guard before granular--pending-*,--review-*, or--statusmutations, 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 redacteddraft_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 existingagent_doc_state_projection/agent_doc_record_state_eventFFI 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 olderagent-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_bazandfoo__bar__bazstay 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_opkeeps logging producer-sideeditor_op_recordedmarkers and now includes byte-count detail (delete_len,insert_bytes,insert_non_ascii) without logging inserted text.merge_contents_crdt_with_opslogseditor_ops_for_base accepted=truewhen the base-keyed sidecar is actually offered to the CRDT merge, including accepted offsets and aggregate byte counts. Newagent-doc verify-op-capture <FILE> [--expect-cafe-demo]fails closed unless the target document'sops.logproves both the editor reporter and merge consumer ran; cafe-demo mode verifies the canonicalcafé 日本 😀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-checkrecords asession_check_supervisor_drain_handoffops-log marker tied to the current focused-cycle head hash when it emitsui_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#qfocsupfor the forced/clear, and includesproof=go_drain_dispatchon 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 recordsroute_dispatch_submit_recycle_settlewhen the post-recycle dispatch resumes after settle and records adispatch_start_proof ... proof=submittedmarker when the pending dispatch is proved. The targeted recycle-boundary scenario now asserts no injection while recycling, exactly one post-settledispatch_inject attempt=1, exactly one recycle-settle submit marker, exactly one dispatch-start proof, noattempt=2, and no strandedstart_session failedtext. -
JetBrains paused-queue route failures now render actionable notifications (
#qpauseux).Run Agent Docroute output withfailed_stage=queue_pausedis 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 readsagent-doc session statusto prove the current actor generation, then runsagent-doc admin queue resume --observed-generation <gen> --json. The notification and action paths leavejb_queue_paused_route_notification/jb_queue_paused_route_actionmarkers in.agent-doc/logs/ops.logfor 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 forcingagent-doc reset --from-current. The rebase is component-scoped: onlyagent: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 Summaryexchange replacements with compact archive markers as safe historical reductions, sofinalize --streamcan refresh stale pre-compact snapshot/CRDT sidecars instead of refusing and forcingagent-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). Whencontent_ours/ HEAD already contains the response but the live editor buffer holds a preservedagent:queueaddition, 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 stableAgentDoc.RestartSupervisorProcessaction ID, VS Code keeps the stableagentDoc.restartSessioncommand ID, and both continue to callagent-doc session restart-supervisorwhile 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 JetBrainsRestartAgentActionrecords a best-effortrestart_agent_menu_invoked file=<relative-path> source=jetbrains action=restart_agentline in.agent-doc/logs/ops.logbefore 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-sideagent_restart_performedmarker 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 routedRun Agent Docsubmit/proof path exhausts its bounded Enter or dispatch-start proof window, route now adds a#jbrunautobug #agent-doc-bugbacklog 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, andops.logmarker/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 byAGENT_DOC_DIRECT_PANE_MAX_ENTER_RESUBMITSand 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-textagent:queuehead 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-diskcloseout 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 withreason=ack_mismatch. Closeout now threadsforce_diskthrough pending maintenance as well, recording an attributablepending_maintenance_writeback ... transport=disk_force reason=force_diskmarker 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-checkstale-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-libsnow keeps Unixkill(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 missinglibc::killsymbol 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 addscycle_state_machine::CyclePhaseMachine, backed bylazily::ThreadSafeStateMachine, and routes the phase-changing sidecar mutators through typedCycleEventtransitions before the durable.agent-doc/state/cycles/<hash>.jsonjournal 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
#ctlrecycleUnixexecveadoption 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 asdeploynow count as answered when, and only when, exchange history contains an explicit labeled> **Queue prompt:**echo for that exact head.session-checknow interrupts if that proved-answered free-text head is still active inagent: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/loopand wroteowner=supervisor-failsafebefore 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 reportqueue_control_pausedand 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/loopowner, stalesupervisor-failsafesidecars 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 liveagent-doc-bugs2.mdqueue 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 inertFreeform, and the drainability classifier also treated prose+fence as noise under a preset, so preflight reported only the operator-verify heads andqueue_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-noisenow 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 fromagent: claudetoagent: codex, an old healthyclaude-codeactor record no longer produces a permanentbound to harness claude-code, not codexfailure. 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). Fullagent-doc sync/session doctor --repairnow runs the existingjb_cache_conflict_canceldetector 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 whileHEADstill lacks it, the sync repair path performs the same narrowgit::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 Docnow fences the full startup ready-probe from supervisor/clearinjection (#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/clearor/newwhile an editor route is still proving the recovered pane is dispatch-ready. The marker recordsreason=dispatch_only_ready_probe, set/clear ops lines, and the idle watcher logs when a persisted or orphan context-clear draft waits onreason=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 Docfailure was not a slow boot — the Codex pane was up in under two seconds, idle, and manually usable, while route still refused withunblocker=wait_for_dispatch_ready_promptbecausedispatch_only_requires_ready_probetrusted the latest opencodex_startlog 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-generationReadyactor and the normal prompt-ready barrier is satisfied (prompt_ready,dispatch_ready_prompt, oridle_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 Dockeeps a 120-second dispatch-readiness window as extra slow-start margin. The editor action invokesagent-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). Internalagent-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-sqliteandagent-doc-orchestrationare publishable crates,make version-syncverifies all internal publish-unit versions match the top-level crate and PyPI metadata, andmake publish-cratepublishestmux-router, internal crates, and the CLI in dependency order with crates.io index visibility waits and skip-existing behavior. The tracked manifests now use registryagent-kit 0.4.1andlazily 0.12.0; the live tmux-router API dependency is explicit as siblingtmux-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 inagent:exchangewith no drift outsideexchange; only then it sends a hash/length-guardedrefresh_contentmessage 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
/cleardrafts before queue triggers (#clearresubmit). The idle-watch pending-payload detector now treats context-clear slash commands separately fromagent-doc ...triggers, using the same active-composer evidence as explicitsession clear: a visible Codex/clearor OpenCode/newdraft 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 durableadmin queue pausecannot strand/clearin the input and require the operator to press Enter before the nextagent-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). Therealtime_model::broadcast_editor_changepath no longer queues peer editor convergence as component-only replacement payloads. It now computesnode_patchesfrom 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-nodenode_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-contentwrite as part of patch success for response patches andsave_document: if the editor cannot write the ACK-content sidecar (missing FFI/root, write failure, or failed document save),applyPatchreturns 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 sawno_ackwith 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-documentwrite --commitpaths now require a real### Re:response heading inpatch:exchangeor 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 consumenow tells agents the safe next operation for an id-backed head: complete/gate it through closeout, explicitly acknowledge a correction head with the newagent-doc queue consume --ack-id <id>path, or leave it queued.--ack-idstrikes 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-noisenow bulk-strikes orphan id-backed queue heads. Ado [#id]/[#id]head whose id names no openagent:backlogitem ("orphan") was already excluded fromqueue_drainable_head_count(head_is_drainable), but it had no bulk removal path:queue consumerejects id-backed heads,--done <id>is a no-op, andprune-noiseskipped anything carrying an#id. So the orphan sat at the queue head, was excluded from the drainable count, yet BLOCKED the leading-runqueue consumefrom reaching answered free-text heads behind it — the#qchurnno-op loop (the live:pushpin: [#kcb5]repro, whose backlog item had been dropped as a#6b5hduplicate). Fix:prune_noise_queue_headsnow 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 anagent:backlogcomponent 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 targetedqueue consume --id <id>escape hatch with a position-independent bulk sweep. Full suite + clippy green.
0.34.34
#qstallguardLayer 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 loggedqueue_paused_failsafe_single_owner_drainand fell through — reintroducing the exact#rt83/#qfloodper-tick (~2/sec) flood the pause exists to prevent (observed live: 3000+ ops.log lines; log-only — the downstreamturn_activeguard 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 assupervisor-failsafewhen 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/loopowner 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-cigreen. (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
#qstallguardLayer 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 spuriousqueue_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-Dispatchdecision, so a supervisor-progressed drain is correctly not classified as an in-session stall. Full suite + clippy +make tmux-cigreen.
0.34.32
#qstallguard— make non-stalling of a drainable queue a code-enforced invariant, not advisory prose. A live dogfooding stall (the binary reportedqueue_continuation_required=true/queue_drainable_head_count=1and the agent stopped anyway) exposed that the extensive "do not stall" guidance inSKILL.mdis 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 needingmake tmux-ciacross 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) andpartition_drainable_backlog_ids(backlog→queue sync) both key offloop_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 hardqueue_stall_detectedwarning +ops.logline 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, andsemantic_completion_matchare 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 acceptedadmin queue pauseis the#rt83/#qfloodflood guard; it previously made the attended in-session/loopthe 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 normalturn_active/ route-in-flight / cooldown-guarded drain decision — one dispatch per turn, never the 2/sec flood). Logsqueue_paused_failsafe_single_owner_drain. - New unit tests:
focused_cycle_tag_is_loop_undrainable_but_clean_session_is_not, thedrain_stallsuite (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-cigreen. End-to-end SimWorld coverage of the pause-failsafe + the operator live two-pane verification are tracked as a[focused-cycle]follow-up.
- Layer A — drainability is a typed attribute, never inferred. New
0.34.31
#orchver— the stale-binary warning no longer lies "launched as 0.1.0".supervisor_stale_warning_message(and thecontent_ours_adoption_refused_stale_supervisorops.log lines it feeds) stamped the controller/supervisor version fromControllerBinaryIdentity.version, which was recorded viaenv!("CARGO_PKG_VERSION")inside theagent-doc-orchestrationcrate. That internal workspace crate is pinned at0.1.0and never bumped in lockstep with the top-levelagent-docbinary (now0.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 therecorded != currentdecision). Observed dogfooding onsampleorders.md(sample-app), whose long-livedagent-doc start --route-ownedsupervisor genuinely needed a recycle after acargo installbut reported the bogus0.1.0. Fix: the binary crate injects its realCARGO_PKG_VERSIONonce atmain()startup viaproject_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 testsidentity_version_prefers_injected_binary_version+identity_version_falls_back_to_crate_versioncover both paths. The underlying staleness detection is unchanged — a genuinely stale supervisor is still flagged; the warning now names the true installed version soagent-doc admin recycleguidance 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 andcontent_oursoutsideexchange— trippinglive_prompt_drift_after_preflighton every cycle, which forced thecontent_ourscarry-forward + a recoveryagent-doc commit. That is the recurring race observed dogfooding this session (and a contributor to the#editorbufwin/#docdriftgrace/#hap7family). Fix: after queue maintenance,realign_baseline_to_converged_queuesplices the converged queue component into the pre-maintenance disk content and re-saves the baseline — socontent_oursmatches 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 testqueue_convergence_realigns_baseline_so_finalize_sees_no_false_driftproves the false drift before / clean after / and that a real concurrent user prompt still drifts. Full suite (4837) + clippy green.
0.34.29
#kcb5Phase 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 serveas the sole daemon) wedges every finalize: the controller hosts the editor-IPC socket even with no plugin attached, sois_listener_activereturns 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-disksucceeds. 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}inagent-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-diskcase routes to disk. Unit truth-table coverage + aeditorless_cli_sim_force_disk_but_live_editor_fail_closedSimWorld 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 nextsave_documentthe stale buffer would overwrite HEAD — the#postcommit-ipc-worktree-corruptiondirection. The plugin now reconciles on IPC (re)connect: inPatchWatcher.registerRoot, after the socket listener starts, it walks every open.mdsession document under that root and asks the new binary FFIagent_doc_reconnect_buffer_decision(root, file, buffer)whether the buffer is stale. The decision is owned by the binary (puredecide_reconnect_bufferinagent_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 cleanHEAD(ffi_show_head). Otherwise it keeps the buffer, so genuine unsynced user edits are never clobbered (editor wins, per#editorbufwin). Areread_diskdecision carries the disk content; the plugin applies it viaapplyReconnectReread(re-checks the live editor generation,setText+saveDocumentto clear the dirty flag). Emits areconnect_buffer_decision decision=... #yzermarker toops.logfor live verification. New tests:decide_reconnect_bufferunit coverage (in_sync / reread / keep) plus thereconnect_buffer_sim_rereads_stale_then_keeps_user_editsSimWorld scenario (re-read a prior-commit buffer, keep an offline-edited buffer). Binary half (reset --from-currentconverge 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-currentresume-clear now routes through the listener-guarded converge seam (#evmh/#cyh0). The defaultreset --from-current(without--preserve-session) clears theresumefrontmatter pointer and rewrote the session document with a bare unguardedstd::fs::write. When a live JB editor listener held the document open, that disk write diverged the editor buffer from disk and raised aFile Cache Conflict— one of the recovery-path FCC triggers diagnosed in the agent-doc-bugs2 dogfooding session (the others —apply_compacted_documentvia#w42v, the post-commit worktree reconcile, andwrite.rsatomic_write— were already listener-guarded or are the intentional--force-disk/IPC-unavailable fallback). The resume-clear write now goes throughagent_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 headlessresetis byte-identical.reset --from-current --preserve-sessionalready only rebuilt sidecars and never touched the document, so it was never a trigger. New regression testreset::tests::from_current_routes_resume_clear_through_converge_seamasserts the resume-clear write is source-labelled inops.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#evmhremains 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-keyedsemantic_mergealready prevents the operator-reported queue-prompt duplication (a concurrent operator queue edit can no longer duplicate/reverse/drop adjacent structure — see regression testsqdup_operator_queue_add_during_exchange_turn_no_duplicationandqdup_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 (anOperatorDeletedAgentEditedNodeoutcome), 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 theexchangeactive area, so a queue/backlog deletion ack could be silently dropped. Nowsemantic_mergerecords each such fact as anexchange_notesentry and injects a one-line blockquote note into the mergedagent:exchangecomponent (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 noexchangecomponent exists. The in-editor document remains the source of truth — the agent change is never merged back. New unit tests insemantic_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 submitfollowed 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), sofree_text_head_answered_by_responsealways 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 newfree_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 testcode_fenced_free_text_head_strikes_on_prose_lead_match; full suite + clippy green.
0.34.24
- Stale-binary recycle-yield: a self-draining
/loopnow yields one boundary so the supervisor can hot-reload onto a freshly-installed binary (#wd40/#staleloop-recycle-restart). A continuously self-draining Claude Code/loopholds a fresh drain-owner lease AND keeps the harnessturn_activeback-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 recurringcontent_oursfinalize drift +#rt83phantom-pin flood seen when dogfooding across a mid-sessioncargo install. Previously the operator had to manuallymake 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 bareDetect, 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_SECSoverride). While that request is live,queue_continuation::detect,preflight, andsession-checkdropqueue_continuation_requiredand surfaceRECYCLE_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 theexecverecycle fire on its own, and the fresh (no-longer-stale) supervisor clears the request so the drain resumes on the new binary. Mid-turnexecvestays out of scope — the yield is exactly what produces a clean boundary without a mid-write swap. New modulerecycle_yield.rs(request producer/reader/clear + pure freshness predicate) mirrors thedrain_ownersidecar layout; pure policydecisions::stale_drain_recycle_yield_requestedis unit-tested via truth table; the supervisor's own idle-watch drain useslive_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 inAGENTS.md.
0.34.23
- Route trigger injections now emit a
dispatch_inject attempt=Nops.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, JBRun Agent Doctyped theagent-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 (#jbtsiftnosubcold-start) and 0.34.22 (#runexitrestartrestart-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 fromops.logwhether a given dispatch re-typed. This adds adispatch_inject file=… pane=… harness=… transport=<direct_pane|supervisor_ipc> attempt=Nmarker at both real injection funnels (send_command_once_uncheckeddirect-pane text+Enter,dispatch_via_supervisor_ipc_with_modeIPC inject) keyed off a process-global monotonic counter. A healthy dispatch logsattempt=1exactly once; a multi-inject regression (or a legitimate but visiblenot_dispatchedfull-trigger resend) showsattempt=2,attempt=3, … making the stacking class directly auditable. The route process is short-lived (one logical dispatch peragent-doc routeinvocation), 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_sendnow also asserts thedispatch_injectmarker is absent across all 7 not-ready restart ticks (dispatch_injects == 0) and present exactly once asattempt=1afterPromoteStartingPromptReady— neverattempt=2(a newdispatch_injectscoverage 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, JBRun Agent Doc, then checkops.logshows a singledispatch_inject attempt=1) and thecargo install/admin recycledeploy 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 Docno-submit-duplicate race (#runexitrestart). Operator-reported (2026-06-20): after RESTARTING an agent-doc session, then JBRun Agent Doconsampleportal.md, theagent-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#jbtsiftnosubfixed in 0.34.21: the route auto-start path (route::startup) and both existing-pane route paths (dispatch_only_send_reopenrequires_ready_probe,ensure_existing_pane_ready_for_dispatch) already gate dispatch behindwait_for_agent_ready_outcome/ready_prompt_candidate(the strongis_dispatch_ready_prompt_linepredicate), but the supervisor idle-watch drain loop (idle_watch.rs, the one looping site that can re-type each tick) gates onidle_queue_prompt_visible, which off theactor_state == Readyfast path falls back to the weakchild_output_prompt_visible→matches_prompt. On a fresh restart the actor isStarting, the edge-triggered ptyterminal_screenbuffer can render a prompt glyph (matchingmatches_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#qflood2pre-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_visiblenow, when the actor is NOT yetReadybut the weak pty-buffer signal is positive, re-verifies against a fresh tmux capture of the owned pane via the newsupervisor_pane_dispatch_readyhelper (mirroringsupervisor_pane_has_busy_cue's live-capture pattern) using the same canonicalroute::ready_prompt_candidate/is_dispatch_ready_prompt_linepredicate 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. Theactor_state == Readyfast path and the OpenCode/Codex idle-chrome paths are unchanged;idle_queue_prompt_visiblehas 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(newDispatchIdleQueueDrainAfterRestartcommand +drain_into_restarting_pane_blockscoverage) restarts a ready session toStarting, then asserts the idle-watch drain fails closed on all 7 ticks (recordsdispatch_into_restarting_pane,route_dispatch_acceptances == 0,go_drain_dispatches == 0— no duplicate triggers), then dispatches exactly once afterPromoteStartingPromptReady. Full suite + clippy green. Live-verify (operator): the two-pane restart repro (restart a session, JBRun Agent Doc) and thecargo install/admin recycledeploy 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 (
#editorbufwinFix A). (1) Compaction overlay-CRDT staleness (compact.rs): a CRDT-modecompact --commitno longer leaves the overlay CRDT carrying the PRE-compaction (large) markdown. The early template-branchsave_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 trippedguard_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 (freshCrdtDoc::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 byqueue_journal::replay_missingat the nextstartand resurface over the current queue. (3) Queue-consume head divergence (write/queue_consume.rs,#editorbufwinFix 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 — logqueue_consume_head_divergence_reconciled reason=live_buffer_addition_authoritativeand 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 (theipc_live_prompt_drift_content_ours_ignores_unproven_live_queue_deletionsinvariant 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): thecargo install/admin recycledeploy and the live zero-drift proof against a running route-owned supervisor remain operator-gated.
Unreleased
-
Busy
session clearnow queues one deferred clear and dedupes repeats (#p6a0). A non-interruptingagent-doc session clearagainst 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/clearinto 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 chooseRecycleImmediate, and oncesupervisor_binary_stale_self_recycledmaps the installed inode, the stale-supervisorcontent_oursrefusal 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 JetBrainsRun Agent Doc: a binary-owned auto continuation could add/clearplusagent-doc tasks/...while the operator was still typing inagent:queue, racing the manual JB route that submits the absolute-pathagent-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, logsidle_queue_watch_skipped ... reason=editor_typing_active, and the reset/drain decision policy now has explicitSkipEditorTypingoutcomes. Coverage: pure reset/drain decision tests plusidle_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 JBRun 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 throughAGENT_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; seesupervisor_recycle_action). A long in-session Claude Code/loopdrain holds a fresh drain-owner lease AND keeps the harnessturn_activeback-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 thecontent_oursfinalize drift +#rt83phantom-pin flood, since#supselfhealalready notes a stale binary does not self-heal by lease expiry; it needed a manualmake install+admin recycle+ end-turn). Fix: whenidle_watchdetects 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, newrecycle_yieldmodule, default 120s TTL,AGENT_DOC_RECYCLE_YIELD_TTL_SECSoverride). The attended in-session loop reads it at its next inter-item boundary —queue_continuation::detectreturns no continuation,session-checkprintsqueue_recycle_yield=true+ the newRECYCLE_YIELD_GUIDANCE, and preflight dropsqueue_continuation_requiredwith the same guidance — and yields one boundary instead of re-triggering. The resulting idle turn lets theexecverecycle fire on its own; the fresh (no-longer-stale) supervisor clears the request and the drain resumes on the new binary (the loop mayagent-doc drain-claim <FILE> --releaseto hand back immediately rather than wait for the lease TTL). The supervisor's OWN idle-watch drain useslive_drainable_continuation_head(notdetect), so it is unaffected and resumes the drain after recycling. A bareDetect(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. Newops.log/session-log markersupervisor_recycle_yield_requested(reason=stale_binary_drain action=signal_loop_yield) proves the request live. Mid-turnexecvestays 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 decisionstale_drain_recycle_yield_requested(unit-tested truth table) +recycle_yieldmodule unit tests +detect_yields_when_supervisor_requests_recycle_yieldintegration test; full suite + clippy green. Live-supervisor-critical (operator): the two-panemake tmux-ciself-recycle repro and thecargo installdeploy 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 Doccold-start race (#jbtsiftnosub). Operator-reported (2026-06-19): JBRun Agent Docauto-started a fresh supervisor/tmux pane, typed theagent-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 astartingactor toreadyonly 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: afterwait_for_agent_readyproved a (possibly transient) dispatch-ready prompt while the Claude TUI was still coming up, the path went straight todispatch_routed_reopenwith 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 newreverify_auto_start_dispatch_readybounded-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 whenready_prompt_candidatestill 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 logsdispatch_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 asdispatch_into_shell(the issue-A signature). The newauto_start_dispatch_ready_blockhelper classifies the pane state (StartingPanevsDeadShell) 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(newDispatchAutoStartRoutePromptcommand +auto_start_starting_pane_blockscoverage) asserts that an auto-start dispatch into a still-Startingpane fails closed and recordsdispatch_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 (JBRun Agent Docauto-starting a fresh pane) and thecargo installdeploy are operator-gated. -
Per-node semantic merge now preserves free-text / fenced queue heads, fixing the persistent
live_prompt_drifteditor-IPC convergence failure (#qdup-freetext). Root cause of the recurring degraded session where every write failsack_mismatch/live_prompt_drift_after_preflight(557 consecutive blocked events observed onagent-doc-bugs2.md, zerolive_prompt_drift_semantic_mergedsuccesses): the node-keyedsemantic_mergereconstructs a non-exchangecomponent 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, sooverlay::componentsnever parses it as anItem.merge_components_into_bodythen emitted only the bullet items and dropped every other inner line, so the merge silently lost the free-text head, tripped its owndropped_queue_prompt_lines_after_content_oursanti-data-loss gate, declined the merge on every cycle, and left every IPC write stuck on the blockedcontent_ourscarry-forward path — surfacing to the operator as permanent buffer corruption / queue churn that "only an IDE file reload clears." Fix: a newmerge_nonexchange_innerpreserves 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 forexchangeheading-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) andsmconv_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.rsguard_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.logto verify reported issues (e.g. correlating the#tsiftmdcrashSIGTERM to wall-clock time). Newagent_doc_core::log_timemodule formats epochs asYYYY-MM-DDTHH:MM:SSZand 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/tmpwrite-dedup / sync debug logs. Crucially,parse_log_timestampis 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 thegate_verifyops.log scanner (incl. thes760_clear_decision_clear_trueverifier, which compares marker times toset_at). The helper lives inagent-doc-coreso the coregate_verifyscanner and the orchestration writers share one implementation. Tests:log_timeknown-vector + epoch/ISO round-trip (incl. leap day) + garbage-rejection, and theops.logintegration 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 intsift-v0.1.log: the managed OpenCode session on pane%78was killed twice withopencode_exit code=143(143 = 128+15 = SIGTERM), each time at the same instant asopencode_capability_proof status=failed attempts=3("opencode child network probe timed out after 45s"). The capability-proof thread'sGiveUpbranch calledshared.kill_child()(start.rs:1844), SIGTERM-ing the live interactive harness the operator was actively using because a separate backgroundopencode runnetwork-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: theFailedgate already blocks all prompt dispatch viacapability_dispatch_blocker, so no unsafe work can be auto-dispatched even with the child alive. Fix: theGiveUpbranch no longer kills the child — it keeps the gateFailed(dispatch disabled), marks the actorBlocked, 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 theFailedgate 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_preservedin the session log and noexit_code=143kill. -
Closeout now reaps dead-PID live-buffer sidecars, not just patch files (
#lbreap). The#sqdriftstorm only happened because closed-IntelliJ orphan live-buffer sidecars accumulated unbounded (this doc had 187).#fccreapreaped dead-pid patch files at closeout but never the live-buffer sidecars, and#sqdriftreaps a dead peer only when a broadcast happens to touch it. Newreap_stale_jetbrains_live_buffersruns in the post-commit closeout (besidereap_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-finalizelive_prompt_drift_after_preflight+postcommit_worktree_check match=falsedegraded session:realtime_model::broadcast_editor_changebuilt its peer set from everylive-buffersidecar 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: 247realtime_broadcast_queuedevents in a few minutes fanning out to ~22 deadjetbrains-<pid>consumers (all pids dead, no live IntelliJ), each (a) re-creating the dead-pid patch file the#fccreapreaper 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_changenow 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 viaclear_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. Logsrealtime_broadcast_skipped reason=dead_origin_editorandrealtime_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-racePhase 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>.jsonlease (short self-healing TTL, default 15s; mirrors thedrain-ownerlease) is held for the wholeagent-doc queue prune-noise/agent-doc queue consumeread-modify-write via an RAIIQueueEditGuard(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_maintenancereturns early without mutating (logsqueue_maintenance_deferred reason=queue_edit_lease holder_pid=<pid>), and the supervisor idle-queue-watch skips the dispatch tick (logsidle_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. Newqueue_edit_ownermodule with the lease primitive, freshness predicate, foreign-holder detection (different-pid + fresh + live), and RAII guard. Tests: 5queue_edit_ownerunit 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.rsreason=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-gatedmake install→admin recycle→ liveprune-noiseno-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 (
#jbrundispatchdirective 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_acceptancetreated 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 asAccepted, and nothing re-dispatched. Fix: a newnot_dispatchedoutcome — 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, reusingis_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_uncheckednow re-sends the full trigger (text+Enter, not a bare Enter — there's no draft to submit) up todirect_pane_max_enter_resubmits()times until it lands, loggingroute_redispatch_not_landed, and reports a genuineTimedOut(not a falseAccepted) if the budget exhausts. Tests:pane_idle_dispatch_ready_distinguishes_non_dispatch_from_fast_submit; dispatch suite (130) +make checkgreen. 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 JBRun Agent Docops 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 fixedDIRECT_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 viaAGENT_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_visibleupdated to the tunable cap; full dispatch suite (129) +make checkgreen. 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-awarededup_live_promptswas never wired in (preservingdo [#id]duplicates is the deliberate#queue-dedup-destroys-intentional-duplicatesinvariant). So when the backlog→queue mirror / CRDT replay re-emitted a bare[#id]reference head (the pure mirror form, nodo), nothing collapsed it. Newqueue::dedup_bare_id_reference_headsruns inrun_queue_maintenanceafter the node-key dedup: it collapses duplicate bare[#id]/#idreference heads (pin markers stripped) to the first occurrence, while deliberately leavingdo [#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; thepreflight_preserves_intentional_duplicate_tracked_queue_promptinvariant still green; full queue (481) + maintenance (67) suites +make checkgreen. Backlog:#sqedit-race(the queue-edit-race hazard this is one facet of). -
queue prune-noisenow 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-noiseenumerated heads via themarkdown_astitem_nodesoverlay, which recognizes ONLY bulleted (- …) lines and skips fenced code, so operator-pasted:round_pushpin:console dumps — surfaced byqueue::parseas multiline----fencedPromptheads or, for a bare```console paste, as a run of preservedFreeformlines — were invisible to the strike path and accumulated on disk forever (the editor convergence then faithfully re-pushed them). Fix: (1) newqueue::parse_spansis the single byte-range-aware source of queue-head segmentation, andprune_noise_queue_headsexcises multiline noisePromptblocks AND pasted-evidenceFreeformlines (newqueue::is_noise_freeform_line, which preserves---/~~~separators andre [#id]references) by exact range, alongside the existing bulleted node-key strike; (2) the drainability classifieris_drainable_queue_head_with_contextnow 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, thequeue_stale_noise_linescounter, andqueue prune-noiseagree; a single-linedo [#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:boundaryleft) 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 checkgreen. 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#pcwcdiskfreelistener-active path no longer claims editor-IPC reconciliation beforerefresh_contentactually acks. If the editor refresh no-acks/errors, post-commit cleanup falls back to the authoritativeHEADdisk write and logstransport=disk_after_failed_editor_refresh, preventing the corrupted working tree from re-seeding the nextlive_prompt_driftcycle. Separately, controller-paused queues now surfacequeue_pause_reasonand pause-awarequeue_continuation_guidance, soqueue_paused: truebesidequeue_continuation_required: trueis 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, andrun_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#fullboundaryis cited in prose inside the#ftstrikeagent:doneentry ("behind do[#fullboundary]").collect_agent_done_ids_with_rootscanned the whole done-component/archive text viaextract_pending_ids_from_text, harvesting every bracketed id anywhere. Newagent-doc-coreextract_done_item_own_idscollects 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 inlineagent:donecomponent and the externalarchive=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 checkgreen. 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 unfinisheddo [#id]head (e.g. behinddo [#fullboundary]) was never struck even after the response addressed it. New closeout passstrike_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 viafree_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-headqueue_consumption_alloweddecision, strikes document + snapshot in sync (consume_queue_nodes_by_key), best-effort. Mirrorsstrike_done_queue_head_promptsfor 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 checkgreen. 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 thecontent_ours/live_prompt_drift/postcommit_worktree_check match=false/inflight=5send_faileddrift family — the drain lease#kp5zserializes dispatch ownership but NOT editor convergence). New pureconvergence_gatemodule:ConvergenceFacts(committed, editor_converged, inflight==0, actor_idle, elapsed/timeout) +convergence_gate_decision→Dispatch/Defer { unmet }/ForceDiskFallback { unmet }(no I/O, fully unit-tested). Newconvergence_playbackmodule:ConvergencePlaybackartifact 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-levelconvergence_gate_force_disk_fallback severity=error … playback=<path>ops-log line viarecord_force_disk_fallback. 16 unit tests;make checkgreen. 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-diskwrite + playback on a bounded timeout) needs a focused clean cycle withmake check+make tmux-ciacross 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). Changingagent:in frontmatter (e.g. claude→opencode) is default-on restartable through theagent_doc_agent_change_restartknob (envAGENT_DOC_AGENT_CHANGE_RESTART> frontmatter > project config > default ON; resolverresolve_agent_change_restart/agent_change_restart_enabled). The supervisor idle-queue watch re-resolves the harness from current frontmatter each tick, logsharness_change_detected, gates withagent_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 withagent_restart_performed. Route/startup now refuses to cold-replace a healthy live authoritative actor solely because the document'sagent:changed, loggingroute_authoritative_actor_harness_mismatch_deferredand 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 Docnow starts even on a paused queue (#qpauserun). Operator-reported: JBRun Agent Doc"did not start. It should have started" — the controller dispatch RPC blocked it withfailed_stage=queue_paused. Apausedqueue 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 whosecommand_kindis an explicit operator reopen (managed_reopen/dispatch_only_reopen, classified bydispatch_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 untiladmin 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 anagent:queueitem, 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 everycommit_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/#pcwccarry-forward invariant guards (fullmake checkgreen, 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 pausenow suppresses the unattended supervisor auto-injection on ago-mode queue, without stalling the attended in-session loop (#qpausego). An acceptedagent-doc admin queue pause <FILE>records a durable controllerqueue_controlsrow that the controller dispatch RPC already honored (failed_stage=queue_paused), but the supervisor idle-queue watch injectsagent-doc <FILE>triggers straight into the pane — bypassing the dispatch RPC — so ago-mode auto-queue kept re-dispatching after an accepted pause (the unattended flood). The idle-watch now consults the new best-effort, read-onlyqueue_continuation::document_queue_controller_paused(file)(resolves project root + canonical document id, reads the effectivequeue_controlsstate from.agent-doc/state.db; returnsfalse— 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.preflightsurfaces a newqueue_paused: boolfor visibility. The pause deliberately does NOT dropqueue_continuation_required/queue_drainable_head_countand does NOT short-circuitqueue_continuation::detect: the attended in-session/loopis the legitimate single-owner drain and keeps working real queue backlog (stalling it on a pause strands genuine drainable items —queue: stopfrontmatter /--- stopfences are the in-session stop control).admin queue resume(stateresumed) clears the flag;drain(draining) is notpaused. 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 inactiveagent:queuehead 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_contentnow 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 (returnsNone, logsroute_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 throughqueue_continuation::live_continuation_headand 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. FlowCoreroute.rsreason=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. NewSimWorld::converge_semantic_mergeruns the productionsemantic_merge_scoped(the sameexchange-active scoping the real#smconvtry_semantic_merge_convergenceapplies) and foursemmerge_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 (#smturnactivegating), 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-nodestrikepatches (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#smconvcontent_ours convergence wheresemantic_mergetreatsstruckas a node-disjoint flag-edit. Addedsmqstrike_struck_head_survives_concurrent_operator_queue_addproving 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 loggedsemantic_merge_ack_pendingtoops.logfor 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 eachrequires_ackAckRequestto cycle_state viarecord_semantic_merge_acks;start_preflightcarries un-surfaced acks forward exactly one cycle (driven by asurfacedflag, not a millisecond-collidable cycle-id compare) into the newCycleState.pending_semantic_merge_acks, and preflight surfaces them assemantic_merge_acksplus a companionsemantic_merge_ack_pendingwarning 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 stableAckReason::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_mergeack 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 asemantic_merge_ack_pendingfor every same-node collision — including the common case where the operator edits the queue / a backlog item while the agent writes its response (the firsthandqueue: stop+ cross-head drift).agent-doc-markdown-ast::semantic_mergenow takes a first-class turn-active node-set: newActiveNodes(whole-component viaactive_componentor node-granular viawith_node) +semantic_merge_scoped(base, ours, theirs, &active). The merged document and per-node outcomes are identical tosemantic_merge(the operator still wins every same-node conflict — no content is ever lost or changed); onlyrequires_ackis filtered: a conflict whose node is OUTSIDE the active area auto-resolves silently, while an in-area collision still raises itsAckRequest. The convergence caller (write/ipc.rs::try_semantic_merge_convergence) marks theexchangecomponent active (the turn-active area is the exchange tail), so operator drift in queue/backlog/frontmatter that collides with an agent edit no longer emitssemantic_merge_ack_pendingnoise; only an exchange-response-area collision does.semantic_mergeis 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 idjetbrains-<pid>-<uuid>(TypingTracker.kt), watches.agent-doc/patches/, and applies +saveDocuments the same untargeted (broadcast) patch — N live writers racing into the File Cache Conflict / cross-buffer drift family. Newagent-doc-orchestration::plugin_owneradds a per-document single-owner lease (.agent-doc/plugin-owner/<hash>.json, mirrorsdrain_owner) claimed atomically viacreate_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 explicitrelease_plugin_owneron dispose hands it over immediately without waiting out the 30s TTL (AGENT_DOC_PLUGIN_OWNER_TTL_SECSoverride). Exposed via FFIagent_doc_plugin_owner_try_acquire/agent_doc_plugin_owner_release;PatchWatcher.processPatchFilegates 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_ownerunit 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 (
#supselfhealPhases 2–5,#supselfheal-rest). Completes the wedge plan on top of the Phase 1explicit_adminoverride.supervisor_recycle_actionnow takes two more typed evidence inputs:write_wedged(Ph2,#supselfheal-wedgetrigger) andreexec_failed(Ph3,#supselfheal-reexecescalate), and gains a newEscalateKillRelaunchaction. (1) Wedge trigger: the write/converge closeout derives a typedwrite_wedgedfact from repeatedsend_failed/no_ackagainst a nominally-active JB listener (the#fcc0ede-wedge latch) —write_wedged_from_ipc_failures+ the supervisor-facingeditor_ipc_write_wedgedreader — and logswrite_wedged_supervisor_recycle_requestedinstead 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 stayDetect, and never waits for an idle boundary that may never come). (2) Re-exec escalation: when the in-placeexecverecycle cannot start (deleted-inodeENOENTfrom a freshmake install, or another syscall error), the policy returnsEscalateKillRelaunchand the idle watch escalates to a bounded (MAX_REEXEC_ESCALATIONS) kill+relaunch of the harness child — reusing the#supkill-bgdrain-and-relaunch path — instead of loopingcontinue_current_binaryforever. (3) Guidance: the SKILL.md auto-loop note andrunbooks/commit.mdno 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_actiongains anexplicit_adminoverride soagent-doc admin recyclecan recycle a stale-binary supervisor (#supselfhealPhase 1 policy core,#supselfheal-adminrecycle). The route-owned supervisor recycle policy (start/decisions.rs::supervisor_recycle_action) previously returnedDetect(surface only, keep running the stale binary) whenever auto-recycle was opted OUT — so an explicitadmin recycle, the gentle fix the closeout path itself recommends, had no policy path to actually clear a stale supervisor. The predicate now takesexplicit_admin: an operator/agentadmin recyclerequest overrides the default-OFF opt-out and returnsRecycleImmediatefor a stale supervisor at the next turn boundary, while still respectingturn_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 liveadmin recycle→ route-owned-supervisor IPC adapter that flips this input totrue(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 passfalseuntil it lands, so current behavior is unchanged. -
parse_close_markeraccepts the standard<!-- /agent:name -->close-marker spelling soComponent.end_bytespans the full component (#gszq/#mdastclose). Inagent-doc-markdown-astoverlay.rs,parse_close_markeronly 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 returnedNone, 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), butComponent.end_bytepointed just past the open marker instead of through the close line, so any consumer slicing a component by itsstart_byte..end_bytebyte span got a truncated range.parse_close_markernow accepts both spellings (/agent:nameandagent:/name);end_bytespans 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 idjetbrains-<pid>-<uuid>(TypingTracker.kt), and per-instance patch files<doc_hash>.jetbrains-<pid>-<uuid>.jsonland 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_commitnow best-effort reaps them (next to the existingreap_local_model_leasescloseout reap):reap_stale_jetbrains_consumers(project_root)scans the patches dir, parses the pid from eachjetbrains-<pid>-<uuid>.json, and removes only files whose pid is provably dead — Unixkill(pid,0)whereESRCH⇒ dead/reap andEPERM⇒ alive/keep, never the current pid, never a non-jetbrains-file (base<hash>.json/.vscodevariants 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/#semmergePhase 2). Root-cause fix for thelive_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 manualgit checkout HEAD/reset --from-currentrecovery dance.write/ipc.rs::guard_ipc_snapshot_adoption_against_live_prompt_driftnow tries a node-keyedagent_doc_markdown_ast::semantic_merge::semantic_merge(base, candidate, content_ours)BEFORE the existing#fintol2line-merge /content_oursfail-close: when the operator and agent edited disjoint nodes (the common case — operator flipsqueue: 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_oursempty + every new### Re:heading preserved) — otherwise it falls through to today's recorded-evidencecontent_ourspath unchanged (fail-closed only when the merge can't be represented). Logslive_prompt_drift_semantic_merged;requires_ackoutcomes (same-node operator-wins, operator-deleted-an-agent-node) are applied (operator-wins already encoded inmerged_doc) and loggedsemantic_merge_ack_pendingpending 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.rsguard_12→16 test-call,reason=17→18). -
semantic_mergemodels### Re:heading-prose exchange turns as append-only nodes (#semmerge-ownerheading-prose extension). The shipped Phase-1semantic_mergekeyed 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#smconvdecline for every real session).semantic_mergenow splits theexchangecomponent 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 — asAppliedAgentAddoutcomes. Append-only by construction (no in-place prose merge).overlay.rsis unchanged; the heading split is local tosemantic_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 (#qheadsyncorphan-shadow).pending::detect_shadow_open_itemsexcluded tracked-work andexchangecomponents from its scan but NOTagent:queue, so a queue head referencing an id absent from the live backlog — a reaped id's lingering[#id]: notehead (e.g.[#jbacceptwedge]: 0.2.170 installed), or any#mirrorall-mirroreddo [#id]whose item is reaped — was misclassified as an "open backlog item that exists only outside the live backlog" and madeenforce_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 manualEdit+reset --from-current+commitdance after--donethis 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 likeexchange/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#qheadsyncqueue-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." Previouslyrun_queue_maintenancenarrowed the sync source to the drainable subset (partition_drainable_backlog_ids), so[operator-verify]items were skipped from the queue entirely (the#goqueuestall/#qcontdrainanti-thrash rule). They are now mirrored into the queue asdo [#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: …**). Cruciallybacklog_idsis NOT narrowed:head_is_drainablestill defers operator-verify ids viadeferred_backlog_ids, soqueue_drainable_head_countcontinues 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/#733rwas) 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 assertsdrainable_head_count == 1); existingpartition_drainable_backlog_ids_skips_only_operator_verify(the pure partition is unchanged) and the queue-sync suite stay green. -
session-checkself-clears the#queue-user-edit-overwritewedge by id (#qheadsync). Operator: "You should automate the wedge clear." Observed onagent-doc-bugs2.md: after an IPCcontent_oursmerge,session-checkstayedINTERRUPTEDindefinitely on dropped queue edits[#qpauseux]/[#docdriftgrace]even though both were preserved in HEAD as struck~~do [#id]~~heads from a prior cycle. Noresetvariant (--preserve-session, bare,--force-disk) cleared it because the only escape was a manual supervisor restart / patch surgery. Root cause:check_dropped_queue_prompt_guardproved preservation only by (a) normalized text identity — which cannot bridge the recorded bare[#id]form against HEAD'sdo [#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/visibleagent:queuein any form, including struck/consumed lines: newqueue_ids_including_struckstrips the strike wrapper before id extraction (committed_queue_head_idsdeliberately 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 staledropped_queue_promptsmarker 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; existingsession_check_fails_closed_on_dropped_queue_editproves 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: JBFile Cache Conflictstill occurring. Please replace all direct file writes on the hot path." Root cause:emit_postcommit_worktree_checkranstd::fs::write(file, &head_doc)unconditionally wheneverpostcommit_worktree_lost_committed_contentreturned true, then calledsend_postcommit_editor_refreshto push HEAD content through the editor IPC. The disk write was the recurringFile Cache Conflictsource — 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 letssend_postcommit_editor_refreshcarry HEAD content back throughrefresh_content— the editor buffer becomes authoritative and the disk catches up on the IDE's next save (loggedpostcommit_worktree_auto_reconciled ... transport=editor_ipc_skipped_disk_write). With no listener (headless / CI), it still writes HEAD to disk authoritatively (loggedtransport=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. Thestart_fake_listenertest helper now modelsrefresh_contentcorrectly (applies the message'scontentfield 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; existingpostcommit_worktree_auto_reconcile_refreshes_live_editor_bufferandpostcommit_worktree_check_logs_match_false_for_real_corruptioncontinue to pass. FlowCorereason=token budget bumped 11 → 13 intests/test_cli.rs. -
Closeout now auto-reaps crashed-session GPU leases (
#kgleasereap).tsift kg extract's cooperative GPU lease (#kgleasewire) previously needed a manualtsift local-model lease reap --unload-emptyto 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 existingtsift-memorycloseout-capture seam: it spawnstsift local-model lease reap --unload-empty --jsonunder the resolved project root only when a.tsift/gpu-lease.jsonregistry is present, and every failure mode (no project root, no registry,tsiftnot 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#kgrefleaseonly 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. Purereap_command_argsbuilder (default--unload-empty, optional--lease-file/--hostoverrides) 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: everystd::fs::write/atomic_writecall site that targets the session document is audited against an active JB editor IPC listener and either routed through the#fcc0converge gate or documented as editor-safe. Two normal-path gaps were routed throughconverge_or_disk_write: theagent-doc write --statuscomponent 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 (noFile 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#pcwcpost-commit worktree reconcile (HEAD-authoritative repair of a tree that lost committed content, followed by an editor-buffer IPC refresh). Recovery/scaffold/migration writes (claimscaffold,repairorphan recovery, preflight migration/repair,session-checkover-application remedy,resetresume-clear) stay authoritative must-hit-disk by design — they restore correctness when the editor/IPC path may itself be wedged.(HEAD)heading annotations andagent:boundarymarkers are deliberately out of scope (the working tree/editor preserve them). Coverage:set_writes_status_to_disk_without_listener;strip_guard_markersroutes through the existingconverge_or_disk_writegate tests. -
Backlog→queue sync honors
not-before=YYYY-MM-DDscheduling 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 anot-before=YYYY-MM-DDtoken is held out of thequeue-attribute sync while the current UTC date is before that threshold (pending::active_item_ids,active_item_priorities, andactive_enqueue_item_idsall exclude it — an explicit:inbox_tray://enqueuemarker 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 strictYYYY-MM-DD; malformed values are ignored. Day math uses a proleptic-Gregoriandays_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 IntelliJFile Cache Conflictdialog still fired during go-mode queue drains. Root cause:run_queue_maintenancepersisted its mutations (queue body sync, opening-tag activation-token strip,queue:frontmatter state) with four unconditionalstd::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 singlepersist_queue_maintenance_docgate: with a JB editor IPC listener active it converges the queue shape through the editor (converge_live_buffer_queue_shape→ pluginsetText+saveDocument, no external-modification dialog) and skips the disk write, recordingwrite_authority action=routed surface=queue_maintenanceinops.log; with no listener it writes to disk exactly as before (byte-identical non-IDE behavior). This brings queue maintenance to the same#fcc0converge-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-listenerrun_queue_maintenance_go_mode_appends_fresh_backlog_into_nondrained_queueproves the disk-write path is unchanged without a listener. -
Route ready-prompt barrier now accepts supervisor-proven
idle_pane_reconciletransitions (#monster60stimeout). A JBRun Agent Docafter a session-close/reopen could wait the full 60s route timeout even though the actor was alreadyreadywithruntime_state=ready supervisor_health=healthy, because the edge-triggered pty redraw missed re-emitting a prompt shapeready_prompt_candidaterecognized and the fallback only matchedprompt_ready/dispatch_ready_promptreasons. The supervisor's idle-watch recordsidle_pane_reconcileonly aftersupervisor_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-addcloseout now populates active go-mode backlog queues (#pendingaddqueuesync).finalize/write --commitapply pending-add mutations after preflight queue maintenance, so a captured follow-up could land inagent:backlog priority queuewithout a matching active queue head until another preflight happened to repair it. Closeout now appends ids recorded incycle_state.pending_added_idsinto active go/start queues whose backlog carries a recognizedqueueattribute, 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
/clearor recycle. Claude now participates in the same managed-TUI stderr redirection policy as Codex/OpenCode in bothagent-doc startandagent-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.mdtrigger against the visible composer text literally. If Codex was already showing the equivalent relative draftagent-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% usesuffix. A JetBrainsRun Session Contextreroute after clear could still spend the full startup wait and fail aslatest run is still bootingwhen Codex rendered onlygpt-5.5 xhigh · ... · Context 0% useinstead of the previously-coveredContext N% usedform. 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_promptandidle_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_handoffwhenagent_doc_queue_context_reset/agent_doc_clear_thresholdrequired 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 Doccould spend the full startup wait on Codex panes whose bottom line was onlygpt-... · ... · Context N% used, then fail aslatest run is still bootingeven 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_promptandready_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 showingagent-doc tasks/sampleorders.mdreceives the submit key instead ofagent-doc /abs/.../tasks/sampleorders.mdbeing 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_reasonnow treatsWaiting for background terminal (... esc to interrupt)asactive codex turnevidence, 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 foragent-doc start, so supervisoreprintln!diagnostics are redirected to.agent-doc/logs/supervisor-stderr.loginstead 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, baselineagent:queueprompts missing from that candidate are now preserved in the agent-ownedcontent_ourssnapshot instead of being removed as if the user deleted them. The live-prompt-drift branch logsqueue_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, andfintol_queue_deletion_is_not_forward_merged_with_outside_edit. -
VS Code Run/Clear actions now match the JetBrains per-document command contract.
Run Agent Docclicks dedupe behind the first alive plugin-spawned route process,Clear Session Contextcancels a still-dispatching route process before invokingagent-doc session clear <FILE>, andRun Agent Docclicks during an active clear queue until the clear and any selected clear-refusal recovery action finish. Addededitors/vscode/SPEC.md, refreshed the VS Code README/session-command docs, and bumped the local VS Code package to0.2.28. Coverage:editorCommandState.test.tsplus 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, andcanceling 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_writeand 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, andtry_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
/clearproof 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-doclogssession_clear_submit_verification_skipped ... reason=no_supervisor_actor_paneinstead of treating a default-tmux capture failure as proof that/clearwas 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
Entersubmit-key retry. If Codex later reaches accepted-without-dispatch-start proof and the same routed prompt is visibly drafted, route sends one lateEnterretry, logsroute_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 intoagent-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 exacttmux_text_enter/key=Enterproof inops.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-diskremains the operator-controlled direct-write escape hatch. -
--force-diskcloseout 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 threadsforce_diskthrough 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 Dochead sendsagent-doc <FILE>with no/clearwhen 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+Enterdelivery is successful and logsproof=accepted proof_scope=accepted_onlywhen no stronger proof appears. Codex hook proof and OpenCode pane-state proof still upgrade telemetry todispatch_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+Entersubmit merely because dispatch-start proof did not arrive inside the proof window; they complete asproof=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 normalagent-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_refreshplus 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_pausedcontrols fromstale 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/\nstripped 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 toraw_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_linesandrepair_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 aliveRun Agent Docroute process on a repeated click; the first route process owns the submit/proof wait and later clicks are deduped with a durableroute_already_in_flight/route_process_already_in_flightattempt 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:EditorCommandStateMachineTestandstarting a route while one is alive keeps the first submitter. -
Supervisor context clears now survive JetBrains/Codex restart races (
#jbclearrestart). Idle-queue owned/clearsubmits now write a short-lived per-documentcontext-clear-in-flightmarker. 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 sameCLEAR_COOLDOWN_RESUME_IDLE_TICKSfresh-idle debounce used by the in-memory gate, so repeatedRun Agent Docrestarts no longer stack/clearor 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, andcontext_clear_marker_blocks_until_settled_idle_prompt. -
JB
Run Agent Docnow fences route submit from idle-queue/clearinjection (#jbrouteinflight). Route dispatch writes a short-lived per-documentroute-in-flightmarker while the editor-triggeredagent-doc <FILE>submit is awaiting acceptance/proof. The supervisor idle-queue watcher treats that marker as a first-class reset/drain skip reason, logsidle_queue_watch_skipped ... reason=route_submit_in_flight, and does not advance clear-settle debounce counters from the pre-submit composer. This prevents the observedagent-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 focusedroute_submit/idle_queuesuites. -
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 sharetmux send-keys -t <pane> <text> Enter, and empty-text re-submit sends onlyEnter. Route, idle-queue, and session-clear one-shot resubmits use the same profile key and logaction=submit_key key=Enter. The idle context-reset watcher also latches a clear-in-flight across in-place queue head edits, preventing a fresh/clearfrom 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.
TmuxSubmitProfileis now a single policy surface: it always emitstmux_text_enter, andEnteris the diagnostic submit-key label. -
Workflow invariants now have an autofix planner (
#wfinvautofix). Addedagent-doc autofix <FILE>to consume the doctor report andworkflow-invariant-catalog-v1, plan invariant-keyed remediations, recordworkflow_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). Addedagent-doc doctor <FILE>(aliasdiagnose) to evaluateworkflow-invariant-catalog-v1against 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 reportsok,recoverable,operator, orblockedwith 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). Addedagent_doc_workflow::invariantswith 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 asworkflow-invariant-catalog-v1so doctor/autofix work can evaluate data instead of scraping prose. -
Route/write UI outcomes now use a typed vocabulary (
#archuistates). Added theui-outcome-v1user-facing outcome contract with stable tokens forqueued_behind_owner,recovered_and_retried,deferred_for_operator_proof,no_drainable_work,real_component_conflict, andblocked_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::ALLnow 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 JBRun Agent Docprompt-context queuing during an open closeout. -
Closeout recovery mutations share one primitive (
#smrecoverymutate).flow::closeout::apply_closeout_recovery_mutationnow owns replay-baseline refresh, sidecar rebuild/reset-from-visible, restore-from-HEAD, and stale-capture retirement mechanics.capture::validate_replay,repairstale-capture retirement, and metadata recovery now route through that primitive and logcloseout_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 carryCloseoutRecoveryDecisionthrough the route boundary instead of surfacing raw capture or snapshot blocker strings. Existing active queue heads wait behind the unresolved closeout with a typedcloseout 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_evidencenow 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_recoverynow 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, andrecovery_evidence_reports_superseded_capture_heading. -
Closeout recovery now has a typed action decision boundary (
#smcloseoutdecision).flow::closeout::CloseoutRecoveryDecisionmaps the existing recovery classifier intoAlreadyCommitted,ReplaySafe,RetireStaleCapture,ResetSidecarsFromVisible,QueuePromptForAfterCloseout, orBlockedoutcomes, 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_outcomesplus the route closeout-block decision tests. -
JB
Run Agent Docrecovers legacy stale-supervisor queue pauses (#jbrestale). Route now treats a markerlessfailed_stage=queue_paused reason=#qchurn ... stale host supervisor pid<N> ...dispatch error from an old route-owned supervisor the same as the newersupervisor_restart_redirectbail: 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/clearoragent-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 reachesresponse_captured, the operator edits onlyagent:queue, and the response has not yet crossed write/commit,validate_replayno longer deadlocks route/JB Run Agent Doc behindcaptured 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 containingsrc/agent-docas eligible for build/install. Agent-doc sessions undertasks/agent-doc/, legacy agent-doc task docs, and docs inside the agent-doc source checkout can still auto-install; sibling project sessions such astasks/professional/sampleportal.mdandtasks/software/lazily-rs.mdnow resolve no auto-install crate root even ifAGENT_DOC_SUPERVISOR_AUTO_INSTALLor 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 statusandagent-doc admin inspect --jsoninclude afreshnessobject with installed binary identity, installed/running inode comparisons, stale/unknown/fresh classification, and operator guidance; plainadmin inspectprints a compactfreshness=controller:<state>,supervisor:<state>summary. Coverage:controller_process_freshness_classifies_inode_identity,controller_status_reports_startup_binary_identity, andcontroller_queue_control_rejects_stale_generation_and_blocks_dispatch_when_paused. -
JB
Run Agent Docno longer silently retries Codex latest-run boot timeouts. A dispatch-onlylatest 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 requestedbetween_turn_enqueue deduped=N kept=/clear,/agent-doc result=deliveredproof 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. Thetsift-memory 0.1.70dependency 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 forinstruction-files,notify,portable-pty,pulldown-cmark,rusqlite,sha2,signal-hook,similar,toml,ureq,yrs, andzip, with API migrations forureq 3response/body handling,sha2 0.11digest formatting, andportable-pty 0.9'sMasterPty::tty_name. Therusqlite 0.40move is kept link-safe by aligning the first-partytsift-memorypath dependency, and the oldgeneric-array 0.14.7chain is removed by moving first-partytagpathtosha2 0.11. Verification:cargo check -p agent-doc, affected-crate cargo tests, and fullmake check. -
JB Clear Session Context now retries drafted Codex/Claude
/clearcommands 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 logssession_clear_submit_observation ... issue=prompt_not_submitted, sends one bare profile submit key, and recordssession_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/cleartranscript 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 whenagent_doc_finalizedurably captures a response and the transport closes before the write/commit boundary. The next realagent-doc preflightmust replay the capture, create exactly one closeout commit, clear the pending response, and leavesession-checkgreen 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, orcurrent_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 syncnow explains active backlog ids that were skipped because an existing queue prompt already represents the same#id(for exampleadvance [#id], not only canonicaldo [#id]), and reports newly materialized ids. The CLI regression covers the sampleordersqueue: start+agent:queue ... priority go+agent:backlog priority queueshape, commits the synced queue, and provessession-checkstays 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-backeddo [#id]item, it logsqueue_consume_refused_id_backed_head_without_explicit_signaland leaves the item runnable unless an explicit--done/--pending-gate/--pending-editid 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 anops.logproof marker, and SimWorld now models a JBRun Agent Doccycle reaching the recycle boundary: success records a preserved-pane fresh-binary proof, while failedexecvemaps directly to#recyclerestart-verify/#aazp/#4mydinstead 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#idin the document or done archive. Git-history-only prompts are reported separately as review context, and a zero-candidate report emits explicituser_removal_or_completion_proofevidence, so foreign-owned documents such astasks/software/lazily-rs.mdcan 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-paneSync Tmux Layoutpath 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 previousPrior summary/contextpayload. 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
/clearalready 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, logsidle_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 withagent-doc <FILE>drafted but the queue stopped until the operator pressed submit. Coverage:pending_payload_enter_resubmit_is_scoped_and_one_shot, existingidle_queuefiltered tests. -
Realtime multi-editor broadcast now has headless targeted-delivery proof (
#rtwbcast-simproof).SimEditornow 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 throughmulti_editor_crdt_broadcast_converges_without_file_cache_conflict. The productioncompute_broadcastseam 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/clearit sends before dispatch now emitsidle_queue_watch_context_resetto.agent-doc/logs/ops.logwith the document, harness, target, head hash, and#cleandrainsupreason. The existingidle_queue_watch_drainmarker 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 foragent-doc compact <FILE> --component exchange --commitwhereHEADcontains finalized### Re:response history, the compacted exchange converges through the editor-IPC component patch path, and the compact closeout commits without tripping the committed-historicaltyped_component_driftguard. 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-*.jsonlfor the document project, reads the latesttoken_countevent, and computes ctx% fromlast_token_usageplusmodel_context_window. Whenagent_doc_queue_context_resetis enabled and that pct crossesagent_doc_clear_threshold, the hook logs[s760] ... pct=N clear=true, records atranscript context ... >= clear threshold ...reason, and hands the queue continuation to the supervisor viacodex_fresh_context_handoffinstead of continuing inside the already-loaded pane. Missing/unreadable Codex token counts still fail safe withpct=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 controllerqueue_pausedreason likeadvance-review preset head is spent ... Operator can clear the '- #advance-review' lineis 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
/clearto 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, emitscodex_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 Docno longer stacks repeated Codex reopen text when the same trigger is already drafted. Before direct-pane dispatch appendsagent-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. JetBrainsRun Agent Docand 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_oursadoption now refuses known-stale supervisors and repairs proven duplicate singleton component blocks (#dupcontent2). The IPC adoption guards forlive_prompt_drift_after_preflightandprompt_duplication_in_ack_contentnow fail closed whenstale_supervisor_warning_for_docclassifies the serving controller/supervisor assupervisor_binary_stale, leaving the candidate snapshot in place and loggingcontent_ours_adoption_refused_stale_supervisorplus anipc_proof_insufficient invariant=supervisor_binary_stalebreadcrumb. 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 andipc_snapshot_dedupes_duplicate_singleton_component_from_before_content. -
Preset-bearing go queues no longer misclassify genuine prompt lines as stale noise (
#goqnoise).queue_continuationnow treats a queue markerpreset="..."as supplying the directive verb for each non-empty, non-fenced free-textPromptline, so noun-phrase feature requests and terse lines likedeployremain drainable instead of producingqueue_continuation_required=false queue_stale_noise_lines=N. Fenced console/evidence pastes still self-defer as noise even under a preset queue, anddeployis now recognized as a standalone directive verb for non-preset queues. Coverage exercisesdetect,live_drainable_continuation_head,drainable_head_count, andqueue_stale_noise_lineson 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 aPreparingone (#stuckhandoff2M1b — fixes the root IPC-drift cause). Live-repro 2026-06-15: a wedgedcontroller serve … --handoff-state preparingorphan 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_handoffflips a handoff replacement straight tohandoff_state = Stable+ clearshandoff_started_atthe instant the client asks —ControllerHandoffState::Promotedis parsed/serialized but NEVER written as a production transition. A client that dies AFTERpromote_handoffbut BEFOREstd::fs::rename(temp_sock → public_sock)(handoff_stale_controller) leaves aStable-in-memory controller stranded on itscontroller-handoff-*temp socket. M1'scontroller_self_watchdog_should_suicideonly fires onPreparing/Promoted, so it could not see theStableorphan —stale_preparing_controller_self_reapedhad ZERO occurrences ever, and only the slow/proc-cmdline gc/M5 sweep reaped these at 7–21 min. FIX (M1b, structural,rpc.rs): newcontroller_handoff_replacement_is_stranded(handoff_temp_socket, launched_elapsed, threshold)keyed off the launch socket, not in-memory state — a replacement launched on a tempcontroller-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 ofhandoff_state. Wired into the serve-loopWouldBlockbranch asshould_suicide || is_stranded.controller_self_watchdog_suicidehardened with a generation-ownership guard so a stale stranded generation never clobbers a newer clean controller's shared on-disk record toFailed(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 manualpkill/git checkoutrecovery. Coverage: 6 new deterministic unit tests inproject_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-installnow AUTO-recycles running controllers onto the freshly-installed binary instead of only printing the hint (#autorecycle-on-install, upgrades#ctlrecycleR4 from print-only to action). Closes the recurringsupervisor_binary_stale/#fcc0/#no-mid-session-installpain: after alib-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 PRINTEDrun \agent-doc admin recycle --all-projects`; the operator (and dogfood loop) still had to run it by hand.lib_install::run_pathsnow callsrecycle_controllers_all_projects()directly after a successful install, so every running controller is marked to recycle at its next idle boundary (the same idle-gatedrecycleRPCadmin recycle --all-projectssends — 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 falseyAGENT_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 — andsession-check's#queue-clear-unrun-itemsguard had to catch it and force a manual un-strike +reset --from-current. ROOT CAUSE:repair::first_queue_head_is_free_text(the guard gatingstrike_recovered_free_text_queue_head→consume_queue_prompt_force_disk, which strikes the leading head by POSITION) used a brittledo [#/do #prefix test. That test only recognized thedo-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 authoritativewrite::queue_head_is_free_text_promptclassifier (which already resolves#id,[#id],do [#id], pinned, and preset spellings correctly viatopic_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-#idhead still strikes; inactive queue strikes nothing) + extendedwrite::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 toagent:reviewas a gated[/]item and the drain KEEPS advancing the remaining phases/queue; only done/blocked terminate the drain. Codifies theSKILL.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 openagent:reviewitem relative to the pre-commit HEAD (a phase routed to review rather than completed/blocked), the binary emitsdrain_continue_after_review file=… next_head=… (#mphaseloop)toops.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 helperqueue_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 socketsave_documentpath only. The binary's post-commit carry-forward flush (flush_editor_buffer_to_clear_driftingit.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.signalfile (JSON{ file, patch_id }) when no socket listener is active or the send returns no ack, mirroring the existingvcs-refresh.signalchannel. The VS Code extension'sPatchWatcherwatches that signal, flushes the matching editor buffer to disk viaTextDocument.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'ssaveDocumentViaDocument. Proof line:postcommit_editor_save_flushed … transport=socket|file_signal(or…_skippedon failure). Spans the binary (git.rsfile-signal fallback) + the VS Code extension (extension.tssave-document.signalwatcher + new puresaveSignal.tshelpers). Coverage:postcommit_carry_forward_superset_writes_file_signal_without_socket_listener(Rust),saveSignal.test.tsparseSaveDocumentSignal/ackContentSidecarPath(TS). FlowCoregit.rsreason=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." Whenfinalize/writedetectslive_prompt_drift_after_preflight(the IntelliJ editor buffer holds unsaved edits ahead of disk,content_ours_len > candidate_len), the prior behavior adoptedcontent_oursas 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-socketsocket_ack_contentpath, once the drift guard adoptscontent_oursthe binary sends a newsave_documentIPC request to the live plugin (ipc_socket::send_save_document); the plugin runsFileDocumentManager.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) viaupgrade_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_sidecarwhen 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 (orchestrationsend_save_document+ reconcile, shipped viacargo build --release && agent-doc lib-install) + the JB plugin (save_documenthandler inPatchWatcher.handleSocketMessageV2→saveDocumentViaDocument, plugin0.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 showslive_prompt_drift_resolved_via_editor_saveand 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
/loopnow drains[clean-session]heads in place —queue_continuation_requiredstays 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 theDrainScope::Loopclean-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. Nowqueue_continuation::deferred_backlog_ids_with_ipc_scopedand 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/loopdrains it in the current session rather than stalling. The supervisor still force-/clears before its own dispatches (head_requires_clean_session), and the#freshgrantshort-TTL grant +_live_ipc/DrainScopeplumbing 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 reportsqueue_continuation_required: true(the/loopdrains them) instead offalse. -
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."#cleandrainsupmade 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 inDrainScope::Loop, which still deferred[clean-session]under a live editor-IPC listener (deferred_backlog_ids_with_ipc_scoped) — soqueue_continuation_requiredcame backfalseand the fresh agent declined the very head it was cleared for, churning a no-op (#qchurn). Now: when the idle-watch sends the#cleandrainsup/clearfor 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.logclean_session_fresh_context_grant … (#freshgrant)). TheDrainScope::Loopdeferral consultsactive_clean_session_grant_idsand 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 fireidle_queue_watch_context_reset … (#cleandrainsup)+clean_session_fresh_context_grant … (#freshgrant), and confirm the re-dispatched agent's preflight reportsqueue_continuation_required: truefor that head (drains it) instead offalse. -
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 manualcargo 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, emittingsupervisor_auto_install_started→supervisor_auto_install_succeeded, after which the existing#supselfhealstaleness check firessupervisor_binary_stale_self_recycledand the session continues on the new build. The install rung precedes the#ctlrecyclerecycle rung at the idle boundary. Dogfood-only — it never fires for non-dogfooding documents. Opt out with a falseyAGENT_DOC_SUPERVISOR_AUTO_INSTALLenv /agent_doc_supervisor_auto_installfrontmatter /.agent-doc/config.tomlknob. 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_idsdeferred a head whenoperator_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'sactive_headwasNone— nobody ever drained them, even though SKILL.md promised "the supervisor owns the/clearand re-dispatches the next item to a freshly-/cleared agent." The drainability filter is now scoped (DrainScope::LoopvsDrainScope::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-/clearing before each dispatch (head_requires_clean_session→clean_session_head_forces_context_reset, independent of theagent_doc_queue_context_resetopt-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 onqueue_continuation_required == falsefor 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 acargo installonly loggedsupervisor_binary_stale_detectedand kept re-filing File Cache Conflict / IPC-drift dialogs (#fcc0/#ipcdrift) until an operator manually ranrestart-supervisor(which itself failsgeneration N is closedwhen the wedged controller can't cooperate),interrupt-clear --force, orkill <pid> && agent-doc start. The turn-boundary blue/greenexecveself-recycle machinery already existed (#ctlrecycleR3 /#supkill-bg) but was gated behind the opt-inAGENT_DOC_SUPERVISOR_AUTO_RECYCLE/ frontmatter / project knob.resolve_supervisor_auto_recyclenow 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 failedexecvedisables further attempts for the process lifetime (#suprecyclestall, neverprocess::exit). Opt out with a falseyAGENT_DOC_SUPERVISOR_AUTO_RECYCLEenv / frontmatter /.agent-doc/config.tomlagent_doc_supervisor_auto_recycle = false. Bootstrap caveat: a supervisor ALREADY running the pre-#supselfhealbinary cannot self-heal retroactively (old code lacks the default-on logic) — restart it once onto a#supselfhealbuild, then it is hands-off. Coverage:stale_supervisor_self_recycles_at_turn_boundary_by_default,opted_out_document_clears_and_drains_without_auto_recycle, updatedresolve_supervisor_auto_recycle_precedencedefault-on assertions. -
start --route-ownedno longer wedges on a pane-move against an up-to-date controller actor (#startgencas). Root-fixes the recurring liveError: project controller command \start_session` failed: controller failed to start session actorwall.start/run.rscomputed 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 viainfer_latest_generation. The controller'sstart_sessionCAS unconditionally expectsstart_generation - 1as the prior generation, andinfer_latest_generationreturnsmax(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, sostartnow always takes thenext_generation(infer + 1) path and logs theownership_transition, keeping the value handed to the controller aligned with the CAS contract regardless of registry-pane staleness. The fix lives in thestartprocess (which computes the generation it sends), so it takes effect on the nextagent-doc startwithout 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). -
startno longer wedges forever once the session log runs ahead of the committed actor generation (#startgenlogdrift). Follow-up to#startgencas— the samecontroller failed to start session actorwall, but a self-sustaining variant.start/run.rswrites thesession_start/ownership_transitionlog 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_generationreturnedmax(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, everystartfailing). The committed controller actor record is the sole CAS authority, soinfer_latest_generationnow 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 nextstartreads 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'sexecvehot-reload onto a freshly-installed binary (#ctlrecycleR3 /#suprecyclequeue) was opt-in only via theAGENT_DOC_SUPERVISOR_AUTO_RECYCLEenv var and the project-configagent_doc_supervisor_auto_recycle. A per-document frontmatteragent_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_recycletakes the new frontmatter arg andsupervisor_auto_recycle_enabledreads the doc's frontmatter; spec (specs/06-config.md) and the project-config doc comment list the full precedence. Coverage: extendedresolve_supervisor_auto_recycle_precedence. -
A benign in-flight dispatch coalesce reports deduped-success instead of exit-1 (
#qflood2). WhenJB 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 asproject 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 stablefailed_stage=coalesced_in_flightmarker,authorize_controller_dispatchclassifies it across the IPC boundary into a typedRouteDispatchAuthorization::CoalescedDedupedoutcome, and every route dispatch site returns the already-running dispatch pane viaroute_dispatch_deduped_pane**without re-sending** (loggedroute_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, extendedqflood_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 aftercargo installuntil manually restarted" churn (the#ctlstalebindispatch reject was only a per-dispatch backstop). Each long-lived process now compares its launch identity against the installed binary (current_binary_identitystats 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 withcontroller_self_recycled reason=stale_binaryonce no dispatch is in flight for any document (has_any_open_in_flight_dispatch) and it isStable; the nextconnect_or_launchrelaunches 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 (arecycleRPC, idle-gated so it never interrupts a turn) — deterministic for the release flow (cargo install && agent-doc admin recycle --all-projects). R3 — thestart --route-ownedsupervisor self-recycles from its idle-queue watch when idle + stale, opt-in behindAGENT_DOC_SUPERVISOR_AUTO_RECYCLE(it ends the live agent child, so default OFF logssupervisor_binary_stale_detectedinstead); chose cleanprocess_exit-and-relaunch (supervisor_binary_stale_self_recycled via=process_exit) over re-exec for safety. R4 —agent-doc lib-installprints 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 recordedcontroller_binaryno longer matches the installed binary keeps serving OLD code.connect_or_launchalready hands cross-process callers off to a fresh controller (the commoncargo installrecycle), but a dispatch that still reached the stale controller'shandle_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 acargo install).handle_dispatchnow refuses such a dispatch with acontroller_binary_stalerejection receipt +dispatch_refused_stale_binaryops.log line, andauthorize_dispatchretries the dispatch exactly once so the retry'sconnect_or_launchpromotes 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-processstart --route-ownedsupervisor'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-ASTnode_patchesstrike instead of a broad legacyqueuecomponent 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_fencedandPatchGenerationFenceTestnormalized-hash cases. -
Finalize-tolerance + post-commit-repair groundwork; behavior-flip rungs held for operator confirmation (
#fintol/#pcwc/#rtwbcast). Three phases fromplan-clean-exchange-run.mdlanded 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-exchangecheck plus a conflict-free 3-way merge that preserves both sides) whether a concurrent user edit is disjoint from the response target. Unit coveragewrite::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-exchangeedit by carrying it forward UNCOMMITTED (the finalize succeeds) — a shipped invariant asserted byfinalize_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/#fintol3are 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_checkrestores the file to HEAD and sends a guardedrefresh_contentsocket 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 newreason=logs.#rtwbcastOption C —realtime_model::compute_broadcast(base, originator, peer) -> BroadcastMerge { merged, originator_echo_suppressed }, the pure MERGE-ONLY multi-editor convergence seam (no delivery). The SimWorldmulti_editor_crdt_broadcast_converges_without_file_cache_conflicttest now drives this production seam instead of an inlinemerge_contents_crdt. Coveragerealtime_model::tests::compute_broadcast_*. Full multi-editor delivery (Options A/B — aneditor_idFFI 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-filecommon ancestor the finalize merge uses) from the on-disk.mdfile onto the structured document model. On by default; opt out withAGENT_DOC_MPS=0(false/no/off) for the pure.mdpath. Defaulting on is non-regressing by construction while the.mdremains the cross-check cache: the model projection is used only when it is byte-identical to the legacy.md, and the proven.mdwins on any divergence. Rung 2 (pin):preflight'ssave_baseline_contentalso persists the baseline as an overlay sidecar (baselines/<hash>.overlay.yrs) viasnapshot::save_baseline_model, loggingmps_baseline_pin. Rung 3 (flip):write::read_explicit_baselinesources the base by projecting that overlay (snapshot::load_baseline_model), loggingmps_baseline_resolve source=model|md_backstop|md_fallbackand, on disagreement, a loudmps_baseline_divergence(with first-differing byte) while preferring the.mdbackstop. Rung 4 (derive): the.mdbaseline 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 + emitsmps_baseline_pin, finalize resolvessource=model diverged=false, response merges and commits;AGENT_DOC_MPS=offwrites 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.mdsnapshot onto the structured document model (tasks/agent-doc/plan-model-projected-snapshot.md, successor to the superseded#snbc). Addssnapshot::overlay_projection_is_byte_stable— a pure check that round-trips content through the sameOverlayCrdtDoc::from_markdown → encode_state → decode_state → to_markdownpipeline the merge base (crdt_merge_base_state) uses — and an env-gated shadow probe wired at the centralsnapshot::savefunnel (AGENT_DOC_MPS_PROJECTION_PROBE=1) that emits a grep-ablemps_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). AddedSimEditortosrc/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 productionrealtime_model::resolve_current_docseam, 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_commitis the deterministic#rtwverifyproof (unsaved buffer wins, emits therealtime_doc_resolve authority=editor_bufferops.log marker, edit survives commit) plussimeditor_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 surfacedCacheConflictsignal). Slice 3 —multi_editor_crdt_broadcast_converges_without_file_cache_conflictdrives two emulated editors throughmerge::merge_contents_crdt(#rtwbcast). Slice 4 —integrated_editor_edit_routes_drains_under_drain_owner_gate_and_broadcasts_backconnects the editor seam to the route/controller model and the publicdrain_ownerlease (#kp5z). New production primitivedebounce::clear_live_buffermodels the editor-close lifecycle (clears the sidecar so the cycle falls back to disk); coverageclear_live_buffer_removes_sidecar_and_is_idempotent. Spec:specs/12-deterministic-simulation.md(#swintsection). -
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 viaatomic_write_if_current_pub, which diverged from an open JetBrains buffer and raised aFile Cache Conflict. It now callswrite::try_compact_editor_convergefirst: when a JB/VS Code IPC listener is active it sends componentop:replacepatches for the changed components (mirroring the#q7jmlive_prompt_drift convergence — neverfullContent) and verifies the editor ack matches the compacted target, loggingcompact_writeback transport=editor_ipc. The guarded direct write is now only the no-listener fallback, loggingcompact_writeback transport=disk_fallback reason=no_listener(orreason=listener_degradedonly while no listener is accepting connections); active-listener no-delta/no-ack/mismatch/send failures logtransport=blockedand fail closed. Spec updated in07-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 logsqueue_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-invokesdispatchwhile the pane is mid-turn, pinning the source before a busy-gate/dedupe fix. -
JB
Run Agent DocandClear Session Contexton Codex now use the shared tmux submit profile (#jbcodexsubmit). The sharedsessions::send_submitted_text_for_harnesslive-pane path sends Codex command text through the same profile as queue drains and clear retry. Current diagnostics reporttmux_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, andsimworld_jb_run_and_clear_share_codex_enter_submit_contract. -
JB Stale File Cache recovery now converges through editor IPC first (
#q7jm). Thelive_prompt_drift_after_preflightauto-recovery path no longer writes the adopted snapshot directly to disk while a JetBrains listener is active. It now sends componentop: "replace"convergence patches through the editor, verifies ack-content against the adopted snapshot, and logstransport=editor_ipc; the direct disk write remains only as the no-listenertransport=disk_fallbackpath. If the listener is active but editor convergence is unproven, recovery logs[jbstalecache] auto_recovery_disk_write_blockedand fails closed. JetBrains and VS Code patch appliers now honor explicit componentopoverrides ahead of markerpatch=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-sideopoverride wiring tests. -
Codex auto-queue owner continuations report their harness submit mode. The idle queue watch now logs
idle_queue_watch_drainwith the actual submit mode and file-scoped prompt hashes, while Codex Stop-hook queue blocks logcodex_stop_queue_continuationproof 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 fromtasks/agent-doc/agent-doc-bugs2.md. -
Queue convergence IPC now carries the queue body (
#samplepcdrift). The preflight halt/drain convergence patch now sends the correctedagent:queuecomponent body alongsidequeue_autoand canonicalqueue: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_disknow logssource,expected_len/hash,disk_len/hash,live_len/hash, andlive_tswhen 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-containedops.logline instead of a token that can be confused withqueue_diff_active_prompt_differsprose. Regressionvisible_write_reconcile_treats_editor_matching_disk_as_reconcilable_driftnow asserts the structured fields. -
Structured
#s760clear-decision gate verifier (#ktw8). Added the built-ins760_clear_decision_clear_trueops-log verifier for destructive queue-turn/clearproof. It accepts only anchored^[epoch] [s760] clear-decision ...lines withoptIn=true, numericpct >= threshold, andclear=true; prose-only mentions insidequeue_diff_active_prompt_differsremain pending, and structured false fires (clear=truebelow 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, andgate_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
.mdwrite routes through the session actor's single ordered write queue unconditionally. RemovedAGENT_DOC_WRITE_AUTHORITY, theWriteAuthorityGateenum,current_gate, and the bare-atomic_writeoffbypass..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 standaloneagent-doc writeserializes correctly without a runningstartsession. - Supervisor host:
agent-doc startalways hosts the harness child throughsupervisor::in_process::InProcessSupervisor. RemovedAGENT_DOC_SUPERVISOR,supervisor_authority.rs, and the out-of-processsession.wait()host branch instart.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_WATCHand theactivepath;agent_doc_plugin_watch_readonlyalways 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 checkgreen; 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_WATCHrollback flags. Setting them now has no effect.
- Write authority: every editor-visible
-
Demote the JetBrains plugin WatchService to read-only buffer reporting (
#dsqa/#pcp7— 08b cut-over residual phase 2). NewAGENT_DOC_PLUGIN_WATCHrollback flag (watch_authority.rs, mirroring the#dav9/AGENT_DOC_SUPERVISORladder):active(default = today's behavior) /read-only(demoted). Atread-only, the JB plugin's autonomous NIOWatchServicefile-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 producedlive_prompt_drift_after_preflight/ipc_socket_already_applied_live_buffer_divergedeven under in-process hosting (the#dav9swap moved who hosts the child, not who writes the buffer). The plugin reads the flag fresh via the newagent_doc_plugin_watch_readonlyFFI export (the flag lives in the binary, so the operator opts in once on theagent-docsession and every IDE instance honors it without depending on the IDE's inherited environment); the export emits a structuredplugin_watch_readonlyops.logmarker so the cut-over is log-verifiable (#q6js/#lvbremain). Defaultactiveis unchanged — shipped users keep the WatchService applier until an operator opts in, and the gate fails safe back toactiveon 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. JBpluginVersion0.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 withAGENT_DOC_PLUGIN_WATCH=read-only+ the 0.2.159 plugin installed, drive an edit-during-finalize cycle) so the agent can confirm zero-drift + thevisible_write_live_buffer_matches_diskmarker fromops.log. -
Land the in-process supervisor hosting swap (
#dav9/pcpc5e1authority rung).AGENT_DOC_SUPERVISOR=in-processnow actually hosts the harness child throughsupervisor::in_process::InProcessSupervisorinstead of only logging the gate.PtySession::take_childhands theportable-ptychild to the adapter, which reaps exits non-blockingly viatry_wait(no reaper thread/channel), drives heartbeat + crash policy through itstickloop, and ownskill.start.rs'srun_with_reap_policybranches onsupervisor_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). Defaultoffis unchanged — shipped users still block onsession.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_workcomputed 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 thatpos - 30landed inside it, the slice panicked (byte index … is not a char boundary), crashingfinalize/session-check(rc=134) on otherwise-valid responses and wedging closeout. The window start now rounds down withfloor_char_boundary. Regression testquantified_remaining_work_handles_multibyte_char_in_window. -
#lvbremainverification markers now reachops.log(#x9ds). Two of the marker emissions the#lvbremainops.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 toops.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 exactearly_ack_pendingpredicate token toops.log(the human stderr line "early-ack pending" is hyphenated and never contained the token); new testableipc_socket::early_ack_ops_marker. The third marker,run_clear_coalesce(#9adk), is not a binary behavior — it is the JetBrains-pluginInvocationCoalescer(Run/Clear dedup) whose marker goes toidea.logand is operator-verified via the#lvbatchlive 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. Testsenforce_cross_session_claim_errors_on_reject(asserts the marker reaches ops.log) andearly_ack_ops_marker_carries_predicate_token. -
Ops-proof auto-completion no longer reaps a same-cycle add (
#5b28/#opsproof-samecycle-add). A gatedagent:reviewitem (oragent:backlogitem) added in the samewrite/finalizeinvocation via--review-add/--pending-add/--pending-add-gatedcould 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#s760doperator-verify gate that cites "Code shipped"). The existing #opsproof-falseposguard 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 incycle_state(pending_added_ids), and the reap excludes them.review_addreturns its assigned id; newcycle_state::{record_pending_added_ids, pending_added_ids}. Regression testops_proof_does_not_reap_same_cycle_added_gated_item. -
Transcript-token context-% pre-emptive
/cleargate (#s760c). The supervisor idle-queue watch now derives its pre-emptive/cleardecision from the real harness context-usage % (cumulative session-transcript tokens ÷ model context window, via the#s760a/#s760bcontext_pctsource) instead of the exchange-size accretion heuristic. On each idle gap, when the default-offagent_doc_queue_context_resetopt-in is set, it locates the active Claude transcript (newest*.jsonlunder~/.claude/projects/<project-hash>/), computes ctx%, emits the canonical[s760] clear-decision optIn=.. threshold=.. pct=.. clear=..line toops.log, and fires the tracked/clearonly whenpct >= clear_threshold_for_doc(agent_doc_clear_threshold, default 50). New pure, unit-testedcontext_pct::{clear_decision, claude_projects_subdir, latest_claude_transcript}. Fails safe per the destructive-/clearinvariant: an unknown model, missing/empty transcript, or unsupported harness (Codex/OpenCode) yieldspct=Noneand 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:reviewitem can now carry a typed proof/disproof predicate so the binary auto-proves or auto-disproves it fromops.logmarkers during the normal preflight read — no dedicated live-verify session. Set it withagent-doc backlog <FILE> set-verify <id> "verify=ops_log:<marker>;disproof=ops_log:<text>"(or--pending-set-verify id=<spec>onwrite/finalize); the gate-set time is stamped automatically. The predicate persists inline as a<!-- gate-verify verify=… disproof=… set_at=… -->annotation (new pureagent_doc_core::gate_verifymodule). The scan is fail-safe: a proof marker at or afterset_atwith no disproof →provable; a disproof marker →failed(disproof wins ties); neither →pending; stale pre-set_atmarkers never count. Preflight surfaces agate_verifyresult per predicate-bearing gated item and logsoptverify review=<id> status=<…>. The[/]→[x]auto-transition is opt-in and default off — only whenagent_doc_gate_autoverifyis true (frontmatter, then project.agent-doc/config.toml) does aprovablegate 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). FlippedEARLY_ACK_ENABLED false → trueinipc_socket.rs: the sender now auto-tags live closeoutpatchmessages withearly_ack: true, so an early-ack-aware listener emits apendingack 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 unknownearly_ackfield and still get exactly the prior single terminal ack. Live verification (#xkpf/#lvb-run) grepsops.logfor[ipc-socket] early-ack pending emitted before applywith a paired terminal ack and noack_timeout/false_success. Testearly_ack_tagging_is_dormant_by_default→ renamed/inverted toearly_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_ourscounted onlycontent_oursactive queue prompts, so a queue item the user added this cycle thatcontent_oursconsumed (struck~~…~~) read as "dropped" — tripping the#queue-user-edit-overwriteguard with a falsesession-checkINTERRUPTED on a correct closeout. The detector now counts consumed (QueueEntry::Completed) items toward coverage viaqueue_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-intermixauto-recovery no longer fails closed on a false-positive dropped-prompt record (#exch-intermix-falsedrop).try_auto_recover_live_prompt_driftbailed whenever the cycle recorded ANYdropped_exchange_prompts/dropped_queue_prompts. But that record compares the divergent IPC candidate againstcontent_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 manualgit checkout HEAD/reset --from-current/finalize --force-diskrecovery (hit live on the#opsproof-falseposcloseout:dropped_queue_prompt_recorded count=3blocked 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_completionnow 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, rungspcpc5a–pcpc5c). NewAGENT_DOC_WRITE_AUTHORITYenv gate routes the centralwrite::atomic_writefor editor-visible session documents up the 08b migration ladder (specs/08b-single-process-control-plane.md§"Document write and watch authority"):off(default — unchanged bareatomic_write, the rollback target),shadow(raw write unchanged, reports the would-route decision toops.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), andremoved(reserved for supervisor/plugin-writer removal; routes likeauthorityat this layer). A thread-local owner-scope re-entrancy guard (write_authority::owner_scope_guard, installed bywrite_queue::run_serialized) keeps the queue's inneratomic_writeon 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. Theremovedrung 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 callsagent_doc_document_changed_digest_content(full buffer text) instead of the len/hash-onlyagent_doc_document_changed_digest, matching the JetBrainsTypingTracker. This completes the editor side of#pcp6: the binary FFI (agent_doc_document_changed_digest_content), content sidecar (record_live_buffer_digest_content), andlive_buffer_diverges_from_contentclassifier 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. Newnative.documentChangedDigestContentbinding + function. VS Code0.2.25. End-to-end live-editor verification of the classify-vs- fail-closed behavior remains gated on#xkpf. -
Pre-emptive
/cleardecision predicate (groundwork) (#s760/ review#clear-opt-in-threshold, phase 2). Added the pure, unit-testedContextResetDecision.shouldClearBeforeDispatch(contextUsagePct, clearThreshold, optIn)that pins the phase-2 semantics: clear before a Run Agent Doc dispatch only when the operator opted intoagent_doc_queue_context_resetAND the live Claude Code context-usage % is known and at/above the configuredclear_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 inSubmitAction's pre-dispatch path — plus their live in-IDE verification remain gated on#xkpf. JB plugin0.2.158. -
JetBrains plugin coalesces rapid-fire Run Agent Doc / Clear invocations (
#9adk/ review#console-input-accumulation). A per-document, per-actionInvocationCoalescerguard 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-docor/clearkeystroke at the terminal layer below the route/queue dedup.SubmitActionkeys onrun:<routeKey>andClearSessionContextActiononclear:<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 givennowMillis(unit-tested inInvocationCoalescerTest); live in-IDE behavioral verification of rapid-fire coalescing is gated on#xkpf. JB plugin0.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_listeneremits apendingack the instant it receives a patch that opted in ("early_ack": true) — before the blocking apply — andsend_messagereads acks in a loop, treating apendingack 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). NewAckClassification::Pending;message_requests_early_ack/early_ack_line/early_ack_tagged_messagehelpers; 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 setearly_ackget a single terminal ack exactly as before, and the read loop runs once. Activation is gated byEARLY_ACK_ENABLED(off — the sender does not yet tag patches) pending end-to-end verification under live typing load (#xkpf). Tests inipc_socket.rs(classify_ack_treats_pending_status_as_pending,send_message_handles_early_then_terminal_ack, dormancy + flag-parse); specspecs/process-topology.mdR2. 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 binarycross-session-rejectmarker, 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) andcrossSession.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). Directrunprompts 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. -
claimemits 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),claimnow 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_markerwith 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 inclaim.rs(cross_session_reject_marker_carries_stable_fields); contract inspecs/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 theagent-doc+stashwindows,setcalls the newresync::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 helperslist_window_names,list_session_panes, andkill_sessionintmux-router; live-tmux coverage inresync.rs(kills a pure orphan, preserves a user-window session, treats an already-gone session as closed); spec inspecs/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 realcrdt_merge_base_stateoverlay path for an exchange-with-response document, fixed a stale client-id comment incrdt::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'ssubmodule pointercommit 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 outsideHEAD. 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 (thestuck_captured_cyclesymptom). 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 plainagent:queue, strip a legacyautoattribute from any touched queue tag, and usequeue_active: true/ start fences for activation. Editor diagnostics accept both the newactive agent:queuewording and olderagent:queue autooutput. 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 JetBrainsTerminalUtilTest. -
Queue slash commands now run as commands after the current turn. Active queue heads such as
/clearor/model sonnetare classified in the managed idle-queue drain path, submitted literally to the owner pane at the next idle prompt, consumed fromagent: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/clearand context reset clears use the supervisor's gate-exempt clear submit path instead of generic prompt injection. Codex Stop-hook andsession-check --codex-final-gatediagnostics 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, nopreflight_startedresponse 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 inagent: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, andstop_blocks_clean_closeout_when_auto_queue_has_generic_slash_command. -
Exchange slash commands now enter the same after-turn command path. Routed
agent:exchangeprompts whose pending text is a literal slash command such as/clear, with or without a❯prompt prefix, are copied intoagent:queue autoas 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, androute_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.mdfrom leaving that document pointed at the previousagent-doc-bugs2.mdpane after recovery.session statusno longer treats a closed actor's stale pane as live evidence, andsession doctor --repairclears old closed actor pane projections. Testsbinding_a_pane_evicts_other_documents_bound_to_it,sessions_projection_removes_displaced_cross_document_owner, andclosed_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 committedagent:exchangemust contain the response or queue-prompt echo, otherwise#lr-queue-patchback-missfails closed. Tests:free_text_queue_head_guard_fires_when_binary_consume_lacks_responseandfree_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 autoheads by injecting an in-owner-pane continuation instruction instead of the recursiveagent-doc <file>trigger. Claude and OpenCode keep their configured trigger command. This keeps JetBrainsRun Agent Docprompts queued behind a busy Codex owner from stalling on the recursive-direct-invocation guard once the owner goes idle. -
agent-doc focusis 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 tosync --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 asagent-doc focus <file> --blockingor--synchronous. Current JetBrains and VS Code tab selection now call plainagent-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 aslazily-rs.mdfrom letting a long-running CLI focus attempt delay the UI handoff. Bumped the JetBrains plugin build version to0.2.153and the VS Code extension version to0.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.yrssidecar 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. Testscrdt_merge_base_state_prefers_matching_overlay_projectionandcrdt_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. Testssafe_passive_sync_focuses_local_projection_when_sync_lock_is_contendedandtry_startup_lock_reports_busy_without_waiting. -
Session-isolated self install.
agent-doc self-installnow installs the current committed checkout from a temporary sibling git worktree, preserving relative Cargo path dependencies like../agent-kitwhile avoiding dirty files from concurrent sessions in the sharedsrc/agent-doccheckout. It runscargo install --path ., builds the release cdylib, installs it with the existing atomiclib-installpath, and removes the worktree unless--keep-worktreeis set. Testisolated_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 ... turndispatch-only refusal to the immediate still-running notification instead of the persistent route-failure path, so the#jb-run-agent-doc-busy-active-turn-stallskip-wait fix is visible to IDE users. Testactive-turn skip-wait route refusal is reported as still running immediately. -
JetBrains File Cache Conflict keeps visual highlighting fresh. The JetBrains plugin now reschedules
VisualHighlighterManageron 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. Testfile cache conflict path refreshes visual highlighters. -
JetBrains cold-open tabs apply agent-doc visual highlighting. The JetBrains plugin now reschedules
VisualHighlighterManagerfromFileEditorManagerListenerfile-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. Testcold opened markdown files refresh visual highlighters. -
Preflight/plan propose semantic completion matches. The shared
tsift-memorysession-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.preflightemitssemantic_completion_matchwarnings andplanincludes the same warning text; the signal is proposal-only and does not mark work done. Testssemantic_completion_matches_done_archive_for_free_text_queue_promptandbuild_plan_warns_on_semantic_completion_match_for_free_text_queue. -
Preflight auto-completes deterministic ops-proof tracked work. During pending maintenance, active
agent:backlogandagent:reviewitems 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 asauto_complete_ops_proof. Blocker language such as partial, remaining, reopened, deferred, false-closeout, or follow-up work keeps items active. Testpending_maintenance_auto_reaps_ops_proof_done_items. -
Preflight reaps stale active mirrors for archived done ids. During pending maintenance, active
agent:backlogandagent:reviewitems whose ids already exist in inlineagent:doneor the configured externalagent: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. Testspending_maintenance_reaps_inline_done_backlog_and_review_mirrors,pending_maintenance_reaps_external_done_archive_backlog_and_review_mirrors, andrun_queue_maintenance_excludes_external_archive_done_ids. -
Codex installs now register the agent-doc MCP server.
agent-doc skill install --harness codexwrites[mcp_servers.agent-doc]into.codex/config.tomland the Codex Stop hook prefers theagent_doc_preflight/agent_doc_plan/agent_doc_finalize/agent_doc_session_checkcontinuation 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. Testsprotected_prompt_input_reason_ignores_agent_doc_session_control_commandsandprotected_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.yrssidecar from the same markdown snapshot as the legacy text.yrsmerge 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. Testsdocument_crdt_save_persists_legacy_and_overlay_stateandensure_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. Repeateddo [#id]or free-text prompts remain executable queue intent instead of being collapsed by raw prompt text. Testpreflight_preserves_intentional_duplicate_tracked_queue_prompt. -
Icebox queue sync no longer auto-promotes parked work.
agent:iceboxmay still be sorted by priority and individual icebox items can still opt into queueing with per-item enqueue markers, but a component-levelqueueattribute onagent:iceboxnow warns and does not populateagent:queue. A drained queue plus drained backlog remains terminal until work is explicitly moved to backlog, queued manually, or marked for enqueue. Testsrun_queue_maintenance_does_not_sync_icebox_into_empty_queueandmisplaced_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. Testno_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 insideagent:exchangeresponse history as live pending shadows. Completed items archived elsewhere, such as lazily-rs#ipc1, no longer blocksession-checkjust because an earlier response listed next steps. -
Missing-response recovery trusts committed exchange bodies without capture metadata.
session-checknow treats any committedagent:exchange### Re:body as sufficient proof that a missing-response closeout has been repaired, even when the stale queue-drain cycle lostcapture_id/response_sha256metadata. This letsagent-doc write --commitrecovery clear interrupted sessions liketasks/software/lazily-rs.mdinstead of re-failing forever. Testcommitted_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_dispatchedwhile reconciling a stale busy actor over an idle pane. This stops a stuck active head from repeatedly injectingagent-doc <FILE>after each reconcile tick, while still allowing dispatch when the head drains or advances. Teststale_busy_reconcile_preserves_already_dispatched_head_dedup; specspecs/supervisor.md. -
Completed queue items can be marked in place. Explicit done IDs now mark matching
agent:queueprompts 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. Testsdone_id_marks_later_queue_prompt_completed_without_consuming_headanddone_id_marking_ignores_already_completed_queue_prompt; specspecs/07-orchestration-commands.md. -
Queue overwrite guard tracks free-text queue items. The
#queue-user-edit-overwritedetector now compares parsedagent:queueprompt 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 acontent_oursIPC adoption can silently delete them. Testsdropped_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
queueandprioritynow triggers queue priority / auto-DAG ordering for the syncedagent:queueeven when the queue marker itself has noprioritytoken. 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. Testsrun_queue_maintenance_backlog_queue_priority_sorts_and_marks_promoted_itemandroute_enqueue_priority_dispatch_*; specsspecs/pending-system.md,specs/07-orchestration-commands.md. -
Session memory retrieval (
#agent-doc-memgraphrag-retrieval).agent-doc memory index/searchnow indexes current session tracked work (agent:backlog,agent:review,agent:icebox,agent:doneincluding repo-relative.done.mdarchives) plus live exchange response summaries into.tsift/memory.dbthrough the sharedtsift-memorylibrary 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. Testsmemory_cmd::*; specsSPEC.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-decoratedenqueuetoken such as**enqueue**now appenddo [#id]toagent:queuewithout requiring the whole component to carry aqueueattribute. The path is idempotent, excludes gated/done/unmarked items, works in both preflight andagent-doc queue sync, and lets explicit markers bypass the plain active-loop fresh-item hold. Testsactive_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, andsync_accepts_enqueue_marker_without_queue_attr. Specspecs/07-orchestration-commands.md. -
Watch daemon emits node-keyed document events (
#md-ast-realtime-watcher). The markdown AST crate now exposesevents::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 logsdocument_node_eventsJSON batches on subsequent file changes, giving realtime enqueue/follow-up work a stable node-keyed event stream. Testsdiff_node_events_reports_insert_with_anchors,diff_node_events_reports_strike_by_stable_node_key,diff_node_events_reports_reorder_without_text_matching, andupdate_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 componentpatcheswhile adding explicitop/node_idmetadata and anode_patchesarray for item-level insert, strike, unstrike, replace, remove, and move operations. JetBrains and VS Code patch DTOs accept the new fields. Testsqueue_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, andparsePatchJson preserves node-addressed component patch fields. Specsspecs/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/writealready drop the legacyqueue_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 anyqueue_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 canonicalqueue:control took over (operators saw a persistentqueue_active: truethat "couldn't be removed"). Preflight repair now drops it once, byte-precisely, directly on disk + snapshot viastrip_deprecated_queue_active_line— but ONLY when the canonicalqueue:control is present, so no queue state is lost. Idempotent; legacy-only docs (noqueue:) keep their line. Testsstrip_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 theagent:queueauto-loop. The mutating session-check entrypoints (enforce_clean_closeouton thefinalizeboundary,run_with_optionsfor direct-execagent-doc session-check) now restore the committed HEAD in place viaself_heal_late_ipc_overapplication(logslate_ipc_response_overapplication_self_healed) instead of bailing — the same remediationpreflightapplies, taken only whendetect_late_ipc_response_overapplicationproves it safe. The read-onlyinspect*family stays non-mutating. Testenforce_clean_closeout_self_heals_late_ipc_overapplication. Specspecs/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, soqueue_file_ipc_reposition_boundarynow also tags the queued.agent-doc/patches/<hash>.jsonwithcycle_idand abaseline_hashof the live doc it targets, giving the asynchronous applier the same generation token to drop a superseded patch. Testqueued_file_reposition_patch_carries_generation_token. The plugin-side consumption of the token (PatchWatcherapply fence) and listener de-wedge are tracked follow-ups; specspecs/07-closeout-commands.md, plantasks/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 puredo x→❯ do xprefix 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--streamfinalize 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❯ Xor bareXform) is no longer foreign. Genuinely new unrelated❯prompts still keep the head queued. AddedAGENT_DOC_DEBUG_QUEUE_CONSUMEenv-gated instrumentation that logs each❯added line and its classification. Regression testfree_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
## Runbookssection 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-cating closeout runbooks every cycle); renders to all harness surfaces viaskill.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). Whenagent-doc runfinds no document/snapshot diff but the latest cycle was aCommittedno-response bookkeeping-only closeout (repair/reap following an abandoned recursive invocation or failed run), the classifier now returns anAbnormalverdict with a typed diagnostic naming the cycle id, last event, and recovery command, instead of plain "Nothing changed since last run". This prevents JBRun Agent Docfrom 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 remainsCleanregardless of bookkeeping. Spec:specs/07-closeout-commands.md(#jb-codex-nochange-after-repair). -
Dedicated
blocked_in_interactive_substateroute guard reason (#snrun). When a dispatch-onlyRun Agent Docreopen 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 dedicatedRoutedReopenGuardReason::BlockedInInteractiveSubstate(prompt_ready_barrierFlowEvent) and a stage-specific error ("blocked in an interactive terminal substate") instead of the genericdispatch_only_busy_actor_not_ready. Pure helpersrouted_reopen::is_interactive_shell_substate_reason/dispatch_only_blocked_guard_reason; regressioninteractive_substate_gets_dedicated_guard_reason. FlowCore hot-path token budget bumped (route.rsguard_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
setTextpayload at debug level (#jb-settext-payload-log, plugin 0.2.145).PatchWatcher.ktnow logs the fullresultpayload beforedocument.setTexton both theapplyPatch.componentandrepositionBoundarypaths, behindLOG.isDebugEnabled(only when#com.github.btakita.agentdocdebug 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.mdis no longer auto-converted into an agent-doc session.route,run, andstartnow fail closed before injectingagent_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) thedocuments.auto_session_for_all_md = trueescape hatch (restores old behavior). Existing session docs already carryagent_doc_session:/agent_doc_format:so they are unaffected; only brand-new plain notes/README.mdchange 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 predicateproject_config::is_agent_doc_document(rel_path, content, config)+ minimal zero-dep glob matcherproject_config::glob_match(*/?/**); FFIagent_doc_is_session_document(path)for editor plugins to gate Run Agent Doc / SubmitAction.agent-doc init/claimremain 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 JBSubmitAction.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 inagent:donebefore mintingdo [#id]prompts. Previously a lingering active backlog[ ]bullet whose id was also inagent:donewas 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:doneids are computed once up front and reused by both the sync filter and the strike pass. The done-strike pass also now reaps a resolveddo [#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-tonow fail closed when given an explicit custom id (id=<id>or[#id]) that collides with a frontmatterprompt_presetskey or an existing active backlog/review/icebox item id, so a new ambiguous#idis never written. Auto-id adds (no explicit prefix) are never blocked. Builds on the existingdetect_identity_collisionsregistry (now factored throughdocument_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 existingpreset_item_id_collisionpreflight 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::backfillnow 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 staysPending(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 helperproof_retry_decisionhas deterministic unit coverage. - Gate-exempt operator recovery. The supervisor IPC layer now gates only
real prompt dispatch (
Inject); the newClearcontrol method plusStopandRestartbypass the capability gate.agent-doc session clear/session interrupt-cleardeliver/clearthrough the gate-exemptClearmethod, so they stop or clear a proof-Failedsession withoutkill -9instead of failing withprompt 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.
- 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
-
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 tripscheck_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_modifiedcompared 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-placeitem_modifiededit. Preflight then halted the queue, strippedauto, setqueue_active: false, and stranded every remaining livedo [#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 directeddo [#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 newcheck_gated_phase_split_guardwarns to split each phase into its own child ID so deferred work stays independently trackable/queueable (sibling of#blocked-closeout-followup-captureand 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 --probeis a side-effect-free inspection mode (#preflight-probe-side-effect-free). A diagnosticagent-doc preflightused to open apreflight_startedcycle even when it was only inspecting state, leaving an open cycle that later wedgedsession-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 apreflight_startedcycle. The default response-bound preflight (and internal callers likeorchestrate) 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 existingpreflight_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-paneRun Agent Docroute appends a prompt toagent:queue autoand returnsOk, the drain was harness-delegated and a Claude session not running/loophad 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 livequeue_active: trueready head (sharedqueue_continuation::live_continuation_head) by injecting the harness trigger through the existing capability-gatedauto_trigger_inject_commandpath. The drain decision is the pure, testedidle_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-readywaits 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 to0.2.142and 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 thecodex-stophook 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 sharedqueue_continuation::detectis the single source of truth (queue_active+agent:queue auto+ activeresolve_activation+ a ready, unmodified head);active_auto_queue_promptnow delegates to it. Every clean binary closeout (finalize/write --commit/repair/ already-committed no-op) reconciles a durable.agent-doc/queue-continuations/<doc-hash>.jsonmarker (written when owed, cleared on drain /autoremoval /queue_activefalse / head advance).codex-stopnow 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-checksurfacesqueue_continuation_required=…/next_queue_prompt=…; the new strictagent-doc session-check <FILE> --codex-final-gateexits nonzero when continuation is required. Newqueue_continuationunit tests, twocodex_hookmarker-fallback tests, and acodex_hook_integrationstrict-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-backedagent-doc <FILE>runs inside the same tmux pane that already owns the document, the recursive-invocation guard fails fast with the existingrecursive direct invocation would deadlockdiagnostic — but it previously left the freshly-opened preflight cycle inpreflight_started, sosession-checkreported an interruption and the owner session stayed wedged until a manualagent-doc cancel.run.rsnow marks that empty cycle terminal (Abandoned) via a newabandon_run_recursive_cycle(the guard fires before any response capture, so nothing is lost);session-checkaccepts 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-closingsession-checkuntil a manualgit checkout. Fixes:canonicalize_answered_prompt_prefixesnever❯-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, sopreflightrestores clean HEAD automatically. Regressions indedupe,git, andsession_check. -
Prompt-prefix dedup gap fixed (
#prompt-duplicated-while-typing, partial). The shared-core append dedup (component::append_patch_already_presentvianormalize_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 thecontainsdedup missed and the prompt re-appended → the duplicate-while-typing report.normalize_append_patch_contentnow 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, soagent-doc lib-installships 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 activeagent:queuehead now requires an explicit completion signal. The CLIfinalize/write --commitpath requires a closeout flag —--done,--pending-gate, or--pending-edit "<id>=…"— naming the head id (or a genuine fresh operator prompt-target /do queuetrigger); 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. Newqueue_head_has_explicit_completion_signalinwrite.rs;response_topic_matches_queue_headnarrowed to exact-match. Coverage:explicit_signal_*+heading_topic_matches_head_exactly_only(write.rs) andhalt_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 thequeue_active:frontmatter, so a live IDE buffer re-addedauto/queue_active: trueon its next flush and the snapshot/HEAD drift loop regenerated on every preflight. Newagent_doc_converge_queue_autoFFI export (agent-doc-core, takes a C int for a stable JNA ABI) rewrites the queue opening-tag attribute;ipc_socket::send_queue_convergencecarries the desiredqueue_autostate plus thequeue_activefrontmatter; preflight'srun_queue_maintenancepushes the convergence through the listener after each halt/drain disk write (best-effort, non-fatal). JetBrainsPatchWatcherparsesqueue_autoand applies it viaNativePatching.convergeQueueAutoin both the Document-API and VFS apply paths (plugin0.2.137). Deterministic SimWorld repro:queue_maintenance_converges_live_ipc_buffer_on_item_modified_haltstarts 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-corev0.1.0 published to crates.io. The pure document data layer (#adcrextraction: 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 = falseremoved; all dependencies are crates.io crates (no path/git deps). Enables third-party FFI consumers and the editor-plugin slim-link target (linkagent-doc-core— ~9.87s cold / 74 crates — instead of the fullagent-docorchestration crate — 129s / 266 crates). The#k9e1/#epv5/#vb8h/#e130FFI relocations moved all 15 pure FFI functions intoagent_doc_core::ffiahead 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
finalizeor strictwrite --commit, if the supplied--baseline-fileis missing exchange content already committed inHEAD, the write path now applies the response on top ofHEADbefore producingcontent_ours, IPC snapshots, or commit-staged snapshots. This keeps back-to-back finalizes from dropping the previous### Re:block and logsexplicit_baseline_rebased_to_headwhen 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 autohead is the only prompt diff,finalizecan 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-onlyqueues a busy-actor rerun by savingagent:queue autoto the snapshot butHEADstill 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 genericsnapshot differs from HEADrecovery hint. Repeating the editor action with updated prompt text now replaces the sole live route-ownedagent:queue autoprompt 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-onlyaccepts a prompt by adding it toagent:queue autobehind 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_oursresponse image,agent-doclogsipc_snapshot_adoption_blocked reason=prompt_duplication_in_ack_content, savescontent_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 thetasks/professional/sampleportal.mdcorruption 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 bootingroute refusal when the ready probe ended onactive codex turnortimed_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_appliedwedge. The preflight regression suite now covers the exact Cancel-shaped closeout where the working tree and snapshot already contain the response butHEADdoes not and the cycle is stillwrite_applied. Preflight must classify that asjb_cache_conflict_canceland 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_changeswas 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 ascontent_edit/prompt_targetand tripped the guard. Net effect: the auto-loop almost never fired for real queue work. Preflight now emits a newuser_intent_prompt_changesfield that filters the same change list throughdiff::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 readsuser_intent_prompt_changesinstead ofprompt_bearing_changesso routine session bookkeeping does not interrupt the queue drain. Real user prompts (free-text questions, imperative directives outside the managed components) still appear inuser_intent_prompt_changesand continue to block. 7 new unit tests indiff::tests::change_is_managed_state_only_*. Plan:#ccloopguard. -
JetBrains plugin (0.2.131) now emits
already_appliedsocket-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 returns2 → {"type":"ack","status":"error","reason":"already_applied"}instead of1 → status:ok. The binary'sis_already_applied_errorgate then skips the file-IPC fallback that would otherwise stack a duplicate### Re:heading on top of the live buffer. New FFI exportagent_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_appliedsocket-IPC gate (and to#ipcpluginalreadyuntil every plugin emits the signal). Intry_ipc, when the patches are response-bearing (contain at least one### Re:heading) andapply_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 testtry_ipc_file_fallback_skips_when_patches_already_applied_to_live_buffer. Closes#ipcfilehashskip. -
Test fixture migration:
agent:pending→agent:backlog. Thetagpath lint --dialect agent-docgate added in the prior release blocked the deprecatedagent:pendingcomponent name. Migrated 31 sites intests/finalize_integration.rsand 4 sites intests/run_integration.rsto the canonical name; 30 previously-failing integration tests now pass.tests/pending_integration.rskeeps 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:exchangescratch comment during finalize. New deterministic scenario insrc/sim_world.rsexercises theis_already_applied_errorgate: 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 provededupe_ipc_snapshot_contentstill collapses the duplicate if the gate ever regresses. Also adds two integration-style tests for therecover_empty_response_for_strict_closeoutwrapper insrc/write.rscovering the fullagent-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 oftasks/agent-doc/plan-ipc-corruption-and-duplicate-during-typing.md. -
finalize/write --commitnow invoketagpath lint --dialect agent-docbefore 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 insidefinalize. The gate is a library call againsttagpath's agent-doc dialect (no subprocess overhead). Mode resolution: CLI--lint=off|warn| strict> frontmatteragent_doc_lint_dialect: off|warn|strict> workspace.agent-doc/config.toml[lint] dialect> default (warn). Default behavior: errors block, warnings surface on stderr.strictpromotes warnings to errors;offskips the gate (logged viaops_logfor 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 plusautofrom 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
fullContentsocket/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 logfull_content_ipc_disabledbefore falling back to guarded disk/snapshot repair. JetBrains and VS Code now also reject or delete legacy/foreignfullContentpayloads without applying whole-document editor replacements. Added a separate Mermaid reference for the repeated corruption chain. Bumped local plugin builds to JetBrains0.2.129and VS Code0.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 insideagent:exchangeremain 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_exchangefullContent 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 --commitno 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-stepscannot advanceagent:queue auto. -
Repair closeouts preserve auto queues unless the response targets the head. Missing-response materialization through
write --commitor the Codex Stop hook now leaves the active queue head andautoattribute 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
ETXTBSYfor 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 withnormalize_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.mdbuffer 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-stopnow detects an activeagent:queue autowith a ready next head prompt, blocks final-answer delivery, and tells Codex to invokeagent-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 --commitcloseouts now consume all contiguous active queue head prompts whosedo #iditems were resolved by repeated--doneflags, 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_LINESguard now logsnormalize_threshold_exceeded action=passthroughand 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_repairlog 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-stepscomment 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:boundarywhen that prompt block already has an assistant response earlier inagent: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.mdmixed-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 Docfinds 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, logsroute_dispatch_only_authoritative_degraded_direct_pane, and then uses the normal direct-pane readiness/blocker/proof gates before submitting, avoiding a first-open manualagent-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_responseand falling back when the editor returns prompt-only or partial response content.agent-doc commitalso refuses thesnapshot == HEADalready-current no-op when an active captured response is absent from the staged snapshot, leaving the cycle recoverable throughagent-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:exchangeonly 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 outsideagent:exchange. -
Route pre-dispatch preserves visible scratch comments.
agent-doc routestill 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 plannow 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). Newagent-doc jobs create/list/status/collectcommands generateagent-doc-job-packet-v1markdown packets under.agent-doc/jobs/<cycle>/, expand compounddodirectives 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 validatedagent-doc-worker-result-v1envelopes for parent review without applying patches or bypassing finalize. -
tsift dispatch-trace audit data now rides with graph-backed orchestration.
agent-doc plan/orchestratenow collectdispatch-trace-v1alongside 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 hiddenworker_resultline with status, target id, touched files, tests, and follow-up ids beforefinalize, 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 planand orchestration prompts now preserve theconflict-matrix-v1context-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 begraph-db-evidence-v1with packet ids, projection hashes, replay commands, and repair commands; conflict matrices must beconflict-matrix-v1; worker packets must beworker-prompt-packet-v1with packet ids, projection hashes, token budgets, and explicit fail-closed prompt text. Parallel orchestration now blocks unless tsift explicitly reportscan_parallel=trueandfail_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_oursresponse insertion hunks against the currentagent:exchangebefore 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---donewarnings 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 completeddo #idbatches 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:exchangeclose markers and strands live prompt text in that duplicated segment, the closeout normalizer now refuses automatic repair and logs a typedflow::document_mutationevent withreason=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
fullContentemission by default and editor plugins no longer apply legacy/foreign whole-document replacements. Bumped local plugin builds to JetBrains0.2.127and VS Code0.2.20. -
FlowCore now has an executable guard/proof regression gate. Routed-reopen prompt-ready and dispatch-proof failure reasons now pass through
RoutedReopenGuardReasoninstead of free-form strings fromroute.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-docwrapper process as blocking evidence by itself. File-scopedsession clearnow 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 whenpane_current_command=agent-doc. JetBrains now parses legacyactive_agent_docclear refusals as typed busy-session warnings and exposes a standaloneInterrupt and Clear Session Contextaction. Bumped the JetBrains plugin build version to0.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:erroracks are no longer treated as successful delivery. Bumped local plugin builds to JetBrains0.2.125and VS Code0.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_mutationbefore 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 to0.2.123. -
FlowCore owns the next closeout, mutation, and session-cycle slices.
agent_doc_template::patchbacknow 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_mutationemits the file-scoped FlowEvent/ops-log adapter.flow::closeoutowns the strict terminal transaction for commit, snapshot convergence, parent gitlink verification, session-check, and fallback-patch cleanup.preflightandplannow shareflow::session_cycleprompt-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 byagent_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.rsmaps 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.rsmaps supervisor/controller facts into those typed decisions and remains the tmux/supervisor/controller I/O coordinator. The large route test module was split out tosrc/route/tests.rsso live tmux fixtures no longer live inline in production routing code. -
FlowCore mirror-mode typed events are in place. Added the first
flowmodule set for session-cycle, routed-reopen, closeout, document-mutation, operator-clear, and orchestration-batch ownership; ops summary now groupsflow_eventdiagnostics 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 @filenameidle placeholder. Operator status/clear readiness now treats the current dim Codex suggestion› Write tests for @filenameas prompt-ready idle evidence, so anagent-docwrapper pane with only that placeholder and the Codex model/cwd/context footer no longer stays classified asalive-busy prompt_ready=false. Real drafted input, queued drafts, shell search, active permission prompts, and panes showingWorking (... 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
#wnj2intasks/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=trueeven 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 activeoutput, including the genericagent-doc command failedwrapper, 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 to0.2.122. -
JetBrains Clear Session Context typed warning was live-validated. A live IDEA replay against
tasks/agent-doc/agent-doc-bugs2.mdnow surfaces the typed running-session warning for an activeagent-docpane, 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
readywhile the direct Codex pane is still running the activeagent-docturn. The JetBrains refresh-retry readiness helper now has coverage thatalive-busy prompt_ready=falsedoes 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 commitnow treats that state as prompt handoff instead of re-emittingcommit_noop/commit_already_currentlifecycle 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-kitandbtakita/tmux-routernext to theagent-doccheckout before runningmake check, matching the local workspace layout required by the../agent-kitCargo path dependency and theCargo.tomltmux-router patch. -
CI now names the tmux integration leg explicitly. The GitHub Actions workflow labels the normal suite as
Run make checkand the live tmux sweep asRun make tmux-ci, with a visibleRunning make tmux-cimarker 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:exchangeand 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 anythingplus the Codex model/cwd/context footer as an idle composer, so JetBrains Clear Session Context does not fail closed withcurrent_command=agent-doc prompt_ready=falsewhen the pane is only showing the default Codex placeholder/status UI. Drafted prompt input, queued drafts, and shell search still fail closed and point tosession 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:exchangeonly 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_readytimeout 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#rtbrintasks/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 autoblock has no remaining prompt entries because every item was already marked complete, preflight now clears the queue body, removesauto, syncs the snapshot, and leavesqueue_active: falseinstead of preserving a completed queue run for later cycles. -
Direct active-queue runs are explicitly single-step resumable. Bare-path /
runinvocations now synthesize the active queue head whenqueue_active: trueand the document has no diff, consume one queue prompt before strict closeout, and print a continuation diagnostic when anagent:queue autoblock 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#lastpfxintasks/agent-doc/agent-doc-bugs2.md. -
Active queue prompts no longer get hidden behind empty document diffs. When
queue_active: trueand the document matches its snapshot,preflightandplannow synthesize the queue head item as the prompt diff, soagent-doc <FILE>opens a real cycle instead of returningno_changes=true. Added regressions for the#oobpmtqueue-resume shape and updated the git integration spec. -
Preflight now warns on harness/document mismatch.
agent-doc preflightcompares frontmatteragent:against the active Claude Code, Codex, or OpenCode harness, emits a structuredharness_mismatchwarning 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 validpatch: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 intoagent:exchange. This closes#rspdigestintasks/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 observedagent:exchangelive-typing corruption shape and documented the plan intasks/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 restartby default and reserve--reload compactplus/compactprompting for sessions that explicitly opt intoagent_doc_auto_compactin 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 latestroot.mdauto-compaction report intasks/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 withreason=drafted_prompt_inputwhen the live pane only shows placeholder/status UI such asgpt-5.5 high ... Context ... used. Real non-dim typed prompt input, queued drafts, and shell search still fail closed and point tosession interrupt-clear. This addresses the latest JetBrains Clear Session Context blocker intasks/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 logsipc_prompt_duplicate_repairedbefore saving the snapshot, andsession clearcan 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/clearplus duplicate prompt report intasks/agent-doc/agent-doc-bugs2.md. -
Answered closeout markers no longer keep committed cycles open.
session-checknow 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 unfinishedagent-doc-bugs2closeout shape and documented the follow-up plan intasks/agent-doc/plan-finish-closeout-after-answered-marker-drift.md. -
Sidecar normalization fallback now has direct repair diagnostics coverage. A regression recreates the
#normfallbackshape fromtasks/agent-doc/agent-doc-bugs2.md: the plugin ack-content sidecar strips a required prompt prefix, the binary rejects that primary snapshot withreason=prefix_divergence, repairs the snapshot and working tree from the normalized fallback, and records thesidecar_normalization_fallback_repaired_working_treeops-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_startedcycle, lets repair abandon that stale prompt-bearing cycle, and proves the original writer stops withpartial_response_checkpoint_stoppedinstead of writing another checkpoint for the abandoned cycle. The backend spec names stale-preflight abandonment as part of the checkpoint stop contract for#staleckptintasks/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 -> readyschedule: dispatch remains blocked through restart-bootstrapbusyand throughreadywithout 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#startrouteintasks/agent-doc/agent-doc-bugs2.md. -
Pending-only empty write closeouts cover completed items.
write --commitwith empty stdin now has regression coverage for--doneas well as--pending-add: it reaps and archives the completed item, commits the document, leaves the exchange untouched, and passessession-check. The closeout spec now names both add-only and done-only pending mutation shapes for the#writeemptycontract intasks/agent-doc/agent-doc-bugs2.md. -
Terminal closeout lifecycle updates are idempotent. Repeated repair/replay/no-op bookkeeping after a cycle is already
Committedno longer rewrites cycle-state, refreshes committed capture timestamps, or re-emitscapture_committed_after_replay; late fallback rejection diagnostics now include the patch id.agent-doc ops summaryalso separatescommit_noop drift_kind=noneand 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#sp3qand#s8csintasks/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:exchangecloseouts, 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 reportpost_commit_user_follow_upwhen 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#aymrintasks/agent-doc/agent-doc-bugs2.md. -
Codex non-streaming patchback now filters progress chatter. Direct Codex child response capture now selects the last
item.completedagent_messagebeforeturn.completedas 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#codexverbosepbintasks/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 permittedas active SSH capability loss, so searches through.agent-doc/capturesor 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#sshcapfalsecaptureintasks/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 promptnow 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#hookreviewrouteintasks/agent-doc/agent-doc-bugs2.md. -
Ops summaries now separate expected follow-up noise from anomalous drift.
agent-doc ops summarynow buckets benignpost_commit_user_follow_up,post_commit_local_drift kind=user_follow_up, andcommit_noop drift_kind=user_follow_upevents separately from working-tree drift/noop diagnostics. No-op closeouts now log their drift kind inops.log, making routine user follow-up reruns distinguishable from real post-commit local edits. This closes#opsnoisereduceintasks/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 tooutcome=timed_outplus a loose last command. This closes#interruptcleartimeoutintasks/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/ legacyagent:pendingafter the response is merged intoagent:exchange, while preserving normal tracked backlog edits and pending state changes. This closes#backlogorphanintasks/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-exchangenow resolve a launchableagent-docbinary before spawningpreflight,finalize, orsession-check, falling back whencurrent_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#synchorchstopintasks/agent-doc/agent-doc-bugs2.md. -
Sequential orchestration now freezes exchange task lists.
agent-doc orchestrate --mode sequential --from-exchangerecords 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#orchmidrunintasks/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#clearinterruptvimintasks/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
#tmuxe2etestsintasks/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_eventlines 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#opencodeinputdiagintasks/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
readyprompt 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 asWorking ... esc to interruptcannot enter prompt sampling. This closes#ownedreapbusyintasks/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 logsession_operator_starting_guard_refused. This closes#clearstartingraceintasks/agent-doc/agent-doc-bugs2.md. -
Orchestrated template closeout now accepts clean plain child responses.
writeno longer requires an explicitpatch:exchangeblock for orchestrate-origin template responses when the child returned a single clean assistant body; the existing unmatched-content synthesis appends it toagent:exchange. Patch-bearing orchestrate responses still requirepatch: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#orchplainrespintasks/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#latefallbackloopintasks/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_proveninstead oftimed_out/over_budget. Updated route regressions and session tmux specs. This closes#directsubmitbudgetintasks/agent-doc/agent-doc-bugs2.md. -
Starting actor reroutes now refresh terminal lifecycle states immediately. While route is waiting for a
startingauthoritative actor to become dispatch-ready, a supervisor refresh toclosedorblockednow stops the wait and surfaces that terminal actor state instead of burning the startup-ready timeout and reporting stalestartingstate. Updated route specs and added SimWorld plus tmux-backed route coverage. This closes#startreadytimeoutintasks/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 distinguishesreturnfromctrl+jsubmit 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.mdappends 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#donearchiveattrintasks/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: directalive-busyevidence alone no longer fails closed, so JetBrains/VS Code Clear Session Context does not get stuck behind Codex status/footer panes such asgpt-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 recordsession_clear_protected_input_guard_refusedand point operators toagent-doc session interrupt-clear <FILE>for an intentional discard. This closes the latest Clear Session Context repro intasks/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 JetBrains0.2.118and VS Code0.2.17. -
Managed OpenCode permission arrows no longer leak escape text. While the supervisor sees an active OpenCode
Allow once/Allow always/Rejectpermission 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/^[[Dleak shape from a live permission dialog. -
Editor prompt answers now run from the owning session cwd.
agent-doc prompt --allentries includecwd, and the JetBrains/VS Code prompt UIs use that root when callingprompt --answerinstead 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 JetBrains0.2.117and VS Code0.2.16. -
Editor prompt answers now use the
prompt --answerpositional contract. JetBrains and VS Code prompt UIs accept flatagent-doc prompt --allentries withselected, keep the selected state in their prompt item model, and send the selected option's one-based position toagent-doc prompt --answerinstead of forwarding the displayed TUI option number. Bumped the local-testing plugin builds to JetBrains0.2.116and VS Code0.2.15. -
OpenCode permission prompt answers now use the actual TUI selector state.
agent-doc prompt --answernow captures OpenCode panes with ANSI attributes before parsing, so it can read the highlightedAllow once/Allow always/Rejectoption 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/^[[Dtext. -
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 alwaysfollow-up confirmation prompt. -
OpenCode dispatch-only startup probes now use the OpenCode redraw budget. JetBrains
Run Agent Doccan 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 falselatest run is still bootingrefusals 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 orcontext ... % usedfooter. 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 withroute_authoritative_actor_starting_not_readyafter 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
startnow surfaces the user-visible[start] managed ... capability proofline withtmux display-messagetargeted 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-onlycontext ... % usedfooter as an idle composer. That lets route/start promote a proven OpenCode actor toreadyand dispatch the trigger instead of timing out withroute_authoritative_actor_starting_not_readyafteropencode_capability_proof status=proven. -
Strict closeout now reports slow commit phases and fails explicitly on stale parent gitlinks.
finalize/ strictwrite --commitrecord acloseout_latencydiagnostic 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 parentHEAD:<submodule>still differs from the submoduleHEAD, namingagent-doc commit <FILE>as the idempotent recovery. This closes#rspcmtdelayintasks/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 sendingagent-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 asroot.mdwhere the IDE action must send the plain Codex-compatible form. Bumped the JetBrains plugin build version to0.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 Docin Claude after closing a Codex session no longer fails onbound 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_prooffor SSH-gated documents, runs a boundedopencode run --format jsonchild 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 assocket: Operation not permittednow 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#opencodecapfailintasks/agent-doc/agent-doc-bugs2.md. -
Post-commit follow-up prompts no longer look like missed patchback repair. When
commitseessnapshot == HEADand the live file only adds a later user follow-up, it now logs a dedicatedpost_commit_user_follow_upmarker and suppressesprior_patchback_without_response_body/out_of_band_writenoise. The follow-up still remains uncommitted for the next response cycle, but ops diagnostics no longer imply a missing assistant response body. This closes#codexpatchbodyloopintasks/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>.jsonfile 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-dupintasks/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 codebaseidle placeholder as prompt-ready evidence. This closes the follow-up JetBrains Clear Session Context repro intasks/agent-doc/agent-doc-bugs2.md. -
Clear Session Context no longer treats Codex status-only panes as busy. File-scoped
session statusandsession clearnow 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 to0.2.113. This closes the latest JetBrains Clear Session Context stale-busy repro intasks/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
#4w5xintasks/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-busynotification 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 to0.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, runsrepair_layoutimmediately, and rechecks layout before emitting JSON. If automatic repair cannot run, stderr names the explicitagent-doc session doctor <FILE> --repairaction instead of silently waiting for a second detection. This closes#baseindexrepairintasks/agent-doc/agent-doc-bugs2.md. -
Dispatch-only proof scope is explicit across harnesses.
route --dispatch-onlynow logs bothproofandproof_scopeso 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#clauderouteproofintasks/agent-doc/agent-doc-bugs2.md. -
JetBrains real markdown navigation now always validates the actor/supervisor path. A true
selectionChangedevent still runs the guarded backgroundsync --no-autostartreconciliation 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 liketasks/software/corky.mdhas no actor, preventing laterClear Session Contextfrom surfacingstage=missing_actor. Bumped the JetBrains plugin build version to0.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 rawagent-doc command failednotification. Updated editor specs and added parser/message regressions. -
Stale prompt-bearing preflight cycles are abandoned, not placeholder-closed. If a pane dies after
preflight_startedbefore any response capture exists, and the live document still has an unresolved prompt target,repairnow 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 nextpreflightopens 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-recoveryintasks/agent-doc/agent-doc-bugs2.md. -
Prompt+response exchange drift now fails closed.
session-checknow treats an uncommitted appended exchange chunk containing both a user prompt and a new assistant### Re:/## Assistantmarker 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#rspcmt7shape where the visible response closeout landed intasks/root.mdbut the owning repo stayed dirty. -
Clear Session Context now works after a closed actor generation. The project controller still rejects
blockedactors and non-clear commands forclosedactors, but an explicitsession_clearoperator command now records anoperator_closedacceptance so the CLI/editor can send/clearto the live harness context before the next run. Added a controller regression and updated the session tmux command spec. This closes the latest JetBrainsClear Session Contextclosed-generation repro intasks/agent-doc/agent-doc-bugs2.md. -
Harness-agnostic uncommitted exchange drift detection.
session_check.rsnow 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. Previouslydetect_active_session_post_commit_driftrequired Codex session tracking (CODEX_THREAD_ID) and silently returnedNonefor all other harnesses, allowing uncommitted responses to pass all guards. The newdetect_uncommitted_exchange_driftfunction 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#rspcmt6intasks/agent-doc/agent-doc-bugs2.mdand extendstasks/agent-doc/plan-response-patchback-uncommitted.mdwith the harness-agnostic drift evidence. -
OpenCode CLI-only-output anti-pattern. The OpenCode section of
runbooks/harness-invocation.mdnow explicitly names the anti-pattern of outputting a response to the CLI without piping it throughagent-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 inSKILL.mdreinforces that the response does not exist until it crossesfinalizeorwrite --commit. Added regression tests proving session-check catches an OpenCode prompt-only exchange tail and the runbook section names the anti-pattern. This closes#noexchopencode2intasks/agent-doc/agent-doc-bugs2.mdand followstasks/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 withagent-doc write --commit <FILE>andagent-doc session-check <FILE>passes. Theharness-invocation.mdrunbook 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#rspcmt5intasks/agent-doc/agent-doc-bugs2.mdand extendstasks/agent-doc/plan-response-patchback-uncommitted.mdwith the direct-chat closeout invariant. -
OpenCode direct-exec session-check guard. The OpenCode harness runbook now requires
agent-doc session-check <FILE>immediately afterfinalizeand after manualwrite --commit, matching the existing Codex fail-closed contract.runbooks/commit.mdandREADME.mdnow name both Codex and OpenCode for the direct-exec post-write guard.session_check.rserror messages no longer reference "the active Codex session" or "the Stop hook" exclusively — they use harness-agnostic language. This closes#rspcmt4intasks/agent-doc/agent-doc-bugs2.mdand extendstasks/agent-doc/plan-response-patchback-uncommitted.mdwith 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 rerunagent-doc <FILE>or useagent-doc write --commit <FILE>for a missing response. Route'sstartingauthoritative-actor failure now says to wait and rerun, and namesagent-doc start <FILE>for stuck-owner recovery. This closes#rspcmt3intasks/agent-doc/agent-doc-bugs2.md. -
OpenCode harness support.
agent: opencodenow resolves to an OpenCode managed pane withagent-doc <file>trigger routing,opencode_model/opencode_argsfrontmatter and config aliases, and a minimal non-streamingagent-doc run --agent opencodebackend that invokesopencode run. This supports OpenCode model IDs such aszai/glm-5via the same--modelinjection path. -
#agent-doc-bugdeclaration chains now preserve backlog order.agent-doc plannow expands multiple prompt-bearing#agent-doc-bugdeclarations 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#bugchainorderintasks/agent-doc/agent-doc-bugs2.md. -
Standalone boundary setup no longer advances the commit snapshot.
agent-doc boundarystill 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
startingauthoritative actors. Managed and dispatch-only reroutes now wait for astartingcontroller actor to refresh toreadybefore recording a dispatch attempt or sending tmux/supervisor input; if the actor staysstarting, route fails closed with a state-gate diagnostic instead of creating an interrupted startup cycle.busyactors remain eligible for one supervisor-owned queued reopen. Added route regressions and updated the routing/session-actor specs. This closes#startingdispatchintasks/agent-doc/agent-doc-bugs2.md. -
Stale
startingactor cleanup no longer trusts a live PID forever. Normalpreflight,start,sync, andgccleanup now keep a one-hour-oldstartingactor only when the recorded supervisor PID is alive and its lease heartbeat is still fresh. A stuckagent-doc start --route-ownedprocess 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#startgcleakintasks/agent-doc/agent-doc-bugs2.md. -
Direct
agent-doc runnow 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 toHEAD,runrechecks the diff and fails before child-agent dispatch with anagent-doc write --commit <FILE>recovery hint. Added an integration regression proving a configured child agent is not invoked. This closes#emptyrsprepairintasks/agent-doc/agent-doc-bugs2.md. -
Agent-owned partial patchbacks can be adopted from empty strict repair writes.
agent-doc repairnow adopts already-visible responses from interruptedresponse_captured/write_appliedcycles even when no pending response artifact remains, and strictwrite --commitwith empty stdin runs that adoption path before failing as an empty response. This closes#partialpatchbackadoptintasks/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 repairnow 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#blockedstopextractintasks/agent-doc/agent-doc-bugs2.md. -
Stale
startingactor cleanup now runs on normal paths, not just daily GC.preflight,start, andsyncnow run the lightweight controller actor cleanup every cycle, closing one-hour-oldstartingrecords 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.stampdaily cadence. Added regressions for preflight with a fresh GC stamp and caller-specific actor transitions. This closes#autogcstartintasks/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 loggingsidecar_normalization_fallback reason=prefix_divergence. Added editor regressions and updated the plugin IPC spec. This closes#sidecarfallbackstillintasks/agent-doc/agent-doc-bugs2.md. -
Tracked-work completion now uses
--done.writeandfinalizenow expose--done <id>as the public flag for marking eitheragent:backlogoragent:iceboxwork complete. The old--pending-donespelling and the transitional--backlog-donespelling are accepted as deprecated aliases with warnings, whileplanand recovery hints now emit--done. This closes the CLI rename request intasks/agent-doc/agent-doc-bugs2.md. -
Prompt-only exchange tails now fail closed after closeout.
session-checknow scans the liveagent:exchangetail 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-deploypatchback miss where implementation commits succeeded but the final response never landed in the session document. This closes#rootpatchmissintasks/agent-doc/agent-doc-bugs2.md. -
BREAKING CHANGE: completed backlog archives now use
agent:done. The completed/reaped archive component was renamed fromagent:backlog-donetoagent:done, andagent:backlog-done/agent:pending-doneare no longer accepted as archive aliases by closeout, history replay, or pending resolution.agent-doc migraterewrites both legacy tags toagent:done, and newly reaped items create or append toagent:done. This closes the follow-up archive rename request intasks/agent-doc/agent-doc-bugs2.md. -
Cross-document backlog capture now has a binary-owned target path.
writeandfinalizeaccept--pending-add-to <file> <text>for explicit backlog targets, fail closed when the target file is missing or lacks a backlog component, andplannow surfaces those target files inpending_mutations/ finalize hints. Closeout guards no longer let a current-document--pending-addbypass explicit target validation, preventing#agent-doc-bugitems from landing in the wrong session document. This closes#crossdocpendintasks/agent-doc/agent-doc-bugs2.md. -
Prompt-prefix normalization now uses opt-in response-block exits. The
content_oursnormalizer no longer leaves an inserted assistant response block just because a response sentence looks prompt-like, and target-based prefix repair must match an explicitnormalize_prefix_linestarget 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#spfxnormintasks/agent-doc/agent-doc-bugs2.md. -
Direct
agent-doc runwaits now emit and persist heartbeats. After preflight opens the response cycle, long non-streaming child-agent waits print[run] heartbeat ...progress everyAGENT_DOC_RUN_HEARTBEAT_SECSseconds (default 30) and update the open cycle state'supdated_at/last_eventwithout 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#runhbintasks/agent-doc/agent-doc-bugs2.md. -
Compact Exchange now uses editor IPC before falling back to disk writes.
agent-doc compact <file> --component exchange --commitdelivers 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 syncimports legacy.agent-doc/last_layout.jsononce when.agent-doc/state.dbhas no layout row, then reads and writes the controller-backedlayout_statestable as the authoritative column-memory state.last_layout.jsonis still emitted for compatibility, but drifted JSON no longer overrides SQLite. This closes#stateprojintasks/agent-doc/agent-doc-bugs2.md. -
Submodule closeout now fails closed on stale parent gitlinks. Strict
finalize/write --commitandsession-checknow 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 idempotentagent-doc commit <file>recovery. This closes#rspcmt2intasks/agent-doc/agent-doc-bugs2.md. -
Managed Codex capability proof now reports phase timings. Successful
codex_capability_proofevents includetimings_msfor host DNS, child network, required SSH, launcher writable-root checks, child writable-root checks, and total proof time, so slowagent-doc startruns 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#caplatintasks/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.runnow bounds the agent-child wait withAGENT_DOC_RUN_AGENT_TIMEOUT_SECS(default 1800s), records a recoverablepreflight_startedtimeout 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-checknow surfaces those timeout events with concrete retry/restart guidance. This closes#preflighthangintasks/agent-doc/agent-doc-bugs2.md. -
Codex network-required sessions now prove network from inside a Codex child.
codex_network_access: enabledstill clears inheritedCODEX_SANDBOX_NETWORK_DISABLED, but managedstartnow also runs a boundedcodex exec --jsonprobe 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#codexnonetintasks/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 to0.2.106. -
Managed Codex panes now prove capabilities before reuse. Codex
startrecords acodex_capability_proofevent after successful live network, isolated SSH, and writable-root probes whenever the document requests network access,required_ssh_targets, or extra--add-dirroots. Route no longer trusts a ready managed Codex actor without a current proof after the latestsession_start; it restarts fresh once with the original launch contract before rerouting, andsession statusreports whether the proof isproven,missing, ornot_required. This closes#codexcapstaleintasks/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-autostartnow 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 broadwindow_resolutionbucket. This targets the current#syncbudgetstilltraces 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.jsonis absent, sync now derives the sibling projection from registered panes already visible in the targetagent-docwindow 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 focusno 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 tosessions.json, without launching or blocking on the controller actor-binding request. Backgroundsync --no-autostartstill 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 debouncedsync --no-autostartreconciliation 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 to0.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
#stashprunefastintasks/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 bareNo 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 oragent-doconPATH, 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-regressintasks/agent-doc/agent-doc-bugs2.md. -
Dispatch-only route now submits to healthy
startingcontroller actors instead of refusing editor reruns. If the controller and healthy supervisor still reportstarting,route --dispatch-onlykeeps the same direct-pane submit boundary as file-scopedsession clearinstead of focusing and dropping the rerun. When the pane is visibly dispatch-ready, route also promotes stale lifecycle state toready, but split-pane submission no longer depends on that prompt probe. Added a four-pane tmux regression and updated the routing spec. This closes#editorswitchctlintasks/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-donenow closesdo #idturns 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 intoagent: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-cascadeintasks/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#editorswitchintasks/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-autostartruns with the same visible column/window mapping skip the expensiveprune_stash_windowsandprune_stash_paneswork 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#syncpruneintasks/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
#syncproofintasks/agent-doc/agent-doc-bugs2.md. -
Cross-document sync no longer waits behind another document's closeout pane. Manual
Sync Tmux Layoutand passive editor autosync still protect panes that own openpreflight_started,response_captured, orwrite_appliedcycles 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 theagent-doc-bugs2.mdrepro. -
Protected sync edge coverage now has deterministic SimWorld traces and fewer default-suite tmux variants. Added named
#tmuxbudgetsimulator 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, andprojection_refreshalongside the existing window, prune, ownership, router, and safe-passive total timings. The live#synclagtraces 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-fetchedpane_current_commandmetadata 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 chooseagent-doc focusfor 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 Layoutand 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, andstatus --ensurereleases its readiness stream before issuing the status RPC. Added regressions for response timeout and idle-client isolation, and updated the controller specs. This closes#ctrlsockintasks/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_launchcompares 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 assession_statusas unknown commands. Added focused controller identity regressions and updated the controller command spec. This closes#ctrlreloadintasks/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/doctornow 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 refreshingsessions.jsonas 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#pcopsintasks/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
dispatchattempts 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#pcroutesintasks/agent-doc/agent-doc-bugs2.md. -
Project Controller Phase C now routes supervisor lifecycle facts through controller IPC.
agent-doc startlazy-launches the project controller, records the starting actor generation throughstart_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#pcsuperintasks/agent-doc/agent-doc-bugs2.md. -
Claude streaming prompt writes now tolerate early child exit. If the child exits before reading stdin, a
BrokenPipeduring 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.rsroutes actor load/store through the controller state boundary,project_controller.rsowns.agent-doc/state.dbtables for documents, transitions, leases, dispatch attempts, and projection diagnostics, and compatibility projections are emitted from committed state. Existingsessions.jsonentries 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#pcstoreintasks/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>.mdto the new hash beforefinalizereads 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#pathmoveintasks/agent-doc/agent-doc-bugs2.md. -
JetBrains protected-layout sync warnings now identify the blocking pane. The live
tasks/software/tsift.mdreplay showed the backend was correctly preserving pane%208while itspreflight_startedcloseout was open, but the JetBrains notification collapsed that into a generic "another pane" warning.SyncLayoutActionnow 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 to0.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 to0.2.98. This closes#respfxintasks/agent-doc/agent-doc-bugs2.md. -
Codex latest-prompt lookup now skips malformed hook-state entries.
codex_hook.rsno 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 makingload_latest_prompt_for_filereturnNone. Added a direct regression and updated the shared spec. -
IPC
content_oursprefix fallbacks now repair the working tree before commit. When plugin sidecar verification rejects a normalization result,write.rsstill falls back to normalizedcontent_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 containingCommit / 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#pfxcoursintasks/agent-doc/agent-doc-bugs2.md. -
Concurrent prompts added during explicit-baseline closeout now fail closed.
write.rsnow classifies live disk drift against the pre-response baseline before the response is merged, so a prompt typed after preflight but beforefinalizecannot be mistaken as answered by the response that was already in progress. The committed snapshot stays atcontent_ours,session-checkinterrupts on the unresolvedprompt_target, and the closeout spec plus integration coverage now encode the contract. This closes#concpromptintasks/agent-doc/agent-doc-bugs2.md. -
Streaming responses now leave durable partial checkpoints before final closeout.
capture.rsnow maintains a.partial.jsoncheckpoint 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 toresponse_captured. Bothagent-doc streamand CRDT orchestration streaming feed that checkpoint writer, with regressions proving the partial checkpoint survives before final closeout and remains diagnostic-only. This closes#chkptcapintasks/agent-doc/agent-doc-bugs2.md. -
JetBrains protected-layout sync warnings are now deferred-sync UX instead of raw CLI diagnostics. Manual
Sync Tmux Layoutstill 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 includessafe_passive_layout_preserved_reselected_focus, covering theagent-doc-bugs2.mdtotasks/software/tsift.mdnavigation repro. Bumped the JetBrains plugin build version to0.2.97and refreshed editor specs/tests for#jbsyncwarn. -
Assistant response tails committed in
HEADnow have explicit prompt-prefix regression coverage. The#pfxleak3repro was a narrower variant of thecontent_oursfallback 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 theHEADprefix-state invariant explicitly, and the write-path regression proves the tail remains bare while only the newdo [#pfxleak3]...prompt receives❯. -
Post-commit prompt-prefix repair no longer treats assistant
Commit / push:evidence labels as prompt targets. The#pfxleakcloseout 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, trippingsession-checkas an unstarted prompt. The plain-response classifier now recognizesCommit / push:before the genericcommit ...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_oursprompt-prefix normalization now preserves multi-line user prompts after stale inserted response blocks. The#pfxstrip2repro showed a stale snapshot keeping the normalizer in agent-response mode long enough to skip ordinaryPlease ...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-committedHEADprefix 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-checkno longer treats plaincontent_editdrift as an unstarted closeout after a committed cycle, so minor already-answered transcript edits do not force a second finalize. Thecontent_oursprompt-prefix fallback now preserves unprefixed exchange lines already committed inHEAD, 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.rsno longer converts the repeatedcommit_noopsubset of session-accretion into a mandatoryagent-doc compact ... --commitcommand, 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 intasks/agent-doc/agent-doc-bugs2.md. -
Dispatch-only Codex accepted-but-unproven failures no longer print an optimistic fallback line first.
route.rsnow suppresses the legacy "accepted but no proof" progress message whenRun Agent Docis 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 syncpreserves 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 thesafe_passive_layout_preserved_reselected_focusproof 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:exchangeand markdown section breaks. The latest#oobpromptrepro intasks/agent-doc/agent-doc-bugs2.mdwas narrower than the earlier escaped-response gap bug:repair/preflightalready fixed### Re:or## Assistanttails stranded before later components, but a bare prompt target such asdo [#id]...typed after<!-- /agent:exchange -->and before a plain###/## Pendingsection marker stayed outside exchange because the shared detector only keyed on escaped response headings.template.rsnow 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#oobpromptintasks/agent-doc/agent-doc-bugs2.md. -
Required SSH prelaunch probes now isolate themselves from shared SSH socket state.
agent::codexwas provingrequired_ssh_targetsby running realssh <target> truechecks 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#sshcutintasks/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.mdregression came from the new preserve-layout exits insync --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 theagent-doctmux window stuck on the old pane.sync.rsnow 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 intasks/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-tocto enumerate current live### Re:sections alongside matching archived response sections for the same document, andagent-doc response-fetchto load exact live or archived sections with bounded neighbors.prompt_context.rsnow includes that TOC in warn/block context packs and explicitly points agents atresponse-fetchfor 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#restocintasks/agent-doc/agent-doc-bugs2.md. -
Warn/block bounded context packs now anchor response history to the prompt's position in
exchangeinstead of always replaying the newest### Re:turns.prompt_context.rsnow 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#wv7gintasks/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.rsnow classifies Codex clean-exit restart prompts as an immediate dispatch blocker, routes healthy authoritative-actorRun Agent Docsubmits 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#fye2intasks/agent-doc/agent-doc-bugs2.md. -
Resumed required-SSH Codex streams now discard stale prelude text before the fresh retry.
agent::codexno 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#sshpreludeintasks/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.rsnow saves a blocked-stop diagnostic even whenlast_assistant_messageis 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 /authenticateflow 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 throughfinalize/write --commitplussession-check. This closes#257pintasks/agent-doc/agent-doc-bugs2.md. -
Session-accretion heuristics are now advisory only.
plan.rsno longer blocks normal turns on churn-heavy session metrics, andpreflight.rsno longer auto-compacts exchanges at all, including documents that still carry legacyauto_compactfrontmatter. 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 intasks/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.rsno longer auto-compacts template exchanges just because session-accretion heuristics tripped.prompt_context.rsnow 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#ratecmpintasks/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.preflightnow emits a structuredsession_accretionadvisory when those local heuristics trip, and the prompt-building path can still choose a bounded recent-context pack from that report, butplanno 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#ctxaccintasks/agent-doc/agent-doc-bugs2.md. -
Sync reconcile now preserves panes whose documents still have an open closeout cycle.
sync.rsnow re-enables tmux-router's DETACH protection only for panes whose registered document is still inpreflight_started,response_captured, orwrite_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#busychkintasks/agent-doc/agent-doc-bugs2.md. -
Stale empty
preflight_startedcycles now auto-close on the next preflight instead of trapping the document in manual recovery. The#stalefltrepro fromtasks/agent-doc/agent-doc-bugs2.mdshowed a narrow crash window where a pane could die afterstart_preflight()but before any response capture existed, leaving laterpreflightruns with an open cycle that had no replay artifact and no exact hash proof.repair.rsnow treats that shape as a bounded stale-empty-cycle case: if the cycle is stillpreflight_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#stalefltintasks/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.tomlmappings ([ssh.docs."<path>"],[ssh.profiles.<name>]), so known ops docs no longer bypass the required-SSH contract just because frontmatter omittedrequired_ssh_targets.preflight,plan,run,start, androutenow 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#sshmetaintasks/agent-doc/agent-doc-bugs2.md. -
Phase-4 authoritative actor dispatch is now explicitly closed out in spec and regressions.
route.rsalready switched normal reroutes onto the authoritative actor record and supervisor IPC in312851e, 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 proveblockedandclosedauthoritative 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#sgown4intasks/agent-doc/agent-doc-bugs2.md. -
JetBrains passive tab sync now trusts the selection event target instead of a potentially stale
selectedTextEditorsnapshot. The latesttasks/agent-doc/agent-doc-bugs2.mdrepro showed a narrower editor-side skip than the queued-replay race: when the user switched fromsampleportal.mdback toagent-doc-bugs2.md, JetBrains could enterselectionChangedwithevent.newFilealready updated whileFileEditorManager.selectedTextEditorstill pointed at the previous file. That made the automatic snapshot/dedup planner think focus had not changed and suppressagent-doc sync --no-autostartentirely.EditorTabSyncListenernow 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 to0.2.94and updated the plugin spec with the same callback contract. -
Explicit
repairnow fails closed when only later prompt drift remains after committed historical patchback recovery. The#rprdriftrepro intasks/agent-doc/agent-doc-bugs2.mdexposed a bad explicit-repair downgrade:repair::run()could legitimately find no pending/capture artifact, butagent-doc repairthen returnedNo pending response foundeven thoughsession-checkwould still interrupt the same committed cycle after repairing the snapshot fromHEADand 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
#mlreaprepro intasks/agent-doc/agent-doc-bugs2.mdshowedpending::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 inpending.rs, the backlog CLI integration, and preflight's live-prompt preservation path. -
Codex
UserPromptSubmitnow finds the realagent-doc <FILE>line after injected prompt preambles. The latest#rspcmtcloseout miss showed a direct Codexagent-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 theStophook had nothing to recover or block.codex_hook.rsnow scans the prompt from the end, skips fenced placeholder examples likeagent-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
#9aeptsift repro showed an inconsistency between agent-doc's backlog guards and its write-time pending mutations:cycle_stateandsession-checkalready normalized#id, butpending_cmd::done()and the lower-level pending ops still compared raw strings, soagent-doc finalize --pending-done '#9aep'failed withid not found in backlog/iceboxeven 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 forop_done,write --pending-done '#id', andfinalize --pending-done '#id'. -
Safe passive sync now locks the exact VS Code mixed-root split replay into spec and tmux regression coverage.
sync.rsalready preserved visible layout when a passive--no-autostartfile stayed blocked, but the coverage was still generic. The latest#vssplitreplaycloseout now names the concretetasks/agent-doc/agent-doc-bugs2.md+src/session-share/tasks/claudescore-3.mdsplit, 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 beforefinalize/write --commitcloses the session document. Added regression coverage inskill.rsso 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 compoundcommit + pushwork must exclude the active session document from any ordinary repogit commit, defer the session-doc closeout toagent-doc finalize/write --commit, and only push after that binary-owned closeout commit lands. Added regression coverage inskill.rsso 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.mdrepro showed one remaining race in rapidGo to Next Splittersequences: the plugin could queue a replay correctly, then rebuild its command from a later background-thread view ofFileEditorManagerand land tmux on the first splitter hop instead of the final one.EditorTabSyncListenernow 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 to0.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.mdrepro 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
writeno longer reports success after a synthetic/templatewrite_streamleaves closeout open. The historical BuildPartydev.mdrepro intasks/agent-doc/agent-doc-bugs2.mdshowed a narrower closeout gap than the earlier generic missed-commit family: the CRDT/template write path had already preserved the response, capture, and syntheticwrite_streamstate, but the command still looked successful until a later explicitagent-doc commitfinally recordedcommit_success.write.rsnow keeps that response/capture state for recovery but immediately fails closed when a real session doc uses bareagent-doc writeand the cycle remains open, soresponse_captured/write_appliedcan no longer masquerade as a completed turn. Added an integration regression that proves the bare stream write returns nonzero, preserves the syntheticwrite_appliedevidence, and still lets a later explicitagent-doc commitfinish the boundary. -
Answered-prompt closeout canonicalization no longer rewrites prior assistant tail prose into fake
❯prompts. The latesttasks/agent-doc/agent-doc-bugs2.mdrepro exposed an over-greedy commit-time heuristic ingit.rs: when a real answered prompt such asdo [#tailpatch]...shared one contiguous block with the previous response tail, the closeout canonicalizer could prefix the whole block and commit assistant prose likeThere 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 latesttasks/agent-doc/agent-doc-bugs2.mdrepro 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_statenow exposes a unified resolved-id set from both explicitpending_done_idsand same-cyclereaped_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:exchangeand later components. The latesttasks/agent-doc/agent-doc-bugs2.md#tailpatchrepro 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 oragent:backlogcould still survive closeout even though the document remained parseable.template.rsnow 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_linestarget is missing its❯prefix, both socket and file IPC fallback paths now run the target-based exchange prefix repair overcontent_oursafter preserving any on-disk backlog mutations. This closes the#bppfxstripshape where a sidecar-divergence fallback could still save a baredo #...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.mdregression was an editor-plugin convergence bug rather than a snapshot-classification miss: the binary already emittednormalize_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 treatedpatches: []as a pure boundary move even when the payload still carriednormalize_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.mdregression was not a one-sided snapshot bug:repair/preflightcould reap user-marked[x]items correctly, but a stale local/editor rewrite could put those ids back into the liveagent:backlogbefore the same cycle reachedgit::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_statenow records ids reaped during the active cycle,preflight.rsandrepair.rspublish those ids when they remove completed tracked work, andgit.rsnow 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-docwindow. The latesttasks/agent-doc/agent-doc-bugs2.mdpane-retention repro was not a normal editor sync failure: local verification was still callingsync_after_claim(...)with an injectedTmux, but the helper delegated tosync::run(...), which silently jumped back to the default tmux server. In practice that meant a route/unit-test reconcile using dummy files likefile_a.md/file_b.mdcould stash a visible sibling pane such assrc/session-share/tasks/buildparty-investor-demo/dev.mdout of the operator's realagent-docwindow, after which a normal sync would merely rescue it back.route.rsnow keeps that reconcile onsync::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.mdops-log evidence showed thatsession clearwas already succeeding viadelivery=direct_pane_submit, butroute --dispatch-onlycould still refuse the same pane withstill bootingbecausedispatch_only_send_reopen(...)ran an extra ready-probe loop before it was allowed to use that direct tmux submit path.route.rsnow 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 inroute.rs,queue_dispatch.rs, andparallel.rsto usesessions::send_submitted_text(...)instead of open-codedtmux.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
startingorbusy, and supervisor inject has a real socket-to-tmux regression. The latesttasks/agent-doc/agent-doc-bugs2.mdops-log evidence showed the remaining mismatch clearly: file-scopedsession clearwas already usingdelivery=direct_pane_submit, but prompt-bearingroute --dispatch-onlyafter clear could still take the authoritative actor's optimistic supervisor-IPC queue path whenever the actor runtime still reportedstarting/busy. That keptRun Agent Docon a different delivery boundary than the known-good clear path.route.rsnow keeps dispatch-only authoritative reroutes on the same live-panesend_submitted_text(...)helper even in that short starting/busy window instead of queueing through supervisor IPC, andstart.rsnow 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.mdrepro still left one structural mismatch: routed reopens, supervisor-owned injects, and file-scopedsession clearwere all supposed to share the same tmux submit boundary, but agent-doc still had multiple direct-pane wrappers andsession clearonly trusted the registry pane before dropping back to supervisor IPC.sessions.rsnow owns the canonical live-pane submit helper used by route, start/supervisor inject, andsession clear, andsession_actor_cmd.rsnow 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 soClear Session Contextfollows the same live-pane preference model asRun Agent Doc. -
Shared tmux submit now pauses briefly before
Enter, which fixes real Codex slash-command submits while preserving Claude behavior. The latesttasks/agent-doc/agent-doc-bugs2.mdinvestigation finally used isolated live harness panes instead of shell-loop stand-ins. That replay showed the currenttmux send-keys -l ... ; send-keys Enterhelper left/clearand/helpdrafted 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-doclogs the mode astmux_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
Enterpresses. The latesttasks/agent-doc/agent-doc-bugs2.mdrepro showed that agent-doc was still carrying multiple overlapping newline/CR workarounds even after the live-pane submit boundary had been unified.supervisor::ipcnow normalizes submitted command text once for tmux-bound injects, leaves raw\rencoding only for the direct PTY-writer fallback, and route/queue-dispatch no longer send follow-upEnterretries after the first tmux submit. That strips the accumulated defensive submit branches back to one literal-text-plus-Enter tmux path forRun 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
Entertmux submit path, and they log which delivery branch fired. The latesttasks/agent-doc/agent-doc-bugs2.mdrepro still showed drafted\nbehavior 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 namedEnterinstead of using the ASCIIsend-keys -H ... 0dfast path, soRun Agent DocandClear Session Contextcross the same live-pane submit boundary as the known-good Claude clear flow.route --dispatch-onlyand file-scopedsession clearnow 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 latesttasks/agent-doc/agent-doc-bugs2.mdrepro 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-paneroute --dispatch-onlyreroutes could still fall back to one-shot supervisor IPC injects.route.rsnow keeps dispatch-only reopens on the resolved live pane's tmux input path for both authoritative and non-authoritative existing sessions, soRun Agent DocandClear Session Contextshare 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.mdClaude repro showed a mismatch between two editor-adjacent flows: file-scopedsession clearcould still work because it only needed the live bound pane, whileroute --dispatch-onlyrefused 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.rsnow 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 clearnow submit straight to the live pane when the current binary already owns that pane boundary. The latesttasks/agent-doc/agent-doc-bugs2.mdrepro showed one stale-supervisor surface still left after the earlier Enter fixes: editorRun Agent Docand file-scopedagent-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.rsnow sendsroute --dispatch-onlyreopens directly through the authoritative pane's tmux input path once the actor-owned pane is ready, andsession_actor_cmd.rsnow sends/cleardirectly 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.mdEnter 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-inputsend-keysboundary.start.rsnow keeps supervisor IPC as the authoritative control surface but re-delivers submitted input through the claimed pane's tmux key path, soRun 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
/clearinto an editor-side restart. The latesttasks/agent-doc/agent-doc-bugs2.mdrepro showed that the shared Enter-submit fix had landed, butroute.rswas still applying the older tracked-/clearfresh-restart policy before everyagent-doc route --dispatch-onlydispatch. That madeRun Agent Docrestart Codex instead of sending the expected bareagent-doc <FILE>reopen into the live session. Dispatch-only reroutes now keep using the existing supervisor-owned submit path aftersession clear, while the managed non-dispatch route retains the tracked-/clearfresh-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 Contextrepro intasks/agent-doc/agent-doc-bugs2.mdexposed 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 clearand 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 thesession clearand 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.rsstill 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 directsend-keysreopen 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.mdrepro still draftedagent-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 reusedtmux-router's literal-text send followed by a separateEnter, 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 onetmux send-keys -H ... 0dcommand so the pane receives the full text plus carriage return as one stream. Non-ASCII payloads keep the old literal-text fallback. Addedtmux-routerregressions 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
startingorbusy. The latest JetBrainsRun Agent Docrepros intasks/buildparty-investor-demo/dev.mdandtasks/agent-doc/agent-doc-bugs2.mdwere failing earlier than the existing busy-pane optimism ladder: once route resolved a healthy authoritative actor,route.rsstill hard-bailed on supervisor statesstartingandbusybefore it ever tried the existing optimistic dispatch behavior. That meant a live pane that was still accepting keystrokes could reject reroutes withroute will not inject a new trigger because the authoritative actor is busyeven though the supervisor IPC path was available.route.rsnow allows one optimistic supervisor-IPC reopen for prompt-bearing reroutes while the authoritative actor isstartingorbusy, while keepingwaiting_input,blocked, andclosedfail-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.mdJetBrains repro was not a stale actor record: the durable store correctly recordedharness: claude-code, butroute.rsstill compared that value against the live supervisor binary nameclaudeand failed closed withbound to harness claude-code, not claude. Route now normalizes the live harness into the same canonical identity set used by.agent-doc/session-actors.jsonbefore 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 soclaudevsclaude-codestays 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 Docrepro intasks/agent-doc/agent-doc-bugs2.mdwas not just a stale-busy route policy issue: authoritative-actor reroutes and other supervisor IPC inject paths were still forwarding...\nverbatim, 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 inBusyand caused follow-up reroutes to fail closed against the same pane.start.rsnow normalizes supervisor-injected submit bytes (\nand\r\n) to\rbefore 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 fromtasks/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.rsnow 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 exactsession statusdisplay, actor-backedsession clearwiring, 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 andsession clearcommand wiring helpers directly. This closes#sgown9intasks/agent-doc/agent-doc-bugs2.md. -
Phase-8 now removes legacy owner election from the normal route/start/sync path.
route.rs,start.rs, andsync.rsnow 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#sgown8intasks/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.rsno longer runs hiddenrepair_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 openpreflight_started/response_captured/write_appliedcycle would have required repair. The corresponding repair work now lives on explicit surfaces:agent-doc repair <FILE>still owns document-cycle recovery, andagent-doc session doctor <FILE> --repairnow 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#sgown7intasks/agent-doc/agent-doc-bugs2.md. -
Forwarded
Ctrl+Dno longer has a committed-turn keepalive exception. The old pane-retention hardening still left a committed-cycleCtrl+Dpolicy branch and closeout probe instart.rs, even though the user-facing contract had already moved back to "show the quit menu."start.rsnow removes that lingeringctrl_d_committed_cycle_restart_freshpolicy path entirely, so stdin-forwarded EOF/Ctrl-D always reaches the canonicalEnter/qprompt, 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 intasks/agent-doc/agent-doc-bugs2.md. -
Forwarded Codex
Ctrl+C/Ctrl+Dnow always surface the quit menu instead of silently chaining fresh restarts. The latestagent-doc-bugs2.mdrepro was two separate policy bugs instart.rs: stdin-forwardedCtrl+Cwas still classified throughCrashPolicyas a transient non-zero exit before the quit-menu override could run, and stdin-forwardedCtrl+Dstill short-circuited toRestartFreshwhenever the previous run had already committed or had exited before surfacing a prompt.start.rsnow treats a forwarded operatorCtrl+Cas clean-exit policy input for supervisor bookkeeping, and any forwarded operatorCtrl+Dor terminatingCtrl+Cnow routes to the canonicalEnter/qprompt 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 latestCtrl+C/Ctrl+Drestart loop intasks/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.mdrepro was not another restart-policy misclassification: the quit menu itself still usedread_line()after restoring whatever stdin termios the parent harness originally gaveagent-doc, and some managed binding sessions left that inherited tty raw-ish enough thatEnterarrived as literal^Mbytes instead of terminating the prompt read.start.rsnow derives an explicit canonical prompt mode from the saved tty state before every restart/quit menu, re-enablingICANON,ECHO, signal handling, andICRNL/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 intasks/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 ExchangeandRestart Supervisor Processin the primary numbered popup, whileRun with JunieandForce Claim for Tmux Paneremain available from a non-numbered overflow path. The binary also exposes the explicitagent-doc session restart-supervisor <FILE>surface (withsession restartkept 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 sessionstill keeps the existing tmux-session pinning flow (session,session set, baresession clear), but it now also exposes actor-backedstatus,history,attach,restart, file-scopedclear, anddoctorcommands. 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.mdrepro was not another generic restart-policy failure:start.rsalready handled stdin-forwarded EOF/Ctrl-D on the clean-exit path, but a live paneCtrl+Cstill arrived asexit_kind=signal exit_signal="Interrupt"and fell throughCrashPolicyas a transient non-zero exit. That made the supervisor auto-restart after two seconds instead of offering the cooked-modeEnter/qchoice.start.rsnow tracks stdin-forwardedCtrl+Cexplicitly, 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 latestCtrl+Cquit-menu regression intasks/agent-doc/agent-doc-bugs2.md. -
Phase-1 single-owner session actor semantics are now pinned in spec and emitted in session logs.
agent-docnow documents a stable session-actor contract inspecs/08a-session-actor-contract.mdand starts writing monotonic ownership-generation provenance to.agent-doc/logs/<session>.log. Freshstartgenerations recordownership_transition ... prior_generation=... new_generation=..., and registry handoffs now include the same generation metadata on the transition, supersession, andsession_end origin=registry_rebindlines. Legacy logs still infer generation count from repeatedsession_startevents 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
#ctrldmenuregression intasks/agent-doc/agent-doc-bugs2.mdwas caused by an over-broad keepalive hardening:start.rstreated every forwarded stdin EOF/Ctrl-D asRestartFresh, which removed the cooked-modeEnter/qdecision path even when the child had already shown a real prompt and the operator was intentionally trying to quit.start.rsnow 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 asrestart freshrather thanquit. Added start-level regression coverage for the restored split strategy. This closes#ctrldmenuintasks/agent-doc/agent-doc-bugs2.md. -
Dispatch-only Codex reroutes now follow same-file restart handoffs before surfacing a stale
still bootingerror. The busy-pane recovery path could already trigger a fresh supervisor restart, but the finaldispatch_only_send_reopen(...)probe still treated the original pane as fixed and failed closed after the first 2s ready wait. In the live JetBrainsRun Agent Docrepros fortasks/claudescore-3.mdandtasks/sampleorders.md, that surfaceddispatch-only codex reopen refused ... still bootingeven 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.rsnow 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-pluginRun Agent Docfalse-refusal shape fromtasks/agent-doc/agent-doc-bugs2.md. -
Normal tmux turn paths now fail closed instead of killing panes or manufacturing duplicate stash fallbacks.
start.rsno longer auto-focuses, restarts, or supersedes another alive pane for the same document during ordinaryagent-doc start; it now errors with explicit tmux inspect/capture/kill commands so the user chooses the winner manually.route.rsalso dropped the "create then stash" fallback branches that could proliferate hidden duplicate panes whensplit-windowfailed or when anagent-docwindow already existed without a safe registered anchor. On the sync side, ordinary missing-pane recovery now keeps dead panes retained for diagnostics instead of callingtmux 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.rswas still callingregister_dispatch_target(...)before it had proven the candidate pane was safe to reuse for the requested file. In the live#jbpdroprepro, that lettasks/software/tsift.mdbriefly emitsession_superseded old_pane=%177 new_pane=%169even though%169was the authoritativetasks/agent-doc/agent-doc-bugs2.mdCodex 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 temporaryregistry_rebindthat later has to be undone. Added a regression that proves the original file keeps%169while the requesting file keeps%177. -
Committed Codex keepalive restarts now discard inherited pre-prompt
Ctrl-Dbytes instead of letting the fresh successor quit itself. The earlier pane-retention change correctly flipped committedCtrl-Dexits fromprompt_usertorestart_fresh, but the immediate successor run could still inherit the same rawCtrl-Dbyte before it ever surfaced a prompt. In the livesampleorders.mdrepro that producedctrl_d_committed_cycle_restart_fresh, then a second clean exit withctrl_d=true,ctrl_d_prompt_user, anduser_quit_after_ctrl_don the successor pane.start.rsnow suppresses stale pre-promptCtrl-Dbytes only for that keepalive successor, while still forwarding freshCtrl-Dnormally once the child has shown a real prompt. Added start-level regression coverage for the byte-filter helper. This closes#kpaneintasks/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-onlyintentionally skips the heavier ack/auto-fix machinery, but it was still treating any alive registered pane as immediately injectable. In the livesampleorders.mdchurn, that allowed a bare reopen to be sent to pane%175even though its latest session-log run was still justcodex_start mode=freshwith 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#samplerouteintasks/agent-doc/agent-doc-bugs2.md. -
Live
registry_rebindsuccessors now remain authoritative even when PID/process-tree provenance drifts.sync.rsalready usedsession_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 toregistered_pane_unownedand 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_rebindcloseouts once their successor pane is gone, while still honoring a live handoff pane.sync.rspreviously treated any latestsession_end origin=registry_rebind ...as a permanent--no-autostartblocker, even after the recorded successor pane had died or drifted away. That stranded mixed-root documents likesrc/sample-app/tasks/sampleorders.mduntil 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,cfor both manual sync and automatic tab-sync, even when the user had separate visible editor groups. In narrow sharedagent-docwindows 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--colper 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 latestclaudescore-3.mdpassive-stash gap fromtasks/agent-doc/agent-doc-bugs2.md. -
Codex now treats bare SSH
socket: Operation not permittedoutput 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 Codexcommand_executionevent likecommand: "ssh sampleorders-server true"withaggregated_output: "socket: Operation not permitted"leaked through as a raw task failure and skipped the fresh-retry path.agent/codex.rsnow inspects command-execution context: if the command itself proves SSH against a declaredrequired_ssh_targetsentry, 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#sshepermintasks/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 livesampleorders.mdrepro was no longer a sync/rebind ownership bug: pane%166completedcommit_success, thenstart.rssawctrl_d_forwarded, dropped into the quit prompt path, and loggeduser_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 committeddocument_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 latestsampleorders.mdpane-drop fromtasks/agent-doc/agent-doc-bugs2.md. -
JetBrains route failures now stay copyable after the first notification moment.
TerminalUtil.sendToTerminal()was still collapsingagent-doc route --dispatch-onlyfailures into a plain IDE error notification, which made startup-miss and pending-drift diagnostics effectively transient when the user launchedRun Agent Docfrom 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#jberrintasks/agent-doc/agent-doc-bugs2.md. -
Optional closeout sidecar reads now treat late
ENOENTas absence instead of a hard failure.session-checkand the closeout helpers were still usingexists()-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, bubblingNo such file or directory (os error 2)out of otherwise-valid closeout checks such assession_check_skips_pending_done_warning_when_id_was_recorded. Optional sidecar loads now read directly and downgrade onlyNotFoundtoNone, preserving other I/O failures while eliminating the transientENOENTflake. Added unit coverage for the shared optional-read helper. This advances#scenointasks/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.rsalready accepted genericagent-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 livesampleorders.mdreroute 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 newestsession_start, which in turn causedregistered_pane_missingon 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#mrreapintasks/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.rswas still re-readingsessions.jsonafter 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 JetBrainsagent-doc-bugs2.mdrepro this surfaced asfresh_route_dispatch_handoff ... fresh_pane=%144 dispatch_pane=%127, immediately superseding the new pane inside the sameagent-docwindow 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#jbpdropintasks/agent-doc/agent-doc-bugs2.md. -
Split the command spec monolith into focused sibling specs and added a reusable split runbook.
specs/07-commands.mdis now the stable command-spec index, while the normative detail moved intospecs/07-core-commands.md,specs/07-session-tmux-commands.md,specs/07-closeout-commands.md, andspecs/07-orchestration-commands.md. Addedrunbooks/split-spec-files.md, bundled it into installed harness runbooks, and documented the stable-index split rule plus the managed-vs-custom ownership boundary inCLAUDE.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.rsalready refused to reuse an alive registered pane without live-owner proof, but it could still fall through torepair_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 livesampleorders.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 explicitregistered_pane_open_session_log_owner ... action=fail_closedprovenance, 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.rsalready required live-owner proof before trusting an existing registered pane, but an alive pane that temporarily lost that proof could still fall through torepair_missing_registered_pane(...), record syntheticregistered_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 explicitregistered_pane_protected ... action=fail_closedprovenance, and blocks replacement for that cycle instead of emittingsession_end origin=sync_missing_pane. Added harness/sync regression coverage for drafted prompts, queue-state protection, and idle-placeholder non-matches. This advances#prreapintasks/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.rsnow reuses the same root-aware session chooser assync: a nested-repoagent-doc routewithout explicit window context honors the target file's own nearest.agent-doc/config.tomlpin, and a mixed-root editor layout prefers the shared workspace-root pin over the focused child root. This prevents JetBrainsRun Agent Docfrom auto-starting nested documents into the wrong submodule session when the visible split already proves a shared workspaceagent-docwindow. 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.rsnow letsagent-doc sync --no-autostartreuse 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.rsnow 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-autostartintentionally leaves a managed file unresolved, agent-doc now tellstmux-routernot to donate a same-column or spare visible pane to that file, preventingtasks/software/tsift.mdand similar selections from reusing the oldagent-doc-bugs2.mdpane. 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-autostartfrom donating a spare pane to the blocked file, but the reconcile phase could still collapse the sharedagent-docwindow down to whichever foreign pane remained resolved, effectively making that foreign pane authoritative anyway.sync.rsnow 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#jbsubrootmixed-root passive-sync replay fromtasks/agent-doc/agent-doc-bugs2.md. -
agent-doc syncnow reuses the recorded layout by default and re-normalizes tmux windows after reconcile.sync.rsnow reads and writes.agent-doc/last_layout.jsonfrom the resolved sync scope instead of blindly anchoring it to the caller's CWD, and a no---colagent-doc syncreplays that saved layout as its default input. Sync also runsrepair_layoutagain aftertmux_router::sync, then pushesagent-docback to index0and 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.rsnow derives its session pin from the visible document set's shared.agent-docroot 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 session4and session1. On the tmux side,tmux-routernow 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 session4stash could recreateagent-docunder the currently attached session1. 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
selectionChangedfor 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. AddedBounceBackclassification 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.rsalready triedrepair()before starting a replacement pane, but a replay failure such aspending/backlog patch changed non-list contentstill 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 theresponse_captured+ unsupported backlog patch shape behind the latestsampleorders.mdchurn. -
Busy-route progress logging no longer panics on Unicode prompt lines during live reroutes.
route.rswas 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 livesampleorders.mdreroute shape that previously crashed in the busy-pane replay. -
Passive
sync --no-autostartcan 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 leftdev.md,claudescore-3.md, and similar documents stranded after their last pane had already exited cleanly.sync.rsnow 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 theagent-docwindow. Passive sync still refuses that cold-start when the latest closeout is onlysession_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. -
claimnow treats normalized registry keys as document identity instead of mistaking them for session UUIDs. The session registry is keyed by canonical absolute file path, butclaim.rswas still comparing that key directly to the current document'sagent_doc_session. That made a document's own live pane look like a foreign claim and causedClaim for Tmux Paneon submodule-backed files such assampleorders.mdto provision a duplicate pane instead of reusing%75.claimnow 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:backlogblock 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 latestsampleorders.mdpane-loss shape wheresync_missing_pane_closeout_recoveryfailed onpending/backlog patch changed non-list contentand 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 instashand therefore missing from the visibleagent-doctmux window. Both plugins now keep multi-document visible layouts onagent-doc sync --no-autostart ...even when only the active tab changes, while single-document tab switches still usefocus. 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_targetsin 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 freshcodex 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 issuesagent-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_messageincludes plain progress commentary ahead of the patch body. The latest directagent-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 validpatch:exchange+patch:backlogcloseout.replay_guard.rsnow 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#adinv2fromtasks/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-docroot as theagent-doc syncworking directory, so focusingsrc/agent-doc/specs/08-session-routing.mdwhiletasks/agent-doc/agent-doc-bugs2.mdandsrc/sample-app/tasks/sampleorders.mdshared the screen made sync read/writesrc/agent-doc/.agent-doc/last_layout.jsoninstead 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.rshad drifted from the documented session-resolution contract and was resolving its target session as--window -> current session, which let an attached session like1take over even while.agent-doc/config.tomlstill pinned a live session0. Sync now shares the same precedence contract as route: explicit window/session context first, then live projecttmux_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 coveringagent-doc/stashoutcomes. -
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
syncand routed layout hints, so a workspace-root + submodule split could oscillate between the correct two-column absolute layout and a one-columnsampleorders.mdreport. That one-column report letagent-doc synclegitimately 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 to0.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.rsno 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 considersregistered_pane_missingreplacement 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#ownergapintasks/agent-doc/agent-doc-bugs2.md. -
Missing-pane sync recovery now reopens stranded closeouts before it starts another pane.
sync.rsalready loggedregistered_pane_missing/ dead-pane provenance, but it only self-healed stalepreflight_startedlocks. If the owning pane disappeared afterresponse_capturedorwrite_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_capturedreplays throughrepair+ strict closeout,write_appliedfinishes the missing commit boundary when the file/snapshot already prove the response landed, and the session log records explicitsync_missing_pane_closeout_recovery_*provenance before replacement starts. Added sync regressions for both the replayed-response and already-applied commit-boundary shapes. This advances#jbcapintasks/agent-doc/agent-doc-bugs2.md. -
JetBrains passive sync no longer reads
.agent-doc/sessions.jsonor live tmux state to decide window/autostart policy. The plugin now reports only absolute layout/focus file paths toagent-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 to0.2.87. -
Editor-driven tmux sync now has a non-destructive mode, and JetBrains startup no longer auto-runs
resync --fix.agent-doc syncnow 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 fromagent-doc resync --fixto a report-onlyagent-doc resyncaudit, 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#jbptrkguidance intasks/agent-doc/agent-doc-bugs2.md. -
JetBrains repeat
Run Agent Docclicks now supersede stale plugin-spawned route processes instead of waiting behind them. The editorSubmitActionalready stopped inferring "already running" from local state, but a previousagent-doc route --dispatch-onlyprocess 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 intasks/agent-doc/agent-doc-bugs2.md. -
JetBrains
Run Agent Docis 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 onRun Agent Doc" report intasks/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 earlierbuildparty-investor-demo/repo.mdfix taughtsession_check.rsand 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-leveldiff.rsclassifier still emitted that same tail as three freshprompt_targetblocks. That leftagent-doc preflight/agent-doc planfalsely 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, sopreflight,plan, route/session-check, prompt-prefix normalization, and write-path snapshot decisions all agree on the same actionable tail. Added a regression with the exactsrc/session-share/tasks/buildparty-investor-demo/repo.mdstale 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-onlyresolves the owning pane, sends the bareagent-doc <FILE>reopen, and returns without route-owned startup-miss gating,/clearrelaunch policy, busy-pane recovery, or cycle-ack waiting. JetBrainsRun Agent Docnow 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. Managedagent-doc routekeeps 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 Docno 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 +/clearno longer gets trapped in the editor before the CLI runs. On the backend,route.rsstill validates the target pane/file binding and still records startup-miss diagnostics, but once a live Codex pane for that file has accepted the bareagent-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/clearreroute blocker fromtasks/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.rsandroute.rsalready shared the "answered prompt below a stale boundary" detector, but it only recognized a later### Re:/## Assistantmarker. The latest JetBrainsRun Agent Docfailure forsrc/session-share/tasks/buildparty-investor-demo/repo.mdhit 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 ghostpending 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 bothsession-checkand 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 fromtasks/agent-doc/agent-doc-bugs2.md. -
Busy same-document Codex reroutes now probe
C-gbefore the generic interrupt closeout so reverse-history-search panes can recover instead of fail-closing.route.rsalready had the bounded same-document interrupt ladder (Escape+C-c) after scoped fix and fresh restart, but the latest JetBrainsagent-doc-bugs2.mdreroute 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 shortC-greadiness probe first, immediately reuses the pane when that clears a latentreverse-i-search/ history-search substate, and only falls back to the existingEscape+C-csequence when the probe does not restore readiness. Added a tmux regression that requiresC-gto recover the live pane and updated the route command spec. This closes the latest JB-plugin reroute failure fromtasks/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.rsalready ran the scopedagent-doc fix, one bounded fresh supervisor restart, and one bounded Codex interrupt recovery for theagent-doc-bugs2.md#selfrtfamily, but the final fail-closed error still reused the older pre-interrupttimeoutdetail even when the last readiness check had already proven a more specific blocker likereverse-i-search. The interrupt recovery path now returns structuredready / blocked / timed_out / skippedoutcomes, 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 ininteractive shell reverse-i-search. This closes#selfrtfromtasks/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.rsnow detects the narrow shape where the live document already contains a fresh### Re:/## Assistantblock that the snapshot lacks, but no pending/capture artifact or replayablelast_assistant_messagesurvived. 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#8zjhfromtasks/agent-doc/agent-doc-bugs2.md. -
Template write/repair now recover the duplicated
agent:exchangeclose-marker shape before failing on a stranded pending response.write.rsno longer stops at the rawclosing marker <!-- /agent:exchange --> without matching openparser 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, usestemplate.rsto 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.rsapplies the same canonicalization when fixing no-pending template drift, so the May 1claudescore-3.md#xguardfamily 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#xguardfromtasks/agent-doc/agent-doc-bugs2.md. -
Routed Codex submission-proof gating now respects nearer
.codexshadowing instead of assuming every workspace-root hook install is reachable from nested repos.route.rsalready learned to scan every tracked.agent-docancestor for.codex/hooks.json, but that was still too optimistic for child repos likesrc/session-sharewhen a nearer.codexpath existed as a file or hookless boundary. In that shape the live Codex pane never emitsUserPromptSubmitstate 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.codexwalk, and a new regression covers the nested-shadowing case. This closes#cs3intrfromtasks/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.rsalready 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#rbgapfromtasks/agent-doc/agent-doc-bugs2.md. -
Routed Codex submission proof now stays enabled across nested
agent-docroots when the workspace-level install owns the hooks.route.rsused the nearest.agent-docroot to decide whether hook-backed dispatch-start proof was available, which silently disabled the stronger "submitted/consumed" stage for child repos likesrc/session-shareandsrc/sample-appwhen only the workspace root had.codex/hooks.json. Route now scans every tracked.agent-docancestor for hook installation, matchingcodex_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#rbgapfromtasks/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.rsnow watches resumedcodex exec resume <id>responses for the specific local socket EPERM signature (Operation not permittedon127.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 freshcodex exec, and lets the fresh thread replace the savedresumeid instead of trusting the stale one again. Added blocking and streaming regressions plus spec updates. This closes#cxcdpfromtasks/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/clearreroute miss was not inroute.rs's path handling; it was in the supervisor clean-exit policy. A fresh/fresh-restart Codex child could exit0before ever surfacing an idle prompt, andstart.rswould still treat that as a healthy clean exit and chain--continue, which later collapsed intoauto_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#clrrtfromtasks/agent-doc/agent-doc-bugs2.md. -
Fresh-restart routed retries now preserve the original resolved reopen path instead of downgrading to
file.display().route.rsalready resolved routed triggers to an absoluteagent-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 resendagent-doc tasks/claudescore-3.mdinto asrc/session-shareCodex 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/clearreroute miss fromtasks/agent-doc/agent-doc-bugs2.md. -
Component parsing no longer panics when the non-agent comment preview lands inside a multibyte glyph.
component.rsnow bounds its fast-path<!-- ... -->preview on UTF-8 char boundaries before checking whether a comment is really anagent: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#utf8prepro shape and documented the valid-UTF-8 no-panic invariant inSPEC.md. -
Busy same-document Codex reroutes now get one bounded interrupt recovery before the final fail-closed error.
route.rsstill 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 bareagent-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 fromtasks/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.rsnow classifies busy Codex panes by reason, including interactive terminal substates likereverse-i-search, soroute.rsstops 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#snrunfromtasks/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.rsnow exposes the latest tracked prompt state for a document, androute.rsuses thatUserPromptSubmithook record as an explicit dispatch-start proof for bareagent-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#runsmfromtasks/agent-doc/agent-doc-bugs2.md. -
Tracked Codex
/clearreroutes now restart fresh before dispatch to preserve the original launch policy.codex_hook.rsnow exposes the latest tracked prompt for a document and flags an exact/clearas a capability-reset marker. Beforeroute.rsreuses an otherwise healthy live Codex pane, it now checks that marker and forces one fresh supervisor restart before injecting the nextagent-doc <FILE>reopen, so the originalcodex_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#clrprfromtasks/agent-doc/agent-doc-bugs2.md. -
Codex busy-pane reroute now gets one fresh-session retry when command acceptance never clears after
/clear.route.rsalready 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 forsend_command_checkedto 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/clearreroute regression fromtasks/agent-doc/agent-doc-bugs2.md. -
Direct
agent-doc runnow reuses the pending/backlog normalization gate fromwriteandrepair.run.rsno longer rejects a valid template response just because it still contains a legacypatch:backlogblock. The run path now normalizes backlog mutations beforereplace:pendingenforcement and reuses the same real-response-body proof as the other write paths, so a normalpatch:exchange+patch:backlogcloseout no longer dies early onreplace:pending block forbidden. Added a regression for the direct run template path and captured the remaining live validation astasks/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.rsnow reuses the same authoritativeHEADcleanup after a real git commit that it already used forcommit_already_currentno-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 committedHEAD, refreshes CRDT sidecars, and leaves the owning repo worktree clean. Added regression coverage for the real-commit path. This closes#cs3turnfromtasks/agent-doc/agent-doc-bugs2.md. -
Codex idle-placeholder readiness is now structural instead of a three-string allowlist.
harness.rsnow 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@filenameormy 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#cdxidlefromtasks/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.rsnow treats it the same as the previously-known idle suggestions, soroute.rsno 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 livesrc/session-share/tasks/claudescore-3.mdreroute miss recorded intasks/agent-doc/agent-doc-bugs2.md. -
Busy-pane supervisor restarts now wait for authoritative handoff before retrying route.
route.rsstill 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.rsstill creates and registers a fresh pane before launchingagent-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.rsnow 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 onsrc/session-share/tasks/claudescore-2.md, which closes the#wcrprepair gap intasks/agent-doc/agent-doc-bugs2.md. -
Parallel tmux full-suite routing/sync regressions now pin per-document registry roots instead of ambient
cwd.route.rsnow looks up split-anchor panes from the target document's own.agent-docproject root,sync.rswrites 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#rtanchfull-suite flakes fromtasks/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 changedcwd. Added/strengthened full-suite regression coverage via the existing route and cross-root sync tests. -
Manual
startnow clears dead stash registrations instead of fail-closing behind them forever.start.rsstill 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 astashwindow, 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,startderegisters that stale stash binding and claims the current pane. Added stash-specific regression coverage and updated the start command spec. This closes the latestagent-doc start ... still alive but no live owner was provenrepro fromtasks/agent-doc/agent-doc-bugs2.md. -
JetBrains
Run Agent Docnow 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 spawnsagent-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 to0.2.82. -
Busy same-document reroutes with pending prompt drift now fail closed instead of reporting false success.
route.rsstill 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 latestRun Agent Docfalse-success shape fromtasks/agent-doc/agent-doc-bugs2.md. -
Full-suite verification is now explicitly fail-closed against "unrelated" or "flaky" waivers. The bundled
agent-docskill andSPEC.mdnow 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 installnow resolves the siblingagent-kitcrate without manual patch flags.src/agent-doc/Cargo.tomlnow pinsagent-kitwith bothpath = "../agent-kit"andversion = "0.4.0", socargo install --path src/agent-doc --forcefrom the workspace root andcargo install --path . --forcefromsrc/agent-docno longer fall back to the older crates.io copy that lacksagent_kit::skill. Added a regression test that locks this manifest contract. -
audit-docsinstruction discovery now prunes heavy skip dirs before descending.agent-kit::audit_common::find_instruction_files()no longer uses raw recursive globbing forsrc/**/...,.claude/**/..., or.agents/**/...matches. It now walks those trees explicitly and stops descent as soon as a directory name matchesAuditConfig.skip_dirs, so audit runs skip vendored/cache subtrees likenode_modules,.venv,target,.git,vendor,.next,dist, and similar directories instead of traversing them first and filtering later. Added direct discovery coverage for skippedsrc,.claude,.claude/skills/**/runbooks, and.agentsdescendants. -
Fresh routed auto-starts now rebind their own new pane immediately before the first guarded trigger dispatch.
route.rsstill 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 firstRun Agent Docattempt still succeeds instead of failing withroute dispatch target ... is not registered. -
No-op
commit_already_currentcloseouts now refresh CRDT/editor sidecars when they rewrite live drift back toHEAD.git.rsstill 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#6bttparity gap fromsrc/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 barecompact 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.rsnow treats a non-artifact user block appended immediately before<!-- /agent:exchange -->as aprompt_target, sosession_check.rsandroute.rsno longer drop editor-added follow-ups like "When I runRun Agent Docon this document...nothing happens..." just because they are plain prose below a stale boundary. This closes the latestagent-doc-bugs2.mdJetBrains 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.rsnow validates the final Codexsend-keyspayload 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-secondno new document cycle startedstartup-miss. Added direct guard coverage plus a live-child regression that keeps thecontent_editreroute path on the same bare reopen contract. This hardens thesampleorders.md/claudescore-3.mdfailures recorded intasks/agent-doc/agent-doc-bugs2.md. -
repair/preflightnow deterministically move stale boundaries past already-answered turns. When a template document has no pending capture to replay but still shows a staleagent:boundarymarker above a prompt/response pair that is already complete,repair.rsnow 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 letspreflightcommit 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#bdrycfromtasks/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 changesor› Find and fix a bug in @filenameabove the footer.harness.rsnow treats those observed placeholder lines as idle chrome instead of drafted user input, soroute.rsno 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 latestRun Agent Docfailures fromtasks/agent-doc/agent-doc-bugs2.md. -
JetBrains now exposes a first-class
Fix Documentaction for tracked markdown sessions. The plugin addsFix Documentto the popup, Tools menu, editor context menu, and project view context menu, and it runsagent-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 whenRun Agent Docsurfaces a recoverable session/layout issue. Bumped the JetBrains plugin build version to0.2.81. -
Busy live-pane reroutes now auto-apply the scoped fix path once before the final fail-closed error.
route.rsno 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 asagent-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 intoresume --last, because that could just resurrect the prior unrelated task and keepRun Agent Doctrapped 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 JetBrainsRun Agent Docrepro fromtasks/agent-doc/agent-doc-bugs2.md. -
Answered tails below a stale boundary no longer masquerade as new pending work.
session_check.rsnow suppresses the oldest prompt-bearing change when the current exchange tail already contains that prompt below the lastagent:boundarymarker and a real### Re:/## Assistantblock later in the same tail proves the turn was answered.route.rspicks up the same shared detector through its routed-cycle-ack gating, so reruns no longer wait 30 seconds forpending 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#bdrycshape fromtasks/agent-doc/agent-doc-bugs2.md. -
Busy live-pane reroutes now only fail closed when there is real document drift to dispatch.
route.rsstill refuses to injectagent-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 thetasks/software/corky.mdJetBrains repro fromtasks/agent-doc/agent-doc-bugs2.md, where an already-active session blockedagent-doc routeeven 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.rsno longer treats› some drafted text/> some drafted textas an idle dispatch target, so a live Codex pane must expose a truly empty composer before route injectsagent-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 JBclaudescore-3.mdstartup-miss shape fromtasks/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.rsnow strips YAML frontmatter before classifying unresolved prompt-bearing changes, so harmless metadata churn such asagent: codexno longer surfaces ascontent_editand forcesrouteto wait 30 seconds for a cycle that never needed to start.route.rspicks up the same body-only behavior through its shared pending-change lookup, which closes the JBclaudescore-3.mdfailure fromtasks/agent-doc/agent-doc-bugs2.mdwhere a routed Codex reopen could fail closed onpending content_edit: agent: codexdespite there being no new user prompt in the document body. Added direct regression coverage in bothsession_check.rsandroute.rs, and updated the backend/routing specs. -
replace:iceboxnow parses as a real template patch instead of falling through to exchange.template.rsnow accepts<!-- replace:icebox -->...<!-- /replace:icebox -->alongside the existingpatch:iceboxform, so skill closeouts can rewriteagent:iceboxthrough the binary-owned patch path without tripping the0 template patches foundwarning or dumping the list body intoexchangeas unmatched content. Updated the skill/runbook text and added parser + write regression coverage for the#iceboxpatchshape fromtasks/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.rsnow inspects the prompt tail that lived below the previous boundary marker, matches pending ids referenced by the newpatch:exchangeresponse, 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#pbordshape fromsrc/sample-app/tasks/sampleorders.md, where a newer#wcx1status reply could land ahead of an older unresolved#wcup1prompt 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.rsno 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.rsnow 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 fromHEADwith no open/recoverable cycle now aborts preflight immediately.session_check.rsalso names tracked side-effect files and prints the exactagent-doc write --commit <FILE>follow-through command in those failures. This closes the#codcommitshape fromsrc/session-share/tasks/claudescore.md, where a Codex-side direct patchback plusnews/README.mdedits 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:todowould drop checklist items from an existing todo component.write.rscounts Markdown checklist rows in the liveagent:todobody and rejects any replacement patch whose new body contains fewer checklist items than the current component. This closes the#ptdrshape fromsrc/session-share/tasks/claudescore.md, where a partial Phase 1 todo patch silently deleted the rest of the backlog sections becauseagent:todostill 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.rsnow 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#lvlsshape fromsrc/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 inprompt_contract.rs,run.rs,stream.rs, andorchestrate.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.rsno longer saves rawcontent_oursby itself. Both IPC success paths now splice the current on-disk backlog/pending component back into that fallback snapshot first, sofinalize --stream --pending-addcannot silently drop earlier pending mutations just because editor-side normalization diverged. Added regression coverage for the#splpendshape fromtasks/claudescore-3.md. -
Strict retry dedup now adopts already-present template responses before the no-edit fast path.
write.rsnow checks for an already-visible response block before taking thecontent_current == baseshortcut across the template closeout/replay paths, so afinalize/write --commitretry 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#duppbshape fromtasks/agent-doc/agent-doc-bugs2.mdwhere 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.rsskips auto-start for a document because a startup-miss marker or repeated recentmissing_panerecovery 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 nearestsessions.json, sync prunes it instead of immediately writing the geometry-only assignment back. This closes the livesrc/session-share/tasks/claudescore-3.mdshape fromtasks/agent-doc/agent-doc-bugs2.md, where%261could keep being rebound toclaudescore-3.mdeven 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.rsnow filters the per-run session-id registry it builds for tmux-router so one stale pane cannot stand in for bothsrc/session-share/tasks/claudescore.mdandsrc/session-share/tasks/claudescore-3.mdduring 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.rsnow 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 theagentic-harness-engineering.mdshape fromtasks/agent-doc/agent-doc-bugs2.md, wheresrc/session-share/.agent-doc/sessions.jsoncould 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.rsnow refuses to downgrade an already-committed cycle back toresponse_capturedorwrite_appliedwhen a later repair/replay path touches the same cycle.run.rsnow also opens a freshpreflight_startedcycle after its pre-commit boundary so the current response closeout does not inherit that older committed state. This closes the#stphkshape fromtasks/agent-doc/agent-doc-bugs2.md, where a post-commitrepair_appliedevent could leave the cycle-state file open even though the capture ledger and session log already provedcommit_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 tocontent_ours,git.rsnow 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.rsalso upgrades the live-listener path to send a zero-content IPC patch carryingnormalize_prefix_linesplusreposition_boundarywhen 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.rsnow gives fresh auto-starts the same longer start-ack budget as routed live-child dispatches (30s in production, 2s under tests), so the firstRun Agent Docattempt no longer fails closed when the initial pane immediately recycles throughsync_missing_pane/startup recovery before the firstpreflight_startedbecomes 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.rsnow synthesizes a temporary tmux-router registry from each visible document's own nearest.agent-doc/sessions.jsonbefore reconcile. This closes theagent-doc-bugs2.md/claudescore-3.mdregression 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. -
startnow reuses alive session-log owners before fail-closing stale live-pane recovery.start.rsnow consults the owning session log's latest still-open pane as an extra provenance source wheneversessions.jsonpoints at an alive pane but current live-owner proof is missing. If that latest-open pane is still alive,startfocuses and reuses it instead of falling straight into the "supervisor unavailable, no live owner proven" fail-closed path. This closes the#asfcrepro fromtasks/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.rsnow 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 theagent-doc-bugs2.mdmixed-root layout collapse wheretasks/agent-doc/agent-doc-bugs2.mdandsrc/session-share/tasks/claudescore-3.mdcould both believe%75was their live owner, causingagent-doc syncto 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.rsnow kills unregisteredremain-on-exitpanes in non-stash windows during both automaticprune()and explicitresync --fixcleanup, but still preserves the last pane in a window for manual inspection. This closes the latentPane is deadclutter reported fromtasks/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.
LayoutDetectornow groups visible editor windows by their actual x/y bounds instead of assumingFileEditorManagerEx.windowsis left-to-right stable. This closes theagent-doc-bugs2.mdrepro 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, andresync.rsnow re-register recovered panes through the owning tmux handle and restore authoritativesupervisor_pid + supervisor_instance_idwhen 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-livedroute/syncprocess 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.rsno 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-syncsessions.jsonupdates now all canonicalize the document path, resolve that document's nearest.agent-docroot, and read/write the registry there. This closes the cross-repo layout bug wheretasks/agent-doc/agent-doc-bugs2.mdcould borrowsrc/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 assrc/session-share/src/session-share/...in the child registry. Added regression coverage for cross-root registry resolution and per-root sync registration. -
sessions.jsonis now path-keyed and live-owner proof is top-down by default.sessions.rsnow normalizes the registry around canonical absolute document-path keys, keepssession_idin the value, and records asupervisor_instance_idalongside the supervisor PID.start.rsstamps that supervisor identity into the registry and exposes it over IPC, whilesync.rsnow treatspane + supervisor PID + supervisor instance idas 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.rsnow 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-checkinterrupts so the Stop hook can recover fromlast_assistant_messageor block the turn. Added regression coverage for both the directsession-checkguard and the Stop-hook auto-close path, and updated the command spec. -
Open session-log provenance now blocks halted-supervisor rebinds in
start.start.rsno longer treatsstate="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 orsession_end. In that stranded-owner shape, manual/editor-drivenstartnow fails closed instead of emitting anothersession_superseded ... origin=registry_rebindpane 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.rsnow appliesnormalize_prefix_lineswhen IPC has to synthesize an append-modeexchangepatch from raw unmatched content, not just for explicit patch blocks. That closes the remaining JB-plugin shape where a prompt-bearing line such asdo #expatch. spec-test-build-install-commit-pushcould 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#expatchshape. -
agent-doc patchnow replaces component bodies by default, even on append-mode exchange docs. The standalonepatchsubcommand no longer inherits a component's configuredpatch=append/patch=prependmode as an implicit behavior change. Bareagent-doc patch <FILE> exchange ...now replaces the paired-marker body as the command synopsis promises, which fixes the#expatchrepair path where exchange restores duplicated history instead of overwriting it. Intentional cumulative edits still exist behind the explicit--mode append|prependescape hatch. Added CLI/unit regression coverage and updated the command spec. -
Alive stale-owner panes no longer get silently replaced when
startloses ownership proof.start.rsnow 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. Whenstartdoes 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 normalsession_superseded/session_end origin=registry_rebindprovenance 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_jsonnow 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 whereagent:exchange/backlog content stayed visually flat and tag-like labels looked like malformed references. -
JetBrains plugin version bumped to
0.2.79for the latest local-testing build. Updatededitors/jetbrains/gradle.propertiesso the nextbuildPluginartifact 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 thepreserve_headboundary 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#qprxshape 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.rsnow treats a prompt-time stdin EOF asrestart freshinstead ofquitwhen Codex clean-exits immediately after a fresh pane start and theCtrl-D/EOF prompt fires inside the early-start grace window. That closes thesampleorders.mdrebind-churn shape where a transient tmux stash/rescue input race could look likeuser_quit_after_ctrl_d, close the claimed pane, and trigger%546 -> %550 -> %552replacement 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.rsno longer reports a genericpreflight_startedinterruption when the working tree already contains a fresh### Re:block thatHEADstill does not prove. That shape now surfaces as a manual-repair / commit-boundary interruption with a concreteagent-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.rsnow re-runs exchange prompt-prefix normalization on the final merged template/CRDT document, not just oncontent_ours, so a concurrent baredo #...line cannot survive the merge and trip post-commitsession-checkafterfinalizealready committed the response. When a manualwrite --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.rsexposes 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.rsnow 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.rsnow 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#rbretshape where returning to an already-running document could cascade%529 -> %533 -> %536 -> %540registry rebinds and look like a tmux-pane crash even though the session log showed onlysession_superseded/session_end origin=registry_rebindprovenance. 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_endevents.startup_miss.rsno longer treats only a bare literalsession_endline as proof that the latest pane era closed. Session-log analysis now closes the latest run/session whenever the event token issession_end, even if recovery metadata follows (for examplesession_end origin=registry_rebind ...orsession_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#tmuxcrashforensics path. Added regression coverage for metadata-bearingsession_endparsing and updated the session-log spec. -
Session logs now record document closeout phase transitions alongside harness/pane provenance.
cycle_state.rsnow appendsdocument_cycle phase=... cycle=... event=...entries to the owning.agent-doc/logs/<session>.logwhenever a session document crossespreflight_started,response_captured,write_applied, orcommitted. That puts the document closeout boundary in the same timeline as*_start,*_exit,supervisor_exit, and dead-pane diagnostics, so#tmuxcrashforensics 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-panemoves. Fresh panes provisioned for agent-doc sessions now enable tmux pane-localremain-on-exitinstead of setting the option on the original window. That means a Claude/Codex pane moved into a stash window still retainspane_dead_statusand visible tail output if the harness exits while stashed, closing the#stshroepath 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.rsno longer flattens every harness exit into a bare*_exit code=<n>line. The session log now recordsexit_kind, signal name when applicable, and the rendered exit status text on both*_exitandrestart_eval, and the supervisor now appendssupervisor_exit reason=...immediately before the finalsession_end. This keeps true#tmuxcrashforensics 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.rsno 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 wheresrc/session-sharepanes were stashed out of the sharedagent-doctmux window, then incorrectly killed as "unregistered" by a root-workspace prune pass, which forced repeatedsync_missing_paneauto-start loops for documents likedocs.md,claudescore.md, andclaudescore-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.rsnow summarizes recentsupervisor_exit code=missing_paneevents from the session log, keyed by document session, and bothroute.rsandsync.rsconsult 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.rsnow treats a same-UUID re-registration onto a different pane as a provenance boundary: beforesessions.jsonoverwrites the binding, it best-effort appendssession_superseded old_pane=... new_pane=...andsession_end origin=registry_rebind ...to the existing session log. That keeps crash/recovery forensics from showing an old pane as forever-open whenroute,sync, orstartmoved 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.rsandroute.rsno longer collapse supervisor statehaltedinto the generic "restartable" bucket. Explicitagent-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.rsnow 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.rsnow narrows process-tree ownership proof soagent-doc route <FILE>/claim <FILE>utility invocations do not count as associated document panes; only the long-livedagent-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 runningroutewas 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.rsnow enable tmux pane-localremain-on-exit, andtmux-routernow treats retained dead panes as dead rather than alive so route/sync do not accidentally reuse them. Whensync.rsreplaces a registered pane that has died, it now captures tmux's retainedpane_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 syntheticsupervisor_exit/ stale-preflight repair before replacement.resync.rsnow 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 bothtmux-routerandagent-docfor 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 baredo ...directly above the same response block).commit/session-checknow compare snapshots with the same exchange-only normalization, repair the stale snapshot from committedHEAD, 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.rsnow parses multiline--pending-editpayloads 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 bothpending.rsandpending_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.rsno 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 theagent-docwindow; 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.rsnow detects the stale-marker shape where the persisted miss still points at an older pane, butsessions.jsonand the session log already prove a newer open start on a different registered pane for the same document.route.rsclears that stale marker before reuse/restart decisions, andsession_check.rsnow 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.rsbackfill 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.rsnow re-runs that canonicalization after granular edits/adds/state transitions so--pending-editcan 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.rsno longer reasons only fromsession_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.rsnow 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 laterfresh_restartas superseding the old miss. -
Routed startup-miss errors now surface the recorded timestamp and stop clearing unresolved live-pane misses.
route.rsnow appends the persisted startup-miss timestamp to the fail-closedno new document cycle startederror 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.rsno 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.rsnow records explicitsupervisor_exit code=missing_paneprovenance in the owning session log and repairs a stalepreflight_startedcycle 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.rsnow carries the first unresolved prompt-bearing change text alongsideagent-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.rsnow 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.rsnow 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 fromsrc/sample-app/tasks/...can still commit insidesrc/sample-app/src/sampleorders-devwithout tripping a misleadingindex.lockpermission failure on the real gitdir under.../.git/modules/.... Added regression coverage in bothgit.rsandagent/mod.rs, and updated the config/command/git specs to document the deeper writable-root set. -
#agent-doc-bugcloseout now proves that the requested plans were actually created.prompt_contract.rsnow detects preset-expanded "create a plan" requirements,preflight.rspersists the required plan-reference count in cycle state, andwrite.rs/session_check.rsnow 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.rsnow treats lines like⏵⏵ ... (shift+tab to cycle)as a valid Claude prompt shape, androute.rshas regression coverage provingwait_for_agent_ready()no longer misclassifies that newer idle UI as a busy pane. This fixes routedRun Agent Docfailures 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.rsnow 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 fromctrl_d_prompt_userstraight to anothercodex_startwith 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.rsnow enumerates every pane that still proves ownership of a document via process-tree or supervisor-PID evidence,route.rsonly auto-picks a winner when that evidence is decisive (single owner overall, or single active-window owner with only stashed duplicates), andresync.rs/fixnow re-register a unique winner before generic issue cleanup. Scopedfix <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-routerto the siblingsrc/tmux-routercheckout 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 ofagent-doc, andsessions.rs/ related helpers now delegate reusable session/key primitives totmux-routerinstead of carrying their own shell-level copies. -
Added
agent-doc fixas the canonical session-repair surface, with document-scoped targeting.main.rsnow exposes a top-levelfix [FILE]command, whileresync --fix [FILE]routes through the same implementation.resync.rsnow 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, andspecs/07-commands.md. -
Preflight no longer swallows prompt-bearing status edits into step-2 OOB absorbs.
git.rsnow rejects safe-status snapshot absorbs when the inserted status text contains prompt work, including preset-token leads like#next-stepsand#next-steps ..., imperative directives, or other prompt-bearing lines. That keeps compact-follow-up status edits visible topreflightstep 4 instead of letting step 2 commit them as prior-cycle out-of-band status churn and collapse the turn tono_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.rsnow parses the owning session log for the latest live harness run in the session,route.rslogs 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_endwas ever recorded.session_check.rsnow 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-bugcloseout now proves the full transferred bug set, not just target drift.prompt_contract.rsnow derives a minimum explicit transfer count from the prompt-bearing bug report itself,preflight.rspersists that count in cycle state, andwrite.rs/session_check.rsnow 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-buginventory contract. -
Explicit backlog-target closeout now proves every newly promised
[#id]landed.preflight.rsnow snapshots the baseline open-item ids for each prompt-contract target named byAdd to the backlog of ..., andwrite.rs/session_check.rsnow 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,finalizeandsession-checkfail 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.rsnow renders fresh-start and routed-trigger startup-miss notices through a tmux-owneddisplay-messageoverlay instead of draftingecho '...'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 draftedechotext 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.rsnow 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 intoexchange/outputbefore the strict closeout can proceed. Emptypatch:exchangeshells, frontmatter-only payloads, or normalization-only responses therefore fail beforeipc_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.rsaddsagent_doc_collaboration: sharedplusagent_doc_security_review: <review-id>,extract.rsnow blocks cross-documentextract/transferwhen a shared source or target lacks that review marker, andplan.rsnow blocks shareddo #idwork when the referenced backlog/icebox item points at another.mdplan 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.91and VS Code0.2.11. -
Editor plugins now visually distinguish agent-doc markdown structures from ordinary prose.
syntax.rsadds a shared Rust token scanner exposed through the newagent_doc_visual_tokens_jsonFFI 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_changesdirect-entry turns.codex_hook.rsnow persists the last CodexUserPromptSubmittext alongside the tracked document, andpreflight.rs/plan.rsnow consume that harness prompt body (or an explicitAGENT_DOC_HARNESS_PROMPToverride) when the document itself has no diff. The binary strips the leadingagent-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, ordo #id ...no longer collapse tono_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 existingdo #idresolution. -
Backlog and icebox now support ordered parent items for explicit priority.
pending.rsnow recognizes flush-left1. .../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.rsalso now lets legacyremove/prunehelpers 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.rsnow 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.rsnow moves those nested blocks with their parent during selective--itemstransfers. Updated the pending spec/runbook text and added regression coverage for nested parsing, reorder, transfer, shadow detection, and archive preservation. -
do #idcloseout now treats icebox items as tracked work.session_check.rsandwrite.rsnow enforce missing---pending-doneagainst still-open ids from bothagent:backlog/ legacyagent:pendingandagent:icebox.pending_cmd.rsnow resolves--pending-done <id>in either tracked list surface,preflight.rsreaps completed icebox items through the same snapshot/archive closeout path as backlog items, andplan.rsemitsresolve_existingfordo #iddirectives 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.rsnow parses backlog bodies with non-item lines intact, so markdown headings and blank separators insideagent:backlog/agent:iceboxsurvive backfill, reap, add, done, edit, clear, reorder, gate, and resolve operations instead of being dropped between the first and last bullet.write.rsalso 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:iceboxas a first-class tracked list surface.extract.rsnow accepts--itemsforiceboxas well asbacklog/legacypending, 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.rsnow replaces a fullexchangecompact with a default### Session Summarythat includes the archive pointer plus concise state from the liveagent:backlog/agent:pending,agent:queue, andagent:iceboxcomponents whenever no custom--messageis supplied. The bundled compact-exchange runbook and command spec now also direct agents to treat those components as the canonical compaction inputs, withprompt_presetslimited 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.rsnow 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.rsandroute.rsuse that wrapper directly, so malformed frontmatter fails closed with actionable feedback.sync.rsnow logs the same contextual warning during file resolution and auto-start, mirrors it into the document'sagent:statuscomponent 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.rsno 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.rsnow 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. -
claimnow rejects live cross-session tmux mismatches unless--forceis explicit.claim::run()no longer logs and proceeds whencross_session_decision()resolves toReject. 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 reapand stale-completed-itemrepairnow 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.rspath matching no longer requires the runningagent-doc start <file>argv to contain the exact registry string. When a submodule-hosted pane starts with a narrowed path liketasks/docs.md, root-level ownership proof for the same document now still matches the longer superproject form such assrc/session-share/tasks/docs.mdby normalized path-component suffix. This closes the falseNoLiveOwner/ stale-deregister shape that appeared in rootresyncoutput 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, andsession-checknow share a narrow self-heal path for openresponse_captured/write_appliedcycles and log-only write-complete/no-commit tails: whenHEADalready 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 asstatus/ 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 acrossgit,repair,session-check, andpreflight. -
Completed-backlog repair no longer lets preflight swallow a live prompt into
no_changes. Whenrepairreaps 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 nextpreflightdiff and prevents the#nodiffswallowshape where a plain prompt inserted beforeagent:boundarycould survive in the file but disappear behindno_changes: true. Added regression coverage for both the directrepairpath and the fullpreflightcloseout path. -
Strict post-write closeout is now shared across
run,finalize,write --commit,repair, and the Codex Stop-hook.write.rsnow exposes one binary-owned helper that runsgit::commit(), requires the cycle state to be closed, retries once when the snapshot still differs fromHEAD, and then enforcessession-check.run.rs,repair.rs, andcodex_hook.rsnow use that same helper instead of weaker ad hocgit::commit()paths. This closes the#patchregrfamily where a response path could look successful after commit/no-op closeout without proving the same post-commit invariants asfinalize. Added regression coverage for the already-committed-plus-later-prompt-drift shape. -
Bare
compact exchangedirectives now fail closed unless the binary compaction path is used.run.rsnow rejects a pending diff that contains a directcompact exchangerequest instead of sending a normal agent-response cycle.write.rs/finalizeapply the same pre-write guard against unresolved compaction directives, andplan.rsnow emits aCompacthandoff withagent-doc compact <file> --commitinstead of a misleading finalize placeholder. Added regression coverage for both therun/writeguards and the new plan handoff. -
Route now reuses or restarts the registered pane via supervisor health before spawning a fallback session. When
route.rscannot 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 arestartIPC 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.rsnow persistscontent_oursinstead of merged diskfinal_contentonly when an explicit--baseline-filewas supplied and the live file diverged during the response merge. That keeps user edits pasted whilefinalizeis 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.rsnow 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 updatedspecs/07-commands.md. -
resync --fixnow preserves active bound panes even when session/window cleanup heuristics disagree.resync.rsnow requires the live-owner proof to resolve back to the registered pane itself; if another pane owns the file, the registration is treated as staleNoLiveOwner. When the registered pane does still prove live ownership,WrongSession/WrongWindowfix paths preserve that active bound session instead of killing or stashing it based only on foreground-command or layout heuristics. Updatedspecs/07-commands.md. -
Stabilized the remaining tmux readiness/full-suite regressions. The
routeprompt-readiness tests now wait for an actual idle shell before injecting their mock agent, and theresyncwrong-window tests no longer depend on process-global cwd or fixed sleeps for pane/window relationships. This keeps the parallelcargo testsuite deterministic without changing runtime command behavior. -
startno 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 afterstarthas already ruled out successful reuse/restart of an existing live owner pane. This closes the cross-session bug where invokingagent-doc start <file>from another tmux session couldjoin-panethe 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-checknow verifies that the current snapshot matchesgit 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-checkexits1with a specific diagnostic.finalizeretries the commit once before handing off tosession-checkwhen it detects this mismatch. Additionally,commitnow updates the parent submodule pointer even during no-op (commit_already_current) cycles when the pointer is stale. Addedverify_snapshot_committed()andis_submodule_pointer_stale()togit.rs, the snapshot-committed guard tosession_check.rs, retry logic towrite.rs, and 6 regression tests. -
Backlog-replay guard detects open items silently dropped from recent history.
preflightandsession-checknow compare the current document's backlog against the pre-cycle baseline (.agent-doc/baselines/, falling back togit 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'spending_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. Addeddetect_dropped_from_history()detector inpending.rs, guards in bothsession_check.rsandpreflight.rs, and 8 regression tests. -
Codex Ctrl-D now shows quit menu instead of auto-restarting fresh.
restart_continue_exit_strategy()now routesctrl_d_forwardedtoPromptUserso the user sees "Press Enter to restart fresh, or 'q' to exit" instead of an automatic fresh restart. The supervisor log recordsctrl_d_prompt_user/user_quit_after_ctrl_dfor the new path. TheRestartFreshhandler 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.rsnow records a startup-miss marker at.agent-doc/state/startup-miss/<doc-hash>.jsonand 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-checkreports a warning when a startup-miss marker exists. Addedstartup_missmodule with persistence/load/clear/detection, 4 unit tests, 4 route-level integration tests, and updatedspecs/07-commands.md. -
startreuse now probes supervisor health before switching focus. Whenstartfinds a live owner pane, it queries the supervisor IPCstatemethod. Healthy sessions get focus-switched as before. Unhealthy sessions (halted/degraded/not-running) get arestartIPC 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 whereagent-doc start <file>silently switched to a stuck or dead session. -
Cross-session
startreuse now switches the current tmux client before focusing. When the live owner pane is in another tmux session,startnow 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
startreuse no longer prints shared[sync]probe diagnostics.start.rsnow 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.routeandresynckeep the richer[sync]owner-proof logging they use for recovery and diagnostics. -
Duplicate live
startnow reuses the existing pane instead of erroring.start.rsnow excludes the current transientagent-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,startnow 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. -
resyncnow shares route's live-owner proof and stale-owner recovery.sync.rsnow 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.rsreports alive-but-unowned registrations asNoLiveOwner,resync --fixderegisters them without killing the pane, androute.rsnow 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, unregisteredagent-doc/codex/claudepanes 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.rstreats 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.rsstill prefers a tmux process-tree match on the document path, but when a registered pane is alive and the long-livedagent-docsupervisor 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 liveagent-docpane 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.rsnow preserves that pane if it is still the live registered owner instead of cleaning it up as an orphan. This keepsfresh_route_start_missing/fresh_route_trigger_missingfrom 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-panehistory. 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 oftmux send-keys. That closes the#rvinjectracewindow 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
HEADbefore later local drift. Whensession-checkorcommitsees thatHEADalready contains a previously bypassed assistant response, snapshot repair no longer requires the live worktree to be exactlyHEADorHEADplus an exchange-only prompt follow-up. It now advances the snapshot to the committedHEADstate for any later local drift that does not introduce a newer### Re:/## Assistantblock beyondHEAD, then reclassifies the remaining user edits normally. This closes the stale-snapshot/manual-commit#pbc2shape where a structurally valid committed response was still misreported as a direct patchback bypass. Added regressions for bothsession-checkandcommiton the committed-head-plus-local-status-edit case. -
agent-doc backlogis now the canonical backlog CLI, withagent-doc pendingretained as a deprecated alias. The top-level backlog management subcommand now lives underagent-doc backlog ...; invoking the legacyagent-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-checknow 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-addparsing still accepts canonicalid=<custom> ...and compatibility[#custom] ..., but it now rejects bare[#]placeholders, emptyid=prefixes, and stacked leading prefixes like[#a] [#b] ...orid=a [#b] .... The accidentalreplace:pending/patch:pendingnormalization 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_argsno 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 insrc/session-sharecan 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 resumestill strips--add-dirbecause 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
HEADbut the working tree contains a likely direct assistant patchback (### Re:/## Assistant) with no neweragent-doccycle,git::commitnow fails closed instead of classifying that state as ordinary post-commit working-tree drift and returningcommit_already_current. This closes the#pbypass1shape 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/finalizegained 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#idbut the cycle recorded no matching--pending-done <id>. Session documents now defaultpending_done_guardtostrictunless 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 provingfinalizeleavesHEADunchanged when the gate trips. -
Blank
--windowsync scope now fails safe instead of reconciling the whole tmux server.sync.rsnow normalizes empty/whitespace-only window overrides to "unset" before repair, auto-start scoping, andtmux_router::sync, androute.rsignores blankcontext_sessionoverrides the same way. This closes the tmux-instability path where a JetBrains/plugin sync passed an empty window id, producingtarget_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.rsandsync.rsnow rescue stashed session panes back into theagent-docwindow with guardedjoin-pane, placing them on the requested left/right edge instead of preferringswap-pane. This closes the remainingclaudescore-3.mdtmux 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 theagent-docwindow during rescue. -
Duplicate live
startnow fails closed before spawning a second pane.agent-doc startnow 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 thecorky.mdrestart 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,
repairnow advances the snapshot andwrite_appliedphase 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.
preflightno longer openspreflight_startedon pure agent-owned(HEAD)/ boundary churn in template docs. It classifies that shape first, collapses it back tono_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 ascycle started but no write/commit followed. -
compact exchangewrite-back now replacesagent:exchangefor that turn. When the user-added diff explicitly starts with a directcompact exchangedirective, template/CRDT write paths now override the normal append mode foragent:exchangeand 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.rsno 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-cyclecommit_already_currentupdates fail closed instead of logging a falseroute_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.rsno longer rewritesconfig.tomlwhen a configuredtmux_sessionis 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.rsnow 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.rsnow applies the same fail-closed start-ack rule to dispatches into an already-running pane when the document already has unresolvedprompt_target/content_editdrift 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 tocodex (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 cleanHEAD. -
session-checknow 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 neweragent-doccycle ever started,session-checknow fails closed instead of reporting the stale committed state orno cycle state or ops.log — ok. The Codex Stop hook inherits that signal and can auto-close the missed-start case fromlast_assistant_messagethrough the normal repair/write/commit path. Addedsession_checkand Codex hook regression coverage. -
Session-document
write --commitnow fails closed.write --commitstill 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/ legacysession) it now upgrades to the same strict closeout contract asfinalize: reject non-git docs before mutation, fail the command on commit failure, and only return success once the cycle reachescommitted. 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:pendingblock, the write path now translates it into granular pending mutations before durable capture instead of capturing first and then failing onreplace:pending block forbidden. That closes theresponse_capturedorphan path behind#pendops.repairreplays 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.rsno longer treats a consumedagent-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_startedor later) before declaring success, logsfresh_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 resumedoes not accept--add-dir, butappend_resume_argswas passing it through frombase_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 originalexec, 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
finalizenow blocks those uncaptured single-item responses before commit, andsession-checkwarns on the same shape post-commit. Added regression coverage for unresolved-vs-resolved bug prose.
0.33.15
-
Supervisor model injection from frontmatter.
start.rsnow injects--modelfromclaude_model/codex_model/modelfrontmatter when the freeform args (claude_args,agent_args, etc.) don't already contain--model. Precedence: harness-specific field (claude_modelfor Claude,codex_modelfor Codex) > genericmodelfield. -
Pre-commit pending capture gate in
finalize. Whenpending_capture_guard: strict,finalizescans the response for uncaptured recommendations before committing. If recommendation-like items are detected without--pending-addflags, finalize exits non-zero before the commit step. -
planemitsExpectAddpending mutations. When prompt targets contain backlog/recommendation signals ("tasks", "todo", "backlog", "what's next", "recommendations", "next steps", "action items"),planemits anexpect_addentry inpending_mutations. Tells the skill that finalize should include--pending-addflags for actionable items in the response. -
Post-preflight planning command.
agent-doc plan <FILE>emits a structured planning/dispatch record withprompt_targets,repo_actions,required_commands,pending_mutations,handoff, andblockers.
0.33.14
-
Inline guard marker stripping.
strip_guard_markersnow 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 -->withagent:pendingaccepted as a backward-compatible alias.patch=replaceattribute on backlog/pending tags is deprecated and auto-stripped. Addedagent:iceboxcomponent to template scaffold for parked items. -
agent-doc migratecommand. New subcommand for deprecated component name/attribute migrations (e.g.,pending→backlog). -
Per-harness model override. Frontmatter
claude_modelandcodex_modelfields 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.rsnow 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_presetsare now resolved during orchestrate task expansion, and--planflag 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.
startnow 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-direntries for the submodule's external gitdir under the superproject.git/modules/...tree plus the superproject.gitused 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-dirargs when switching tostream-json. -
agent-doc orchestratenow executes real DAG batches. The shared orchestration surface still resolves task batches from repeated--task,--from-file, and--from-exchange, but--mode dagnow parses optional[id=... after=...]metadata, falls back to the first#tokenin each prompt as the node id, validates duplicate/missing/cyclic dependencies, and runs the resulting graph in deterministic topological order through the same per-stepinject -> preflight -> fresh agent -> finalize -> session-checklifecycle. 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
parallelnow routes through the orchestrate dispatcher.agent-doc parallelremains available, but it now forwards its explicit task list into the sameorchestrate --mode parallelrouting 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 + pushinto 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.mdrunbook 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-versionfromCARGO_PKG_VERSIONinstead of inheriting a stale literal from the source template, Codex environment detection now recognizes live Codex shell vars likeCODEX_THREAD_ID/CODEX_CI, and the rendered auto-update step now uses harness-specific install/reload commands (--harness claude --reload compactfor Claude Code,--harness codex --reload restartfor 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.rsnow treats prompt-prefix targets as a shared binary invariant derived fromdiff.rs's canonicalprompt_targetclassifier instead of relying only on a separate line-shape heuristic, andsession-checknow reports bare prompt-target lines when a bypassed### Re:/## Assistantpatchback left the transcript uncanonicalized. Added unit coverage for prompt-prefix target extraction and the newsession_checkfailure 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: strictor project[guards] pending_capture = "strict"upgrades the condition to a nonzerosession-check, and<!-- no-pending-capture -->suppresses the guard for intentional skips. Added heuristic unit coverage plussession_checkcoverage for warn, strict, suppression, and frontmatter-overrides-project precedence. -
Unified prompt-bearing change classifier. The diff/prompt contract no longer splits explicit
required response targetsfrominline_annotations.diff.rsnow classifies ordered user-authored changes asprompt_target,content_edit,recovery_artifact, orboundary_artifact, prompt builders render that typed section directly, and preflight surfaces the canonical list asprompt_bearing_changeswhile keepinginline_annotationsas 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.
repairnow ignores terminal durable-capture states (committed,discarded) unless there is still a pending response file to reconcile, so routinepreflightruns 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
repositionmessages can carry the exact exchangeboundary_id, and both editor helpers now preserve that marker instead of minting a new one aftercommit(). 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 testsphrases: 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_atandcommitted_at, andops.logemitscapture_committed_after_replaywhen 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. -
commitnow explains post-commit local drift explicitly. When the stripped snapshot already matchesHEADbut the working tree still has later local edits,agent-doc commitnow 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 commitnow repairs the snapshot up toHEADbefore theHEAD-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-docis invoked from inside a submodule with a relative document path liketasks/sampleorders.md, path resolution now prefers the caller's existing cwd-local file before consulting the superproject root. This fixes the case wherecommit/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 likego) 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 finalizeis now the strict happy path for normal session responses, Codex/direct-exec instructions require an immediateagent-doc session-check <FILE>afterfinalizeorwrite --commit, and the installed CodexStophook can auto-close a pending response cycle fromlast_assistant_messagebefore failing closed. Added CLI/integration coverage for thefinalize + session-checkpath and the real Codex hook flow. -
Codex hook state now survives root / turn drift. The repo-local
UserPromptSubmit/Stopbridge now mirrors active-session state across nested.agent-docroots and still inspects the tracked document on laterStopevents 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 nextStoparrives with a newer turn id. Added regression coverage for the nested-root replay path. -
Interrupted-cycle + historical-drift repair.
preflightnow fails closed on unrecoverablepreflight_startedcycles instead of snapshot-committing over newer live content, whilecommit/session-checkcan narrowly repair already-committed historical### Re:drift whenHEADproves the response is no longer out-of-band. -
Bare-path compatibility restored.
agent-doc <FILE>once again aliases toagent-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 whenindex.lockcontention hitsupdate-index,git add, orgit commit. Added regression coverage for a stagedindex.lockretry and two different docs contending on closeout in the same repo. -
repairnow 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 laterpreflight. Added regression coverage for both replayed and already-applied repair paths.
0.33.12
- Codex agent backend (Phase 1). New
agent/codex.rsimplementsAgent+StreamingAgentfor the OpenAI Codex CLI. Parses Codex JSONL event stream (thread.started,item.completed,turn.completed). Session resume viacodex exec resume <id>, fork viacodex exec resume --last. Registered inagent::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()inlib_install.rspreviously usedstd::fs::copy(source, &dst)which overwrites the versioned.soin 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 callsrename()— 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()incomponent.rspreviously 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()incrdt.rspreviously only checkedcommon_prefix_lento 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 computesours_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, andclear_ipc_degradedremoved fromwrite.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 withcleanup_legacy_ipc_degradedthat removes any stale.agent-doc/ipc-degradedmarker left by older installs. -
JB plugin 0.2.71: writeAckContent fires on all patch paths. Previously
writeAckContentwas 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) callwriteAckContent, ensuring the ack-content sidecar always fires regardless of which code path processes the patch. -
Fix: Makefile
testtarget unsets git hook env vars.make testnow runsenv -u GIT_DIR -u GIT_INDEX_FILE -u GIT_WORK_TREE cargo test. When the pre-commit hook callsmake precommit, git setsGIT_DIRto 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. Theenv -ustrips 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 calledfs2::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 butgit::commitblocked at the flock — causing the skill process to hang. Changed totry_lock_exclusive(): returnsNoneimmediately when contended, proceeding unlocked. Git's ownindex.lockretry loop (3 attempts with exponential backoff) handles serialization at the git layer.
0.33.8
- Rename debounce (#qam7).
agent-doc sync --renamewrites 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 whenFileRenameListener(JB) oronDidRenameFiles(VS Code) triggers sync for a file with no alive pane. Both editor plugins now pass--renameon file rename/move events. JB plugin 0.2.70, VS Code extension 0.2.7. - Auto-start pane ID logging.
route::provision_panenow returnsResult<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()inPatchWatcher.ktandrepositionBoundaryWithDebounce()inextension.tsnow verify the document content is unchanged between thedocument.textread anddocument.setText()/WorkspaceEdit.apply(). If the user typed betweenawait_idletimeout expiry and the write dispatch, the reposition is silently skipped rather than overwriting the new keystrokes. AddsrepositionBoundaryToEndUtil/findCodeBlockRangesUtilas internal top-level functions (JB) andrepositionBoundaryToEndas a vscode-free module (VS Code) for unit testability. New:RepositionBoundaryTest.kt(7 cases) andreposition.test.ts(5 cases). -
Skip working-tree boundary reposition when IPC available.
reposition_boundary_in_snapshot()ingit.rsnow 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_availableandreposition_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 broaderprompt_bearing_changescontract. -
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 installoverwrotelibagent_doc.sowhile IDEA held it mmap'd via JNA. Both plugins now stat the.soon everyget()/ensureLoaded()call; if mtime changed, they forceNative.unregister+ reload (JNA) orkoffi.unload+ reload (VS Code). Onestat(2)/statSync()per FFI dispatch — negligible overhead. Race window narrows to sub-microsecond. -
Versioned cdylib install.
cargo install/make installnow writeslibagent_doc-<version>.soand atomically updates thelibagent_doc.sosymlink vialn -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-pathstill returnslibagent_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>.lockcontaining their PID; on clean exit (JVM shutdown hook / VS Codedeactivate()), they remove the lock.agent-doc gc-libswalks alllibagent_doc-*.sosiblings: keeps the current symlink target and any .so whose.lockhas 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-addin 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 returnedsuccess: truewhen 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 returnssuccess: 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_iddeduplication prevents double-apply. -
IDE buffer stale fix (JB plugin 0.2.64).
repositionBoundaryViaDocument()inPatchWatcher.ktnow callsreloadFromDisk(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-added.runbooks/pending-ops.mdnow 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 toagent:pendingin the same cycle, so the proposal survives user non-reply.
0.33.2
-
agent_doc_resolve_project_pathFFI 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 whereRun Agent Docon 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, producingfile 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:pendingcomponent. Falls back tosplice_pending_componentonly 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_linenow strips[#]markers instead of prepend-on-backfill, preventing placeholder accumulation across cycles. -
Pending dedup on
--pending-add.op_addchecks for identical text before appending, preventing duplicate items when the same add is retried. -
Content-shrink guard for
--streamwrites.check_exchange_shrink_guard()inwrite.rsrefuses 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 onGatedstate, cleared when resolved to[x]. Untyped[/]items are never touched byresolve-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-gatecommand (agent-doc resolve-gate <type>): Scans all.mdfiles under project root (or--scope <dir>) for items with matching typed gates. Designed for hook integration:{ "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=typefor atomic pending+response cycles. -
--pending-add-gatedflag: Add items pre-gated as[/]instead of[ ]. Available on bothwriteandnotifycommands. -
--pending-onlyflag: Skip stdin reading and exchange synthesis — only apply pending mutations. Requires at least one--pending-*flag; incompatible with--template/--stream/--ipc. -
--statusflag onwrite: Replace theagent:statuscomponent content inline during a write operation, same pattern as pending ops. -
statussubmodule (status_cmd.rs): New module for status component manipulation. -
Notify with pending:
agent-doc notifygains--pending-add,--pending-add-gated, and--no-create-pendingflags. Message is now optional when--pending-addis used. -
session clearsubcommand: 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.rsno 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 Codextab to queue messagefooter, which is a queue-only busy state rather than a true idle prompt. This closes the failure mode where route loggedcodex ready after 0.0s, injectedagent-doc <file>into a live pane, then timed out withno new document cycle startedbecause Codex had only queued the message. Tests: newharness::has_busy_cue_*coverage plusroute::wait_for_agent_ready_rejects_codex_queue_message_footer. In the same pass,resync.rsbulk stash cleanup now matches the stricter single-pane cleanup behavior: unregistered stash panes runningagent-doc/claude/codexare 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: newresync::purge_unregistered_stash_panes_bulk_kills_unregistered_agent_without_live_owner. - Fix submodule auto-start
file not found(route.rsrewrite_start_path): When the spawned tmux pane'scwdis narrowed to a submodule root (bygit::resolve_pane_cwd), theagent-doc start <path>send-keys invocation now rewrites the caller-supplied super-root-relativefile_pathto be relative to that narrowedcwdbefore composition. Previously a path likesrc/session-share/tasks/foo.mdwas passed verbatim to a pane alreadycd'd intosrc/session-share, producingError: file not found: src/session-share/tasks/foo.mdand blocking auto-claim + auto-start on every submodule-hosted document. Fix lives at a single funnel (auto_start_in_session) and also feedssend_command's/agent-doc <path>slash command for the same reason. Pure helperrewrite_start_path(file, cwd, original) -> Stringcanonicalizes both sides, strips the cwd prefix, and falls back tooriginalon 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 fullroute::suite (43 passing). Forward-compatible with the supervisor track (#jg0d/#b486/#40ct/#vnp0/#6ae3/#zp02/#f7d5) — whenPtySpawnConfig.argslands, the same helper feeds path rewriting at the new spawn funnel. - Binary strips trailing bare
❯lines from exchange writes (template::strip_trailing_caret_linesinapply_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 helperstrip_trailing_caret_lines(content)collapses all trailing lines whose trim is exactly❯; called onpatch.contentwhenpatch.name == "exchange"and on unmatched content when it routes toexchange/output(including the auto-created-exchange path). Non-exchange components are untouched —❯innotes,pending, or user-authored content like❯ follow-upis 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). Fulltemplate::suite: 64 passing. See 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!insrc/skill.rs::BUNDLED_RUNBOOKSand installed to.claude/skills/agent-doc/runbooks/onagent-doc skill install:model-tier-gate.md(precedence chain,required_tiergate,model_switchack — 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), andcode-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_filehandling, slash-command dispatch viaSkilltool,### Re:header rule + model attribution, pending granular-ops 3-line summary,--streamwrite-back + immediateagent-doc commit, and theIMPORTANT: Do NOT use Edit toolguard. Memory cleanup:feedback_no_trailing_prompt_glyph.mddeleted from~/.claude/projects/-home-brian-work-btakita-agent-loop/memory/and itsMEMORY.mdindex 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): NewPendingState::Gatedvariant for pending items that are code-complete but awaiting an external gate (release, telemetry, field validation). Rendered as- [/] [#id] textin 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 → Gatedviagate,Gated → Openviaungate,Open|Gated → Doneviamark-done. Illegal transitions (ungatefromOpen/Done,gatefromDone) return errors; idempotent transitions (GateonGated,MarkDoneonDone) are no-ops. Parser:pending::parse_item_lineaccepts[ ]/[/]/[x]/[X];PendingItem::renderround-trips. CLI:agent-doc write --pending-gate <id>and--pending-ungate <id>flags on thewritesubcommand, combinable with--pending-add/--pending-done/--pending-edit/--pending-reorderin a single call (gate/ungate run before done so--pending-gate X --pending-done Xpromotes throughOpen → Gated → Doneatomically). Preflight: emitspending_gated_count: Nin the JSON output when at least one item is gated (omitted when zero to keep happy-path output compact), alongside the existingpending_reorderedsignal. Reaper: preflight's reap pass skipsGateditems unchanged. Tests:tests/pending_integration.rscovers parser round-trip for[/], all valid/invalid state transitions, reaper respectsGated, 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 thependingcomponent is renamed from<!-- patch:pending -->...<!-- /patch:pending -->to<!-- replace:pending -->...<!-- /replace:pending -->. Thereplace:prefix signals full-replacement semantics explicitly (all otherpatch:<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 deprecatedpatch:pendingform,--allow-patch-pendingflag (via clap alias), and legacy env var all continue to work for one release. The parser emits a stderr deprecation warning on everypatch:pendingblock so callers can find and update their usage. The default-reject gate applies to both forms — enforcement recognizesname == "pending"regardless of which prefix opened the block. Rationale: thereplace: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 thatpatch: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:pendingwill 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 causedfatal: Pathspec '...' is in submodule '...'errors duringagent-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: Addednarrow_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 actualgit submodule addsandboxing). Live verification: Two separate submodule documents (src/session-share/tasks/claudescore.md,src/sample-app/tasks/sampleorders.md) now commit cleanly with zerofatal:lines. -
Feature:
out_of_band_writealways-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_exceededdetection 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), plusnormalize_threshold_exceeded file=... delta=... snap_len=... base_len=...(fires ifdelta > 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:
envfrontmatter 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: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>(viacmd.env()on spawned process) - Non-streaming submits via
agent-doc run(viaClaude::with_env()) - Streaming submits via
agent-doc stream(viaStreamingAgent::send_streaming()) - Parallel fan-out (via unexpanded shell exports in tmux send-keys, so target shell handles expansion safely)
/agent-docskill in existing sessions (preflight JSON returns unexpanded values; skill runsexportin Bash)
- Interactive Claude sessions started via
- 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)— buildsexport 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 rancompact, the binary correctly rewrote the file and snapshot on disk, but the CRDT state in.agent-doc/crdt/<hash>.yrswas stale. On the nextagent-doc writeorstream, the 3-way merge loaded the stale CRDT (containing pre-compact exchange AND pre-compact pending), causing non-target components (likeagent:pending) to be clobbered by old CRDT view of pending items. Fix: Afterrun_component_compactorrun_component_compact_partial, whenis_crdt, refresh CRDT state by creating a newCrdtDocfrom 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.mdnow 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()andtry_ipc_full_content()inwrite.rsnow usegit::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 tofind_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), andtest_submodule_write_patches_dir_structure(integration-level directory layout validation). -
Feature: Harness-agnostic model tier selection: New
model_tiermodule defines aTierenum (auto | low | med | high) and composes aneffective_tierfrom four sources, highest precedence first:- Inline
/model <x>command in the diff (stripped from downstream diff/classifier) <!-- agent:model -->component contentagent_doc_model_tierfrontmatter field- Diff heuristic (
suggested_tier) based ondiff_type+ document path
- Inline
-
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()checksCLAUDE_CODE_SESSION/CLAUDECODE/CODEX_SESSIONenv vars and returnsclaude-code | codex | default. -
Preflight JSON additions:
effective_tier,required_tier,suggested_tier,model_switch,model_switch_tierfields. -
Diff scanner strips
/modellines:scan_model_switchruns 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_tierand either proceed, acknowledge a/modelswitch, or ask the user to/modelbefore re-invoking. -
Frontmatter field:
agent_doc_model_tier: low | med | high | autoon session documents. -
Tests added: 48 tests in
model_tier.rscovering 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.rsnow saves snapshot + callsgit::commitbeforeprocess::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.rsreordered to run commit beforeresult?, ensuring partial writes that saved a snapshot are always tracked in git. - Fix: Commit-reliability — retry on git index.lock contention:
git.rsretriesgit commitup to 3× with exponential backoff (100/200/400ms) when concurrent sessions cause lock contention. - Fix: Commit-reliability —
agent_doc_commitFFI export:ffi.rsexportsagent_doc_commit(file_path)for IDE plugins to call after applying a patch.NativeLib.kt+PatchWatcher.ktupdated to call it on the Document API path. - Fix: Commit-reliability — preflight cross-document sweep:
preflight.rsscans 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) toggledin_fenceon every line starting with 3+ backticks — including```bashwhich per CommonMark can only OPEN a fence, not close one. When a```plain fence contained inner```bashlines (e.g., terminal output referencing a bash command), the state inverted, causing# On the server — run onceinside a subsequent```bashblock to appear "outside" the fence and receive a(HEAD)marker it must not have. Fix: replace the ad-hocis_fence_marker/in_fencetoggling instrip_head_markersand all four code paths inadd_head_marker(step 1 cleanup, step 2 heading collection, step 3 HEAD heading counting, re-application loop) with CommonMark-compliant code block detection viapulldown-cmark. A closing fence cannot have an info string —pulldown-cmarkcorrectly 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```bashline, followed by a real heading, followed by a```bashfence with a# commentline.
0.31.30
- Fix:
❯prefix applied toagent:pendingpatches (regression in v0.31.29):normalize_patch_contentwas called on all IPC patches, not just exchange patches. Whennormalize_prefix_linescontained a line that also appeared verbatim in theagent:pendingpatch content, that line incorrectly received the❯prefix. Fix: gatenormalize_patch_contentonis_append_mode_component(&p.name)at both the primary IPC write path and the IPC timeout fallback inwrite.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 thatagent:pendingcontent is not normalized.
0.31.29
agent-doc write --commitflag: Runsgit::commitimmediately after a successful write. Eliminates the separateagent-doc commitstep — 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-treeguard). Streaming checkpoint writes do not use--commit; only the final write does.git::is_in_git_repohelper: Newpub(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.rsauto-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_sessionhelper + 3 tests: Extracted guard into a testablepub(crate)function; 3IsolatedTmux-based tests cover noop, cross-session success, and no-anchor fallback.
0.31.27
pane_policymodule (tmux-router 0.3.10): NewPaneMoveOp+CrossSessionenum as a mandatory gateway for all pane movement.CrossSession::Denyby default;CrossSession::Allow { reason }for intentional cross-session relocations. All 7join_panecall sites in agent-doc migrated to usePaneMoveOp.- Guard
start.rsregistration: When claiming a pane, warns if$TMUX_PANE's session ≠project_tmux_session()— prevents silent session drift on claim. - Guard
resolve_target_sessionauto-update (route.rs): No longer overwritestmux_sessionconfig 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.rsWrongSession detection:detect_issuesnow falls back toconfig::project_tmux_session()whenfrontmatter.tmux_sessionis absent. Panes in a wrong session are flagged even without per-document session frontmatter.apply_fixes_to_registryusesPaneMoveOp::allow_cross_session("relocate WrongSession pane to project session")to move them.
0.31.26
- Fix: orphan repair dedup guard (repair.rs):
repair::runnow 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 butclear_pendingwas 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
preflightdiff-only always (preflight.rs):documentfield is alwaysnull— the full document is never sent automatically. Useagent-doc read <FILE>to fetch on demand.- BREAKING CHANGE:
--diff-onlyand--with-documentflags removed frompreflight: 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_layoutno longer flags panes instash*windows as layout issues. Stash windows hold intentional backgrounded sessions. - Fix:
collapsible_ifingit.rs(CI): Nestedifat 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_exchangepreviously only tracked```(backtick) fences. Lines inside~~~fenced regions could incorrectly enteruser_addedand receive a❯prefix. Fixed by extractingfence_open/fence_closehelpers that handle both`and~fence chars with proper length tracking (matchingdiff.rs'sfence_char/fence_lenapproach). New test:normalize_user_prompts_tilde_fence_interior_skipped.
0.31.23
- Fix:
❯prefix normalization via IPCfullContent(write.rs): Whennormalize_prefix_linesis non-empty,try_ipcnow also sendsfullContent = content_oursin the IPC payload (both socket and file paths). The plugin'sfullContentpath 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❯ dois restored when editor file has baredo. agent-doc compact --tag <name>(compact.rs): Creates a lightweight git tag at HEAD before compaction as a pre-compact checkpoint. Without--tag, auto-generatesagent-doc/<doc-name>/pre-compact-N. Use--tag skipto disable. Tagging failure is a warning, not an error.agent-doc log <FILE>(history.rs): Annotated git log for a session document. Walksgit log, loads allagent-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 Nmaps toHEAD~N;--at Nselects 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.--todefaults toHEAD. 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_exchangenow 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_exchangenow takes abaselineparameter. User-added lines are identified by diffingsnapshot → baseline(notsnapshot → content_ours user_region). Afterapply_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 inpatch=appendexchange components are prefixed with❯to visually distinguish user input from agent responses. Implemented viasimilardiff of snapshot vscontent_ours; only Insert lines before the boundary marker are prefixed.normalize_user_prompts_in_exchange()andextract_normalization_targets()added. 6 tests.- IPC-side prefix normalization (write.rs + PatchWatcher.kt v0.2.49):
try_ipcpassesnormalize_prefix_lines: Option<&[String]>in the IPC payload. JetBrains plugin appliesnormalizeExchangePrefixes()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=appendexchange 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 skipsstash_pane+rescue_from_stashfor 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 byis_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):WrongSessionfix now supports--session <name>to relocate panes viajoin-paneinstead of killing them.apply_fixes_to_registrytakesrelocate_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 Narchives 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 inslash_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>.jsonto preventprocessPendingPatches()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; usesjoin-panefor cross-session panes. Session-drift detection added tocheck_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 clearingeffective_unmatchedto""when synthesis occurred, on both socket and file IPC paths. - Write-time dedup (write.rs):
build_ipc_patches_jsonnow 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
.mdfiles get the full template (UUID + format + crdt + components) when claimed. Previously only wroteagent_doc_session, causing scaffolding to skip (no format detected).
0.31.15
- Transfer auto-init (extract.rs):
agent-doc transferauto-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_streamwarns when file has no template components but receives unmatched content.try_ipclogsipc_unmatched_content_droppedto ops.log. Improved ops.log to includeipc_patchescount alongside originalpatchescount. - Investigation runbook: New
runbooks/investigate-behavior.mdfor 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,
claimnow provisions a new pane instead of erroring. Enforces SPEC §8.5: "never commandeer another document's pane." - Sync auto-scaffold (sync.rs): Empty
.mdfiles 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 transfernow automatically transfers thependingcomponent 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 asdiff_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 asannotated_diff. 5 tests. - Content-source annotation sidecar (P4): New
agent-doc annotatecommand generates.agent-doc/annotations/<hash>.jsonmapping each line to agent/user source. SHA256 cache invalidation. GC integration. 6 tests. - Reproducible operation logs (P5): New
.agent-doc/logs/cycles.jsonlwith 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_commentstocomponent.rs(shared between binary and eval-runner). eval-runner preprocesses diffs with comment stripping. - Transfer-source metadata:
PatchBlocknow supportsattrsfield.<!-- 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\\nunescape 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.mdkernel node. - Module-harness: New
ontology-referencesrunbook 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(). Compositeensure_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'sresolve_file. Files withagent_doc_formatbut 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()insnapshot.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_fileandagent_doc_await_idle_via_filefor CLI tools running in separate processes.is_idleandawait_idlenow bridge to file-based indicator when untracked in-process. - Diff stability fix:
wait_for_stable_contentcounter now tracks consecutive stable reads across outer iterations (was resetting within each pass). - IPC error propagation:
ipc_socket::send_messagenow returns proper errors instead of swallowing connection/timeout failures asOk(None). - Template patch boundary fix: Improved boundary marker handling in
apply_patches_with_overrides. - CI/build:
make releasetarget, idempotent release workflows, version-sync check inmake check.
0.31.9
- Transfer-extract runbook: New bundled runbook for cross-file content moves (
agent-doc transfer/extract). Installed viaskill 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 automaticprune()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
flockon.agent-doc/sync.lockto serialize concurrent sync calls. Prevents race conditions when rapid tab switches fire overlapping syncs. - Route sync removal: Removed redundant
sync::run_layout_onlyfrom Route command dispatch andsync_after_claimfrom route.rs. The JB plugin'sEditorTabSyncListeneris 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_debouncefrontmatter field. - Structured logging: Added
tracing+tracing-subscriber+tracing-appender. SetAGENT_DOC_LOG=debugto 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::Dropnow deletes the lock file (not just unlocks). CRDT lock acquisition cleans stale locks (>1 hour old). agent-doc gcsubcommand: Garbage-collects orphaned files in.agent-doc/directories. Supports--dry-runand--rootflags.- Auto-GC on preflight: Runs GC once per day via
.agent-doc/gc.stamptimestamp check. - Cleanup runbook: New
runbooks/cleanup.mddocumenting.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 extractandagent-doc transfernow 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
CRITICALif 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 claimnow 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-docworks on new files without claiming first. - VCS refresh after commit:
agent-doc commitwrites a VCS refresh signal file, prompting IDEs to update their git status display. - Preflight
--diff-onlyflag: Omits the full document from preflight JSON output, reducing token usage by ~80% on subsequent cycles. - Skill-bundled runbooks:
agent-doc skill installnow 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.rsfor 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=Nattribute: Component tags supportmax_lines=Nto 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 claimnow scaffolds a<!-- agent:console -->component for template-mode documents. - HEAD marker cleanup:
git.rsstrips stray(HEAD)markers from working tree after commit (defensive cleanup). - StreamConfig max_lines:
agent_doc_stream.max_linesfrontmatter 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 claimnow 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:
--originflag onagent-doc writelogs the write source (skill/watch/stream) to ops.log. Aids diagnosis when snapshot drift occurs. - Commit drift warning: Warns when
file_len - snap_len > 100bytes, 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+Cnow callsagent-doc claimon 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 claimrefuses to overwrite an existing live claim without--force, preventing silent pane corruption from fallback position detection. - HEAD marker duplicate fix:
add_head_markeruses 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.rsembeds a build timestamp. On sync, the binary compares against.agent-doc/build.stampand clears stale startup locks on new build detection. - Plugin binary resolution fix: EditorTabSyncListener and SyncLayoutAction now pass
basePathtoresolveAgentDoc(), correctly resolving.bin/agent-docinstead 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 sessionCLI: Show/set configured tmux session with pane migration (session_cmd.rs).- Stash pane safety:
purge_unregistered_stash_panesno 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.tswith 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.rsfollows pane session,claim.rsupdates config to match. - 2 new FFI tests: Coverage for
agent_doc_is_idleand related FFI surface. - Dependencies:
tmux-routerv0.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_panecallback 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>.lockwith 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/gcCLI. Cross-session event coordination viaagent-kithooks (v0.3).post_writeandpost_commitevents fired from write + commit paths. - HookTransport trait: Abstract delivery mechanism with
FileTransport,SocketTransport,ChainTransportimplementations. - Ops logging tests: 2 new tests for
.agent-doc/logs/ops.log. - Dependencies:
agent-kitv0.3 (hooks feature),tmux-routerv0.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 viaureq, converted HTML→markdown viahtmd(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>.logwith timestamped events for session start, claude start/restart/exit, user quit, and session end. - Auto-trigger on restart: After
--continuerestart, background thread sends/agent-doc <file>viatmux send-keysafter 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:
htmdv0.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_streamdisk,run_streamIPC) skip the write when merged content is identical to the current file. Dedup events logged to/tmp/agent-doc-write-dedup.logwith backtrace. - Pane ownership verification:
verify_pane_ownership()called at entry ofrun,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.jsonsaves column→agent-doc mapping (carried from v0.28.1, now documented).
0.28.1
- Column memory:
.agent-doc/last_layout.jsonsaves 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:
syncnow filters out empty strings fromcol_argsbefore processing. Fixes phantom empty columns sent by the JetBrains plugin during rapid editor split changes. - Sync debug logging: Added
/tmp/agent-doc-sync.logtrace 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::syncalways 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 forresolved < 2bypassed 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 deaddoc_tmux_sessionpath - VERSIONS.md backfill: Added entries for v0.23.2 through v0.26.6
0.27.7
- Sync path column-aware split:
auto_start_no_waitnow acceptscol_argsand computessplit_beforeviais_first_column(). Previously hardcodedsplit_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()ingit.rsnow 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_sessionpicks 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 < 2early return intmux-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.rsnow 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_socketmodule using Unix domain sockets via theinterprocesscrate 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/unlockFFI exports for cross-editor concurrency control. Addedagent_doc_sync_bump/check_generationfor cross-editor event coalescing. - Layout debounce fix:
LayoutChangeDetectoruses 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
Unmanagedfor files without session UUIDs; onlyclaimadds frontmatter now. - Plugin mixed-layout sync: Uses focus-only when non-
.mdfiles 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_claimnow 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_busyfor 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 inEditorTabSyncListener+TerminalUtil.
0.26.1
- Sync layout authority:
sync_after_claimuses editor-providedcol_args, preventing 3-pane layout regression on file switch. - Clippy fixes:
doc_lazy_continuationfixes in sync.rs, upgrade.rs. Unused variable fix in tmux-routerbreak_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_panerefuses to destroy a session's last window (tmux-router v0.3.0). - IPC verification: Content verification catches partial plugin application failures.
--force-diskcleans 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_sessionfrontmatter 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.shwith platform detection and improved install paths. - Homebrew formula: Added
Formula/agent-doc.rbfor macOS/Linux Homebrew installation. - Deprecate
tmux_sessionfrontmatter: Sync strips the field on encounter instead of repairing it. Routeauto_startno longer attempts repair.
0.25.12
- Sync swap-pane atomic reconcile:
context_sessionoverrides frontmattertmux_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 --fixdisabled 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-panefor 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
--debounceflag: Opt-in mtime polling for coalescing rapid editor triggers. is_trackedFFI export: For editor plugins to check file tracking status.- Sync no-wait auto-start:
auto_start_no_waitfor 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_trackedbinding + 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
submittorun:submit.rsrenamed torun.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/--focusargs: Declarative layout sync from the route command. PluginsendToTerminalpasses editor layout in a single CLI call. - Layout change detection:
LayoutChangeDetectorusingContainerListenerwith 5s fallback poll in the JetBrains plugin. - EDT-safe threading: Plugin uses
invokeLaterfor 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
:summarysuffix (filename stem).new_boundary_id_with_summary()wired into all write paths. - Snapshot boundary cleanup: Commit path uses
remove_all_boundaries(). Working tree cleaned viaclean_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
repositionBoundaryToEndremoves 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/checknow 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 preflightcommand: 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: trueflag 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_startto 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 --fixon 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 installcommand: System-level setup that checks prerequisites (tmux, claude) and detects/installs editor plugins.agent-doc initproject mode: No-arginitnow 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()usesps(1)instead of/procfor Linux+macOS compatibility. Pre-sync auto-start checks alive panes before creating duplicates. - Clippy fixes: Fix
collapsible_ifwarnings in template.rs, git.rs, terminal.rs. Suppressdead_codewarnings 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 toexchange/output. Previously only explicit patches used boundary markers; unmatched content used plain append. - IPC snapshot correctness:
try_ipc()now accepts acontent_oursparameter (baseline + response, without user concurrent edits). On IPC success the snapshot is saved fromcontent_oursinstead 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/outputand 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-parsedComponentfor any boundary marker UUID, skipping matches inside code blocks. Used bytemplate.rsand 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_idwhen 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 commitright afteragent-doc write, replacing the old "Do NOT commit after writing" instruction. All sessions get the new behavior afteragent-doc skill install. - Plugin default modes:
exchangeandfindingscomponents now default toappendmode in the JetBrains plugin (matching the Rust binary'sdefault_mode()), so<!-- agent:exchange -->works without explicitpatch=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, writesvcs-refresh.signalto.agent-doc/patches/. Plugin watches for this and triggersVcsDirtyScopeManager.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 terminalsubcommand: Cross-platform terminal launch from editor plugins. Config-first (no hard-coded terminal list):[terminal] commandinconfig.tomlwith{tmux_command}placeholder. Fallback to$TERMINALenv var. Detects stale frontmatter sessions and scans registry for live panes.- Selective commit:
agent-doc commitstages only the snapshot content viagit 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()incomponent.rs. JNA binding inNativeLib.kt. - JetBrains plugin v0.2.7: Cursor-aware append ordering via native FFI with Kotlin fallback. Captures caret offset from
TextEditorbeforeWriteCommandAction.
0.21.0
agent-doc parallelsubcommand: 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-worktreefor read-only tasks.- CRDT post-merge reorder: Agent content ordered before human content at append boundary using Yrs per-character attribution (
Text::diffwithYChange::identity). - README: Added parallel fan-out documentation section.
0.20.3
agent-doc claimssubcommand: Read, print, and truncate.agent-doc/claims.login 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()whentmux_sessionis 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:frontmatternow 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:
claimscaffolds 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:
claimrefuses to overwritetmux_sessionunless--force. Prevents cross-session pane swapping.
0.17.29
- JNA FFI integration:
NativeLib.ktJNA 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
prependcase inapplyComponentPatch().
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 --waitfor truncation detection.
0.17.24
- Pulldown-cmark for code range detection: Replace hand-rolled code span/fence parser with
pulldown-cmarkin 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:pendingin claim for template documents.
0.17.20
- BREAKING CHANGE: Rename
modetopatchfor inline component attributes (patch=append|replace).mode=accepted as backward-compatible alias.
0.17.19
- Split-window in auto_start: Use
split-windowinstead ofnew-windowfor auto-started Claude sessions. Resync tests added.
0.17.18
- Resync
--fixenhancements: Detect wrong-session panes and wrong-process registrations. Renamed--dangerously-set-permissionsto--dangerously-skip-permissions.
0.17.17
- Parse fix:
parse_option_linematches[N]bracket format only. Fixfind_registered_pane_in_sessionlookup.
0.17.16
- Cursor editor support: Add Cursor as a supported editor.
claude_argsfrontmatter 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 historyshows exchange version history from git with restore support. IPC-priority writes with--force-diskflag 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_writesplit: Replaceagent_doc_modewith 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 compactnow 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 optionalthinking_targetrouting. Deferred commit workflow. Snapshot resolution prefers snapshot file over git.
0.14.9
- Multi-backtick code span support:
find_code_rangeshandles 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 convertworks 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 commandssubcommand: 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.converthandles 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
--reloadflag: 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 claimdefault to template format.agent-doc modeCLI 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 syncregisters 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
exchangecomponent uses append patch mode by default instead of replace.
0.12.1
- Lazy claim fallback:
agent-doc claimwithout--panefalls back to the active tmux pane.
0.12.0
agent-doc convertcommand: Convert between inline and template document formats. Lazy claim support.agent-doc compactfor git history squashing. Exchange component as default template target.
0.11.2
- Strip trailing
## Userheading: Also strip trailing## Userheading from agent responses (complement to v0.11.1).
0.11.1
- Strip duplicate
## Assistantheading: Remove duplicate## Assistantheading 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_modetoagent_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 writecommand: 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 installCLI: 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 patchfor programmatic updates,agent-doc watchdaemon 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-routeras a dependency for pane management. Fixrouteauto_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
--focuson sync:agent-doc syncaccepts--focusflag. 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 synccommand: 2D columnar tmux layout synced to editor split arrangement. Dynamic pane groups.
0.5.6
- Commit message includes doc name:
agent-doc commitmessage format now includes the document filename.agent-doc outlinecommand for markdown section structure with token counts.
0.5.5
- Window-scoped routing: Route commands scoped to tmux window (not just session).
--pane/--windowflags. 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 resyncvalidates 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.rsfor cross-platform compilation.
0.5.0
agent-doc focusandagent-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 DIRflag for audit-docs (§7.6),agent-doc-versionfrontmatter 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. Refactoredinstall/checkto 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.rsto 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-versionfrontmatter field enables pre-flight version comparison. If the installed binary is newer,agent-doc skill installruns automatically before proceeding. - PromptPanel: JDialog to JLayeredPane overlay: Replace
JDialogpopup with aJLayeredPaneoverlay 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, updatessessions.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.mdin the current project. The skill content is embedded in the binary viainclude_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 claimsupport. - 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 --allpolls all live sessions in one call, returns JSON array.SessionEntrynow includes afilefield 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 promptsubcommand added in v0.2.0 (unreleased). - send-keys fix: Literal text (
-l) + separate Enter,new-window -aappend flag (unreleased since v0.2.0).
0.1.4
agent-doc upgradeself-update: Downloads prebuilt binary from GitHub Releases as the primary upgrade strategy. Falls back tocargo install, thenpip install --upgrade, then manual instructions includingcurl | 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 triescargo installthenpip 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-fileif 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.
-bflag for auto-branch,--no-gitto 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_presetsexpansion (for example#code-reviewchaining into#follow-up-backlog).plannow emitsexpect_addfor those preset-driven review prompts, andfinalize/session-checknow 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-Dexits now restart fresh instead of stalling reroutes behind the supervisor quit prompt.start.rsnow treats a forwardedCtrl-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-promptCtrl-Dbyte until a real prompt appears. This closes thesampleorders.md%179shape fromtasks/agent-doc/agent-doc-bugs2.md, where dispatch-only reroutes kept failing withstill bootingwhile the live pane sat behindctrl_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.rsstill requires a real document-cycle ack after injectingagent-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 +/clearshape fromtasks/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.rsstill 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 bareagent-doc <FILE>reopen once and requires the usual cycle-start acknowledgment before success. This removes the false-negativesampleorders.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 thesampleorders.mdrepair-blocked shape where an orphaned<!-- patch:backlog -->replay warned about legacy backlog syntax and then died onduplicate leading custom id prefixeven though the live document itself had nopatch:backlogblock. 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-onlystill resolves the authoritative pane and sends the literalagent-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 likenvim. Dispatch-only also now checks the current pane capture for explicit interactive blockers such asreverse-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 JetBrainsagent-doc-bugs2.mdunexpectednvimlaunch report.