Skip to content

Latest commit

 

History

History
44 lines (32 loc) · 8.29 KB

File metadata and controls

44 lines (32 loc) · 8.29 KB

AGENTS.md

Philosophy

One path. No fallback. No ceremony.

  • One path — no mode flags or opt-out env vars. A fallback nobody sets is an untested code path that rots while the default gets all the coverage. If two behaviors are both defensible, pick one and delete the other; verify it with the gates, not with a switch.
  • No fallback — do not degrade around hypothetical edges. Enumerate real edges and kill them by construction (idempotency, mechanism guarantees). If a failure mode cannot occur on the supported floor (Node ≥ 20, Go 1.26, current V8), do not code for it — fail loudly so real problems surface instead of hiding behind a silent degraded mode.
  • No ceremony — if the language/runtime contract already provides the behavior (e.g. returning a NULL-initialized value propagates a pending napi exception), do not restate it in code. Comments explain why (ownership, contracts, invariants), never what the next line does.

Reference implementation of the style: patches/typescript-go/overlay/bridge/napi_shim.c.

Convergence rules (so the tree never needs a sweep)

  • Env knobs need a committed consumer — otherwise they're deleted on sight. One name per knob.
  • No debug scaffolding in product code — instrumentation and its witness live and die together.
  • The pinned bundle is the floor — call its API directly; no presence guards or compat branches. A mismatch must throw.
  • Abandoned approaches die in the same commit — dead code is part of the pivot's diff.
  • Triage scripts are disposable — wired gate, exit-coded probe, or deleted when the issue closes.
  • One harness, parameterized — never fork a script to vary it.
  • Mechanism changes sweep their vocabulary — grep the old name across patches/, tools/, comments in the same change.
  • Deletions are verified by the gates, not switches.
  • No framework hardcoding — behavior keys on registered mechanisms (extraFileExtensions / supportedTSExtensionsFlat), never on literal framework suffixes or names (.vue, vue, svelte…). If a fix needs a framework literal, the mechanism is what's missing.
  • One Go→JS file-name boundarywireFileNameToHost (tsgoLibPaths.ts) is the only normalization point for wire-decoded file names; every new transport/payload must funnel through it at its decode point, call sites never re-normalize. (Key folding is canonicalSourceFilePath; JS→Go input is resolveHostFileName.)
  • Review the landed diff for elegance before committing — top smells: duplicated truth (call the source instead), caches that hide the problem, hidden contracts. Don't defend the first draft.

Repo in one paragraph

TNB is a tsgo-backed TypeScript fork: upstream microsoft/TypeScript and microsoft/typescript-go pinned as submodules, a small patch set on top (patches/), and a NAPI bridge (bridge.node) that runs the tsgo checker in-process. Sources of truth: patches/typescript/overlay + patches/typescript/*.patch (edit via the submodule then npm run save-ts-patches), patches/typescript-go/overlay + *.patch (npm run save-patches). Build: npm run build:lib (fork bundles) and npm run build:bridge (NAPI addon).

Gates (run before committing behavior changes)

  • npm run check:lib / check:enums / check:sourcefile-guard
  • Witnesses: node tools/triage-sim-xfile.mjs, triage-sim-edit.mjs, triage-quickinfo-emptyparity.mjs, triage-crossgen-reuse.mjs (issue #11: cross-generation RemoteSourceFile reuse + edit invalidation), triage-framework-checks.mjs (svelte-check/astro-check/glint vs stock parity — cached fixture installs under /tmp/tnb-fw-fixtures), f2hl/f2r6 series, triage-refs-exportspec.mjs, triage-display-tokens.mjs, triage-computed-literal.mjs, triage-napi-fuzz.mjs, triage-arena-parity.mjs (arena-vs-JSON transport differential: every arena-capable method's result byte-equal across the two transports), triage-bridge-thread-race.mjs (worker_threads hosts: cross-thread text-response integrity + teardown-unload survival), triage-nuxtui-exportstar.mjs (issue #26: ./X.vue specifier with an on-disk X.d.vue.ts must resolve to the declaration, not the literal SFC — the @nuxt/ui dist pattern), triage-program-info.mjs (program-info API stock differential: missing paths, include reasons + explainFiles reason lines, resolution caches, ATA names/resolutions, usesUriStyleNodeCoreModules, classifiable names, isEmittedFile, ref lookups — the TnbProgramCoverage surface), triage-type-field-audit.mjs (type-payload exhaustiveness: every data field stock puts on a Type must cross the bridge equal or carry an inline exemption — the #16/#19/#22/#23 gap class), triage-checker-differential.mjs (checker-API stock differential: one self-contained corpus queried through stock 6.0.3 and the TNB watch/builder path, byte-equal canon per method@location; known divergences keyed in-file with upstream/lib-delta/tracked-bug classes, stale exemptions fail), triage-electron-abi.mjs (wired in CI via npm i --no-save electron@42), triage-cli-only-flags.mjs (CLI-only options vs stock differential: --strict/--noUncheckedIndexedAccess/--target/--module family — effective options ride updateSnapshot to Go; a flag that only exists on the CLI must move diagnostics exactly like stock), triage-eslint-typeref-target.mjs (issue #35: typescript-eslint's isTypeReference→target→getSymbol() chain — nested Type/Symbol wire handles must read back as registry-identical objects, never raw ids, including getBaseTypes() results that never pass an adapter method)
  • Big net: sim-nav vs test/baselines/ (npm run check:sim-nav — 4 parallel shards from isolated tools copies, merged and baseline-gated by tools/sim-nav-parallel.mjs) — no new divergences allowed. Baselines are slim (keys/counters/labels only) with test-workspace-relative keys — machine-local paths in a committed baseline make the nightly gate red by construction. Refresh by running check:sim-nav then node tools/sim-nav-merge.mjs --slim /tmp/tnb-simnav-merged-4.json test/baselines/nav-results-<sha>-t<N>.json (refuses absolute keys), re-pin BASELINE in .github/workflows/nightly.yml, delete the superseded baseline. The local gate cannot catch non-portable keys (same machine, same prefix) — nightly is the clean-machine gate, check its result after a refresh.
  • volar suite: npm test in the volar checkout (205 tests)

Conventions

  • Commits: plain-language, component-prefixed (fix(bridge):, perf(bridge):, ci:, docs(readme):), says what and why, no filler.
  • Issue replies: written by a human or reviewed by the maintainer before posting. No templated "thanks for the detailed report" tone.
  • Memory and behavior parity with stock typescript are the two hard constraints; measure before claiming either. TNB fixes only bridge-introduced divergences. That category includes the bridge contract surface (stock-API semantics the bridge must implement so stock's own services/session code runs correctly — additive APIs like getSymbolOfExpando, wire shapes, symbol identity) and bridge-only-triggered engine bugs (engine invariants no upstream caller relies on but the bridge's call patterns violate — fix locally, link the upstream report).
  • For engine-owned behavior, the reference is tsgo's actual behavior (its own LSP/IDE output), not stock — deliberate engine design is documented, not overridden, and engine bugs go upstream (file the issue, link it, document the divergence as a known issue) rather than being patched here. Any "is it tsgo's or the bridge's?" verdict requires verification against pristine, unpatched tsgo — never the patched submodule, never TNB-vs-stock inference.
  • Exceptions: a crasher or a silent wrong result on the headline path may carry a stopgap patch — each must be listed in the README's tsgo-behavior-changes section with the upstream issue link, and removed once upstream lands the fix. Every TNB change to tsgo behavior is listed in the README, and the list is gate-enforced against the patch contents — the mechanism is annotation-driven: the change-site hunk carries a Ledger(<key>) comment and tools/check-readme-ledger.mjs fails on either direction of drift (annotation without a README row, row without an annotation). A large alignment cost is never the default action — it needs maintainer sign-off.