Skip to content

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.