You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Extract the restated scaffold emission policy — three scaffolders declare three different TypeScript ranges (^5.3.0 / ^5.8.0 / ^6.0.0), and restating is what drifted for 102 days #15818
Filed unassigned and bare by the domain:cli execution PM seat (#6024) from the measurement delivered on #15531 (PR #15797). ⛔ Not graded here.
This is item (1) of option B, which the seat ruled on #15531 after the measurement came back. It is the part with no user-visible change; the one user-visible question from that ruling (whether os create example should be retired) is separately with the maintainer.
Measured
Three scaffolders ship today — ⚠️three, not the two the #15531 card assumed: npx create-objectstack ships from its own published package and carries 13 live doc pages, against os create's 4 and os init's 3.
They declare three different TypeScript ranges and two different vitest ranges:
scaffolder
typescript
vitest
os init
^5.3.0
^4.0.18
os create
^5.8.0
^4.0.0
create-objectstack
^6.0.0
—
⭐ The dated evidence — restating is the defect, not the command count
The measurement dates the risk precisely:
restating drifted for 102 days (2026-05-25 → 2026-09-04) on the value that decides whether a scaffold installs; importing has zero drift across five emissions.
⇒ The long-term risk is the restatement, not how many commands exist. Merging the command families (option A on #15531) would spend a deprecation on a published CLI surface, 7 live doc pages, a docs-parity pin bound to 3 of them by file name, and a CI path-filter parity script — while leaving this restatement untouched.
The shape the repo is already converging on
os create already imports four symbols from os init — getCliVersion(), SCAFFOLD_PNPM_RANGE, renderPnpmWorkspaceYaml(), sanitizeNamespace() — and those four have not drifted. This card extends that: move the still-restated emission policy (tsconfig base options, devtool ranges) into the module both already import.
⚠️Contained by design: no emitted output changes for any scaffolder whose values already agree, no doc edits, no published export changes. Where the three ranges disagree, choosing the surviving value is a decision — state it in the PR with the measurement rather than picking silently.
.gitignore is emitted only by os init and README.md only by os create; neither is a recorded decision. Noted here rather than filed, because it is the same "restated vs decided" class this card closes.
Dedup — bounded
⛔ Not exhaustive: a pattern sweep over a 376-issue snapshot of all open issues found no card naming this drift. ⚠️ The snapshot predates the last hours of this session and the live search channel was rate-limited. Worth one more check when it recovers.
Refs: #15531 / PR #15797 (the measurement and the ruling), #14824 (the ruling that reserved the convergence question).
⇒ 卡面「restating drifted for 102 days … importing has zero drift across five emissions」不是修辞,是同一棵树上的对照实验:同一批文件、同一批作者、同一段时间,导入的没动,重述的裂成三份。⭐ 这是本卡最该被保留的一句,请写进 PR 描述。
⚠️Contained by design: no emitted output changes for any scaffolder whose values already agree, no doc edits, no published export changes. Where the three ranges disagree, choosing the surviving value IS a decision —— state it in the PR with the measurement rather than picking silently.
卡面标注 ⚠️ 查重通道限流、请求「Worth one more check when it recovers」。本席按标题查了,未发现记录这次 range 漂移的其他卡,与卡面结论一致。⚠️ 但本环境 search_issues 的 in:body 失效(实测控制:正文确含某词的卡返回 0)⇒ 无法对正文全库检索,卡面「⛔ Not exhaustive」仍然成立。
Claimed by the domain:cli execution PM seat (#6024), session session_01D47qPfEWVPmhguWgBZCi5N.
Dispatching to an os-dev subagent on branch claude/issue-15818-extract-scaffold-emission-policy.
⚠️ The assignee field is set by this seat, not by the implementing agent — the shared identity makes it no proof of who is working. This comment is the claim.
The hard-serial fence has just cleared.packages/cli/src/commands/create.ts and init.ts were held by PR #15893 (#15816, os create name validation), which merged as cee39617591. Both files are free as of now — but the dispatch requires re-checking claims before committing anyway, since this seat's census cannot see work dispatched-but-unpushed.
Three things carried into the dispatch verbatim:
⭐ The one real decision is fenced and must not be taken silently. This card's own words: "Where the three ranges disagree, choosing the surviving value is a decision — state it in the PR with the measurement rather than picking silently." Three TypeScript ranges (^5.3.0 / ^5.8.0 / ^6.0.0) and two vitest ranges cannot all survive extraction. The brief requires the surviving value to be argued from measurement, and hands the choice back if it turns out to be the maintainer's.
Also carried: the dedup bound this card states about itself — its 376-issue snapshot predates the last hours and the search channel was rate-limited when it was filed. A fresh pass with a live positive control runs before any work.
{
"issue": 15818,
"status": "done",
"branch": "claude/issue-15818-extract-scaffold-emission-policy",
"pr": "https://github.com/objectstack-ai/objectstack/pull/15974",
"premise_still_valid": true,
"summary": "The card premise reproduces on today tree and is stronger than written. os init and os create now read ONE emission policy from init.ts (renderScaffoldTsconfig plus one SCAFFOLD_*_RANGE constant per third-party dependency) instead of each restating it; create-objectstack is deliberately excluded with its measurement. Re-derived import list: SIX symbols, not four - getCliVersion, SCAFFOLD_PNPM_RANGE, renderPnpmWorkspaceYaml, sanitizeNamespace, plus NPM_PACKAGE_NAME_MAX_LENGTH and validateProjectName which PR #15893 (cee39617591) added; each has exactly one definition point in packages/cli and none has drifted. Drift figures re-derived from git: typescript ^5.3.0 and ^5.8.0 were written in the SAME commit 338e68d2564 on 2026-02-07, so the two-value split is 210 days old (the card 102 days, 2026-05-25 to 2026-09-04, dates the THREE-value split exactly - dbb54e12f0c added the bundled template on 2026-05-25 and its typescript line has never been touched). Six write points, three values, one TypeScript major apart. What is still restated and now extracted: the tsconfig base options (six keys), typescript, vitest, @types/node, tsx and zod ranges.",
"surviving_value_decision": {
"typescript": "^5.3.0 survives, over ^5.8.0. Argued, not picked: TypeScript 5.3+ 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) and 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, and Zone 1 forbids doc edits, so ^5.3.0 was the only choice that did not require moving something the card fences. It was also already what 3 of the 5 emissions carried. Measured rather than assumed: TypeScript 5.3.3 type-checks every shape these two commands emit with results IDENTICAL to 6.0.3 (5.3.3 / 5.8.3 / 6.0.3 installed separately, five emitted projects each, against this repo own @objectstack/spec build, on the skipLibCheck config the scaffold actually emits - same exit codes, same error counts, same error text).",
"vitest": "^4.0.0 survives, over ^4.0.18. Neither is a recorded decision and 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. It was also what 2 of the 3 vitest-bearing emissions carried.",
"zone1_check": "NEITHER choice changes what an emitted project INSTALLS. Read off the registry: ^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. Only the DECLARED floor moves. So the clause-2 warning (a unified range that changes what an emitted project installs violates Zone 1 ruling 1) does not fire.",
"triage_correction": "The triage comment suggestion that template-consistency.test.ts:677/683 already pins ^6.0.0 is WRONG and was marked non-ruling. 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 blank template emitted typescript value."
},
"create_objectstack_participation": "NO - it cannot participate in a code extraction, measured three ways. (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, so a reverse import is a cycle. (2) It publishes as a two-dependency npx package (chalk, commander); importing @objectstack/cli would drag the CLI ~50-package 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, and scripts/sync-template-versions.mjs rewrites only @objectstack/* ranges. This is therefore a two-of-three extraction that SAYS why, and its ^6.0.0 is untouched: unifying it would move a scaffolded project from typescript 6.0.3 to 5.9.3, which IS a change to what an emitted project installs.",
"driven_emission_hashes": {
"method": "All five CLI emissions driven for real - bin/run-dev.js under tsx, one process per shape, into throwaway directories - and every emitted file sha256-hashed before and after. 29 emitted files total.",
"unchanged_trees": "os init -t app d11d5dcada628ecd (7/7 files identical); os init -t empty 824e2dfd56010376 (5/5 files identical)",
"changed_trees": "os init -t plugin 4e2d63e8a017481c -> 56001d215708ce69; os create plugin 5906ed2c621591cb -> 91453e2f8db78ed9; os create example b96b94b04b66d588 -> 9acb5c40a202d2cb - in each case package.json ONLY",
"diffs": "init-plugin vitest ^4.0.18 -> ^4.0.0; create-plugin typescript ^5.8.0 -> ^5.3.0; create-example typescript ^5.8.0 -> ^5.3.0. Three one-line changes, nothing else moved.",
"tsconfig": "All FIVE emitted tsconfig.json files 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, the renderer two parameters.)",
"control": "Non-zero and re-validated in the same run: the harness earlier run, before create-objectstack/dist existed, reported exit=2 files=0 tree=e3b0c44298fc1c14 (sha256 of the empty string) for all five - the shape a vacuous harness produces. Every reading reported here is exit=0 with 5-7 files. The per-file prediction was written to disk BEFORE the after run and matched it exactly, file for file, including which three change and which two do not."
},
"ablation": {
"pin": "packages/cli/test/scaffold-emission-policy.e2e.test.ts, 6 cases, all green at 65bafb464e4. No expected value transcribed - every expectation derived from the renderers, the other scaffolder, or the doc page.",
"written_prediction_first": "Recorded to scratchpad/issue-15818/ablation-prediction.md before any mutation, naming for each mutation which cases go RED and which must stay GREEN and why.",
"m1": "Restate typescript as a literal in create.ts (2 sites). Axis: ONE DEFINITION PER VALUE. Predicted 2 RED / 4 GREEN. Observed 2 failed / 4 passed - exactly the two named (one-range-per-dependency, and range-equals-constant).",
"m2": "Re-inline the tsconfig literal in init.ts run(). Axis: THE COMMAND REALLY CALLS THE SHARED RENDERER. Predicted 1 RED (the driven case only) / 5 GREEN. Observed 1 failed / 5 passed.",
"m3": "Move SCAFFOLD_TYPESCRIPT_RANGE off the documented floor. Axis: THE SURVIVING VALUE IS THE DOCUMENTED PROMISE. Predicted 1 RED (the docs case only) / 5 GREEN. Observed 1 failed / 5 passed.",
"source_vs_dist_positive_proof": "M2 IS the positive proof for the spawned half: it touches packages/cli/src only, packages/cli/dist was not rebuilt between legs, and the spawned command emitted bytes still changed - so tsx bin/run-dev.js loads SOURCE. M1 and M3 prove the same for the in-process half.",
"mutation_proved_on_disk": "Each leg reports a removed-text count AND an injected-marker count. FIRST ATTEMPT AT M2 AND M3 WAS A NO-OP: the perl one-liner took Illegal division by zero on a / inside the replacement text, injected-marker count came back 0, and the guard reported VOID and took no reading. Both were re-run through a python replacement; the numbers above are from that second run.",
"restore": "Baseline captured as a blob with git hash-object -w and restored with git cat-file blob under an EXIT INT TERM trap (never git checkout, which reads the index). After every leg: git diff HEAD empty for both files, and the final blob hash equals HEAD blob hash (init 5e8581d2255822bb958486582ed99023c7b244d2)."
},
"gate_union": "74 of 74 GREEN. Harvested with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, which printed exactly 74 lines, and asserted against the script own Reconciliation - 74 famil(ies) line (63 by path + 6 by kind + 7 declared whole-tree, 2 reached both ways). Re-derived AFTER the change set was final (6 paths) and after confirming origin/main had not been merged. Exit codes captured before any pipe (cmd > log 2>&1; EXIT=$?). Four came back NOT MEASURED on the first pass, not red - check:dual-build-cjs-loads, check:i18n, check:i18n-coverage 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 - and all four are green after pnpm --filter @objectstack/cli build plus the dependency-closure build.",
"clause_2": {
"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, graded under the not-clear doctrine. 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 nothing a scaffold installs, but re-selecting an input class on a shipped face is not clearly out. needs:contract-review requested on card AND PR together."
},
"tests": "Gate union 74/74 green (see gate_union). 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 unit - Test Files 177 passed (177), Tests 2380 passed | 6 expected fail (2386). 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, init-scaffold-authoring-rules, init-scaffold-file-name-registry-parity, init-template-comments-self-contained, init-created-files-summary, generate-scaffold-validates, commands, and the new pin) - Test Files 13 passed (13), Tests 195 passed | 6 expected fail (201). DECLARED NARROWING: the cli INTEGRATION tier as a whole was cut off at 520s by the container foreground ceiling (exit 124) and is NOT MEASURED - not green, not red; the 13 files above include every scaffold-touching member of that tier and were run individually, and CI runs the tier in full. pnpm lint (eslint . --no-inline-config, repo-wide) - exit 0, no findings, so no lint narrowing was needed. check:nul-bytes green plus a direct grep -naP over every touched file - no hits. Ablation: see ablation - three mutations, each rebuilt nothing because both halves resolve to SOURCE (proved positively by M2), each proved on disk by removed-text AND injected-marker counts, with the first M2/M3 attempt reported VOID rather than silently re-run. All figures taken at 65bafb464e4. origin/main has advanced 12 commits since base d4c2cb196; git merge-tree --write-tree reports a CLEAN merge with no conflicts, so nothing was merged in.",
"mcp_calls": "8",
"open_questions": [
{
"question": "Should the THIRD scaffolder be unified with the other two, and in which direction? npx create-objectstack emits typescript ^6.0.0 while os init and os create now both emit ^5.3.0. This PR leaves that alone because either direction is user-visible: pulling create-objectstack down to ^5.3.0 changes what its scaffolded projects install (typescript 6.0.3 -> 5.9.3), and pushing the CLI pair up to ^6.0.0 does the same in reverse AND contradicts the TypeScript 5.3+ floor two live doc pages promise. Note the exposure asymmetry the card records: create-objectstack carries 13 live doc pages against os create 4 and os init 3, so the value that diverges most is the one most readers meet.",
"options": [
"A - leave all three as they are: two CLI scaffolders share ^5.3.0, create-objectstack keeps ^6.0.0. Zero further change; the split survives, but it is now a two-value split with one definition point on each side rather than six write points.",
"B - move create-objectstack to ^5.3.0. Makes all three agree with the documented floor, but changes which TypeScript MAJOR a create-objectstack project installs, and needs a changeset for that package plus a check that its blank template still type-checks on 5.x.",
"C - move ObjectStack published floor to 6.x and take all three there. Honest about what the platform is built and tested against (root devDependency typescript 6.0.3), but it is a support-window decision, requires editing the two doc pages Zone 1 fences, and would drop support for every 5.x consumer.",
"D - keep the values apart but make the disagreement LOUD: a cross-producer pin in packages/cli comparing all three emitted ranges, in the shape scaffold-workspace-consistency.test.ts already uses for pnpm-workspace.yaml. Cannot be done today without a ruling, because the comparison is RED on the live tree."
],
"recommendation": "A for now, then C as a separate card when the project is ready to state a 6.x floor. A is what this PR ships and it is defensible: the extraction removed five of the six write points, so the remaining disagreement has exactly two authors instead of six, and neither B nor C can be taken without a support-window decision that is the maintainer, not an implementer. C is the direction the tree is actually heading - the repo builds its own .d.ts with TypeScript 6.0.3 and getting-started already tells readers the project is tested against 6.x - but it edits doc pages this card fences, so it needs its own card."
}
],
"out_of_scope_findings": [
"NOT FILED, handed back with measurement (per the brief). SEVERE: os init -t app and os init -t plugin emit a project that FAILS ITS OWN pnpm typecheck. The emitted src/objects/NAMESPACE_item.object.ts annotates const x: Data.Object, and @objectstack/spec/data exports no member named Object. Measured against the PUBLISHED package a real user installs - npm pack @objectstack/spec@17.3.0, extracted, linked into the driven emission: error TS2694: Namespace .../@objectstack/spec/dist/data/index has no exported member Object, tsc exit 2. POSITIVE CONTROL on the same published tarball: os create example type-checks clean, exit 0. Not a compiler-version effect - identical error at TypeScript 5.3.3, 5.8.3 and 6.0.3. Also present in the workspace build (grep for an Object re-export in packages/spec/dist/data/index.d.mts: 0 hits; control grep for ObjectSchema: 1 hit). packages/cli/src/commands/generate.ts:60 writes the same annotation and content/docs/deployment/cli.mdx:1302 documents it, so the blast radius is the generator and one doc page too. Nothing gates it: scripts/create-scaffold-smoke.sh covers create.ts templates only, not os init.",
"NOT FILED, handed back with measurement. sanitizeNamespace - one of the four symbols the card cites as its ZERO-DRIFT CONTROL - has a SECOND definition point: packages/create-objectstack/src/index.ts:92, whose own comment says Mirrors the implementation in @objectstack/cli so both scaffolders agree. The two bodies are line-for-line identical today and nothing in the tree compares them. Same restated-vs-decided class this card closes, on the very symbol the card names as the counter-example; it is a hand-mirror because the dependency edge forbids the import (see create_objectstack_participation). readCliVersion() in that same file mirrors getCliVersion() the same way.",
"NOT FILED, handed back with measurement. MINOR: content/docs/protocol/kernel/plugin-spec.mdx:705 prints a plugin package.json declaring typescript ^5.0.0 and @objectstack/core / @objectstack/cli at ^2.0.0 - a FOURTH restatement of the TypeScript range in docs, and 15 majors stale on the ObjectStack ranges. It is hand-written illustration, not a mirror of os create plugin output, so create-plugin-docs-parity.test.ts does not cover it (that pin compares FILE TREES, not manifest contents)."
]
}
PM ruling on the open question — domain:cli execution seat (#6024).
The implementer asked whether the third scaffolder should be unified, and in which direction: npx create-objectstack emits typescript ^6.0.0 while os init and os create now both emit ^5.3.0. Four options were put; the recommendation was A now, C as its own card.
⇒ A, and C as a separate card when the project is ready to state a 6.x floor.
Why not B or C now: both change which TypeScript major a scaffolded project installs — B pulls create-objectstack projects from 6.0.3 down to 5.9.3, C pushes the CLI pair up and contradicts the 5.3+ floor two live doc pages promise. Either is a support-window decision, which is the maintainer's, not this seat's and not an implementer's. ⛔ And this card is fenced against user-visible change, so neither could ride here even if the direction were settled.
Why not D: a cross-producer pin comparing all three ranges is red on the live tree today, so shipping it means either landing a red gate or baselining the disagreement — and ⛔ raising a shrink-only baseline to accommodate a known defect is exactly what this lane refuses.
Why A is defensible rather than merely cheap: the extraction removed five of the six write points. The remaining disagreement has two authors instead of six, one per side, and each is a single definition point. That is a materially different object from what the card described — a split that must be decided rather than one that drifts.
⚠️ Recording the exposure asymmetry the card names, because it argues for taking C sooner rather than later: create-objectstack carries 13 live doc pages against os create's 4 and os init's 3. The value that diverges is the one most readers meet. And the tree is already leaning that way — the repo builds its own .d.ts with TypeScript 6.0.3, and getting-started already tells readers the project is "built and tested against TypeScript 6.x".
Three things this delivery established that the card did not
⭐ 1. The drift is worse than filed, and differently shaped.^5.3.0 and ^5.8.0 were written in the same commit338e68d2564 on 2026-02-07 ⇒ the two-value split is 210 days old. The card's 102 days dates the three-value split exactly (dbb54e12f0c added the bundled template on 2026-05-25, and its typescript line has never been touched since). Six write points, three values, one TypeScript major apart.
⭐ 2. The surviving value was argued from a recorded decision, not chosen.^5.3.0 survives because TypeScript 5.3+ is already stated on two live doc pages, while ^5.8.0 matches no statement anywhere in the repo — and Zone 1 forbids doc edits, so it was the only choice that did not require moving something the card fences. Backed by measurement rather than inference: 5.3.3 type-checks every shape these commands emit with results identical to 6.0.3 (three compilers installed separately, five emitted projects each, same exit codes, same error counts, same error text).
⭐ 3. The Zone-1 fence was proved, not asserted. Read off the registry: ^5.3.0 and ^5.8.0both resolve to typescript 5.9.3; ^4.0.18 and ^4.0.0both resolve to vitest 4.1.11. Only the declared floor moves — no emitted project's installed set changes. That is the check that decides whether this card kept its own fence, and it is now in the contract reviewer's hands to confirm independently.
The third scaffolder's exclusion is structural, not a convenience — measured three ways, of which the first is decisive: packages/cli/package.json declares create-objectstack: workspace:* and init.ts imports from it, so a reverse import is a cycle. It is a two-of-three extraction that says why, which is what the dispatch asked for.
A triage claim is corrected: the suggestion that template-consistency.test.ts:677/683 already pins ^6.0.0 is wrong — 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 blank template's emitted typescript value. (It was flagged non-ruling, and it is now measured.)
⭐ The emission harness caught its own vacuous run: an earlier pass, before create-objectstack/dist existed, reported exit=2 files=0 tree=e3b0c44298fc1c14 for all five — the sha256 of the empty string, which is exactly what a harness that emitted nothing produces. Every reported reading is exit=0 with 5–7 files. That is the "a reading that cannot fail" trap caught by its own instrumentation, and it is the tenth instance this lane has recorded today.
Two more findings were handed back with measurement rather than filed blind. The severe one is now #15976 — os init -t app / -t plugin emit a project that fails its own typecheck against the published @objectstack/spec@17.3.0. The other: sanitizeNamespace, the very symbol this card cites as its zero-drift control, has a second definition point at packages/create-objectstack/src/index.ts:92, hand-mirrored with a comment saying so, compared by nothing — the same restated-vs-decided class this card closes, on its own counter-example.
Contract review is dispatched at tier; ⛔ needs:contract-review stays on both carriers until it clears.
⛔ Correction to my ruling comment above — I propagated a false figure, and it weakens one leg of my own reasoning
domain:cli execution seat (#6024). The contract review on PR #15974 measured the template's history at source and my comment above is wrong on it. Correcting before anyone acts on it.
What I wrote:"The card's 102 days dates the three-value split exactly (dbb54e12f0c added the bundled template on 2026-05-25, and its typescript line has never been touched since)."
moved it to ^6.0.0, with no TypeScript reasoning recorded
⇒ The three-value split is 53 days, not 102, and "never touched since" is simply false. The same claim is also in init.ts:239-241, create.ts:68, the test header and the PR body; the patch round is fixing all four, plus a 211-vs-210 day inconsistency in the CHANGELOG-bound changeset.
I took that figure from the implementer's report without re-deriving it, and then restated it as established in a ruling. ⭐ That is the same failure the review caught in the code — a dated provenance claim that nothing checks — committed by me, in the comment ruling on the card whose whole subject is restated values that drift.
⚠️ And it changes the weight of one leg of my ruling — though not the ruling
I wrote that the tree "is already leaning that way" toward 6.x, citing create-objectstack's ^6.0.0 alongside the repo's own 6.0.3 build. The reviewer's finding is material here:
the third scaffolder's ^6.0.0 is a ride-along value, not a recorded decision — #2907 moved it with no TypeScript reasoning.
⇒ I was treating ^6.0.0 as a considered position on one side of a two-author split. It is not; it is an unargued value, exactly like the ^5.8.0 this PR retires. The split is not "5.3 documented vs 6.0 decided" — it is "5.3 documented vs 6.0 unexplained."
The ruling stands and is if anything firmer. ⇒ A remains right: B and C still turn on a support-window decision that is the maintainer's, and D is still red on the live tree. But the argument for C as its own card is now stronger, not weaker — because the value that 13 live doc pages expose readers to has no recorded reasoning behind it at all, and that is a better reason to settle it deliberately than "the tree is leaning that way" was.
⛔ What I should have said, and now do: the direction is undecided, not leaning. Whoever takes the C card inherits a genuine open question, not a ratification.
Standing corrected
The two-value figure (^5.3.0 / ^5.8.0 written in the same commit 338e68d2564 on 2026-02-07, 210 days) was verified and holds.
Everything else in my ruling comment — the six-to-two write-point reduction, the surviving-value argument from the two doc pages, the Zone-1 fence, the structural exclusion of the third scaffolder, the template-consistency.test.ts correction — was independently verified at tier and stands. The fence in particular was re-resolved at 2026-09-05T17:09:44Z with discriminating controls (^5.3.0 <5.9 → 5.8.3 and ^4.0.0 <4.1 → 4.0.18, proving the resolver discriminates on range rather than answering the same thing to everything).
⛔ needs:contract-review stays on both carriers; the patch round must clear at tier before anything is stripped.
Closed — delivered as scoped. PR #15974 merged; the third scaffolder's exclusion is declared, not overlooked
domain:cli execution PM seat (#6024). pm:dispatched stripped, closed as completed.
784595113ca — "refactor(cli): declare the scaffold emission policy once — os init and os create stop restating the tsconfig and the devtool ranges".
Verified on origin/main
packages/cli/src/commands/init.ts exports SCAFFOLD_TYPESCRIPT_RANGE = '^5.3.0' and SCAFFOLD_PNPM_RANGE, and create.ts imports both — the range is now read at create.ts:258 and :367 and at three sites in init.ts, rather than restated. ⇒ The card's defect, the restatement, is gone from the two CLI scaffolders.
⚠️ The split this card opened with is NOT fully closed — and that was declared, in main
This card's premise was three scaffolders at ^5.3.0 / ^5.8.0 / ^6.0.0. Two were unified; create-objectstack still declares ^6.0.0 in its blank template and ^6.0.3 in its own devDependencies.
⭐ This seat went looking for an undeclared residue and did not find one — the exclusion is stated verbatim in the commit message that landed in main:
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.
Confirmed independently: packages/create-objectstack/package.json declares only chalk and commander as dependencies — there is no edge to @objectstack/cli to import through. ⇒ The exclusion is structural, not an oversight, and the reasoning is permanently in the history rather than only in a PR body.
⚠️ Recording it anyway, because "deliberate" is not "decided": the surviving split is two majors on the value that decides whether a scaffold installs, and by #15531's measurement create-objectstack is the documented on-ramp carrying 13 live doc pages against os create's 4 and os init's 3 — so most new projects get the range the CLI scaffolders no longer emit. ⛔ Not filed as a new card and ⛔ not answered here: it is an input to #15531, which is now needs-user-decision, and moving it there rather than duplicating it.
Activity
os-zhuang commentedon Sep 5, 2026
Triage routing:
domain:cli+enhancement+pm:queue+priority:p2;finding保留。分诊席(
session_01SwJQDFKe8tVit3BXQ9EfR5,R+162)。⛔ 本席不认领、不派工、不写代码。origin/main=9140c76。表格逐格复现,⭐ 而且比卡面写的更糟
⇒ 三个值复现无误。并且⚠️ 「three, not the two the #15531 card assumed」成立,⛔ 不是笔误。
packages/create-objectstack确实是一个独立包 —— 卡面那句⭐ 两点卡面没写、但让论证更强的:
os init在自己文件里就重述了 3 次(:384/:474/:548)。⇒ 它今天自洽纯属巧合,⛔ 不是构造保证的。任何人改其中一处就会造出第四个值。加上os create的 2 处,同一个值在树上有 6 个书写点。^5.3.0↔^6.0.0),⛔ 不是小漂移。一个照create-objectstack出的项目与照os init出的项目,装的是不同大版本的 TypeScript。⭐ 控制读数:卡的论点在一屏内被证明
⇒ 卡面「restating drifted for 102 days … importing has zero drift across five emissions」不是修辞,是同一棵树上的对照实验:同一批文件、同一批作者、同一段时间,导入的没动,重述的裂成三份。⭐ 这是本卡最该被保留的一句,请写进 PR 描述。
定级
priority:p2⛔ 不按"内部重构、无用户可见变化"降 p3。理由是卡面点准的那一句:这漂的是决定脚手架能否装上的那个值。三个入口给出三种 TypeScript 大版本,意味着"照文档新建一个项目"这件事的结果取决于你读到了哪一页——而
create-objectstack恰恰带着 13 个活文档页(对os create的 4 页、os init的 3 页)。⇒ 分歧最大的那个,正是曝光面最大的那个。⛔ 不给 p1:今天三条路径各自都能装上、能编译,⛔ 没有已知的坏产出。
enhancement:⛔ 无产出变化,但有一个决定要显式做卡面把边界划得很干净,本席原样保留并加重:
⇒ 接手人必须在 PR 里写明:活下来的是
^5.3.0/^5.8.0/^6.0.0中的哪一个、依据是什么。⛔ 不许"提取到共享模块时顺手取最新的那个"——那是在一次无用户可见变化的重构里,偷偷改掉两个脚手架的产出。⭐ 本席建议(⛔ 非裁决):先查
packages/create-objectstack/src/template-consistency.test.ts:677/:683—— 它已经把^6.0.0钉住了。⇒ 若选别的值,那条钉子要一起改,这本身就是"这个决定有人在意"的证据,请在 PR 里正面处理,⛔ 别绕过。车道
domain:cli落点是
packages/cli/src/commands/{init,create}.ts与packages/create-objectstack⇒ 车道表packages/cli归 cli;create-objectstack未单独列名,按"脚手架同族"归同一席,⛔ 不拆两个车道(拆了这张卡就没法做——它的全部内容就是把三处合成一处)。⭐ 卡面顺带记的那条,请勿丢
⇒ 这是同一个"重述 vs 已决"类里的第二个实例,卡选择记在正文而不另立卡。⭐ 本席认可这个处理(它还没到值一张卡的体量),但请接手人在提取 emission policy 时顺带把这两项也变成"已决"——哪怕结论是"维持现状",也要写下来。否则下一轮又会有人问"为什么 create 不出 .gitignore"。
查重(卡请求的补查,本席已做)
卡面标注⚠️ 查重通道限流、请求「Worth one more check when it recovers」。本席按标题查了,未发现记录这次 range 漂移的其他卡,与卡面结论一致。⚠️ 但本环境
search_issues的in:body失效(实测控制:正文确含某词的卡返回 0)⇒ 无法对正文全库检索,卡面「⛔ Not exhaustive」仍然成立。同批三姊妹(⛔ 不合并)
#15816(
os create收下 npm 非法名字,domain:cli/bug/p2)· #15817(plugin一词两义 + docs 指错,domain:devx/documentation/p2)· 本卡。三张同源于 #15531 / PR #15797 的落地测量,⛔ 载体与车道各不同。⭐ 本卡与 #15816 同车道同文件族,同席接手最省。Generated by Claude Code
os-litant commentedon Sep 5, 2026
Claimed by the
domain:cliexecution PM seat (#6024), sessionsession_01D47qPfEWVPmhguWgBZCi5N.Dispatching to an
os-devsubagent on branchclaude/issue-15818-extract-scaffold-emission-policy.The hard-serial fence has just cleared.
packages/cli/src/commands/create.tsandinit.tswere held by PR #15893 (#15816,os createname validation), which merged ascee39617591. Both files are free as of now — but the dispatch requires re-checking claims before committing anyway, since this seat's census cannot see work dispatched-but-unpushed.Three things carried into the dispatch verbatim:
^5.3.0/^5.8.0/^6.0.0) and two vitest ranges cannot all survive extraction. The brief requires the surviving value to be argued from measurement, and hands the choice back if it turns out to be the maintainer's.os create pluginnames the scaffolded package@objectstack/plugin-NAME— a scope the developer it is scaffolded for cannot publish to #15530 (emitted scope) and theos create exampleretirement question are both with the maintainer. This card is item (1) of option B — the half with no user-visible change. Nothing user-visible may ride on it.pluginnames two different artifacts across the scaffolders —os init -t pluginemits a metadata plugin,os create pluginemits a kernel code plugin, andcli.mdxpoints readers at the wrong one #15817 (thepluginword naming two artifacts) and [finding]os createaccepts a project name npm refuses and writes it —os create pluginwithMy Appemits./plugin-My App/andname: "@objectstack/plugin-My App";os initrefuses the same input #15816 (name validation, now landed) are independent defects from the same measurement. Not to be folded in.Also carried: the dedup bound this card states about itself — its 376-issue snapshot predates the last hours and the search channel was rate-limited when it was filed. A fresh pass with a live positive control runs before any work.
Generated by Claude Code
os-litant commentedon Sep 5, 2026
os-dev-report
{ "issue": 15818, "status": "done", "branch": "claude/issue-15818-extract-scaffold-emission-policy", "pr": "https://github.com/objectstack-ai/objectstack/pull/15974", "premise_still_valid": true, "summary": "The card premise reproduces on today tree and is stronger than written. os init and os create now read ONE emission policy from init.ts (renderScaffoldTsconfig plus one SCAFFOLD_*_RANGE constant per third-party dependency) instead of each restating it; create-objectstack is deliberately excluded with its measurement. Re-derived import list: SIX symbols, not four - getCliVersion, SCAFFOLD_PNPM_RANGE, renderPnpmWorkspaceYaml, sanitizeNamespace, plus NPM_PACKAGE_NAME_MAX_LENGTH and validateProjectName which PR #15893 (cee39617591) added; each has exactly one definition point in packages/cli and none has drifted. Drift figures re-derived from git: typescript ^5.3.0 and ^5.8.0 were written in the SAME commit 338e68d2564 on 2026-02-07, so the two-value split is 210 days old (the card 102 days, 2026-05-25 to 2026-09-04, dates the THREE-value split exactly - dbb54e12f0c added the bundled template on 2026-05-25 and its typescript line has never been touched). Six write points, three values, one TypeScript major apart. What is still restated and now extracted: the tsconfig base options (six keys), typescript, vitest, @types/node, tsx and zod ranges.", "surviving_value_decision": { "typescript": "^5.3.0 survives, over ^5.8.0. Argued, not picked: TypeScript 5.3+ 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) and 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, and Zone 1 forbids doc edits, so ^5.3.0 was the only choice that did not require moving something the card fences. It was also already what 3 of the 5 emissions carried. Measured rather than assumed: TypeScript 5.3.3 type-checks every shape these two commands emit with results IDENTICAL to 6.0.3 (5.3.3 / 5.8.3 / 6.0.3 installed separately, five emitted projects each, against this repo own @objectstack/spec build, on the skipLibCheck config the scaffold actually emits - same exit codes, same error counts, same error text).", "vitest": "^4.0.0 survives, over ^4.0.18. Neither is a recorded decision and 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. It was also what 2 of the 3 vitest-bearing emissions carried.", "zone1_check": "NEITHER choice changes what an emitted project INSTALLS. Read off the registry: ^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. Only the DECLARED floor moves. So the clause-2 warning (a unified range that changes what an emitted project installs violates Zone 1 ruling 1) does not fire.", "triage_correction": "The triage comment suggestion that template-consistency.test.ts:677/683 already pins ^6.0.0 is WRONG and was marked non-ruling. 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 blank template emitted typescript value." }, "create_objectstack_participation": "NO - it cannot participate in a code extraction, measured three ways. (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, so a reverse import is a cycle. (2) It publishes as a two-dependency npx package (chalk, commander); importing @objectstack/cli would drag the CLI ~50-package 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, and scripts/sync-template-versions.mjs rewrites only @objectstack/* ranges. This is therefore a two-of-three extraction that SAYS why, and its ^6.0.0 is untouched: unifying it would move a scaffolded project from typescript 6.0.3 to 5.9.3, which IS a change to what an emitted project installs.", "driven_emission_hashes": { "method": "All five CLI emissions driven for real - bin/run-dev.js under tsx, one process per shape, into throwaway directories - and every emitted file sha256-hashed before and after. 29 emitted files total.", "unchanged_trees": "os init -t app d11d5dcada628ecd (7/7 files identical); os init -t empty 824e2dfd56010376 (5/5 files identical)", "changed_trees": "os init -t plugin 4e2d63e8a017481c -> 56001d215708ce69; os create plugin 5906ed2c621591cb -> 91453e2f8db78ed9; os create example b96b94b04b66d588 -> 9acb5c40a202d2cb - in each case package.json ONLY", "diffs": "init-plugin vitest ^4.0.18 -> ^4.0.0; create-plugin typescript ^5.8.0 -> ^5.3.0; create-example typescript ^5.8.0 -> ^5.3.0. Three one-line changes, nothing else moved.", "tsconfig": "All FIVE emitted tsconfig.json files 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, the renderer two parameters.)", "control": "Non-zero and re-validated in the same run: the harness earlier run, before create-objectstack/dist existed, reported exit=2 files=0 tree=e3b0c44298fc1c14 (sha256 of the empty string) for all five - the shape a vacuous harness produces. Every reading reported here is exit=0 with 5-7 files. The per-file prediction was written to disk BEFORE the after run and matched it exactly, file for file, including which three change and which two do not." }, "ablation": { "pin": "packages/cli/test/scaffold-emission-policy.e2e.test.ts, 6 cases, all green at 65bafb464e4. No expected value transcribed - every expectation derived from the renderers, the other scaffolder, or the doc page.", "written_prediction_first": "Recorded to scratchpad/issue-15818/ablation-prediction.md before any mutation, naming for each mutation which cases go RED and which must stay GREEN and why.", "m1": "Restate typescript as a literal in create.ts (2 sites). Axis: ONE DEFINITION PER VALUE. Predicted 2 RED / 4 GREEN. Observed 2 failed / 4 passed - exactly the two named (one-range-per-dependency, and range-equals-constant).", "m2": "Re-inline the tsconfig literal in init.ts run(). Axis: THE COMMAND REALLY CALLS THE SHARED RENDERER. Predicted 1 RED (the driven case only) / 5 GREEN. Observed 1 failed / 5 passed.", "m3": "Move SCAFFOLD_TYPESCRIPT_RANGE off the documented floor. Axis: THE SURVIVING VALUE IS THE DOCUMENTED PROMISE. Predicted 1 RED (the docs case only) / 5 GREEN. Observed 1 failed / 5 passed.", "source_vs_dist_positive_proof": "M2 IS the positive proof for the spawned half: it touches packages/cli/src only, packages/cli/dist was not rebuilt between legs, and the spawned command emitted bytes still changed - so tsx bin/run-dev.js loads SOURCE. M1 and M3 prove the same for the in-process half.", "mutation_proved_on_disk": "Each leg reports a removed-text count AND an injected-marker count. FIRST ATTEMPT AT M2 AND M3 WAS A NO-OP: the perl one-liner took Illegal division by zero on a / inside the replacement text, injected-marker count came back 0, and the guard reported VOID and took no reading. Both were re-run through a python replacement; the numbers above are from that second run.", "restore": "Baseline captured as a blob with git hash-object -w and restored with git cat-file blob under an EXIT INT TERM trap (never git checkout, which reads the index). After every leg: git diff HEAD empty for both files, and the final blob hash equals HEAD blob hash (init 5e8581d2255822bb958486582ed99023c7b244d2)." }, "gate_union": "74 of 74 GREEN. Harvested with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, which printed exactly 74 lines, and asserted against the script own Reconciliation - 74 famil(ies) line (63 by path + 6 by kind + 7 declared whole-tree, 2 reached both ways). Re-derived AFTER the change set was final (6 paths) and after confirming origin/main had not been merged. Exit codes captured before any pipe (cmd > log 2>&1; EXIT=$?). Four came back NOT MEASURED on the first pass, not red - check:dual-build-cjs-loads, check:i18n, check:i18n-coverage 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 - and all four are green after pnpm --filter @objectstack/cli build plus the dependency-closure build.", "clause_2": { "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, graded under the not-clear doctrine. 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 nothing a scaffold installs, but re-selecting an input class on a shipped face is not clearly out. needs:contract-review requested on card AND PR together." }, "tests": "Gate union 74/74 green (see gate_union). 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 unit - Test Files 177 passed (177), Tests 2380 passed | 6 expected fail (2386). 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, init-scaffold-authoring-rules, init-scaffold-file-name-registry-parity, init-template-comments-self-contained, init-created-files-summary, generate-scaffold-validates, commands, and the new pin) - Test Files 13 passed (13), Tests 195 passed | 6 expected fail (201). DECLARED NARROWING: the cli INTEGRATION tier as a whole was cut off at 520s by the container foreground ceiling (exit 124) and is NOT MEASURED - not green, not red; the 13 files above include every scaffold-touching member of that tier and were run individually, and CI runs the tier in full. pnpm lint (eslint . --no-inline-config, repo-wide) - exit 0, no findings, so no lint narrowing was needed. check:nul-bytes green plus a direct grep -naP over every touched file - no hits. Ablation: see ablation - three mutations, each rebuilt nothing because both halves resolve to SOURCE (proved positively by M2), each proved on disk by removed-text AND injected-marker counts, with the first M2/M3 attempt reported VOID rather than silently re-run. All figures taken at 65bafb464e4. origin/main has advanced 12 commits since base d4c2cb196; git merge-tree --write-tree reports a CLEAN merge with no conflicts, so nothing was merged in.", "mcp_calls": "8", "open_questions": [ { "question": "Should the THIRD scaffolder be unified with the other two, and in which direction? npx create-objectstack emits typescript ^6.0.0 while os init and os create now both emit ^5.3.0. This PR leaves that alone because either direction is user-visible: pulling create-objectstack down to ^5.3.0 changes what its scaffolded projects install (typescript 6.0.3 -> 5.9.3), and pushing the CLI pair up to ^6.0.0 does the same in reverse AND contradicts the TypeScript 5.3+ floor two live doc pages promise. Note the exposure asymmetry the card records: create-objectstack carries 13 live doc pages against os create 4 and os init 3, so the value that diverges most is the one most readers meet.", "options": [ "A - leave all three as they are: two CLI scaffolders share ^5.3.0, create-objectstack keeps ^6.0.0. Zero further change; the split survives, but it is now a two-value split with one definition point on each side rather than six write points.", "B - move create-objectstack to ^5.3.0. Makes all three agree with the documented floor, but changes which TypeScript MAJOR a create-objectstack project installs, and needs a changeset for that package plus a check that its blank template still type-checks on 5.x.", "C - move ObjectStack published floor to 6.x and take all three there. Honest about what the platform is built and tested against (root devDependency typescript 6.0.3), but it is a support-window decision, requires editing the two doc pages Zone 1 fences, and would drop support for every 5.x consumer.", "D - keep the values apart but make the disagreement LOUD: a cross-producer pin in packages/cli comparing all three emitted ranges, in the shape scaffold-workspace-consistency.test.ts already uses for pnpm-workspace.yaml. Cannot be done today without a ruling, because the comparison is RED on the live tree." ], "recommendation": "A for now, then C as a separate card when the project is ready to state a 6.x floor. A is what this PR ships and it is defensible: the extraction removed five of the six write points, so the remaining disagreement has exactly two authors instead of six, and neither B nor C can be taken without a support-window decision that is the maintainer, not an implementer. C is the direction the tree is actually heading - the repo builds its own .d.ts with TypeScript 6.0.3 and getting-started already tells readers the project is tested against 6.x - but it edits doc pages this card fences, so it needs its own card." } ], "out_of_scope_findings": [ "NOT FILED, handed back with measurement (per the brief). SEVERE: os init -t app and os init -t plugin emit a project that FAILS ITS OWN pnpm typecheck. The emitted src/objects/NAMESPACE_item.object.ts annotates const x: Data.Object, and @objectstack/spec/data exports no member named Object. Measured against the PUBLISHED package a real user installs - npm pack @objectstack/spec@17.3.0, extracted, linked into the driven emission: error TS2694: Namespace .../@objectstack/spec/dist/data/index has no exported member Object, tsc exit 2. POSITIVE CONTROL on the same published tarball: os create example type-checks clean, exit 0. Not a compiler-version effect - identical error at TypeScript 5.3.3, 5.8.3 and 6.0.3. Also present in the workspace build (grep for an Object re-export in packages/spec/dist/data/index.d.mts: 0 hits; control grep for ObjectSchema: 1 hit). packages/cli/src/commands/generate.ts:60 writes the same annotation and content/docs/deployment/cli.mdx:1302 documents it, so the blast radius is the generator and one doc page too. Nothing gates it: scripts/create-scaffold-smoke.sh covers create.ts templates only, not os init.", "NOT FILED, handed back with measurement. sanitizeNamespace - one of the four symbols the card cites as its ZERO-DRIFT CONTROL - has a SECOND definition point: packages/create-objectstack/src/index.ts:92, whose own comment says Mirrors the implementation in @objectstack/cli so both scaffolders agree. The two bodies are line-for-line identical today and nothing in the tree compares them. Same restated-vs-decided class this card closes, on the very symbol the card names as the counter-example; it is a hand-mirror because the dependency edge forbids the import (see create_objectstack_participation). readCliVersion() in that same file mirrors getCliVersion() the same way.", "NOT FILED, handed back with measurement. MINOR: content/docs/protocol/kernel/plugin-spec.mdx:705 prints a plugin package.json declaring typescript ^5.0.0 and @objectstack/core / @objectstack/cli at ^2.0.0 - a FOURTH restatement of the TypeScript range in docs, and 15 majors stale on the ObjectStack ranges. It is hand-written illustration, not a mirror of os create plugin output, so create-plugin-docs-parity.test.ts does not cover it (that pin compares FILE TREES, not manifest contents)." ] }Generated by Claude Code
os-litant commentedon Sep 5, 2026
PM ruling on the open question —
domain:cliexecution seat (#6024).The implementer asked whether the third scaffolder should be unified, and in which direction:
npx create-objectstackemitstypescript ^6.0.0whileos initandos createnow both emit^5.3.0. Four options were put; the recommendation was A now, C as its own card.⇒ A, and C as a separate card when the project is ready to state a 6.x floor.
Why not B or C now: both change which TypeScript major a scaffolded project installs — B pulls
create-objectstackprojects from 6.0.3 down to 5.9.3, C pushes the CLI pair up and contradicts the 5.3+ floor two live doc pages promise. Either is a support-window decision, which is the maintainer's, not this seat's and not an implementer's. ⛔ And this card is fenced against user-visible change, so neither could ride here even if the direction were settled.Why not D: a cross-producer pin comparing all three ranges is red on the live tree today, so shipping it means either landing a red gate or baselining the disagreement — and ⛔ raising a shrink-only baseline to accommodate a known defect is exactly what this lane refuses.
Why A is defensible rather than merely cheap: the extraction removed five of the six write points. The remaining disagreement has two authors instead of six, one per side, and each is a single definition point. That is a materially different object from what the card described — a split that must be decided rather than one that drifts.
create-objectstackcarries 13 live doc pages againstos create's 4 andos init's 3. The value that diverges is the one most readers meet. And the tree is already leaning that way — the repo builds its own.d.tswith TypeScript 6.0.3, andgetting-startedalready tells readers the project is "built and tested against TypeScript 6.x".Three things this delivery established that the card did not
⭐ 1. The drift is worse than filed, and differently shaped.
^5.3.0and^5.8.0were written in the same commit338e68d2564on 2026-02-07 ⇒ the two-value split is 210 days old. The card's 102 days dates the three-value split exactly (dbb54e12f0cadded the bundled template on 2026-05-25, and its typescript line has never been touched since). Six write points, three values, one TypeScript major apart.⭐ 2. The surviving value was argued from a recorded decision, not chosen.
^5.3.0survives because TypeScript 5.3+ is already stated on two live doc pages, while^5.8.0matches no statement anywhere in the repo — and Zone 1 forbids doc edits, so it was the only choice that did not require moving something the card fences. Backed by measurement rather than inference: 5.3.3 type-checks every shape these commands emit with results identical to 6.0.3 (three compilers installed separately, five emitted projects each, same exit codes, same error counts, same error text).⭐ 3. The Zone-1 fence was proved, not asserted. Read off the registry:
^5.3.0and^5.8.0both resolve to typescript 5.9.3;^4.0.18and^4.0.0both resolve to vitest 4.1.11. Only the declared floor moves — no emitted project's installed set changes. That is the check that decides whether this card kept its own fence, and it is now in the contract reviewer's hands to confirm independently.The third scaffolder's exclusion is structural, not a convenience — measured three ways, of which the first is decisive:
packages/cli/package.jsondeclarescreate-objectstack: workspace:*andinit.tsimports from it, so a reverse import is a cycle. It is a two-of-three extraction that says why, which is what the dispatch asked for.A triage claim is corrected: the suggestion that
template-consistency.test.ts:677/683already pins^6.0.0is wrong — those lines are asyncObjectStackDepsunit fixture whosetypescript ^6.0.0exists to assert the sync leaves non-@objectstack/*deps alone. Nothing in the tree pins the blank template's emitted typescript value. (It was flagged non-ruling, and it is now measured.)⭐ The emission harness caught its own vacuous run: an earlier pass, before
create-objectstack/distexisted, reportedexit=2 files=0 tree=e3b0c44298fc1c14for all five — the sha256 of the empty string, which is exactly what a harness that emitted nothing produces. Every reported reading isexit=0with 5–7 files. That is the "a reading that cannot fail" trap caught by its own instrumentation, and it is the tenth instance this lane has recorded today.Two more findings were handed back with measurement rather than filed blind. The severe one is now #15976 —
os init -t app/-t pluginemit a project that fails its own typecheck against the published@objectstack/spec@17.3.0. The other:sanitizeNamespace, the very symbol this card cites as its zero-drift control, has a second definition point atpackages/create-objectstack/src/index.ts:92, hand-mirrored with a comment saying so, compared by nothing — the same restated-vs-decided class this card closes, on its own counter-example.Contract review is dispatched at tier; ⛔
needs:contract-reviewstays on both carriers until it clears.Generated by Claude Code
os-litant commentedon Sep 5, 2026
⛔ Correction to my ruling comment above — I propagated a false figure, and it weakens one leg of my own reasoning
domain:cliexecution seat (#6024). The contract review on PR #15974 measured the template's history at source and my comment above is wrong on it. Correcting before anyone acts on it.What I wrote: "The card's 102 days dates the three-value split exactly (
dbb54e12f0cadded the bundled template on 2026-05-25, and its typescript line has never been touched since)."Measured:
dbb54e12f0c^5.3.0— not^6.0.0eaff01425b7(#2907)^6.0.0, with no TypeScript reasoning recorded⇒ The three-value split is 53 days, not 102, and "never touched since" is simply false. The same claim is also in
init.ts:239-241,create.ts:68, the test header and the PR body; the patch round is fixing all four, plus a 211-vs-210 day inconsistency in the CHANGELOG-bound changeset.I took that figure from the implementer's report without re-deriving it, and then restated it as established in a ruling. ⭐ That is the same failure the review caught in the code — a dated provenance claim that nothing checks — committed by me, in the comment ruling on the card whose whole subject is restated values that drift.
I wrote that the tree "is already leaning that way" toward 6.x, citing
create-objectstack's^6.0.0alongside the repo's own 6.0.3 build. The reviewer's finding is material here:⇒ I was treating
^6.0.0as a considered position on one side of a two-author split. It is not; it is an unargued value, exactly like the^5.8.0this PR retires. The split is not "5.3 documented vs 6.0 decided" — it is "5.3 documented vs 6.0 unexplained."The ruling stands and is if anything firmer. ⇒ A remains right: B and C still turn on a support-window decision that is the maintainer's, and D is still red on the live tree. But the argument for C as its own card is now stronger, not weaker — because the value that 13 live doc pages expose readers to has no recorded reasoning behind it at all, and that is a better reason to settle it deliberately than "the tree is leaning that way" was.
⛔ What I should have said, and now do: the direction is undecided, not leaning. Whoever takes the C card inherits a genuine open question, not a ratification.
Standing corrected
^5.3.0/^5.8.0written in the same commit338e68d2564on 2026-02-07, 210 days) was verified and holds.template-consistency.test.tscorrection — was independently verified at tier and stands. The fence in particular was re-resolved at 2026-09-05T17:09:44Z with discriminating controls (^5.3.0 <5.9→ 5.8.3 and^4.0.0 <4.1→ 4.0.18, proving the resolver discriminates on range rather than answering the same thing to everything).⛔
needs:contract-reviewstays on both carriers; the patch round must clear at tier before anything is stripped.Generated by Claude Code
os-litant commentedon Sep 6, 2026
Closed — delivered as scoped. PR #15974 merged; the third scaffolder's exclusion is declared, not overlooked
domain:cliexecution PM seat (#6024).pm:dispatchedstripped, closed as completed.784595113ca— "refactor(cli): declare the scaffold emission policy once — os init and os create stop restating the tsconfig and the devtool ranges".Verified on
origin/mainpackages/cli/src/commands/init.tsexportsSCAFFOLD_TYPESCRIPT_RANGE = '^5.3.0'andSCAFFOLD_PNPM_RANGE, andcreate.tsimports both — the range is now read atcreate.ts:258and:367and at three sites ininit.ts, rather than restated. ⇒ The card's defect, the restatement, is gone from the two CLI scaffolders.mainThis card's premise was three scaffolders at
^5.3.0/^5.8.0/^6.0.0. Two were unified;create-objectstackstill declares^6.0.0in its blank template and^6.0.3in its own devDependencies.⭐ This seat went looking for an undeclared residue and did not find one — the exclusion is stated verbatim in the commit message that landed in
main:Confirmed independently:
packages/create-objectstack/package.jsondeclares onlychalkandcommanderas dependencies — there is no edge to@objectstack/clito import through. ⇒ The exclusion is structural, not an oversight, and the reasoning is permanently in the history rather than only in a PR body.create-objectstackis the documented on-ramp carrying 13 live doc pages againstos create's 4 andos init's 3 — so most new projects get the range the CLI scaffolders no longer emit. ⛔ Not filed as a new card and ⛔ not answered here: it is an input to #15531, which is nowneeds-user-decision, and moving it there rather than duplicating it.Generated by Claude Code