Documentation Index
Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt
Use this file to discover all available pages before exploring further.
Version: 1.0.0
Last Updated: 2026-04-23
Owner: Platform Architecture
ADR: ADR-018
Status: Phase 1 ready to implement; Phase 2 gated; Phase 3 follow-up
TL;DR
This is the executable companion to ADR-018. It breaks the migration to the Go-based tsgo compiler into three phases with concrete diffs, verification commands, gating criteria, and rollback procedures.
| Phase | Goal | Risk | Gating | Status |
|---|
| 1 | Opt-in tsgo for local dev (typecheck:fast); CI gate unchanged | None — purely additive | None — implement now | Ready |
| 2 | Cut CI typecheck over to tsgo; introduce npm:@typescript/typescript6 alias for tools that import the TS Compiler API | Low–medium (touches CI gate, lockfile) | (a) TS 7.0 RC or GA shipped, (b) typescript-eslint peer range opens to >=6.1.0 (ideally explicit TS 7 support) | Blocked on upstream |
| 3 | Drop the @typescript/typescript6 alias; let typescript resolve to 7.x natively | Low | All TS-API-consuming deps (linter, msw, vitest, ts-node, vite-plugin-checker, eslint-plugin-tsdoc, internal audit scripts) advertise TS 7-compatible peer ranges | Long-tail follow-up |
- TS 7.0 Beta = same diagnostics as TS 6.0 on this commit (validated: 0 errors → 0 errors).
tsgo cold typecheck on this repo: 53 s vs tsc (TS 6.0.3) cold: 478 s (~9× faster).
tsgo warm typecheck (no cache configured): 3.6 s vs tsc warm-cache target: ~8 s (per .github/workflows/build.yml comment).
- Currently 0 uses of
ignoreDeprecations in any tsconfig (the most common 6→7 break) — see tsconfig.json, tsconfig.app.json, tsconfig.node.json, packages/docs/tsconfig.json, supabase/functions/tsconfig.json.
- Internal TS Compiler API consumers: 3 audit scripts.
scripts/audit/doc-comment-coverage/parser.ts
scripts/audit/audit-appicon-tones.ts
scripts/audit/audit-page-spacing.ts
- Third-party TS-API consumers in
node_modules (peer ranges as of writing):
typescript-eslint@8.58.x / @typescript-eslint/parser@8.58.x → typescript >=4.8.4 <6.1.0
eslint-plugin-tsdoc@0.5.2 (via @typescript-eslint/utils@8.56) → same range
ts-node@10.9.2 → typescript: >=2.7 (compatible)
tsx@4.21.x (esbuild-based; no TS Compiler API peer)
fallow@2.66.x → standalone Rust binary (no TS Compiler API peer dependency)
msw@2.13.2 → typescript: >=4.8
@vitest/coverage-v8@4.1.5 / @vitest/ui@4.1.5 (no direct TS API peer; bundles its own)
vite-plugin-checker@0.13.0 → spawns tsc/vue-tsc from the project’s typescript
- Current
package.json declares typescript@^6.0.3; lockfile resolved 6.0.3; in node_modules actually deduped to 5.9.3 (because peer ranges in installed minor versions of typescript-eslint cap <6.1.0 and the deduper picked the lower-but-satisfying version after a fresh npm ci --legacy-peer-deps).
- The
packages/docs/ workspace pins its own typescript@~5.2.2; isolated from this plan.
.github/workflows/build.yml step “Cache TypeScript incremental build info” caches node_modules/.cache/tsc keyed on tsconfig*.json + package-lock.json + src/integrations/supabase/types.ts.
- Husky
pre-commit runs npm run typecheck (full project); changing this script changes pre-commit behavior.
Phase 1 — Opt-in tsgo for local development
Goal: Give every developer a sub-minute cold typecheck, with zero impact on the existing npm run typecheck / CI gate / linter / pre-commit hook.
Risk: None (purely additive — existing TS 6.0.3 path is unchanged).
No gating criteria — implement immediately.
Diff 1.1 — package.json
Add @typescript/native-preview as a devDependency (pin to a specific beta build to keep CI deterministic; let renovate roll it forward).
"devDependencies": {
"@biomejs/biome": "^2.4.11",
+ "@typescript/native-preview": "7.0.0-dev.20260421.2",
...
"typescript": "^6.0.3",
"typescript-eslint": "^8.59.0",
...
}
Rationale for pinning the exact beta build (no caret): the package’s dist-tags show fast-moving latest/beta channels (e.g. 7.0.0-dev.20260421.2 vs 7.0.0-dev.20260422.1); we don’t want CI to roll silently within a beta train.
Diff 1.2 — package.json scripts
Add a fast-path typecheck. Do not modify the existing typecheck script.
"scripts": {
...
"typecheck": "cross-env NODE_OPTIONS=--max-old-space-size=8192 tsc --noEmit -p tsconfig.app.json --incremental --tsBuildInfoFile node_modules/.cache/tsc/app.tsbuildinfo",
+ "typecheck:fast": "tsgo --noEmit -p tsconfig.app.json",
...
}
Notes:
tsgo does not require --incremental to be fast (warm 3.6s without one in our test). Omitting it keeps the script simple and avoids creating a second cache directory.
- No
NODE_OPTIONS=--max-old-space-size=8192 because tsgo is a Go binary (not Node) and uses far less memory; the env flag is harmless if a wrapper Node entry point is involved, but adding it would imply Node memory tuning that doesn’t apply.
Diff 1.3 — AGENTS.md “Pre-Flight Checklist → Before commit”
Add a one-liner pointer for fast local iteration. Keep npm run validate as the authoritative gate.
**Before commit (required):** `npm run format:check && npm run typecheck && npm run lint && npm run build` (or `npm run validate`). ...
+
+**Faster local typecheck (developer ergonomics):** `npm run typecheck:fast` (uses the TypeScript 7.0 native compiler `tsgo`; ~9× faster cold, ~2× faster warm; same diagnostics as `tsc`; opt-in). The authoritative pre-commit / CI gate remains `npm run typecheck`. See `docs/development/TYPESCRIPT_7_MIGRATION_PLAN.md`.
Diff 1.4 — docs/development/CI_PIPELINE.md
In the “What to run locally before pushing” section, add:
Or the same steps individually: `npm run format:check`, `npm run typecheck`, `npm run lint`, `npm run build`, `npm run test`, `npm run test:rls:smoke` (or full RLS as needed). See [TESTING_SETUP_AND_RUN.md](../testing/TESTING_SETUP_AND_RUN.md) for E2E and env requirements.
+
+**Tip:** For inner-loop iteration, `npm run typecheck:fast` runs the TypeScript 7.0 (Go) compiler and is roughly 9× faster on cold runs. It produces the same diagnostics as `npm run typecheck`. CI continues to use `tsc` until [Phase 2 of the TS 7 migration plan](TYPESCRIPT_7_MIGRATION_PLAN.md#phase-2--cut-ci-typecheck-over-to-tsgo) lands.
Diff 1.5 — docs/development/DEVELOPMENT_QUICK_REFERENCE.md
Add a row to the developer ergonomics table referencing typecheck:fast and the TypeScript Native Preview VS Code extension.
Verification (Phase 1)
Run, in this order, on a clean checkout:
npm ci --legacy-peer-deps
node -e "console.log(require('@typescript/native-preview/package.json').version)"
./node_modules/.bin/tsgo --version # → Version 7.0.0-dev.20260421.2
# Existing path still works and still gates the same as before
npm run typecheck
npm run validate
# Fast path returns identical diagnostics
npm run typecheck:fast
# Ergonomics check: cold time
rm -rf node_modules/.cache/tsc
time npm run typecheck # baseline
time npm run typecheck:fast # should be ~9× faster cold
Acceptance criteria (Phase 1)
Rollback (Phase 1)
git revert the Phase 1 commit. npm ci --legacy-peer-deps removes @typescript/native-preview and its platform-specific binary. There are no schema, format, or behavioral changes to reverse.
Phase 2 — Cut CI typecheck over to tsgo
Goal: Make tsgo the authoritative typecheck for npm run typecheck, the CI build gate, and the husky pre-commit hook. Save ~7 minutes on cold-cache CI runs (Supabase types regen, dep bumps, tsconfig changes).
Risk: Low–medium. Touches the CI gate, the lockfile, and the package the linter resolves at runtime. Mitigated by the npm:@typescript/typescript6 alias and a one-PR-at-a-time staged rollout.
Gating criteria (BOTH must hold before opening the Phase 2 PR)
- Microsoft has shipped TypeScript 7.0 RC or GA. Track via the TypeScript blog and
npm view @typescript/native-preview dist-tags (looking for a non--dev tag, or a typescript@7.x publication on the latest channel).
typescript-eslint ships a release whose installed peerDependencies.typescript range opens beyond <6.1.0 (verified via npm view typescript-eslint peerDependencies and npm view @typescript-eslint/parser peerDependencies). The cleanest signal is an explicit “TypeScript 7 support” line in their changelog; otherwise verify the peer range alone.
If only criterion (1) is met but not (2), Phase 2 is still doable using the alias — but the optics of the alias are simpler to explain after typescript-eslint openly supports TS 7. Prefer waiting for both unless CI cold-cache pain becomes acute.
Diff 2.1 — package.json
"devDependencies": {
...
"@typescript/native-preview": "7.0.0-dev.20260421.2",
...
- "typescript": "^6.0.3",
+ "typescript": "npm:@typescript/typescript6@^6.0.0",
+ "@typescript/typescript-7": "npm:typescript@^7.0.0",
"typescript-eslint": "^8.59.0",
...
}
Why two entries:
typescript resolves to @typescript/typescript6 so any tool that does import ts from 'typescript' (linter, msw, vitest, ts-node, vite-plugin-checker, eslint-plugin-tsdoc, and our 3 audit scripts) keeps the TS 6 Compiler API. This matches Microsoft’s documented compatibility recipe.
@typescript/typescript-7 (alias of stable typescript@^7) provides the binary we want to invoke directly. By the time Phase 2 ships, the published typescript@7.x package will own the tsc binary; we keep @typescript/native-preview installed only as long as we still need a beta channel.
Once stable typescript@7 is published, the @typescript/native-preview dep can be dropped in the same PR. If only @typescript/native-preview is available (RC era), keep using tsgo and skip the second alias.
Diff 2.2 — package.json scripts
Switch the canonical typecheck to tsgo (or to tsc from the aliased TS 7 package, depending on what’s published at cutover time). Drop the now-unnecessary --incremental and --tsBuildInfoFile.
- "typecheck": "cross-env NODE_OPTIONS=--max-old-space-size=8192 tsc --noEmit -p tsconfig.app.json --incremental --tsBuildInfoFile node_modules/.cache/tsc/app.tsbuildinfo",
+ "typecheck": "tsgo --noEmit -p tsconfig.app.json",
+ "typecheck:legacy": "cross-env NODE_OPTIONS=--max-old-space-size=8192 tsc6 --noEmit -p tsconfig.app.json --incremental --tsBuildInfoFile node_modules/.cache/tsc/app.tsbuildinfo",
"typecheck:fast": "tsgo --noEmit -p tsconfig.app.json",
tsc6 is the binary the @typescript/typescript6 package exposes (per Microsoft’s compatibility docs). Keep typecheck:legacy available for ~1 release cycle as an escape hatch if a tsgo regression is discovered post-cutover.
typecheck:fast is now redundant with typecheck; keep the alias for one release for muscle memory, then remove in Phase 3.
Diff 2.3 — .github/workflows/build.yml
- - name: Cache TypeScript incremental build info
- uses: actions/cache@v4
- with:
- # Survives across runs so warm `npm run typecheck` finishes in
- # ~8s instead of the 7m+ cold compile against the 100k-line
- # generated Supabase types file.
- path: node_modules/.cache/tsc
- key: tsc-${{ runner.os }}-${{ hashFiles('tsconfig.json', 'tsconfig.app.json', 'tsconfig.node.json', 'package-lock.json', 'src/integrations/supabase/types.ts') }}
- restore-keys: |
- tsc-${{ runner.os }}-
-
- name: Typecheck
run: npm run typecheck
Drop the tsc incremental cache step entirely — tsgo cold time is already < 1 minute. Keeping the cache step would be harmless but wasteful (cache restore alone takes a few seconds on every job).
Diff 2.4 — .husky/pre-commit
No diff required — npm run typecheck is unchanged in name. Pre-commit hook now invokes tsgo and finishes in seconds instead of minutes (a major DX win independent of CI).
Diff 2.5 — Documentation refresh
| File | Change |
|---|
AGENTS.md “Pre-Flight Checklist → Before commit” | Remove the “faster local typecheck” addendum from Phase 1; update the canonical command list to mention TS 7 / tsgo is now the gate. |
docs/development/CI_PIPELINE.md Job sequence step 5 | ”Types: npm run typecheck (TypeScript 7 / tsgo)“ |
docs/development/CI_PIPELINE.md “Tip” added in Phase 1 | Remove (no longer needed). |
.github/workflows/build.yml header comment | Replace “7m+ cold compile” mention with TS 7 cold-time. |
docs/architecture/decisions/ADR-018-typescript-7-go-compiler-evaluation.md | Flip status to Accepted. Add a “Phase 2 implemented in PR #NNN” line. |
docs/architecture/decisions/index.md | Update ADR-018 row status. |
CLAUDE.md “Common Commands” table | If it pins a TS version anywhere, bump. (Currently version-free.) |
docs/VERSIONS.md | If AGENTS.md minor-version is bumped per the maintenance guide, mirror here. |
Verification (Phase 2)
In a feature branch, after Diffs 2.1–2.5 are applied:
# Lockfile health
npm ci --legacy-peer-deps
node -e "const v=require('typescript/package.json').version; console.log('typescript=', v); if(!/^6\\./.test(v)) process.exit(1)"
./node_modules/.bin/tsc --version # via the TS 7 alias entry → Version 7.x.y (or `tsgo --version` if not yet published)
./node_modules/.bin/tsc6 --version # via the alias → Version 6.0.x
# Authoritative gate
npm run typecheck # → expect < 90 s cold, < 10 s warm, 0 errors
npm run typecheck:legacy # → still works; 0 errors; same as old gate
npm run validate # full pipeline
# Linter unchanged
npm run lint
npm run lint:ci
# Audit scripts that import 'typescript' still work
npx tsx scripts/audit/audit-appicon-tones.ts --help 2>&1 | head -5
npx tsx scripts/audit/audit-page-spacing.ts --help 2>&1 | head -5
npx tsx scripts/audit/doc-comment-coverage/audit.ts --help 2>&1 | head -5 # entry that uses parser.ts
# Tests
npm run test
npm run test:integration
npm run test:rls:smoke
npm run test:e2e:smoke
CI run on the PR must pass:
Build workflow (with the new tsgo-based typecheck step)
governance-weekly workflow (if it runs on PR; otherwise verify it succeeds on first scheduled run after merge)
db-advisor-quarterly, db-migration-guard, e2e-full, fhir-validate, supabase-deploy-*, supabase-drift-check workflows must continue to pass — none of these invoke tsc directly, so no diff is expected.
Acceptance criteria (Phase 2)
Rollback (Phase 2)
If a regression surfaces post-merge:
git revert the Phase 2 commit on prod.
npm ci --legacy-peer-deps restores TS 6.0.3 as the resolved typescript.
- CI
Build reverts to the old typecheck step automatically (cache step is restored by the same revert).
- Pre-commit hook reverts to the old
tsc invocation.
- Open an issue capturing the regression with reproduction steps. File upstream against microsoft/typescript-go if it’s a
tsgo bug.
The escape hatch npm run typecheck:legacy (added in Diff 2.2) provides per-developer rollback without touching the repo: anyone can run the TS 6 compiler against the same source.
Phase 3 — Drop the alias; let typescript resolve to 7.x natively
Goal: Simplify dependency graph by removing the npm:@typescript/typescript6 alias.
Risk: Low. Mostly a cleanup; the heavy lifting was Phase 2.
Gating criteria (ALL must hold)
Diff 3.1 — package.json
"devDependencies": {
...
- "@typescript/native-preview": "7.0.0-dev.20260421.2", // already removed in Phase 2 if stable shipped
- ...
- "typescript": "npm:@typescript/typescript6@^6.0.0",
- "@typescript/typescript-7": "npm:typescript@^7.0.0",
+ "typescript": "^7.0.0",
"typescript-eslint": "^8.59.0", // bump to whatever explicit-TS-7 version is current
...
}
Diff 3.2 — package.json scripts
- "typecheck": "tsgo --noEmit -p tsconfig.app.json",
- "typecheck:legacy": "cross-env NODE_OPTIONS=--max-old-space-size=8192 tsc6 --noEmit -p tsconfig.app.json --incremental --tsBuildInfoFile node_modules/.cache/tsc/app.tsbuildinfo",
- "typecheck:fast": "tsgo --noEmit -p tsconfig.app.json",
+ "typecheck": "tsc --noEmit -p tsconfig.app.json",
(tsc here is now the TS 7 binary owned by the typescript package.)
Diff 3.3 — Internal audit scripts
If any of the 3 internal audit scripts emits a deprecation warning or breaks against the TS 7 Compiler API, change their import to:
-import ts from 'typescript';
+import ts from '@typescript/typescript6'; // pin TS 6 API for legacy AST shape
…and add a single line of @typescript/typescript6 as a devDependency. This is per-script and only if needed; first preference is to migrate the script to TS 7.
Diff 3.4 — Documentation refresh
docs/architecture/decisions/ADR-018-typescript-7-go-compiler-evaluation.md → add a “Phase 3 completed in PR #NNN” line.
- Any remaining mention of
tsgo in dev docs → simplify to “TypeScript 7 (tsc)”.
Verification (Phase 3)
npm ci --legacy-peer-deps
./node_modules/.bin/tsc --version # → Version 7.x.y
node -e "console.log(require('typescript/package.json').version)" # → 7.x.y
npm run typecheck
npm run lint
npm run validate
npm run test
Acceptance criteria (Phase 3)
Rollback (Phase 3)
git revert to Phase 2 state. The Phase 2 alias-based configuration is the long-lived stable target; Phase 3 is purely a cleanup that can be deferred indefinitely if upstream peer ranges don’t open up.
Cross-cutting concerns
Renovate / Dependabot
- During Phase 1, allow renovate to bump
@typescript/native-preview within the 7.0.0-dev.* train at most weekly; require manual approval for jumps across train boundaries (e.g., dev → rc → 7.0.0).
- During Phase 2, pin
typescript to the alias literal; renovate cannot upgrade an alias automatically but can flag a PR when @typescript/typescript6 itself publishes a new minor.
Editor / IDE guidance
- Phase 1: VS Code “TypeScript Native Preview” extension is opt-in per developer. Workspace settings already use the workspace TypeScript via
node_modules/typescript, so Cursor / VS Code default behavior is unchanged.
- Phase 2: Recommend developers uninstall the Native Preview extension and rely on the workspace TS 7 install instead (avoids the bundled-vs-workspace mismatch).
packages/docs/ workspace
This Docusaurus workspace pins typescript@~5.2.2 for its own build. Out of scope for this plan — leave it as is. If/when Docusaurus’s @docusaurus/tsconfig supports TS 7 cleanly, it can be migrated as an independent change.
supabase/functions/
Deno’s tsconfig.json is consumed by Deno, not the npm typescript package. Out of scope.
Test-completeness audit interactions
Steps 12 (RLS coverage) and 16 (test:baseline:smoke) of the CI build do not invoke tsc; they run via tsx/vitest/playwright. No diffs needed.
tsx, ts-node script runtime
tsx is esbuild-based and ignores the installed typescript version for its transform — out of scope.
ts-node consumes the typescript package at runtime. With Phase 2’s alias, it sees TS 6, which matches today’s runtime behavior — out of scope.
Lint warning budget
npm run lint:ci currently enforces --max-warnings 1100 (see package.json for the authoritative value). The linter’s behavior is type-information-driven (via @typescript-eslint/typescript-estree resolving the project tsconfig with the TS 6 API in Phase 2). No warning-count change is expected.
Risk register
| ID | Risk | Likelihood | Impact | Mitigation |
|---|
| R1 | tsgo emits a diagnostic that tsc does not (or vice versa) | Low (validated 0/0 on this commit) | Medium (could surface a real type error or false positive) | Phase 1 ships behind opt-in script; CI gate is unchanged until Phase 2. Phase 2 verification step diffs the diagnostic outputs explicitly. |
| R2 | A tsgo beta release introduces a regression mid-Phase-1 | Medium | Low (developers can fall back to npm run typecheck) | Pin exact beta version in package.json; require manual approval to bump within the dev train. |
| R3 | typescript-eslint peer range remains stuck <6.1.0 indefinitely | Low | Medium (delays Phase 2 and 3) | The @typescript/typescript6 alias is the documented Microsoft escape hatch — Phase 2 can ship without typescript-eslint changes. |
| R4 | An internal audit script breaks against the TS 7 Compiler API in Phase 3 | Medium | Low (per-script alias workaround in Diff 3.3) | Run the audit-script tests in Phase 3 verification before flipping the dep. |
| R5 | CI cache step removal in Phase 2 leaves stale node_modules/.cache/tsc directories on self-hosted runners | Low | Negligible | One-time actions/cache purge or accept the few-MB carry-over until cache TTL evicts. |
| R6 | vite-plugin-checker invokes tsc directly during dev (npm run dev) and breaks if tsc resolves to TS 7 in Phase 3 while plugin is pinned to TS 6 expectations | Low | Low (dev-only impact) | Bump vite-plugin-checker to a release that supports TS 7 before Phase 3, or omit Phase 3 until it does. |
Tracking artifacts
This plan does not create GitHub issues or specs. When implementation begins:
- Phase 1 PR title:
feat(toolchain): add opt-in TypeScript 7 (tsgo) typecheck:fast script — link this plan and ADR-018 in the body.
- Phase 2 PR title:
feat(toolchain): cut CI typecheck over to TypeScript 7 (tsgo) with @typescript/typescript6 alias — link this plan and ADR-018; mention the gating-criteria evidence (RC/GA URL, typescript-eslint peer range output).
- Phase 3 PR title:
chore(toolchain): drop @typescript/typescript6 alias; let typescript resolve to 7.x natively.
Each PR should update the Status column of the table at the top of this doc.