fix(devx): partition os-regen-merge.sh's step-1 conflict message by class - #14733
Conversation
…lass
os-regen-merge.sh step 1 called every merge conflict "in NON-generated
files" and told the operator "do not resolve generated files textually" --
correct for a plain non-regen conflict, but exactly backwards for a
`merge=os-regen` path the driver text-merged and left conflicted (a MIXED
row `git-merge-regen.mjs` declined to defer because the deferral would have
silently dropped hand-written prose). Following that line's instruction on
a MIXED conflict -- taking one side whole -- is precisely the outcome the
driver refused to allow.
Partition the conflicted set (`git diff --diff-filter=U`) against the
`merge=os-regen` path list the script already reads at :220, reusing it as
a pathspec the same way step 2 already does. Three messages:
- non-regen only: today's message, unchanged.
- regen-path only (MIXED, driver-declined): "hand-resolve the prose, take
either side of the anchors" -- the "do not resolve generated files
textually" line is suppressed, since it is about deferrable regen paths
and is actively wrong advice for a MIXED one.
- both classes: name each file's class; the suppressed line stays
suppressed since some of the generated conflicts present DO need
hand-resolution.
The merge's own output (including the driver's remedy, e.g. its printed
regeneration command) is now captured and echoed on both the success and
failure paths, so the operator sees the driver's own notice rather than a
generic pointer to "step 4".
Pinned in --self-test: a MIXED-conflict fixture (a real driver that
text-merges and conflicts, not the existing fixture's `true` no-op) for the
regen-only and both-classes cases, whose key assertion is the ABSENCE of
the "do not resolve generated files textually" line -- proven
discriminating by mutating the script back to the old unconditional message
and watching that assertion red.
scripts/git-merge-regen.mjs (the driver) is untouched; it behaved correctly
throughout. No relaxation of the deferral rules.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 33695084575 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 33696601161 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Fixes #14671
os-regen-merge.shstep 1 called everygit mergeconflict "in NON-generatedfiles" and told the operator "do not resolve generated files textually" —
correct for an ordinary conflict, but exactly backwards for a
merge=os-regenpath the driver text-merged and left conflicted (a MIXED row
git-merge-regen.mjsdeclined to defer because deferring would silently drophand-written prose). Following that line's instruction on a MIXED conflict —
take one side whole — is precisely the outcome the driver refused to allow.
scripts/git-merge-regen.mjs(the driver) is untouched — it behavedcorrectly throughout; the defect was entirely in the wrapper's summary of it.
No relaxation of the deferral rules, no "generated always takes ours"
shortcut.
The fix
Partition the conflicted set (
git diff --name-only --diff-filter=U) againstthe
merge=os-regenpath list the script already reads at:220, reusing itas a
git diffpathspec the same way step 2 already does forbranch_edited/main_edited. No new inputs, no new config.Any conflict on a regen path is necessarily one the driver declined to defer
(a non-
mixedrow always resolves with exit 0, no markers), so a regen-pathconflict always means "hand-resolve the prose," never "take one side" — the
opposite of what a wholly-generated deferred path calls for.
Before (every conflict, regardless of class):
After — non-regen conflicts only (unchanged):
After — regen-path conflicts only (MIXED, driver-declined; the
suppressed-line case):
The "⛔ Do not resolve generated files textually" line is gone here — it is
correct advice for a deferrable regen path and actively wrong, silently
destructive advice for a MIXED one. The driver's own notice (including the
regeneration command it prints, e.g.
pnpm gen:system-context-census) iscaptured from the merge's output and echoed above this message rather than
scrolling past unread — so the operator is pointed at the driver's own
remedy, not a generic "step 4".
After — both classes present:
Each file is named under its own class; the suppressed line stays suppressed
here too, since some of the generated conflicts present in this run do need
hand-resolution.
Self-test
Added
st_fixture_regen_conflict— a fixture whose merge driver is a smallscript that runs a real
git merge-file(not the existing fixture'strueno-op) and, on a genuine 3-way conflict, prints a driver-shaped remedy(mirroring
git-merge-regen.mjs's actual "NOT deferred… take either side andthen run:
pnpm gen:fixture-mixed" notice) before exiting non-zero — the samemove the real driver makes for a MIXED row it cannot losslessly defer.
Three new case blocks:
say "NON-generated files", and — the assertion that matters — the
absence of "Do not resolve generated files textually"; also asserts the
driver's own notice and its regeneration command are still shown.
vacuous: a literal (non-regex,
perl -0777 \Q..\E) replacement of theregen-branch message back to the old unconditional one, run against the
same fixture, and the suppressed line reappears in that run's output —
i.e. the pin reds on the original bug.
src/prose.txt) alongsidethe regen-path MIXED conflict (
gen/mixed.txt); asserts both files arenamed under their own class and the suppressed line stays absent.
(38 cases total pass, including all pre-existing ones — none were changed.)
Scope
Only
scripts/pm/os-regen-merge.shchanged.scripts/git-merge-regen.mjs(the merge driver) is untouched — confirmed by the diff above, and by design:
it classified the MIXED file, declined to defer, explained why, and printed
the right remedy throughout. The defect was entirely in the wrapper's summary
of a correctly-behaving driver.
Gates
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands scripts/pm/os-regen-merge.shderived 14 families; all 14 ran(under
scripts/pm/os-verify-lock.sh) and passed —node scripts/pm/dispatch-gates.mjs --ran ran.listconfirms14 derived, 14 run, 0 NOT-MEASURED, 0 UNRUN(check-test-completeness.mjsis aPREREQUISITE-NOT-MET/NOT-MEASURED by its own design when run without a saved
turbo run testlog — not a red, per its own printed text — and CI suppliesthat log).
bash scripts/pm/os-regen-merge.sh --self-test(the always-runslint.yml step) passes directly.
skip-changesetapplies: this PR touches onlyscripts/pm/**.🤖 Generated with Claude Code
https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
Generated by Claude Code