Skip to content

fix(scripts): replace the stale merge=os-regen rationale with one the tree still shows - #19075

Merged
os-bill merged 1 commit into
mainfrom
claude/issue-19007-stale-os-regen-rationale
Sep 18, 2026
Merged

os-bill merged 1 commit into
mainfrom
claude/issue-19007-stale-os-regen-rationale

Conversation

@os-bill

@os-bill os-bill commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

Fixes #19007

Clause-②: no

Three same-cause prose statements were left behind when #13585 loosened owner resolution.
Two of them are replaced here with statements this tree still shows; the third is left
alone, because it is the evidence rather than the defect. Prose only — no behaviour
change
, and ⛔ no merge=os-regen registration is added, ⛔ no .gitattributes row is
touched.

1. scripts/tenant-audit-census.mjs — the reason emitted INTO the generated counts file

Before (verbatim, as renderCountsFile() emitted it):

merge cleanly and WRONG. This file is deliberately NOT `merge=os-regen`: that
driver resolves an artefact's `gen:`/`check:` scripts in `@objectstack/spec`
only, and these are root-level tooling. The gate is the backstop — a wrongly

After:

merge cleanly and WRONG. This file is NOT `merge=os-regen`: no `.gitattributes`
row names it, so `git check-attr merge` over it reads `unspecified`. Routing it
would take a `REGEN_ARTIFACTS` row whose `gen:`/`check:` names exist in the
manifest that row declares as owner, and no manifest declares such a pair for
this census — the gate runs straight from the lint workflow. Root-level tooling
is no obstacle by itself: the driver resolves those names in whichever manifest
the row names, the root one included. The gate is the backstop — a wrongly

The FACT is kept (this file is not routed). The REASON is replaced, and every clause of
the new one is measurable on this tree:

new clause how it was measured reading
no .gitattributes row names it grep -n tenant-audit .gitattributes 0 hits
git check-attr merge reads unspecified git check-attr merge -- docs/audits/2026-08-tenant-audit-write-call-sites.counts.md unspecified
a row needs gen:/check: in its owner's manifest reconcileScripts() fails a row whose names are missing — fired below red on demand
no manifest declares such a pair for this census every tracked package.json, scripts keys AND values 0 of 601 entries in 83 manifests
the gate runs straight from the lint workflow .github/workflows/lint.yml, step "Tenant-audit census matches the tree" node scripts/check-tenant-audit-census.mjs
root-level tooling is no obstacle REGEN_ARTIFACTS rows declaring owner: ROOT_OWNER, and what git says about them 2 rows, both merge: os-regen

Fire control for the 0-hit row: the same instrument searching for strictness-ledger
the neighbouring .counts.md that IS registered — returns 2 hits (gen: and check:
in packages/spec). Reachable radius: manifest scripts text. A known target outside
it: a manifest script that reaches the census through an indirection that never spells
its name ("gen:x": "node scripts/wrapper.mjs" where the wrapper spawns it) — this
instrument reads manifests, it never follows a spawn.

2. scripts/git-merge-regen.mjs — "every row is spec-owned"

Before (verbatim):

 * `reconcileScripts` above is green on this tree for the same reason it was green
 * before #13585: every row is spec-owned, so it exercises exactly one manifest and
 * would keep passing if the loosening were reverted.

After (verbatim, plus the paragraph break the replacement adds):

 * `reconcileScripts` above no longer rests on a single manifest. Rows that declare
 * no owner default to `DEFAULT_OWNER`; rows that declare `ROOT_OWNER` send the
 * lookup to the root manifest instead (measured on this tree: 18 rows — 16
 * defaulted, 2 root-owned, none naming `DEFAULT_OWNER` explicitly). The four
 * `gen:`/`check:` names those two root-owned rows carry exist ONLY in the root
 * manifest, so reverting #13585's loosening would now turn that reconciliation RED
 * rather than leave it green.
 *
 * What a live tree still cannot show is the RULE. Which manifests get exercised is
 * a property of whatever the table happens to hold, and a table that fell back to a
 * single owner would stop touching the root path with nothing saying so.

What the paragraph exists to explain — why the pinned owner-resolution cases are needed
even though the live tree is green — is kept, and now rests on something true: the
composition that exercises the root path is a property of today's table, not of the rule.

3. scripts/git-merge-regen.mjs:769-777 — deliberately NOT touched

That block quotes #13585 correctly. It is the evidence the two statements above drifted
away from, and the diff contains zero lines from it.

The registry re-count, taken from the registry's own data structure

The card reported 4 ROOT_OWNER + 19 named + 41 default = 64. Re-counted by importing
scripts/regen-artifacts.mjs and bucketing with the module's own
ownerOf(entry) = entry.owner ?? DEFAULT_OWNER, at merge base 0ec81857a:

table rows owner: ROOT_OWNER named package owner no owner, defaults to DEFAULT_OWNER
REGEN_ARTIFACTS (registered; what reconcileScripts iterates) 18 2 0 16
NOT_DRIVER_MANAGED (deliberately not registered) 33 2 19 12
both tables 51 4 19 28

Each row reconciles: 2+0+16 = 18, 2+19+12 = 33, 4+19+28 = 51.

Three consequences:

  1. The card's 4 + 19 is a BOTH-TABLES count. The registration table declares
    ROOT_OWNER twice, and both of those rows really are routed
    (content/docs/permissions/system-context.mdx and
    scripts/platform-object-tenancy-census.json, each merge: os-regen). The card's
    other two ROOT_OWNER rows are NOT_DRIVER_MANAGED entries — reading unspecified
    is those rows working as recorded, not a failure to route.
  2. 41, and the 64 total it implies, reproduce under NEITHER instrument: structurally
    the defaulted bucket is 16 (registration table) or 28 (both tables), against totals of
    18 and 51.
  3. reconcileScripts itself prints the refutation of "exercises exactly one manifest":
    all 36 gen:/check: names resolve in their declared owner (@objectstack/spec (packages/spec/package.json), @objectstack/spec-monorepo (package.json)) — 36 = 18
    rows x 2 names, across TWO manifests.

Evidence

Behaviour leg — the generated artefact changes in the prose and nowhere else.
node scripts/tenant-audit-census.mjs --write, then both the committed and the
regenerated counts file put through the gate's own maskUnenforced() (the mask
scripts/check-tenant-audit-census.mjs applies to both sides of its byte comparison):
the masked diff is exactly the replaced paragraph, 3 lines out, 7 lines in, nothing else.

The raw diff carries one more line, the Measured on ... at ... marker, in the counts
file and in the census page. That marker is not attributable to this edit: a
regeneration on the PRISTINE tree, with zero source edits, already moved exactly those
two lines and nothing else. The gate masks its value deliberately
(UNENFORCED_MEASURED_AT: "The date and the ref are deliberately NOT compared to
anything"), its self-test pins "a stale measurement date passes", and the last commit to
touch these artefacts (#18943) carried the same marker bump. Both artefacts are committed
as the generator wrote them.

reconcileScripts leg, with a fire control. The card said it had not been measured
whether that reconciliation is still green under today's owner distribution.

  • node scripts/git-merge-regen.mjs --self-test → exit 0, including
    ✓ all 36 gen:/check: names resolve in their declared owner.
    Its CI spelling pnpm check:merge-driver → exit 0.
  • Fire control (a green that can go red, and proof the ROOT manifest is really read):
    through scripts/ablation-replace.mjs, the root manifest's gen:system-context-census
    key was renamed. The anchor hit once, the blob moved
    (57ab936beaf0 to 1074c54635), and the self-test went red naming the row:
    content/docs/permissions/system-context.mdx → gen:system-context-census [owner @objectstack/spec-monorepo, package.json]. A spec-only resolution could not produce
    that failure — which is the measured form of "reverting the loosening would now turn it
    red".
  • Restored and proven restored: blob back to 57ab936beaf0 = the HEAD blob,
    git diff HEAD empty, git status --porcelain empty.

Gates. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack
derived 64 families from this tree; all 64 were run with the exit code captured before any
pipe, and --ran reconciles: 64 derived, 64 run, 0 NOT-MEASURED, 0 UNRUN, every one
exit 0. Five of them first exited on a PREREQUISITE NOT MET (unbuilt @objectstack/lint,
@objectstack/formula, @objectstack/client-react, and gen:schema); the closures were
built and all five then passed — none was a finding. pnpm lint over the whole repo:
exit 0 in 1m27s. pnpm check:nul-bytes: exit 0, plus a direct control-character scan of
both edited files: 0 hits.

Changeset. Measured rather than assumed: each of the 4 changed paths against every
tracked manifest's files[]. The root manifest is private: true, and all four paths lie
outside every publishable package directory, so nothing already published moves — hence
skip-changeset. Fire control: the same instrument scores packages/spec/src/index.ts as
inside a published surface.

Acceptance notes

Observed while working, out of scope here, not filed:

  • The docblock this PR edits sits immediately above ANOTHER docblock, so it documents no
    declaration; the pinned cases it describes (reconcileOwnership) are about 330 lines
    below. pnpm check:scripts-symbol-anchors is green on it, so the repo's own instrument
    does not read this as a violation. Successor: the next PR editing these self-test blocks.
  • The NOT_DRIVER_MANAGED disposition for docs/audits/** explains itself in terms of
    hand-written ledgers and names the strictness .counts.md as "the ONE exception", while
    a second generated .counts.md — this census's — sits under the same prefix. Whether
    that one ought to route is the question scripts/tenant-audit-census.mjs states both that its counts file is and is not merge=os-regen #18960 deliberately left open, so this is that
    neighbour, not a new card.
  • No manifest script names this census, so its generator is invisible to the generator
    disposition reconciliation — a bound that reconciliation states about itself in its own
    docblock, and pnpm check:ratchet-remedy-authority (which owns that population) is
    green.

Generated by Claude Code

… tree still shows

The generated counts file explained "why this file is not `merge=os-regen`" with
a driver limitation that no longer exists: resolution has read the owner's own
manifest since the loosening, and two root-owned rows are registered and routed
today. The fact stays (the file is not routed); the reason is replaced with what
is measurable on this tree — no `.gitattributes` row names the path, routing
would take a registry row whose `gen:`/`check:` names exist in the manifest it
declares as owner, and no manifest declares such a pair for this census.

`git-merge-regen.mjs` carried the same stale statement one level up: "every row
is spec-owned, so it exercises exactly one manifest and would keep passing if the
loosening were reverted". Measured from the registry's own data structure, the 18
rows resolve to two owners (16 defaulted, 2 root-owned), and the four names those
two rows carry exist only in the root manifest — so a reverted loosening would
turn that reconciliation red, not leave it green. What the pinned cases still add
is the rule, which no live composition can show.

Prose only; no behaviour change. The counts artefact is regenerated, and the
unenforced `Measured on … at …` marker moves as it does on any regeneration.

Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3
Co-authored-by: Claude <noreply@anthropic.com>
@os-bill os-bill added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 18, 2026 — with Claude
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Sep 18, 2026
@os-bill
os-bill marked this pull request as ready for review September 18, 2026 16:30
@os-bill
os-bill added this pull request to the merge queue Sep 18, 2026
Merged via the queue into main with commit ed6c554 Sep 18, 2026
38 checks passed
@os-bill
os-bill deleted the claude/issue-19007-stale-os-regen-rationale branch September 18, 2026 17:00
os-elon-musk pushed a commit that referenced this pull request Sep 18, 2026
…ement

Conflict set: exactly ONE line in each generated artefact — the deliberately
UNENFORCED `Measured on <date> at <ref>` marker, which sits inside the census
page's generated region and in the counts file's corpus-scale block. Both sides
were stale; main's side is kept so this merge never asserts a marker older than
what landed, and the generator rewrites it in the commit that follows.

Everything else text-merged with BOTH intents intact, verified rather than
assumed:

- scripts/tenant-audit-census.mjs auto-merged with no conflict and now carries
  #19075's corrected `merge=os-regen` rationale (the emitted "no
  `.gitattributes` row names it ... Root-level tooling is no obstacle by
  itself") AND this branch's inline-type-literal placement
  (inlineEngineDoorOrOther, 9 occurrences). Zero conflict markers.
- the counts file holds #19075's new rationale paragraph and this branch's
  population (227, `declares tenancy off` 1, both new site rows, the
  0-by-construction paragraph); #19075's superseded rationale is gone.
- the census page holds every hand-written prose edit from this branch,
  including the claim the placement falsified. Measured: main's ONLY change to
  that page between this branch's base and origin/main is the one in-region
  marker line, so there is no hand-written text of main's to carry over.

Main still reads population 225 on both artefacts, so #19075 re-baselined
nothing and this branch's 225 -> 227 is still the whole change. Regeneration
from the merged tree follows as its own commit, per the ordering rule: running
a generator while the tree is in MERGE state stamps the PRE-MERGE tip, because
`measuredAt()` reads `git rev-parse HEAD`.

Claude-Session: https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh
Co-authored-by: Claude <noreply@anthropic.com>
os-elon-musk pushed a commit that referenced this pull request Sep 18, 2026
…rged tree

`node scripts/tenant-audit-census.mjs --write`, run AFTER the merge commit so
`measuredAt()` reads the merged HEAD rather than the pre-merge tip.

The staged diff is exactly two lines — the deliberately UNENFORCED
`Measured on ... at ...` marker in each artefact, now naming the merge commit
`30def652e` instead of main's `d4cb05cbf`. Nothing else moved, which is the
proof the merge resolution was byte-faithful: the generator, run against the
merged tree, reproduces both hand-resolved artefacts exactly. Had the semantic
merge dropped or mangled a row, a total or #19075's rationale paragraph, this
commit would carry it.

That also reconciles the two rules that govern this file. `os-regen-merge.sh`
requires a non-driver-managed generated artefact to be hand-resolved for the
merge commit and ⛔ not regenerated mid-merge (a mid-merge recompute describes
a half-merged tree); the file's own header requires a conflict to be resolved
BY REGENERATION and ⛔ never by hand. Both hold when the order is: resolve,
commit the merge, regenerate, and let the regeneration diff certify the
resolution.

#19075's corrected rationale re-measured on the merged tree with its own
instruments and still true: 0 `.gitattributes` rows name this census (fire
control: `strictness-ledger` returns 1, so the zero is not a dead reading),
`git check-attr merge` reads `unspecified`, 0 manifests name it in a `scripts`
entry, and the lint workflow runs the gate directly.

Claude-Session: https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants