fix(deps): patch tsx 4.23.15 so oclif registers it on every supported Node, re-arming the reroute probe CONTROL leg - #21356
Conversation
…ves its own hooks module again tsx 4.23.15 moved its ESM-API registration out of a dist/ chunk and into dist/esm/api/index.cjs without rebasing three file-relative specifiers. The module.register() hooks URL now names dist/esm/api/esm/index.mjs, which does not exist. A CommonJS caller of tsx/esm/api therefore gets ERR_MODULE_NOT_FOUND on every Node that lacks tsx's registerHooks path (below 22.22.3 on the 22 line). @oclif/core's ts-path is that caller: it logs "Could not find tsx" and stays on dist/. So the CONTROL leg of published-entry-node-env-source-reroute could not arm on such a Node, and the absence legs beside it passed with the guard they pin deleted. The patch points the three specifiers back at the files 4.23.14 resolved. The lockfile moves no version; only the tsx patch hash is added. Claude-Session: https://claude.ai/code/session_018gA1pE6eJtwHhqx72G8U9X Co-authored-by: Claude <noreply@anthropic.com>
…nds on A CONTROL red whose output is only the version line is a tsx registration failure inside oclif's ts-path, not a fixture or guard defect. The docblock says so and points at the tsx patch note in pnpm-workspace.yaml. Claude-Session: https://claude.ai/code/session_018gA1pE6eJtwHhqx72G8U9X Co-authored-by: Claude <noreply@anthropic.com>
…x-esm-api-register
…n src/ On a Node without tsx's registerHooks path, unpatched tsx 4.23.15 also sends packages/cli/bin/run-dev.js to dist/commands. Measured with --version under DEBUG=oclif:config:ts-path on Node 22.22.0: "Could not find tsx" without the patch, src/commands with it. Claude-Session: https://claude.ai/code/session_018gA1pE6eJtwHhqx72G8U9X Co-authored-by: Claude <noreply@anthropic.com>
…x-esm-api-register
…x-esm-api-register
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
Review: ACCEPT, PR #21356 (head
|
Fixes #21308
Clause-②: no
What this changes
tsx4.23.15 moved its ESM-API registration out of adist/chunk and intodist/esm/api/index.cjs, two directories deeper. It did not rebase three file-relative specifiers. On a Node without tsx'smodule.registerHookspath, the CommonJS build oftsx/esm/apithen callsmodule.register()with a hooks URL that namesdist/esm/api/esm/index.mjs, a file that does not exist.@oclif/core'sts-pathis the caller in this repo. It swallows the error, logsCould not find tsx, and stays ondist/.This PR adds a
pnpm patchoftsx@4.23.15that points the three specifiers back at the files 4.23.14 resolved. No version moves.patches/tsx@4.23.15.patch: new, written bypnpm patch/pnpm patch-commit.pnpm-workspace.yaml: thepatchedDependenciesentry and its note, next to the existingtsup@8.5.1entry.pnpm-lock.yaml: regenerated bypnpm install, not edited by hand.packages/cli/test/published-entry-node-env-source-reroute.test.ts: a docblock pointer only. No leg, assertion or bound changes.Measured
H0: CI's reading of the CONTROL leg is NOT MEASURED (log hosts unreachable)
Both log hosts refused this session at CONNECT (
gateway answered 403):productionresultssa14.blob.core.windows.net, the job logs of all sixTest Coreshards of run 36964705223 on 6091136;results-receiver.actions.githubusercontent.com, the run log zip.node scripts/pm/ci-failure.mjs --run 36964705223exited 2 withjob log NOT RETRIEVED.What was measured instead is the mechanism that decides it. tsx 4.23.12, 4.23.14 and 4.23.15 all take the synchronous
registerHookspath only on Node 22.22.3+, 24.11.1+, 25.1.0+ or 26+ (version table[[22,22,3],[24,11,1],[25,1,0],[26,0,0]]). That path never reaches the broken specifier. Node 22.22.3 was released on 2026-05-13. CI'ssetup-nodeasks fornode-version: '22', and the dev containers run v22.22.0. Same tree (6091136), unpatched tsx:published-entry-node-env-source-reroute.test.tsSHASUMS256.txt)So CI arms the leg if its runner resolved
'22'to 22.22.3 or later. The runner's actual version is in the log this session could not read.H1: confirmed, with a version bound
DEBUG=oclif:config:ts-pathon v22.22.0, CONTROL preload, the test's own env (noNODE_PATH):Next it logs
Cannot find module 'ts-node', andtsPathreturns thedist/path. In the tarballs, 4.23.12, 4.23.13 and 4.23.14 keep theregister()call indist/register-*.cjs, where it resolves. 4.23.15 inlines it intodist/esm/api/index.cjs, which grows from 1,298 to 16,770 bytes. The.mjsbuild oftsx/esm/apiis unaffected. oclif reaches the.cjsbuild because it locates the module withrequire.resolve.H2: holds
npm view tsx dist-tagsgiveslatestas 4.23.15 (2026-09-20). There is no newer release, so no UP move (remedy d) exists.A second casualty of the same defect
On v22.22.0 with unpatched tsx,
bin/run-dev.jsalso lands ondist/commands. That file is the CLI's SOURCE entry, the onerunServeand the other spawning tests launch (104 test files underpackages/climention it).tsx bin/run-dev.js --versionunderDEBUG=oclif:config:ts-pathlogsCould not find tsxand neverFound source directory. With the patch it logsFound source directory for .../dist/commands at .../src/commands. So on such a Node, source-entry tests have measured the build output rather thansrc/since #21162. The same patch fixes this with nothing added.Why (b), and not (a) or (c)
(a) pins tsx down to 4.23.14. That is a DOWN move and needs a ruling, and measurement does not make it the only sound remedy. Not taken.
(c) reworks the CONTROL leg. To arm without oclif's own tsx registration, the child would need a test-only change to how it resolves
tsx/esm/api: aModule._resolveFilenameshim in the preload, or--conditions=importon the child. That fails the second acceptance criterion as measured. The absence legs do not load the preload, so on v22.22.0 with unpatched tsx, deleting the declaration leaves them green (row 4 below). Arming them too would put the shim in every leg, and then every leg measures a resolution no real install has.(b) patches the component that fails. The test's behaviour is unchanged, and both acceptance criteria hold on v22.22.0 and on v22.22.3.
Four axes:
bin/run-dev.js, which is the larger one, because the CLI's spawning tests silently randist/. The published CLI is not reached:bin/run.jssetssettings.enableAutoTranspile = false, so oclif never registers tsx there, and nothing inpackages/**source importstsx/esm/api.pnpm install(ERR_PNPM_UNUSED_PATCH, the same rule thetsup@8.5.1entry records), and the patch cannot outlive its reason silently. Retire it when a tsx release resolves the three paths itself, or when the toolchain's Node floor reaches 22.22.3. (c) would carry a test shim with no such tripwire. After an upstream fix it would become dead code that still makes every leg diverge from a real install.CONTROL leg and reverse verification (Node v22.22.0 unless stated)
bin/run.jsdevelopment/testlegs@objectstack/cli/17.6.0 linux-x64 node-v22.22.0expected '(node:...) [MODULE_NOT_FOUND] Warni...' not to contain 'Cannot find module './registry''packages/cli/node_modules/tsxlinked to the pristine 4.23.15scripts/ablation-replace.mjs --deleteremoved the declaration (anchor 1 to 0, blob569e6b6fto40a368ce). The same tool restored it and showed the blob equal to HEAD's, withgit diff HEADempty. The tsx link swap ran under an EXIT/INT/TERM trap, andreadlinkconfirmed the restore.bin/run.jsruns unbuilt, so nodist/step is involved.Reviewing the patch
The patch replaces one minified line. Review it with this comparison, which splits the pristine and the patched file on commas and prints every token that differs:
Output:
The
register()specifier is the one that breaks things. The twonew URL(...)entries feed tsx'sisTsxImportset and carry the same wrong base. They are fixed in the same patch because they are the same defect, in the same file, with the same mechanical change. Both rebased targets exist on disk (dist/esm/index.mjs,dist/loader.mjs). No test here reaches those two entries, because no child in this suite passes a TypeScript preload ahead of an absolute tsx loader path.Lockfile
pnpm installregenerated it. With the tsx patch hash normalised away, every removed line equals an added line, except for the three newpatchedDependencieslines. 0 versions moved, 0 DOWN, 0 packages added or removed. Ten snapshot keys change only by the hash suffix:tsxitself, and the peer suffix oftsup,postcss-load-config,fumadocs-mdx,vitex2,vitestx2 and@vitest/mockerx2.pnpm install --frozen-lockfilepasses on the final head.Verification
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsat 8569d5f derives 58 commands from 4 paths. All 58 were run with exit codes captured before any pipe, and all exited 0.--ranreports 58 derived, 58 run, 0 NOT-MEASURED. (In an earlier pass at 6a22902,check:dual-build-cjs-loadsanswered PREREQUISITE NOT MET while sibling packages had nodist/. Once those were built it passed: 105 entry points across 66 packages.)@objectstack/cliunit tier at 6a22902: 244 files, 3461 tests passed.@objectstack/cliintegration tier at 6a22902, two runs under the verify lock: 71 files, 607 tests passed, 1 skipped. The skip is the existingit.skipIfintest/migrate-meta-default-range.test.ts, a file this branch does not touch.pnpm --filter @objectstack/cli typecheckat 6a22902: exit 0,check:test-typecheckincluded.main: fix(plugin-security): the RLS write check refuses an operator the read refuses on a declared JSON-stored column (#21254) #21317 (plugin-security), fix(objectql): redact a driver fault where it leaves the engine, not only in the engine's log line #21335 (objectql, rest, plugin-auth), and two docs-only commits (docs(skills): the platform skill counts the eight generator barrels, picklists included #21337, docs(adr): ADR-0128 §4 dated note — the keyed-digest break did not carry D1, and why #21343). None touches a CLI file, the lockfile,pnpm-workspace.yamlor tsx, so the CLI tiers were not re-run for them. The packages the merge touched (objectql, plugin-auth, rest) were rebuilt. Then the reroute file (5 passed on v22.22.0, 5 passed on v22.22.3) and all 58 gates were re-run on 8569d5f.pnpm lint(repo-wide, CI's run).skip-changeset: nothing published moves.
@objectstack/cliships onlydist,README.mdandCHANGELOG.md, so the test file is not published.patches/,pnpm-workspace.yamlandpnpm-lock.yamlare root files no package ships. The CLI's dependency range on tsx is unchanged.Acceptance notes
setup-nodefloats on'22'. That gap is why the same file read red in three dev containers and green inTest Core. Observation only, no card. Carrier: none.privatenumber/tsxthrough the API. The retirement condition is in thepnpm-workspace.yamlnote.@objectstack/clidepends ontsx ^4.23.15at runtime, so a customer on Node 22.0 to 22.22.2 installs the defective build. No published code path callsrequire('tsx/esm/api'), so no public entry point reaches it. Measured by a grep ofpackages/**source and by the published entry'senableAutoTranspile = false.packages/cli/README.mdandpackages/cli/package.jsonbelong to cli README: documents-h/-vshort flags that exit 2, and says there is noos plugincommand group whileos plugin build|sign|publishis registered #21310 and are not touched.Generated by Claude Code