From f3f301dafc3f5975d2e82861ecc608d53971363f Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Aug 2026 08:34:33 +0000 Subject: [PATCH 1/5] fix(cli): route the console route-ledger census through the shared comment mask `console-route-ledger.conformance.test.ts` carried a private hand-rolled stripper: string-aware but REGEX-BLIND, so the `//` that closes a regex literal such as `/^https?:\/\//i` read as a line-comment opener and it deleted to end of line. Measured on this base over the file's real population (`packageSourceFiles()`, 110 non-test `.ts` under `packages/cli/src`, 1.64 MB): 518 non-whitespace characters of live code deleted across 7 files -- `commands/dev.ts`, `commands/init.ts`, `commands/serve.ts`, `commands/start.ts`, `utils/config.ts`, `utils/dev-restart.ts`, `utils/storage-driver.ts` -- plus 25,637 non-whitespace characters of block-comment prose KEPT behind phantom strings opened at a quote inside a regex character class. Converted to `stripComments` from `scripts/js-comment-mask.mjs`. That projection, not `maskComments`: this file reports `file:line` and bare file names, never an offset into the original, and `stripComments` keeps every newline so `lineOf()` still counts the real line. The census verdict does not move on today's tree -- `MOUNT_SHAPED` resolves to exactly `[utils/console.ts]` either way -- which is a fact about this week's sources, not a safety property: a mount added tomorrow inside a swallowed span was invisible to this guard. Deletes the shrink-only ledger row in the same commit, and corrects the gate header prose that said this row "stays recorded". Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw --- .../console-route-ledger.conformance.test.ts | 74 ++++++++----------- scripts/check-comment-mask-adoption.mjs | 9 +-- 2 files changed, 34 insertions(+), 49 deletions(-) diff --git a/packages/cli/src/utils/console-route-ledger.conformance.test.ts b/packages/cli/src/utils/console-route-ledger.conformance.test.ts index 3eb3b24848..8aba4c8c0b 100644 --- a/packages/cli/src/utils/console-route-ledger.conformance.test.ts +++ b/packages/cli/src/utils/console-route-ledger.conformance.test.ts @@ -24,6 +24,19 @@ import { readdirSync, readFileSync, statSync } from 'node:fs'; import { dirname, join, relative, sep } from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, it, expect } from 'vitest'; +// The repo's ONE answer to "is this span a comment, or code?" — its header +// carries the two private-stripper families that drifted apart and the +// parser-differential sweep that measured which way each fails. The private +// scanner this replaces was string-aware but REGEX-BLIND: the doubled slash +// closing `/^https?:\/\//i` read as a line-comment opener and took the rest of +// the line with it, which is the same defect #12398 found live in two sibling +// guards. `stripComments` (not `maskComments`) is the projection this file +// wants: it deletes comment characters but keeps every newline, so the +// `file:line` every finding here reports still points at the real line, and +// nothing in this file reports an offset. The `.mjs` specifier is deliberate; +// `scripts/js-comment-mask.d.mts` beside it is a hand-written declaration, so +// this import needs no `allowJs`. +import { stripComments } from '../../../../scripts/js-comment-mask.mjs'; import { CONSOLE_ROUTE_LEDGER } from './console-route-ledger.js'; /** @@ -59,49 +72,6 @@ const NON_ROUTE_MEMBERS = new Set(['use', 'notFound', 'onError', 'fire', 'fetch' // Scanning machinery // --------------------------------------------------------------------------- -/** - * Strip comments before scanning, PRESERVING newlines inside block comments so - * every finding's `file:line` points at the real line — `console.ts` opens with - * a 35-line header, and reporting a mount 35 lines short makes an accurate - * finding read as a wrong one. - */ -export function stripComments(source: string): string { - let out = ''; - let i = 0; - while (i < source.length) { - const c = source[i]; - const next = source[i + 1]; - if (c === '/' && next === '/') { - while (i < source.length && source[i] !== '\n') i++; - continue; - } - if (c === '/' && next === '*') { - i += 2; - while (i < source.length && !(source[i] === '*' && source[i + 1] === '/')) { - if (source[i] === '\n') out += '\n'; - i++; - } - i += 2; - continue; - } - if (c === '\'' || c === '"' || c === '`') { - const quote = c; - out += c; - i++; - while (i < source.length) { - if (source[i] === '\\') { out += source.slice(i, i + 2); i += 2; continue; } - out += source[i]; - if (source[i] === quote) { i++; break; } - i++; - } - continue; - } - out += c; - i++; - } - return out; -} - /** Module-scope `const NAME = '';` bindings, exported or not. */ export function constantBindings(code: string): Map { const out = new Map(); @@ -380,13 +350,29 @@ describe('cli console route ledger hygiene', () => { }); describe('scan machinery, pinned in both directions', () => { - it('the comment stripper drops prose paths, keeps code paths, and preserves line numbers', () => { + it('the shared stripper drops prose paths, keeps code paths, and preserves line numbers', () => { + // Not a re-pin of `js-comment-mask.mjs` -- that module pins its own + // behaviour. This pins the PROPERTY this census rests on: comment + // characters go, every newline stays, so `lineOf()` below still counts + // the real line. const stripped = stripComments("// app.get('/ghost', h)\n/* a\nb */\napp.get(`/real`, h);\n"); expect(stripped).not.toContain('ghost'); expect(stripped).toContain('/real'); expect(censusOf(['f.ts'], () => "/* a\nb\nc */\napp.get('/x', h);\n").routes[0].line).toBe(4); }); + it('a doubled slash inside a REGEX LITERAL does not swallow the rest of its line', () => { + // The defect the private scanner this file used to carry was measured + // committing on 7 of this package's 110 sources: string-aware but + // regex-blind, it read the `//` that CLOSES `/^https?:\/\//i` as a + // line-comment opener and deleted to end of line. A mount sharing that + // line went with it, and the census reported clean over text it never + // read. Live in `commands/dev.ts`, `commands/serve.ts` and + // `commands/start.ts` at conversion time. + const stripped = stripComments("const ok = /^https?:\\/\\//i.test(u); app.get('/real', h);\n"); + expect(stripped).toContain('/real'); + }); + it('resolves the spellings this package uses, and refuses the rest', () => { const b = constantBindings("export const CONSOLE_PATH = '/_console';\n"); expect(b.get('CONSOLE_PATH')).toBe('/_console'); diff --git a/scripts/check-comment-mask-adoption.mjs b/scripts/check-comment-mask-adoption.mjs index 745618045f..7a5a0124ec 100644 --- a/scripts/check-comment-mask-adoption.mjs +++ b/scripts/check-comment-mask-adoption.mjs @@ -80,9 +80,10 @@ * `packages/cloud-connection/src/marketplace-proxy-plugin.ts` (38 bytes, inside * a declared MOUNT SOURCE). That is the naive-`//` family this gate exists for, * found live rather than argued from the shape. `packages/cli/src/utils/ - * console-route-ledger.conformance.test.ts` is the fourth guard in that family - * and stays recorded: its population limb already stripped, so it was out of - * that card's scope and nobody has re-read its scanner. + * console-route-ledger.conformance.test.ts` was the fourth guard in that family + * and was left recorded here because its population limb already stripped; the + * THIRD SHRINK below converts it, and its scanner was committing the same + * defect on 7 of that package's 110 sources. * * ## SECOND ROUND (#12475): every remaining row now carries a MEASUREMENT * @@ -289,8 +290,6 @@ const LEDGER = new Map([ { shapes: ['regex-block', 'regex-line'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (the two brace-matched interface BODIES interfaceFields() strips -- ResolvedMultiNodeVerdict in packages/services/service-cluster/src/multi-node-gate.ts and MultiNodeGateVerdict in commands/serve.ts, 1,462 chars): agrees with the shared mask. Narrow by construction rather than by luck -- a TSDoc-only interface slice carries no regex literal and no string holding a comment opener. The rest of the file already imports maskComments' }], ['packages/cli/src/commands/serve-verify-security-parity.contract.test.ts', { shapes: ['regex-block', 'regex-line'], verdict: 'specimen', why: 'the private two-regex strip this file carried until its conversion, kept as a NEGATIVE CONTROL that the shared mask beats it; converting it would delete the evidence' }], - ['packages/cli/src/utils/console-route-ledger.conformance.test.ts', - { shapes: ['scanner-decl'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (packageSourceFiles(), 110 non-test .ts under packages/cli/src, 1.6 MB): DELETES LIVE CODE on 9 of them, 10,263 chars. Same defect and same spelling as the two the FIRST SHRINK found -- string-aware but REGEX-BLIND, so the doubled slash inside /^https?://i reads as a line-comment opener and the rest of the line goes: dev.ts:169, serve.ts:3021, start.ts:460 and start.ts:475 are live sites today. It also KEEPS 35,382 chars of block-comment prose swallowed by phantom STRINGS opened at a quote character inside a regex class. This is the fourth guard in that family, the one #12398 left recorded; its MOUNT_SHAPED identity is still exactly [utils/console.ts] under both strippers, so the blindness has not yet cost it a finding' }], ['packages/create-objectstack/src/template-registry.test.ts', { shapes: ['regex-line'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (packages/create-objectstack/src/index.ts): deletes no live code -- it has no block arm at all -- and therefore KEEPS 2,744 chars of block-comment prose, the FABRICATION direction. Its line arm is anchored and reaches only whole-line comments' }], ['packages/drivers/driver-sql/src/live-dialect-matrix.isolation.test.ts', From 6fbba971ad3c0ec40193ecaa486325616d5969db Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Aug 2026 08:51:53 +0000 Subject: [PATCH 2/5] fix(app-showcase): route the inert-wirings guard through the shared comment mask `inert-wirings.test.ts` stripped block comments with the naive `/\*[\s\S]*?\*/` sweep, which has no idea what a string literal is: it opened a phantom comment at a block-comment opener sitting INSIDE a string and ran to the next terminator far below. Measured on this base over the file's real population (`sourceFiles()`, 91 `.ts` under `examples/app-showcase/src`, 0.63 MB): 4,399 non-whitespace characters of live code deleted across 5 files -- `coverage.ts`, `ui/actions/index.ts`, `ui/pages/crm-workbench.page.ts`, `ui/pages/renewals-pipeline.page.ts`, `ui/pages/task-desk.page.ts`. Its line arm dropped only whole-line `//` comments, deliberately never a trailing one, so it also KEPT 225 non-whitespace characters of trailing prose that the guard could have matched on. The shared scanner removes both and is string-aware, so the reason that arm was written anchored -- "a trailing `//` would eat the `//` in a URL inside a string" -- no longer costs anything. Converted to `stripComments`, not `maskComments`: the one guard here reports bare file paths, never a line or an offset. The import makes this package's suite read outside itself for the first time, so it takes its first `CROSS_PACKAGE_TEST_INPUTS` entry and a matching `@objectstack/example-showcase#test` turbo task. Both halves are required: without the turbo `$TURBO_ROOT$` inputs the package's test cache stays keyed on package-local files and a change to the shared mask replays a stale green. `check:cross-package-test-inputs` named both, and named the exact inputs list. The retired-`retryDelayMs` offender set stays empty under the shared mask, so the verdict does not move on today's tree -- luck about this week's sources rather than a safety property. Deletes the shrink-only ledger row in the same commit. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw --- .../app-showcase/test/inert-wirings.test.ts | 18 +++++++++++------- scripts/check-comment-mask-adoption.mjs | 2 -- scripts/cross-package-test-inputs.mjs | 16 ++++++++++++++++ turbo.json | 12 ++++++++++++ 4 files changed, 39 insertions(+), 9 deletions(-) diff --git a/examples/app-showcase/test/inert-wirings.test.ts b/examples/app-showcase/test/inert-wirings.test.ts index 01752d75e0..8c1b8f1c22 100644 --- a/examples/app-showcase/test/inert-wirings.test.ts +++ b/examples/app-showcase/test/inert-wirings.test.ts @@ -2,6 +2,15 @@ import { readdirSync, readFileSync } from 'node:fs'; import { describe, it, expect } from 'vitest'; +// The repo's ONE answer to "is this span a comment, or code?". The naive block +// regex this replaces had no idea what a string literal is: it opened a phantom +// comment at a block-comment opener sitting INSIDE a string and ran to the next +// terminator far below, deleting live code on 5 of this app's 91 sources. +// `stripComments` (not `maskComments`) is the projection this file wants -- the +// one guard below reports bare file paths, never a line or an offset. The +// `.mjs` specifier is deliberate; `scripts/js-comment-mask.d.mts` beside it is a +// hand-written declaration, so this import needs no `allowJs`. +import { stripComments } from '../../../scripts/js-comment-mask.mjs'; import stack from '../objectstack.config.js'; import { PLATFORM_CAPABILITY_NAMES } from '@objectstack/spec/security'; import { FILE_REFERENCE_TYPES, valueSchemaFor } from '@objectstack/spec/data'; @@ -44,15 +53,10 @@ function sourceFiles(dir: string = SRC_ROOT): string[] { * Source text with comments removed, so a source-scan guard judges CODE. * Documentation must stay free to name a retired key (this file's own comments * do, and so do the ones explaining the rename) without tripping the guard that - * bans authoring it. Block comments go first; then whole-line `//` comments — - * never a trailing `//`, which would eat the `//` in a URL inside a string. + * bans authoring it. */ function codeOf(file: string): string { - return readFileSync(file, 'utf8') - .replace(/\/\*[\s\S]*?\*\//g, '') - .split('\n') - .filter((line: string) => !line.trimStart().startsWith('//')) - .join('\n'); + return stripComments(readFileSync(file, 'utf8')); } /** Every `functions` entry, whichever spelling it was authored in. */ diff --git a/scripts/check-comment-mask-adoption.mjs b/scripts/check-comment-mask-adoption.mjs index 7a5a0124ec..b8cf573b83 100644 --- a/scripts/check-comment-mask-adoption.mjs +++ b/scripts/check-comment-mask-adoption.mjs @@ -278,8 +278,6 @@ const VERDICTS = new Set(['unconverted', 'specimen']); * one private stripper for another has not been re-read by anyone. */ const LEDGER = new Map([ - ['examples/app-showcase/test/inert-wirings.test.ts', - { shapes: ['regex-block'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (codeOf() across 91 .ts files under examples/app-showcase/src): DELETES LIVE CODE on 9 of them, 5,987 chars, because the block regex opens a phantom comment at a block-comment opener sitting inside a string literal -- the accept-glob strings in ui/actions/index.ts and the prose strings in coverage.ts -- and runs to the next terminator far below; it also KEEPS 1,813 chars of trailing line-comment prose, which its line filter deliberately never removes. Its own retired-retryDelayMs offender set is empty under both strippers today' }], ['packages/cli/src/commands/artifact-child-env.pin.test.ts', { shapes: ['regex-block'], verdict: 'unconverted', why: 'MEASURED #12475: NOT a stripper feeding a scan. This file\'s guard is ts.createSourceFile; the one block-regex call is a 7-line inline SPECIMEN asserting the text scan it replaced reports that specimen clean, and it does -- 112 chars of live code deleted at the block-comment opener inside the route-wildcard string. Recorded as unconverted only because a shape scan cannot tell a negative control from a caller; converting it would delete the evidence, exactly as for the specimen row below' }], ['packages/cli/src/commands/migrate/multi-value-columns.no-auto-run.test.ts', diff --git a/scripts/cross-package-test-inputs.mjs b/scripts/cross-package-test-inputs.mjs index bf36177d13..c8435610b9 100644 --- a/scripts/cross-package-test-inputs.mjs +++ b/scripts/cross-package-test-inputs.mjs @@ -679,6 +679,22 @@ export const CROSS_PACKAGE_TEST_INPUTS = { 'packages/spec/src/**': ['packages/qa/downstream-contract/test/source-resolution.pin.test.ts'], }, }, + '@objectstack/example-showcase': { + // test/inert-wirings.test.ts imports `stripComments` from + // `js-comment-mask.mjs` to decide which text under this app's `src/` is a + // comment and which is an authored wiring -- the same conversion #12398 + // made for the route-ledger guards. The coupling is real: that guard's + // offender set is a function of the module's scanning behaviour, so a + // change to it has to re-run this package's suite. The `.d.mts` sibling is + // declared alongside it because it is what gives `stripComments` its type, + // so this package's typecheck verdict is a function of it too -- the reason + // the `@objectstack/cli` entry above declares the pair rather than the + // module alone. + globs: [ + 'scripts/js-comment-mask.mjs', + 'scripts/js-comment-mask.d.mts', + ], + }, 'create-objectstack': { // src/template-consistency.test.ts reads doc frontmatter by repo-relative // path to decide which templates are internal. diff --git a/turbo.json b/turbo.json index 2ac1225814..dd20c7455e 100644 --- a/turbo.json +++ b/turbo.json @@ -176,6 +176,18 @@ "$TURBO_ROOT$/scripts/js-comment-mask.d.mts" ] }, + "@objectstack/example-showcase#test": { + "dependsOn": ["^build"], + "outputs": [], + "inputs": [ + "$TURBO_DEFAULT$", + "!dist/**", + "!coverage/**", + "!.turbo/**", + "$TURBO_ROOT$/scripts/js-comment-mask.mjs", + "$TURBO_ROOT$/scripts/js-comment-mask.d.mts" + ] + }, "@objectstack/plugin-auth#test": { "dependsOn": ["^build"], "outputs": [], From a12a53a7d6eb4d5eb7a74850b79736bc62fd95ea Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Aug 2026 09:03:23 +0000 Subject: [PATCH 3/5] fix(plugin-auth): route the rate-limit isolation scan through the shared comment mask `rate-limit-storage-isolation.test.ts` carried a private hand-rolled stripper that tracked the three string forms but was REGEX-BLIND, so the doubled slash closing a literal like `/^https?:\/\//i` read as a line-comment opener and it deleted to end of line. Measured on this base over the file's real population (every `.ts` under `plugin-auth/src` as a declared superset of the resolver-dependent import-graph limb, plus the two cross-package roots it reads whole -- `packages/runtime/src` and `packages/services/service-sms/src` -- 423 files, 7.27 MB): 439 non-whitespace characters of live code deleted across 7 files, this package's own `auth-manager.ts` among them, plus 16,392 non-whitespace characters of comment prose KEPT behind phantom strings. Converted to `stripComments`, not `maskComments`: every finding here reports a package-relative file path and a specifier, never a line or an offset into the original. `check:cross-package-test-inputs` then named the module and its `.d.mts` sibling as paths no declared glob covered, so both are added to this package's globs and to `@objectstack/plugin-auth#test` inputs. The coupling is real rather than a scanner artefact: the import refs this scan extracts -- and therefore its reachability verdict -- are a function of the module's scanning behaviour. The extracted refs are unchanged on all 423 files, so the `#6040` packaging invariant's verdict does not move on today's tree. That is a fact about this week's sources, not a safety property. Deletes the shrink-only ledger row in the same commit. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw --- .../src/rate-limit-storage-isolation.test.ts | 55 ++++--------------- scripts/check-comment-mask-adoption.mjs | 2 - scripts/cross-package-test-inputs.mjs | 10 ++++ turbo.json | 4 +- 4 files changed, 23 insertions(+), 48 deletions(-) diff --git a/packages/plugins/plugin-auth/src/rate-limit-storage-isolation.test.ts b/packages/plugins/plugin-auth/src/rate-limit-storage-isolation.test.ts index 12bf8bfcdc..8d0ef4db7c 100644 --- a/packages/plugins/plugin-auth/src/rate-limit-storage-isolation.test.ts +++ b/packages/plugins/plugin-auth/src/rate-limit-storage-isolation.test.ts @@ -42,6 +42,16 @@ import { describe, it, expect } from 'vitest'; import { readFileSync, existsSync, readdirSync } from 'node:fs'; import { dirname, join, relative, resolve } from 'node:path'; +// The repo's ONE answer to "is this span a comment, or code?". The private +// scanner this replaces tracked the three string forms but was REGEX-BLIND, so +// the doubled slash closing a literal like `/^https?:\/\//i` read as a +// line-comment opener and took the rest of the line -- `auth-manager.ts` in this +// very package was losing that line. `stripComments` (not `maskComments`) is the +// projection this file wants: every finding reports a package-relative FILE PATH +// and a specifier, never a line or an offset into the original. The `.mjs` +// specifier is deliberate; `scripts/js-comment-mask.d.mts` beside it is a +// hand-written declaration, so this import needs no `allowJs`. +import { stripComments } from '../../../../scripts/js-comment-mask.mjs'; /** * Seeded from `__dirname`, not from a `findUp` walk of `process.cwd()`, and not @@ -109,51 +119,6 @@ const SRC = HERE; const RUNTIME_SRC = resolve(REPO, 'packages/runtime/src'); const SERVICE_SMS_SRC = resolve(REPO, 'packages/services/service-sms/src'); -/** - * Strip comments before scanning. The distinction this file turns on — a - * `import type` versus a value `import` of the same specifier — is invisible to - * a raw-text regex the moment a doc comment quotes an import line, and this - * module's own header quotes several. Handles `//`, block comments and the - * three string forms so a `'http://…'` literal is not mistaken for a comment. - */ -function stripComments(src: string): string { - let out = ''; - let i = 0; - while (i < src.length) { - const c = src[i]!; - const next = src[i + 1]; - if (c === '/' && next === '/') { - while (i < src.length && src[i] !== '\n') i++; - continue; - } - if (c === '/' && next === '*') { - i += 2; - while (i < src.length && !(src[i] === '*' && src[i + 1] === '/')) i++; - i += 2; - continue; - } - if (c === "'" || c === '"' || c === '`') { - out += c; - i++; - while (i < src.length && src[i] !== c) { - if (src[i] === '\\') { - out += src[i]! + (src[i + 1] ?? ''); - i += 2; - continue; - } - out += src[i]; - i++; - } - out += c; - i++; - continue; - } - out += c; - i++; - } - return out; -} - interface Ref { spec: string; /** `import type … from` / `export type … from` — erased at build, costs nothing at runtime. */ diff --git a/scripts/check-comment-mask-adoption.mjs b/scripts/check-comment-mask-adoption.mjs index b8cf573b83..31b0de06d0 100644 --- a/scripts/check-comment-mask-adoption.mjs +++ b/scripts/check-comment-mask-adoption.mjs @@ -304,8 +304,6 @@ const LEDGER = new Map([ { shapes: ['regex-block', 'regex-line'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (codeOf() across all 11 *.test.ts in packages/metadata-protocol/src/migrations): deletes no live code; KEEPS 130 chars of trailing line-comment prose on 4 files, the FABRICATION direction. LIVE_TEST_FILES resolves to the same three files under both strippers, so the derived population this file guards is unaffected today' }], ['packages/plugins/plugin-approvals/src/admin-exemption-retired.test.ts', { shapes: ['scanner-decl'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (collectSources(), 25 non-test .ts under packages/plugins/plugin-approvals/src, 463 KB): agrees with the shared mask. It is REGEX-BLIND like its plugin-auth twin below and survives only because this package\'s sources carry no regex literal holding a quote character and no doubled slash inside one -- a fact about the population, not about the scanner' }], - ['packages/plugins/plugin-auth/src/rate-limit-storage-isolation.test.ts', - { shapes: ['scanner-decl'], verdict: 'unconverted', why: 'MEASURED #12475 over 423 files / 7.3 MB -- every .ts under plugin-auth/src as a declared SUPERSET of the import-graph limb (whose exact set is resolver-dependent), plus the two cross-package roots it reads whole, packages/runtime/src and packages/services/service-sms/src. 15 files disagree: DELETES 6,375 chars of live code, the regex-blind doubled-slash defect again (auth-manager.ts loses the line holding /^https?://i), and KEEPS 22,329 chars of comment prose behind phantom strings. Its own extracted import refs are identical under both strippers on every one of the 423 files, so the reachability verdict has not moved' }], ['packages/qa/downstream-contract/test/source-resolution.pin.test.ts', { shapes: ['regex-line'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (packages/qa/downstream-contract/tsconfig.json, 5,145 chars of JSONC): agrees with the shared mask. An anchored line arm over a file with no regex literals and no block comments -- the narrowest population in this ledger' }], ['packages/runtime/src/error-envelope.conformance.test.ts', diff --git a/scripts/cross-package-test-inputs.mjs b/scripts/cross-package-test-inputs.mjs index c8435610b9..fd97b139f8 100644 --- a/scripts/cross-package-test-inputs.mjs +++ b/scripts/cross-package-test-inputs.mjs @@ -532,6 +532,16 @@ export const CROSS_PACKAGE_TEST_INPUTS = { 'scripts/check-published-files.mjs', 'scripts/check-cross-package-test-inputs.mjs', 'packages/types/src/node-isolation.test.ts', + // That same test imports `stripComments` from `js-comment-mask.mjs` to + // separate code from prose in the 423 sources it walks -- the conversion + // #12398 began. Unlike the three mentions above this is a real coupling + // rather than a scanner artefact: the import refs the scan extracts, and + // therefore its reachability verdict, are a function of the module's + // scanning behaviour. The `.d.mts` sibling is declared alongside it + // because it is what gives `stripComments` its type, so this package's + // typecheck verdict is a function of it too. + 'scripts/js-comment-mask.mjs', + 'scripts/js-comment-mask.d.mts', ], heldBy: { // The pair #10566 was measured on. That test's walk of `PACKAGES_DIR` diff --git a/turbo.json b/turbo.json index dd20c7455e..9f9a37402d 100644 --- a/turbo.json +++ b/turbo.json @@ -202,7 +202,9 @@ "$TURBO_ROOT$/packages/services/service-sms/src/**", "$TURBO_ROOT$/scripts/check-published-files.mjs", "$TURBO_ROOT$/scripts/check-cross-package-test-inputs.mjs", - "$TURBO_ROOT$/packages/types/src/node-isolation.test.ts" + "$TURBO_ROOT$/packages/types/src/node-isolation.test.ts", + "$TURBO_ROOT$/scripts/js-comment-mask.mjs", + "$TURBO_ROOT$/scripts/js-comment-mask.d.mts" ] }, "@objectstack/plugin-auth#typecheck": { From 4c24a5865bfbece6ca4c21d4c97077c52ddf5295 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Aug 2026 09:08:19 +0000 Subject: [PATCH 4/5] fix(driver-sql): route the live-dialect isolation scan through the shared comment mask `live-dialect-matrix.isolation.test.ts` stripped comments with the naive two-regex pair. Measured on this base over the file's real population (`codeOf()` across all 152 `*.test.ts` in `packages/drivers/driver-sql/src`, 2.00 MB): 54 files disagree with the shared scanner. Mostly the safe direction -- 4,420 non-whitespace characters of trailing line-comment prose KEPT by the anchored line arm, which the guard could have matched on -- but the block arm also DELETES 66 non-whitespace characters of live code in `logger-receiver-detach.test.ts`, where a fixture STRING quotes a docblock and the sweep eats the string. Converted to `stripComments`, not `maskComments`: the guard reports bare file names, never a line or an offset. `check:cross-package-test-inputs` then named this package as reading outside itself for the first time, so it takes its first `CROSS_PACKAGE_TEST_INPUTS` entry and a matching `@objectstack/driver-sql#test` turbo task, without which the package's test cache stays keyed on package-local files and a change to the shared mask replays a stale green. The direct-`OS_TEST_*_URL` offender set stays empty under the shared mask, so the verdict does not move on today's tree -- a fact about this week's sources, not a safety property. Deletes the shrink-only ledger row in the same commit. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw --- .../src/live-dialect-matrix.isolation.test.ts | 14 ++++++++++---- scripts/check-comment-mask-adoption.mjs | 2 -- scripts/cross-package-test-inputs.mjs | 14 ++++++++++++++ turbo.json | 12 ++++++++++++ 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/packages/drivers/driver-sql/src/live-dialect-matrix.isolation.test.ts b/packages/drivers/driver-sql/src/live-dialect-matrix.isolation.test.ts index bfd44646b2..bb5491a4d9 100644 --- a/packages/drivers/driver-sql/src/live-dialect-matrix.isolation.test.ts +++ b/packages/drivers/driver-sql/src/live-dialect-matrix.isolation.test.ts @@ -43,6 +43,14 @@ import { readFileSync, readdirSync } from 'node:fs'; import { SqlDriver } from '../src/index.js'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; +// The repo's ONE answer to "is this span a comment, or code?". The naive block +// regex this replaces had no idea what a string literal is: in +// `logger-receiver-detach.test.ts` a fixture STRING quotes a docblock and the +// sweep ate the string. `stripComments` (not `maskComments`) is the projection +// this file wants -- the guard below reports bare file names, never a line or +// an offset. The `.mjs` specifier is deliberate; `scripts/js-comment-mask.d.mts` +// beside it is a hand-written declaration, so this import needs no `allowJs`. +import { stripComments } from '../../../../scripts/js-comment-mask.mjs'; import { LIVE_SCHEMA_PREFIX, MYSQL_CELL, @@ -127,11 +135,9 @@ describe('live-dialect matrix — per-file schema isolation (#9350)', () => { }); describe('live-dialect matrix — the cell is the only route to a live server (#9350)', () => { - /** Source with line and block comments removed, so prose about the env var is not a hit. */ + /** Source with comments removed, so prose about the env var is not a hit. */ const codeOf = (file: string): string => - readFileSync(join(SRC_DIR, file), 'utf8') - .replace(/\/\*[\s\S]*?\*\//g, '') - .replace(/^[ \t]*\/\/.*$/gm, ''); + stripComments(readFileSync(join(SRC_DIR, file), 'utf8')); /** * The needle is ASSEMBLED rather than written as a literal. diff --git a/scripts/check-comment-mask-adoption.mjs b/scripts/check-comment-mask-adoption.mjs index 31b0de06d0..a4d16252f9 100644 --- a/scripts/check-comment-mask-adoption.mjs +++ b/scripts/check-comment-mask-adoption.mjs @@ -290,8 +290,6 @@ const LEDGER = new Map([ { shapes: ['regex-block', 'regex-line'], verdict: 'specimen', why: 'the private two-regex strip this file carried until its conversion, kept as a NEGATIVE CONTROL that the shared mask beats it; converting it would delete the evidence' }], ['packages/create-objectstack/src/template-registry.test.ts', { shapes: ['regex-line'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (packages/create-objectstack/src/index.ts): deletes no live code -- it has no block arm at all -- and therefore KEEPS 2,744 chars of block-comment prose, the FABRICATION direction. Its line arm is anchored and reaches only whole-line comments' }], - ['packages/drivers/driver-sql/src/live-dialect-matrix.isolation.test.ts', - { shapes: ['regex-block', 'regex-line'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (codeOf() across all 152 *.test.ts in packages/drivers/driver-sql/src, 2.0 MB): 54 files disagree. Mostly the safe direction -- 4,582 chars of trailing line-comment prose KEPT by the anchored line arm -- but the block arm also DELETES 228 chars of live code in logger-receiver-detach.test.ts, where a fixture STRING quotes a docblock and the naive block regex eats the string. Its own direct-OS_TEST_URL offender set is empty under both strippers' }], ['packages/lint/src/validate-expressions.test.ts', { shapes: ['regex-block', 'regex-line'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (packages/lint/src/validate-expressions.ts, 75,403 chars): agrees with the shared mask. The unanchored trailing line arm is the dangerous spelling and it holds here only because that one rule source happens to carry no doubled slash inside a regex literal or string -- a property of today\'s file, not of this stripper' }], ['packages/lint/src/validate-org-axis-red-lines.test.ts', diff --git a/scripts/cross-package-test-inputs.mjs b/scripts/cross-package-test-inputs.mjs index fd97b139f8..ce7f7a1076 100644 --- a/scripts/cross-package-test-inputs.mjs +++ b/scripts/cross-package-test-inputs.mjs @@ -689,6 +689,20 @@ export const CROSS_PACKAGE_TEST_INPUTS = { 'packages/spec/src/**': ['packages/qa/downstream-contract/test/source-resolution.pin.test.ts'], }, }, + '@objectstack/driver-sql': { + // src/live-dialect-matrix.isolation.test.ts imports `stripComments` from + // `js-comment-mask.mjs` to decide which text in this package's 152 test + // sources is a comment and which is a direct `OS_TEST_*_URL` read -- the + // conversion #12398 began. The coupling is real: that guard's offender set + // is a function of the module's scanning behaviour, so a change to it has to + // re-run this package's suite. The `.d.mts` sibling is declared alongside it + // because it is what gives `stripComments` its type, so this package's + // typecheck verdict is a function of it too. + globs: [ + 'scripts/js-comment-mask.mjs', + 'scripts/js-comment-mask.d.mts', + ], + }, '@objectstack/example-showcase': { // test/inert-wirings.test.ts imports `stripComments` from // `js-comment-mask.mjs` to decide which text under this app's `src/` is a diff --git a/turbo.json b/turbo.json index 9f9a37402d..f27a7971f2 100644 --- a/turbo.json +++ b/turbo.json @@ -176,6 +176,18 @@ "$TURBO_ROOT$/scripts/js-comment-mask.d.mts" ] }, + "@objectstack/driver-sql#test": { + "dependsOn": ["^build"], + "outputs": [], + "inputs": [ + "$TURBO_DEFAULT$", + "!dist/**", + "!coverage/**", + "!.turbo/**", + "$TURBO_ROOT$/scripts/js-comment-mask.mjs", + "$TURBO_ROOT$/scripts/js-comment-mask.d.mts" + ] + }, "@objectstack/example-showcase#test": { "dependsOn": ["^build"], "outputs": [], From 2659125a0f716d8821c795cc79830c6a1279ffae Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Aug 2026 09:13:54 +0000 Subject: [PATCH 5/5] fix(runtime): route the error-envelope conformance scan through the shared comment mask `error-envelope.conformance.test.ts` stripped comments with the naive two-regex pair, whose trailing arm is UNANCHORED: a doubled slash anywhere on a line opened a phantom comment and the rest of the line went. Measured on this base over the file's real population (the ten modules in its own `MODULES` list, 0.39 MB): 166 non-whitespace characters of live code deleted on 2 of them and nothing kept -- purely the blind direction. `dispatcher-plugin.ts` loses the rest of the line at a doubled slash in a mount path inside a template literal, `domains/mcp.ts` at the one in an https URL it builds. Converted to `stripComments`, not `maskComments`: these guards report match counts and matched text, never a line or an offset. `check:cross-package-test-inputs` then named this package as reading outside itself for the first time, so it takes its first `CROSS_PACKAGE_TEST_INPUTS` entry and a matching `@objectstack/runtime#test` turbo task, without which the package's test cache stays keyed on package-local files and a change to the shared mask replays a stale green. All four per-module counts (numeric code, type-as-code, envelopes, builder calls) are identical across all ten modules under both strippers, so the `#3842` numeric-code pin's verdict does not move on today's tree. That is a fact about this week's sources, not a safety property: a numeric `code` written tomorrow after a doubled slash on the same line was invisible to this guard. Deletes the shrink-only ledger row in the same commit, taking the shrink-only ledger from 19 rows to 14. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01CPrUz21stTFhJRUirdc4yw --- .../runtime/src/error-envelope.conformance.test.ts | 14 +++++++++++--- scripts/check-comment-mask-adoption.mjs | 2 -- scripts/cross-package-test-inputs.mjs | 14 ++++++++++++++ turbo.json | 12 ++++++++++++ 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/packages/runtime/src/error-envelope.conformance.test.ts b/packages/runtime/src/error-envelope.conformance.test.ts index f63a0460fd..a4338be8f7 100644 --- a/packages/runtime/src/error-envelope.conformance.test.ts +++ b/packages/runtime/src/error-envelope.conformance.test.ts @@ -27,6 +27,16 @@ import { describe, it, expect, vi } from 'vitest'; import { readFileSync } from 'node:fs'; +// The repo's ONE answer to "is this span a comment, or code?". The naive pair +// this replaces had an UNANCHORED trailing arm, so a doubled slash anywhere on a +// line opened a phantom comment and the rest of the line went -- measured +// eating route paths inside template literals in `dispatcher-plugin.ts` and an +// https URL that `domains/mcp.ts` builds. `stripComments` (not `maskComments`) +// is the projection these guards want: they report match counts and matched +// text, never a line or an offset. The `.mjs` specifier is deliberate; +// `scripts/js-comment-mask.d.mts` beside it is a hand-written declaration, so +// this import needs no `allowJs`. +import { stripComments } from '../../../scripts/js-comment-mask.mjs'; import { ApiErrorSchema, BaseResponseSchema, @@ -455,9 +465,7 @@ describe('#3842 — no dispatcher module may reintroduce the drift', () => { // Comments stripped first: these modules' own prose quotes the old shape, // and a doc comment is not a code path. const read = (file: string) => - readFileSync(new URL(file, import.meta.url), 'utf8') - .replace(/\/\*[\s\S]*?\*\//g, '') - .replace(/\/\/[^\n]*/g, ''); + stripComments(readFileSync(new URL(file, import.meta.url), 'utf8')); /** Every module that can put a body on this wire surface. */ const MODULES = [ diff --git a/scripts/check-comment-mask-adoption.mjs b/scripts/check-comment-mask-adoption.mjs index a4d16252f9..52d1b3abf4 100644 --- a/scripts/check-comment-mask-adoption.mjs +++ b/scripts/check-comment-mask-adoption.mjs @@ -304,8 +304,6 @@ const LEDGER = new Map([ { shapes: ['scanner-decl'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (collectSources(), 25 non-test .ts under packages/plugins/plugin-approvals/src, 463 KB): agrees with the shared mask. It is REGEX-BLIND like its plugin-auth twin below and survives only because this package\'s sources carry no regex literal holding a quote character and no doubled slash inside one -- a fact about the population, not about the scanner' }], ['packages/qa/downstream-contract/test/source-resolution.pin.test.ts', { shapes: ['regex-line'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (packages/qa/downstream-contract/tsconfig.json, 5,145 chars of JSONC): agrees with the shared mask. An anchored line arm over a file with no regex literals and no block comments -- the narrowest population in this ledger' }], - ['packages/runtime/src/error-envelope.conformance.test.ts', - { shapes: ['regex-block', 'regex-line'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (the 10 modules in its own MODULES list, 390 KB): DELETES 166 chars of live code on 2 of them and keeps nothing -- purely the blind direction. The unanchored trailing line arm eats route paths inside template literals: dispatcher-plugin.ts loses the rest of the line at a doubled slash in a mount path, domains/mcp.ts at the one in an https URL it builds. All four of its own per-module counts are identical under both strippers today, so the numeric-code pin has not yet been cheated' }], ['packages/spec/scripts/lazify-schemas.ts', { shapes: ['regex-block', 'regex-line'], verdict: 'unconverted', why: 'MEASURED #12475 over its real population (listZodFiles(), 208 *.zod.ts under packages/spec/src, 4.4 MB): the two COMMENT arms of the import-block matcher claim no character the shared scanner calls code, on any file. Sound by construction rather than by luck: both arms are anchored to a line start after indent only, and the matcher runs at the file top where no template literal can be open. (First measured as 208/208 disagreeing -- that was the instrument counting the newline each arm consumes; see the SECOND ROUND note.)' }], ]); diff --git a/scripts/cross-package-test-inputs.mjs b/scripts/cross-package-test-inputs.mjs index ce7f7a1076..b005cc1b07 100644 --- a/scripts/cross-package-test-inputs.mjs +++ b/scripts/cross-package-test-inputs.mjs @@ -689,6 +689,20 @@ export const CROSS_PACKAGE_TEST_INPUTS = { 'packages/spec/src/**': ['packages/qa/downstream-contract/test/source-resolution.pin.test.ts'], }, }, + '@objectstack/runtime': { + // src/error-envelope.conformance.test.ts imports `stripComments` from + // `js-comment-mask.mjs` to decide which text in the ten dispatcher modules + // it scans is a comment and which emits an error body -- the conversion + // #12398 began. The coupling is real: the four per-module counts that guard + // reports are a function of the module's scanning behaviour, so a change to + // it has to re-run this package's suite. The `.d.mts` sibling is declared + // alongside it because it is what gives `stripComments` its type, so this + // package's typecheck verdict is a function of it too. + globs: [ + 'scripts/js-comment-mask.mjs', + 'scripts/js-comment-mask.d.mts', + ], + }, '@objectstack/driver-sql': { // src/live-dialect-matrix.isolation.test.ts imports `stripComments` from // `js-comment-mask.mjs` to decide which text in this package's 152 test diff --git a/turbo.json b/turbo.json index f27a7971f2..a995a3adae 100644 --- a/turbo.json +++ b/turbo.json @@ -176,6 +176,18 @@ "$TURBO_ROOT$/scripts/js-comment-mask.d.mts" ] }, + "@objectstack/runtime#test": { + "dependsOn": ["^build"], + "outputs": [], + "inputs": [ + "$TURBO_DEFAULT$", + "!dist/**", + "!coverage/**", + "!.turbo/**", + "$TURBO_ROOT$/scripts/js-comment-mask.mjs", + "$TURBO_ROOT$/scripts/js-comment-mask.d.mts" + ] + }, "@objectstack/driver-sql#test": { "dependsOn": ["^build"], "outputs": [],