Skip to content

fix(CC-572): pr-gate synthesis retry removes (not truncates) the result path - #541

Merged
screenleon merged 7 commits into
mainfrom
fix/CC-572-synthesis-retry-clear-output-file
Aug 26, 2026
Merged

fix(CC-572): pr-gate synthesis retry removes (not truncates) the result path#541
screenleon merged 7 commits into
mainfrom
fix/CC-572-synthesis-retry-clear-output-file

Conversation

@screenleon

Copy link
Copy Markdown
Owner

Summary

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 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_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.

Fix: remove the path entirely (not truncate 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. 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

  • 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, 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 genericFinal: 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).
  • All existing synthesis-protocol (14) and sequential-protocol (4) tests still pass.
  • tests/bin/run-all-tests.sh104 passed, 0 failed, 0 skipped (full sign-off; caught and fixed a lint-test-docstrings violation on the two new test wrappers along the way).

🤖 Generated with Claude Code

https://claude.ai/code/session_0177ds9nxEMtsh3bwpBxNHYx

screenleon and others added 7 commits August 26, 2026 13:20
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
@screenleon
screenleon merged commit 6ff4c9a into main Aug 26, 2026
64 checks passed
@screenleon
screenleon deleted the fix/CC-572-synthesis-retry-clear-output-file branch August 26, 2026 08:10
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant