Skip to content

chore(types): close the .vue type-check gap - adopt vue-tsc in verify - #52

Merged
wormeyman merged 2 commits into
mainfrom
chore/vue-tsc-type-check
Jul 30, 2026
Merged

chore(types): close the .vue type-check gap - adopt vue-tsc in verify#52
wormeyman merged 2 commits into
mainfrom
chore/vue-tsc-type-check

Conversation

@wormeyman

Copy link
Copy Markdown
Collaborator

pnpm run check:vue (vue-tsc --noEmit) now runs inside verify, between vp check and vp test. It is the only thing in this repo that type-checks <script setup lang="ts"> bodies - 22 .vue files that until now were checked by nothing at all.

The gap is reproduced, not asserted

Planted a TS2322 in src/ui/FInfo.vue and ran both tools on the same tree:

tool result
vp check pass: Found no warnings, lint errors, or type errors in 301 files
vue-tsc src/ui/FInfo.vue(3,7): error TS2322: Type 'string' is not assignable to type 'number'
pnpm run verify exit 2, and the test suite never ran

That plant-and-check is the proof the guard is not vacuous. If a future change makes check:vue pass on a planted error, it has been neutered - the note in CLAUDE.md says so explicitly.

Against the real tree: 22 files, 0 errors, ~2.5s. No latent breakage was hiding behind the gap, so this is a regression guard rather than a bug hunt. It runs on the existing typescript 6.0.3 (vue-tsc's peer range is >=5.0.0), so no TypeScript 7 work was involved.

Two CLAUDE.md claims this falsified

1. "Bare vue-tsc --noEmit crashes; it needs its own tsconfig excluding vite.config.ts."

No longer true, so no extra tsconfig is added here. That was measured 2026-07-22 - one day before the vue() as Plugin cast landed in 388f285. Confirmed causally rather than guessed, by deleting the cast and re-running:

with cast without cast
vp check pass TS2321: Excessive stack depth
tsc --noEmit exit 0 Debug Failure. False expression...
vue-tsc --noEmit exit 0 Debug Failure. False expression...

So the TS2321 and the Debug Failure crash are one pathology with one fix, not two problems. That is now recorded, because a reappearance of either should send you to the same place.

2. "verify is ~9.5s." Measured 63s. Already wrong by ~6x before this commit - the suite grew to 143 files through the Vulcanus and cliff work - so it is corrected outright rather than nudged for the 2.5s this adds.

Why it was rejected in July and why that expired

The only stated blocker was supply-chain freshness: vue-tsc@3.3.8 was under an hour old on 2026-07-22, and installing it made pnpm silently write a minimumReleaseAgeExclude bypass into pnpm-workspace.yaml.

It was 7.3 days old here. The gate passed on its own and pnpm-workspace.yaml is untouched by this commit - checked, not assumed. The old fallback advice to "pick 3.3.7 instead" is obsolete and removed. Ranged ^3.3.8 to match every sibling devDependency.

Also documented while in the area

  • pnpm config get minimumReleaseAge reports undefined, which reads like "no policy". It isn't - the 24-hour guard comes from pnpm 11's own defaults table ("minimum-release-age": 24 * 60, // 1 day), read out of pnpm 11.17.0's shipped code. Worth knowing before someone concludes the guard is off.
  • TypeScript 7 is now written up as a "don't", not a vague deferral. 7.0 ships no programmatic API at all (planned for 7.1); the official migration is a dual-install alias (typescriptnpm:@typescript/typescript6); and vue-tsc hard-crashes on a bare typescript@7 with ERR_PACKAGE_PATH_NOT_EXPORTED: './lib/tsc' (Failed with typescript@7.0.2 vuejs/language-tools#6124). Meanwhile the type-check already runs TS7 semantics via tsgolint, so there is nothing to gain. pnpm outdated's 6.0.3 -> 7.0.2 row is a trap.

Gate

pnpm run verify exit 0 - 314 files formatted, 301 files clean, check:vue clean, app 1249 passed / 3 skipped, worker 12, container 3.

Found by the dependency audit of 2026-07-29.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GjMSePtN9aTgr8EtZp66k1

wormeyman and others added 2 commits July 29, 2026 22:43
`pnpm run check:vue` (`vue-tsc --noEmit`) now runs inside `verify`, between
`vp check` and `vp test`. It is the only thing in this repo that type-checks
`<script setup lang="ts">` bodies - 22 `.vue` files that until now were
checked by nothing at all.

The gap was real and is reproduced here rather than asserted: a planted
`TS2322` in `src/ui/FInfo.vue` left `vp check` printing "Found no warnings,
lint errors, or type errors in 301 files", while `vue-tsc` reported
`src/ui/FInfo.vue(3,7): error TS2322` and `pnpm run verify` exited 2 with
the test suite never running. That plant-and-check is the proof the guard
is not vacuous; if a future change makes `check:vue` pass on a planted
error, it has been neutered.

Against the real tree: 22 files, 0 errors, ~2.5s. No latent breakage was
hiding behind the gap, so this is a regression guard, not a bug hunt. It
runs on the existing `typescript` 6.0.3 - `vue-tsc`'s peer range is
`>=5.0.0`, so no TypeScript 7 work was involved.

Two CLAUDE.md claims this falsified, both now corrected in place:

- **"Bare `vue-tsc --noEmit` crashes, it needs its own tsconfig excluding
  `vite.config.ts`."** No longer true, so no extra tsconfig is added here.
  That was measured 2026-07-22, one day before the `vue() as Plugin` cast
  landed in 388f285. Confirmed causally by deleting the cast and re-running:
  without it, `vp check` fails `TS2321` and `tsc` AND `vue-tsc` both die on
  the same `Debug Failure` assertion; with it, all three pass. So the
  `TS2321` and the `Debug Failure` crash are one pathology with one fix,
  not two problems.
- **"`verify` is ~9.5s."** Measured 63s. That was already wrong by ~6x
  before this commit - the suite grew to 143 files through the Vulcanus and
  cliff work - so it is corrected rather than merely adjusted for the
  ~2.5s this adds.

**Why it was rejected on 2026-07-22 and why that expired.** The only stated
blocker was supply-chain freshness: 3.3.8 was under an hour old and
installing it made pnpm silently write a `minimumReleaseAgeExclude` bypass.
It was 7.3 days old here, so the gate passed on its own and
`pnpm-workspace.yaml` is untouched by this commit - checked, not assumed.
The old fallback advice to "pick 3.3.7 instead" is now obsolete and has
been removed.

Also documented while in the area, because it misleads in a dangerous
direction: `pnpm config get minimumReleaseAge` reports `undefined`, which
reads like "no policy". The 24-hour guard comes from pnpm 11's own defaults
table (`"minimum-release-age": 24 * 60`), read out of pnpm 11.17.0's
shipped code. And TypeScript 7 is now written up as a "don't" rather than a
vague deferral: 7.0 ships no programmatic API at all (planned for 7.1), the
official migration is a dual-install alias, and `vue-tsc` hard-crashes on a
bare `typescript@7` with `ERR_PACKAGE_PATH_NOT_EXPORTED`.

`vue-tsc` is ranged `^3.3.8` to match every sibling devDependency.

Gate: `pnpm run verify` exit 0 - 314 files formatted, 301 files clean,
`check:vue` clean, app 1249 passed / 3 skipped, worker 12, container 3.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GjMSePtN9aTgr8EtZp66k1
`verify` gained a fourth phase, so three places that spell the list out went
stale the moment it did. CLAUDE.md was updated in the parent commit; these were
not:

- `README.md` listed the gate as `vp check` + `vp test` + `preview:test` in two
  places (the script list and the deploy section) and had no `check:vue` entry
  at all, so the one command that type-checks `.vue` bodies was undiscoverable
  from the README.
- `.github/workflows/verify.yml`'s comment said "its three phases". The count is
  the whole point of that comment - it explains why the workflow calls
  `pnpm run verify` verbatim instead of re-listing phases as steps.

Comment-only in the YAML: no step is added and no phase is mirrored into CI,
which is deliberate - `verify` stays the single definition and CI picks up the
new phase with no workflow change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F3a2HLJ4beKARi7SPmaM54
@wormeyman
wormeyman force-pushed the chore/vue-tsc-type-check branch from 66a3645 to 9934a45 Compare July 30, 2026 05:44
@wormeyman
wormeyman merged commit 605a4cc into main Jul 30, 2026
1 check passed
wormeyman added a commit that referenced this pull request Jul 30, 2026
Verified locally and in CI, and the archive itself was opened by two readers other than the one the test uses.

## The ZIP output is byte-identical across the swap
Generated the real artifact both ways (jszip on `main`, client-zip on this branch) for the `Default` preset and compared with **Info-ZIP `unzip`** and **Python `zipfile`** - neither of which is jszip, the reader the spec uses:

```
unzip -t: "No errors detected in compressed data" for BOTH archives
```

| entry | before | after | bytes identical |
| --- | --- | --- | --- |
| `map-gen-settings.json` | 2936 B | 2936 B | **yes** |
| `map-settings.json` | 3806 B | 3806 B | **yes** |
| `Default.txt` | 798 B | 798 B | **yes** |

Entry **names, order and bodies all identical**; both archives `Stored` at 0%, confirming jszip was not compressing either, so the pako 1.x payload was pure waste. Archive total 7888 -> 7936 B (+48, central-directory layout). Also checked the contents against the exporters directly - with no zip library in the loop - and `Default.txt` is verbatim equal to the `builtin-presets.json` fixture.

## No `eval`, no Node builtins, no new warnings
`pnpm vp build` exit 0 and **zero warnings**. Scanning the emitted bundle:

| marker | count in `dist` |
| --- | --- |
| `eval(` / `new Function(` | **0** |
| `jszip` / `JSZip` | **0** |
| `setImmediate`, `readable-stream`, `_tr_stored_block` | **0** |
| `require(`, `node:fs`, `node:path`, `process.nextTick`, `Buffer.from` | **0** |
| `incorrect header check` (pako) | **1** - the codec's pako 3.0.1 correctly remains |

`public/_headers` and `vite.config.ts` are untouched: CSP is still `script-src 'self' https://static.cloudflareinsights.com` with no `unsafe-eval`, and **no `build.rollupOptions.onLog` suppression was added**. Upstream, `client-zip@2.5.0` is 6.4 kB of single-file ESM, MIT, **zero runtime dependencies**, and its tarball contains no `eval`, `new Function`, `require(` or `node:*` import.

## Bundle size re-measured, not taken from the description
Built both branches here: **302.07 kB -> 211.44 kB raw (-90.64 kB, -30.0%)**, gzip -26.7%. Matches the claim.

## Gate
- Local `pnpm run verify` exit **0** in 60s on the rebased tree: format 316, lint+type 301, `check:vue` clean, app **1251 passed / 3 skipped** (+2 as described), worker 12.
- CI `verify`: **pass** on head `39a5890`, rebased onto `605a4cc` so it ran against a `main` containing both #51 and #52 - including #52's new `check:vue` phase.
- `jszip` is in `devDependencies` and `client-zip` in `dependencies`; `pnpm install --frozen-lockfile` clean.

## One thing the PR missed
`test/actionBar.spec.ts:72` still carries a jszip-specific comment - "buildZip -> JSZip.generateAsync resolves across macrotasks/setImmediate, which flushPromises (microtasks only) does not drain". The 100ms real-timer wait it justifies still works, so nothing fails, but the comment now explains the code by naming a dependency the app no longer bundles. Left as-is rather than widened into this PR; worth a one-line follow-up.
wormeyman added a commit that referenced this pull request Jul 30, 2026
…RULE, not the fields (#57)

Verified locally and in CI, and the anti-vacuity control was tested by breaking it.

## The `+3` control is genuinely load-bearing - proven, not assumed
This repo has a documented history of confirmations that turned out vacuous, so I sabotaged the substitution rather than trusting the assertion. Replacing the game-field lookup with one that never matches - i.e. simulating every lookup silently falling through to our own field:

| test | with substitution dead |
| --- | --- |
| "substituting the game's own elevation and cliffiness does not move a single cell" | **still passes** |
| "and the substitution really is live - a `+3` elevation bias does move cells" | **FAILS**: `AssertionError: expected 0 to be greater than 5` |

So the headline assertion *would* have passed vacuously, and the `+3` control is exactly what catches it. Restored and re-confirmed 9/9 green. `expect(game.size).toBeGreaterThan(500)` is a second, independent guard against an empty placement set making the equality trivial.

## Provenance
`test/fixtureProvenance.spec.ts` passes (4/4). Both new fixtures carry entries with `factorioVersion: "2.1.12"` and concrete `evidence` naming the capture script and recording that `refs:sync --check` reported in-sync at capture time - so neither lands as `unknown`, and the spec's `unknown` cap is not touched.

## Gate
- Local `pnpm run verify` exit **0** in 60s on the exact tree that lands (`5399b6f`): format 316, lint+type 301, `check:vue` clean, app **1255 passed / 3 skipped**, worker 12.
- CI `verify`: **pass** (4m41s). The branch was updated **twice** so the run tests the real target - it now contains #51, #52, #55 and #59, not the older `main` the first run saw.
- No `src/` change, so nothing shipped to users; the new spec needs no Factorio at runtime because the fixtures are committed, which is why CI can run it.

## Two notes, neither blocking
- The description reads as though `test/vulcanusOreCliffSeparation.spec.ts` is new. It is not - it already existed on `main` at 222 lines and this extends it to 597 (3 -> 7 `it` blocks at the top level, 9 tests total), which is why the file count stays 143 and the suite moves +4 rather than +9.
- Re-deriving the binary reading and the statistics was explicitly out of scope for this pass, so the `0x1016229b4` / `0x101625038` disassembly claims and the Poisson figures are **recorded as the author's measurements, not independently reproduced here**. What was checked is that the code is gated, the fixtures have provenance, and the assertions can fail.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant