feat(guide): entry authorization replaces per-action consent [spec 03-03] - #137
Merged
Conversation
The guide's message loop dropped tool_use and tool_result blocks, so a long repair (brew upgrade during setup) showed nothing between the permission ask and the agent's next sentence. runGuideSession now surfaces both through GuideRequest callbacks, and runSetup routes them to the host: the command as it starts, the tail of its output when it lands, errors labeled. A secret-bearing tool use the user approved by hand keeps its output withheld — info lines mirror into the dev log. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Commands are the diagnostics trail (quit latency, menu picks); chat and ask answers stay out — an answer may be a pasted secret. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-03] A typed /quit (or Ctrl-C) mid-onboarding now ends the run in under a second instead of waiting out the guide's turn in flight: - GuideRequest gains an interrupt signal; runGuideSession races every message against it and closes the SDK subprocess on fire (and now restores the for-await guarantee: the iterator is closed however the loop exits, a thrown handler included). - runSetup skips the re-probe and the closing verdict when the user is leaving; cliPermission/cliConversation stop asking once the latch has fired. - lineReader's latch is required, and a read resolved through EOF or the latch marks itself settled so its stale peekLine callback can no longer swallow a later line (the second /quit a stuck user types). - first-run binds the fallback latch it actually checks, so a latch-less /quit writes no persona marker. - One escalating SIGINT handler covers onboarding through the Director handover (and the explicit make setup entries), so a plain-mode Ctrl-C is never a bare process death mid-question. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The mirror keyed on a leading slash, so an ask answer that is an absolute path (or chat opening with '/') was persisted — the very lines the adjacent rule keeps out of the log. Membership in the COMMANDS grammar is the test now. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-03] User decision recorded in spec 03-03 §3: the offer card's y IS the authorization, and the checkpoints that remain are conversational. - cliPermission allows every tool call and never asks; the y/N wall and the isReadOnlyCommand classifier that patched it are deleted, tests included. The persona and task prompts act on the authorization and stop, in natural language, only at substantive forks (destructive, a genuine choice, costs money). - The secret guard's real enforcement point is a PreToolUse hook in guideOptions: a real-SDK smoke proved canUseTool is consulted only when the SDK's own policy would ask — a Read of .env sailed straight through the permission callback (a hole in the per-action design too). The hook fires for every tool use; the guard covers credential paths, secret-shaped Bash names (URLs stripped: opening a provider's api-keys page stays legal), environment dumps, and parameter expansion. A tripwire, not a sandbox; murmur-owned tools are exempt. - The offer card's y row states what it buys; bypassPermissions stays banned (grep-guard kept). - Spec 03-03 §1/§2/§3/§5/§6 revised; §5.3 owes a fresh by-ear pass under the new flow. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A Ctrl-C during a cold-cache pull printed "stopping..." and then waited out the whole manifest. pullBed now consults shouldStop between refs, and a quit that landed before the pull skips it entirely; the in-flight download still runs out (ytdlpDownload has no abort seam — the known cancellable-task gap). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Issue #102 measured ~6 burned rounds against fish.audio's fetcher-hostile pages. The voice walkthrough now makes at most two WebFetch attempts and then degrades honestly — says it could not verify and hands over the link. §7.2 carries the cap. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 tasks
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #136 (quit correctness), which stacks on #134. Merge order: #134 → #136 → this.
Implements the 2026-08-18 consent revision of specs/spec03/03-03-guide-harness.md (§3, §5, §2 updated in this PR; recorded as a user decision).
What
Entry authorization replaces per-action consent: the offer card's
yis the authorization, and the guide acts on it.cliPermissionrewritten: allows every tool call, never asks — the y/N wall (and theisReadOnlyCommandclassifier that patched it, tests included) is deleted. Denies secret-bearing input with a reason the model can act on; a fired quit latch still denies.PreToolUsehook (guideOptions): a smoke against the real SDK provedcanUseToolis consulted only when the SDK's own policy would ask — aReadof.envsailed straight through the permission callback (a pre-existing hole in the per-action design too). The hook fires for every tool use;SECRET_BEARINGalso widens to secret-shaped names and environment dumps (*API_KEY,printenv,env), closing the review finding deferred from fix(guide): /quit cuts the session instead of waiting it out [spec 03-03] #136. murmur-owned tools are exempt —write_voice_configowns the keyboard secret channel by design.GUIDE_PERSONA+ the task sections): routine steps — investigation, installs/upgrades on the user's own channel — run without asking, narrated as they go; the guide stops in natural language only at substantive forks (destructive/hard-to-reverse, a genuine choice, costs money). The never-take-a-credential-in-conversation rule is unchanged.yrow states the authorization: "I'll run the fixes, and check with you at real choices".bypassPermissionsstays banned (grep-guard kept); the surface stays bounded viatools.Evidence
Real-SDK smoke (
runGuide, Opus):node --versionran with zero questions reaching the host; aReadof.envwas denied by the hook and the model quoted the credential reason back verbatim. Note: the SDK's safe-command classifier runs read-only commands without consultingcanUseToolat all — visibility comes from the tool-activity stream (#134), which narrates every tool use into the dev log; the spec now records this seam fact.Spec 03-03 §5.3 now owes a fresh by-ear pass under the new flow (the 2026-07/08 passes ran under per-action consent) — tracked as an issue.
Peer review: codex skipped — workspace spend cap; fell back to
/code-review high(multi-agent): 8 findings, 6 applied, 2 dismissed. Applied: theapi[_-]?keyregex denied the walkthrough's ownopen .../app/api-keysstep (URLs now stripped before the name test); the deny-list's bypass set (set/export -p//proc/self/environ/~/.murmurglobs/bare$KEYexpansions) now trips the guard; the false-positive set (/usr/bin/envas launcher, WebFetch pricing prompts, script shebangs) now passes; the quit latch reachespullBed(stops at the next ref boundary); stale per-action-consent comments scrubbed across four files; date stamps dropped from code comments. Dismissed: the dev-log "command received" line states wire receipt — its purpose is quit-latency measurement, receipt ≠ handled; the SIGINT handover's escalation-counter reset is latent (the run still winds down via the quit check; the cost is one extra press only ifvoice.start()stalls).AI coding brief
canUseToolnever sees the calls the SDK auto-allows.Read, and only a real SDK run showed it. Any consent-layer brief should demand a real-SDK probe of the enforcement point first.🤖 Generated with Claude Code