feat(acp): park, pause, breaker and replay in the agent harness (T16) - #35
Merged
Conversation
…T18) with T16 fixture tests Three design specs after Devin's 2026-09-06 decisions (pause on session limit, no seat rotation; only never-started batches replay; nothing discarded) and GPT-5.6 Sol's audit of the question set. Wave 6 tickets added to the implementation plan. crates/buzz-acp/src/reliability.rs holds the T16 fixture tests on the real log lines of 2026-09-02/03 plus the smallest stubs that let them compile. All seven are #[ignore] and fail with --ignored; T16 is ready when they pass un-ignored. Signed-off-by: wiggdevin <202901685+wiggdevin@users.noreply.github.com>
The harness used to dead-letter a batch after ten retries: it logged at ERROR, posted one warning and dropped the events. A Claude session limit lasts hours, so every message in that window died after 25 minutes. Nothing is discarded any more. A failure that used to dead-letter now parks the batch in an owner-only park file in the agent's state directory, and either replays it automatically after a successful live turn or hands it to the operator for review. - reliability/error_class.rs classifies a provider error and parses "resets 4:20am (America/Los_Angeles)" into the next occurrence of that wall time, via chrono-tz. An unparseable reset pauses 30 minutes; a pause is clamped at 6 hours. - reliability/state.rs holds the per-agent pause and the per-scope breakers: three consecutive provider errors open a breaker with a 10-minute probe and a 6-hour cap, then the batch parks. - reliability/state_dir.rs resolves BUZZ_ACP_STATE_DIR (0700 dir, 0600 files), falling back to ~/.buzz/.state/<pubkey prefix>/. - reliability/ledger.rs is the append-only ledger.jsonl: one serde struct per record kind, fsync per append, 30-day retention truncated on start and every 6 hours, 10 MB cap. - reliability/park.rs is parked.jsonl, written atomically, with caps on bytes, batches per scope and batches in total. - reliability/runtime.rs orders the writes: park before the batch is dropped, batch_replayed before the prompt is staged. - queue.rs: FlushBatch carries a stable batch_id; requeue no longer returns a batch to be discarded but hands it to the park path; stage_replay merges parked events ahead of newer ones with the "Delivered late" annotated section, reusing the cancelled-events merge and never editing the event text. - lib.rs gates dispatch on the pause and the breakers, drives the machinery on every prompt result, and accepts replay_batch, discard_batch, resume_now and keep_paused on the same owner-checked path as switch_model. The seven T16 fixture tests pass with their bodies unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Z6iidtozXxgx58BUZUKnu Signed-off-by: wiggdevin <202901685+wiggdevin@users.noreply.github.com>
…nd in progress; review before use) Signed-off-by: wiggdevin <202901685+wiggdevin@users.noreply.github.com>
…nd state-dir fixtures (T16) Updates the two dead-letter tests to the park behaviour the design specifies, clears two clippy errors, adds the design's replay fixtures (#5, #6), and adds the desktop state-dir reserved-key and pubkey validation tests. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P8VoJ9givMm44HNc4gx5Gz Signed-off-by: wiggdevin <202901685+wiggdevin@users.noreply.github.com>
…(T16) state.rs: release probe permits on hold/spawn-failure, not just success — test_dispatch_pending_does_not_leak_probe_permit_on_hold state.rs: on_failure clears consecutive streak on Auth/CapacityExhausted too — non_provider_failure_resets_consecutive_streak state.rs: consecutive map entry removed on every terminal park/pause, bounded — test_consecutive_map_stays_bounded_across_many_scopes park.rs: reject an oversized individual serialized line before park commits — test_park_rejects_oversized_individual_line park.rs: scope-cap demotion computed before the single atomic commit — test_park_101st_batch_scope_cap_single_atomic_write state_dir.rs: write_atomic propagates parent-dir open/fsync errors — test_write_atomic_propagates_parent_dir_fsync_error ledger.rs: Ledger::open quarantines a dangling no-newline final line — test_ledger_open_quarantines_dangling_final_line_without_newline runtime.rs: discard() returns false when the ledger append failed — test_discard_fails_contract_when_ledger_append_fails lib.rs: handle_prompt_result branches on Disposition so a failed park always re-enters queue/hand-off — test_park_failure_does_not_discard_batch_on_hard_timeout_or_auth runtime.rs: plan_replay builds plans from whole batches under MAX_BATCH_EVENTS, leaving the rest parked — test_replay_plan_respects_max_batch_events_and_preserves_unincluded_batches lib.rs: Pause/OpenBreaker batches are durably parked, recoverable across restart — test_pause_held_batch_is_durable_across_restart lib.rs: state-dir open failure gets a bounded periodic reopen retry — test_state_dir_failure_refuses_work_and_picks_up_on_reopen lib.rs: owned select! timer arm wakes on min(pause.until, breaker.next_probe) — test_probe_timer_fires_without_external_relay_event lib.rs: dispatch_pending short-circuits on global pause before the per-scope loop — test_dispatch_pending_short_circuits_global_pause_in_o1 acp.rs/lib.rs: turn_saw_output mirrored to a shared atomic so a panic preserves it for parking — test_panicked_agent_after_output_parks_with_started_true_and_needs_review acp.rs: turn_saw_output set only on agent_message_chunk/tool_call, not every session/update — wire_session_info_and_available_commands_do_not_set_turn_saw_output, wire_agent_message_chunk_and_tool_call_set_turn_saw_output pool.rs: post_failure_notice retries with backoff and reports success via NoticeAck — test_failure_notice_not_consumed_until_ack_received queue.rs: mark_complete_preserving_retries keeps the retry count across Pause/BreakerOpen — test_retry_counts_preserved_across_pause_and_breaker error_class.rs/lib.rs: sanitize_error_diagnostic redacts/caps raw provider errors at the ACP boundary — test_error_boundary_sanitizes_diagnostic_and_preserves_raw_in_ledger desktop runtime.rs: apply_state_dir_env wired into the real spawn seam, test asserts via get_envs() (no ambient-env subprocess leak) — test_command_execution_overrides_and_ignores_ambient_state_dir Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P8VoJ9givMm44HNc4gx5Gz Signed-off-by: wiggdevin <202901685+wiggdevin@users.noreply.github.com>
Second and final fix round on the T16 reliability harness. Verified all 16 BLOCK findings in the delta-1 adversarial review (7 new regressions introduced by the first fix round, plus 9 sub-findings across 8 prior findings the first round left unresolved) and fixed every one: - park hand-off overflow no longer drops a batch (return_unparked hands it back instead of taking it by value) - the probe-timer busy-spin: dispatch now runs on every valid probe wake regardless of live queue depth, and pause/breaker deadlines reschedule forward instead of re-triggering on a stuck past deadline - cancelled_events (interrupted/replay carryover) are now persisted when a batch is parked instead of requeued - commit_replay rolls back earlier marks when a later one in the same plan fails; finish_replay retains in-flight ownership on partial removal failure - a directory-fsync failure after a successful atomic rename no longer makes write_atomic report the write as lost - the park reader quarantines corrupt/over-cap records to a .corrupt sibling file instead of silently truncating and admitting them - open breakers are now bounded (MAX_OPEN_BREAKERS) and swept for 6h expiry independent of new traffic on the scope - push refuses new admission at cap while reliability state is unavailable, instead of evicting an already-queued message with nowhere to land - pause/breaker probe leases are released on every dispatched outcome (retry, park, panic), not only the hold paths - a panicked turn that already produced output is parked directly as needs_review instead of losing its started flag through the plain retry queue - the ledger sanitizes a dangling partial line before every append, not only at open - discard_batch distinguishes NotFound from Discarded from DiscardedUnrecorded instead of collapsing the last two into "unknown_batch" - a ledger write failure during park_batch now surfaces a channel notice (the previously dead-code state_write_failures function) - failure-notice retries extended from ~15s to ~7-8 minutes of backoff - credential redaction now normalizes key names (strips separators/casing) and covers common token prefixes (ghp_, gho_, etc.), closing JSON/env-var/ hyphenated-key gaps - the desktop state-dir env application is now gated by a StateDirApplied proof token consumed at the real spawn call, matching the EffortApplied/McpEnvApplied pattern, so the production seam cannot be deleted or reordered without a compile error Two items are disclosed as scoped follow-ups rather than force-fit into this round: a fully durable cross-restart notice outbox, and extending the reliability-unavailable admission gate to the aggregate per-channel cap (the per-scope gate already covers the finding's exact repro). Verified: cargo fmt/clippy clean for buzz-acp and desktop/src-tauri; targeted test suites (reliability, queue, hard_timeout, managed_agents) all green. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P8VoJ9givMm44HNc4gx5Gz Signed-off-by: wiggdevin <202901685+wiggdevin@users.noreply.github.com>
wiggdevin
marked this pull request as ready for review
September 7, 2026 01:48
Author
|
Re-triggering PR CI: GitHub created no workflow run for the ready_for_review event. Reopening immediately. |
…bility PR #32 landed on zs/main while this branch was in flight and touched the same two files this branch's T16 implementation owns. Resolved by keeping this branch's implementation in both cases, since #32's content was the earlier design-stage scaffolding for the same work: - crates/buzz-acp/src/reliability.rs: add/add conflict. #32 added a stub module (ErrorClass, Action, a no-op ReliabilityState, and #[ignore]'d fixture tests). This branch already has the full T16 implementation (error_class, state, state_dir, ledger, park, notices, runtime submodules) whose own test suite includes every one of #32's fixture cases unignored and passing, plus additional coverage. Kept this branch's file as-is (`git checkout --ours`); nothing from #32's stub needed folding in. - crates/buzz-acp/src/lib.rs: content conflict on the module declaration line — #32 added `mod reliability;` (private, sufficient for its internal stub) where this branch already had `pub mod reliability;` (public, since the real runtime's types are consumed elsewhere in this file and are intended for future external test crates). Kept this branch's `pub mod reliability;`. All other files auto-merged cleanly; PR #32's three unrelated design docs (docs/plans/2026-09-04-zs-implementation-plan.md, 2026-09-06-agent-health-design.md, 2026-09-06-agent-self-improvement-design.md) already exist identically on this branch, so the merge is a no-op outside of history. # Conflicts: # crates/buzz-acp/src/lib.rs # crates/buzz-acp/src/reliability.rs Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: wiggdevin <202901685+wiggdevin@users.noreply.github.com>
PR #35's Windows clippy job failed with 'unused import: super::*' at state_dir.rs:192, because the sole test in that module is cfg(unix) but the use super::* sat under a bare cfg(test), so on non-unix targets the module still compiles with the glob import but no unix-gated test left to use it, tripping -D warnings. Moved the unix gate up to the module attribute (cfg(all(test, unix))) so the whole test module — import included — compiles only on unix, where it behaves exactly as before; on Windows the module is skipped entirely instead of leaving an unused import. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: wiggdevin <202901685+wiggdevin@users.noreply.github.com>
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Sep 7, 2026
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Sep 7, 2026
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.
Summary
This branch closes T16: park, pause, breaker, and replay in the ACP agent harness, per
docs/plans/2026-09-06-harness-reliability-design.md. Commits on the branch:b4ce86acb— docs(zs): harness reliability, agent health and curator designs (T16 design doc + related plans)6977f9c44— feat(acp): park, pause, breaker and replay in the agent harness (T16)b42de461c— wip: uncommitted agent work at session stop 2026-09-06 05:30 (fix round)3d8281fee— fix(acp): park semantics in the retry tests, clippy, and the replay testsc691edb83— fix(acp): close the verified Sol findings on the reliability harness19c4cbe89— fix(acp): close the delta findings on the reliability harness (T16)What the harness now does, per the design doc's decisions:
breaker expiry) is written durably to
state/parked.jsonlinstead of being discarded.A batch that had already started producing agent output/tool calls is marked
needs_reviewand requires an operator to retry or discard it; a batch that neverstarted is replay-eligible.
CapacityExhaustederror (session limit, rate limit, 429, overloaded, quota)pauses the agent until the parsed reset time (clamped to at most 6 hours), rather than
burning the retry budget and dead-lettering the message.
ProviderInternal/Unknownfailures on one scope open abreaker that probes every 10 minutes for up to 6 hours before parking.
parked batches for that scope replay oldest-first, framed with a "delivered late" section
header; started batches never auto-replay.
state/ledger.jsonlrecords every state transition(
turn_started,turn_finished,batch_parked,batch_replayed,agent_paused,breaker_opened, etc.) with 30-day retention and size caps.BUZZ_ACP_STATE_DIR(set by desktop at spawn, falling back to~/.buzz/.state/<pubkey-prefix-16>/) holds the ledger and park file at 0700/0600permissions.
BUZZ_ACP_STATE_DIRwhen spawning a managed agentand reserves it against user override (
managed_agents/reserved_env_keys.rs,managed_agents/runtime.rs).No message is discarded by the harness on this branch's happy path; every failure mode
routes to park, pause, or the breaker instead of the old 10-retry-then-drop behavior.
What changed
By area, from
git diff --stat origin/zs/main...HEAD(26 files, +8289/-207):crates/buzz-acp/src/reliability/*(new, ~3.5K lines) — the reliability subsystem:error_class.rs(failure classification),ledger.rs(append-only durable ledger),park.rs(durable park file, replay eligibility, scope caps),runtime.rs(pause/breakerstate machine, replay orchestration),
state.rs(per-scope/per-agent state),state_dir.rs(state directory resolution and permissions),
notices.rs(channel notice templates).crates/buzz-acp/src/lib.rs(+2698/-…) — wires reliability into the main dispatchloop: pause/breaker gates, park-or-fallthrough on failure, replay-after-success, control
frame handling (
replay_batch,discard_batch,resume_now,keep_paused).crates/buzz-acp/src/queue.rs(+470/-…) —requeue_preserve_timestamps, replaystaging (
stage_replay), scope-aware carryover handling.crates/buzz-acp/src/pool.rs(+137/-…) — failure notice posting to the channel(pause/park/breaker templates).
crates/buzz-acp/src/acp.rs(+164 lines) —turn_saw_output/started-state wiringfrom the wire protocol (
session/update, Goose usage updates).desktop/src-tauri/src/managed_agents/*—runtime.rs,storage.rs,reserved_env_keys.rs, plus their test files: sets and reservesBUZZ_ACP_STATE_DIRwhenspawning a managed agent.
Cargo.toml/Cargo.lock/desktop/src-tauri/Cargo.lock— new dependency for thereliability crate additions.
docs/plans/*— the T16 design doc plus three related design docs from the samesession (
2026-09-04-zs-implementation-plan.md,2026-09-06-agent-health-design.md,2026-09-06-agent-self-improvement-design.md) landed alongside it.Gates run
No full suite ran locally on this branch — only targeted/scoped gates below plus the
repo's own pre-push hook (lefthook) on the final push. The merge queue runs the full
suite; this PR relies on that run for full-repo coverage.
From
C4-t16-acp-tests.txt(pre-second-fix-round baseline, before the Sol-verified fixes landed):cargo nextest run -p buzz-acp— not completed in this run (superseded bycargo test)cargo test -p buzz-acp— exit 101 (failing, pre-fix state)cargo clippy -p buzz-acp --all-targets -- -D warnings— exit 101 (failing, pre-fix state)cargo fmt -p buzz-acp -- --check— exit 0From
C4-t16-acp-verify.txt(post-fix re-verify):cargo fmt -p buzz-acp -- --check— exit 1 (formatting diffs found), then fmt appliedcargo clippy -p buzz-acp --all-targets -- -D warnings— exit 0cargo test -p buzz-acp reliability— exit 0cargo test -p buzz-acp queue— exit 0cargo test -p buzz-acp hard_timeout(post-fmt re-verify) — exit 0cargo clippy -p buzz-acp --all-targets -- -D warnings(post-fmt re-verify) — exit 0From
C4-t16-tauri-tests.txt:cargo test managed_agents— exit 0 (87s)cargo clippy --all-targets -- -D warnings— exit 0 (111s)cargo fmt -- --check— exit 0 (2s)all gates exit 0: ok=trueFrom
T16-fix-verify.txt(final fix-round verification, buzz-acp target dir then Tauri target dir):cargo fmt -p buzz-acp -- --check— exit 0cargo clippy -p buzz-acp --all-targets -- -D warnings— exit 0cargo test -p buzz-acp reliability— exit 0cargo test -p buzz-acp queue— exit 0cargo test -p buzz-acp hard_timeout— exit 0cargo test managed_agents(TAURI target dir) — exit 0 (1379 passed, 0 failed)cargo clippy --all-targets -- -D warnings(TAURI target dir) — exit 0cargo fmt -- --check— exit 0Pre-push hook (lefthook) on the final push to
origin/feat/harness-reliability:push-head-scope— passbranch-skew— passfile-size-check— pass (10 file-size-core tests, all passing)desktop-check— passdesktop-typecheck— passrust-tests— pass (all 12 workspace crates, includingbuzz-acpat 25 tests,buzz-mcp-launch, etc.)desktop-tauri-checks— passdesktop-test— pass (6546 tests, 0 failed, 86 suites) — note: the first push attemptfailed this gate with 5 test files hitting
ERR_MODULE_NOT_FOUNDonlucide-reactbecause
desktop/node_moduleswas stale in this worktree relative to the merged base(the base merge added the dependency). Root cause confirmed environmental, not a code
defect:
pnpm install --frozen-lockfileindesktop/(no lockfile change) resolved it,and the retried push passed this gate cleanly on the same commit.
Tested base OID
d0cbd2ebda9cbc0f8b56f1e7dd425f76e4a141da(git merge-base HEAD origin/zs/main).origin/zs/mainwas already an ancestor ofHEADat PR time — no merge was needed.Sol verdicts
GPT-5.6 Sol ran three adversarial static review passes against this branch. Sol's sandbox
is read-only and cannot execute the test suite, so its closure judgments below are based on
static code/test-seam tracing, not runtime verification.
Full pass (
T16-sol-verdict.json, at3d8281fee): verdict BLOCK, 25 findings total— 21 BLOCK, 4 WARN.
Verification (
T16-sol-verified.md): each of Sol's 25 findings was independentlyre-opened against the actual file/line at HEAD and checked against the design doc.
Result: 19 of 21 BLOCK findings confirmed as-is, 2 downgraded BLOCK→WARN (#6 — a
liveness stall requiring five independent config knobs at their extreme, not data loss;
#21 — the symlink/length hardening gap on
BUZZ_ACP_STATE_DIRis defense-in-depth forbuzz-acp-as-a-library, since the shipped desktop topology always overwrites the valuefrom a trusted source). The 4 original WARNs were all reconfirmed as WARN. Net: 19
confirmed BLOCK + 2 downgraded BLOCK→WARN + 4 confirmed WARN = 21 total confirmed findings
requiring changes, 0 refuted.
Delta #1 (
T16-sol-delta1.json, after the first fix commitc691edb83): verdictBLOCK, 22 findings — 16 BLOCK (7 new + 9 sub-findings against 8 prior BLOCK
categories that remained unresolved), 6 WARN. All 16 BLOCK findings were confirmed and
fixed in the second fix commit (
19c4cbe89).Delta #2 (
T16-sol-delta2.json, after the second fix commit19c4cbe89): verdictBLOCK, 25 findings — 17 BLOCK, 8 WARN. Sol's summary: "2 new BLOCKs and 15
unresolved or reopened prior BLOCK findings... Closure evidence supports original findings
#1, #2, #3, #7, #10, #11, #16, #17, #18 and DELTA-1 findings #2 and #7." Sol could not
execute tests in its read-only sandbox for this pass either, so these closure judgments are
static-inspection-based, not test-verified.
The branch is NOT clean on Sol. The fix loop hit its cap at delta #2 with 17 BLOCK and
8 WARN findings still open. These are listed verbatim below.
Follow-ups (open Sol findings at the loop cap)
BLOCK
crates/buzz-acp/src/reliability/park.rs:662— NEW: corrupt-record quarantine grows without bound. Trigger: place one malformed park record inparked.jsonland repeatedly restart the harness.read_batchesexcludes but never removes the source record, whilequarantine_lineappends it toparked.jsonl.corrupton every open with no byte cap, rotation, or deduplication. Fix: use a bounded, atomically replaced quarantine with deduplication or fail closed until the source record is reconciled.crates/buzz-acp/src/reliability/ledger.rs:524— NEW: ledger recovery scans an unbounded file before enforcing limits. Trigger: supply a very largeledger.jsonlwith no newline. Startup seeks backward and reads until BOF without a ledger-size or scan-size ceiling, allowing unbounded startup I/O and memory work. Fix: reject or quarantine ledgers above a fixed byte ceiling before recovery, and cap the backward scan to the maximum permitted record size.crates/buzz-acp/src/reliability/park.rs:579— Prior DELTA-1 feat(desktop): show each agent's role in the @-mention picker (port of #2706) #6 remains: park reader silently abandons unreconciled records. Trigger: a park file larger than 10 MiB, containing over 1,000 valid batches, or containing an invalid line whose.corruptappend fails still opens successfully with only a prefix/subset. A later mutation rewrites that subset and destroys the unread suffix or invalid message record. Fix: failParkFile::openon any cap breach or parse failure unless a bounded quarantine commit succeeds atomically; never overwrite a source containing unreconciled records.crates/buzz-acp/src/reliability/runtime.rs:269— Prior DELTA-1 feat(acp): extra MCP servers via env, with a trust boundary for their credentials #4 remains: multi-batch replay commit is still torn. Trigger: let the first replay marker commit, fail a later marker or ledger append, then failunmark_replayedduring rollback. Rollback errors are discarded, leaving some batches permanently replay-stamped even though nothing was sent. Fix: commit every replay marker and ledger intent as one atomic state transition or durable transaction; propagate any rollback failure.crates/buzz-acp/src/queue.rs:724— Prior DELTA-1 spike(pdf): PDF route decision for T8 #3 remains: retry requeue drops cancelled carryover. Trigger: build a batch with 50 cancelled/replay carryover events plus one new event, then return a retryable provider failure. Parking rejects the 51 combined records, and requeue restores onlybatch.events, silently losingcancelled_events. Fix: preserve the complete batch, including cancelled carryover and metadata, through retry; split it safely before exceeding the durable per-record cap.crates/buzz-acp/src/lib.rs:5365— Prior DELTA-1 fix(desktop): prefer the bundle's harness binaries over workspace target dirs #1 remains: full handoff fallback shifts message loss into the live queue. Trigger: fill the 200-entry returned-handoff queue, then fail another park handoff while its scope or channel queue is at capacity. The fallback requeue invokes queue eviction, discarding either an existing client message or part of the returned batch. Fix: retain failed handoffs in a bounded durable journal or apply backpressure; do not route them through an evicting queue.crates/buzz-acp/src/queue.rs:410— Prior feat(acp): extra MCP servers via env, with a trust boundary for their credentials #4 remains: unavailable durability still consumes and evicts ingress. Trigger: start with reliability state unavailable and deliver a 501st event to one scope, or exceed the aggregate channel cap across scopes.pushrejects or evicts an event after the relay delivery has been consumed, without a durable retry record or replay floor. Fix: do not admit subscriptions until durability is available, or durably spool/NACK deliveries and reconnect from an explicit replay floor; disable aggregate eviction during the outage.crates/buzz-acp/src/lib.rs:5722— Prior feat(desktop): open markdown attachments in a viewer panel #5 remains: probe-lease release guards lack binding production tests. Trigger: delete or move the productionrelease_pause_probe_fororrelease_breaker_probecalls on failed and panicked probe outcomes. Existing state-helper tests still pass, so the first failed probe can permanently wedge the scope. Fix: add tests that dispatch a real probe throughPromptResultfailure and panic paths and prove a subsequent probe becomes eligible.crates/buzz-acp/src/lib.rs:6023— Prior feat(managed-agents): add OpenSEO-shaped agent config generator (T6-config) #8 remains: panic fallback can erase started provenance. Trigger: panic after provider output, then make direct parking fail or run without a reliability runtime. The fallbackqueue.requeuedeconstructs the started batch; a later flush can classify it as not started and auto-replay duplicate side effects. Fix: carry immutable started provenance in queued records, or retain the intact batch until durable parking succeeds; test both fallback branches.crates/buzz-acp/src/acp.rs:2012— Prior docs(zs): MCP registry design memo (T7) #9 remains: Goose started-state gate compares cumulative rather than turn-local output. Trigger: after one turn produces tokens, begin another turn that fails before output but emitsaccumulated_output_tokensfrom the previous turn. The nonzero cumulative value marks the new turn started and changes it from automatic replay to manual review. Fix: compare the usage snapshot with the per-turn baseline or use a current-turn output delta; add a two-turn wire-level regression.crates/buzz-acp/src/reliability/state_dir.rs:165— Prior ci(zs): fix the inbox-live-update scroll baseline and settle layout before measuring #12 and DELTA-1 feat(desktop): open markdown attachments in a viewer panel #5 remain: directory fsync failure is reported as durable success. Trigger: allow rename to succeed and force the parent-directory fsync to fail. The helper logs and returns success, allowing the caller to release its only live copy even though a power loss can erase the renamed directory entry. Fix: return an explicit committed-but-not-durable outcome and retain custody or stop dispatch until durability is confirmed.crates/buzz-acp/src/reliability/ledger.rs:379— Prior feat(desktop): export a markdown document as PDF (T9) #13 remains: failed ledger append poisons later records in-process. Trigger: forcewrite_allto leave a partial JSON record, then restore the filesystem and append again without reopening.O_APPENDjoins the next record to the partial tail; startup repair is never invoked between writes. Fix: repair or truncate to the last complete frame after every failed append and before another append; test short-write recovery without reopening.crates/buzz-acp/src/reliability/runtime.rs:384— Prior feat(mcp): MCP registry core: secret store, launcher and proxy, registry loader, staged config generation (T7a) #14 remains: discard removes message custody before its audit record. Trigger: issue discard, let park removal commit, then fail the ledger append.DiscardedUnrecordedhonestly reports the problem but cannot restore the deleted messages or the required audit trail. Fix: persist a durable discard intent before removal, or atomically transact the park mutation and ledger/outbox record.crates/buzz-acp/src/reliability/runtime.rs:182— Prior feat(mcp): MCP registry core: secret store, launcher and proxy, registry loader, staged config generation (T7a) #14 remains: ordinary ledger failures are swallowed. Trigger: forcebatch_parkedorbatch_needs_reviewrecording to fail.park_batchignores the false result and returns success, allowing callers to proceed without the required ledger record. Fix: makerecordreturnResultand propagate it through a coherent park-plus-ledger transaction or durable outbox.crates/buzz-acp/src/pool.rs:5058— Prior docs(zs): calendar view design memo (T12a) #15 remains: failure notices disappear after bounded retries or restart. Trigger: keep the relay unavailable for all 12 attempts or restart during retry. The detached task has no durable outbox, and a quiet paused scope has no later action that guarantees the missing operator notice is retried. Fix: write notices to a bounded idempotent outbox before sending and resume pending delivery during startup.crates/buzz-acp/src/reliability/error_class.rs:198— Prior docs(zs): agent-minutes script and manifest behind the throughput baselines #19 remains: redactor exposes whitespace-separated credential values. Trigger: return an error such asX-API-Key: secret-valueor JSON formatted as"api_key": "secret-value". Tokenization redacts the key-bearing token but leaves the following value token intact for logs, observers, notices, or ledger text. Fix: parse and redact key-value pairs across whitespace and punctuation boundaries; add exact log, observer, notice, and ledger sink tests.desktop/src-tauri/src/managed_agents/runtime.rs:1054— Prior ci(zs): shorten the merge gate and serialize heavy local gates #20 remains: state-directory env ordering test does not bind the spawn builder. Trigger: moveapply_state_dir_envbefore thedescriptor.envloop while retaining the proof token. Production still compiles and the test atruntime/tests.rs:1334still passes because it manually simulates the desired ordering instead of invoking the builder. Fix: extract the real command construction path and assert its finalget_envsresult, or encode the ordering with a type-state builder that cannot be reordered.WARN
crates/buzz-acp/src/reliability/error_class.rs:150— Provider-error sanitization performs full-size work before final truncation. Trigger: a maximum-size provider error is cloned, normalized, split, and rebuilt before the final output cap is applied, increasing transient CPU and allocation pressure. Fix: apply a bounded input slice before normalization and tokenization, preserving a small allowance for redaction context.crates/buzz-acp/src/reliability/state_dir.rs:36— Standalone state-directory input still follows symlinks and lacks a path-length bound. Trigger: launch the harness directly withBUZZ_ACP_STATE_DIRpointing through an attacker-controlled symlink or an excessively long path; directory and state-file operations follow that path. Fix: bound the environment value, reject symlink components where the trust model requires containment, and document the allowed root.crates/buzz-acp/src/lib.rs:1997—replay_batchacknowledgement still means scheduled rather than immediately dispatched. Trigger: sendreplay_batchwhile another turn owns the scope. The control response reports success although replay waits behind the active turn and may later fail before dispatch. Fix: return a distinct scheduled status with the blocking scope state, or acknowledge only after dispatch begins.crates/buzz-cli/src/lib.rs:268— Replay control still has nobuzz-clioperator command. Trigger: an operator must construct raw control events becauseAgentsCmdexposes no typed replay-batch operation, making validated recovery unnecessarily error-prone. Fix: add a typedbuzz-clireplay command with batch-id validation and normalized status output.crates/buzz-acp/src/lib.rs:5431— Replay acknowledgements do not expose the newly generated batch identifier. Trigger: a successful replay creates a new batch UUID internally but the control result returns only the requested parked identifier, preventing direct ledger correlation. Fix: include both the requested parked batch IDs and the generated replay batch ID in the acknowledgement.crates/buzz-acp/src/lib.rs:5160— Expired breaker path is recorded with misleading park provenance. Trigger: a scope whose breaker exceeds the expiry window takesAction::Parkbut is recorded using the generic auth-style park path rather thanParkReason::BreakerExpired, obscuring why work was withheld. Fix: carry the state-machine reason throughAction::Parkand recordBreakerExpiredexplicitly.crates/buzz-acp/src/reliability/state.rs:182— Breaker-cap enforcement silently removes active containment. Trigger: open breakers for 1,001 scopes. Capacity enforcement evicts the oldest still-active breaker, permitting that scope to dispatch again before recovery was demonstrated. Fix: reject or park new scopes at capacity, or retain an explicit global-open sentinel instead of silently lifting an existing breaker.crates/buzz-acp/src/reliability/park.rs:613— Park records are not validated against their signed event and stored scope. Trigger: tamper a syntactically valid owner-local park line so its events have invalid signatures or channel tags inconsistent with the storedchannel_idand scope. The record is accepted and can be replayed under the wrong recovery context. Fix: verify event signatures, permitted kinds, and channel/scope consistency while loading; quarantine records that fail validation.Security note
During the fix round, an intermediate, uncommitted desktop test (a
managed_agentsstate-dir env test) spawned
/usr/bin/envas a real child process and printed the wholeprocess environment on assertion failure; that output briefly reached a proof file and an
agent transcript before being redacted (see
$STATE/proof/SECURITY-env-leak-2026-09-07.mdfor the full incident record and exposedvariable names, values not included). The committed test on this branch does not do
this — it uses
Command::get_envs()to inspect the constructed command's environmentin-process and spawns nothing.
Co-Authored-By: Claude Fable 5.1 noreply@anthropic.com
🤖 Generated with Claude Code
https://claude.ai/code/session_01P8VoJ9givMm44HNc4gx5Gz
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.