chore(cli): retire SCAFFOLD_TSX_RANGE, a conditional statement nothing satisfies - #16951
Conversation
…g satisfies The `os create example` template was the only emission that ever declared a `tsx` range; once it was retired the constant reached no scaffold at all. Its docblock -- "The `tsx` range a scaffolded project declares when its scripts need it" -- is not a false statement but a conditional whose antecedent nothing on the tree satisfies. Vacuously true, so a review hunting false statements never catches it; what it misleads is the reader's default assumption that some scaffold satisfies the antecedent. The constant and its docblock are removed together. No surviving emission should declare `tsx`: all four (`os init -t app` / `-t plugin` / `-t empty`, `os create plugin`) plus the bundled `create-objectstack` blank template run every script they emit through `objectstack`, `tsc` or `vitest`, none of which executes a `.ts` entrypoint directly. `@objectstack/cli`, which each of them declares, already carries `tsx` as its own dependency. No assertion is added for the retired range. The comment in `test/scaffold-emission-policy.e2e.test.ts` is updated to describe the retirement rather than the intermediate state, keeping its rule intact: a row is owed by a range some emission really declares, and by nothing else. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015QE8qk46e5CHJxyQEUjbf8
📓 Docs Drift CheckThis PR changes 1 package(s): 5 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 22 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 11e8350c72e1486ca8ac41e1fc6b01c40378213c && git checkout 11e8350c72e1486ca8ac41e1fc6b01c40378213c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin f7da71eb7e9c734d21c738e211ceccc2c176d7c9 89a49d0923236385f5bfb0d4c3c8862a5df8b968 && git checkout -B drift-repro f7da71eb7e9c734d21c738e211ceccc2c176d7c9 && git merge --no-ff 89a49d0923236385f5bfb0d4c3c8862a5df8b968
node scripts/docs-audit/affected-docs.mjs --json f7da71eb7e9c734d21c738e211ceccc2c176d7c9
|
PM review — accepted, armingCI, with the latest-per-name collapse stated even though it changes nothing. 34 raw check-run rows; every Clause-② re-run by me, not relayed. The clause-② Removal safety — anchor finds, I judge. The direction answer's load-bearing claim, verified verbatim. The PR argues direction 2 would have been adding a capability rather than restoring one, because What I am accepting on the delivering seat's measurement rather than re-measuring. The Docs drift read correctly. The tool's Marking ready for review, then arming auto-merge — in that order, since a draft conversion after arming destroys both. Generated by Claude Code |
Fixes #16668
Clause-②: no
SCAFFOLD_TSX_RANGEwas a package-internal module export, not a published surface.Re-declared here from the delivered diff rather than from card content: this
package's
exportsmap is././console/./hook-body/./package.jsonwith no subpath pattern,
src/index.tsre-exports only the oclif command classes,and there is no
packages/cli/api-surfacebaseline. Removing the constant relaxesno accept set and withdraws nothing anyone could import. The diff emits no new
scaffold script, so nothing about what the tool produces changes.
The direction question, answered first
No — and the taker is: none. Direction 1, retire the constant.
The reading, re-taken on
origin/main@9a89a0040drather than relayed:Control (the instrument fires):
SCAFFOLD_PNPM_RANGE/SCAFFOLD_TYPESCRIPT_RANGEhit 8 times in that same file. So the two hits above are a zero, not a broken search.
Why no surviving scaffold should declare it, which is the half a grep cannot answer.
tsxexists to execute a.tsentrypoint directly. Reading the scripts every survivingemission actually writes:
os init -t appobjectstack dev·objectstack compile && objectstack serve·objectstack compile·objectstack validate·objectstack lint·tsc --noEmitos init -t pluginobjectstack compile·objectstack validate·objectstack lint·vitest run·tsc --noEmitos init -t emptyobjectstack compile·objectstack validate·objectstack lint·tsc --noEmitos create plugintsc·tsc --watch·vitest·tsc --noEmitcreate-objectstackblank templateobjectstack dev·objectstack start·objectstack build·objectstack validate·objectstack lint·tsc --noEmitNot one of them runs a
.tsfile directly. Every one routes throughobjectstack,tscor
vitest, each of which carries its own TypeScript handling. And the capability is notmissing from a scaffolded project either:
@objectstack/cli— which every emission abovedeclares — lists
tsx: ^4.23.12among its owndependencies. A scaffold re-declaringtsxwould have been announcing a second, lower floor (^4.21.0) for a binary theproject never invokes by name.
That also settles the character of the alternative: direction 2 would have been adding
a capability, not restoring a lost one — so there was no gap to close.
Before / after
Constant and docblock removed together. Not rewritten to "reserved for future use":
that is a promise nobody made.
The reason: removing a conditional statement whose antecedent nothing satisfies
The card called the docblock false. Strictly it is not:
That is a conditional, and its antecedent holds nowhere on the tree — so the sentence
is vacuously true, not false. That is a worse class than a lie, not a lesser one: no
review looking for false statements catches it, because it tells none. What it misleads is
the reader's default assumption that some scaffold satisfies the antecedent. So the fix is
described as removing a conditional statement nothing satisfies, never as "fixing a false
comment".
The
:207-211comment, updated rather than left describing the intermediate stateNo assertion is added for the retired range — PR #16654 already wrote down why, and that
reasoning is kept intact rather than inverted: a row is owed by a range some emission
really declares, and by nothing else. The comment used to describe the halfway state
("the constant is still here but reaches nothing"); it now describes the retirement, and
states the condition under which a
tsxrow would legitimately return.Changeset: measured, not assumed — and
skip-changesetis not defensible herepackages/clibuilds with plaintsc -p tsconfig.build.json, no bundler and notree-shaking, so a package-internal export still reaches the tarball. Measured with a
before/after build of
packages/cliin one run (mutate the two files back to the baserevision, rebuild, snapshot; restore, rebuild, snapshot), hashing every file under the
published
files[]entrydist:Symbol search inside those shipped paths at the base state, with its positive control:
Restore leg proven rather than assumed: both files restored to their
HEADblob hashesbyte-for-byte (
bdc57a5b8c…and9c2201fa1b…),git diff HEADempty, and the rebuiltdistsnapshot identical to the firstaftersnapshot — so the rebuild really re-ran andthe reading is not a cache artifact.
Reading: something inside
files[]moves, so this diff publishes. Apatchchangesetfor
@objectstack/cliis added. It ispatchand not breaking because theexportsmapnever exposed the symbol — nothing importable is withdrawn.
Docs drift: re-derived from a clean worktree, plus a hand sweep with a live control
scripts/docs-audit/affected-docs.mjs --json, re-derived on a clean tree:The tool's number is not zero, and reading it correctly matters: all six pages are
reached through the
os initcommand anchor — they mention the command whose file thisdiff touches. The
SCAFFOLD_TSX_RANGEsymbol anchor matched zero pages.Because the tool's zero on the symbol is not a clean bill,
content/was swept by hand forthis change's tokens:
Live positive control for the blind spot named on the card — a page stating a rule by
its inputs, sharing no identifier with the emitter:
Two hits. That is exactly the shape the tool cannot see and the sweep can, so the sweep is
live rather than silently empty. Reading: no
content/page states or implies that ascaffolded project declares
tsx. No docs change is owed.Verification
Exit codes captured before any pipe, throughout.
node scripts/pm/dispatch-gates.mjs --commandsderived 59; all59 were run and reconciled with
--ran:✓ 59 derived famil(ies) accounted for — 59 run, 0 NOT-MEASURED, 0 UNRUN.Re-derived at the final commit: the family list is byte-identical.
PREREQUISITE NOT MET(exit 3) and are recordedas NOT MEASURED, never as red:
check:dual-build-cjs-loads,check:i18n-coverageandcheck:type-check-debteach refuse to measure until a whole-repopnpm buildexists(12 packages, among them
@objectstack/studioand the connectors, have nodist). CIbuilds that closure on a fresh checkout. Each gate says so in its own verdict text.
pnpm check:scaffold-emission-policy— exit 0. This is the family the diff touchesmost directly:
scripts/sync-scaffold-emission-policy.mjsreadsSCAFFOLD_*constantsout of
init.ts, and itsPOLICY_STAMPStable names onlySCAFFOLD_PNPM_RANGEandSCAFFOLD_TYPESCRIPT_RANGE, so the retired constant had no stamp row to remove.*.e2e.test.ts) and anintegration-tier file by behaviour, so the default population excludes it; it was run
explicitly:
OS_TEST_TIERS=nightly vitest run test/scaffold-emission-policy.e2e.test.ts→
Test Files 1 passed (1) · Tests 10 passed (10). Plus the package's unit tier:vitest run --project unit→Test Files 189 passed (189) · Tests 2612 passed | 6 expected fail.tierSignalsover the comment-masked source readschildProcess, entryBasename, tsxBinand
isIntegration = trueat both the base revision and this commit, identically.pnpm --filter @objectstack/cli typecheck— exit 0,tsc --noEmitpluscheck:test-typecheck, both clean. (A first run exited 2 with 33TS7016errors naming@objectstack/driver-sqland@objectstack/plugin-hono-server; that is the shared-turbo-cachestale-artifact class AGENTS.md §9 describes — six packages had
dist/*.jsbut nodist/*.d.ts. Zero of those 33 errors namedinit.ts,SCAFFOLD_or the test file. Aforced rebuild of those six cleared all 33.)
eslint . --no-inline-config --format json,exit 0: 6383 files linted, 0 errors, 0 warnings, with both touched files present in
the run at 0/0. No narrowing argument is needed because nothing was narrowed.
All readings above were taken on the tree of the final commit
89a49d0923; the gate sweepran against a byte-identical tree (both resolve to tree
ac7db785fa).验收备注
Out-of-scope observations, noted and deliberately not filed — none of them is a
reproducible defect, a declared-contract violation, or a metadata-authoring trap, which
are the only three filing categories here:
@objectstack/clideclarestsx: ^4.23.12while the retiredconstant said
^4.21.0. The two floors never had to agree — one was the CLI's owndependency, the other a range a scaffold would have declared — and with the constant gone
there is only one number left. Successor: none; nothing reads the removed value.
scripts/sync-scaffold-emission-policy.mjsstamps templates from anexplicit
POLICY_STAMPSallowlist, soSCAFFOLD_VITEST_RANGE,SCAFFOLD_TYPES_NODE_RANGEand
SCAFFOLD_ZOD_RANGEreach the bundledcreate-objectstacktemplate through nogenerator either. Unlike
tsxthese three are declared by live emissions, so they areconsumed and this is an observation about the stamping table's coverage, not a dead
constant. Successor: whoever next extends
POLICY_STAMPS.allSixManifests()will name five once #16665 lands — a stale identifier arriving by INTERACTION between two PRs, which neither author's review can see #16677 was left strictly alone: same file family and same retirement round, but adifferent symbol, a different file and a different remedy. [finding]
allSixManifests()will name five once #16665 lands — a stale identifier arriving by INTERACTION between two PRs, which neither author's review can see #16677 remains open, and isdeliberately not addressed here.
Fences honoured:
packages/cli/src/index.tsandpackage.json'sexportsare untouched;content/docs/releases/is untouched; no assertion was added for the retired constant; thediff stays inside the two declared files plus the changeset.
Generated by Claude Code