Repository navigation
[Phase 4] Post-conflict-resolution integrity check — catch duplicated/corrupted content before it's trusted #1482
Description
Story
As a maintainer relying on dev-lead/auto-rebase to resolve conflicts automatically,
I want a post-resolution integrity check that catches content corruption (duplicated declarations, silently dropped content) before the resolution is trusted,
so that a botched conflict resolution is caught immediately at the point it happens, not discovered hours later by an unrelated test that happens to exercise the corrupted code.
Problem — conflict resolution completed, but the content was wrong, and nothing checked
#1449, overnight 2026-08-06→07: two auto-rebase-conflict events (01:37Z, 09:37Z) against main. After the second, scripts/engine.sh — a file this PR's own story explicitly says it shouldn't touch — had grown from main's 2688 lines to 4011 (1323 extra), with core functions (run_writer, parse_reset_time, extract_verdict_json, etc.) duplicated roughly 2x their count on main. This wasn't caught by any purpose-built guard: it surfaced only because the Test Dev-Lead Agent / unit job happened to exercise the corrupted function, producing 60+ unrelated-looking failures (rate-limit handling, model-chain fallback, redaction) that took real investigation to trace back to the actual cause. It was found manually during steering review, not by CI design.
This had already happened once, earlier in the same PR's lifetime: commit f5bbcc5c fix(ci): remove duplicated function/variable declarations that broke unit and bats tests shows the exact same failure mode was hit and patched — and then recurred on the very next rebase. The fix didn't generalize into a check; it fixed the instance.
This is a distinct gap from what's already tracked
- bug(dev-lead): rebase handler times out (exit 124) on hard conflicts instead of aborting; sentinel bursts #865 (open) covers the rebase handler timing out on a hard conflict — the process fails to complete. This issue is the opposite: the process completes, reports success, and the content is wrong.
- P2 (#860 follow-up): keep agent branches rebased/squashed instead of merge-stacked #949 (closed, feat: implement issue #846 — [Phase 2] Cache the LSP index and enforce the 30s cold-start SLA with auto-skip #860 follow-up) covers agent branches accumulating
merge maincommits instead of staying rebased — a volume/mechanism concern (commit bloat, conflict churn frequency). It does not verify the correctness of whatever a conflict resolution produces — and notably, feat: implement issue #687 — [Phase 2] Generalized CI compliance test: reject SHA / # main pins on any first-party reusable #1449 still picked up aMerge branch 'main' into ...commit despite this being closed. - [Phase 4] Name and guard the "agent optimizes the check, not the intent" failure mode #1468 (open, this epic) names the broader "agent optimizes the check, not the intent" pattern. This issue is a specific, mechanically-detectable instance of it: a conflict-resolution result should be checkable independent of which downstream test happens to notice.
Acceptance Criteria
- After any automated conflict-resolution commit (auto-rebase's
update-branch, or dev-lead'srebaseintent indev-lead-fix-reviews.sh), a lightweight integrity check runs against the resolved tree, at minimum detecting duplicate top-level function/variable declarations within a file that were not already duplicated in both parent versions. - The check runs as part of the conflict-resolution path itself — not dependent on an unrelated test suite happening to exercise the corrupted code.
- On detecting an anomaly, the PR gets a specific, actionable comment naming the file and the duplicated symbols (mirroring the manual finding on feat: implement issue #687 — [Phase 2] Generalized CI compliance test: reject SHA / # main pins on any first-party reusable #1449) — the resolution is flagged, not silently trusted.
- Signal-only initially: post a comment, do not auto-block or auto-revert (mirrors [Phase 4] Detect merge-ready PRs stranded with no agent acting and no signal #1469's caution about automated actions on PRs re-arming agent churn) — escalate to blocking only after the detector has run advisory for a period with no false positives.
- Regression coverage using feat: implement issue #687 — [Phase 2] Generalized CI compliance test: reject SHA / # main pins on any first-party reusable #1449's actual corruption (
scripts/engine.sh2688→4011 lines,run_writer/parse_reset_time/extract_verdict_jsonduplicated) as the fixture. - Scoped to avoid false positives on legitimate large diffs: a file that's supposed to grow substantially (e.g., a real upstream feature merge) shouldn't trip this — duplicate-declaration detection is the precise signal; a raw line-count-delta heuristic is advisory-only, not a trigger on its own.
Tasks / Subtasks
- Write a duplicate-top-level-declaration detector for shell scripts (generalize whatever
f5bbcc5cdid by hand into a reusable script/function). (AC: test issue from agent #1, feat: feature ideation framework — adversarial proposer/challenger/synthesizer #6) - Wire it into the rebase/merge-conflict-resolution path:
dev-lead-fix-reviews.sh(rebase intent) and/or theauto-rebase-reusable.ymlworkflow, immediately after a conflict-resolution commit lands. (AC: Go-live improvements for PR review agent #2) - Post a specific, named-symbol comment on detection; no auto-block yet. (AC: Add @claude delegation, auto-merge, and rebase handling #3, Optimize review: small-PR and incremental fast paths #4)
- Add a regression test using the feat: implement issue #687 — [Phase 2] Generalized CI compliance test: reject SHA / # main pins on any first-party reusable #1449 engine.sh corruption as fixture. (AC: feat: add Copilot engine support via REVIEW_ENGINE toggle #5)
Dev Notes
- Keep the detector narrow and mechanical (duplicate declarations) rather than attempting general "is this diff correct" semantic analysis — that's not tractable and would be its own scope creep.
f5bbcc5c(on feat: implement issue #687 — [Phase 2] Generalized CI compliance test: reject SHA / # main pins on any first-party reusable #1449's branch) is a real prior instance of manually fixing exactly this — read it before implementing, it's the closest existing precedent for what "corrected" looks like.- Coordinate with bug(dev-lead): rebase handler times out (exit 124) on hard conflicts instead of aborting; sentinel bursts #865: both live on the conflict-resolution path but at different points (mid-resolution timeout vs. post-resolution correctness) — likely share a home in
dev-lead-fix-reviews.sh's rebase intent but are separable pieces of work.
References
- feat: implement issue #687 — [Phase 2] Generalized CI compliance test: reject SHA / # main pins on any first-party reusable #1449 (scripts/engine.sh corruption, 2026-08-07 — the fixture)
- bug(dev-lead): rebase handler times out (exit 124) on hard conflicts instead of aborting; sentinel bursts #865 (sibling: unresolvable-conflict timeout, not corruption)
- P2 (#860 follow-up): keep agent branches rebased/squashed instead of merge-stacked #949 (closed; merge-vs-rebase volume, not correctness)
- [Phase 4] Name and guard the "agent optimizes the check, not the intent" failure mode #1468 (the broader "check vs. intent" pattern this instantiates)
f5bbcc5condev-lead/issue-687-20260803-0252(prior manual fix of the same failure mode)
Filed from steering review of epic #1402, 2026-08-07, per maintainer request after #1449's engine.sh corruption was found and flagged.
Activity
don-petry commentedon Aug 12, 2026
Dev-Lead Implementation Plan
Issue: #1482 — [Phase 4] Post-conflict-resolution integrity check — catch duplicated/corrupted content before it's trusted
Scope
Add a narrow, mechanical duplicate-top-level-declaration detector for shell scripts and run it on the dev-lead
rebase(conflict-resolution) path immediately after a resolution lands, posting an advisory, signal-only comment naming the file and duplicated symbols when the resolution introduced new duplicate declarations — without auto-blocking or auto-reverting.Design
scripts/lib/conflict-integrity.sh(new pure-logic lib):extract_top_level_symbols(column-0fn:/var:declarations),symbol_counts, andnew_duplicate_symbols <resolved> <parentA> <parentB>— flags a symbol only when its declaration count in the resolved file exceeds its count in both parents (so pre-existing/legit repetition and large legitimate upstream merges don't trip it — AC feat: feature ideation framework — adversarial proposer/challenger/synthesizer #6). Plusformat_integrity_findingsto render the comment body.scripts/dev-lead-fix-reviews.shrebase intent: capture the pre-resolution SHA before the engine runs; after a successful resolution, diff changed*.shfiles, extract each file's two parent blobs (origin/$BASE_REFand the pre-resolution SHA), run the detector, and post one idempotent advisory comment (<!-- dev-lead-conflict-integrity ... -->) on any finding. The check never changes the exit code (signal-only, AC Optimize review: small-PR and incremental fast paths #4).Implementation Checklist
scripts/lib/conflict-integrity.sh— detector + formatter (AC test issue from agent #1, feat: feature ideation framework — adversarial proposer/challenger/synthesizer #6)scripts/dev-lead-fix-reviews.sh— source lib, capture pre-resolve SHA, run check + post advisory comment in rebase path (AC Go-live improvements for PR review agent #2, Add @claude delegation, auto-merge, and rebase handling #3, Optimize review: small-PR and incremental fast paths #4)tests/dev-lead/fixtures/conflict-integrity/— feat: implement issue #687 — [Phase 2] Generalized CI compliance test: reject SHA / # main pins on any first-party reusable #1449 engine.sh corruption fixture + a legit-large-diff fixture (AC feat: add Copilot engine support via REVIEW_ENGINE toggle #5, feat: feature ideation framework — adversarial proposer/challenger/synthesizer #6)Tests to Write
tests/dev-lead/unit/test_conflict_integrity.bats— detectsrun_writer/parse_reset_time/extract_verdict_jsondoubled in the resolved fixture (the feat: implement issue #687 — [Phase 2] Generalized CI compliance test: reject SHA / # main pins on any first-party reusable #1449 corruption); asserts no false positive on a legit large diff and on symbols already duplicated in a parent (AC feat: add Copilot engine support via REVIEW_ENGINE toggle #5, feat: feature ideation framework — adversarial proposer/challenger/synthesizer #6). Auto-discovered bytest-dev-lead.yml(tests/dev-lead/unit/*.bats) — the same job that surfaced feat: implement issue #687 — [Phase 2] Generalized CI compliance test: reject SHA / # main pins on any first-party reusable #1449.Test Command
bats tests/dev-lead/unit/test_conflict_integrity.bats(full dev-lead suite:bats tests/dev-lead/unit/); lint:bash .dev-lead/scripts/dev-lead-lint.shdon-petry commentedon Aug 12, 2026
Dev-Lead: Implementation Complete
Plan Execution
scripts/lib/conflict-integrity.sh— pure-logic duplicate-top-level-declaration detector (AC test issue from agent #1, feat: feature ideation framework — adversarial proposer/challenger/synthesizer #6)dev-lead-fix-reviews.shrebase (conflict-resolution) intent, running on the resolution path itself (AC Go-live improvements for PR review agent #2)engine.shcorruption (run_writer/parse_reset_time/extract_verdict_jsondoubled) as the fixture (AC feat: add Copilot engine support via REVIEW_ENGINE toggle #5)How it works
The detector emits top-level
fn:/var:declarations (column-0 only, so nested/localdecls are ignored) and flags a symbol only when its declaration count in the resolved file exceeds its count in both parents — the base ref (origin/<base>) and the branch tip captured before the resolution. A raw line-count delta is deliberately not a trigger (AC #6); duplicate-declaration count-growth is the precise signal. It is signal-only: the advisory comment is idempotent (<!-- dev-lead-conflict-integrity pr=N -->) and the call is|| true-guarded so a detector fault can never fail a real resolution.Scope note
Wired into dev-lead's
rebaseintent only.auto-rebase.ymlis a thin caller stub delegating to a cross-repo reusable (petry-projects/.github) that AGENTS.md/CLAUDE.md forbid modifying here; the issue's Dev Notes namedev-lead-fix-reviews.sh's rebase intent as the shared home, which is where it landed. If the check is later promoted into the central auto-rebase reusable, the pure lib is reusable as-is.Test Results
An end-to-end smoke test in a scratch git repo (base → branch → botched resolution on HEAD,
origin/mainset) confirmed the wrapper's git plumbing extracts both parent blobs viagit showand reproduces the exact #1449 finding:Files Changed
scripts/lib/conflict-integrity.sh(new):extract_top_level_symbols,symbol_counts,new_duplicate_symbols,format_integrity_findings.scripts/dev-lead-fix-reviews.sh: source the lib; capture pre-resolution SHA; run the check + post the advisory comment (post_integrity_warning,run_post_resolution_integrity_check) in the rebase intent.tests/dev-lead/unit/test_conflict_integrity.bats(new): regression + false-positive coverage.tests/dev-lead/fixtures/conflict-integrity/(new):parent_base.sh,parent_branch.sh,resolved_corrupted.sh(the feat: implement issue #687 — [Phase 2] Generalized CI compliance test: reject SHA / # main pins on any first-party reusable #1449 fixture),resolved_clean.sh,parent_predup.sh,resolved_large_legit.sh.Notes
lint.ymledit needed: dev-lead unit tests are auto-discovered bytest-dev-lead.yml(bats tests/dev-lead/unit/), the same job that surfaced feat: implement issue #687 — [Phase 2] Generalized CI compliance test: reject SHA / # main pins on any first-party reusable #1449.feat: implement issue #1482 — [Phase 4] Post-conflict-resolution inte…
feat: implement issue #1482 — [Phase 4] Post-conflict-resolution inte…
feat: implement issue #1532 — conflict-integrity parser misses next-l…