Context
Part of #9515. The epic's "nothing hand-maintained" principle extends to the language layer: hand-written .mjs/.js files are untyped surfaces that TypeScript can't protect and that drift exactly like hand-copied schemas do. metagraphed finished this migration and then made it permanent with validate:no-hand-written-mjs — a git ls-files scan failing the build for any tracked .mjs/.js/.cjs outside a small commented allowlist, run unconditionally in CI. LoopOver has no such gate, and hand-written JS remains:
packages/loopover-miner/scripts/generate-env-reference.mjs (249 lines — a load-bearing generator, run by root miner:env-reference/:check in test:ci; it even imports from the ORB generator and carries a "skip a .js with a same-basename .ts sibling" guard — migration-era logic that outlived the migration)
packages/loopover-mcp/scripts/strip-bin-sourcemap.mjs + check-syntax.mjs (build steps of the published MCP package)
scripts/control-plane-coverage.mjs (root control-plane:coverage)
packages/loopover-miner/scripts/benchmark.mjs, packages/loopover-miner/scripts/cross-repo-evaluation.mjs, packages/loopover-engine/scripts/load-test-iterate-loop.mjs
- plus whatever a full
git ls-files '*.mjs' '*.js' '*.cjs' inventory turns up (the requirement below starts there — the list above is the investigation's sample, not the census)
Hand-written ambient .d.ts files are in scope the same way: where one exists it either becomes a real .ts module or is proven to be generated. Generated artifacts (worker-configuration.d.ts via cf-typegen, dist/**, node_modules) are explicitly out of scope — the gate targets tracked, hand-written files only.
Requirements
- Census first:
git ls-files '*.mjs' '*.js' '*.cjs' '*.d.ts' inventory committed to the PR description (not the repo), each file classified: migrate / generated (exempt) / allowlist-with-reason.
- Migrate every hand-written
.mjs/.js/.cjs to .ts, preserving behavior: generators keep byte-identical output (their --check modes prove it), build scripts keep identical effects (npm run build:mcp output unchanged, test:mcp-pack allowlist updated for renamed script paths — scripts/mcp-package-allowlist.ts lists them per file), coverage/benchmark scripts keep identical CLI contracts. Runner is the repo's existing conventions (tsx or node --experimental-strip-types, matching how each script's siblings already run).
- Migrate or justify every hand-written
.d.ts: convert to .ts where it carries real logic/types consumed at build time; allowlist with a comment where an ambient declaration is genuinely required.
- The permanent gate:
scripts/validate-no-hand-written-js.ts, run in test:ci unconditionally (no docs-only skip), failing on any tracked .mjs/.js/.cjs (and hand-written .d.ts) outside a small, commented allowlist. Allowlist entries require an inline reason; the check fails on allowlist entries that no longer exist (metagraphed's rot lesson — a watched path that disappears must fail loudly, not silently pass).
- Clean up migration-era guards made obsolete: the
.js-sibling skip in the env-reference generator, and any dist/-era special cases the census surfaces.
- Full branch-counted coverage on migrated generators' logic where it's importable; fix-what-you-find applies (expect stale paths in the older benchmark/load-test scripts).
Non-goals
- Generated files (
worker-configuration.d.ts, dist/**, lockfiles) — exempt by definition.
- Rewriting script logic beyond the language port (generators must stay output-identical in the same PR as their port).
review-enrichment/ if its census shows a separate runtime constraint — classify and record, don't force.
Deliverables
Expected outcome
git ls-files shows zero hand-written JavaScript outside a tiny, reasoned allowlist; the gate makes regression impossible; every script that generates artifacts or ships in a package is typed. Combined with the rest of #9515, "hand-maintained" is extinct at both the contract layer and the language layer.
References
Part of #9515. Independent of the other sub-issues (can start immediately). Pattern source: metagraphed's validate:no-hand-written-mjs post-epic sweep.
Context
Part of #9515. The epic's "nothing hand-maintained" principle extends to the language layer: hand-written
.mjs/.jsfiles are untyped surfaces that TypeScript can't protect and that drift exactly like hand-copied schemas do. metagraphed finished this migration and then made it permanent withvalidate:no-hand-written-mjs— agit ls-filesscan failing the build for any tracked.mjs/.js/.cjsoutside a small commented allowlist, run unconditionally in CI. LoopOver has no such gate, and hand-written JS remains:packages/loopover-miner/scripts/generate-env-reference.mjs(249 lines — a load-bearing generator, run by rootminer:env-reference/:checkintest:ci; it even imports from the ORB generator and carries a "skip a.jswith a same-basename.tssibling" guard — migration-era logic that outlived the migration)packages/loopover-mcp/scripts/strip-bin-sourcemap.mjs+check-syntax.mjs(build steps of the published MCP package)scripts/control-plane-coverage.mjs(rootcontrol-plane:coverage)packages/loopover-miner/scripts/benchmark.mjs,packages/loopover-miner/scripts/cross-repo-evaluation.mjs,packages/loopover-engine/scripts/load-test-iterate-loop.mjsgit ls-files '*.mjs' '*.js' '*.cjs'inventory turns up (the requirement below starts there — the list above is the investigation's sample, not the census)Hand-written ambient
.d.tsfiles are in scope the same way: where one exists it either becomes a real.tsmodule or is proven to be generated. Generated artifacts (worker-configuration.d.tsviacf-typegen,dist/**,node_modules) are explicitly out of scope — the gate targets tracked, hand-written files only.Requirements
git ls-files '*.mjs' '*.js' '*.cjs' '*.d.ts'inventory committed to the PR description (not the repo), each file classified: migrate / generated (exempt) / allowlist-with-reason..mjs/.js/.cjsto.ts, preserving behavior: generators keep byte-identical output (their--checkmodes prove it), build scripts keep identical effects (npm run build:mcpoutput unchanged,test:mcp-packallowlist updated for renamed script paths —scripts/mcp-package-allowlist.tslists them per file), coverage/benchmark scripts keep identical CLI contracts. Runner is the repo's existing conventions (tsxornode --experimental-strip-types, matching how each script's siblings already run)..d.ts: convert to.tswhere it carries real logic/types consumed at build time; allowlist with a comment where an ambient declaration is genuinely required.scripts/validate-no-hand-written-js.ts, run intest:ciunconditionally (no docs-only skip), failing on any tracked.mjs/.js/.cjs(and hand-written.d.ts) outside a small, commented allowlist. Allowlist entries require an inline reason; the check fails on allowlist entries that no longer exist (metagraphed's rot lesson — a watched path that disappears must fail loudly, not silently pass)..js-sibling skip in the env-reference generator, and anydist/-era special cases the census surfaces.Non-goals
worker-configuration.d.ts,dist/**, lockfiles) — exempt by definition.review-enrichment/if its census shows a separate runtime constraint — classify and record, don't force.Deliverables
.mjs/.js/.cjsported to.ts(or allowlisted with reason).d.tsfiles ported or allowlistedvalidate-no-hand-written-jsgate intest:ci, unconditional, with existence-checked allowlist--checkmodes prove byte-identical outputs across the portExpected outcome
git ls-filesshows zero hand-written JavaScript outside a tiny, reasoned allowlist; the gate makes regression impossible; every script that generates artifacts or ships in a package is typed. Combined with the rest of #9515, "hand-maintained" is extinct at both the contract layer and the language layer.References
Part of #9515. Independent of the other sub-issues (can start immediately). Pattern source: metagraphed's
validate:no-hand-written-mjspost-epic sweep.