Repository navigation
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.