From de0c3240a35e40128f12d01ec03dacdbfd7ebe7c Mon Sep 17 00:00:00 2001 From: Nick Gomez <122398915+nick-inkeep@users.noreply.github.com> Date: Wed, 1 Jul 2026 06:09:25 -0700 Subject: [PATCH] chore(open-knowledge): test-file naming convention + CI guards (#2323) * chore(open-knowledge): test-file naming convention and CI guard maintenance Standardize a consistent suffix convention across the engine test files and add CI guards (each with a planted-positive self-test) plus a lint rule and fixtures. Reference-path and configuration upkeep across the affected packages. Internal test and CI tooling only. Engine source, the published CLI, and the desktop build inputs are unchanged; no public API, schema, or runtime behavior change. * fix(open-knowledge): guard desktop .app scan against a dangling symlink findAppBundles fell through to statSync(top) after the existsSync-guarded isAppDir returned false, so a broken symlink (or a racing unlink) in the output dir threw an uncaught ENOENT. Guard the statSync with existsSync like the sibling checks; add a dangling-symlink regression test. * fix(ci): build native-config fresh in the OK test cell + decouple TOML tests from the backend native-config's napi .node is host/toolchain-specific and git-ignored; a turbo build cache hit (Rust inputs unchanged) can restore a stale/unloadable binary and skip napi build, so getTomlConfigEngine silently falls back to smol-toml and the init/Codex TOML tests flake (i64 -> decline, format-preserving edit lost). Force a fresh native build (bypassing the cache) + assert the binding loads/executes before the suites, and skip the native-only assertions when the addon is unavailable so a fallback host doesn't red them (the fallback dispositions stay covered by the forced () => null engine tests). * chore(ci): tighten guard step-detection and widen desktop-smoke triggers - check-mirror-gate-reach: scope step-command detection to each step's run: scalar (not the name/env/comment text), so a renamed run: command that leaves the old string in the step name no longer registers the step as present. Add a planted-evasion test that plants the command in a step name: without run: and asserts the removal is still flagged. - public-open-knowledge-validation: desktop-smoke now also triggers on copybara manifest changes. desktop-smoke validates the materialized export and is the desktop floor gate; the manifest defines that export, so a manifest-only change that breaks the desktop build closure must run it. Manifest-only PRs are rare, so the macOS cost stays bounded. - knip.config: genericize a dependency-ignore comment (drop a stale file-path reference). * test(ci): cover runScalarText block-scalar path + trigger/positive-control edges - check-mirror-gate-reach.test: exercise runScalarText's block-scalar run: | branch (positive + name-only evasion), and the inline-flow on: [pull_request, ...] trigger form. - check-mirror-export-faithful.test: assert a markdown .test.ts/.e2e.ts alone does not satisfy the engine-source positive control. GitOrigin-RevId: 66ed7c5fe88fd44027d740401c30d3a393d9d0d2 --- biome-plugins/README.md | 49 +++--- .../no-inline-tolerance-class.fixture.tsx | 68 ++++++++ .../playwright-topass-budget.fixture.tsx | 50 ------ biome-plugins/no-inline-tolerance-class.grit | 60 +++++++ .../no-roundtrip-identity-oracle.grit | 16 +- biome-plugins/playwright-topass-budget.grit | 48 ------ biome.jsonc | 57 ++++--- knip.config.ts | 2 + .../no-inline-tolerance-class.test.ts | 69 ++++++++ .../no-roundtrip-identity-oracle.test.ts | 11 +- packages/cli/src/commands/init.test.ts | 31 ++-- packages/desktop/src/main/mcp-wiring.test.ts | 155 +++++++++--------- .../playwright-topass-budget.test.ts | 38 ----- .../desktop/tests/smoke/calibration.test.ts | 91 ---------- 14 files changed, 365 insertions(+), 380 deletions(-) create mode 100644 biome-plugins/__fixtures__/no-inline-tolerance-class.fixture.tsx delete mode 100644 biome-plugins/__fixtures__/playwright-topass-budget.fixture.tsx create mode 100644 biome-plugins/no-inline-tolerance-class.grit delete mode 100644 biome-plugins/playwright-topass-budget.grit create mode 100644 packages/app/tests/lint-plugins/no-inline-tolerance-class.test.ts delete mode 100644 packages/desktop/tests/integration/playwright-topass-budget.test.ts delete mode 100644 packages/desktop/tests/smoke/calibration.test.ts diff --git a/biome-plugins/README.md b/biome-plugins/README.md index f1dd2f58d..e9f7bc143 100644 --- a/biome-plugins/README.md +++ b/biome-plugins/README.md @@ -1,6 +1,6 @@ # Biome GritQL plugins -Custom lint rules for this workspace, registered in [`biome.jsonc`](../biome.jsonc) at the top-level `plugins` array (workspace-wide) OR a scoped `overrides[].plugins` entry (file-specific — used when the rule's invariant only applies to a known subset of files; see `playwright-topass-budget.grit` below). Each `.grit` file is a single GritQL pattern (or `or { ... }` of patterns) emitting diagnostics via `register_diagnostic()`. +Custom lint rules for this workspace, registered in [`biome.jsonc`](../biome.jsonc) at the top-level `plugins` array (workspace-wide) OR a scoped `overrides[].plugins` entry (file-specific — used when the rule's invariant only applies to a known subset of files). Each `.grit` file is a single GritQL pattern (or `or { ... }` of patterns) emitting diagnostics via `register_diagnostic()`. Plugins surface as lint errors during `biome check` (i.e. `bun run lint` and `bun run check`) and as inline editor squiggles via the Biome LSP. @@ -84,21 +84,6 @@ createPortal( Plugin: [`biome-plugins/no-unportaled-editor-content.grit`](no-unportaled-editor-content.grit). Fixture: [`biome-plugins/__fixtures__/no-unportaled-editor-content.fixture.tsx`](__fixtures__/no-unportaled-editor-content.fixture.tsx). Test: [`packages/app/tests/integration/no-unportaled-editor-content.test.ts`](../packages/app/tests/integration/no-unportaled-editor-content.test.ts). See [PRECEDENTS.md #44](../PRECEDENTS.md) for the H6 cross-doc DOM bleed contract and [PRECEDENTS.md #42](../PRECEDENTS.md#custom-lint-enforcement-precedent-42) for the GritQL-plugin convention. -### `playwright-topass-budget.grit` - -Flags `toPass({ timeout: N })` calls where `N` is a thousand-range literal below `15_000` (the canonical range `1_000`–`14_999`, with or without the `_` digit separator). The rule pairs with `tests/smoke/calibration.test.ts` Invariant B: the calibration unit test enforces the boundary at `bun run check` time (PR-tier gate); this plugin fires at editor-save / lint-time so authors see the regression instantly. - -**Why 15s.** macOS `open(1)` Apple-Event delivery + window creation + IPC roundtrip empirically takes 2-8s on a healthy CI runner and can hit 8.8s under sustained load. 15s gives 2-3x headroom over typical worst case. Precedent: `external-link.e2e.ts:83` `firstWindow({ timeout: 15_000 })`. - -**Scoped via `overrides[].plugins`** to `packages/desktop/tests/smoke/{deep-link,external-link}.e2e.ts` + the fixture itself. `packages/app`'s `*.e2e.ts` files drive Chromium in-process (NOT macOS `open(1)` Apple-Event dispatch), so their sub-15s `toPass` budgets are legitimate and must not be flagged. The override scope mirrors `tests/smoke/calibration.test.ts`'s `TOPASS_BUDGET_FILES` whitelist — same boundary, two enforcement layers (lint-time + check-time). - -The rule does NOT catch: -- Sub-1000ms literals (e.g., `timeout: 500`) — the canonical range starts at `1_000`. Sub-1s budgets aren't realistic for the macOS Apple-Event path. Calibration unit test backstops all values regardless of magnitude. -- Non-literal expressions (variables, computed values, member access) — pattern matches numeric literals only. Calibration unit test reads the same regex contract from source text and would still catch non-literal forms via the parser's literal extraction. -- Invariant A (cumulative inner timeouts ≤ outer per-test budget) — requires aggregation across same-file helper-call graphs, which GritQL pattern matching can't express. Enforced exclusively by the calibration unit test. - -Plugin: [`biome-plugins/playwright-topass-budget.grit`](playwright-topass-budget.grit). Fixture: [`biome-plugins/__fixtures__/playwright-topass-budget.fixture.tsx`](__fixtures__/playwright-topass-budget.fixture.tsx). Test: [`packages/desktop/tests/integration/playwright-topass-budget.test.ts`](../packages/desktop/tests/integration/playwright-topass-budget.test.ts). See [PRECEDENTS.md #42](../PRECEDENTS.md#custom-lint-enforcement-precedent-42) for the GritQL-plugin convention. - ### `path-conditional-map-driven-origin.grit` Observer A origin discipline. Inside `packages/server/src/server-observers.ts`, every `Y.Doc.transact()` call MUST pass the sanctioned origin `OBSERVER_SYNC_ORIGIN` as its second argument (`doc.transact(fn, OBSERVER_SYNC_ORIGIN)`). Bare `doc.transact(fn)` - or a wrong origin - routes the write to `openknowledge-service` and breaks per-session UndoManager attribution (the `trackedOrigins` Set-identity match skips the transaction). @@ -162,7 +147,7 @@ Plugin: [`biome-plugins/playwright-prefer-to-have-count.grit`](playwright-prefer ### `no-roundtrip-identity-oracle.grit` -Forbids the byte-fidelity round-trip oracle in public-mirrored tests. Asserting that re-serializing a freshly-parsed document yields back the *same* input — `serialize(parse(x))` (or the MarkdownManager method form `m.serialize(m.parse(x))`) compared equal to that same `x` via `.toBe` / `.toEqual` / `.toStrictEqual` or `===` — is the engine's byte-identity correctness oracle. That oracle is private quality-bar IP that the engine fidelity suite owns; this rule keeps a new public test from reintroducing it on the public mirror. +Forbids the byte-fidelity round-trip oracle in public-mirrored tests. Asserting that re-serializing a freshly-parsed document yields back the *same* input — `serialize(parse(x))` (or the MarkdownManager method form `m.serialize(m.parse(x))`) compared equal to that same `x` via `.toBe` / `.toEqual` / `.toStrictEqual` or `===` — is the engine's byte-identity correctness oracle, exercised by its own fidelity suite. A public test should pin a specific expected output instead; this rule keeps a new public test from reintroducing the general oracle. **Identity, not contract.** The rule fires only when the parse input and the expected value are the *same expression* — GritQL metavariable reuse (`$x` … `$x`) enforces textual equality. That is what separates the oracle from the assertions that must stay public and green: @@ -170,7 +155,7 @@ Forbids the byte-fidelity round-trip oracle in public-mirrored tests. Asserting - The **Bridge-invariant comparator** `normalizeBridge(a) === normalizeBridge(b)` (precedent #38, the documented public contract) contains no `serialize(parse(...))` and is never flagged. - The **normalizing-construct detector** `serialize(parse(x)) !== x` uses `!==`, a different operator, and is never flagged. -**Scoped via `overrides[].plugins`** to the public-mirrored test surface (`packages/**/*.test.ts`, `*.test.tsx`, `*.e2e.ts`). The engine-test clusters that legitimately own the oracle are excluded as negative globs, mirroring the set excluded from the public mirror: `packages/app/tests/fidelity/**`, `packages/core/src/markdown/**/*.test.ts`, `packages/core/src/bridge/**/*.test.ts`, the enumerated byte-oracle tests, and the private `packages/md-conformance/**` estate. Inside those clusters the identity oracle is the whole point; outside them (the public surface) it is forbidden. +**Scoped via `overrides[].plugins`** to the public-mirrored test surface (`packages/**/*.test.ts`, `*.test.tsx`, `*.e2e.ts`). The engine-test clusters that legitimately own the oracle are excluded as negative globs: `packages/app/tests/fidelity/**`, `packages/core/src/markdown/**/*.test.ts`, `packages/core/src/bridge/**/*.test.ts`, the enumerated byte-oracle tests, and the `packages/md-conformance/**` estate. Inside those clusters the identity oracle is the whole point; outside them (the public surface) it is forbidden. The rule does NOT catch: - Round-trip identity through a helper (`mdRoundTrip(x)`, `normalize(...)`) or an intermediate variable (`const out = serialize(parse(x)); expect(out).toBe(x)`) — the pattern matches the inline call shape, not helper bodies or cross-statement data flow. Those forms live in the path-excluded fidelity suite, covered by exclusion. @@ -179,6 +164,26 @@ The rule does NOT catch: Plugin: [`biome-plugins/no-roundtrip-identity-oracle.grit`](no-roundtrip-identity-oracle.grit). Fixture: [`biome-plugins/__fixtures__/no-roundtrip-identity-oracle.fixture.tsx`](__fixtures__/no-roundtrip-identity-oracle.fixture.tsx). Test: [`packages/app/tests/lint-plugins/no-roundtrip-identity-oracle.test.ts`](../packages/app/tests/lint-plugins/no-roundtrip-identity-oracle.test.ts). See [PRECEDENTS.md #42](../PRECEDENTS.md#custom-lint-enforcement-precedent-42) for the GritQL-plugin convention and [PRECEDENTS.md #38](../PRECEDENTS.md) for the Bridge-invariant contract. +### `no-inline-tolerance-class.grit` + +Forbids a public-mirrored test from writing a bridge normalization-class value inline as a string literal. `BRIDGE_TOLERANCE_CLASSES` (`packages/core/src/bridge/normalize.ts`) is the bridge normalizer's catalog of byte-difference equivalence classes it tolerates. A public test should assert observable `normalizeBridge` equivalence between inputs rather than hard-coding one of those class labels inline — the label is an internal classification detail, and pinning it inline both couples the test to that detail and re-declares the catalog outside the modules that own it. `check-mirror-test-policy` Check B already blocks a public test from *importing* the catalog symbol; this rule closes the complementary gap where a test re-encodes a class value inline (`expect(applied).toBe('jsx-container-boundary-blank')`, an array of class names), past the import check. + +**Identity, not substring.** The `or {}` matches a string-literal node whose value *is* exactly a catalog member, so the names that appear legitimately on the public surface as prose are not flagged: + +- A class name inside a longer **test-title sentence** (`test('… (block-separator-collapse class)', …)`) — a different node value, so it does not fire. +- A class name embedded in a **docName** with a prefix (`'fr34-doc-start-thematic'`) — likewise a substring, not the whole value. +- A class name in a **comment** — GritQL matches the string-literal node, not trivia. + +The match is quote-style independent (a single-quoted pattern matches the double-quoted form Biome emits). The four **universal text-encoding** classes — `bom`, `crlf`, `trailing-whitespace`, `trailing-newline` — are deliberately NOT matched: they are normalizations every text tool performs, not distinctive classes, and the public floor telemetry runtime (`tolerance-telemetry.ts`) surfaces them, so public tests legitimately assert that runtime emits `class: 'crlf'` for a CRLF input. The 12 markdown-fidelity classes plus those 4 universal classes partition the catalog exactly, and the fixture test's drift canary pins that partition — a class added to `BRIDGE_TOLERANCE_CLASSES` reddens until it is classified into one bucket. + +**Scoped via `overrides[].plugins`** to the public-mirrored test surface (`packages/**/*.test.ts`, `*.test.tsx`, `*.e2e.ts`), with the catalog-owning clusters excluded as negative globs (the bridge tests, the markdown precision suite, `packages/app/tests/fidelity/**`, the enumerated byte-oracle one-offs, and `packages/md-conformance/**`), so the catalog stays usable in those clusters but the inline form is forbidden on the public surface. + +The rule does NOT catch: +- A class name built by concatenation or template interpolation (`'doc-start-' + 'thematic'`) — neither operand is the whole value. +- A class name in a template literal — the pattern matches string-literal nodes, not template content. + +Plugin: [`biome-plugins/no-inline-tolerance-class.grit`](no-inline-tolerance-class.grit). Fixture: [`biome-plugins/__fixtures__/no-inline-tolerance-class.fixture.tsx`](__fixtures__/no-inline-tolerance-class.fixture.tsx). Test: [`packages/app/tests/lint-plugins/no-inline-tolerance-class.test.ts`](../packages/app/tests/lint-plugins/no-inline-tolerance-class.test.ts). See [PRECEDENTS.md #42](../PRECEDENTS.md#custom-lint-enforcement-precedent-42) for the GritQL-plugin convention. + ## Suppression Inline `// biome-ignore` comments silence individual diagnostics. The most specific form names the rule and the reason: @@ -199,8 +204,8 @@ Current production suppressions: - `no-loosely-typed-webcontents-ipc`: 15 sites (`preload/index.ts` ×12, `shared/ipc-send.ts` ×1, `tests/smoke/theme-sync.e2e.ts` ×2) - `no-raw-html-interactive-element`: 20 file-level `biome-ignore-all` headers in `packages/app/src/{components,presence}/**` (pre-rule backlog awaiting shadcn migration; see the rule's section above for the ratchet contract) - `no-resolved-value-theme-source`: 0 sites -- `playwright-topass-budget`: 0 sites - `no-roundtrip-identity-oracle`: 0 sites +- `no-inline-tolerance-class`: 0 sites ## Adding a new plugin @@ -235,7 +240,7 @@ language js Pick the scope: - **Workspace-wide** (default — used by `microcopy-ellipsis`, `no-loosely-typed-webcontents-ipc`, `no-resolved-value-theme-source`): add the path to the top-level `plugins` array. The rule fires on every linted file. -- **Scoped to specific files** (used by `playwright-topass-budget`): add an entry to the `overrides` array with `includes: [...]` listing the in-scope files (and the fixture path so the fixture test still triggers the rule) and `plugins: ['./biome-plugins/.grit']`. Use this when the rule's invariant only holds for a known subset of files — e.g., a budget that depends on a platform-specific code path. Document the scope-discipline rationale in the rule's docstring and assert it in the fixture-file test. +- **Scoped to specific files** (used by `no-raw-html-interactive-element`): add an entry to the `overrides` array with `includes: [...]` listing the in-scope files (and the fixture path so the fixture test still triggers the rule) and `plugins: ['./biome-plugins/.grit']`. Use this when the rule's invariant only holds for a known subset of files — e.g., a rule that should only fire under a specific source subtree. Document the scope-discipline rationale in the rule's docstring and assert it in the fixture-file test. Either shape participates in the same `biome check` pass; the override form just adds Biome's path matcher in front of the GritQL pattern. @@ -291,7 +296,7 @@ describe(' GritQL plugin', () => { The "plugin is registered" test catches the failure mode where a `.grit` file is added but the `biome.jsonc#plugins` entry is missing. -**For override-scoped plugins** (step 2 second variant): swap the registration assertion for one that asserts the plugin is in `config.overrides[].plugins`, the matching override's `includes` covers every in-scope file (including the fixture), and the plugin is NOT at root `plugins[]` (so an accidental move from override to root, which would over-fire, fails). `playwright-topass-budget.test.ts` is the reference shape. +**For override-scoped plugins** (step 2 second variant): swap the registration assertion for one that asserts the plugin is in `config.overrides[].plugins`, the matching override's `includes` covers every in-scope file (including the fixture), and the plugin is NOT at root `plugins[]` (so an accidental move from override to root, which would over-fire, fails). `no-roundtrip-identity-oracle.test.ts` is the reference shape. ### 5. Verify @@ -320,7 +325,7 @@ Add a section under `## Rules` with: what it flags, what it doesn't catch, links ## Out of scope - **Autofix.** Biome 2.4's GritQL plugins are diagnostic-only. Plugin diagnostics cannot apply code fixes. If autofix is required, a different enforcement mechanism is needed (build-time codemod, separate `--fix` script). -- **GritQL-internal path filters.** GritQL itself doesn't support file-path allowlists. The natural scope of the GritQL pattern (e.g., JSX-only) is the primary in-pattern mechanism for excluding files; inline `// biome-ignore` comments handle the residual. When a rule needs explicit per-file scoping, register the plugin under Biome's `overrides[].plugins` instead (see `playwright-topass-budget` and step 2 of "Adding a new plugin") — that runs the path matcher at the Biome layer before invoking the GritQL pattern. +- **GritQL-internal path filters.** GritQL itself doesn't support file-path allowlists. The natural scope of the GritQL pattern (e.g., JSX-only) is the primary in-pattern mechanism for excluding files; inline `// biome-ignore` comments handle the residual. When a rule needs explicit per-file scoping, register the plugin under Biome's `overrides[].plugins` instead (see `no-raw-html-interactive-element` and step 2 of "Adding a new plugin") — that runs the path matcher at the Biome layer before invoking the GritQL pattern. - **CLI string content.** `process.stderr.write('...')` / `console.log` template-literal content is not reliably matchable via GritQL call-expression patterns (false-positive rate too high). Review discipline covers these surfaces. ## References diff --git a/biome-plugins/__fixtures__/no-inline-tolerance-class.fixture.tsx b/biome-plugins/__fixtures__/no-inline-tolerance-class.fixture.tsx new file mode 100644 index 000000000..05dd74f96 --- /dev/null +++ b/biome-plugins/__fixtures__/no-inline-tolerance-class.fixture.tsx @@ -0,0 +1,68 @@ +/** + * Fixture for `no-inline-tolerance-class.grit`. + * + * Pairs 8 positive cases (a bridge normalization-class value + * written inline as a string literal — plugin MUST fire) with negative cases + * (the four universal text-encoding classes used as legitimate inline literals, + * a class name appearing only as a SUBSTRING of test-title / docName prose, a + * comment mention, a concatenation, and a non-catalog literal — plugin must NOT + * fire). The fixture-file test asserts the diagnostic count with exact equality + * (`toBe(8)`) so both a weakened pattern (drops below 8) and a widened pattern + * that catches a negative (rises above 8) fail the gate. + * + * The universal-encoding negatives (`crlf`, `bom`, `trailing-whitespace`, + * `trailing-newline`) are the load-bearing precision boundary: they are real + * `BRIDGE_TOLERANCE_CLASSES` members, yet they are deliberately NOT flagged + * because the public floor telemetry runtime surfaces them and public tests + * legitimately assert that behavior. Flagging them would redden those floor + * tests; the fixture pins that they stay silent. + * + * Deliberately NOT linted by the main `bun run lint` pass (biome-plugins/ is + * outside the lint paths); only the scoped override in biome.jsonc reaches it, + * via the fixture-file test. + */ + +declare const expect: (v: unknown) => { toBe: (v: unknown) => void }; +declare const applied: string; +declare const cls: string; + +function positives() { + // P1: a single distinctive fidelity class written inline. + const single = 'emphasis-around-code'; + // P2-P5: four class labels written inline as an array. + const reencoded = [ + 'doc-start-thematic', + 'block-separator-collapse', + 'table-align-row-spacing', + 'row-no-trailing-pipe', + ]; + // P6: asserting an observed class against an exact fidelity literal. + expect(applied).toBe('jsx-container-boundary-blank'); + // P7: an equality comparison against an exact fidelity literal. + const isCanonical = cls === 'list-indent-canonical'; + // P8: a fidelity class as an object property value. + const record = { className: 'ordered-list-marker-number' }; + return { single, reencoded, isCanonical, record }; +} + +function negatives() { + // N1-N4: the four universal text-encoding classes are real catalog members but + // are deliberately not flagged — the public floor telemetry runtime emits + // them and public tests assert that behavior. They must stay silent. + const isCrlf = cls === 'crlf'; + const isBom = cls === 'bom'; + const fired = ['trailing-whitespace', 'trailing-newline']; + // N5: a class name appearing only as a SUBSTRING of a test-title sentence — + // the rule keys on the literal's whole value, not a substring. + const title = 'watchdog tolerates a doc-start-thematic divergence at column 1'; + // N6: a class name embedded as a substring of a docName carrying a prefix. + const docName = 'fr34-doc-start-thematic'; + // N7: a class name mentioned in a comment (emphasis-around-code) is trivia, + // not a string-literal node; this assignment is an ordinary literal. + const mode = 'replace'; + // N8: split across a concatenation — neither operand is the whole value. + const split = `doc-start-${'thematic'}`; + return { isCrlf, isBom, fired, title, docName, mode, split }; +} + +export { negatives, positives }; diff --git a/biome-plugins/__fixtures__/playwright-topass-budget.fixture.tsx b/biome-plugins/__fixtures__/playwright-topass-budget.fixture.tsx deleted file mode 100644 index c9740d6c3..000000000 --- a/biome-plugins/__fixtures__/playwright-topass-budget.fixture.tsx +++ /dev/null @@ -1,50 +0,0 @@ -// FIXTURE — drives `playwright-topass-budget.test.ts` via shell-out to -// `biome check`. Not part of the main lint (lives outside the lint -// command's path list). -// -// Five positive cases (deliberate violations — plugin must fire) + four -// negative cases (well-calibrated `toPass` budgets — plugin must NOT -// fire). Exact-equality (`toBe(5)`) in the test catches both -// false-negative regressions (drop below 5) and false-positive widenings -// (above 5). - -// We're not actually executing this fixture — it just needs to parse -// with realistic call shapes. `expect.toPass` is the real shape. -declare const expect: { (fn: () => unknown): { toPass: (opts: { timeout: number }) => unknown } }; - -// === Positive cases — toPass budgets BELOW 15_000 === - -// (1) Canonical 5s budget — the exact shape PR #533 originally shipped -// for deep-link/external-link tests. -void expect(() => 'x').toPass({ timeout: 5_000 }); - -// (2) Same value without the underscore separator — both forms are -// valid JS literals; the plugin must catch both. -void expect(() => 'x').toPass({ timeout: 5000 }); - -// (3) 10s budget — also below 15s threshold. Tests the 10000-14999 band. -void expect(() => 'x').toPass({ timeout: 10_000 }); - -// (4) Edge case just below threshold — 14_999 is the largest still-flagged value. -void expect(() => 'x').toPass({ timeout: 14_999 }); - -// (5) Sub-second budget — way too short for Apple-Event roundtrip, -// plugin must flag. -void expect(() => 'x').toPass({ timeout: 1_000 }); - -// === Negative cases — toPass budgets AT OR ABOVE 15_000 === - -// (6) Exact threshold — must NOT fire. The calibration unit test's -// invariant is `>= 15_000`; the plugin mirrors that boundary. -void expect(() => 'x').toPass({ timeout: 15_000 }); - -// (7) Comfortable headroom — the value the fix in this PR adopts. -void expect(() => 'x').toPass({ timeout: 30_000 }); - -// (8) `15000` without underscore — the literal form variant must NOT fire. -void expect(() => 'x').toPass({ timeout: 15000 }); - -// (9) `toPass()` without options — no `timeout` to flag, must NOT fire. -// Playwright's default ~5s applies internally, but that's a separate -// concern; the plugin only checks declared `timeout:` literals. -void expect(() => 'x').toPass(); diff --git a/biome-plugins/no-inline-tolerance-class.grit b/biome-plugins/no-inline-tolerance-class.grit new file mode 100644 index 000000000..97d35fe5a --- /dev/null +++ b/biome-plugins/no-inline-tolerance-class.grit @@ -0,0 +1,60 @@ +// no-inline-tolerance-class — forbid an inline bridge normalization-class value in a public test. +// +// `BRIDGE_TOLERANCE_CLASSES` (packages/core/src/bridge/normalize.ts) is the +// bridge normalizer's catalog of byte-difference equivalence classes it tolerates. +// A public test should assert OBSERVABLE behavior — that two inputs reduce to the +// same `normalizeBridge` output — rather than hard-coding one of those class +// labels as an inline string literal: the label is an internal classification +// detail, and pinning it inline both couples the test to that detail and +// re-declares the catalog outside the modules that own it. `check-mirror-test-policy` +// Check B blocks a public test from IMPORTING the catalog symbol; this rule closes +// the complementary gap where a test re-encodes a class value as an inline string +// literal, past the import check. +// +// NOT matched, by design: the four universal text-encoding classes — `bom`, +// `crlf`, `trailing-whitespace`, `trailing-newline`. Those are normalizations +// every text tool performs, and the public floor telemetry runtime +// (packages/core/src/bridge/tolerance-telemetry.ts) surfaces them; public tests +// legitimately assert that runtime emits `class: 'crlf'` for a CRLF input. +// Matching them would redden those floor tests. The fixture test's drift canary +// pins this partition: every catalog member is either a matched arm here or one +// of those four universal classes, so a class added to the catalog reddens until +// it is classified. +// +// IDENTITY, not substring: the `or {}` matches a string-literal node whose value +// IS exactly a catalog member. A class name that appears only as a substring of a +// larger string — a test-title sentence (`'... (doc-start-thematic class)'`), a +// docName (`'fr34-doc-start-thematic'`), or a label — has a different node value +// and does not fire. The match is quote-style independent (a single-quoted +// pattern matches the double-quoted form Biome emits). +// +// Does NOT catch (documented honestly): +// - A class name built by concatenation (`'doc-start-' + 'thematic'`) or +// interpolation — neither operand is the whole value. +// - A class name in a template literal or a comment — GritQL matches the +// string-literal node, not template content or trivia. +// +// Suppress a legitimate inline use (none expected on the public surface) with: +// // biome-ignore lint/plugin/no-inline-tolerance-class: + +language js + +or { + `'commonmark-escape'` as $lit, + `'emphasis-around-code'` as $lit, + `'leading-newline'` as $lit, + `'doc-start-thematic'` as $lit, + `'block-separator-collapse'` as $lit, + `'table-align-row-spacing'` as $lit, + `'row-no-trailing-pipe'` as $lit, + `'list-indent-canonical'` as $lit, + `'ordered-list-marker-number'` as $lit, + `'paragraph-continuation-indent'` as $lit, + `'jsx-container-boundary-blank'` as $lit, + `'blank-line-collapse'` as $lit +} where { + register_diagnostic( + span = $lit, + message = "Inline bridge normalization-class value in a public test. Assert observable `normalizeBridge` equivalence between inputs instead of hard-coding a BRIDGE_TOLERANCE_CLASSES label as a string literal. See https://github.com/inkeep/open-knowledge/blob/main/biome-plugins/README.md#no-inline-tolerance-classgrit" + ) +} diff --git a/biome-plugins/no-roundtrip-identity-oracle.grit b/biome-plugins/no-roundtrip-identity-oracle.grit index 8a2093bb7..11a75d2b0 100644 --- a/biome-plugins/no-roundtrip-identity-oracle.grit +++ b/biome-plugins/no-roundtrip-identity-oracle.grit @@ -3,10 +3,10 @@ // Asserting that re-serializing a freshly-parsed document yields back the // SAME input bytes — `serialize(parse(x))` (or the MarkdownManager method // equivalent `m.serialize(m.parse(x))`) compared equal to that same `x` — is -// the engine's byte-identity correctness oracle. That oracle is the private -// quality-bar IP and lives in the private engine fidelity suite; it must not -// ship on the public mirror. This rule flags the inline assertion shape so a -// new public test can't reintroduce it. +// the engine's byte-identity correctness oracle. This general round-trip-identity +// check is exercised by the engine's own fidelity suite; a public test should pin +// a specific expected output instead. This rule flags the inline assertion shape +// so a new public test can't reintroduce the general oracle. // // IDENTITY, not contract: the rule only fires when the parse input and the // expected value are the SAME expression (metavariable reuse `$x` ... `$x` @@ -22,8 +22,8 @@ // - Round-trip identity through a helper (`mdRoundTrip(x)`, `normalize(...)`) // or via an intermediate variable (`const out = serialize(parse(x)); ...`) // — GritQL matches the inline call shape, not helper bodies or cross- -// statement data flow. Those forms live in the private fidelity suite, -// which is path-excluded from the mirror, so they are covered there. +// statement data flow. Those forms live in the engine's fidelity suite and +// are covered there. // - Matchers other than `toBe` / `toEqual` / `toStrictEqual` and operators // other than `===` (e.g. a custom `assertByteIdentical` helper). // @@ -46,7 +46,7 @@ or { }, register_diagnostic( span = $assertion, - message = "Byte-fidelity round-trip oracle in a public test: `serialize(parse(x))` asserted equal to the same input `x` exposes the engine's byte-identity correctness oracle on the public mirror. Keep round-trip-identity assertions in the private engine fidelity suite (packages/app/tests/fidelity or packages/core/src/markdown), or assert against a fixed expected literal for a public contract test. See https://github.com/inkeep/open-knowledge/blob/main/biome-plugins/README.md#no-roundtrip-identity-oraclegrit" + message = "Byte-fidelity round-trip oracle in a public test: `serialize(parse(x))` asserted equal to the same input `x`. This general round-trip-identity check belongs to the engine's fidelity suite (packages/app/tests/fidelity or packages/core/src/markdown); a public test should assert a fixed expected literal for a specific contract instead. See https://github.com/inkeep/open-knowledge/blob/main/biome-plugins/README.md#no-roundtrip-identity-oraclegrit" ) }, // serialize(parse($x)) === $x in either operand order (covers `assert(...)`, @@ -71,7 +71,7 @@ or { }, register_diagnostic( span = $cmp, - message = "Byte-fidelity round-trip oracle in a public test: `serialize(parse(x))` asserted equal to the same input `x` exposes the engine's byte-identity correctness oracle on the public mirror. Keep round-trip-identity assertions in the private engine fidelity suite (packages/app/tests/fidelity or packages/core/src/markdown), or assert against a fixed expected literal for a public contract test. See https://github.com/inkeep/open-knowledge/blob/main/biome-plugins/README.md#no-roundtrip-identity-oraclegrit" + message = "Byte-fidelity round-trip oracle in a public test: `serialize(parse(x))` asserted equal to the same input `x`. This general round-trip-identity check belongs to the engine's fidelity suite (packages/app/tests/fidelity or packages/core/src/markdown); a public test should assert a fixed expected literal for a specific contract instead. See https://github.com/inkeep/open-knowledge/blob/main/biome-plugins/README.md#no-roundtrip-identity-oraclegrit" ) } } diff --git a/biome-plugins/playwright-topass-budget.grit b/biome-plugins/playwright-topass-budget.grit deleted file mode 100644 index 1eb4fc1b7..000000000 --- a/biome-plugins/playwright-topass-budget.grit +++ /dev/null @@ -1,48 +0,0 @@ -// playwright-topass-budget — flag `toPass({ timeout: N })` with N < 15_000. -// -// Pairs with `tests/smoke/calibration.test.ts` Invariant B (per-assertion -// timeout calibration). The calibration unit test enforces the invariant -// at `bun run check` time (PR-tier gate); this rule fires at editor-save -// / lint-time so authors see the regression instantly instead of waiting -// for the gate to run. -// -// Why 15_000: macOS `open(1)` Apple-Event delivery + window creation + -// IPC roundtrip empirically takes 2-8s on a healthy CI runner and can hit -// 8.8s under sustained load. 15s gives 2-3x headroom over typical worst -// case. Precedent: `external-link.e2e.ts:83` `firstWindow({ timeout: -// 15_000 })`. -// -// Scope: the cumulative-budget invariant (Invariant A — cumulative inner -// timeouts ≤ outer per-test budget) is enforced by the calibration unit -// test, NOT here. Invariant A requires aggregation across same-file -// helper-call graphs, which GritQL pattern matching can't express. Only -// Invariant B's single-pattern check fits cleanly. -// -// Regex captures the canonical `N_000` / `N000` thousand-range literal -// forms: 1000-9999 (single-digit prefix + 3 digits) and 10000-14999 -// (1[0-4] prefix + 3 digits), with optional `_` digit separator. Above -// 15_000 doesn't match — those are well-calibrated. -// -// Suppress legitimate uses (e.g., a fast non-Electron path that doesn't -// touch Apple-Event delivery) with: -// // biome-ignore lint/plugin/playwright-topass-budget: - -language js - -// `toPass` is always invoked as a member call on the result of `expect(...)` -// (per Playwright's API surface). The dominant shape is -// `expect(probeFn).toPass({ timeout: N })`. `$_.toPass($opts)` matches the -// member-call form and captures the options bag as `$opts`. -// -// `contains \`timeout: $n\`` traverses into the options-object literal to -// find the `timeout: N` property; `$n` captures the numeric-literal AST -// node, whose text the regex below matches against. - -`$_.toPass($opts)` as $call where { - $opts <: contains `timeout: $n`, - $n <: r"^(?:[0-9]|1[0-4])_?[0-9]{3}$", - register_diagnostic( - span = $call, - message = "Invariant B: raise the `toPass({ timeout })` budget to at least 15_000ms — macOS Apple-Event / IPC roundtrip needs ≥15s headroom under CI runner-class load. See https://github.com/inkeep/open-knowledge/blob/main/biome-plugins/README.md#playwright-topass-budgetgrit" - ) -} diff --git a/biome.jsonc b/biome.jsonc index f6eb1713f..c597b6c3a 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -111,21 +111,6 @@ ], "plugins": ["./biome-plugins/class-proof-registration-discipline.grit"] }, - { - // Apple-Event / IPC roundtrip toPass budget enforcement is scoped to - // the desktop-smoke files (and the plugin's own fixture for testing). - // packages/app's *.e2e.ts files drive Chromium in-process, NOT macOS - // `open(1)` Apple-Event dispatch, so their sub-15s toPass budgets are - // legitimate (fast in-process roundtrip, no Launch Services latency). - // Pairs with `tests/smoke/calibration.test.ts` Invariant B, which is - // scoped to the same surface via `TOPASS_BUDGET_FILES`. - "includes": [ - "packages/desktop/tests/smoke/deep-link.e2e.ts", - "packages/desktop/tests/smoke/external-link.e2e.ts", - "biome-plugins/__fixtures__/playwright-topass-budget.fixture.tsx" - ], - "plugins": ["./biome-plugins/playwright-topass-budget.grit"] - }, { // One-shot `expect(await locator.count())` reads never retry and are // the canonical hidden-flake assertion shape (2026-06 e2e audit). @@ -219,11 +204,11 @@ // Byte-fidelity round-trip oracle ban, scoped to the public-mirrored test // surface. `serialize(parse(x))` (or the MarkdownManager method form) // asserted equal to the same `x` is the engine's byte-identity correctness - // oracle — private quality-bar IP. It legitimately lives in the engine - // fidelity suite (packages/app/tests/fidelity), the markdown + bridge - // precision suites, and the enumerated byte-oracle tests; those are - // excluded from the public mirror, and are excluded here too so the oracle - // stays allowed there but forbidden on the public surface. + // oracle. It legitimately lives in the engine fidelity suite + // (packages/app/tests/fidelity), the markdown + bridge precision suites, and + // the enumerated byte-oracle tests; those are excluded from this override's + // scope so the oracle stays allowed there but forbidden on the public + // surface. // packages/md-conformance is the private conformance estate // (also mirror-excluded). The fixture is in scope so its planted oracle // drives the fixture-file test; the Bridge-invariant comparator @@ -237,15 +222,35 @@ "!packages/app/tests/fidelity/**", "!packages/core/src/markdown/**/*.test.ts", "!packages/core/src/bridge/**/*.test.ts", - "!packages/server/src/prd-6654-multi-client-repro.test.ts", - "!packages/app/tests/integration/source-mode-byte-preservation.test.ts", - "!packages/app/tests/integration/init-load-byte-stable.test.ts", - "!packages/app/tests/integration/init-load-byte-stable-corpus-coverage.test.ts", - "!packages/app/tests/stress/init-load-byte-stable.e2e.ts", - "!packages/app/tests/stress/single-file-ephemeral.e2e.ts", + "!**/*.private.*", "biome-plugins/__fixtures__/no-roundtrip-identity-oracle.fixture.tsx" ], "plugins": ["./biome-plugins/no-roundtrip-identity-oracle.grit"] + }, + { + // Inline bridge normalization-class value ban, scoped to the + // public-mirrored test surface. BRIDGE_TOLERANCE_CLASSES + // (packages/core/src/bridge/normalize.ts) is the bridge normalizer's class + // catalog; importing it into a public test is blocked by + // check-mirror-test-policy Check B, and this rule blocks the complementary + // inline re-encoding. The catalog legitimately lives in the bridge tests, + // the markdown precision suite, the fidelity suite, and the byte-oracle + // one-offs — excluded from this override's scope (as they are from the + // plugin pass) so the catalog stays allowed there but the inline form is + // forbidden on the public surface. The fixture is in scope so its planted + // literals drive the fixture-file test. + "includes": [ + "packages/**/*.test.ts", + "packages/**/*.test.tsx", + "packages/**/*.e2e.ts", + "!packages/md-conformance/**", + "!packages/app/tests/fidelity/**", + "!packages/core/src/markdown/**/*.test.ts", + "!packages/core/src/bridge/**/*.test.ts", + "!**/*.private.*", + "biome-plugins/__fixtures__/no-inline-tolerance-class.fixture.tsx" + ], + "plugins": ["./biome-plugins/no-inline-tolerance-class.grit"] } ], "files": { diff --git a/knip.config.ts b/knip.config.ts index 020d6d598..662ca81cf 100644 --- a/knip.config.ts +++ b/knip.config.ts @@ -52,6 +52,8 @@ export default { 'scripts/assert-smoke-not-vacuous.mjs': ['files'], 'scripts/assert-smoke-not-vacuous.test.mjs': ['files'], 'scripts/check-no-ci-skip-in-e2e.test.mjs': ['files'], + 'scripts/assert-app-built.mjs': ['files'], + 'scripts/assert-app-built.test.mjs': ['files'], '.github/scripts/cla-gate.test.mjs': ['files'], '.github/scripts/cla-gate.mjs': ['exports'], '.github/scripts/bridge-public-pr-to-monorepo.test.mjs': ['files'], diff --git a/packages/app/tests/lint-plugins/no-inline-tolerance-class.test.ts b/packages/app/tests/lint-plugins/no-inline-tolerance-class.test.ts new file mode 100644 index 000000000..2a41625bc --- /dev/null +++ b/packages/app/tests/lint-plugins/no-inline-tolerance-class.test.ts @@ -0,0 +1,69 @@ +import { describe, expect, test } from 'bun:test'; +import { spawnSync } from 'node:child_process'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; + +const REPO_ROOT = join(__dirname, '..', '..', '..', '..'); +const FIXTURE_REL = 'biome-plugins/__fixtures__/no-inline-tolerance-class.fixture.tsx'; +const PLUGIN_REL = './biome-plugins/no-inline-tolerance-class.grit'; +const GRIT_ABS = join(REPO_ROOT, 'biome-plugins/no-inline-tolerance-class.grit'); +const CATALOG_SOURCE_ABS = join(REPO_ROOT, 'packages/core/src/bridge/normalize.ts'); + +describe('no-inline-tolerance-class GritQL plugin', () => { + test('fires on exactly 8 inline fidelity-class literals (and on no negative case)', () => { + const result = spawnSync('bunx', ['biome', 'check', FIXTURE_REL], { + cwd: REPO_ROOT, + encoding: 'utf-8', + }); + expect(result.error).toBeUndefined(); + expect(result.status).not.toBe(0); + const output = `${result.stdout}\n${result.stderr}`; + const fires = (output.match(/Inline bridge normalization-class value in a public test/g) ?? []) + .length; + expect(fires).toBe(8); + expect(output).toContain('hard-coding a BRIDGE_TOLERANCE_CLASSES label'); + expect(output).toMatch(/https?:\/\/[^\s]+/); + expect(output).toContain('biome-plugins/README.md#no-inline-tolerance-classgrit'); + }); + + test('plugin is registered as an override scoped to the public test surface (not workspace-wide)', () => { + const config = require(join(REPO_ROOT, 'biome.jsonc')); + const rootPlugins: string[] = config.plugins ?? []; + expect(rootPlugins).not.toContain(PLUGIN_REL); + + const overrides: Array<{ includes?: string[]; plugins?: string[] }> = config.overrides ?? []; + const entry = overrides.find((o) => (o.plugins ?? []).includes(PLUGIN_REL)); + expect(entry).toBeDefined(); + const includes = entry?.includes ?? []; + expect(includes).toContain(FIXTURE_REL); + for (const excluded of [ + '!packages/md-conformance/**', + '!packages/app/tests/fidelity/**', + '!packages/core/src/markdown/**/*.test.ts', + '!packages/core/src/bridge/**/*.test.ts', + '!**/*.private.*', + ]) { + expect(includes).toContain(excluded); + } + }); + + test('matched fidelity set + universal-encoding set partition BRIDGE_TOLERANCE_CLASSES', () => { + const UNIVERSAL_ENCODING = ['bom', 'crlf', 'trailing-whitespace', 'trailing-newline']; + + const catalogSrc = readFileSync(CATALOG_SOURCE_ABS, 'utf-8'); + const arrayBody = catalogSrc.match(/BRIDGE_TOLERANCE_CLASSES\s*=\s*\[([\s\S]*?)\]/)?.[1]; + expect(arrayBody).toBeDefined(); + const catalog = [...(arrayBody ?? '').matchAll(/'([^']+)'/g)].map((m) => m[1]).sort(); + expect(catalog.length).toBeGreaterThan(0); + + const gritArms = readFileSync(GRIT_ABS, 'utf-8') + .split('\n') + .filter((line) => !line.trimStart().startsWith('//')) + .join('\n'); + const matched = [...gritArms.matchAll(/`'([^']+)'`/g)].map((m) => m[1]).sort(); + + expect(matched.filter((c) => UNIVERSAL_ENCODING.includes(c))).toEqual([]); + const union = [...new Set([...matched, ...UNIVERSAL_ENCODING])].sort(); + expect(union).toEqual(catalog); + }); +}); diff --git a/packages/app/tests/lint-plugins/no-roundtrip-identity-oracle.test.ts b/packages/app/tests/lint-plugins/no-roundtrip-identity-oracle.test.ts index 6246f2e30..0142e57b0 100644 --- a/packages/app/tests/lint-plugins/no-roundtrip-identity-oracle.test.ts +++ b/packages/app/tests/lint-plugins/no-roundtrip-identity-oracle.test.ts @@ -17,9 +17,7 @@ describe('no-roundtrip-identity-oracle GritQL plugin', () => { const output = `${result.stdout}\n${result.stderr}`; const fires = (output.match(/Byte-fidelity round-trip oracle in a public test/g) ?? []).length; expect(fires).toBe(10); - expect(output).toContain( - 'Keep round-trip-identity assertions in the private engine fidelity suite', - ); + expect(output).toContain('assert a fixed expected literal for a specific contract'); expect(output).toMatch(/https?:\/\/[^\s]+/); expect(output).toContain('biome-plugins/README.md#no-roundtrip-identity-oraclegrit'); }); @@ -39,12 +37,7 @@ describe('no-roundtrip-identity-oracle GritQL plugin', () => { '!packages/app/tests/fidelity/**', '!packages/core/src/markdown/**/*.test.ts', '!packages/core/src/bridge/**/*.test.ts', - '!packages/server/src/prd-6654-multi-client-repro.test.ts', - '!packages/app/tests/integration/source-mode-byte-preservation.test.ts', - '!packages/app/tests/integration/init-load-byte-stable.test.ts', - '!packages/app/tests/integration/init-load-byte-stable-corpus-coverage.test.ts', - '!packages/app/tests/stress/init-load-byte-stable.e2e.ts', - '!packages/app/tests/stress/single-file-ephemeral.e2e.ts', + '!**/*.private.*', ]) { expect(includes).toContain(excluded); } diff --git a/packages/cli/src/commands/init.test.ts b/packages/cli/src/commands/init.test.ts index 5671edbed..3cd7d96df 100644 --- a/packages/cli/src/commands/init.test.ts +++ b/packages/cli/src/commands/init.test.ts @@ -52,6 +52,8 @@ import { } from './init.ts'; import { LAUNCH_JSON_PORT } from './ui.ts'; +const NATIVE_TOML_AVAILABLE = createTomlConfigEngine().backend === 'native'; + describe('LAUNCH_UI_CHAIN_V1 (published launch.json recipe shell chain)', () => { it('is syntactically valid POSIX sh (sh -n)', () => { const result = spawnSync('sh', ['-n', '-c', LAUNCH_UI_CHAIN_V1], { encoding: 'utf-8' }); @@ -447,7 +449,7 @@ describe('runInit', () => { expect(config.mcp_servers[result.editors[0].serverName]).toEqual(expectedDevMcpEntry()); }); - it('preserves existing Codex MCP entries', async () => { + it.skipIf(!NATIVE_TOML_AVAILABLE)('preserves existing Codex MCP entries', async () => { mkdirSync(dirname(codexConfigPath()), { recursive: true }); writeFileSync( codexConfigPath(), @@ -2131,18 +2133,21 @@ describe('classifyExistingMcpEntry', () => { }); }); - it('no-entry (not decline) on a valid Codex config with a 2^53+ integer', () => { - const path = resolveCodexConfigPath({ home: fakeHome, env: {} }); - mkdirSync(dirname(path), { recursive: true }); - writeFileSync( - path, - '# keep my comments\nmodel = "gpt-5"\n[mcp_servers.other]\ncommand = "node"\nstartup_timeout_ms = 9223372036854775807\n', - 'utf-8', - ); - expect(classifyExistingMcpEntry(EDITOR_TARGETS.codex, '', fakeHome)).toEqual({ - kind: 'no-entry', - }); - }); + it.skipIf(!NATIVE_TOML_AVAILABLE)( + 'no-entry (not decline) on a valid Codex config with a 2^53+ integer', + () => { + const path = resolveCodexConfigPath({ home: fakeHome, env: {} }); + mkdirSync(dirname(path), { recursive: true }); + writeFileSync( + path, + '# keep my comments\nmodel = "gpt-5"\n[mcp_servers.other]\ncommand = "node"\nstartup_timeout_ms = 9223372036854775807\n', + 'utf-8', + ); + expect(classifyExistingMcpEntry(EDITOR_TARGETS.codex, '', fakeHome)).toEqual({ + kind: 'no-entry', + }); + }, + ); it('present on a valid Codex config with a microsecond datetime and OK entry', () => { const path = resolveCodexConfigPath({ home: fakeHome, env: {} }); diff --git a/packages/desktop/src/main/mcp-wiring.test.ts b/packages/desktop/src/main/mcp-wiring.test.ts index 74fdfcfd8..cd7c1c437 100644 --- a/packages/desktop/src/main/mcp-wiring.test.ts +++ b/packages/desktop/src/main/mcp-wiring.test.ts @@ -35,6 +35,8 @@ import { writeMcpStatusMarker, } from './mcp-wiring.ts'; +const NATIVE_TOML_AVAILABLE = createTomlConfigEngine().backend === 'native'; + function memoryFs( initial: Record = {}, ): McpWiringFsOps & { files: Record } { @@ -241,83 +243,86 @@ describe('checkAndRepairMcpWiringOnStartup — non-destructive decline', () => { dir = undefined; }); - test('real classify on a 2^53+ integer TOML → byte-unchanged, no .broken, no-op', async () => { - dir = mkdtempSync(join(tmpdir(), 'ok-reset-repro-')); - const tomlPath = join(dir, 'config.toml'); - const original = [ - '# my codex config — keep my comments!', - 'model = "gpt-5"', - '', - '[mcp_servers.other]', - 'command = "node"', - 'startup_timeout_ms = 9223372036854775807', - '', - ].join('\n'); - fsWriteFileSync(tomlPath, original); - - const target: EditorMcpTarget = { - id: 'codex' as McpWiringEditorId, - label: 'Codex', - format: 'toml', - topLevelKey: 'mcp_servers', - serverName: () => 'open-knowledge', - configPath: () => tomlPath, - buildEntry: () => buildManagedServerEntry({ mode: 'published' }), - scope: 'global', - }; - - expect(classifyExistingMcpEntry(target, '', undefined, tomlPath)).toEqual({ - kind: 'no-entry', - }); - - const realFs: McpWiringFsOps = { - existsSync: (p) => fsExistsSync(p), - readFileSync: (p) => fsReadFileSync(p, 'utf8'), - writeFileSync: (p, c) => fsWriteFileSync(p, c), - mkdirSync: (p, o) => { - fsMkdirSync(p, o); - }, - renameSync: (from, to) => fsRenameSync(from, to), - unlinkSync: (p) => fsUnlinkSync(p), - }; - const writes: McpWiringEditorId[][] = []; - const cli: McpWiringCliSurface = { - detectInstalledEditors: () => ['codex' as McpWiringEditorId], - classifyExistingMcpEntry: () => classifyExistingMcpEntry(target, '', undefined, tomlPath), - readExistingMcpEntry: () => null, - allEditorIds: ['codex' as McpWiringEditorId], - editorTargets: { codex: target } as Record, - writeUserMcpConfigs: async ({ editors }) => { - writes.push([...editors]); - fsWriteFileSync(tomlPath, '{"mcp_servers":{"open-knowledge":{}}}'); - return editors.map((editorId) => ({ - editorId, - label: editorId, - action: 'written' as const, - configPath: tomlPath, - serverName: 'open-knowledge', - })); - }, - }; + test.skipIf(!NATIVE_TOML_AVAILABLE)( + 'real classify on a 2^53+ integer TOML → byte-unchanged, no .broken, no-op', + async () => { + dir = mkdtempSync(join(tmpdir(), 'ok-reset-repro-')); + const tomlPath = join(dir, 'config.toml'); + const original = [ + '# my codex config — keep my comments!', + 'model = "gpt-5"', + '', + '[mcp_servers.other]', + 'command = "node"', + 'startup_timeout_ms = 9223372036854775807', + '', + ].join('\n'); + fsWriteFileSync(tomlPath, original); + + const target: EditorMcpTarget = { + id: 'codex' as McpWiringEditorId, + label: 'Codex', + format: 'toml', + topLevelKey: 'mcp_servers', + serverName: () => 'open-knowledge', + configPath: () => tomlPath, + buildEntry: () => buildManagedServerEntry({ mode: 'published' }), + scope: 'global', + }; + + expect(classifyExistingMcpEntry(target, '', undefined, tomlPath)).toEqual({ + kind: 'no-entry', + }); - const events: Array> = []; - const result = await checkAndRepairMcpWiringOnStartup({ - isPackaged: true, - executablePath: PACKAGED_EXE, - home: dir, - platform: 'darwin', - ipcMain: inertIpcMain, - cli, - fs: realFs, - logger: { info() {}, warn() {}, error() {}, event: (e) => events.push(e) }, - }); + const realFs: McpWiringFsOps = { + existsSync: (p) => fsExistsSync(p), + readFileSync: (p) => fsReadFileSync(p, 'utf8'), + writeFileSync: (p, c) => fsWriteFileSync(p, c), + mkdirSync: (p, o) => { + fsMkdirSync(p, o); + }, + renameSync: (from, to) => fsRenameSync(from, to), + unlinkSync: (p) => fsUnlinkSync(p), + }; + const writes: McpWiringEditorId[][] = []; + const cli: McpWiringCliSurface = { + detectInstalledEditors: () => ['codex' as McpWiringEditorId], + classifyExistingMcpEntry: () => classifyExistingMcpEntry(target, '', undefined, tomlPath), + readExistingMcpEntry: () => null, + allEditorIds: ['codex' as McpWiringEditorId], + editorTargets: { codex: target } as Record, + writeUserMcpConfigs: async ({ editors }) => { + writes.push([...editors]); + fsWriteFileSync(tomlPath, '{"mcp_servers":{"open-knowledge":{}}}'); + return editors.map((editorId) => ({ + editorId, + label: editorId, + action: 'written' as const, + configPath: tomlPath, + serverName: 'open-knowledge', + })); + }, + }; + + const events: Array> = []; + const result = await checkAndRepairMcpWiringOnStartup({ + isPackaged: true, + executablePath: PACKAGED_EXE, + home: dir, + platform: 'darwin', + ipcMain: inertIpcMain, + cli, + fs: realFs, + logger: { info() {}, warn() {}, error() {}, event: (e) => events.push(e) }, + }); - expect(result.status).toBe('ok'); - expect(fsReadFileSync(tomlPath, 'utf8')).toBe(original); - expect(readdirSync(dir).some((name) => name.includes('.broken-'))).toBe(false); - expect(writes).toEqual([]); - expect(events.find((e) => e.event === 'mcp-config-decline')).toBeUndefined(); - }); + expect(result.status).toBe('ok'); + expect(fsReadFileSync(tomlPath, 'utf8')).toBe(original); + expect(readdirSync(dir).some((name) => name.includes('.broken-'))).toBe(false); + expect(writes).toEqual([]); + expect(events.find((e) => e.event === 'mcp-config-decline')).toBeUndefined(); + }, + ); test('real classify on a genuinely-malformed TOML → decline, byte-unchanged, no write, bounded signal', async () => { dir = mkdtempSync(join(tmpdir(), 'ok-decline-sweep-')); diff --git a/packages/desktop/tests/integration/playwright-topass-budget.test.ts b/packages/desktop/tests/integration/playwright-topass-budget.test.ts deleted file mode 100644 index a5d4a800e..000000000 --- a/packages/desktop/tests/integration/playwright-topass-budget.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { describe, expect, test } from 'bun:test'; -import { spawnSync } from 'node:child_process'; -import { join } from 'node:path'; - -const REPO_ROOT = join(__dirname, '..', '..', '..', '..'); -const FIXTURE_REL = 'biome-plugins/__fixtures__/playwright-topass-budget.fixture.tsx'; - -describe('Invariant B — playwright-topass-budget GritQL plugin', () => { - test('fires on exactly 5 sub-15s toPass budgets (and on no negative case)', () => { - const result = spawnSync('bunx', ['biome', 'check', FIXTURE_REL], { - cwd: REPO_ROOT, - encoding: 'utf-8', - }); - expect(result.status).not.toBe(0); - const output = `${result.stdout}\n${result.stderr}`; - const fires = (output.match(/Invariant B: raise the `toPass/g) ?? []).length; - expect(fires).toBe(5); - expect(output).toContain('raise the `toPass({ timeout })` budget'); - expect(output).toMatch(/https?:\/\/[^\s]+/); - expect(output).toContain('biome-plugins/README.md#playwright-topass-budgetgrit'); - }); - - test('plugin is registered in biome.jsonc overrides, scoped to deep-link + external-link + fixture', () => { - const config = require(join(REPO_ROOT, 'biome.jsonc')); - const pluginPath = './biome-plugins/playwright-topass-budget.grit'; - - const rootPlugins: string[] = config.plugins ?? []; - expect(rootPlugins).not.toContain(pluginPath); - - const overrides: Array<{ includes?: string[]; plugins?: string[] }> = config.overrides ?? []; - const matching = overrides.find((o) => (o.plugins ?? []).includes(pluginPath)); - expect(matching).toBeDefined(); - const includes = matching?.includes ?? []; - expect(includes).toContain('packages/desktop/tests/smoke/deep-link.e2e.ts'); - expect(includes).toContain('packages/desktop/tests/smoke/external-link.e2e.ts'); - expect(includes).toContain('biome-plugins/__fixtures__/playwright-topass-budget.fixture.tsx'); - }); -}); diff --git a/packages/desktop/tests/smoke/calibration.test.ts b/packages/desktop/tests/smoke/calibration.test.ts deleted file mode 100644 index 3c55e048f..000000000 --- a/packages/desktop/tests/smoke/calibration.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { describe, expect, test } from 'bun:test'; -import { readdirSync } from 'node:fs'; -import { resolve } from 'node:path'; -import { parsePlaywrightConfigTimeout, parseTestFile } from './_helpers/parse-timeouts'; - -const DESKTOP_ROOT = resolve(__dirname, '..', '..'); -const CONFIG_PATH = resolve(DESKTOP_ROOT, 'playwright.config.ts'); -const SMOKE_DIR = resolve(DESKTOP_ROOT, 'tests', 'smoke'); - -const SMOKE_FILES = readdirSync(SMOKE_DIR) - .filter((f) => f.endsWith('.e2e.ts')) - .sort(); - -const TOPASS_BUDGET_FILES = ['deep-link.e2e.ts', 'external-link.e2e.ts'] as const; - -const MIN_TOPASS_BUDGET_MS = 15_000; - -describe('Playwright smoke test calibration', () => { - describe('outer per-test timeout configuration', () => { - test('playwright.config.ts has a parseable top-level timeout', () => { - const cfg = parsePlaywrightConfigTimeout(CONFIG_PATH); - expect(cfg.ci).toBeGreaterThan(0); - expect(cfg.local).toBeGreaterThan(0); - }); - }); - - describe('Invariant A: cumulative inner timeouts fit within outer CI budget', () => { - const cfg = parsePlaywrightConfigTimeout(CONFIG_PATH); - const outerCiMs = cfg.ci; - - for (const file of SMOKE_FILES) { - const filePath = resolve(SMOKE_DIR, file); - const fa = parseTestFile(filePath); - for (const t of fa.tests) { - test(`${file}:${t.lineNumber} ${t.testName} cumulative ≤ outer CI`, () => { - const budgetMs = t.perTestTimeoutMs ?? outerCiMs; - const breakdown = { - file, - line: t.lineNumber, - test: t.testName, - cumulativeMs: t.cumulativeMs, - budgetMs, - budgetSource: - t.perTestTimeoutMs != null ? 'test.setTimeout' : 'playwright.config.ts (CI)', - directTimeoutsMs: t.directTimeoutsMs, - helperCalls: t.helperCallNames, - tracedHelperBudgetsMs: t.tracedHelperBudgetsMs, - }; - if (t.cumulativeMs > budgetMs) { - throw new Error( - `cumulative inner-timeout budget exceeds per-test budget: ${JSON.stringify(breakdown, null, 2)}`, - ); - } - expect(t.cumulativeMs).toBeLessThanOrEqual(budgetMs); - }); - } - } - }); - - describe('Invariant B: Apple-Event / IPC toPass budgets meet minimum', () => { - for (const file of TOPASS_BUDGET_FILES) { - const filePath = resolve(SMOKE_DIR, file); - const fa = parseTestFile(filePath); - for (const t of fa.tests) { - if (t.toPassBudgetsMs.length === 0) continue; - test(`${file}:${t.lineNumber} ${t.testName} every toPass ≥ ${MIN_TOPASS_BUDGET_MS}ms`, () => { - const undersized = t.toPassBudgetsMs.filter((b) => b < MIN_TOPASS_BUDGET_MS); - if (undersized.length > 0) { - throw new Error( - `toPass({ timeout }) budget(s) below ${MIN_TOPASS_BUDGET_MS}ms minimum: ${JSON.stringify( - { - file, - line: t.lineNumber, - test: t.testName, - toPassBudgetsMs: t.toPassBudgetsMs, - undersizedMs: undersized, - minRequiredMs: MIN_TOPASS_BUDGET_MS, - }, - null, - 2, - )}`, - ); - } - for (const b of t.toPassBudgetsMs) { - expect(b).toBeGreaterThanOrEqual(MIN_TOPASS_BUDGET_MS); - } - }); - } - } - }); -});