AI-Native TypeScript — The Guide
01Discipline v0.2 · status: BETA · T2 · TypeScript only (JavaScript gets a separate guide) · supersedes GUIDE-TYPESCRIPT-v0.1
02The projection of the Discipline onto TypeScript.
03This guide covers TypeScript as a typed language in its own right — not JavaScript-with-types-bolted-on; the JS guide is separate and will address the untyped substrate.
04Read 00-MANIFESTO.xml and 02-EXECUTABLE-SCAFFOLDS.xml (the T1 core) first; this guide assumes the central law and the nine scaffold classes.
05A human CAN read and modify AI-Native TypeScript; it may be less comfortable to write by hand than ordinary TypeScript, but it remains ordinary idiomatic TypeScript at the token level.
06What differs is the envelope:
- 07the maxed compiler configuration,
- branded contract-bearing types,
- runtime validation at the erasure boundary,
- executable scaffolds,
- and a fast per-cell verification loop.
08Structurally parallel to rust/GUIDE-AI-NATIVE-RUST.md so the two projections stay comparable — that comparison is how the language-independent T1 layer gets validated.
09Section cross-references to the Rust guide are marked (≈ Rust §N).
10Where TypeScript has no Rust analogue (the configurable compiler, the erasure boundary, type-level testing), the section is marked [TS-specific]; those are the levers and the hazards that make the TypeScript projection heavier on bans and boundary validation than the Rust one, by design (§0).
0. Why TypeScript is special — and the law applied to TS
11Idiomatic inside the file; engineered around the file. (≈ Rust §0)
12TypeScript is deeply in-distribution (among the most common languages on GitHub), so ordinary typed application code and standard generic APIs are safe to be strict in.
13But TypeScript has, arguably, the most powerful and most tractable tooling of any mainstream language (its only rival for analyzability is C++ via the clang/LLVM backend), and that cuts three ways for the Discipline — two advantages and one hazard that has no Rust analogue.
14Advantage 1 — generation-time type-constrained decoding EXISTS for TypeScript. The one result behind much of this work — type-constrained decoding cutting compile errors ~74.8% — was measured on TypeScript (Mündler et al., PLDI'25; the only language with a real implementation, R2C-005 / DR1-014). One of those two evidence ids does not resolve: R2C-005 is authored (##FINDING-R2C-005 in the core ATLAS), DR1-014 is not — the roster runs DR1-013, then DR1-015, and no document in the tree defines a DR1-014. The claim itself stands on the cited paper and on R2C-005; the second id is a dead reference.
15For Rust, that oracle is a multi-year future bet (vibe-tcg Stage 3).
16For TypeScript it is available today: the compiler-as-oracle can run during generation, not only after.
17This flips the tooling story in TypeScript's favor (see §14).
18Advantage 2 — the most mature codemod/AST ecosystem of any language. ts-morph, the TypeScript Compiler API, jscodeshift, and typed ESLint autofix make Classes A (generators), F (structured diagnostics), and especially I (codemods) far more achievable than in Rust — where Class I is [E-hyp] partly because the tooling is immature.
19In TypeScript, scaffolded edit operations are a near-term reality, not a research gamble.
20The hazard with no Rust analogue — types are erased and can be lied to. Rust's types are load-bearing at runtime; TypeScript's are erased.
21The compiler believes a value as Foo assertion with no runtime check behind it.
22This means TypeScript's type system — unlike Rust's borrow checker — is a verifier you can defeat by writing the right two words (as, any, !, @ts-ignore).
23For an AI reader this is dangerous precisely because those words are statistically common in training data.
24The Discipline's central TypeScript-specific job is therefore to forbid the moves that defeat the type system (§8) and to regenerate trust at runtime boundaries (§2).
25The law, projected. TypeScript source under this discipline reads as ordinary idiomatic TypeScript.
26No invented syntax — that would incur the out-of-distribution penalty (EsoLang-Bench: 0–11% on unfamiliar surface; in-context learning cannot teach it).
27But TypeScript has a second OOD edge Rust lacks: its type-level metaprogramming (deep conditional types, recursive template-literal types, HKT emulation) is a sparse long tail that models handle far worse.
28So the law has a TypeScript-specific clause: use the type system's expressiveness up to the point where the types stay idiomatic, and not one step beyond.
29Type-level wizardry is OOD and is treated like unsafe — isolated, justified, deviation-marked (§8).
30The strictness we add lives in the compiler configuration, the runtime boundary, the metadata, and the verification loop — not in clever types.
31Unlike Rust, where the borrow/type checker's strictness is fixed and free, in TypeScript that strictness is configurable, so the first move of the discipline is to turn it all on (§1).
1. Compiler configuration is discipline (the biggest free lever) [TS-specific]
32Rust's strictness is fixed; you get the borrow checker whether you ask or not.
33TypeScript's strictness is configurable — a non-strict config gives "the syntax of static typing with almost none of the safety."
34AI-Native TypeScript therefore mandates the strictest practical configuration, because every flag turned on is intent moved from prose into the compiler (A3).
35The compiler is a free hallucination detector; we give it the maximum to check.
36This is the TypeScript analogue of the Rust guide's "the compiler is a free hallucination detector" (Rust §0) — except here we must opt in to it, flag by flag.
37Mandatory tsconfig floor:
- 38
"strict": true— bundles the eight base flags (strictNullChecks,strictFunctionTypes,strictBindCallApply,strictPropertyInitialization,noImplicitAny,noImplicitThis,alwaysStrict,useUnknownInCatchVariables). - Beyond
strict(NOT included, all mandatory here):noUncheckedIndexedAccess(array/index access yieldsT | undefined— catches a whole class of "it's always there" hallucinations),exactOptionalPropertyTypes(distinguishes absent fromundefined),noPropertyAccessFromIndexSignature,noImplicitOverride. - Defect-catchers:
noUnusedLocals,noUnusedParameters,noFallthroughCasesInSwitch,noImplicitReturns,allowUnreachableCode: false. - Forward-looking erasure flag:
erasableSyntaxOnly— restricts to syntax that erases cleanly (forbids runtimeenum/namespace), keeping TypeScript a thin typed layer over JavaScript. This matters now that the native compiler (TS 7 / "Corsa") and Node's type-stripping treat types as erasable annotations. It is also AI-native: it removes constructs whose runtime behavior diverges from their syntax.
39The tsconfig is a versioned artifact of the discipline (a card-checked file), not a per-developer preference.
40Loosening any mandatory flag requires deviates + reason.
41Rule: the strict floor is set by config and maxed out; "we use strict TypeScript" while disabling individual flags or bypassing them with @ts-ignore is a discipline violation.
2. The erasure boundary: regenerate trust at runtime edges [TS-specific]
42Because types vanish at runtime, the boundary between the typed interior and the untyped exterior (network, JSON, process.env, user input, third-party any) is where the type system's guarantees end and a model's false confidence begins.
43At that boundary the Discipline requires runtime validation from a single source that is simultaneously the static type and the runtime checker — a schema library (Zod, Valibot, ArkType, TypeBox).
44One declaration yields both type User = z.infer<typeof UserSchema> and UserSchema.parse(input).
45This fuses three scaffolds (A generator + B typed surface + C runnable contract) into one TypeScript-shaped artifact — the densest single move in the projection.
46Rule: untyped external data enters as unknown (never any) and is narrowed only through a runtime validator or an assertion function (§5, §8); a bare as on external data is forbidden (§8).
47The schema is the boundary's contract; inside the boundary the compiler is trusted, outside it is not.
48This is the TypeScript form of "the compiler is a verifier we maximize" — we re-establish the verifier's guarantee at exactly the points where erasure would otherwise silently void it.
3. Cells, closure, ownership (≈ Rust §1)
49The cell is the unit of modification, closed under paging (R3-001): it declares its full semantic dependency set so a pager can assemble sufficient context mechanically.
- 50Granularity: a module (file) or a small directory with a single public entry (
index.tsas the seam), with promotion criteria to a larger cell when cohesion demands. - Explicit imports only; no barrel-file ambient re-export sprawl that hides the dependency graph. Cells import seams + core, never sibling cells' internals (R-002).
- Ownership aligns with file boundaries (R3-013): one cell = one file-set with one public entry. God-modules and giant barrel files serialize the swarm and obscure closure — an anti-pattern. Shared facts go to append-only ledgers, not shared mutable modules.
- Ambient coupling — module-level mutable singletons, global augmentation, ambient
declare global, config read outside the composition root — breaks closure and is forbidden outside the composition root (R3-001).
4. Surface form: naming, position, and the structural-typing trap (≈ Rust §2)
- 51Names are token programs (R3-004, R-020): one name = one referent across the contract surface; no shadowing, no synonym pairs; structural tokens from a closed vocabulary. Length is free; ambiguity is not. (Short closure-local bindings are exempt — scope the rule to contract surfaces.) The computed-name half of R3-004 is enforced on Rust and Go by
cell-name-is-computed; TypeScript is outside that rule by record, not by oversight — a TS cell carries noseam/variantmanifest, so there is nothing to compose, held by the parity lawspec://org.vibevm.ai-native/core-ai-native/00-MANIFESTO#PARITY-GAP-IS-NEVER-SILENT. The remaining halves (closed vocabulary, one referent, no synonyms) are unbuilt in every projection and have no backlog entry yet. - The family-prefix rule (owner policy, 2026-07-07; supersedes the
-typescriptsuffix rule). Every named surface of the TypeScript discipline is language-FIRST: it carries the family stemtypescript-ai-nativeas a prefix, not a-typescriptsuffix (PROP-028 §2.4). The umbrella binary is the family name itself (typescript-ai-native, overinit/floor/ …; its cratetypescript-ai-native-cli); the standalone tools and their crates sharetypescript-ai-native-<role>(typescript-ai-native-conform,typescript-ai-native-specmap,typescript-ai-native-tcg, and the librariestypescript-ai-native-conform-frontend,typescript-ai-native-specmap-scan,typescript-ai-native-tcg-bridge,typescript-ai-native-extract-bridge); the server package/crate/binary istypescript-ai-native-mcpand the agent-visible server name is the family (typescript-ai-native); the skills aretypescript-ai-native-sweep/typescript-ai-native-terraform; the token brief istypescript-ai-native-tcg.xmlbesiderust-ai-native-tcg.xml. Language-NEUTRAL artifacts stay outside the stem (the shared engine crates take the core stemcore-ai-native-*). - Contract-first ordering within an item (R3-002): the exported type/signature, then its invariants, then its error contract, then one canonical example precede the implementation. Autoregression makes reading order conditioning order; intent goes first.
- Position is a resource (R3-003): module-level invariants and the public surface live at the top; prefer more, smaller, single-purpose modules over long files at equal token mass. This is now enforced, not promised: alongside the long-standing
file-lengthcheck,invariant-comment-positionfires through the normal gate when a comment whose marker is in the configured vocabulary lands in a file's middle third — linelwithlines/3 < l <= 2·lines/3(integer-divided; for a 120-line file, lines 41–80) — with the remedy move-to-edge-or-split. For.tsthat gate runs through thetypescript-ai-native-conform-frontendcrate (typescript/tools/conform-frontend-typescript.xml) feeding the same language-neutral engine; the marker vocabulary and the file-length floor are rootconform.tomlkeys shared with the other stacks —invariant_comment_markers(default the five labeled markersINVARIANT:/WARNING:/PANICS:/MUST:/NEVER:— a marker is a labeled tag, not a bare word, so the colon is the markup signal) andinvariant_comment_min_file_lines(default 120 — below it the whole file is skipped, a «third» meaning nothing). Test-context markers are out of scope. - Uniformity is load-bearing (R3-006, H6): one idiom per operation. The codebase is the few-shot prompt; a second coexisting idiom becomes false training signal and propagates. Legitimate exceptions are MARKED (
deviates) so they do not propagate as imitation. - The structural-typing trap (TypeScript-specific). TypeScript is structurally typed: two types with the same shape are interchangeable, so a model can silently pass a
UserIdwhere anOrderIdis expected if both arestring. Rust gets nominal safety free via newtypes; TypeScript must recover it manually through branding. Rule: identifiers and other meaning-bearing primitives crossing a seam are branded (type UserId = string & { readonly __brand: 'UserId' }, or a branding helper) so the wrong same-shaped value failstsc. This is the single most important TypeScript-specific safety move and is the basis of scaffold card B (§5). It is the manual recovery of the nominal safety Rust's newtypes give for free.
5. The nine scaffolds in TypeScript (≈ Rust §3)
52Each is a card in this package's cards/ (the TypeScript projection of the language-neutral scaffold catalog 02-EXECUTABLE-SCAFFOLDS.xml); here is the TypeScript shape and the rule.
- 53A — Generators / codegen (
scaffold-a-generators).ts-morph/ Compiler API generators; types generated from a single schema source (Zod→infer, OpenAPI/GraphQL/Prisma→types);satisfies+as constfor checked literal tables; template-literal types as bounded type-level generation. Committed output is plain idiomatic TS; the generator carries the structural decision. TypeScript's codegen is mature — favor it. Rule: where an artifact is mechanically derivable from a smaller spec, ship generator + committed output + determinism check, not hand-maintained output (A3). - B — Typed surfaces / branding / typestate (
scaffold-b-typed-builders). Branded types for nominal safety over structural typing (§4 — the key TS move); discriminated unions; phantom-type-parameter builders for call-order protocols;satisfiesfor exhaustiveness; sealed unions; no boolean/positional argument soups. Make the statistically-likely wrong same-shaped call failtsc, not a runtime assert. Rule: seam protocols are encoded in types, not docstrings (R3-008; ~94% of compile errors are type-level). - C — Runnable contracts (
scaffold-c-runnable-contracts). Assertion functions withassertspredicates (function assertIsUser(x: unknown): asserts x is User) — uniquely TypeScript: one function that BOTH checks at runtime AND narrows the static type;tiny-invariant; Zod/Valibot schemas as executable contracts at boundaries; invariants restated at use sites (R3-009). Rule: every load-bearing invariant is witnessed by a runnable assertion where it is relied upon, not only documented at definition. - D — Differential / characterization oracles (
scaffold-d-differential-oracle).fast-checkproperty-based differential harnesses (old-vs-new);vitest/jestsnapshot tests for opaque legacy behavior (must fail loudly when stale, never auto-update). Rule: no replacement of a non-trivial cell merges without a differential or characterization oracle against prior behavior (R-040). The modification-specific safety net (§11). - E — Per-cell fast loop (
scaffold-e-fast-loop).tsc --noEmitper project (project references for isolation) +vitestfor the cell; the native compiler (TS 7 / "Corsa", ~10× faster checking) makes per-cell first-signal sub-second — a strong substrate. The agent loop is edit →tsc --noEmit -p <cell>+vitest run <cell>→ read structured diagnostic → edit; first signal < ~60s (R3-007). Rule: whole-repo CI is not an agent loop; the per-cell loop is the substrate that makes every other scaffold's signal fast enough. - F — Structured, REQ-citing diagnostics (
scaffold-f-structured-diagnostics). The third channel this scaffold promises — a project's own checks whose messages name the violatedspec://REQ and the fix surface — is BUILT for TypeScript: the flat-config ESLint plugin@org.vibevm/eslint-plugin-ai-native(typescript-ai-native-lang/v1.0.0/tools/eslint-plugin-ai-native/) ships one rule,diagnostic-cites-req, authored throughESLintUtils.RuleCreator. Every message the rule emits routes through ONE grammar helper,src/req-message.ts(reqMessage/matchesReqGrammar), which reproduces the engine pairreq_message/matches_req_grammarverbatim — a second spelling of the grammar is exactly the bug this channel exists to prevent, so the rule cannot drift from the engine. Wiring is the project's own config, not a floor step: the demo loads it throughresearch/ts-demo/eslint.config.js(plugin keyai-native,"ai-native/diagnostic-cites-req": "error"); the floor'seslint .step is unchanged and picks the plugin up through that config. The Compiler API's own diagnostics are already coded (TS2322 etc.) — wrap them with REQ context, do not replace them. Rule: every project-raised check emits "violates REQ <uri>: <why>; fix surface: <where>", never bare free text (R3-011); error text is the agent's percept. Honest limits of this syntactic heuristic, recorded not claimed (thets-seam-error-cites-reqprecedent — never a silent claim): it sees string literals only — concatenation, interpolated template literals, variables, and imported constants are not tracked (no value tracking) and pass silently; an "Error heir" is recognised by the callee NAME endingError/Exception, not by walking the class hierarchy — a classextends Errorunder another name is missed, aFooErrorthat does NOT extendErroris matched (the grammar burden is identical either way); an object literal'smessage:field is seen only when the literal is thrown directly; andAggregateError's second argument, message-wrapping helper functions, andthrow <non-string>are outside the net. The parity this rests on — no projection enforces the discipline more weakly than another without a recorded reason — is a discipline law in the manifesto (spec://org.vibevm.ai-native/core-ai-native/00-MANIFESTO#PARITY-ACROSS-PROJECTIONS); the asymmetry that TypeScript has this channel built and Rust/Go do not yet, each for a recorded reason, is held by its sibling law (spec://org.vibevm.ai-native/core-ai-native/00-MANIFESTO#PARITY-GAP-IS-NEVER-SILENT), with the route recorded asBACKLOG.md {#b-050}. - G — Executable examples (
scaffold-g-doctests). Twoslash (type-checks code in documentation — the TypeScript doctest equivalent);@exampleJSDoc blocks validated by tooling;expectTypeOf/tsdfor type-level examples;examples/cells built in CI. Rule: every public seam carries ≥1 type-checked example of canonical use; an example that lies fails the build; a prose snippet that lies ships (R2C-004, H4). - H — Local simulators / reference models (
scaffold-h-simulators). In-memory fakes (MSW for network, fake implementations of seams);.d.tsdeclaration files as shape models; runnable reference implementations of protocols/state-machines the reader can step through. Rule: subsystems with non-obvious dynamics ship a runnable model or fake, not a prose description (execution-prediction is where weak models are weakest — DR2-019, CRUXEval ~63% even for strong models). - I — Scaffolded edit operations / codemods (
scaffold-i-codemods).ts-morph/jscodeshiftcodemods for "add a cell," "register a variant," "rename across the seam"; typed ESLint autofix as constrained one-shot transforms. TypeScript's biggest scaffold advantage — mature codemod tooling makes Class I far more achievable here than in Rust. Rule (provisional, [E-hyp]): a capability-demanding multi-file edit is offered as one parameterized checked operation. The tooling-immaturity half of Rust's [E-hyp] does not apply (the ecosystem is mature); the weak-agent-can-parameterize half remains open — validate in pilot.
6. Errors as contract surface (TypeScript has no checked exceptions) (≈ Rust §4)
54throw in TypeScript is untyped — you can throw anything, and the type system is blind to it.
55So a thrown error is invisible to a reader and to the compiler.
56The Discipline therefore makes failure a value, not a throw, on the contract surface: a discriminated union Result<T, E> = { ok: true; value: T } | { ok: false; error: E } (or neverthrow/Effect), with E a discriminated union of named error variants carrying spec:// REQ references.
57Exhaustiveness over E is enforced by a satisfies never / assertNever check in the default branch.
- 58
throwis reserved for truly unrecoverable defects (the panic analogue), at the binary edge. - Fallible seams return
Result, never aPromise<T>that rejects with an untyped error. - This is the TypeScript projection of Rust's "one
thiserrorenum per layer; variants carry REQ edges; panics are defects" — the discriminated-unionEis thethiserrorenum, and the untypedthrowis the panic.
59Rule: failure on a seam is a typed value with REQ-citing variants; the exhaustive switch over the error union is checked at compile time (R-010, projected).
60The gate now checks this — the ts-seam-error-cites-req rule flags a discriminated-union error type alias E whose variants carry no spec:// REQ. Honest limits, recorded (never a silent claim): it detects Form-1 only — a type alias whose RHS is a union of object-literal members, each carrying a discriminant property from the closed set { kind, tag, _tag }; the error position is taken from the alias name (*Error / E), NOT from the second argument of Result<T, E> (the single-file extractor does not resolve references); and the discriminant set is closed to { kind, tag, _tag }, so a union discriminated any other way is not matched — what the heuristic cannot see is recorded here, not claimed. The parity behind it — no projection enforces the discipline more weakly than another without a recorded reason — is a discipline law in the manifesto (spec://org.vibevm.ai-native/core-ai-native/00-MANIFESTO#PARITY-ACROSS-PROJECTIONS).
7. Registry, flags & the composition root (≈ Rust §5)
61The Rust guide forbids if flag in domain logic; the same rule holds in TypeScript, and the erasure boundary (§2) sharpens it.
62Flags and external configuration are read once, at the composition root (the app/entry cell), narrowed there through a schema (so process.env — pure untyped exterior — is validated and typed exactly once), and a registry (a typed as const map, or a discriminated-union selector) chooses the cell/strategy.
63No if (flag) scattered through domain cells (R-001).
64An explicit switch over a discriminated config union at the composition root, exhaustiveness-checked, beats string-keyed dynamic lookup and module-load side effects — "one switch is the system's table of contents."
65Two tiers, mirroring Rust's cargo-features-vs-runtime-flags split:
- 66Build-time: bundler
define/ dead-code elimination / env-gated conditional compilation (code physically absent from the bundle). The TypeScript analogue of cargo features. Specified, not built: no bundler is configured anywhere in this discipline's reach.research/ts-demo, the one TypeScript consumer, has no bundler among its devDependencies (eslint, prettier, typescript, typescript-eslint,@types/node) and no build script — itsfloorandtestscripts runtypescript-ai-native floorandnode --test. Nothing in the stack or the host reads adefinetable or performs dead-code elimination, so this tier has never been exercised. - Runtime: a registry object selects a cell/implementation at run time (code present, cell chosen). The TypeScript analogue of runtime flags. Built and instantiated (B-039):
research/ts-demo/src/main.tsis the composition root — it readsprocess.env.TS_DEMO_GREETINGonce, narrows it to a typed mode, and dispatches to thegreeting/farewellcells through a typedas constregistry. Thets-flag-sitesrule in the conform engine (mounted by the TypeScript gate when[typescript] composition_rootis set) now polices the root: an env/config read in any scanned file outside it is a finding. The rule's honest limit — it catches the env/config half, not theif (flag)half — is recorded at §FLAG-REGISTRY-IS-TYPED-DATA-WITH-PROVENANCE below.
67The flag/registry is typed data with provenance, birth, and sunset — a branded or as const table, not stringly-typed ambient lookup, and never a module-level mutable singleton (which would breach §3 closure). Built (B-039), TS-shaped: the demo registry is research/ts-demo/src/main.ts (a typed as const dispatch table — the provenance the line normatively asks for), and the ts-flag-sites rule in the conform engine polices WHERE the exterior is read — process.env / import.meta.env reads are legal only in the file named by [typescript] composition_root, and any read in a scanned file outside it is a finding. Honest limit: the rule catches the mechanical half (config/env reads outside the root) but NOT the if (flag) half — detecting a conditional on a flag needs flag identity, and no flag table flows through the facts yet, so a bare if (someValue) in a domain cell is undetected. The Rust-shaped FlagSites (R-001, keyed on construction sites) remains Rust-only; this is its TS-native twin keyed on the read sites the ts-tsc frontend can actually see.
68Rule: flags are read at the composition root and dispatched through a typed registry; if (flag) in a domain cell, or reading config outside the root, requires deviates + reason.
8. Bans and their escape hatches — the TypeScript unsafe set (≈ Rust §6)
69These are the moves that defeat the erased type system — the TypeScript analogue of Rust's unwrap/inline-asm/unsafe ban set.
70Forbidden by default in domain code; legal only with the escape hatch shown and a recorded reason (deviates):
- 71
any— disables checking and propagates transitively (oneanypoisons everything it touches). Banned. Escape hatch:unknown+ runtime narrowing; or, at a genuine third-party boundary, a localized// eslint-disable-next-line @typescript-eslint/no-explicit-any -- reasonconfined to one line. - Unchecked
asassertions — the erasure hazard's sharp edge:data as Usermakes the compiler believe a lie. Banned on untrusted/domain data. Escape hatch:asonly after a runtime check, or the always-safeas const; cross-type assertions requiredeviates+ reason. (Note: the common "fix"key as keyof typeof objto silencenoUncheckedIndexedAccessis exactly this hazard — narrow instead.) - Non-null assertion
!— claims non-null without proof. Banned. Escape hatch: narrowing, or an assertion functionfunction assertDefined<T>(x: T): asserts x is NonNullable<T>. @ts-ignore— silences the compiler invisibly and stays silent even after the error is gone. Banned outright. Escape hatch:@ts-expect-error -- reason, which fails if the error disappears (it cannot rot silently) — the only acceptable form.- Type-level metaprogramming beyond the idiomatic (deep recursive/conditional types) — OOD tail (§0). Escape hatch: isolate genuinely needed type-level code behind a documented boundary with
deviates+ reason; never spread it through domain types. - Runtime
enumandnamespace— don't erase cleanly; forbidden undererasableSyntaxOnly(§1). Replacement:as constunion objects and ES modules.
72A ban with no escape hatch is a discipline bug; a deviation with no reason is a code bug.
9. Metadata layer (specmap in TypeScript) (≈ Rust §7)
73spec:// URIs carried by JSDoc tags (/** @implements spec://... */), TC39 decorators (stage-3 ES decorators) on classes/methods, or a sidecar mapping; .d.ts files as a natural meta/shape layer.
74The edge kinds mirror PROP-014 (implements | verifies | documents | deviates | informs, ≤3 edges per item, the specmark budget); two-tier revisions (author-asserted semantic revision + content hash) with asymmetric invalidation (spec bump → edges suspect; code change → edges stay valid); a derived deterministic committed index; an orphan ratchet; deviates requires a reason.
75The metadata is the authored retrieval index (R3-012): stable anchors + a uniform one-line what/why per exported symbol, in a fixed grammar the pager consumes.
76(Decorators have runtime cost and partial erasure — prefer JSDoc tags for inert metadata to stay erasure-clean under §1.)
10. Prose discipline (the asymmetric hazard) (≈ Rust §8)
77Wrong prose is worse than no prose (R2C-004, H4): a model conditions on in-repo text with high trust, so a lying comment is adversarial input, and the harm exceeds that of absence.
78TypeScript-specific sharp edge: a JSDoc @param/@returns that has drifted from the signature is a lie the model trusts over the (correct) types.
79Rule: behavioral claims near code are machine-checked — backed by Twoslash/@example (type-checked) or expectTypeOf (type-level checked) — or explicitly trust-labeled (verified / unverified / aspirational); JSDoc that merely restates the types is duplication (a defect) — let the types speak.
80Misleading console.log/error strings count too (the harm is the false claim, not the syntax).
81TSDoc remains the human detail layer; duplication with the spec is a spec defect.
11. Replacement protocol (≈ Rust §9)
82Replacing a cell ships a differential oracle (Class D, §5) against the old cell — fast-check feeding identical generated inputs to old and new and asserting equal outputs — plus the @verifies spec://… edge (§9).
83Characterization goldens (vitest snapshots) pin opaque legacy behavior; goldens must fail loudly when stale (run under --ci; never --update silently auto-rewriting).
84The characterization variant enshrines current behavior including its bugs — pair it with a spec edge marking which behaviors are intentional vs incidental.
85This is byte-for-byte the Rust replacement protocol (R-040) with TypeScript tools; it is the one place the modification-time safety net is mandatory rather than advisory.
12. Test matrices and type-level testing (≈ Rust §10 + a TS-unique scaffold)
86Test matrices. Declared test matrices, never an implicit 2^n.
87vitest/jest test.each / it.each over a named, bounded case table (as const so the table is typed and exhaustiveness is visible); fast-check for behavioral surfaces; the differential oracle (§11) covers replacement; per-cell vitest runs in the fast loop (§5).
88The matrix is authored data, not a combinatorial explosion the reader must hold in their head (R-060, projected).
89Type-level testing (a TypeScript-unique scaffold). TypeScript can assert type relationships at compile time — a class of runnable contract no mainstream language has so readily, and a place where TypeScript's expressiveness pays the discipline back instead of costing it.
90expectTypeOf<X>().toEqualTypeOf<Y>() (vitest), tsd's expectType, and @ts-expect-error as a negative assertion let you test that a generic, a branded type (§4), or a discriminated union (§6) behaves as intended before any code runs.
91Rule: public generic/branded/union surfaces carry type-level tests asserting their key relationships; these run in the Class E loop (a type-level test that regresses fails tsc).
92This is Class C/D applied to the types themselves — Rust has no comparable readily-available form, so it is additive over the Rust projection, not a mirror.
13. How a weak reader actually uses this guide (≈ Rust §11)
93The weak swarm does not read this guide.
94It receives, per edit, the Band-3 ops extract of whichever cards' triggers fire — a small, activation-matched set (lazy-push, R3-014; minimal sufficiency, AGENTbench).
95This guide and the cards are the authoring/review artifact for the strong author and the human; the runtime surface for the weak reader is "the right TypeScript card's routine + checker, when its trigger fires" — and for .ts edits that is a card from this package's cards/, never the Rust core's.
96Cross-cutting concerns the per-edit loop cannot hold are swept by raids (03-RAID-PLAYBOOK.xml).
14. Tooling roadmap pointer (the tcg line) (≈ Rust §12)
97The tcg line has TWO briefs, split by where the intervention happens:
- 98
typescript/tools/vibe-agentic-tcg-ts.xml— SHIPPED (the agentic oracle): a long-lived language-service oracle the agent CONSULTS — validate-an-overlay / scope / type-valid completions / quick info at millisecond latency, discipline-enriched by the same conform engine as the gate — delivered astcg_*MCP tools and one-shottypescript-ai-native-tcgCLI forms. Mechanisms:mechanisms/TCG-ORACLE-v0.1.xml,mechanisms/TCG-PROTOCOL-v0.1.xml. It is the generation-time complement to the post-generationtsc --noEmitloop (Class E): the loop stays the GUARANTEE; the oracle removes red iterations before they happen. typescript/tools/typescript-ai-native-tcg.xml— VERY-FAR-FUTURE (token-level): logit masking to type-checker-validated, discipline-conformant continuations, by construction. It waits, owner-dispositioned, on an inference substrate (vibe-llmis a stub; hosted agent APIs never expose logits) and will reuse the SAME oracle as its completability answer when it comes.
99The TypeScript tooling asymmetry, stated honestly. The PLDI'25 result proves type-constrained decoding works for TypeScript (75.3% (synthesis) / 70.2% (translation) compile-error reduction; ~94% of TS compile errors are type-level) — but its repository is inspiration-only under the clean-room rule (never a code source; the algorithm would be reimplemented from the paper in structurally different code).
100What makes TypeScript first anyway is the compiler itself: unlike Rust at decode time, TypeScript exposes its checker programmatically (Compiler API / language service), so our oracle stands on the REAL checker rather than a rebuilt subset.
101Combined with the mature codemod ecosystem (Class I), that makes TypeScript the strongest near-term pilot for the swarm story, second only to where the algorithmic core lives (Rust/vibevm).
102The honest counterweight. TypeScript's erasure and structural typing mean its default safety is lower than Rust's — more must be done manually (branding, runtime validation, banning as/any) to reach the same floor.
103TypeScript gives more tooling leverage and more expressiveness, but it also gives more rope.
104The Discipline's TypeScript projection is heavier on bans and boundary validation (§2, §8) than the Rust projection precisely for this reason — and that asymmetry, not a failure of mirroring, is the genuine T2 content.
105The standing open question (shared with Rust). The 74.8% / type-constrained-decoding result is a generation-time result; the Discipline's central unproven bet is whether scaffolds help comprehension and modification of in-distribution code, not just generation.
106A type oracle makes a weak agent write well-typed TypeScript (by construction at token level; by cheap consultation agentically); whether it then modifies existing TypeScript safely is still the pilot's job — and erasure means well-typed code can still lie at runtime if an as slipped through, which is why the §8 ban on as matters even with the type oracle on.
107The agentic battery (two arms, weak model, mechanical verification — see the sibling brief's «Staged ambition», vibe-agentic-tcg-ts.xml §4) is the first standing measurement of exactly this question.
15. Wiring a consumer (the shipped toolchain) (≈ Rust §13)
108The stack ships the toolchain as runnable code (PROP-024); a consumer wires it in five moves:
- 109Install the stack —
vibe installwithstack:org.vibevm.ai-native/typescript-ai-native-langin[requires].packagesmaterialises the slot undervibedeps/(the neutral engines ride along as vendored copies; the slot is its own Cargo workspace and builds standalone). - Get the binaries —
cargo install --path vibedeps/<stack-slot>/crates/typescript-ai-native-cli(plustypescript-ai-native-conform/typescript-ai-native-specmapif you want the narrow engines on PATH), or run in place:cargo run --manifest-path vibedeps/<stack-slot>/Cargo.toml -p typescript-ai-native-cli --bin typescript-ai-native -- <args>. - Project toolchain — node ≥ 22.6 (strip-types runs
.tsdirectly;node --testis the default runner) andnpm install -D typescript prettier eslint typescript-eslint. The structural gate parses through the PROJECT's owntypescript— the same install thetscfloor step uses, so the gate adds no new dependency. - Bootstrap —
typescript-ai-native initwrites conform.toml ([typescript]: roots,cells_dir, seam), specmap.toml (namespace + discovered[[external_specs]]), both ratchet baselines, and the BROWNFIELD registries; thentypescript-ai-native specmapmints the index andtypescript-ai-native floorruns the seven steps. Adoption on a brownfield tree: the/typescript-ai-native-terraformskill. - The generation-time oracle (optional but cheap) — the stack's 4th binary,
typescript-ai-native-tcg, answers validate/scope/complete/type over in-memory overlays (§14). One-shot from anywhere:vibe bin exec typescript-ai-native-tcg -- validate src/cells/<cell>/index.ts --json. Warm, inside an agent session: thetcg_*MCP tools (vibe mcp serve; vibevm PROP-026) hold a persistent oracle per language, so consulting the type checker before an edit costs milliseconds. The floor stays the truth; the oracle exists so the floor stays green on the first try.
110Gotchas the fresh walks caught:
- 111a repo that also carries Rust keeps
[workspace] exclude = ["vibedeps"]; node_modules/is gitignored but lockfiles are committed;- the extractor materialises content-addressed under
target/conform/ts-extract/— clean builds re-materialise it automatically.
16. Sweep idioms (≈ Rust §14)
112The recurring posture is the shipped Sweep Playbook driven by /typescript-ai-native-sweep; the TypeScript-specific idioms:
- 113Danger-band splits keep traceability: the new module gets its own file-level
@scope(or carries the moved exports'@implementstags) so the orphan ratchet never regresses on a refactor. - Suppression drains:
@ts-ignore→@ts-expect-error -- reasonis always a strict improvement (it fails when the error goes); an unreasoned@ts-expect-errorin the health census is unrecorded testimony — reason it or fix it. - Unsafe-set drains go type-first:
any→unknown+ one narrowing helper reused everywhere (uniformity is load-bearing); a cross-typeasat an erasure boundary becomes a schema parse;!becomes anassertsfunction. - Floor disablement is debt: every
[[typescript.floor_disable]]entry prints on every run — re-question the reasons weekly; an empty list is the exit criterion the terraform aims at.