fix(CC-572): pr-gate synthesis retry removes (not truncates) the result path - #541
Merged
Merged
Conversation
Status flip missed at merge time -- PR #539 shipped and merged, but BACKLOG.md's index row and body heading were never flipped from active to done. This is the fourth time this exact pattern has recurred in one session (after CC-567, CC-533, CC-015) -- this time by the same agent that had just fixed the other three, which is its own lesson: the fix has to happen inside the PR before merge, not as a mental note for after. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0177ds9nxEMtsh3bwpBxNHYx
…lt path
Diagnosed from repeated production protocol failures during the CC-571
gate saga: apply_patch failures ("invalid patch: multiple operations
target <file>", "invalid hunk", "Failed to find expected lines") kept
recurring on the synthesis correction retry, in both sequential and
parallel mode.
Root cause: the reviewer-protocol retry (which never showed this failure)
always writes to a brand-new path (reviewer-<name>-<ts>-retry1.md) --
always an unambiguous "Add File" for whatever patch tool the executor
uses. Both synthesis retries instead re-dispatch to the SAME fixed
$OUTPUT_FILE path. The sequential retry already truncated it
(`: > "$OUTPUT_FILE"`) before this fix, and STILL hit the identical
failure -- confirmed by direct trace inspection that a path which still
EXISTS on disk, even at 0 bytes, can lead the executor's patch tool to
choose an "Update File" operation (which locates existing content to
edit) instead of "Add File". An update against empty/rewritten content
then fails outright, since there's no content to locate -- a hard
patch-tool failure unrelated to the actual synthesis content. The
parallel retry didn't even truncate; it left the first attempt's full
content in place.
Fixed by removing the path entirely (not truncating it) before both
retries, via a new shared `gate_clear_retry_target` helper next to
`gate_dispatch_command`. This forces an unambiguous "Add File" the same
way the already-reliable reviewer-retry path's brand-new filename does.
Added a test-only capture hook (CODEX_GATE_CAPTURE_OUTPUT_EXISTS_DIR) that
records, at the very start of each synthesis dispatch, whether the result
path exists on disk at that instant -- and two regression tests (one per
mode) asserting the first dispatch sees "exists" (reviewer content already
written) and the retry sees "absent". All existing synthesis-protocol
(14) and sequential-protocol (4) tests still pass.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0177ds9nxEMtsh3bwpBxNHYx
lint-test-docstrings requires each test_* function to carry its own '# Behavior:' marker directly above its declaration -- the shared helper's docstring didn't satisfy that for the two thin per-mode wrappers, which caused tests/bin/run-all-tests.sh to fail at phase 0 and skip 99 suites. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0177ds9nxEMtsh3bwpBxNHYx
Following the lesson from CC-524/CC-567/CC-533/CC-015/CC-571: flip the status inside the shipping PR, not as a follow-up after merge. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0177ds9nxEMtsh3bwpBxNHYx
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0177ds9nxEMtsh3bwpBxNHYx
gate_clear_retry_target's own comment restated what the function does (the name already says that); trimmed to keep only the why (the Update-vs-Add-File patch-tool failure mode). BACKLOG.md's Requirement/ Update sections similarly dropped line-number-level implementation detail that duplicates the code and would go stale as soon as the code moves -- kept only the outcome and the reasoning. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0177ds9nxEMtsh3bwpBxNHYx
Ticket refs belong in BACKLOG.md/DECISIONS.md, not in code/test comments, which should describe current behavior on their own terms. Removed all CC-572 mentions from runtime/bin/pr-gate.sh and tests/shell/test-pr-gate.sh (comments and the _cc572_-prefixed variable names), rewording each to state what the code does/why without the ticket citation. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0177ds9nxEMtsh3bwpBxNHYx
This was referenced Aug 26, 2026
screenleon
added a commit
that referenced
this pull request
Aug 26, 2026
…ns (#544) * fix(ship): give the permanent-test admission record a PR body template slot Second reading of CC-554's admission-recording rule (effective since pr:#530): across a 12-PR evidence window, both qualifying instances found (pr:#539's 7 new regression cases, pr:#541's 2 new test functions) added a permanent test as a gate-finding remedy but recorded zero admission-criteria citations in the PR body. The rule text lives in Step 2.5, several screens before the PR body actually gets composed in Step 4 -- a free-floating prose reminder read once during remediation is not reliably recalled by the time the PR body template gets filled in. Add an explicit `Permanent test admissions:` field to that template so its omission is conspicuous (fill it in, or write `none`) instead of silent, and add a regression assertion that the template carries the field. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0177ds9nxEMtsh3bwpBxNHYx * fixup: clarify none is not a substitute for an alternative-path record Two gate reviewers (critic, architecture-reviewer) independently flagged that the template's `| none` fallback read as usable whenever no new permanent test was added -- including a round whose finding was resolved via a recorded alternative, which Step 2.5 still requires a line for. Reworded so `none` applies only when no finding this round was assessed against Step 2.5 at all, and added regression assertions for both the alternative-path requirement and the none-is-not-a-substitute wording. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0177ds9nxEMtsh3bwpBxNHYx --------- Co-authored-by: Claude Sonnet 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.
Summary
Diagnosed from repeated production protocol failures during the CC-571 gate saga:
apply_patchfailures ("invalid patch: multiple operations target<file>", "invalid hunk", "Failed to find expected lines") kept recurring on the PM-synthesis correction retry, in both sequential and parallel gate modes — 4 consecutive protocol failures across that saga's rounds.Root cause: the reviewer-protocol retry (which never showed this failure) always writes to a brand-new path (
reviewer-<name>-<ts>-retry1.md) — always an unambiguous "Add File" for whatever patch tool the executor uses. Both synthesis retries instead re-dispatch to the SAME fixed$OUTPUT_FILEpath. The sequential retry already truncated it (: > "$OUTPUT_FILE") before this fix, and still hit the identical failure — confirmed by direct trace inspection that a path which still EXISTS on disk, even at 0 bytes, can lead the executor's patch tool to choose an "Update File" operation (which locates existing content to edit) instead of "Add File". An update against empty/rewritten content then fails outright, since there's no content to locate — a hard patch-tool failure unrelated to the actual synthesis content. The parallel retry didn't even truncate; it left the first attempt's full content in place.Fix: remove the path entirely (not truncate it) before both retries, via a new shared
gate_clear_retry_targethelper next togate_dispatch_command. This forces an unambiguous "Add File" the same way the already-reliable reviewer-retry path's brand-new filename does. The helper extraction (found via the mandatory reuse/simplify pass — all three review angles independently flagged the same duplication) also collapsed two ~15-line near-duplicate root-cause comments into one canonical explanation.Verification
CODEX_GATE_CAPTURE_OUTPUT_EXISTS_DIR) that records, at the very start of each synthesis dispatch, whether the result path exists on disk at that instant — and two regression tests (one per mode, sharing one parameterized helper) asserting the first dispatch sees "exists" (reviewer content already written) and the retry sees "absent".pmctl gate run --executor codex --policy generic— Final: GO, first attempt, all four reviewers approved (didn't happen to trigger a synthesis retry itself, so this specific gate run didn't exercise the fix live — but the mechanism is verified against the actual historical failure traces plus white-box regression tests).synthesis-protocol(14) andsequential-protocol(4) tests still pass.tests/bin/run-all-tests.sh— 104 passed, 0 failed, 0 skipped (full sign-off; caught and fixed alint-test-docstringsviolation on the two new test wrappers along the way).🤖 Generated with Claude Code
https://claude.ai/code/session_0177ds9nxEMtsh3bwpBxNHYx