Agent consent: the pause contract + false-receipt kill (2 fixes, stricter only) #79
Reference in New Issue
Block a user
Delete Branch "feat/agent-phase1-soul"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Rebased clean onto the regenerated main (zero conflicts). Two consent-flow fixes, both verified E2E on the clean test brain:
1. PAUSE CONTRACT — honor require_approval. The client has sent require_approval:true on every agentic request since Phase 1c, but needs_bridge never consulted it: builtin sub-escalate tools ran server-side unasked, making the app's Ask autonomy silently inert for that class. The flag is now persisted per session (so /approve resumes keep it) and ask_all bridges every tool turn. Absent/false = byte-identical legacy behavior. Proven: the probe that executed a write unasked now returns the pause envelope with nothing on disk; full in-app circle verified (card → crash → resurrection → late approve → fence re-fires).
2. BUG-6 — approved writes must land, and say where. Two compounding defects made every pause→approve write_file report success while writing nothing: (a) the naive json_get matched "content" inside tool_input, so approved builtin writes were treated as client-executed and skipped; (b) write_file returned ok unconditionally. Builtins now always dispatch server-side; write_file verifies fs_exists and returns the resolved path. Proven: approve-path write lands byte-exact; auto-run unchanged; deny executes nothing.
Also carried: per-session workspace-root isolation (BUG-LEAK) — sessions can no longer inherit another session's root via the shared state key; re-asserted on /approve and resume. Live proof pending a local rebuild (below).
Ask: local full-source rebuild now compiles all 49 modules (your elc OOM fix works — thank you) but links one symbol short: engram_get_node_by_label (+ friends) aren't in any el_runtime.c we have locally. Where does the current runtime C live? With it we can E2E the root-isolation fix locally too.
Deeper flag: the naive json scanner is a standing landmine (BUG-6 was one instance) — a nesting-aware reader would kill the class.
🤖 Generated with Claude Code
Two compounding defects made every pause->approve write_file report success while writing NOTHING: 1. The naive json_get scanner matches "content" anywhere in the approve body — including INSIDE tool_input, which for write_file always carries a content field. The handler therefore treated every approved builtin write as already-client-executed, skipped dispatch entirely, and handed the model the file's own content as the 'tool result'. The model then narrated 'Done, created' — a false receipt with no file. Builtin tools now ALWAYS dispatch server-side; client content is only honored for non-builtin (MCP/client-executed) tools. Stricter only. 2. write_file returned {"ok":true} unconditionally — fs_write's outcome was never checked, so any failed write also reported success. The write now verifies the file landed (fs_exists) and returns the RESOLVED path in the ok payload; failures return a real error naming the destination. E2E on the test brain (boot 38): approve-path write lands byte-exact and the result carries the resolved path; auto-run writes unchanged; denied writes execute nothing. BUG-5 (approve wire lacked tool_name) had been masking this one — two stacked bugs on the same path. NOTE for review: the deeper cure is a nesting-aware json reader; this fix removes the dangerous consequence at the two spots that lie about disk. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>