Skip to content

Commit a999f52

Browse files
committed
test(scripts/pm): pin repro 1 and its discriminating mutation in the self-test
Case 11 replays the card's sequence on the rerun fixture — conflict, hand resolution, a refused step-3 commit, the by-hand hand-off commit — and pins that the record reads `handoff`, that the next run reports the sequence discharged without re-entering `rerun`, and that the regeneration (`FLOWX`) survives. It also pins the new refusal for a record nothing ever marked. Case 11b puts the containment gate back by literal replacement and replays run 3 in a copy of that tree: the run re-enters `rerun`, takes main's side back and step 3 commits the revert with exit 0 — the card, reproduced on demand. Claude-Session: https://claude.ai/code/session_017ETYWqMQD4qMtZzAGovWNi Co-authored-by: Claude <noreply@anthropic.com>
1 parent bab04b4 commit a999f52

1 file changed

Lines changed: 72 additions & 45 deletions

File tree

‎scripts/pm/os-regen-merge.sh‎

Lines changed: 72 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -628,17 +628,13 @@ rr_write_record() {
628628
# this script cannot prove.
629629

630630
# Is HEAD the very merge this record was written for? ⚠️ Containment is NOT that
631-
# question and using it for the rerun was the measured defect: every later commit
632-
# on the branch contains the recorded pre-merge tip too, so a run made AFTER the
633-
# operator finished step 3 by hand re-entered `rerun`, pinned the base back to the
634-
# pre-merge shas, and took main's side over their own regeneration commit — which
635-
# step 3 then COMMITTED, exit 0, nothing refused. Equality is the question, and
636-
# both parents answer it: the recorded merge is the commit whose first parent is
637-
# the recorded pre-merge tip and whose second is the main tip that was merged.
631+
# question, and asking it for the rerun was the measured defect: every later branch
632+
# commit contains the recorded pre-merge tip too, so a run made after the operator
633+
# finished step 3 BY HAND re-entered `rerun`, took main's side back over their own
634+
# regeneration commit, and step 3 COMMITTED it. Both parents answer equality.
638635
rr_head_is_recorded_merge() {
639-
rr_h1="$(git rev-parse --verify --quiet 'HEAD^1')" || return 1
640-
rr_h2="$(git rev-parse --verify --quiet 'HEAD^2')" || return 1
641-
[ "$rr_h1" = "$rr_rec_branch_tip" ] && [ "$rr_h2" = "$rr_rec_main_tip" ]
636+
[ "$(git rev-parse --verify --quiet 'HEAD^1' || true)" = "$rr_rec_branch_tip" ] &&
637+
[ "$(git rev-parse --verify --quiet 'HEAD^2' || true)" = "$rr_rec_main_tip" ]
642638
}
643639

644640
rr_classify() {
@@ -653,16 +649,14 @@ rr_classify() {
653649
fi
654650
if git merge-base --is-ancestor "$rr_rec_main_tip" HEAD 2>/dev/null; then
655651
# The recorded merge is IN this HEAD, so step 1 is behind us either way.
656-
# ⛔ Redoing step 2 is sound ONLY while HEAD still IS that merge: a branch
657-
# commit past it is work step 2 would discard. `handoff` means step 3's
658-
# commit was refused and step 2 was left discharged in the index, so HEAD
659-
# back AT the merge means the operator dropped that index and step 2 is
660-
# owed again; HEAD past it means they committed and step 4 is what is owed.
652+
# ⛔ Redoing step 2 is sound ONLY while HEAD still IS that merge — a commit past
653+
# it is work step 2 would discard. `handoff` (step 3 refused, the index left
654+
# discharged) AT the merge means that index was dropped and step 2 is owed again;
655+
# PAST it means the operator committed, so step 4 is owed. A `pending` record past
656+
# its merge says nobody ever marked step 2 done — refused below, ⛔ never redone.
661657
if [ "$rr_rec_phase" != done ] && rr_head_is_recorded_merge; then
662658
echo rerun
663659
elif [ "$rr_rec_phase" = pending ]; then
664-
# Never marked, yet the branch moved past the merge: who discharged step 2
665-
# is unreadable from here, so it is refused rather than redone.
666660
echo advanced
667661
elif [ "$(git rev-parse origin/main)" = "$rr_rec_main_tip" ]; then
668662
echo settled
@@ -697,28 +691,23 @@ rr_refuse_stale() {
697691
exit 1
698692
}
699693

700-
# The record still says `pending` and the branch has commits PAST the recorded
701-
# merge — the state step 3's refusal used to leave behind, because it exits before
702-
# marking anything and its own instruction finishes the commit OUTSIDE this script.
703-
# Marked `handoff` that refusal no longer lands here; an unmarked record (a run
704-
# killed after step 1, or one written before this refusal existed) still does.
694+
# Recorded `pending`, yet the branch has commits PAST the recorded merge — the state
695+
# step 3's refusal used to leave behind, since it exits before marking anything while
696+
# instructing the operator to finish the commit OUTSIDE this script. Marked `handoff`
697+
# that refusal no longer lands here; a record nothing ever marked still does.
705698
rr_refuse_advanced() {
706699
echo "✗ the recorded merge is in HEAD, the record still says \`pending\`, and this branch has" >&2
707700
echo " commits PAST that merge — refusing to redo step 2 over them." >&2
708701
rr_print_record
709-
echo " \`pending\` means no run ever marked step 2 discharged; the commits past the merge mean" >&2
710-
echo " somebody did something this script cannot read. Redoing step 2 now would pin the base to" >&2
711-
echo " the PRE-MERGE shas above and take main's side of every generated path both sides moved —" >&2
712-
echo " discarding those later commits' bytes — and step 3 would COMMIT that, exit 0." >&2
713-
echo " ⛔ That is a revert wearing a repair's message." >&2
714-
echo " If you finished step 3 BY HAND (its refusal says to), step 2 is already discharged and" >&2
715-
echo " the sequence resumes at STEP 4 — drop the spent record and go there:" >&2
716-
echo " rm -f $rr_record" >&2
717-
echo " If it is NOT discharged, perform step 2 by hand against the RECORDED base first:" >&2
702+
echo " \`pending\` means no run ever marked step 2 discharged, and the later commits mean" >&2
703+
echo " somebody did. Redoing it now pins the base to the PRE-MERGE shas above, takes main's" >&2
704+
echo " side of every generated path both sides moved — discarding those commits' bytes — and" >&2
705+
echo " step 3 COMMITS that, exit 0: ⛔ a revert wearing a repair's message. Finished step 3 BY" >&2
706+
echo " HAND, as its refusal says to? Then step 2 IS discharged: \`rm -f $rr_record\` and resume" >&2
707+
echo " at STEP 4. If it is NOT, do step 2 by hand against the RECORDED base, commit, then go:" >&2
718708
echo " git diff --name-only $rr_rec_base $rr_rec_main_tip -- <the merge=os-regen patterns>" >&2
719709
echo " git diff --name-only $rr_rec_base $rr_rec_branch_tip -- <the same patterns>" >&2
720-
echo " git restore --source=$rr_rec_main_tip -- <every path in BOTH lists> # tree only, ⛔ never --staged" >&2
721-
echo " then commit that as its own commit and continue with step 4." >&2
710+
echo " git restore --source=$rr_rec_main_tip -- <every path in BOTH lists> # tree only" >&2
722711
exit 1
723712
}
724713

@@ -1182,23 +1171,20 @@ mode_run() {
11821171
if [ -n "$(git status --porcelain)" ]; then
11831172
git add -A
11841173
if ! git commit --no-edit -m "merge origin/main (os-regen artifacts taken from main; regeneration follows)"; then
1185-
# ⛔ MARKED before this exit, and that marking is the repair: step 2 is
1186-
# discharged in the index at this instant, and the line below hands the
1187-
# commit to the operator — the ONE path that finishes step 3 outside this
1188-
# script. Left `pending`, the next run read containment, re-entered `rerun`
1189-
# and took main's side back over whatever that hand-off committed. The
1190-
# recorded conflict set rides along so a rerun after a discarded index
1191-
# still keeps the resolutions it asked for.
1174+
# ⛔ MARKED before this exit, and the marking is the repair: step 2 is discharged in
1175+
# the index at this instant and the line below hands the commit to the operator — the
1176+
# ONE path that finishes step 3 outside this script. Left `pending`, the next run read
1177+
# containment, re-entered `rerun` and took main's side back over whatever that hand-off
1178+
# committed. The conflict set rides along, so a DROPPED index still keeps resolutions.
11921179
rr_write_record handoff "$merge_base" "$branch_tip" "$main_side" "$rr_rec_conflicted"
11931180
echo "✗ step 3's commit was refused — the merge is staged but NOT committed." >&2
11941181
echo " ⛔ Do not regenerate yet: with a staged index a bare \`git commit\` after" >&2
11951182
echo " regeneration lands the STAGED side, not the tree you inspected." >&2
11961183
echo " Clear what the hook reported, then \`git add -A && git commit\` before step 4." >&2
1197-
echo " Step 2 IS discharged in that index and the record now says so (phase=handoff):" >&2
1198-
echo " $rr_record" >&2
1199-
echo " ⛔ Do NOT rerun this script to finish that commit — it would redo step 2 against" >&2
1200-
echo " the pre-merge base and take main's side back over what you just committed." >&2
1201-
echo " Once the commit exists the sequence resumes at STEP 4." >&2
1184+
echo " Step 2 IS discharged in that index and the record says so (phase=handoff), so the" >&2
1185+
echo " sequence resumes at STEP 4 once that commit exists: $rr_record" >&2
1186+
echo " ⛔ Do NOT rerun this script to finish it — that redoes step 2 against the pre-merge" >&2
1187+
echo " base and takes main's side back over what you just committed." >&2
12021188
exit 1
12031189
fi
12041190
else
@@ -2161,6 +2147,47 @@ mode_self_test() {
21612147
"$(git show HEAD:gen/deferred.txt | grep -c 'deferred v1-MAIN' || true)" 0
21622148
cd "$here"
21632149

2150+
# --- 11. REPRO 1 (this card): step 3's commit REFUSED hands the commit to the
2151+
# operator, and until the record said so the NEXT run re-entered `rerun`, redid
2152+
# step 2 against the pre-merge base and COMMITTED a revert of the operator's own
2153+
# regeneration — exit 0, nothing refused. `FLOWX` stands in for it.
2154+
st_fixture_rerun "$tmp/m"
2155+
bash "$SELF" >/dev/null 2>&1 || true # run 1 stops on the MIXED conflict
2156+
st_resolve_and_commit && st_record="$(rr_record_path)" # the merge, committed by hand
2157+
printf '#!/bin/sh\nexit 1\n' > .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit
2158+
out="$(bash "$SELF" 2>&1)" && rc=0 || rc=$? # run 2: the rerun, step 3 refused
2159+
st_case 'r1: the refused step-3 commit fails, MARKS handoff, warns against rerunning' \
2160+
"$rc/$(rr_field phase "$st_record")/$(printf '%s' "$out" | grep -c 'Do NOT rerun this script' || true)" '1/handoff/1'
2161+
# Then do EXACTLY what it says: clear the hook, regenerate, `git add -A && commit`.
2162+
rm -f .git/hooks/pre-commit && printf 'deferred v1-MAIN\nFLOWX\n' > gen/deferred.txt
2163+
git add -A && git commit -qm 'step 4 by hand: regenerate (FLOWX)'
2164+
cp -a "$tmp/m" "$tmp/m-mut" # 11b replays run 3 from right here
2165+
out="$(bash "$SELF" 2>&1)" && rc=0 || rc=$? # run 3: the card's own run
2166+
st_case 'r1: run 3 exits 0 discharged — no rerun, no side taken, and FLOWX SURVIVES (the card read 0)' \
2167+
"$rc/$(printf '%s' "$out" | grep -c '^→ RERUN:' || true)/$(printf '%s' "$out" | grep -c 'already discharged' || true)/$(printf '%s' "$out" | grep -c "TAKING main.s side" || true)/$(git show HEAD:gen/deferred.txt | grep -c FLOWX || true)" '0/0/1/0/1'
2168+
# The safety net for a record nothing ever marked (a run killed after step 1, or one
2169+
# written before the marking existed): refused, ⛔ never silently redone.
2170+
sed 's/^phase=.*/phase=pending/' "$st_record" > "$st_record.t" && mv "$st_record.t" "$st_record"
2171+
out="$(bash "$SELF" 2>&1)" && rc=0 || rc=$?
2172+
st_case 'r1: a pending record past its own merge is REFUSED with the by-hand step 2' \
2173+
"$rc/$(printf '%s' "$out" | grep -c "git restore --source=$(rr_field main_tip "$st_record")" || true)" '1/1'
2174+
cd "$here"
2175+
2176+
# --- 11b. THE DISCRIMINATING MUTATION for case 11: put the containment gate back —
2177+
# the ONE line — and the card reproduces on demand, in the tree case 11 copied
2178+
# aside one commit earlier. Same perl/\Q..\E replacement 6b, 8b, 9b, 10b use.
2179+
mutated_reentry="$tmp/mutated-reentry-os-regen-merge.sh"
2180+
MUT_ANCHOR=' if [ "$rr_rec_phase" != done ] && rr_head_is_recorded_merge; then' \
2181+
MUT_INSERT=' if [ "$rr_rec_phase" != done ]; then' \
2182+
perl -0777 -pe 's/\Q$ENV{MUT_ANCHOR}\E/$ENV{MUT_INSERT}/' "$SELF" > "$mutated_reentry"
2183+
st_case 'the equality gate has ONE call site, the mutation took, and it parses' \
2184+
"$(sed -n '1,/^# --- self-test/p' "$SELF" | grep -c 'rr_head_is_recorded_merge; then' || true)/$(diff -q "$SELF" "$mutated_reentry" >/dev/null 2>&1; echo $?)/$(bash -n "$mutated_reentry" >/dev/null 2>&1; echo $?)" '1/1/0'
2185+
cd "$tmp/m-mut/work"
2186+
mut_out="$(bash "$mutated_reentry" 2>&1)" && mut_rc=0 || mut_rc=$?
2187+
st_case 'mutated: run 3 re-enters the rerun, exits 0, takes main s side back, and step 3 COMMITS the revert — FLOWX gone' \
2188+
"$mut_rc/$(printf '%s' "$mut_out" | grep -c '^→ RERUN:' || true)/$(printf '%s' "$mut_out" | grep -c 'TAKING main.s side of gen/deferred.txt' || true)/$(git show HEAD:gen/deferred.txt | grep -c FLOWX || true)" '0/1/1/0'
2189+
cd "$here"
2190+
21642191
if [ "$st_fail" -ne 0 ]; then
21652192
printf '✗ os-regen-merge self-test: %d case(s) failed.\n' "$st_fail"
21662193
return 1

0 commit comments

Comments
 (0)