Repository navigation
Releases: mitchjablonski/deepPairing
Release list
v0.1.69 — one bad ledger entry no longer disables every stance
v0.1.69 ships the 2 PRs merged on main since v0.1.68, both merge commits: #556 and #559.
What ships
One bad ledger entry no longer disables every stance (#559, fixes #557)
Before, a single malformed taste-ledger entry (for example a non-string concept) made the matcher throw. The failure was silent, and every stance stopped enforcing. That happened in both the hook and the server-side present_* gate. Now:
- A bad concept is ignored or coerced:
concept: {name: "…"}is read as its name. A non-string concept is treated as absent. - The entry still enforces: it matches through its description, with an ask by default and a deny under strict-stop.
- A truly unusable entry is skipped and recorded, while every other valid stance keeps enforcing.
- Valid ledgers behave byte-identically to before.
Preflight failures are classified and visible in doctor (#556, refs #546)
This is observability only; hook decisions are unchanged. The hook's stdout, stderr and exit status are byte-identical to main in every characterized case.
- A failure taxonomy:
input_malformed,ledger_unreadable,ledger_corrupt,matcher_import_failed,evaluator_threw,session_store_unreadableandledger_entry_invalid, written as a bounded, redacted record. - Doctor reports recent failures and the gate mode as visible to doctor. It labels settings env versus shell env, and never claims the hook's effective environment.
docs/preflight-modes.mdsays explicitly that strict-stop is not fail-closed protection.
Known limits (no fail-closed claim)
- File-level corrupt or unreadable ledgers still fail open, as before. These changes are observability plus restored intended behavior.
- #546 stays open for the remaining items, and #553 (worker subagent denies don't reach the parent) is open.
Not in this release: the delegate slices S1–S6, #430 and #434.
Review provenance
Each is from the PR's comments.
- #556: Opus adversarial review requested changes at
04b5ae8b(the only blocker was a rebase onto #552). An Opus delta review approved42fcf2b7, and the refresh onto v0.1.68 (74d6cf7b) was re-checked side by side (108 rows). Merged as2beecf0eby Opus under Mitch's authorization. - #559: Opus adversarial review approved
48880c3e. After a LOW fix (a bad concept no longer skips the entry), an Opus delta re-review approved92dd5bed. Merged asc87f9187by Opus under Mitch's authorization; GitGuardian was skipped on the merge. A GitGuardian alert (incident 38341052) on #559 was checked and is a false positive:const SECRET = "sk-ant-api03-MCP-BAD-ENTRY";atpreflight-bad-entry-mcp.test.ts:18is a made-up redaction canary in a test. This is recorded on #559 and #560.
Verification
Measured on a fresh native clone, branch release/v0.1.69 off main c87f9187:
- Version sources: all four read 0.1.69.
- Diff vs
main: 7 files, 7 insertions and 7 deletions (the four version sources plus thedaemon.js,standalone.jsanddoctor.mjsstamps). - Cold build:
pnpm install --frozen-lockfile, thenpnpm build:clean; the regenerated stamps are committed. - Typecheck: 3 of 3 tasks clean.
- Lint: 0 errors; mcp-server 219 warnings (cap 240), shared 3.
- Tests: full
FORCE_COLOR=0 NO_COLOR=1 pnpm testpasses on the first run, in the foreground: mcp-server 403 files and 6,115 tests (108 skipped), shared 15 files and 321 tests. No timing failures. - Checks:
version-lockstepandplugin-bundle-bootpass (2 files, 5 tests). - Rebuild: a second cold
pnpm build:cleanleavesgit statusclean, andgrep -rF "0.1.68" claude-plugin/serverreturns nothing. - Conflict markers: none.
v0.1.68 — the companion shows why the gate stopped you
v0.1.68 ships the 13 PRs merged on main since v0.1.67, all merge commits: #530, #536, #537, #539, #541, #542, #545, #547, #548, #550, #551, #552, #555.
What ships
Headline: the companion shows why the gate stopped you
The default gate is still ask. On Claude Code 2.1.301 the native permission prompt does not show the reason (upstream anthropics/claude-code#102892), so deepPairing now shows it in the companion.
- The companion shows the stance (#552, refs #544): when the gate asks or denies, the companion shows the matched stance, your original reason and the target. The hook writes a bounded, control-stripped record to
hooks-state.jsonand makes no network call. The daemon delivers it with a file watcher plus a stat-poll fallback, routed to the right session. Detailed gate payloads don't reach other sessions or the legacyhook_firedbroadcast. - The warning is mirrored (#545): both the init-generated and the plugin-bundled hook also copy the reason into the documented top-level
systemMessagefield. Ask decisions and fail-open handling are unchanged. - Strict-stop is opt-in (#547):
DEEPPAIRING_PREFLIGHT_MODE=strict-stopmakes a matching editdenyand stop, with the reason in three fields. Unset, empty oraskkeeps today's output. Any other nonempty value conservatively stops an existing match with a fixed warning. - Worker handoff docs (#548, docs only): they record that a worker subagent's strict-stop deny does not reach the parent (#553), and they bound a supervised retrieval step after each worker.
Decisions and rejections
- Reject is easy to find (#550, fixes #543): Reject and Request changes are enabled on an untouched draft. With an empty reason they focus the response box and prompt for one. The reason is still required.
- Unchosen options no longer become stances (#551, fixes #549): picking A used to record B as rejected, so a later proposal of B hit the gate. Now a pick records only the chosen option. The resolved card lists the others as "Not chosen" with an explicit "Never suggest this" button that asks you to confirm first.
- Remembered state refreshes (#555): an open decision card stops saying "Remembered as rejected" after you retire that stance. Cross-project concept removal is a separate operation.
Subagents and worktrees
check_feedbackroutes by presenter (#539, fixes #533): a subagent's answer can't be consumed by a context-less sibling. Delivery is also crash-safe: acknowledgements happen after the response is built, so a throw or cancelled call doesn't lose items. A dead presenter's decisions are held for about 60 seconds, then released.- One project root across git worktrees (#536, fixes #532): a linked worktree now shares the main checkout's ledger, so the rejection gate holds there.
- Hooks in worktrees (#542, fixes #538): doctor and the first-call preamble warn when the main checkout has hooks and the worktree doesn't, and
initinstalls them. Nothing is written automatically.
Keyboard, CI and protocol
- Next-up keyboard and focus (#530): arrow-key navigation works, Escape closes the queue, and focus returns correctly when a row or the Why button disappears.
- Next-up keyboard smoke in ordinary PR CI (#537): three bounded real-browser cases run in the normal E2E path.
- Coordinator experiment protocol (#541, docs only): preparation for the bounded trial in #540. It launches no trial and changes no product behavior.
Known limits (not claimed as fixed)
askstays the default, and strict-stop is opt-in.- A worker subagent's strict-stop deny doesn't reach the parent (#553).
- Preflight read and evaluation failures can still fail open (#546; #557 is in progress). This release does not claim fail-closed.
Not in this release: #556 and #557 (in review), the delegate slices S1–S6, #430 and #434.
Review provenance
Each is from the PR's comments.
- #552: Opus approved
b44946c3; a Sonnet delta approved4611450f. Sol requested changes on two session-isolation leaks, fixed at75dd9247and approved by Sol and by a Claude Sonnet delta check. Sol then took over the main refresh and approved26c4c4ba, merged as557fae2f, under Mitch's direct ownership-handoff instruction, which is recorded on #552. - #551: Opus requested changes at
a53d34ee, then approved7dabdcf6; Fable's UX check approved. Sol's post-merge P3 became #555. Merged under Mitch's authorization. - #550: Fable UX approved
19ac42e4, and an Opus delta check approved560638c4. Merged under Mitch's authorization. - #555: Sol approved
7073367c, thend902a4ae, then the metadata-only merged589e1fc. CI at merge is unconfirmed from the PR record. - #547: a non-author review approved
858e0cbf. A post-merge Claude Opus review found nothing to revert. - #545: Sol (GPT-6.1) approved
f2c60e4b. #548: a non-author review approvedfc64cb94. - #539: Opus requested changes at
1ff5527b, then approved5dabe042. - #536: Opus approved
92747e44. #542: Opus approved3779751f. - #541: Opus requested changes at
8674521d(stale against #539 and #536). Sol approved the final head81401637, and an Opus delta approved it. - #530: Sol requested changes at
9050e3e(the Why-opener focus path), then approved07c2b871. #537: a Sol review approved80aa0068. - #536, #539 and #542 were merged by Opus under Mitch's authorization to merge adversarially reviewed PRs, now recorded on each PR. Merge authorization for #541, #530, #537, #545, #547 and #548 is unconfirmed here: the record on those PRs shows merges but not who authorized them.
Verification
Measured on a fresh native clone, branch release/v0.1.68 off main 9677cf83:
- Version sources: all four read 0.1.68.
- Diff vs
main: 7 files, 7 insertions and 7 deletions (the four version sources plus thedaemon.js,standalone.jsanddoctor.mjsstamps). - Cold build:
pnpm install --frozen-lockfile, thenpnpm build:clean; the regenerated stamps are committed. - Typecheck: 3 of 3 tasks clean.
- Lint: 0 errors; mcp-server 219 warnings (cap 240), shared 3.
- Tests: full
FORCE_COLOR=0 NO_COLOR=1 pnpm testpasses on the first run, in the foreground: mcp-server 399 files and 6,024 tests, shared 15 files and 321 tests. No timing failures. - Checks:
version-lockstepandplugin-bundle-bootpass (2 files, 5 tests). - Rebuild: a second cold
pnpm build:cleanleavesgit statusclean, andgrep -rF "0.1.67" claude-plugin/serverreturns nothing. - Conflict markers: none.
v0.1.67 — doctor for plugin installs, clock check
v0.1.67 ships the 3 PRs merged on main since v0.1.66: two merge commits (#524, #527) and one squash merge (#526).
What ships
Doctor for marketplace plugin installs (#527, closes #525)
Marketplace installs ship no npm CLI, so you couldn't run doctor. Now /deeppairing:doctor works, backed by a self-contained server/doctor.mjs inside the plugin. It shares the diagnostic code with the npm doctor (clock, daemon, hook, ledger and session-lock checks), and INSTALL.md has terminal instructions.
- Report-only by default: it names the project it resolved and rejects unsupported arguments. A non-interactive
--fixcancels. --fix --yesstays local: it repairs local metadata, gitignore, checkpoint registration and verified local dead locks. It never kills daemons, removes global locks, or touches shared or user hook rows. It also never removes local Stop, PreToolUse or Bash ask rows, because installation can't prove the plugin is enabled for this project. Removing those needs interactive consent, with a warning. Mixed rows keep unrelated commands.- Settings backups first: before any consented settings write, doctor saves an exact-byte backup under a unique name, prints its path, and refuses to write if the backup fails. Report-only, cancelled and no-op runs create none.
- One npm-side change: the npm doctor's ledger diagnosis is now read-only, so it no longer creates a corrupt-file snapshot. Other repair behavior there is unchanged.
Doctor warns about a stepping or drifting clock (#524, closes #500)
Doctor samples the wall clock against the monotonic clock for about 400 ms. It warns, in second person with the fix, if the wall clock steps backwards or drifts. On WSL, the fix is the Windows Time service plus wsl --shutdown. It is warning-only, with no exit-code change and no network. The CONTRIBUTING doc has a "WSL: clock drift" section. This adds the diagnostic for #500; it doesn't claim the flaky tests are gone.
Delegate-first design: packet recovery no longer depends on the clock (#526, docs only)
The design now orders packet recovery by a durable per-debrief Ask generation instead of timestamps. Equal timestamps and backward clock steps can no longer hide a later answered question. No runtime behavior changes.
Not in this release: the delegate slices S1–S6, #430 and #434.
Review provenance
- #527: Implemented by Codex. Per the coordination board, Mitch assigned #525 to Sol 6.1; the GitHub record shows only the shared account. An independent reviewer's two P2 findings were fixed (stale-daemon kill and global-lock repair now need confirmation), and Sol re-approved
ea625bf0and9cf629af. An independent Claude Opus review approved9cf629af, and Astra held the merge to address its recommendations (disabled-plugin handling and settings backups). Sol approved the delta at3233c478. A post-merge Claude verification with a real packaged probe found nothing new. Integrated by Astra. - #524: a Sonnet builder and an independent Sonnet review (approve at
1952509d). - #526: Codex-authored, per the
codex/branch and the coordination board (it follows Astra's final audit of #507). Sol approved8a1357a1and the delta0f2c3c5e, a Claude Opus review approved8a1357a1, and a Claude delta ack approved0f2c3c5e. - Every round is recorded on its PR.
Verification
Measured on a fresh native clone, branch release/v0.1.67 off main a67e04ef:
- Version sources: all four read 0.1.67.
- Diff vs
main: 7 files, 7 insertions and 7 deletions (the four version sources plus thedaemon.js,standalone.jsanddoctor.mjsstamps). - Cold build:
pnpm install --frozen-lockfile, thenpnpm build:clean; the regenerated stamps are committed. - Typecheck: 3 of 3 tasks clean.
- Lint: 0 errors; mcp-server 218 warnings (cap 240), shared 3.
- Tests: full
FORCE_COLOR=0 NO_COLOR=1 pnpm testpasses on the first run, in the foreground: mcp-server 389 files and 5,861 tests, shared 15 files and 321 tests. No timing failures. - Checks:
version-lockstepandplugin-bundle-bootpass (2 files, 5 tests). - Rebuild: a second cold
pnpm build:cleanleavesgit statusclean, andgrep -rF "0.1.66" claude-plugin/serverreturns nothing. - Conflict markers: none.
v0.1.66 — delegate preset, safer auto-approve, visible whitespace
v0.1.66 ships the 5 PRs merged on main since v0.1.65: four merge commits (#507, #508, #520, #522) and one squash merge (#519).
What ships
Delegate a task with one check-in and one packet (#507, #520)
You get a new opt-in preset, /deeppairing:delegate <issue | text>. It runs delegated work in three beats:
- An intent check: one brief that states what the agent understood, what done looks like, what it will decide alone, and what it will ask you.
- A quiet run: the agent interrupts you only for the decisions you need to make.
- One understanding packet: a debrief and its changeset arrive together as a single item to review.
It is guidance only. It uses the existing tools and adds a "Delegated work" paragraph to the pairing SKILL. The design doc (docs/design/delegate-first.md, #507) ships with it.
What this is not. Delegate is not a new autonomy level yet, and it is not the default. The fourth level, the default flip and the packet UI are slices S1–S6 of the design. None of them ships here.
Auto-approve stops when you lower your autonomy (#522, fixes #521)
- The countdown cancels: an armed auto-approve countdown now cancels, and its UI clears, as soon as the artifact stops being eligible. That covers lowering your autonomy level to supervised, a confidence change, the status leaving draft, and hiding the approve button.
- The daemon refuses a stale auto-approval: auto approvals now send
reason: "auto_confidence". The daemon re-checks at commit and answers 409auto_approve_not_permitted, with no state change, if your level no longer permits it. Your own clicks are unaffected. - Limits:
balancedis still eligible, so autonomous to balanced keeps counting down. Thea/Enter keymap countdown is not revoked. A hostile client can leave outreason, which the review noted as a residual.
The CLI preview marks whitespace and invisible characters (#508, #519; fixes #506, #518)
- Changed whitespace is visible: spaces and tabs on changed lines show as
⟨SPACE⟩and⟨TAB⟩. This includes whitespace-only differences inside mixed edits, trailing whitespace and final-newline changes. - Invisible characters are marked: non-ASCII spaces, default-ignorable code points and the braille blank show as tokens (including U+3164, U+2800 and U+034F). Literal marker text is escaped, so it can't pass for a marker.
- Mixed edits (#519): a replacement that also changes text, such as
foo(x)tofoo(y), now shows the trailing space. Ordinary context and identical-layout edits stay readable.
Not in this release: the delegate slices S1–S6, #500 (flaky tests, a WSL clock-offset environment issue), and #430/#434.
Review provenance
- #507: Opus UX review (changes at rev 2, approved at rev 3). Astra's architecture review ran four rounds of findings, all addressed. An independent Opus architecture review approved rev 7, and a Sonnet delta check approved rev 7.1. Merged under Mitch's authorization; Astra's review of the final head is welcome after the fact.
- #520: an independent Sonnet review approved
8404f32e(field names checked against the schemas, 31/31 pass). Merged as4a370dc5after #507 landed. - #522: an independent Opus adversarial review approved
d7350ea2. A Sonnet delta review approvedb2d3ec90. Merged under Mitch's authorization. - #508: Astra approved
b0ab8665(Sol authored), and an independent Claude adversarial review approved the same head (a brute force of 88,741 strings per mode found no collisions). Merged asb9f9fb40. - #519: Sol approved the exact head
6172369d(33,306 distinct pairs, all previews unique), with a root integration review in agreement. Merged under delegated second-agent approval. - Every round is recorded on its PR.
Verification
Measured on a fresh native clone, branch release/v0.1.66 off main e84b2772:
- Version sources: all four read 0.1.66.
- Diff vs
main: 6 files, 6 insertions and 6 deletions (the four version sources plus thedaemon.js/standalone.jsstamps). - Cold build:
pnpm install --frozen-lockfile, thenpnpm build:clean; the regenerated stamps are committed. - Typecheck: 3 of 3 tasks clean.
- Lint: 0 errors; mcp-server 218 warnings (cap 240).
- Tests: full
FORCE_COLOR=0 NO_COLOR=1 pnpm testpasses on the first run, in the foreground: mcp-server 386 files and 5,839 tests, shared 15 files and 321 tests. No timing failures, so there was nothing to re-run. - Checks:
version-lockstepandplugin-bundle-bootpass (2 files, 5 tests). - Rebuild: a second cold
pnpm build:cleanleavesgit statusclean, andgrep -rF "0.1.65" claude-plugin/serverreturns nothing. - Conflict markers: none.
v0.1.65 — allow one proposal past a stance
v0.1.65 ships the 17 PRs merged on main since v0.1.64: five merge commits (#478, #499, #501, #503, #505) and twelve squash merges (#439, #497, #498, #502, #504, #509, #510, #512, #513, #514, #515, #516).
What ships
Allow one proposal past a stance (#470, design #478)
Until now, when a stance blocked a proposal you actually wanted, your only option was to retire the whole stance. Now you can let one proposal through and keep the stance. This closes #470. It landed in four slices, built to the design in #478 (rev 6, design doc only).
Daemon core (#499).
- Single-use and human-only: only a human request can grant an allowance, through one bearer-gated route. A request carrying the agent wrapper's registration header gets a 403. The body must be exactly a reason of 3–280 characters. Team rules, demo sessions, retired stances, ended sessions and ineligible blocks are refused.
- Bound to what you saw: the allowance is bound to the effective proposal snapshot and its preconditions, which the daemon computes itself, not just the raw call.
- Short-lived: it lives only in the daemon's memory (nothing is written to
preferences.json, and a daemon restart ends every grant). It ends when the session ends, and an unused one never outlives 72 hours. - Receipts: the block log records allowed, used, changed and revoked, with ended and expired shown on read.
Web UI (#501).
- Allow once is the primary action on the block toast and the gate-log entry. It opens a dialog with a preview of exactly what you're allowing (a diff for a code change, every option for a decision) and asks for a reason.
- Retire moved behind a confirm: the toast no longer offers a one-click Retire. In the gate log, Retire asks "This deletes the stance from this project…" with focus on Cancel.
- Changed state: if the proposal's dependency changed, the allowance isn't used, and the card says so and links the new block.
- Honest receipts: allowed, used, changed, revoked, "ended (not used)" and "expired (not used)". An allowed receipt offers Revoke.
- Attention bar: a grant drops that block from Held, and a dependency-changed refusal adds a new held block. Held and its Why stay read-only, and an admitted proposal's Decide "Why" reads "Admitted under your allowance…".
CLI, Bash ask, guarantee docs (#503).
deeppairing stance allow <blockId>: interactive only. It refuses flags,CLAUDECODE=1andCI, goes only through the daemon's public route, shows a paged unified-diff preview first (never raw JSON, and different inputs never render the same), then asks for a reason and for you to typeallow.stance exceptions [revoke <id>]lists and revokes allowances.- Narrow Bash ask: a POSIX
sh+awkhook asks you when the agent's shell command contains the wordsstance allowor the grant route's path. Every other command gets no output and exit 0. - Docs: README, SECURITY and the pairing SKILL describe the guarantee and its limits.
deeppairing-state guardrail (#505). Agent Edit, Write, MultiEdit and NotebookEdit calls on .deeppairing/preferences.json, .deeppairing/team.json, anything under .deeppairing/hooks/, or .claude/settings.json / .claude/settings.local.json (project or home) now always ask first. A live findings/options/spec/plan doesn't silence it, the 30-minute dedup window doesn't apply, and DEEPPAIRING_GUARDRAIL_BACKSTOP=off doesn't turn it off. The path check follows symlinks before .., and handles Windows separators and prefixes, trailing dots and spaces, NTFS stream suffixes and case. Older local hook rows are upgraded in place, keeping your own rows.
What this does not guarantee. From SECURITY.md, as shipped:
- Human-only by design, not by enforcement. A process running as your user can still script a terminal, get past the Bash hook's pattern match (build the words or the URL at runtime, encode the command, run a script from a file), or read the bearer token from
.deeppairing/daemon.json(or its runtime sidecar) and call the grant route itself. The hook only asks when the route's path appears literally in the command. Every grant is shown to you in the receipts, but the receipts are not tamper-evident, and theUI/CLIlabel is a self-reported header. - Not guarded by the state guardrail: Bash writes (
echo >,sed -i,cp,mv,tee), hard links, Windows 8.3 short names, the plugin's installed copies ofserver/preflight.mjsandhooks/stance-allow-ask.shin Claude Code's plugin cache, the plugin'shooks/hooks.json, and the builtdist/matcher core and path module. Disabling or uninstalling the plugin removes the guard entirely, along with every other hook it registers. - Permission modes: "you get a prompt in the default,
acceptEdits,planandautomodes and a refusal where nobody can answer; inbypassPermissionsit is no protection."dontAsk"auto-denies every call that would otherwise prompt", and a-prun sends prompts to the Agent SDK host or--permission-prompt-toolif there is one, and denies them if there isn't.
Also in this release
- Review route boundary (#439, issue #433): decision resolution, artifact verdicts and changeset file review move into their own narrow review router. Refactor only; the handlers match
main's behaviour, including #484/#491/#493. - Hydration readiness after resets (#497, refs #430): switching sessions (including A→B→A), resetting the same session, or tearing down the adapter now retires the old "loaded" evidence, so a tab can't report a state it never loaded. Ordinary reconnects keep a loaded frame.
- Attention preview docs (#498, refs #430): corrects stale status notes; the settings copy now says the preview replaces the pending/unanswered banners. No behaviour or default changes.
- Hook lock uses a monotonic clock (#512, closes #511): the hook-state lock's 500 ms wait budget now uses
process.hrtime, so a wall clock that steps back or freezes can't stretch the wait. Stale-lock classification is unchanged. - Test-only (refs #430, #500):
- #502: an opt-in synthetic attention walkthrough harness (48 rows on real daemons). It is not a human usability result.
- #504, #509, #514: bounded diagnostics for hook subprocess, stale-lock and checkpoint-receipt failures.
- #510, #516: stale-lock and checkpoint-scope fixtures declare their clock premise, after wall-clock reversals were observed.
- #513: the Playwright diagnostics test runs in the serialized lane.
- #515: the ledger replayer handoff test owns and bounds its child process.
- #500 stays open: these collect evidence and fix specific mechanisms; they don't claim the flakes are gone.
Not in this release: the delegate-first design (#507, still in review), #506 (whitespace and invisible-character marking) and #500 (test flakes, still open).
Review provenance
- #499, #501, #503: independent approvals from Codex (Astra, Sol, Luna) and Claude, plus Fable's UX approval for #501. Each landed tree is the approved source merged with
main, with the bundle regenerated cold. - #505: an independent Claude delta review verified Sol's and Luna's three P2 fixes (200 cases through both real launchers, zero mismatches). Codex's recheck of the final head is still pending.
- #478: the design was approved by Astra at rev 5; rev 6 answered Fable's final design review.
- Merges: #499, #501, #503 and #505 were merged by Opus under Mitch's authorization.
- Codex-authored PRs: reviewed and integrated by Codex. From each PR's record:
- Every round is recorded on its PR.
Verification
Measured on a fresh native clone, branch release/v0.1.65 off main 72af6f02 (head addd78f3):
- Version sources: all four read 0.1.65.
- Diff vs
main: 6 files, 6 insertions and 6 deletions (the four version sources plus thedaemon.js/standalone.jsstamps). - Cold build:
pnpm install --frozen-lockfile, thenpnpm build:clean; the regenerated stamps are committed. - Typecheck: 3 of 3 tasks clean.
- Lint: 0 errors; mcp-server 218 warnings (cap 240), shared 3 (cap 5).
- Tests: full
pnpm testpasses, 399 files and 6,100 tests (mcp-server 384 / 5,779, shared 15 / 321).- The first full run, at a host load average near 40, had 4 failures:
AgentActivityWindow, a 10 s timeout inStanceExceptionStaleTab,checkpoint-scope"real-clock smoke", and a 15 s timeout instance-allow-ask"200 ordinary commands". Thecheckpoint-scopereceipt showed the wall clock stepping backwards, the #500 class. - All four files then passed alone (92 tests), and a second full run passed.
- The first full run, at a host load average near 40, had 4 failures:
- Checks:
version-lockstepandplugin-bundle-bootpass (2 files, 5 tests). - Rebuild: a second cold
pnpm build:cleanleaves the tree clean, andgrep -rF "0.1.64" claude-plugin/serverreturns nothing. - Conflict markers: none.
v0.1.64 — offline gaps closed, closed decisions refuse answers
v0.1.64 ships the four PRs merged on main since v0.1.63.
What ships
The rest of the offline gating, and an honest stalled load (#487, closes #477, refs #430).
- Remaining act paths gated offline: per-finding verdicts on research artifacts, the suggestion counter actions (Take the counter, Insist on mine, the counter reply), the conversation rail reply, and Send request. Each is disabled with the same visible reason as #467's controls, drafts are kept, and it re-enables on reconnect. The request composer now stays on screen during an outage instead of disappearing with your text. It is still hidden before the first connect.
- Stalled hydration is bounded: a connected tab whose first snapshot never applies used to show "Checking what needs you…" forever. After 10 s it now says "Still loading this session — it's taking longer than usual. It may still finish." with a Reload button. The deadline is scoped to the session and project the tab is bound to, so switching sessions doesn't inherit the previous one's state or deadline. A late load clears the message. With the Next-up bar on, it is announced once.
- Reload asks first: if any composer holds unsaved text, Reload opens an in-app confirm. Keep my text is the default (focused, Enter and Esc both keep), and only "Reload anyway" discards.
- No duplicate request: a request that succeeds after you switch A→B→A no longer brings its sent text back with Send enabled.
Resolve settle follow-ups (#491, closes #490).
- No spurious wake: a resolve that 503s and is rolled back no longer wakes long-polling
check_feedbackwaiters. They wake only when the write commits, or when something else happened during the hold. - Partial-write flush: if a flush lands
decisions.jsonand then fails on a later file, the answer is on disk. The request now counts as committed: 200, announced once, delivered. Otherwise it rolls back as before, and the rollback schedules a re-flush so what didn't land converges.
Late answers on closed decision cards are refused (#493, closes #492).
- Refusal: answering a decision whose card is superseded, retracted or obsolete now gets a 409
decision_closed, on both the public and internal resolve routes. Nothing is written or announced. The check runs inside the same atomic resolve as #484, so an identical re-pick of a recorded answer is still a no-op 200. - Revised questions: for a superseded card, or one that already has a newer version, the 409 links to the newest version. When that newer version can't take an answer either, there's no link, and the copy says so honestly.
- One rule: a shared
decisionCanAcceptAnswerdrives both the refusal and the successor link, on the server and in the card. - The stale card shows the closed state, with options readable and no Select buttons.
CI only: e2e time limits (#495, closes #494). The e2e job gets a 25-minute timeout and its Chromium install a 10-minute one. This came from a hung install that left #483's check pending for 4 hours. Not user-facing.
Not in this release: #439 and #478.
Review provenance
- #487: a Fable UX review (2 rounds), plus independent Claude lifecycle reviews that verified Sol's findings fixed.
- #491 and #493: independent Claude adversarial reviews. The #491 delta was written by Opus and reviewed by the Claude reviewer.
- #495: an independent Sonnet review.
- Merges: all four were merged by Opus under Mitch's authorization. The Codex re-review of #487 is still pending.
- Every round is recorded on its PR.
Verification
Measured on a fresh native clone, branch release/v0.1.64 off main d7e66ee4 (head 65abf694):
- Version sources: all four read 0.1.64.
- Diff vs
main: 6 files, 6 insertions and 6 deletions (the four version sources plus thedaemon.js/standalone.jsstamps). - Cold build:
pnpm install --frozen-lockfile, thenpnpm build:clean; the regenerated stamps are committed. - Typecheck: 3 of 3 tasks clean.
- Lint: 0 errors; mcp-server 227 warnings (cap 240), shared 3 (cap 5).
- Tests: full
pnpm testpasses, 375 files and 5,700 tests (mcp-server 360 / 5,379, shared 15 / 321). - Checks:
version-lockstepandplugin-bundle-bootpass (2 files, 5 tests). - Rebuild: a second cold
pnpm build:cleanleaves the tree clean, andgrep -rF "0.1.63" claude-plugin/serverreturns nothing. - Conflict markers: none.
v0.1.63 — one answer per decision, durable ledger mirrors
v0.1.63 ships the 15 PRs merged on main since v0.1.62.
What ships
One answer per decision (#464, closes #460; #484).
- Second resolutions rejected: resolving a decision a second time used to silently overwrite the first answer and return 200. Now a different pick, or a pick on a decision closed elsewhere, gets a 409
verdict_already_finalthat carries the recorded winner. The same pick is a true no-op. Both the public and internal resolve routes go through the same guard. - Atomic check-and-resolve (#484): Astra's review of #464 found the stale check wasn't atomic with the write, so two concurrent picks could both write (statuses
[400, 200]). Classify-and-write is now one store operation,resolveDecisionAtomic; N concurrent different picks give exactly one write, and every 409 names the same winner. - Durable before success: a resolve reports success only after a durable flush. If the flush lock is busy, you get a 503 and the write is rolled back in memory, so
check_feedbacknever delivers an answer that wasn't saved. A retry after that is an ordinary first write. - Announced once: the first successful save publishes
decision_resolvedexactly once, with the recorded winner. - Stale card: it shows the winning pick with no Select buttons, and the toast says only what's true.
- Sibling change signal: a per-session
revisioncounter (with a restart epoch) moves on status changes and comments, so a decision resolved in another tab clears, and a sibling's new question appears in Waiting. Quiet siblings cost nothing. Older daemons fall back to the artifact count. - Not in this release: two LOW follow-ups are tracked in #490 / #491, which is still in review.
Ledger mirrors survive a busy lock (#488, closes #486).
- Queued, not dropped: when the cross-project ledger lock is busy, a rejection, approval or retire mirror used to fail with
ELOCKEDand only be logged. It is now queued in.deeppairing/ledger-mirror-pending.jsonand replayed on backoff, after the next successful mirror, and when the project's store next starts (for example after a daemon restart). The local record still commits first. - Exactly once: the ledger append is deduplicated on
(project, sessionId, verdict, at), so a replay that already landed, or several processes replaying at once, never adds a duplicate. - No resurrection:
deeppairing philosophy removewrites a removal tombstone under the ledger lock, and a queued mirror from before the removal is skipped. Retiring a rejection cancels its queued mirror. - Consent under the lock: the publish flag, the local row and the approved pattern are re-read from disk inside the ledger lock right before each append. Withdrawn consent drops the queue. A refused write (for example, a corrupt ledger) stays queued instead of being dequeued.
- Bounds: the queue is capped at 200 entries and 30 days.
Honest offline state (#467, refs #430).
- Act buttons: disabled while the tab is offline, with the reason shown, and drafts are kept.
- No false flash on load: a 3s first-connect grace means no "Disconnected" banner on every load. With the Next-up bar on, it shows "Checking what needs you…" until hydration settles.
- Clearer text: an item from another session gets its session label (N1). The copy says this tab lost its connection (N3). 92s now reads "1 min" (N4).
- Remaining offline gaps are tracked in #477 / #487. They are not fixed in this release.
Decision search and failed session opens (#475, closes #469). The project decisions search now matches the recorded reasoning. Opening a past session that fails (HTTP, network or malformed response) shows an alert with Retry, instead of failing silently, and leaves the live view untouched.
Annotation reads no longer create sessions (#479, closes #472). GET /api/sessions/:id/annotations used to create an empty session directory for an unknown id and return 200. It now returns 404 for an absent session, 200 for a real one, and 500 for a genuine read failure.
Authenticated daemon recovery notifications (#480, closes #468). After a daemon loses its session map, the client's recovery notification now carries the bearer token and project header. Failures stay logged and non-fatal.
Isolated sample demo (#481, closes #471). A new claude-plugin/server/demo.mjs runs a scripted sample of a first review from the installed plugin with plain node, no build. It runs in a throwaway temp HOME and sample project on its own port window (41000-41063), and cleans up on exit. It is labelled as scripted: no AI model is called.
Node 20 deprecated, removal planned (#466, closes #461). Node 20 still works, but it's deprecated, with removal no earlier than v0.2.0 and not before January 2027. Node 22 or 24 is recommended. The plugin prints a one-line deprecation notice to stderr only, so stdout stays clean for MCP.
Upgrade preservation test (#482). A new offline test boots the released v0.1.57 runtime, creates sessions, then checks that this candidate reads them, keeps rejection memory blocking, and survives a restart. It also documents backup/restore and the downgrade boundary.
Test reliability (#485, #489). The cross-process ledger race tests re-submit documented ELOCKED refusals instead of treating them as crashes, and the lock-hog case now detects a silently dropped write. Test-only.
- #465: the #430 attention walkthrough rerun after #458/#459 (the findings #467 fixes).
- #476 (closes #473): Plan Mode comparisons re-verified against current sources, positioned on decision continuity.
- #435: protocol preparation for the consent-gated external pairing pilot (#434). It does not start the pilot; Mitch's approval is still required.
Not in this release: #487, #490 / #491 and #439.
Review provenance
- Claude-side adversarial review: #464, #467, #466, #484 and #488, with independent Opus reviewers. Every round is recorded on its PR.
- Codex review and integration: #475, #476, #479 and #481 (Sol/Astra). #485 was reviewed by Luna and integrated by Astra; a Claude review found a MEDIUM problem after merge, which #489 fixed. #435 was reviewed by Terra and Sol, plus a Claude review.
- Independent Claude approval, Astra integration: #480 and #482.
- #489: signed off by an independent agent; the reviewer isn't named in the record.
- #484 and #488 were merged by Opus under Mitch's explicit authorization, after independent Claude reviews verified every Codex finding fixed. The Codex re-review of their final heads was still pending at merge.
Verification
Measured on a fresh native clone, at the merge of main 927d25f5 into this branch (head 54537b3f):
- Version sources: all four read 0.1.63.
- Diff vs
main: 6 files, 6 insertions and 6 deletions (the four version sources plus thedaemon.js/standalone.jsstamps). - Cold build:
pnpm install --frozen-lockfile, thenpnpm build:clean;git status --porcelainis empty, so the committed bundle matches. - Typecheck: 3 of 3 tasks clean.
- Lint: 0 errors; mcp-server 227 warnings (cap 240), shared 3 (cap 5).
- Tests: full
pnpm testpasses, 369 files and 5,640 tests (mcp-server 354 / 5,319, shared 15 / 321). - Checks:
version-lockstepandplugin-bundle-bootpass (2 files, 5 tests). - Rebuild: a second cold
pnpm build:cleanleaves the tree clean, and no0.1.62string remains inclaude-plugin/server(the literalgrep -r "0.1.62"regex matches one unrelated KaTeX number,0,1062;grep -rFreturns nothing). - Conflict markers: none.
v0.1.62 — nothing hides, nothing overclaims
Work waiting in another session no longer goes unnoticed, and the Next-up bar preview only tells you things that are true.
What changed
- No missed work from other sessions (#458): if the session you had open had no items yet, decisions waiting in your other sessions weren't counted. The tab title showed nothing, and the Next-up bar said "Nothing needs you". Other sessions are now always included, and a new decision there appears in your tab and is announced once. Their existing history loads quietly, without "new" highlights. This applies with or without the bar.
- The Next-up bar preview is accurate (#459): after the agent exits, while you're disconnected, or during replay, the bar no longer promises what Claude will do next. It now:
- names the session each item belongs to;
- shows what a held stance actually stopped;
- announces the open-questions count and "Copied ✓";
- shows "(last known)" when disconnected, with a
doctor --fixhint after a minute.
- Validation report (#457): the design's task-based walkthroughs, comparing the bar off and on, were recorded in
docs/design/attention-walkthroughs.md, with an independent review. Every defect that had to be fixed first is fixed in this release.
Known
Another session can still look out of date in two cases: when one of its items changes status, and when someone asks a question there. A fix is in review as #464, closing #460.
Release verification
This release changes only the four version sources and the two generated server version stamps, to 0.1.62. Each PR was reviewed adversarially at its exact merged head by a non-author, using headless probes against real daemons, and every round is recorded on its PR. The release reviewer booted the plugin and got 0.1.62. A cold rebuild reproduces the committed bundle, the full suite passes (5,524 tests), and CI is green. Details are on release PR #463.
v0.1.61 — lanes you can read at a glance
The sidebar now shows at a glance what each item needs from you, and several small annoyances are fixed for everyone.
What changed
- Lane glyphs in the sidebar (#454): every row shows what it needs from you: ▲ decide, ● review, ○ read, ◌ waiting on the agent. The glyphs match the Next-up bar exactly. Hover or keyboard focus shows the label, and the glyph shapes stay distinct in high-contrast mode. Low-contrast dots were also fixed in dark mode.
- Reload works after switching projects (#455): a stale-daemon warning could leave a Reload button that did nothing. Now a new warning replaces the old one, and Reload always reloads.
- Quieter indicators (#455):
- the ⋯ menu dot lights only for things you haven't seen;
- the session dot pulses only while the agent is actually working;
- the two approve countdowns now behave the same way.
- With the Next-up bar preview on (#455):
- a "✎ Request" button in the header;
- the demo and recap cards are reachable from the bar through a "Next step ⌄" marker;
- the agent filter becomes a menu.
Release verification
The release changes only the four version sources and the two generated server version stamps to 0.1.61. Both PRs were reviewed adversarially at their exact merged heads by a non-author, using headless-browser probes on a real demo session, and every round is recorded on its PR. The release reviewer booted the plugin and got 0.1.61. A cold rebuild reproduces the committed bundle, the full suite passes (5,508 tests), and CI is green. Details are on release PR #456.
v0.1.60 — the Next-up bar, as a preview
Try the Next-up bar: a one-line view of what needs you next. It's off by default.
What changed
- Next-up bar preview (#451, #452): turn it on in Settings → "Next-up bar (preview)".
- One line, one item: a single line under the session tabs shows the oldest thing waiting on you, whether that's a decision (▲) or something to review (●). It says why it matters and what happens next, and has an Open button. It also shows "+N high decision" and a count for each lane.
- Problems first: connection problems (disconnected, stale, snapshot, conflict, replay) always show first.
- Replaces the banners: with the bar on, it takes over the pending and resume-questions banners. If the agent has exited with your questions still open, the bar offers Copy resume prompt.
- Accessibility: screen readers hear each change once, and a "Jump to next up" link comes first when you tab.
- With it off: nothing changes.
- Dismiss a changeset from the changeset itself (#452): changesets now have their own two-step "Dismiss — overcome by new information", with or without the bar. Before, the only way to dismiss one was the ✕ on the pending banner.
- "Next pending" command: the command palette's "Next pending" jumps to the oldest item waiting for you.
Release verification
The release changes only the four version sources and the two generated server version stamps to 0.1.60. Both PRs were adversarially reviewed at their exact merged heads by a non-author, using headless-browser probes, and each review round is recorded on its PR. The release reviewer booted the plugin and got 0.1.60. A cold rebuild reproduces the committed bundle, the full suite passes (5,459 tests), and CI is green. Details are on release PR #453.