Skip to content

Releases: mitchjablonski/deepPairing

v0.1.69 — one bad ledger entry no longer disables every stance

Choose a tag to compare

@mitchjablonski mitchjablonski released this 18 Oct 01:25
feeb796

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_unreadable and ledger_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.md says 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 approved 42fcf2b7, and the refresh onto v0.1.68 (74d6cf7b) was re-checked side by side (108 rows). Merged as 2beecf0e by 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 approved 92dd5bed. Merged as c87f9187 by 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"; at preflight-bad-entry-mcp.test.ts:18 is 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 the daemon.js, standalone.js and doctor.mjs stamps).
  • Cold build: pnpm install --frozen-lockfile, then pnpm 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 test passes 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-lockstep and plugin-bundle-boot pass (2 files, 5 tests).
  • Rebuild: a second cold pnpm build:clean leaves git status clean, and grep -rF "0.1.68" claude-plugin/server returns nothing.
  • Conflict markers: none.

v0.1.68 — the companion shows why the gate stopped you

Choose a tag to compare

@mitchjablonski mitchjablonski released this 18 Oct 00:49
8bcdefa

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.json and 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 legacy hook_fired broadcast.
  • The warning is mirrored (#545): both the init-generated and the plugin-bundled hook also copy the reason into the documented top-level systemMessage field. Ask decisions and fail-open handling are unchanged.
  • Strict-stop is opt-in (#547): DEEPPAIRING_PREFLIGHT_MODE=strict-stop makes a matching edit deny and stop, with the reason in three fields. Unset, empty or ask keeps 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_feedback routes 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 init installs 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)

  • ask stays 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 approved 4611450f. Sol requested changes on two session-isolation leaks, fixed at 75dd9247 and approved by Sol and by a Claude Sonnet delta check. Sol then took over the main refresh and approved 26c4c4ba, merged as 557fae2f, under Mitch's direct ownership-handoff instruction, which is recorded on #552.
  • #551: Opus requested changes at a53d34ee, then approved 7dabdcf6; 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 approved 560638c4. Merged under Mitch's authorization.
  • #555: Sol approved 7073367c, then d902a4ae, then the metadata-only merge d589e1fc. 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 approved fc64cb94.
  • #539: Opus requested changes at 1ff5527b, then approved 5dabe042.
  • #536: Opus approved 92747e44. #542: Opus approved 3779751f.
  • #541: Opus requested changes at 8674521d (stale against #539 and #536). Sol approved the final head 81401637, and an Opus delta approved it.
  • #530: Sol requested changes at 9050e3e (the Why-opener focus path), then approved 07c2b871. #537: a Sol review approved 80aa0068.
  • #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 the daemon.js, standalone.js and doctor.mjs stamps).
  • Cold build: pnpm install --frozen-lockfile, then pnpm 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 test passes 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-lockstep and plugin-bundle-boot pass (2 files, 5 tests).
  • Rebuild: a second cold pnpm build:clean leaves git status clean, and grep -rF "0.1.67" claude-plugin/server returns nothing.
  • Conflict markers: none.

v0.1.67 — doctor for plugin installs, clock check

Choose a tag to compare

@mitchjablonski mitchjablonski released this 12 Oct 20:49
a3eaa23

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 --fix cancels.
  • --fix --yes stays 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 ea625bf0 and 9cf629af. An independent Claude Opus review approved 9cf629af, and Astra held the merge to address its recommendations (disabled-plugin handling and settings backups). Sol approved the delta at 3233c478. 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 approved 8a1357a1 and the delta 0f2c3c5e, a Claude Opus review approved 8a1357a1, and a Claude delta ack approved 0f2c3c5e.
  • 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 the daemon.js, standalone.js and doctor.mjs stamps).
  • Cold build: pnpm install --frozen-lockfile, then pnpm 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 test passes 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-lockstep and plugin-bundle-boot pass (2 files, 5 tests).
  • Rebuild: a second cold pnpm build:clean leaves git status clean, and grep -rF "0.1.66" claude-plugin/server returns nothing.
  • Conflict markers: none.

v0.1.66 — delegate preset, safer auto-approve, visible whitespace

Choose a tag to compare

@mitchjablonski mitchjablonski released this 12 Oct 18:06
bfbdc6c

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 409 auto_approve_not_permitted, with no state change, if your level no longer permits it. Your own clicks are unaffected.
  • Limits: balanced is still eligible, so autonomous to balanced keeps counting down. The a/Enter keymap countdown is not revoked. A hostile client can leave out reason, 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) to foo(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 as 4a370dc5 after #507 landed.
  • #522: an independent Opus adversarial review approved d7350ea2. A Sonnet delta review approved b2d3ec90. 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 as b9f9fb40.
  • #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 the daemon.js / standalone.js stamps).
  • Cold build: pnpm install --frozen-lockfile, then pnpm 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 test passes 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-lockstep and plugin-bundle-boot pass (2 files, 5 tests).
  • Rebuild: a second cold pnpm build:clean leaves git status clean, and grep -rF "0.1.65" claude-plugin/server returns nothing.
  • Conflict markers: none.

v0.1.65 — allow one proposal past a stance

Choose a tag to compare

@mitchjablonski mitchjablonski released this 12 Oct 01:14
8857686

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=1 and CI, 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 type allow. stance exceptions [revoke <id>] lists and revokes allowances.
  • Narrow Bash ask: a POSIX sh + awk hook asks you when the agent's shell command contains the words stance allow or 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 the UI/CLI label 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 of server/preflight.mjs and hooks/stance-allow-ask.sh in Claude Code's plugin cache, the plugin's hooks/hooks.json, and the built dist/ 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, plan and auto modes and a refusal where nobody can answer; in bypassPermissions it is no protection." dontAsk "auto-denies every call that would otherwise prompt", and a -p run sends prompts to the Agent SDK host or --permission-prompt-tool if 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 the daemon.js / standalone.js stamps).
  • Cold build: pnpm install --frozen-lockfile, then pnpm 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 test passes, 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 in StanceExceptionStaleTab, checkpoint-scope "real-clock smoke", and a 15 s timeout in stance-allow-ask "200 ordinary commands". The checkpoint-scope receipt showed the wall clock stepping backwards, the #500 class.
    • All four files then passed alone (92 tests), and a second full run passed.
  • Checks: version-lockstep and plugin-bundle-boot pass (2 files, 5 tests).
  • Rebuild: a second cold pnpm build:clean leaves the tree clean, and grep -rF "0.1.64" claude-plugin/server returns nothing.
  • Conflict markers: none.

v0.1.64 — offline gaps closed, closed decisions refuse answers

Choose a tag to compare

@mitchjablonski mitchjablonski released this 08 Oct 20:48
ee7fcfd

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_feedback waiters. They wake only when the write commits, or when something else happened during the hold.
  • Partial-write flush: if a flush lands decisions.json and 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 decisionCanAcceptAnswer drives 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 the daemon.js / standalone.js stamps).
  • Cold build: pnpm install --frozen-lockfile, then pnpm 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 test passes, 375 files and 5,700 tests (mcp-server 360 / 5,379, shared 15 / 321).
  • Checks: version-lockstep and plugin-bundle-boot pass (2 files, 5 tests).
  • Rebuild: a second cold pnpm build:clean leaves the tree clean, and grep -rF "0.1.63" claude-plugin/server returns nothing.
  • Conflict markers: none.

v0.1.63 — one answer per decision, durable ledger mirrors

Choose a tag to compare

@mitchjablonski mitchjablonski released this 08 Oct 20:11
0aac281

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_final that 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_feedback never delivers an answer that wasn't saved. A retry after that is an ordinary first write.
  • Announced once: the first successful save publishes decision_resolved exactly 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 revision counter (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 ELOCKED and only be logged. It is now queued in .deeppairing/ledger-mirror-pending.json and 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 remove writes 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.

Docs (#465, #476, #435).

  • #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 the daemon.js / standalone.js stamps).
  • Cold build: pnpm install --frozen-lockfile, then pnpm build:clean; git status --porcelain is 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 test passes, 369 files and 5,640 tests (mcp-server 354 / 5,319, shared 15 / 321).
  • Checks: version-lockstep and plugin-bundle-boot pass (2 files, 5 tests).
  • Rebuild: a second cold pnpm build:clean leaves the tree clean, and no 0.1.62 string remains in claude-plugin/server (the literal grep -r "0.1.62" regex matches one unrelated KaTeX number, 0,1062; grep -rF returns nothing).
  • Conflict markers: none.

v0.1.62 — nothing hides, nothing overclaims

Choose a tag to compare

@mitchjablonski mitchjablonski released this 07 Oct 02:18
3a4a44a

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 --fix hint 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

Choose a tag to compare

@mitchjablonski mitchjablonski released this 07 Oct 00:46
f1d7f42

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

Choose a tag to compare

@mitchjablonski mitchjablonski released this 29 Sep 03:01
799a123

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.