From 350aadd3d40afa6ce9f098f5b7a2e87199e44d6f Mon Sep 17 00:00:00 2001 From: sebi Date: Tue, 18 Aug 2026 21:42:11 -0500 Subject: [PATCH 1/3] fix(cli,ai,mcp): an apostrophe in JSX text turned the errors gate off for a whole file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Second half of the tier-4/5 sweep, split from #152 at ~60 files each. The gate hole was LIVE, not latent. `maskLiterals` treated `'` as a string opener, so an apostrophe in JSX text blanked everything up to the next quote and `scanFixes` returned nothing for the rest of the file — while `scanCodes` kept passing, masking it. `packages/http/src/errors.ts` contains `…already route "${input.otherRoute}"'s`, so EIGHT real `fix:` lines in that file had never been checked by `x verify`. All eight pass now. One test asserted "nothing in the installed framework raises X_DRAINING" — disproved by `draining()` in the very file the gate had stopped reading. The chosen fix is the cheap one — a quote with no partner on its own line is text, copying `endOfRegex`'s existing rule — and its gap is stated rather than hidden: two apostrophes on one line still blank the span between them. Blast radius drops from rest-of-file to one line. Full coverage needs a JSX tokenizer, which the file's own header rules out. `agent()` sent Anthropic a transcript it rejects, in TWO places. A turn emitting a tool call and `respond` together replayed the `respond` tool_use with no matching tool_result. The repair path had the same hole and is far more reachable: ANY output-schema mismatch in an agent() run was a 400. The loop now answers the superseded `respond` with an is_error result telling the model to read the tool results and answer again — rather than discarding a block the model emitted, or using an answer composed before the tools it called had run. MCP's `additionalProperties: false` accepted and dropped every argument named after an Object.prototype member. Third instance of the class this release, and `Object.hasOwn` alone was NOT sufficient — it turns the `__proto__` drop into a `__proto__` re-prototype of the record the handler reads, so every write goes through Object.defineProperty. A page with zero executable JavaScript could fail its JS budget, with a fix line naming an import that does not exist. `x verify --workers 5000` was accepted although both summaries say max 8 — 842 concurrent Bun processes, each with the module graph and a cloned database. Plus seven more instances of the caught-value totality class in ai and mcp, and the smaller CLI set: dev-traces dropping every request that arrived with an inbound traceparent, a bare Error on a taken metrics port, METRICS_PORT ignored in dev, a missing binary whose fix line checks nothing about missing binaries, JSONC in a root tsconfig silently disabling X_PACKAGE_UNREFERENCED. Co-Authored-By: Claude Opus 5 (1M context) --- CHANGELOG.md | 39 ++++ packages/ai/CLAUDE.md | 34 +++ packages/ai/src/agent-transcript.test.ts | 218 +++++++++++++++++++ packages/ai/src/agent-transcript.ts | 94 ++++++++ packages/ai/src/agent.ts | 59 ++--- packages/ai/src/gateway.test.ts | 48 ++++ packages/ai/src/gateway.ts | 26 ++- packages/ai/src/hive-pool.test.ts | 105 +++++++++ packages/ai/src/hive-pool.ts | 17 +- packages/ai/src/hive-result.ts | 7 + packages/ai/src/tools.test.ts | 100 +++++++++ packages/ai/src/tools.ts | 45 +++- packages/cli/CLAUDE.md | 18 +- packages/cli/src/budgets.test.ts | 21 ++ packages/cli/src/budgets.ts | 20 +- packages/cli/src/cmd-db-branch.test.ts | 30 ++- packages/cli/src/cmd-db-branch.ts | 8 +- packages/cli/src/cmd-dev.test.ts | 19 +- packages/cli/src/cmd-dev.ts | 11 +- packages/cli/src/cmd-doctor.test.ts | 43 +++- packages/cli/src/cmd-doctor.ts | 29 ++- packages/cli/src/cmd-help.test.ts | 19 ++ packages/cli/src/cmd-new.ts | 2 +- packages/cli/src/cmd-test.test.ts | 40 ++++ packages/cli/src/cmd-test.ts | 14 +- packages/cli/src/cmd-verify.test.ts | 50 ++++- packages/cli/src/cmd-verify.ts | 26 ++- packages/cli/src/db-branch.test.ts | 18 ++ packages/cli/src/db-branch.ts | 18 ++ packages/cli/src/dev-assets.test.ts | 27 +++ packages/cli/src/dev-assets.ts | 25 ++- packages/cli/src/dev-roles.ts | 8 +- packages/cli/src/dev-storage.test.ts | 20 +- packages/cli/src/dev-storage.ts | 10 +- packages/cli/src/dev-traces.test.ts | 33 ++- packages/cli/src/dev-traces.ts | 30 ++- packages/cli/src/error-fixes.test.ts | 60 +++-- packages/cli/src/exec.test.ts | 44 ++++ packages/cli/src/exec.ts | 43 +++- packages/cli/src/metrics-endpoint.test.ts | 25 ++- packages/cli/src/metrics-endpoint.ts | 61 ++++-- packages/cli/src/serve.test.ts | 16 ++ packages/cli/src/serve.ts | 18 +- packages/cli/src/storage-surfaces.test.ts | 20 +- packages/cli/src/test-workers.ts | 5 +- packages/cli/src/ts-scan.test.ts | 25 +++ packages/cli/src/ts-scan.ts | 15 +- packages/cli/src/tsconfig-references.test.ts | 28 +++ packages/cli/src/tsconfig-references.ts | 29 ++- packages/manifest/CLAUDE.md | 5 + packages/manifest/src/emit.test.ts | 16 ++ packages/manifest/src/emit.ts | 11 +- packages/mcp/CLAUDE.md | 22 ++ packages/mcp/src/query-limits.test.ts | 23 ++ packages/mcp/src/query-limits.ts | 16 +- packages/mcp/src/registry.test.ts | 30 +++ packages/mcp/src/registry.ts | 22 +- packages/mcp/src/server.test.ts | 73 ++++++- packages/mcp/src/server.ts | 21 +- packages/mcp/src/validate-args.test.ts | 66 ++++++ packages/mcp/src/validate-args.ts | 22 +- 61 files changed, 1846 insertions(+), 201 deletions(-) create mode 100644 packages/ai/src/agent-transcript.test.ts create mode 100644 packages/ai/src/agent-transcript.ts create mode 100644 packages/ai/src/hive-pool.test.ts create mode 100644 packages/cli/src/exec.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index aeb6b97b..d8800c62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -302,6 +302,45 @@ Semver applies from 1.0.0. A breaking change to a documented API needs a major the throws that caused it reached the job retry classifier as bare `Error`s with no code; and restored `localStorage` was written on `about:blank` where it can never reach the site's origin. +- **An apostrophe in JSX text silently disabled the `errors` gate for a whole file, and it was not + hypothetical.** `maskLiterals` treated `'` as a literal opener and blanked everything up to the + next one, so `scanFixes` returned nothing for the rest of the file — while `scanCodes` kept + passing, masking the hole. `packages/http/src/errors.ts` contains + `…already route "${input.otherRoute}"'s`, so **eight real `fix:` lines in that file had never been + checked**. All eight pass now. One test asserted "nothing in the installed framework raises + `X_DRAINING`" — disproved by `draining()` in the very file the gate had stopped reading. + +- **A page with zero executable JavaScript could fail its JS budget**, with a `fix:` line naming an + import that does not exist. Every inline `', dir)).toBe(14); }); + + // The bug this guards: a document that makes the browser execute NOTHING failed its JS budget, + // with a `fix:` telling the author to move an import that does not exist. `@ultimat3/seo` emits + // `application/ld+json` for structured data and `@ultimat3/render` emits `application/json` for + // island props — both are data the parser never runs. + test('a JSON-typed script is data, so a page of only data ships zero JS', async () => { + const document = + '' + + ''; + expect(await measureJsBytes(document, out)).toBe(0); + }); + + test('the rule is the type ending in json, not the one literal type', async () => { + expect(await measureJsBytes('', out)).toBe(0); + expect(await measureJsBytes('', out)).toBe( + 0, + ); + // A module is code, and so is a type nobody declared. + expect(await measureJsBytes('', out)).toBe(7); + expect(await measureJsBytes('', out)).toBe(7); + }); }); describe('unit · writeBuildStats is what makes X_BUDGET_UNMEASURED reachable', () => { diff --git a/packages/cli/src/budgets.ts b/packages/cli/src/budgets.ts index 6a571c93..46207df0 100644 --- a/packages/cli/src/budgets.ts +++ b/packages/cli/src/budgets.ts @@ -123,6 +123,17 @@ export async function readBuildStats(root: string): Promise[^>]*)>(?[\s\S]*?)<\/script>/g; const SRC_ATTR = /\ssrc="(?[^"]*)"/; +const TYPE_ATTR = /\stype="(?[^"]*)"/; + +/** + * `application/ld+json`, `application/json`, any `…+json`: the body is data, not code — the rule + * `@ultimat3/render`'s `head.ts` already states, restated because its `carriesJson` reads a + * `HeadTag` and is not exported, and this side has an attribute string off the emitted document. + * Without it a page shipping only `meta.ld` structured data and island props measured 8kb of JS + * and failed a 2kb budget with a `fix:` naming an import chain that does not exist. + */ +const carriesJson = (attrs: string): boolean => + (TYPE_ATTR.exec(attrs)?.groups?.['type'] ?? '').trim().toLowerCase().endsWith('json'); /** * An island's chunk is reached by `import()` from inside the hydration runtime, so it never appears * as a `'; + expect(await measureJsBytes(withCharset, out)).toBe(0); + expect( + await measureJsBytes('', out), + ).toBe(0); + // And the parameter cannot make code data: the suffix test still decides, on the type alone. + expect( + await measureJsBytes('', out), + ).toBe(7); + }); + test('the rule is the type ending in json, not the one literal type', async () => { expect(await measureJsBytes('', out)).toBe(0); expect(await measureJsBytes('', out)).toBe( diff --git a/packages/cli/src/budgets.ts b/packages/cli/src/budgets.ts index 46207df0..f0abbac2 100644 --- a/packages/cli/src/budgets.ts +++ b/packages/cli/src/budgets.ts @@ -132,8 +132,14 @@ const TYPE_ATTR = /\stype="(?[^"]*)"/; * Without it a page shipping only `meta.ld` structured data and island props measured 8kb of JS * and failed a 2kb budget with a `fix:` naming an import chain that does not exist. */ -const carriesJson = (attrs: string): boolean => - (TYPE_ATTR.exec(attrs)?.groups?.['type'] ?? '').trim().toLowerCase().endsWith('json'); +const carriesJson = (attrs: string): boolean => { + // Everything from the first `;` is a MIME PARAMETER and not the type: a real document writes + // `type="application/ld+json; charset=utf-8"`, which does not END with `json`, so the suffix + // test alone charged an SEO structured-data block as executable JavaScript all over again. + const [type = ''] = (TYPE_ATTR.exec(attrs)?.groups?.['type'] ?? '').split(';'); + return type.trim().toLowerCase().endsWith('json'); +}; + /** * An island's chunk is reached by `import()` from inside the hydration runtime, so it never appears * as a `