-
Notifications
You must be signed in to change notification settings - Fork 1
feat: implement issue #1482 — [Phase 4] Post-conflict-resolution integrity check — catch duplicated/corrupted content before it's trusted #1496
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
d61964e
feat: implement issue #1482 — [Phase 4] Post-conflict-resolution inte…
bb45568
fix(bot): address bot feedback [skip ci-relay]
donpetry-bot ae0bea1
chore: dev-lead update (review-changes) [skip ci-relay]
donpetry-bot 2cfb08a
Merge branch 'main' into dev-lead/issue-1482-20260812-0038
don-petry d790faf
Merge branch 'main' into dev-lead/issue-1482-20260812-0038
don-petry f9b7d01
Merge branch 'main' into dev-lead/issue-1482-20260812-0038
don-petry 1e9d5d8
Merge branch 'main' into dev-lead/issue-1482-20260812-0038
don-petry 0dec927
Merge branch 'main' into dev-lead/issue-1482-20260812-0038
don-petry 7dd6ed1
Merge branch 'main' into dev-lead/issue-1482-20260812-0038
don-petry ce0798e
Merge branch 'main' into dev-lead/issue-1482-20260812-0038
don-petry 36b27b3
Merge branch 'main' into dev-lead/issue-1482-20260812-0038
don-petry 85e20a6
Merge branch 'main' into dev-lead/issue-1482-20260812-0038
don-petry a8efb18
chore: dev-lead update (review-changes) [skip ci-relay]
donpetry-bot 83f667a
chore: dev-lead update (review-changes) [skip ci-relay]
donpetry-bot File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
Large diffs are not rendered by default.
Oops, something went wrong.
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| #!/usr/bin/env bash | ||
| # conflict-integrity.sh — post-conflict-resolution integrity check (#1482). | ||
| # | ||
| # A narrow, mechanical detector for the corruption class from #1449: a botched | ||
| # automated conflict resolution that duplicates whole blocks of a shell script | ||
| # (grew scripts/engine.sh from 2688 to 4011 lines, doubling run_writer / | ||
| # parse_reset_time / extract_verdict_json). Such a resolution "completes" and | ||
| # reports success, so nothing on the conflict-resolution path checks it — it only | ||
| # surfaces hours later when an unrelated test trips over the corrupted function. | ||
| # | ||
| # These helpers are pure (no network/git side effects) so they are unit-testable | ||
| # in isolation; the git/gh plumbing that feeds them lives in the caller | ||
| # (dev-lead-fix-reviews.sh's rebase intent). The detector is deliberately | ||
| # mechanical — duplicate top-level declarations — not a semantic "is this diff | ||
| # correct" analysis, which is neither tractable nor in scope. | ||
|
|
||
| # extract_top_level_symbols <file> | ||
| # Emit one line per top-level declaration in file order, tagged by kind: | ||
| # fn:NAME — a function declaration (`NAME() {` / `NAME()` / `function NAME`) | ||
| # var:NAME — a top-level variable assignment (optionally export/readonly/declare) | ||
| # "Top-level" means the declaration begins at column 0 (no leading whitespace), | ||
| # so nested functions and in-function `local` assignments are ignored. A symbol | ||
| # declared N times appears N times. | ||
| extract_top_level_symbols() { | ||
| local file="$1" | ||
| [ -f "$file" ] || return 0 | ||
| awk ' | ||
| # NAME() { / NAME() (POSIX + ksh function forms), column 0 only. | ||
| /^[A-Za-z_][A-Za-z0-9_]*[[:space:]]*\(\)[[:space:]]*\{/ { | ||
| name = $0 | ||
| sub(/[[:space:]]*\(\).*$/, "", name) | ||
| print "fn:" name | ||
| next | ||
| } | ||
| # function NAME (with or without trailing parens). | ||
| /^function[[:space:]]+[A-Za-z_][A-Za-z0-9_]*/ { | ||
| name = $2 | ||
| sub(/\(.*$/, "", name) | ||
| print "fn:" name | ||
| next | ||
| } | ||
| # [export|readonly|declare -x] NAME=... top-level assignment, column 0. | ||
| /^(export[[:space:]]+|readonly[[:space:]]+|declare[[:space:]]+(-[A-Za-z]+[[:space:]]+)*)?[A-Za-z_][A-Za-z0-9_]*=/ { | ||
| line = $0 | ||
| sub(/^(export[[:space:]]+|readonly[[:space:]]+|declare[[:space:]]+(-[A-Za-z]+[[:space:]]+)*)/, "", line) | ||
| name = line | ||
| sub(/=.*$/, "", name) | ||
| print "var:" name | ||
| next | ||
| } | ||
| ' "$file" | ||
| } | ||
|
|
||
| # symbol_counts <file> | ||
| # Emit "SYMBOL<TAB>COUNT" for every top-level symbol, sorted. | ||
| symbol_counts() { | ||
| extract_top_level_symbols "$1" | LC_ALL=C sort | uniq -c \ | ||
| | awk '{ print $2 "\t" $1 }' | ||
| } | ||
|
|
||
| # _count_of <counts-block> <symbol> | ||
| # Look up a symbol's count in a "SYMBOL<TAB>COUNT" block; 0 if absent. | ||
| _count_of() { | ||
| local counts="$1" sym="$2" c | ||
| c="$(printf '%s\n' "$counts" | awk -F'\t' -v s="$sym" '$1 == s { print $2; exit }')" | ||
| printf '%s' "${c:-0}" | ||
| } | ||
|
|
||
| # new_duplicate_symbols <resolved> <parent_a> <parent_b> | ||
| # Emit "SYMBOL<TAB>COUNT" for each top-level symbol whose declaration count in | ||
| # the resolved file EXCEEDS its count in *both* parents — i.e., the resolution | ||
| # introduced extra copies. A symbol that already appeared N times in a parent and | ||
| # still appears N times is NOT flagged, so a legitimate large upstream merge (or | ||
| # an intentional pre-existing repetition) does not trip the check (AC #1, #6). | ||
| # A missing parent file counts as zero declarations (a brand-new file that itself | ||
| # ships duplicate declarations is still flagged). | ||
| new_duplicate_symbols() { | ||
| local resolved="$1" parent_a="$2" parent_b="$3" | ||
| { | ||
| extract_top_level_symbols "$parent_a" | awk '{print "a\t" $0}' | ||
| extract_top_level_symbols "$parent_b" | awk '{print "b\t" $0}' | ||
| extract_top_level_symbols "$resolved" | awk '{print "r\t" $0}' | ||
| } | awk -F'\t' ' | ||
| $1 == "a" { count_a[$2]++ } | ||
| $1 == "b" { count_b[$2]++ } | ||
| $1 == "r" { count_r[$2]++ } | ||
| END { | ||
| for (sym in count_r) { | ||
| rcount = count_r[sym] | ||
| if (rcount > 1) { | ||
| acount = count_a[sym] + 0 | ||
| bcount = count_b[sym] + 0 | ||
| maxp = (acount > bcount) ? acount : bcount | ||
| if (rcount > maxp) { | ||
| print sym "\t" rcount | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ' | LC_ALL=C sort | ||
| } | ||
|
|
||
| # format_integrity_findings <file> <findings> | ||
| # Render a Markdown bullet naming the file and each duplicated symbol, where | ||
| # <findings> is the "SYMBOL<TAB>COUNT" output of new_duplicate_symbols. Emits | ||
| # nothing when there are no findings. | ||
| format_integrity_findings() { | ||
| local file="$1" findings="$2" | ||
| [ -n "$findings" ] || return 0 | ||
| printf -- '- `%s` — duplicated top-level declarations after resolution:\n' "$file" | ||
| printf '%s\n' "$findings" | while IFS="$(printf '\t')" read -r sym count; do | ||
| [ -n "$sym" ] || continue | ||
| printf -- ' - `%s` (declared %s times)\n' "$sym" "$count" | ||
| done | ||
| } | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| #!/usr/bin/env bash | ||
| # Fixture: the BASE (main) side of the #1449 conflict — each top-level symbol | ||
| # declared exactly once. Represents scripts/engine.sh on `main`. | ||
| set -euo pipefail | ||
|
|
||
| MAX_RETRIES=3 | ||
|
|
||
| run_writer() { | ||
| local prompt="$1" | ||
| echo "writing $prompt" | ||
| } | ||
|
|
||
| extract_verdict_json() { | ||
| local log="$1" | ||
| grep -oE '\{.*\}' "$log" | ||
| } | ||
|
|
||
| parse_reset_time() { | ||
| local header="$1" | ||
| date -d "$header" +%s | ||
| } | ||
|
|
||
| build_prompt() { | ||
| echo "prompt" | ||
| } |
31 changes: 31 additions & 0 deletions
31
tests/dev-lead/fixtures/conflict-integrity/parent_branch.sh
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| #!/usr/bin/env bash | ||
| # Fixture: the BRANCH (PR head) side of the #1449 conflict — each top-level | ||
| # symbol declared exactly once, with the branch's own edits (run_writer gains a | ||
| # model arg, plus a branch-only run_reviewer). | ||
| set -euo pipefail | ||
|
|
||
| MAX_RETRIES=3 | ||
|
|
||
| run_writer() { | ||
| local prompt="$1" | ||
| local model="$2" | ||
| echo "writing $prompt with $model" | ||
| } | ||
|
|
||
| extract_verdict_json() { | ||
| local log="$1" | ||
| grep -oE '\{.*\}' "$log" | ||
| } | ||
|
|
||
| parse_reset_time() { | ||
| local header="$1" | ||
| date -d "$header" +%s | ||
| } | ||
|
|
||
| build_prompt() { | ||
| echo "prompt" | ||
| } | ||
|
|
||
| run_reviewer() { | ||
| echo "review" | ||
| } |
17 changes: 17 additions & 0 deletions
17
tests/dev-lead/fixtures/conflict-integrity/parent_predup.sh
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| #!/usr/bin/env bash | ||
| # Fixture: a parent that ALREADY declares `legacy_shim` twice (an intentional, | ||
| # pre-existing repetition upstream). The detector must not flag a symbol that was | ||
| # already duplicated in a parent — only duplication *introduced* by the merge. | ||
| set -euo pipefail | ||
|
|
||
| legacy_shim() { | ||
| echo "shim v1" | ||
| } | ||
|
|
||
| legacy_shim() { | ||
| echo "shim v2" | ||
| } | ||
|
|
||
| core_a() { | ||
| echo a | ||
| } |
30 changes: 30 additions & 0 deletions
30
tests/dev-lead/fixtures/conflict-integrity/resolved_clean.sh
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| #!/usr/bin/env bash | ||
| # Fixture: a CORRECT resolution of the #1449 conflict — the union of both sides, | ||
| # each top-level symbol declared exactly once. Must produce NO integrity finding. | ||
| set -euo pipefail | ||
|
|
||
| MAX_RETRIES=3 | ||
|
|
||
| run_writer() { | ||
| local prompt="$1" | ||
| local model="$2" | ||
| echo "writing $prompt with $model" | ||
| } | ||
|
|
||
| extract_verdict_json() { | ||
| local log="$1" | ||
| grep -oE '\{.*\}' "$log" | ||
| } | ||
|
|
||
| parse_reset_time() { | ||
| local header="$1" | ||
| date -d "$header" +%s | ||
| } | ||
|
|
||
| build_prompt() { | ||
| echo "prompt" | ||
| } | ||
|
|
||
| run_reviewer() { | ||
| echo "review" | ||
| } |
48 changes: 48 additions & 0 deletions
48
tests/dev-lead/fixtures/conflict-integrity/resolved_corrupted.sh
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| #!/usr/bin/env bash | ||
| # Fixture: the BOTCHED resolution of the #1449 conflict — the merge duplicated | ||
| # whole blocks, so run_writer / extract_verdict_json / parse_reset_time (and the | ||
| # MAX_RETRIES constant) each appear TWICE, roughly doubling the file. This is the | ||
| # exact corruption signature that grew scripts/engine.sh from 2688 to 4011 lines. | ||
| set -euo pipefail | ||
|
|
||
| MAX_RETRIES=3 | ||
| MAX_RETRIES=3 | ||
|
|
||
| run_writer() { | ||
| local prompt="$1" | ||
| echo "writing $prompt" | ||
| } | ||
|
|
||
| extract_verdict_json() { | ||
| local log="$1" | ||
| grep -oE '\{.*\}' "$log" | ||
| } | ||
|
|
||
| parse_reset_time() { | ||
| local header="$1" | ||
| date -d "$header" +%s | ||
| } | ||
|
|
||
| build_prompt() { | ||
| echo "prompt" | ||
| } | ||
|
|
||
| run_reviewer() { | ||
| echo "review" | ||
| } | ||
|
|
||
| run_writer() { | ||
| local prompt="$1" | ||
| local model="$2" | ||
| echo "writing $prompt with $model" | ||
| } | ||
|
|
||
| extract_verdict_json() { | ||
| local log="$1" | ||
| grep -oE '\{.*\}' "$log" | ||
| } | ||
|
|
||
| parse_reset_time() { | ||
| local header="$1" | ||
| date -d "$header" +%s | ||
| } |
39 changes: 39 additions & 0 deletions
39
tests/dev-lead/fixtures/conflict-integrity/resolved_large_legit.sh
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| #!/usr/bin/env bash | ||
| # Fixture: a LEGITIMATELY large resolution — it carries the pre-existing | ||
| # `legacy_shim` double declaration through unchanged (count still 2, same as the | ||
| # parent) and adds many brand-new single-declaration functions (a real upstream | ||
| # feature merge). Must produce NO integrity finding: the raw size grew, but no | ||
| # symbol's declaration count exceeds both parents. (AC #6) | ||
| set -euo pipefail | ||
|
|
||
| legacy_shim() { | ||
| echo "shim v1" | ||
| } | ||
|
|
||
| legacy_shim() { | ||
| echo "shim v2" | ||
| } | ||
|
|
||
| core_a() { | ||
| echo a | ||
| } | ||
|
|
||
| feature_b() { | ||
| echo b | ||
| } | ||
|
|
||
| feature_c() { | ||
| echo c | ||
| } | ||
|
|
||
| feature_d() { | ||
| echo d | ||
| } | ||
|
|
||
| feature_e() { | ||
| echo e | ||
| } | ||
|
|
||
| feature_f() { | ||
| echo f | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.