Skip to content

refactor(cli): declare the scaffold emission policy once — os init and os create stop restating the tsconfig and the devtool ranges - #15974

Draft
os-litant wants to merge 5 commits into
mainfrom
claude/issue-15818-extract-scaffold-emission-policy
Draft

refactor(cli): declare the scaffold emission policy once — os init and os create stop restating the tsconfig and the devtool ranges#15974
os-litant wants to merge 5 commits into
mainfrom
claude/issue-15818-extract-scaffold-emission-policy

Conversation

@os-litant

@os-litant os-litant commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

Part of #15818 — item (1) of option B, the half with no user-visible change.

os init and os create each wrote the tsconfig.json and the third-party dependency ranges a new project receives, in their own words. This makes that one declaration, in the module the second scaffolder already imports from.

Not closing the card: the third scaffolder's ^6.0.0 is deliberately left alone and the reasoning is below, so the PM re-triages rather than the merge deciding it silently.

Patch round (contract review). Two items, both fixed at 78fd6b2ade0: this branch reddened check-ci-filter-parity --self-test by its own diff, and the dated provenance claim it shipped was wrong. Both are written up in place below rather than appended, so the body says one thing. The surviving-value decisions are unchanged.

The measurement this rests on, re-derived on today's tree

The card's claim reproduces, and the numbers are worse than it wrote:

where typescript vitest
os initinit.ts:384/474/548 ^5.3.0 (3 write points) ^4.0.18 (1)
os createcreate.ts:260/375 ^5.8.0 (2 write points) ^4.0.0 (2)
create-objectstacktemplates/blank/package.json:26 ^6.0.0 (1)

Six write points, three values, spanning one TypeScript major. Dated off git log -G over the three files, as of 2026-09-05:

commit date what it did
338e68d2564 2026-02-07 writes ^5.3.0 and ^5.8.0 in the same commit — the two-value split, 210 days old
dbb54e12f0c 2026-05-25 lands the bundled template — also at ^5.3.0, so the tree is still two-valued
eaff01425b7 2026-07-14 moves that template ^5.3.0^6.0.0 — the third value, 53 days old

⚠️ Correcting this PR's first draft, and the card. The first draft of this body said the bundled template arrived at ^6.0.0 on 2026-05-25 and that its typescript line had never been touched since; both are false, and the card's "102 days" measures from the template's arrival rather than from the value's. The error came from a pickaxe misuse — git log -S'"typescript"' counts occurrences of that string, and a value change does not change the count, so the 2026-07-14 commit was invisible to it. git log -G shows both commits.

And the move that made the third value recorded no reasoning about TypeScript at all. #2907's commit message documents the @objectstack/* version sync and nothing else. Its whole one-file diff:

-    "@objectstack/spec": "^6.0.0",              +    "@objectstack/spec": "^14.0.0",
-    "@objectstack/runtime": "^6.0.0",           +    "@objectstack/runtime": "^14.0.0",
-    "@objectstack/driver-memory": "^6.0.0",     +    "@objectstack/driver-memory": "^14.0.0",
-    "@objectstack/plugin-hono-server": "^6.0.0" +    "@objectstack/plugin-hono-server": "^14.0.0"
-    "@objectstack/cli": "^6.0.0",               +    "@objectstack/cli": "^14.0.0",
-    "typescript": "^5.3.0"                      +    "typescript": "^6.0.0"

The five @objectstack/* ranges move off ^6.0.0; the typescript line moves onto it. Read the intent as you like — what is not in dispute is that no statement of it exists anywhere. That makes this card's argument stronger, not weaker: the third value was never a decision.

The control is in the same file as the defect. SCAFFOLD_PNPM_RANGE, getCliVersion(), renderPnpmWorkspaceYaml(), sanitizeNamespace() and — since PR #15893 landed — NPM_PACKAGE_NAME_MAX_LENGTH and validateProjectName() are imported by create.ts rather than restated. That is six symbols today, not the four the card names. Each has exactly one definition point in packages/cli, and none has drifted across any of the five emissions. Same files, same authors, same window: the restated values split into three, the imported ones did not move at all.

The decision: which value survives

The card fences this and requires it argued rather than picked. Two ranges had to survive a merge; both are argued from measurement, and neither changes what a scaffolded project installs.

typescript ^5.3.0. It is already a recorded decision on two live doc pages:

  • content/docs/getting-started/index.mdx:183 — "ObjectStack works with TypeScript 5.3+, but the project itself is built and tested against TypeScript 6.x."
  • content/docs/deployment/troubleshooting.mdx:359 — "TypeScript 5.3.0 or later for full type inference support."

^5.8.0 matches no statement anywhere in the repo. ^5.3.0 was also what three of the five emissions already carried, and it is measured rather than assumed: TypeScript 5.3.3 type-checks every shape these two commands emit with results identical to 6.0.3 — driven against this repo's own @objectstack/spec build, on the skipLibCheck configuration the scaffold actually emits (5.3.3 / 5.8.3 / 6.0.3, five emitted projects each: identical exit codes, identical error counts, identical error text). Choosing ^5.8.0 instead would have contradicted two published pages, and Zone 1 of this card forbids doc edits — so ^5.3.0 was the only choice that did not require moving something the card fences.

vitest ^4.0.0. Neither value is a recorded decision; both were written in the same 2026-02-07 commit. ^4.0.18 claims a patch-level floor nothing justifies and is strictly the narrower of the two.

Neither choice moves an installed byte. Read off the registry today: ^5.3.0 and ^5.8.0 both resolve to typescript 5.9.3; ^4.0.18 and ^4.0.0 both resolve to vitest 4.1.11. What changes is the floor each project declares — a support promise — so the surviving one is the promise the docs already make.

Can create-objectstack participate at all? No, and here is the measurement

It cannot import from @objectstack/cli, in either of the two ways that would matter:

  1. The dependency edge already runs the other way. packages/cli/package.json declares "create-objectstack": "workspace:*", and init.ts imports create-objectstack/created-summary. A reverse import is a cycle.
  2. It publishes as a two-dependency npx package (chalk, commander). Adding @objectstack/cli would drag the CLI's ~50-package workspace closure into npx create-objectstack.
  3. Its emission is a committed template file copied byte-for-byte by copyDir() in template-copy.ts. There is no renderer to route through a constant; scripts/sync-template-versions.mjs rewrites only @objectstack/* ranges and deliberately leaves third-party ones alone.

So this is a two-of-three extraction that says why. Unifying the third would move a scaffolded project from typescript 6.0.3 to 5.9.3 — a change to what an emitted project installs, which is user-visible and a support decision rather than a refactor.

⚠️ One correction to the triage comment, which flagged it as a suggestion rather than a ruling: template-consistency.test.ts:677/683 does not pin the blank template's ^6.0.0. Those lines are a syncObjectStackDeps unit fixture whose typescript: '^6.0.0' exists to assert the sync leaves non-@objectstack/* deps alone. Nothing in the tree pins the emitted value.

What actually changed on disk, driven and hashed

All five CLI emissions were driven for real — bin/run-dev.js under tsx, into throwaway directories, one process per shape — and every emitted file hashed before and after.

emission tree changed files
os init -t app identical (d11d5dcada628ecd, 7/7 files) none
os init -t empty identical (824e2dfd56010376, 5/5 files) none
os init -t plugin changed package.json only
os create plugin changed package.json only
os create example changed package.json only

Three one-line diffs, and nothing else in 29 emitted files:

init-plugin/package.json     -"vitest": "^4.0.18"        +"vitest": "^4.0.0"
create-plugin/package.json   -"typescript": "^5.8.0"     +"typescript": "^5.3.0"
create-example/package.json  -"typescript": "^5.8.0"     +"typescript": "^5.3.0"

Every one of the five tsconfig.json files is byte-identical before and after — the shared renderer reproduces both inline objects exactly. (Four of the five were already byte-identical to each other on main; the fifth, os create plugin, differed only in rootDir and include, which are the renderer's two parameters.)

Anti-vacuity control, non-zero and re-validated in the same run: the harness's own earlier run — before create-objectstack/dist existed — reported exit=2, files=0, tree=e3b0c44298fc1c14 (the sha256 of the empty string) for all five. Every reading above is exit=0 with 5-7 files. The predicted per-file result was written down before the "after" run and matched it exactly, file for file.

The pin, and the ablation that shows it can fail

packages/cli/test/scaffold-emission-policy.e2e.test.ts (6 cases). No expected value is transcribed: every expectation comes from the renderers, from the other scaffolder, or from the doc page that already states the answer. os init writes its tsconfig.json inside run(), so that half is measured by driving the real command and reading the bytes off disk.

Three mutations, each discriminating a different axis. Direction predicted in writing first, including which cases must stay GREEN and why; every mutation proved on disk by a removed-text count and an injected-marker count; baseline captured as a blob with git hash-object -w, restored with git cat-file blob under an EXIT INT TERM trap, and confirmed by git diff HEAD empty plus a blob hash equal to HEAD's after each leg.

mutation axis predicted observed
restate typescript as a literal in create.ts (2 sites) one definition per value 2 RED, 4 GREEN 2 failed, 4 passed — exactly the two named
re-inline the tsconfig literal in init.ts run() the command really calls the shared renderer 1 RED (the driven case only) 1 failed, 5 passed
move SCAFFOLD_TYPESCRIPT_RANGE off the documented floor the surviving value is the documented promise 1 RED (the docs case only) 1 failed, 5 passed

Mutation 2 is also the positive proof of source-vs-dist resolution for the spawned half: it touches packages/cli/src only, packages/cli/dist was not rebuilt between the legs, and the spawned command's emitted bytes still changed — so tsx bin/run-dev.js is loading source.

⚠️ Honest note on the first attempt at mutations 2 and 3: the perl one-liner took an Illegal division by zero on a / inside the replacement text, so nothing landed. The guard caught it — injected-marker count 0, reported VOID, no reading taken — and both were re-run through a python replacement. The numbers above are from the second run; the first produced no result at all rather than a green one.

The docs case has a limitation worth naming: it measures the exported constant against the pages, so mutation 1 (a literal restated in a template) leaves it GREEN. That is why case 2 of the first group exists — the two together cover both directions.

Verification

  • Gate union: 77 of 77 green. Re-derived after the patch round (the change set is 7 paths now), harvested with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands — exactly 77 lines on stdout — and asserted against the script's own Reconciliation — 77 famil(ies) line. The three families the first round did not owe are exactly the ones scripts/check-ci-filter-parity.mjs pulled in: check-ci-filter-parity --self-test, bare-root-worklist --self-test, check:pm-dispatch-gates. Exit codes captured before any pipe.
    • Six came back NOT MEASURED on the first pass, not red — check:dts-closure, check:dual-build-cjs-loads, check:i18n, check:i18n-coverage, check:type-check-debt at exit 3 (PREREQUISITE NOT MET), and check:i18n-walk-parity at exit 1 whose own verdict line reads PREREQUISITE NOT MET: the workspace CLI is not built. All six are green after the workspace build.
  • Artifact-roster block: 36 families, run separately. These sit outside the Reconciliation total by the tool's own design, and the first round did not run them — which is how check-ci-filter-parity --self-test went unrun while it was still in that block. Run now, 33 of 36 exit 0. The other three are NOT MEASURED, each by its own verdict line, and none is implicated by this diff: check-partof-closing-keyword exit 2 NOT WIRED (no PR_BODY/PR_NUMBER outside its workflow — driven separately below with this body's real text), check-single-claim-paths exit 2 NOT WIRED (needs PR_NUMBER and a token), and @objectstack/spec check:react-declaration-parity exit 1 saying MANIFEST is not set … This gate did NOT run — an on-demand gate whose trigger is the .objectui-sha pin moving, which this diff does not touch.
  • The pin this branch reddened, and how it was repaired. check-ci-filter-parity --self-test failed at the previous head: the pre-ci.yml's core filter misses ten more declared cross-package test inputs — content/ (7), docs/, skills/, .github/workflows/ #10015 rollback now uncovers 21 globs against a pin of === 19. ⛔ Not bumped. The delta was measured first — the same rollback fixture run outside the self-test yields 19 at origin/main and 21 here, with the symmetric difference being exactly content/docs/deployment/troubleshooting.mdx and content/docs/getting-started/index.mdx, this branch's two declarations and nothing else. Both are now recorded by name, the way the pin's own comment prescribes for [finding] Two scaffolders, one of which emits output that cannot install outside this monorepo: os create vs os init #14824's three, and the count moved with them. Verdicts, exit codes captured before any pipe:
    • node scripts/check-ci-filter-parity.mjs --self-testexit 0, ✓ check-ci-filter-parity --self-test: 47 assertions — … the pre-#10015 rollback uncovering the ten it fixed plus #10848's one plus #10178's two plus #12201's one plus #12924's one plus #14561's one plus #14824's three plus #15818's two, and the CI wiring read out of lint.yml.
    • node scripts/check-ci-filter-parity.mjsexit 0, OK: all 151 declared cross-package glob(s) (104 unique) are covered by \core` or `crosspkg`, every `crosspkg` entry still covers one, and the `test` job's `if:` still names both filters.`
    • The battery counts in SELF_TEST_BATTERIES are a floor, not an equality, so the two added cases (45 → 47) need no ledger edit.
  • check-partof-closing-keyword, driven with this body's real textPR_BODY="$(cat …)" node scripts/check-partof-closing-keyword.mjsexit 0, ✓ check:partof-closing-keyword: this PR carries no Part-of/closing-keyword contradiction.
  • pnpm --filter @objectstack/cli typecheck — green, including check:test-typecheck. The new test file is genuinely in that program: tsc -p tsconfig.test.json --listFiles names it (1 hit, not 0).
  • pnpm --filter @objectstack/cli exec vitest run --project unit177 files, 2380 passed, 0 failed (6 expected-fail).
  • The 13 scaffold-relevant suites across both tiers — init, create, scaffold-workspace-consistency, scaffold-manifest-schema, create-plugin-docs-parity, create-refuses-invalid-project-name, the three init-scaffold*/init-template*, init-created-files-summary, generate-scaffold-validates, commands, and the new pin — 195 passed, 0 failed.
  • Declared narrowing: the cli integration tier as a whole was cut off at 520s by the container's foreground ceiling (exit 124) and is NOT MEASURED here — not green, not red. The 13 files above include every scaffold-touching member of that tier and were run individually. CI runs the tier in full.
  • pnpm lint (eslint . --no-inline-config, repo-wide, the only style authority here) — exit 0, no findings. No narrowing needed.
  • Control bytes: check:nul-bytes green, plus a direct grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over every touched file — no hits.

All measurements above were taken at 78fd6b2ade0. origin/main has advanced 12 commits since this branch's base d4c2cb196; git merge-tree --write-tree reports a clean merge with no conflicts, so nothing was merged in.

Clause 2, declared per limb

  • Mechanical: no. No new key on any published payload — the emitted package.json and tsconfig.json key sets are unchanged, and only three values move. Nothing under packages/spec/src/** is touched.
  • Conformance: yes. The emitted floor is a declaration on a shipped face, and this widens which TypeScript and vitest versions a scaffolded project declares acceptable. It changes no verdict the CLI itself reaches, and it changes nothing a scaffold installs — but "re-selecting an input class on a shipped face" is not clearly out, and the doctrine is to grade yes when the call is not clear. needs:contract-review is on this PR and on the card.

Deliberately untouched

os-litant and others added 3 commits September 5, 2026 16:14
… module

`os init` and `os create` each restated the third-party ranges and the
`tsconfig.json` an emitted project carries. Measured on the tree, the
TypeScript range was written in six places across three scaffolders and had
split into three values (`^5.3.0` / `^5.8.0` / `^6.0.0`); vitest into two.

Both CLI scaffolders now read one definition per value. Surviving values:
`^5.3.0` for TypeScript (the floor two live doc pages already state) and
`^4.0.0` for vitest (no recorded decision for either; `^4.0.18` claimed a
patch-level floor nothing justifies). Neither changes what an emitted project
installs — `^5.3.0` and `^5.8.0` both resolve to typescript 5.9.3, `^4.0.0`
and `^4.0.18` both to vitest 4.1.11.

`create-objectstack`'s `^6.0.0` is deliberately untouched: it cannot import
from `@objectstack/cli` (the dependency edge runs the other way), and unifying
it would change what a scaffolded project installs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N
…issions

Both ends of every expectation are derived — from the renderers, from the
other scaffolder, or from the doc pages that already state the TypeScript
floor — so a transcription cannot go green on a half-edited tree.

`os init` writes its `tsconfig.json` inside `run()`, so that half is measured
by driving the real command into a throwaway directory and reading the bytes
off disk; an exported renderer nobody calls would pass every in-process
assertion. The two doc pages the floor case reads are declared in
`scripts/cross-package-test-inputs.mjs` and mirrored into `turbo.json`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N
@github-actions github-actions Bot added the size/l label Sep 5, 2026
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/cli, touching 16 documentable anchor(s).

9 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/ai/skills-reference.mdx (via node_modules (literal, a string literal in plugin; a string literal in renderScaffoldTsconfig; a string literal on a changed line))
  • content/docs/deployment/cli.mdx (via node_modules (literal, a string literal in plugin; a string literal in renderScaffoldTsconfig; a string literal on a changed line), os create (command, read off packages/cli/src/commands/create.ts), os init (command, read off packages/cli/src/commands/init.ts))
  • content/docs/getting-started/examples.mdx (via os init (command, read off packages/cli/src/commands/init.ts))
  • content/docs/getting-started/your-first-project.mdx (via os init (command, read off packages/cli/src/commands/init.ts))
  • content/docs/plugins/development.mdx (via moduleResolution (symbol, a field of const object SCAFFOLD_TSCONFIG_COMPILER_OPTIONS; a field of const object STANDALONE_COMPILER_OPTIONS))
  • content/docs/plugins/index.mdx (via os create (command, read off packages/cli/src/commands/create.ts))
  • content/docs/protocol/kernel/index.mdx (via os create (command, read off packages/cli/src/commands/create.ts))
  • content/docs/protocol/kernel/plugin-spec.mdx (via os create (command, read off packages/cli/src/commands/create.ts))
  • content/docs/upgrading.mdx (via node_modules (literal, a string literal in plugin; a string literal in renderScaffoldTsconfig; a string literal on a changed line))

1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17.mdx (via os init (command, read off packages/cli/src/commands/init.ts))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 4 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 22 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json f50c394da55846da8d38f1e1efadbc24faa26ce2packageMentionDocs.

Which tree this was computed on

This run read content/docs from 5599a4e0b459292d81addf7695e0d4deda430712 — the merge of head f43dae79fa8e86c7181ca0a0efaddd49a8adadea into base f50c394da55846da8d38f1e1efadbc24faa26ce2, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 5599a4e0b459292d81addf7695e0d4deda430712 && git checkout 5599a4e0b459292d81addf7695e0d4deda430712
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin f50c394da55846da8d38f1e1efadbc24faa26ce2 f43dae79fa8e86c7181ca0a0efaddd49a8adadea && git checkout -B drift-repro f50c394da55846da8d38f1e1efadbc24faa26ce2 && git merge --no-ff f43dae79fa8e86c7181ca0a0efaddd49a8adadea

node scripts/docs-audit/affected-docs.mjs --json f50c394da55846da8d38f1e1efadbc24faa26ce2

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs f50c394da55846da8d38f1e1efadbc24faa26ce2 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@os-litant os-litant left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Contract review (clause ②) — PR #15974 at 65bafb464e4 — verdict: CHANGES REQUESTED

Implemented-by: claude/issue-15818-extract-scaffold-emission-policy
Reviewed-by: session_01D47qPfEWVPmhguWgBZCi5N

Posted as a COMMENT review on purpose: the clause-② carrier is the needs:contract-review label, which the protocol says never migrates into a PR review state (no Request Changes), and GitHub would refuse an APPROVE from the PR's own account in any case. Labels are the seat's stroke and are untouched here.

Tier reading. Taken from my own subagent transcript, subagents/agent-ae2a533a64a56f10d.jsonl (meta "Contract review PR 15974"; it carries this task's fingerprint 65bafb464e4), not the dispatching session's file. All 10 harness-stamped type:"assistant" records carry the tier that export const CONTRACT_REVIEW_TIER declares at scripts/pm/dispatch-gates.mjs:8659 (21 occurrences of the identifier, one declaration) — equal; a sibling agent file read as control stamps differently, so the read discriminates on file identity. Fuse holds.

The verdict in one paragraph. Every clause-② claim this card was fenced on holds at source — the Zone-1 fence, the argued surviving values, the structural exclusion of the third scaffolder, and the correction to the triage claim (all four measured below, independently). What blocks is outside the limbs: the delivered head reddens a CI gate through its own diff (Lint & Repo Gatescheck-ci-filter-parity --self-test, red at the head and green on origin/main), and the shipped provenance narrative carries a false history claim about the third scaffolder that the seat's own ruling comment has already repeated. Both need a patch commit; since that moves the head, clearing the label now would be wasted. The clause-② substance is accepted as of 65bafb464e4, so the re-review of the patched head can be a delta review.

Blocking — what the patch round must carry

1. check-ci-filter-parity --self-test is red at the head, by this PR's hand. CI Lint & Repo Gates failed at 17:19:20Z: "1 of 45 assertion(s) failed — rolling crosspkg back to its pre-#10015 list uncovers the ten it fixed plus … #14824's three — got 21" (the pin is === 19). Reproduced locally at 65bafb464e4: exit 1, same line. Control on a second worktree at origin/main (aa6ba0623a9): exit 0, 45/45. The discriminating delta is exactly the two content/** declarations this PR adds for @objectstack/cli in scripts/cross-package-test-inputs.mjs (content/docs/deployment/troubleshooting.mdx, content/docs/getting-started/index.mdx): 0 hits on main, 2 at the head, and the rollback removes content/**, so both go uncovered and move the count 19 → 21. The pin's own comment states the rule and the precedent — "#14824's three: the three doc pages that card declared for @objectstack/cli … recorded here by name … a declaration added under a new root moves it and is recorded here by name." This card owes "#15818's two" the same way: in scripts/check-ci-filter-parity.mjs, the assertion (=== 19=== 21, and the message at ~L690), its mirrored description (~L797), and a comment paragraph naming the two pages. The PR does not touch that file today.

Why the dev's 74/74 did not see it: the --self-test spelling is outside the 74-family runnable total. --commands line 7 is node scripts/check-ci-filter-parity.mjs (plain); the human rendering lists node scripts/check-ci-filter-parity.mjs --self-test under "Artifact rosters — 37 famil(ies) whose silent verdict is a fact about a LIST", with the tool's own "⛔ never read their silence as a clearance — run them, or read them." The harvest was faithful; the block the tool says to run separately is where the red gate lives. (A note for the seat, not a re-litigation.)

2. A false history claim in shipped comments and the test header. init.ts:239-241, create.ts:68, scaffold-emission-policy.e2e.test.ts:16 and the PR body say the third value "arrived with the bundled template on 2026-05-25" and that its typescript line "has never been touched since" — 102 days. Measured: dbb54e12f0c (2026-05-25) added templates/blank/package.json with "typescript": "^5.3.0"; eaff01425b7 (PR #2907, 2026-07-14) changed it to "^6.0.0" (git log -L on the line shows exactly those two commits). So the three-value split is 53 days old at 2026-09-05, and from 2026-05-25 to 2026-07-14 the third scaffolder agreed with os init. #2907's message reasons only about the @objectstack/* ranges ("pinned ^6.0.0 while the registry publishes 14.x"); nothing in that diff reasons about a TypeScript version, so the template's ^6.0.0 is a ride-along value, not a recorded floor decision. That strengthens the surviving-value argument — but the narrative that ships must say the true history. The same commit also created the syncObjectStackDeps fixture lines 677/683 (item 4 below).

3. One number, please. The changeset (L7) and the test header (L15) say 211 days; the PR body says 210. 2026-02-07 → 2026-09-05 is 210. The changeset is CHANGELOG-bound.

Clause ②, per limb

  • Mechanical: NO — holds. @objectstack/cli's exports map is ., ./console, ./hook-body, ./package.json; src/index.ts:14 re-exports only the InitCommand default from commands/init.js, so the new SCAFFOLD_* / renderScaffoldTsconfig exports are module-internal. Control after a real build: dist/index.d.ts, dist/index.js, dist/hook-body.js, dist/utils/console.js carry 0 occurrences of either name. No new key on an emitted payload: all five emissions driven at the head, every emitted tsconfig.json equals the renderer output byte-for-byte; only three package.json values move.
  • Conformance: YES — correctly graded under the not-clear doctrine, and the content is accepted. The declared floor on three emitted package.json files widens (^5.8.0^5.3.0 ×2, ^4.0.18^4.0.0 ×1). Checklist ① derived judgments: that is the whole accept-set change; nothing installed changes (fence below). ② semver: @objectstack/cli patch — consistent. ③ boundary flags: the third-scaffolder question was ruled A by the seat on the card; the two handed-back findings (#15976, sanitizeNamespace's second definition) are not re-opened here.

The Zone-1 fence — HOLDS (resolution read 2026-09-05T17:09:44Z, https://registry.npmjs.org/)

typescript@^5.3.05.9.3; typescript@^5.8.05.9.3; vitest@^4.0.184.1.11; vitest@^4.0.04.1.11. Controls in the same run, discriminating on the range rather than replaying a cache: typescript@'^5.3.0 <5.9' → 5.8.3 and vitest@'^4.0.0 <4.1' → 4.0.18 (different answers to different ranges); dist-tags read alongside: typescript latest 7.0.2 (so ^5 caps at 5.9.x), vitest V4 = 4.1.11. Registry state can move; this is the reading at that timestamp. On disk at the head, the five driven emissions declare typescript ^5.3.0 ×5 and vitest ^4.0.0 ×2.

The declared floor is also honest, which #2907's history makes load-bearing: TypeScript 5.3.3 (installed from the registry) against the repo's 6.0.3, on all five freshly emitted projects with the workspace packages linked — init empty 0/0 identical; init app 1 error under each, identical text (the #15976 Data.Object error); init plugin the same; create plugin 0/0 identical; create example 0/0 identical. Control: an injected const ctrl: number = "…" is TS2322-red under both compilers.

Surviving-value decision — both citations hold at source

content/docs/getting-started/index.mdx:183 reads verbatim "ObjectStack works with TypeScript 5.3+, but the project itself is built and tested against TypeScript 6.x"; content/docs/deployment/troubleshooting.mdx:359 reads "TypeScript 5.3.0 or later for full type inference support." Both predate the PR (it touches no content/docs): written 2026-02-12 (5635a41b539, 304351db56a), the first re-affirmed by the 2026-06-15 docs audit (#1866, 7d94d2f1d8a). ^5.8.0 is unrecorded: a repo-wide sweep hits only the audit table docs/audits/2026-09-os-create-os-init-convergence-measurement.md:220 (a measurement row) and this PR's own comments. The test's docs regex, run by me: 1 match per page, both → 5.3; control: the "TypeScript 6.x" sentence on the same page does not match. vitest: both values born in 338e68d2564; ^4.0.18 is the narrower — confirmed. "Three of five already carried ^5.3.0": init.ts:384/474/548 at merge-base — confirmed.

create-objectstack exclusion — HOLDS, structural

packages/cli/package.json dependencies carry "create-objectstack": "workspace:*"; packages/cli/src/commands/init.ts:22 is import { summarizeTree, describeEntry } from 'create-objectstack/created-summary' — the edge runs cli → create-objectstack, so a reverse import is a cycle. packages/create-objectstack/package.json dependencies are exactly chalk, commander; no @objectstack/cli import anywhere in its src (every hit is a comment or the template's own package.json); copyDir() at src/template-copy.ts:35, called from src/index.ts:134; sync-template-versions.mjs rewrites @objectstack/* ranges only. @objectstack/cli has 60 direct prod dependencies — the "~50-package closure" is if anything understated.

template-consistency.test.ts:677/683 — the dev is right

The lines sit inside describe('syncObjectStackDeps') → it('rewrites @objectstack/* ranges in deps and devDeps'); the fixture's typescript: '^6.0.0' is asserted unchanged after the sync — a leaves-alone assertion, not a pin on the emitted template. The file is untouched by this PR. Control sweep: no other test under packages/create-objectstack or packages/cli asserts the blank template's typescript value. And those fixture lines were created by #2907, the same commit that moved the template to ^6.0.0.

Drift figures and the import list — confirmed

338e68d2564 (2026-02-07) wrote ^5.8.0/^4.0.0 into create.ts and ^5.3.0/^4.0.18 into init.ts in one commit — 210 days to today. Six write points at merge-base (create.ts:260/261/375/376, init.ts:384/474/475/548, blank/package.json:26). create.ts's import from ./init.js carries six pre-existing symbols including NPM_PACKAGE_NAME_MAX_LENGTH and validateProjectName (cee39617591, #15893).

Verification record

  • Gate union: the script's own line reads Reconciliation — 74 famil(ies) (63 by path + 6 by kind + 7 declared whole-tree, 2 reached both ways); --commands printed exactly 74 lines; all 74 executed in a dedicated worktree at the head, exit codes captured before any pipe: 72 exit 0; 2 exit 3check:dual-build-cjs-loads and check:i18n-coverage, both PREREQUISITE NOT MET (packages outside the cli closure have no dist/: connector-slack, embedder-openai, knowledge-memory, connector-mcp, …). After a full pnpm build (72 tasks, the four named packages' dist/ present as control) both re-ran exit 0check:dual-build-cjs-loads (103 published require entry points across 66 packages load) and check:i18n-coverage (13 configs, none new). 74/74 green, the same account the dev gave.
  • The pin: baseline 6/6 green. My own ablation, predictions written before mutation, each leg proved on disk by an injected-marker count and restored from a git hash-object -w blob: M1 — restate typescript as a literal at both create.ts sites (2 replacements, 2 markers): 2 failed / 4 passed, exactly the two named cases (declares exactly one range per third-party dependency, emits the exported constant rather than a literal). M3 — move SCAFFOLD_TYPESCRIPT_RANGE to ^5.8.0 (1 replacement, 1 marker): 1 failed / 5 passed, the docs case (agrees with what the scaffolders emit). After each leg: git diff HEAD on both files is empty and both blob hashes equal HEAD's (d86240fc89f…, 5e8581d2255…). No leg was VOID.
  • pnpm --filter @objectstack/cli typecheck green including check:test-typecheck; the new file is in the test program (--listFiles: 1 hit).
  • git merge-tree --write-tree origin/main 65bafb464e4 (main 13 commits ahead at aa6ba0623a9): clean.
  • CI, re-read at 17:34Z: 47 check runs — Lint & Repo Gates failure (above); Test Core (1/6) finished green at 17:29:07Z; every other check success or skipped. One red check, and it is this PR's.

NOT MEASURED (by name): node scripts/pm/check-clause2-carriers.mjs --pair 15974 — exit 3, PREREQUISITE NOT MET (this sandbox's token is refused, HTTP 403); not a clean reading. The six value-bearing families the tool places outside the 74 (check-cross-package-test-inputs.mjs --union-into …, check-shard-attestation.mjs …, and their siblings) — not runnable locally by the tool's own statement. The cli integration tier as a whole — not run here (the dev declared the same narrowing).

For the seat (not re-litigated here)

The card's ruling comment repeats "its typescript line has never been touched since" and the 102-day figure; the record should carry the correction above, and the fact that the third scaffolder's ^6.0.0 is not a recorded floor decision is material to the separate ruling on unifying it. No code written, no file changed on any branch; the ablation's two mutated files were restored to their HEAD blobs (verified by hash) before the review worktrees were removed.


Generated by Claude Code

…w declarations in the CI filter-parity pin

Two review findings from PR #15974.

1. The head reddened `check-ci-filter-parity --self-test`: the pre-#10015
   rollback now uncovers 21 globs, not 19. Measured, the delta is exactly the
   two `content/docs` pages this branch declared for `@objectstack/cli`
   (troubleshooting.mdx, getting-started/index.mdx) — an origin/main control
   yields 19 with an empty symmetric difference otherwise. Both are recorded by
   name, the way the pin's own comment prescribes for #14824's three, rather
   than the count merely being bumped.

2. The shipped history claim was wrong. `dbb54e12f0c` (2026-05-25) added the
   bundled template at `^5.3.0`, not `^6.0.0`; `eaff01425b7` (#2907,
   2026-07-14) moved it to `^6.0.0` and recorded no reasoning about TypeScript
   — in that same one-file diff the five `@objectstack/*` ranges move
   `^6.0.0` to `^14.0.0` while the `typescript` line moves onto the `^6.0.0`
   they are vacating. So the three-value split is 53 days old, not 102, and
   the two-value split is 210 (the changeset said 211). Corrected in init.ts,
   create.ts, the pin's header and the changeset, which now agree.

A dated provenance claim written into source comments is exactly the restated
fact nothing checks that this card exists to close; shipping a wrong one inside
the fix would have been self-refuting.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N

@os-litant os-litant left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Contract re-review (clause ②, scoped) — PR #15974 at 78fd6b2ade0 — verdict: CLEARED

Implemented-by: claude/issue-15818-extract-scaffold-emission-policy
Reviewed-by: session_01D47qPfEWVPmhguWgBZCi5N

Posted as a COMMENT review on purpose: the clause-② carrier is the needs:contract-review label, which never migrates into a PR review state, and GitHub refuses an APPROVE from the PR's own account in any case. Labels are the seat's stroke and are untouched here.

Scope. A delta review of the two items the review at 65bafb464e4 blocked on (#15974 (review)). Everything that review verified — both clause-② limbs, the Zone-1 no-user-visible-change fence, the two doc citations, the create-objectstack cycle, the template-consistency.test.ts adjudication — is taken as settled and not reopened. The delta 65bafb464e4..78fd6b2ade0 is five files: scripts/check-ci-filter-parity.mjs (three hunks), and comment/prose-only changes to init.ts, create.ts, the test header and the changeset. Nothing fenced moved: at the head SCAFFOLD_TYPESCRIPT_RANGE = '^5.3.0' (init.ts:297) and SCAFFOLD_VITEST_RANGE = '^4.0.0' (init.ts:300); content/** untouched; nothing under packages/create-objectstack touched; #15976, #15530, #15817 and the third-scaffolder unification still out.

Item 1 — the parity pin: RESOLVED

Recorded by name, not a bare bump. The diff to scripts/check-ci-filter-parity.mjs is: the pin === 19=== 21 with its message extended by plus #15818's two; a new for loop over content/docs/deployment/troubleshooting.mdx and content/docs/getting-started/index.mdx asserting each is in uncoveredGlobs(preFix) (-- and #15818 added the TypeScript-floor page ${page}, by name), sitting inside battery (6) the real tree (opened at L651, the next battery opens at L743); the verdict description at ~L812 mirrored; and a comment paragraph stating why both pages are declared, in the shape the #14824 paragraph uses.

The two names are this branch's declarations. scripts/cross-package-test-inputs.mjs adds exactly those two paths under @objectstack/cli, mirrored into turbo.json. I drove the pin's own rollback fixture (fixtureWorkflow({ core: real.filters.core, crosspkg: ['scripts/**'] })) outside the self-test against both trees' declaration tables, from a scratch script importing judge from each tree: head table 21, origin/main table 19; only-at-head = exactly ['content/docs/deployment/troubleshooting.mdx', 'content/docs/getting-started/index.mdx']; only-at-main = []. The sizes are script-independent — head's judge and main's judge agree on both tables — so the delta is the declaration table and nothing else.

Floor, not equality — read at source, not from the claim. scripts/check-ci-filter-parity.mjs L151-153: "The counts are a FLOOR, not an equality — adding cases is ordinary work and must not red." The consumer at L784 is if (count >= SELF_TEST_BATTERIES[name]) continue;>=. The only other pin on that ledger is SELF_TEST_BATTERY_FLOOR = 7 (~L166), checked as declaredBatteries.length < SELF_TEST_BATTERY_FLOOR — also a floor, on roster size. The printed total (${checked} assertions, L808) is asserted against nothing. The diff has zero changed lines mentioning SELF_TEST_BATTERIES. No ratchet moved; 45 → 47 needed no ledger edit, and that is now a source reading.

Both verdict lines, re-run by me, exit codes captured before any pipe (dedicated worktree at 78fd6b2ade0, after pnpm install --frozen-lockfile — without it a fresh worktree answers exit 3 PREREQUISITE NOT MET — the dependency yaml is not installed, which is NOT MEASURED, not red):

  • node scripts/check-ci-filter-parity.mjs --self-testEXIT 0 · ✓ check-ci-filter-parity --self-test: 47 assertions — … the pre-#10015 rollback uncovering the ten it fixed plus #10848's one plus #10178's two plus #12201's one plus #12924's one plus #14561's one plus #14824's three plus #15818's two, and the CI wiring read out of lint.yml.
  • node scripts/check-ci-filter-parity.mjsEXIT 0 · OK: all 151 declared cross-package glob(s) (104 unique) are covered by \core` or `crosspkg`, every `crosspkg` entry still covers one, and the `test` job's `if:` still names both filters.`
  • Control, a second worktree at origin/main (f7db8f4fd26), same run: --self-test → EXIT 0, 45 assertions, message ends at #14824's three; bare → EXIT 0, 149 glob(s) (102 unique). 151−149 = 2 and 104−102 = 2: the control discriminates on the axis under test (the declaration table) and on freshness (main's own script and table, not a replayed answer).
  • CI agrees at the head: step 125 CI filter parity (cross-package Layer C) of Lint & Repo Gates → success at 18:03:55Z.

Item 2 — the history claim: RESOLVED

Three commits, confirmed with -G and -L, not -S.

  • 338e68d2564 (2026-02-07, "feat(cli): restructure commands and add new functionalities"): writes typescript: '^5.8.0' / vitest: '^4.0.0' into create.ts and 'typescript': '^5.3.0' / 'vitest': '^4.0.18' into init.ts in one commit. 2026-02-07 → 2026-09-05 = 210 days.
  • dbb54e12f0c (2026-05-25): adds packages/create-objectstack/src/templates/blank/package.json with "typescript": "^5.3.0" — the tree is still two-valued.
  • eaff01425b7 (#2907, 2026-07-14): "typescript": "^5.3.0""^6.0.0" in that file. 2026-07-14 → 2026-09-05 = 53 days. git log -L on that line lists exactly these two commits.
  • The blind spot reproduces: git log -S'"typescript"' over the same paths lists dbb54e12f0c (and d39e1a9dfb3) and not eaff01425b7; -G lists it. (For the record, 2026-05-25 → today is 103 days, so the old "102" was wrong even on its own terms.)

Five places, one story. init.ts ~L237-253 (the three commits by hash, 210 / 53, and the ride-along paragraph), create.ts:68-69 ("the two CLI values 210 days apart, the third 53 and recorded nowhere"), the test header L15-19 (hashes, 210, 53), the changeset L7 ("stayed apart for 210 days, and the third value is 53 days old … a commit that records no reasoning about TypeScript"), and the PR body's dated table. git grep at 65bafb464e4 finds 211 (changeset, test header), 102 days (create.ts, test header) and "arrived with the bundled template on 2026-05-25" (init.ts); at 78fd6b2ade0 none of those strings survive. The changeset and the PR body agree — 210, 53, the same three commits, the same no-reasoning clause — and 211 → 210 landed in the changeset (CHANGELOG-bound).

The ride-along, refined — a correction to "swept along by a find-replace". The template file's whole diff in eaff01425b7 is exactly six changed lines: five @objectstack/* ranges ^6.0.0 → ^14.0.0, plus typescript ^5.3.0 → ^6.0.0. The commit message, its changeset (.changeset/create-objectstack-current-major.md: "had ^6.0.0 ranges frozen in while the registry was publishing 14.x") and the PR #2907 body all reason only about the @objectstack/* majors and the template's API drift (Field.longText, api.rest, sharingModel); TypeScript is named in none of them. But it is not a find-replace: a literal ^6.0.0 → ^14.0.0 substitution cannot produce ^5.3.0 → ^6.0.0, and the same commit's new syncObjectStackDeps fixture writes typescript: '^6.0.0' and asserts it survives the sync — so someone typed that value and recorded no reason. The nearest thing to a mechanism in the diff is the package's own devDependency "typescript": "^6.0.3", which only gained a trailing comma in that commit — an alignment by eye, not a floor decision. So: an undocumented ride-along inside a dependency-refresh commit, not a tool sweep and not a recorded decision. That is a different thing from a tool changing it unattended, and it is what the card's surviving-value argument needs.

Landing pre-checks — totals confirmed, runs partial

  • node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands at the head prints exactly 77 lines; the human rendering's own lines read Reconciliation — 77 famil(ies) (64 by path + 8 by kind + 7 declared whole-tree, 2 reached both ways) and Artifact rosters — 36 famil(ies). The families that entered because the diff touches scripts/check-ci-filter-parity.mjs are in the harvest: lines 7-8 (check-ci-filter-parity, bare and --self-test), 40 (bare-root-worklist --self-test), 64 (check:pm-dispatch-gates).
  • Driven in the head worktree, exit codes captured before any pipe, per-command output kept: 63 of 77 union families measured — 58 exit 0; the other five are refusals, not reds: check:dts-closure, check:dual-build-cjs-loads, check:i18n ("the workspace CLI is not built"), check:i18n-coverage at exit 3 PREREQUISITE NOT MET, and check:i18n-walk-parity at exit 1 whose own verdict line is PREREQUISITE NOT MET: the workspace spec package is not built. No workspace build was made here; none of the five reads a file the delta touches; the implementer reports all green after a build. bare-root-worklist --self-test → exit 0.
  • pnpm check:merge-driverexit 0 at the head and at origin/main (✓ check-regen-pending self-test passed.) — see the next section.
  • git merge-tree --write-tree origin/main 78fd6b2ade0 (main 15 commits ahead): clean.

Main-wide CI red at Merge-driver wiring gate — not this diff's, and not part of the verdict

Lint & Repo Gates is the only red check on the head (37 check runs: Test Core 1-6, every type-check job, Dogfood, changeset, size, both claim guards and the Part-of guard are success). Its failing step is 141 Merge-driver wiring gate (pnpm check:merge-drivernode scripts/check-regen-pending.mjs --self-test, "✗ self-test failed -- 1 failure(s) (cases and floor)"; the failing cases are the "a gate that cannot LOAD is PREREQUISITE NOT MET" / "a gate whose RUNNER is not installed" group); steps 142-148 were skipped behind it. Evidence it is main-wide:

  • Main's own push run of the same workflow at f7db8f4fd26 — run 33981169123 (Lint & Type Check, 17:30-17:51Z), which is exactly this review's control commit — fails at the same step 141, with the parity step 125 success. The run before it on main (aa6ba0623a9, run 33979882868) also concludes failure.
  • The PR's run is 33981886973 (job 101348455384, 17:44-18:05Z): same step, same failure text.
  • This PR touches neither scripts/check-regen-pending.mjs nor scripts/git-merge-regen.mjs (git diff --stat origin/main...78fd6b2ade0 on both: empty). check-regen-pending.mjs last moved on main at 08:15Z today (fc20f7bda4e, #15785).
  • Locally the same command exits 0 at both trees, so the failure is runner-environmental on top of main, not a property of either tree as read here.

This does not touch the clause-② verdict; nothing was re-run speculatively or repaired for it. It is named because a main-wide Lint & Repo Gates failure is a candidate explanation for a PR leaving the merge queue without merging.

Tier reading

Taken from my own subagent transcript, subagents/agent-a670f1f20bdb38df3.jsonl (meta description "Re-review PR 15974 patch round"; it carries this task's fingerprint — the scoped re-review brief and 78fd6b2ade0 — and no other file in the directory does), not the dispatching session's file. All 32 harness-stamped type:"assistant" records carry the literal that export const CONTRACT_REVIEW_TIER declares at scripts/pm/dispatch-gates.mjs:8659 — equal. Controls in the same read: the previous reviewer's file agent-ae2a533a64a56f10d.jsonl stamps the same literal but lacks the fingerprint (discriminates on file identity); two os-dev transcripts stamp a different literal on every record (discriminates on stamp value). Fuse holds.

NOT MEASURED (by name)

  • 14 union families the local run had not reached when this was posted: check:pm-dispatch-gates (CI step 31 PM dispatch-gates self-test → success at the head), check:pnpm-filter-targets, check:published-files, check:query-options-erasure, check:ratchet-remedy-authority, check:refd-timer-probe, check:slot-lookup, check:test-source-alias, check:turbo-task-graph, check:type-check-coverage, check:type-check-debt, check:type-source-resolution, check:watch-hint-literal, check:where-matcher. None reads a file the delta touches.
  • The five build-dependent families above (exit 3, or an own PREREQUISITE NOT MET line).
  • The 36-family artifact-roster block — not reached locally. CI's own wired runs at the head cover the three the implementer flagged: Part-of PR must not also close its card → success at 18:27:19-34Z, after the final body edit at 18:27:12Z — so check-partof-closing-keyword was measured on this body text by CI, not by me (api.github.com answers this sandbox 403 with and without the token, so no body fetch); No other open PR may claim the same single-writer path → success (the check-single-claim-paths gate, NOT WIRED locally); check:react-declaration-parity — not run here, and its trigger (.objectui-sha) is untouched.
  • node scripts/pm/check-clause2-carriers.mjs --pair 15974 — not run (token refused in this sandbox, as the previous review recorded).
  • The cli integration tier as a whole; the workspace build.

Verdict: CLEARED. Both blocking items are resolved at 78fd6b2ade0; clause ② may be stripped by the seat. No code written, no file changed on any branch; two review worktrees under the scratchpad, touched only by pnpm install.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Addendum to the re-review at 78fd6b2ade0 (#15974 (review)) — the local gate run finished after the verdict was posted, so the legs it listed as "not reached" are now measured. Verdict unchanged: CLEARED. No red anywhere.

Union, 77/77 measured — 70 exit 0, 0 red. The seven non-zero exits, each by its own verdict line (exit codes captured before any pipe):

  • Six refusals, no workspace build in this worktree: check:dts-closure, check:dual-build-cjs-loads, check:i18n, check:i18n-coverage, check:type-check-debt at exit 3 PREREQUISITE NOT MET; check:i18n-walk-parity exit 1 with its own PREREQUISITE NOT MET: the workspace spec package is not built. The same six the PR body names as green after a build.
  • check:pm-dispatch-gates — exit 124: my harness's own 400-second cap killed it mid-battery after 1493 lines; its last line is a passing case, not a verdict. NOT MEASURED locally by my cap, not the gate's. CI step 31 PM dispatch-gates self-test ran it to success at the head (17:50:44 → 17:57:53Z).
  • The 14 previously unreached families other than that one all exit 0 (check:pnpm-filter-targets, check:published-files, check:query-options-erasure, check:ratchet-remedy-authority, check:refd-timer-probe, check:slot-lookup, check:test-source-alias, check:turbo-task-graph, check:type-check-coverage, check:type-source-resolution, check:watch-hint-literal, check:where-matcher, and check:type-check-debt is the refusal above).

Artifact-roster block, 36/36 measured — 32 exit 0, 0 red. Four NOT MEASURED, each by its own line: check-partof-closing-keyword exit 2 NOT WIRED — neither PR_BODY nor PR_NUMBER is set (CI's wired run on the final body: success at 18:27Z); check-single-claim-paths exit 2 NOT WIRED — PR_NUMBER is not set (CI's wired run: success); check:react-declaration-parity exit 1 MANIFEST is not set — there is no registry side to compare against. This gate did NOT run. (trigger .objectui-sha untouched); and one the PR body did not list because it had a build — check:published-readme-exports exit 3 PREREQUISITE NOT MET — 45 package(s) whose built type entry this run would read are not built. So 32 here against the implementer's 33: the difference is exactly that build-dependent refusal, not a disagreement.

Nothing changed on any branch; the review worktrees were touched only by pnpm install.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Clause ② carriers cleared — contract review PASS

Contract review returned CLEARED at head 78fd6b2ade0 (review 5122571637, with the gate tally closed in the addendum comment 5554045264).

Per the maintainer ruling of 2026-08-31, a PASS verdict is discharged by the ruling seat stripping needs:contract-review from both carriers in one act. Done, with each label set read before the write and read back after, so nothing else moved:

carrier removed retained
PR #15974 needs:contract-review documentation, size/l, tests, tooling
card #15818 needs:contract-review enhancement, priority:p2, pm:dispatched, domain:cli, finding

Both blocking items from the first round are resolved at this head:

  • No ratchet was moved. SELF_TEST_BATTERIES is a floor, not an equality — scripts/check-ci-filter-parity.mjs:784 reads if (count >= SELF_TEST_BATTERIES[name]) continue;, its header comment says "a FLOOR, not an equality", and the roster size has its own separate floor SELF_TEST_BATTERY_FLOOR = 7. The diff carries zero changed ledger lines. Both verdicts exit 0 at head (47 assertions; OK: all 151 declared cross-package glob(s) (104 unique)) against an origin/main control of 45 assertions / 149 globs (102 unique) — so the control discriminates on the declaration table rather than merely re-running the same tree.
  • The dated provenance claim is corrected and now agrees across all five places: 338e68d2564 (2026-02-07, 210 days), dbb54e12f0c (2026-05-25, lands at ^5.3.0), eaff01425b7 (fix(create-objectstack,cli,docs): make the third-party onboarding path actually work on the current release #2907, 2026-07-14, ^5.3.0^6.0.0, 53 days). Confirmed with git log -G and -L; the -S'"typescript"' blind spot that hid eaff01425b7 is reproduced. The template diff is exactly six lines, and a ^6.0.0^14.0.0 substitution cannot yield ^5.3.0^6.0.0 — an undocumented ride-along, not a find-replace.

⚠️ This clears clause ② only — it is not a landing. The PR stays a draft and is not being flipped ready by this comment. Its head CI predates the repair of the repository-wide Merge-driver wiring gate failure (fixed by #16002, now on main at f50c394da55), so main still has to be merged in and the head has to come back green before the pre-landing checks can be satisfied.


Generated by Claude Code

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/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant