Repository navigation
fix(runtime): stpa actually runs on Node — and CI can now tell - #12
Merged
Merged
Conversation
PR #9 shipped "runs on Node or Bun" green and broken. Every subcommand that spawned a tool died on Node with `SyntaxError: Cannot use import statement outside a module`; only `stpa --help` worked, because it never spawns anything. The root cause is not the one it looks like. Node's module-syntax detection reads these .ts tools as ESM perfectly well in a clean directory — the bug does not reproduce there. It needs an ANCESTOR whose package.json declares {"type":"commonjs"}, because an explicit ancestor declaration beats detection. That is not a corner case: `~/.claude/package.json` declares commonjs and skills install into `~/.claude/skills/`, so the documented install path was precisely the configuration that broke. Anyone who followed the README got a toolkit where nothing but --help ran. The fix is a dependency-free package.json declaring {"type":"module"}, which shadows any ancestor and makes resolution explicit instead of a property of where the repo happens to sit. That declaration then makes the launcher and one tool genuinely ESM, so: - `stpa` moves from require() to import, and from __dirname to fileURLToPath(import.meta.url). fileURLToPath rather than import.meta.dirname so the launcher still covers the whole Node range its own RUNTIME block claims. - `Tools/UcaGrid.ts` had two inline require("node:fs") calls and no imports at all — it was implicitly CommonJS, which is why it alone survived. Now it imports at the top like every sibling. CI grew a Node lane on 22 and 24 that runs the pipeline end-to-end through the CLI, then does it again with the repo staged under a commonjs ancestor. That second step is the one that matters: a Node job in a clean checkout would have passed for the whole four weeks the toolkit was broken. And because a gate that cannot fail is decoration, a final step deletes package.json and asserts the break comes back. Also swept the usage strings: 28 `bun <Tool>.ts` lines across 13 tools became `stpa <verb>`. They hardcoded a runtime the toolkit no longer requires and pointed people at files instead of the command. Closes #11 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #11.
The bug
Every
stpasubcommand that spawns a tool died on Node withSyntaxError: Cannot use import statement outside a module. Onlystpa --helpworked, because it never spawns anything. PR #9's headline claim — "Bun is no longer an install prerequisite" — was false for four weeks.The root cause is not the obvious one
Issue #11 said "the repo has no package.json, so Node resolves
.tsas CommonJS." That is wrong, and I only found out by trying to make the new CI gate fail.In a clean directory, Node's module-syntax detection reads these tools as ESM perfectly well. The bug does not reproduce there. It needs an ancestor directory whose
package.jsondeclares{"type":"commonjs"}— an explicit ancestor declaration beats detection.That is not a corner case. It is the documented install path:
So the README told people to clone into exactly the location that broke it, and a clean-checkout CI job would have passed the entire time.
The fix
A dependency-free
package.jsondeclaring{"type":"module"}. It shadows any ancestor and makes resolution explicit rather than a property of where the repo happens to sit.That declaration makes two files genuinely ESM, so they had to follow:
stpa—require()→import,__dirname→fileURLToPath(import.meta.url). UsedfileURLToPathrather thanimport.meta.dirnameso the launcher still covers the whole Node range its own RUNTIME block claims to support.Tools/UcaGrid.ts— had two inlinerequire("node:fs")calls and no imports at all. It was implicitly CommonJS, which is why it was the one tool that already worked under Node. Now it imports at the top like every sibling.The CI lane that should have existed
Node 22 and 24, running the pipeline end-to-end through the CLI — then doing it again with the repo staged under a
{"type":"commonjs"}ancestor. That second step is the one that matters; without it the job is green theatre.And because a gate that cannot fail is decoration, a final step deletes
package.jsonand asserts the break returns. I wrote that step after the first version of this gate passed with the fix removed — which is how the real root cause surfaced.Verification
stpa --helpstpa runend-to-endBun regression-checked: full pipeline, 32 cells, 8 findings, 100% coverage,
REPORT.html+SUMMARY.html.Also
28
bun <Tool>.tsusage strings across 13 tools becamestpa <verb>. They hardcoded a runtime the toolkit no longer requires and pointed people at files instead of the command.Still open, not in this PR:
Tools/DiscoveryGate.ts:275writes its template withoutmkdirand dies on a raw ENOENT; the committedExamples/ledgerline/REPORT.htmlis 5 lines of CSS stale against its own renderer.🤖 Generated with Claude Code