Uh oh!
There was an error while loading. Please reload this page.
Record a merge disposition for every generator, and gate the ones nobody judged - #13876
Merged
os-project-manager merged 1 commit intoAug 31, 2026
Conversation
Every generator-owned artifact now has a recorded disposition — routed to merge=os-regen, or NOT_DRIVER_MANAGED with a per-path reason — and check:merge-driver reconciles the generators themselves, so the next one is met by a red gate instead of by a merge conflict. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
This was referenced Aug 31, 2026
os-project-manager
marked this pull request as ready for review
August 31, 2026 16:39
Uh oh!
There was an error while loading. Please reload this page.
os-project-manager
deleted the
claude/issue-13731-generator-artifact-routing-dispositions
branch
August 31, 2026 17:04
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 freeto 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#13731
Fixes#13335
Every generator-owned artifact now has a recorded disposition — routed to
merge=os-regen, orNOT_DRIVER_MANAGEDwith a per-path reason — andcheck:merge-drivergained a third reconciliation so that generator number 12 is met by a red gate instead of by a merge conflict.Re-derived census (⛔ not the card's figures — measured on this tree)
Command, and the numbers it produced at merge base
8c6a7fc0b:d32b0f501)gen:*key, or a--fix/--updatemode)genThe split reproduces exactly; only the manifest count differs (the card's "79 workspace manifests plus the root" reads as 80 against this tree's 79).
⭐ One correction that changed the shape of the work: the census is by SCRIPT, but a disposition is per PATH. Resolving each of the 11 to what it actually writes collapses them to 9 artifact groups, and two of those turn out not to be in git at all.
The dispositions — one per path, each with its recorded reason
Routed (
merge=os-regen) — 3 pathsskills/*/references/_index.mdgen:skill-refsskills/objectstack-ui/contracts/react-blocks.contract.jsongen:react-blocksREACT_BLOCKSdefinition. Two PRs adding different blocks are a set union git reports as a conflict.skills/objectstack-ui/references/react-blocks.mdgen:react-blockscheck:skill-refsandcheck:react-blocksrun inlint.ymlonpull_requestandmerge_groupwith nopaths:filter, and both RE-DERIVE the artifact from source rather than reading it back — so they also catch the silent case. This follows #13646's ruling (the CI regeneration check is load-bearing, the driver incidental) rather than re-litigating it.NOT_DRIVER_MANAGED— 7 paths, each with its own reasonskills/README.mdgen:skill-docsBEGIN/END GENERATED: skillsregion: 17 of 114 lines. The other 97 are hand-written prose, and the driver defers the WHOLE file. Routing would launder a sibling's prose edit — the exact trademigrations/registry.tsis kept out for.content/docs/ai/skills-reference.mdxgen:skill-docspackages/client/test-typecheck-debt.jsongen:test-typecheck-debtpackages/rest/test-typecheck-debt.jsongen:test-typecheck-debtpackages/sdui-parser/objectui-lockstep.jsongen:sdui-lockstep--updateneeds an objectui checkout; a merge driver has no network, no build and no sibling checkout, so "recompute from the merged sources" names sources not in this tree. And the pinned.objectui-sharecords which revision a human ported to — an act, not a projection.packages/spec/json-schema/**gen:openapi.gitignore:61) — git never merges it, so it has no merge semantics to decide. Recorded because it looks like the routedjson-schema.manifest/**beside it and invites the symmetry.sbom.jsongen:sbom.gitignore:73) — release-time output; nothing merges it and nocheck:proves it current.Already accounted for — no change beyond making it visible
gen:migration-registryand thepackages/speccopy ofgen:test-typecheck-debtwere alreadyNOT_DRIVER_MANAGEDby path; they gain agenfield so the new accounting can see them.gen:authorable-surface-baseis deliberately not any row'sgen(the reasoning already sits beside theauthorable-surface.base.jsonrow); it gains analsoWrittenByentry so it reads as judged rather than as unaccounted.What the instrument fails on
reconcileGenerators()inscripts/git-merge-regen.mjs. Population is enumerated from the manifests themselves — never a hand-kept list — so generator 12 enters it by existing. It fails, in both directions, when:Accounting is keyed by (owner, script), never by bare name:
gen:test-typecheck-debtexists in three manifests writing three different ledgers, and a name-keyed check would have let thepackages/specentry account for theclientandrestcopies — which were 2 of the 11 gaps, so it would have been born unable to see its own motivating case.Two more reconciliations came with it:
reconcileAttributeSemantics()—entryForPathand git must read a declared path the same way, measured againstgit check-attritself over all 7614 tracked files. This closes a latent bug the segment glob would otherwise have walked into:reconcileAttributescompares bytes, so a pattern git matches butentryForPathdoes not reconciles green and is then REFUSED by the driver mid-merge, blaming a table row that is present. A row matching nothing is a failure too.reconcileUntrackedDispositions()— the two "git never merges it" reasons are expiring claims, so they are asserted rather than asserted once. The daysbom.jsonis committed, a gate reddens instead of a merge conflict surfacing later.The false-positive bound (⚠️ stated, because a bound nobody writes down is a bound nobody checks)
--fix/--updatelimb currently adds zero members beyond thegen:*keys — measured: all 21 members carry agen:key. It is kept because it fails closed; its false-positive class is a transform likeeslint --fix, whose remedy is one ledger line. A gate asking for one line is not a noisy gate.scripts/*.mjsinvoked directly by a workflow is that shape, and that population belongs tocheck:ratchet-remedy-authority, which builds its own fromreaddirSync.#13335's reproduced conflict, and what this PR does to it
The card's exact rows, replayed in a temp repo — control (unrouted, today's
main) against this PR:The resolution #13335 records by hand — take either side, commit the merge, regenerate, let
check:skill-refsprove it — is what this row plusos-regen-merge.shstep 4 now do mechanically.os-regen-merge.shalso stops reporting the file as a conflict in a NON-generated file, which is what it did on that card.Reverse verification — 4 ablations, all RED in the predicted direction
Each mutation was proven on disk before the run (occurrence counts, not a bare
git diff --stat), and each restore proven bygit diff HEADempty and a blob-hash match against the HEAD blob. No build step: these are plain.mjsrun directly bynode, with nodist/resolution between the edit and the run.sdui-lockstepdispositionexit=1·✗ generator(s) with NO recorded merge dispositionexit=1· same refusal, naming itexit=1·✗ git routes these to merge=os-regen but entryForPath does not resolve themand✗ declared path(s) matching no tracked fileexit=1·✗ disposition(s) naming a generator that no manifest definesC is the one worth reading: it confirms the latent bug is real. Without the segment-glob support, routing
skills/*/references/_index.mdwould have reconciled green and then failed at merge time.Gates
Derived with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(13 families + 2 convention-triggered for editing a gate script), all run on final commitdc492c701:node scripts/check-test-completeness.mjsexits 3 when run bare — it requires a test-run log CI tees in, so it is NOT MEASURED locally rather than failed.ESLint — narrowed, with the evidence the narrowing needs: the changed files lint clean (
--format json: 2 files, 0 errors, 0 warnings). Population read from eslint's own--print-config, which reportsparserOptions.project = nullandprojectService = null⇒ type-aware linting is not enabled, so no untouched file's verdict can depend on this diff, and the diff changes no eslint config. The repo-wide sweep remains CI's run.Changeset
None, and
skip-changesetapplied. Measured, not assumed: all three touched paths live at the repo root, owned by the private@objectstack/spec-monorepo; of 69 publishable workspace packages, zero are rooted at the repo root and zero have afiles[]entry reachingscripts/or.gitattributes. Nothing publishes.Governed surface
⛔ No
skills/**file is edited.GOVERNED_SURFACESisdocs/adr/,.claude/,skills/,AGENTS.md,CLAUDE.md; this diff is.gitattributes+ two rootscripts/*.mjs. The fence stays intact and this is not a governed PR — confirming the devx seat's 03:29Z measurement on #13335.Generated by Claude Code
Generated by Claude Code