Skip to content
LogoLogo

VIP-34: compiler and tooling requirements

Status: accepted, 2026-10-29, under Will's standing approval of recommendations ("assume I accept now. I will interrupt you if I need to").

Decision

  1. Diagnostics. Every VibeLang diagnostic has a code (EKxxxx) and a message in the user's words that says what is wrong and how to fix it, with a quick fix wherever the fix is mechanical. TypeScript's own lexical, syntax and static errors on ordinary native syntax keep their TS codes, including independent errors in embedded expressions and types. A rejection that depends on a VibeLang production, placement rule, proposal-specific early error or explicitly adopted reconciliation uses its owning VIP's EK code, even when a TypeScript parser helper implements it. Use an already assigned specific diagnostic for the same rejection; preserve independent native errors.

  2. Codemods. Every breaking change ships a vibelang --fix codemod, so code that compiled under the previous release migrates mechanically.

  3. One formatter. VibeLang's formatter is the only formatter. Prettier, dprint and Oxfmt reach it through plugins, so a repo's existing formatter command covers .vibe files. Under the same compiler/options, formatting preserves parsed program meaning: bindings and scopes, operator and control-flow targets, evaluation order, receivers and call syntax, ordinary runtime values and effects, coldness, failures, interruption and finalizers. It preserves inferred and declared public types, declarations and throws contracts, and diagnostic codes, categories and occurrences; diagnostic locations and quoted source follow the formatted file. Comments and their code associations survive, including JSDoc, compiler/linter/tool directives and directive-prologue meaning. An unparsable file is reported and left unchanged; formatting is idempotent. Semantic migrations and diagnostic repairs belong to vibelang --fix or editor actions.

    Ordinary layout and lexical-spelling normalization may change emitted JavaScript bytes. Function.prototype.toString, generated source text, source offsets, line/column and stack presentation may reflect the formatted text. This source-text boundary never permits changes to ordinary strings/data, tagged-template raw or cooked values, regex meaning, function names or lengths, property names or directive semantics. For example, changing an escaped string into an actual use strict directive changes meaning. Regenerate compiler-generated source-map and declaration-map provenance, including sourcesContent, for the formatted file; never retain stale maps. Preservation tests compare parsed source/emit structure, runtime behavior, types/declarations, diagnostics, comments/directives and maps; arbitrary regex stripping is unsound, and a finite structural oracle is not a proof of all program equivalence. Exact-byte tests remain useful where equality is expected, without restricting advertised native formatter style settings.

  4. Existing tools work. Type checkers work through .d.vibe.ts (VIP-27), and linters, Knip, bundlers and test runners each read .vibe. Every tool is tested against the dogfood repositories.

  5. Severities. Warning severities can be adjusted in the config's "vibe" section (VIP-30). Errors can't be turned off.

  • Amended 2026-10-29: code numbering. Each VIP's rules take one fresh hundred-block of diagnostic codes, EKvvxx for VIP-vv; an older code is kept only where its rule is unchanged. The specification's Appendix B lists every code. Decided by the agent on 2026-10-29 under Will's rule "when I state a principle, the agent derives the concrete rule and acts; I can override later" (decided detail 60).

  • Amended 2026-10-30: warnings never change the exit status. vibelang and vibelang lint exit non-zero only on errors, as tsc and ESLint do by default; a repository that wants a warning to fail CI sets its code to "error" in vibe.severities. Decided by the agent on 2026-10-30 under Will's rule "when I state a principle, the agent derives the concrete rule and acts; I can override later" (decided detail 71).

  • Amended 2026-11-03: imported Effect diagnostics keep the pinned compiler's exit policy. VibeLang's own warnings do not change compiler exit status unless configured as errors. Compiler diagnostics imported under VIP-38 retain the pinned native Effect TypeScript compiler's exit and noEmitOnError policy under the same effective rule, category, inclusion and ignore options; imported warnings affect both by default. The standalone vibelang lint command fails only on errors; warnings or suggestions alone exit 0. This narrows the unqualified 2026-10-30 warning rule in VIP-34 (decided detail 71); it does not restate that old rule as already limited to VibeLang diagnostics. The agent's R3 decision (decide-live-r3-warning-scope.md) applies Will's standing direction, "It is already implied we match typescript" (WILL-ANSWERS.md, 2026-11-03), to the measured pinned compiler. It is an agent derivation from that direction, not a new personal answer from Will.

  • Amended 2026-10-30, clarified 2026-11-03: emit names mirror TypeScript. x.vibe compiles to x.js beside x.d.ts in outDir. With rewriteRelativeImportExtensions, relative specifiers ./x.vibe in emitted JavaScript are rewritten to ./x.js, as for .ts; source-extension admission follows the same tsconfig checks as the native twin (chapter 7). Bundler plugins follow the same names. Decided by the agent on 2026-10-30 under Will's rule "when I state a principle, the agent derives the concrete rule and acts; I can override later" (decided detail 72).

  • Amended 2026-10-30: imported-lint capacity. VIP-38 is the exception to the hundred-code capacity: imported codes use EK38 followed by a decimal ordinal padded to at least two digits, so ordinal 100 follows ordinal 99 in VIP-38's generated table. EKxxxx is a minimum display width for these codes, and EK39xx remains available to VIP-39. Existing assignments remain stable; configuration matches whole codes against the generated table (decided detail 142).

  • Amended 2026-10-31: --fix reaches a fixed point, and a missing runtime is one error. One vibelang --fix run applies fixes until none applies; running it again changes nothing, and a run that has not converged within its pass limit reports the remaining fixes as an error. A compilation that needs vibelang-runtime and cannot resolve it fails with one error naming the package (TS2307), as a missing dependency does in TypeScript, not with an unresolved name per compiler-generated helper.

  • Amended 2026-10-31: --fix scope flags. Two flags scope a vibelang --fix invocation. --fix-migration-only selects migration fixes, excluding TypeScript-form teaching rewrites and prelude/import cleanup. --fix-vip-only selects the VIP migration passes, including the VIP-1/VIP-2 function-kind and try migration, excluding prelude/import cleanup. Either flag requires --fix somewhere in the argument list, before or after it; alone, each is an unknown compiler option (TS5023). This records existing CLI behavior and changes no language rule. Decided by the agent under Will's rule "when I state a principle, the agent derives the concrete rule and acts; I can override later" (decided detail 186).

  • Amended 2026-11-02: the native-resource warning belongs to VIP-34. using in effect code has the default warning EK0801: a failure or interruption can abandon the generator before JavaScript disposal. Its context-safe quick fix uses acquireDisposable inside scoped. The warning never changes TypeScript disposal or starts cleanup by itself. This is VibeLang's TypeScript-form teaching warning, not an imported Effect lint rule. The existing code is retained under the unchanged-code exception. VIP-8's denial authorizes no language rule; its historical warning amendment points here.

Will's words

"make sure we have good compile error messages too"

"They should not only compile and tests pass but we should be able to actually suggest migrating via having everything work including all their existing tooling"

  • Amended 2026-11-01: migration preserves run phase. The explicitly selected --fix-vip-only pass removes a known legacy wrapper's try, even when its success data is an error, a schema error, another Effect or an object with an asEffect member. An originally Effect-returning written or native contract keeps its intentional run. Decide from the original resolved callable and migration provenance; refuse when unknown. Ordinary --fix and --fix-migration-only never reinterpret a valid current try based on its final result type or lack of annotation.

  • Amended 2026-11-01, extended 2026-11-03 for VIP-38’s config migration: which files --fix writes. A run writes only its selected project's owned .vibe/.vibex program sources, including imported owned sources excluded from root discovery, and owned configs for the named legacy-severity, retired-declarationSidecars and VIP-38 EK3012 plugin-entry codemods and VIP-1's --fix-vip-only EK4802 severity override. Config traversal stops at the repository boundary and node_modules; external configs are reported, never edited. Dependencies, typeRoots, bundled libs, foreign TypeScript/JavaScript, declarations, emitted output, build info, lock files and other referenced projects are excluded. In -b, each project fixes its own files in build order. VIP-33's platform repair alone may create a fresh absent sibling .ts file after proving the same run phase, captures, receiver and public types, never overwrite one. Any forbidden write is refused with the not-fixed explanation.

  • Amended 2026-11-01: legacy Effect alias migration. VIP-3 allocates migration warning EK4856 to uses resolving to the retired prelude Effect<A, E, R> alias. Its identity-safe shorthand or qualified-type rewrite precedes coordinated alias removal; user-defined and imported types are unchanged.

  • Amended 2026-11-03: declaration import spelling follows TypeScript. Ordinary package declarations use the native twin’s relative specifiers: ./user.vibe is declared as ./user.ts and ./App.vibex as ./App.tsx; written ./user.js, ./App.jsx, ./user and native ./plain.ts keep their spelling. Synthesized import types use TypeScript’s spelling for the twin under the same imports and tsconfig. Compatibility sidecars instead keep VibeLang source references such as ./user.vibe and ./App.vibex, relocated to the selected compatibility declaration when necessary, to preserve source identity. Neither allowImportingTsExtensions nor rewriteRelativeImportExtensions rewrites declaration specifiers; JavaScript output rewriting is separate. The retired declarationImportStyle paths are ignored with nonfatal deprecation warning EK3011 for every value (VIP-30). This corrects the earlier substitution of VibeLang source extensions into ordinary package declarations, under Will’s standing TypeScript rule: a package ships .d.ts files, not compatibility sidecars.

Amendment: selected ordinary fixes. --fix --only <files> limits repair writes to the listed project modules under the selection contract. Lists may be comma-separated, repeated or read from @list.txt. Validate the full selection before any write. Configs and unlisted files are never repair targets; post-fix compilation retains ordinary outputs and diagnostics. Only selected-file errors refuse repairs, so the report identifies this as partial checking. The option requires --fix and excludes project-wide migration flags.