Fix runner loop-control, consent, and redaction defects#11
Merged
Conversation
Correctness: - fixed_passes: the synthetic 'reviewer pass' marker no longer feeds the no-progress detector, which used to fail any clean reviewer-only gate with max_revisions >= 2 before its passes completed. - Judge verdict parsing extracts the full fenced block (with a raw_decode fallback for trailing prose) instead of a non-greedy regex that truncated nested JSON and forced every structured verdict to revise. - A no-progress stop now ends the whole run instead of only abandoning the current delivery attempt. - Gate-boundary resume: iteration restored from state.json, existing artifacts not regenerated, wall-clock budget accrues across resumed runs, stale stall counts reset at run start, and an already-passed run is not silently re-run. - RunnerError failures now mark state.json failed instead of leaving status 'running'; malformed resolved specs produce friendly 'missing required field' errors instead of KeyError tracebacks. - honor loop_control.human_checkpoints (after_plan). Security: - Consent fails closed: any non-local council member requires explicit first-send consent even with no privacy.egress entry (an entry may pre-grant with consent: granted). - Context gathering honors configured privacy redactions (plus defaults) instead of a hardcoded glob list. - Redaction globs match at any path depth, so config/.env is covered by '.env'; default redactions always apply and configured ones extend rather than replace them. - Workspace and context paths must stay inside the loop directory. Robustness: - Subprocess I/O pinned to UTF-8 (locale codepage corrupted or crashed runs on Windows). - Interactive prompts raise a clean error when stdin is closed instead of an EOFError traceback. - Example wrapper passes its own loop.resolved.json explicitly (runpy made the default resolve to templates/). Five regression tests cover the fixed_passes completion, nested judge JSON, consent fail-closed, nested .env redaction, and workspace escape. 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.
Remediation batch 1 of 3 from the 2026-07-04 audit (see ROADMAP.md in a follow-up PR). All findings verified against
mainbefore fixing; each fix has a regression test.Correctness
fixed_passesgates could never complete cleanly withmax_revisions >= 2: the syntheticfixed_passes reviewer passmarker had a constant no-progress signature, so the stall detector (threshold 2) failed the run on the second pass. Synthetic markers no longer feed the detector.\{.*?\}truncated at the first}, failingjson.loadsand silently degrading torevise. Parsing now takes the whole fenced block, with araw_decodefallback tolerating trailing prose.action: stop.state.json, existing artifacts aren't regenerated, the wall-clock budget accrues across resumed runs instead of renewing, and stale stall counts reset at run start.RunnerErrormarksstate.jsonfailedinstead of leaving it stuck atrunning; malformed specs get a friendlymissing required fielderror instead of aKeyErrortraceback.loop_control.human_checkpoints(after_plan) is now honored — it was silently ignored.Security
local) member with noprivacy.egressentry used to receive artifacts with no consent prompt at all. Now any non-local member requires explicit first-send consent; an egress entry can pre-grant withconsent: granted.gather_contextused a hardcoded default glob list, so configuredprivacyredactions never applied to context inlining..envnow coversconfig/.env. Defaults always apply; configured globs extend rather than replace them.Robustness
errors=replace) — the locale codepage (cp1252) corrupted or crashed Windows runs on any non-ASCII model output.loop.resolved.json—runpymade the default spec path resolve totemplates/(nonexistent), so running the example directly always errored.Tests
12/12 locally on Windows / Python 3.13; 5 new regression tests: fixed_passes clean completion, nested judge JSON, consent fail-closed, nested-.env redaction (context + judge prompt), workspace escape rejection.
🤖 Generated with Claude Code