Repository navigation
fix(pm/os-regen-merge): step 2 keeps a MIXED path's merged bytes, and reads main's side off the pinned tip - #22843
Merged
objectstack-fleet[bot] merged 2 commits intoOct 11, 2026
Conversation
…run over a MIXED path (red before the fix) The self-test gains a classification table for all sixteen merge=os-regen patterns (fifteen whole, one mixed), held equal to .gitattributes and to the driver's `mixed:` rows in both directions, and one rerun fixture per mixed row: a branch-edited hand row main never touched, a conflict elsewhere, the merge committed, main advancing past the record, then the prescribed rerun. Red on this commit by design: the rerun takes main's side of the mixed path (the branch row is lost) and reverts the branch-only path to its base bytes. Claude-Session: https://claude.ai/code/session_011u73oxZ5X95qrARPTeoU6v Co-authored-by: Claude <noreply@anthropic.com>
…reads main's side off the pinned tip Step 2 took main's whole side of every routed path both sides had moved, on the premise that the driver silently dropped one side there. That holds for a wholly generated row and not for a MIXED one: the driver defers a mixed path only when that is lossless and otherwise text-merges or conflicts, so taking main's side reverted the branch's hand-written rows, exit 0, every gate green. Replayed on the real objects of the measured rerun (merge 64e53f1, recorded base e84aeb3, tips 68ba543 and b7cd1af): the old script restored main's side of content/docs/permissions/system-context.mdx and committed it without row 11b; this one keeps the merged bytes and commits nothing. Which paths are mixed is read off the driver table at the pre-merge tip, and an unreadable table refuses step 2 before step 1 merges anything. The set main moved is now read against the pinned main side instead of the live origin/main, which a rerun's own fetch had moved past the recorded merge, so a branch-only edit read as "both sides" and was reverted to the base bytes. The by-hand step 2 the two refusals print now names the mixed exception. Two discriminating mutations put each fix's line back and reproduce exactly its own half of the defect. Claude-Session: https://claude.ai/code/session_011u73oxZ5X95qrARPTeoU6v Co-authored-by: Claude <noreply@anthropic.com>
objectstack-fleet
Bot
deleted the
claude/issue-22829-os-regen-merge-rerun-mixed-rows
branch
October 11, 2026 13:52
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Fixes #22829
Clause-②: no — a PM merge helper under
scripts/pm/**; no package'sfiles[]ships it, and no public surface moves (skip-changeset).Root cause (measured, not inferred)
The card's lead is falsified: the rerun read the right pre-merge base. The defect is step 2's per-file RULE, and it fires on a first run too.
Step 2 took main's whole side of every routed path that both sides had moved, on the premise that the driver silently dropped one side there. That premise holds for a wholly generated row. It is false for a MIXED row (a generated half plus hand-written rows, declared by
mixed:on itsREGEN_ARTIFACTSrow). The driver defers a mixed path only when deferring is lossless; otherwise it text-merges (both sides' rows kept) or conflicts. So "both sides changed it" does not mean a side was dropped. Taking main's side there is a pure revert of the branch's rows.What step 2 computed on the measured rerun, replayed against the real objects:
e84aeb36ce/68ba543e/b7cd1af9df(= the merge64e53f1d's merge-base and its two parents)branch_edited=git diff --name-only e84aeb36ce 68ba543e -- PATTERNScontent/docs/permissions/system-context.mdxmain_edited=git diff --name-only e84aeb36ce origin/main -- PATTERNS(live ref; at leastb7cd1af9df)b7cd1af9dfalone: 22 paths,system-context.mdxamong them; main changed it 9+/10− (census figures, row 14 deleted)system-context.mdxis not in itsystem-context.mdx64e53f1dholds the branch's rows 11b/23b and main's editsgit restore --source=b7cd1af9df -- content/docs/permissions/system-context.mdx(via the per-pattern loop), and step 3 committed itReplay on the real objects. A throwaway worktree at
64e53f1dwas given the record above.d8c7d3864) exits 0 and printsTAKING main's side of content/docs/permissions/system-context.mdx. Step 3 committedbf8a1750b, and row 11b's branch text is absent from HEAD (count 0).KEEPING the merged bytes of content/docs/permissions/system-context.mdx (MIXED …). Step 3 has nothing to commit. Row 11b is present (count 1) and main's row-14 deletion is kept too.Which base and which tips step 2 reads (M1)
branch_editedmain_editedbeforemain_editednowgit merge-base HEAD origin/main, read before step 1origin/main(same sha as$main_side, pinned one statement earlier)$main_side$main_sidebase=branch_tip=origin/main, after this run's owngit fetchmain_tip=main_tip=The second column of
main_editedis the second defect, in the same step and with the same symptom. On a rerun after main advanced, a path only the branch had edited read as "both sides", and step 2 restored the recorded tip's bytes (the base's) over the branch's edit. It did not fire on the measured rerun (b7cd1af9dfhad already movedsystem-context.mdx). The fixture below reproduces it on demand.The change (
scripts/pm/os-regen-merge.shonly)KEEPING the merged bytes … (MIXED …)notice, and step 4's regeneration re-derives the generated half as before.REGEN_ARTIFACTSmixed:rows at the pre-merge tip, which is the copy the driver ran with (git show BRANCH_TIP:scripts/regen-artifacts.mjs).main_editedreads the pinned$main_side, never the liveorigin/main.advancedandorphanrefusals now names the MIXED exception. Without it, the by-hand recipe reproduced the same revert.No change to
.gitattributes,scripts/git-merge-regen.mjs,scripts/regen-artifacts.mjsor any generator. No gate, workflow or script file was added. Everything lives in the existing--self-test, whichlint.ymlalready runs unconditionally.Classification of all 16 routed patterns (
OS_REGEN_CLASSES, in the script)Measured at each generator's write path on
d8c7d3864. The script's table holds this classification; the self-test checks it against the real.gitattributesand against the driver'smixed:rows, in both directions.packages/spec/liveness/state-counts/**gen:liveness-counts,writeTextShardDirpackages/spec/authorable-surface/**gen:schema, renders each shardpackages/spec/authorable-surface.base.jsongen:schema --update-base,serializeSurfaceBasepackages/spec/authorable-defaults/**gen:schemapackages/spec/json-schema.manifest/**gen:schemapackages/spec/api-surface/**gen:api-surfacepackages/spec/src/meta-spelling/meta-url-data.generated.tsgen:meta-url-spelling,render()then write (reads the old file only to compare)packages/spec/export-origins/**gen:export-originspackages/spec/declaration-map/**gen:declaration-mappackages/spec/api-surface-signatures.jsongen:api-surfacedocs/audits/2026-07-unknown-key-strictness-ledger.counts/**gen:strictness-ledger,writeTextShardDircontent/docs/references/**gen:docs(its header: no preserve/marker mechanism, by design)content/docs/permissions/system-context.mdxgen:system-context-census=check-system-context-census.mjs --fix, which rewrites census counts inside hand-written proseskills/*/references/_index.mdgen:skill-refs,generateIndex()skills/objectstack-ui/references/react-blocks.mdgen:react-blocks, oneemitof the whole filescripts/platform-object-tenancy-census.jsongen:platform-object-tenancy-census,renderArtefactFixtures (self-test cases 12, 13, 13a, 13b)
.gitattributesand the real driver (an empty problem list). Three firing controls show the check can go red:unclassified;stale;mixed:rows redsmixed-mismatch.mixedrow itsmixed:on the driver row.mixedrow. There is one today:content/docs/permissions/system-context.mdx, built at that path and routed by the table's own pattern. The fixture driver usesgit merge-file, the real driver's non-lossless limb.KEEPING, neverTAKING; HEAD holds the branch row and both main edits (3 of 3); the branch-only path keeps the branch bytes. A guard reds if the table ever names zero mixed rows.perl \Q..\Econvention). Each one puts back one fix's line and reproduces exactly its own half of the defect.Readings
d8c7d3864326e498500/0/1(wanted0/1/0: TAKING, no KEEPING); branch row 2 of 3; branch-only pathbranchonly v0; unreadable table0/0/merged(wanted1/1/unmerged)d8c7d3864script0/0/0, wanted0/1/1: the branch row is lost on a first run as wella280febb3✓ os-regen-merge self-test: all cases pass.mixed(case "$mixed_edited" inreplaced bycase "" in)a280febb31/1/0; run reads0/1/0/branchonly v1-BRANCH: exit 0, TAKING the mixed path, branch row gone, the other half intactref(main_editedback onorigin/main)a280febb31/1/0; run reads0/0/1/branchonly v0: the branch-only path reverted to base bytes, the mixed half intactLine budget: 2262 lines before, 2511 after, net +249 (+259 / −10) against the +250 cap.
Gates (all at
a280febb3)node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderives all exit 0.--ranreconciliation:✓ dispatch-gates --ran: 24 derived famil(ies) accounted for — 24 run, 0 NOT-MEASURED.bash scripts/pm/os-regen-merge.sh --self-test(exit 0, 140 ok) andpnpm check:pm-governed-merges(exit 0).scripts/check-shell-escape-residue.mjsdoes not exist on this tree, so there is nothing to run there.pnpm check:nul-bytes: OK, plus a raw control-byte scan of the script: 0 hits.Acceptance notes (observations, not filed)
--self-test"). The gap predates this PR. The new per-mixed-row loop carries its own non-vacuity guard. Carrier: none.lint.yml's comment above this step still says "five small fixture repos to pin 23 cases". It is drift in a comment; the current count is 140.origin/maind8c7d3864rather than the dispatch'sefcbac73c, because the standing dev contract saysorigin/main. Between the two commits,scripts/pm/os-regen-merge.sh,.gitattributesandscripts/git-merge-regen.mjshave an empty diff.Family, for the reviewer: #18062, then #18895 (landed in #18941 for its measured case). Both stay as they are; this PR touches neither.
Generated by Claude Code