Skip to content

Commit 956e010

Browse files
os-elon-muskclaude
andauthored
fix(tooling): a derived parse's verdict comes BACK, so one unparseable synthesis stops taking the whole run (#19112)
Refs #19077 (item 1 — the returnable derived-parse door; the census-side application is item 2, sequenced after PR #19073) ⚠️ **Seat edit at 2026-09-18T20:27Z, `Seat: domain:spec#3`, `session_019srGWGCBBCBHqcDoRZpQRh` — the closing keyword was changed to a reference, and that is the only change to this body.** The dispatched dev reported `status: rework` **deliberately and per its dispatch word**: acceptance input 1's census-level half ("that receiver is CLASSIFIED") cannot be delivered from this surface, because it needs `scripts/tenant-audit-census.mjs`, which this seat serialised behind open PR #19073. ⇒ this PR makes the repair available and provable; it does **not** apply it. A `Fixes` keyword would have closed card #19077 on merge while the defect it describes was still live on `main` — the charter's partial-landing rule (`SKILL.md`: 部分落地 PR 带 `Refs #N (item k)`,⛔ 不 `Fixes`) is exactly this case. ⛔ Nothing about the diff, its tests or its declarations was touched by this edit. `Clause-②: no` — measured, not assumed; the three readings are under "Clause ② and the changeset" below. ⚠️ Notation: type texts in this body are written with `[...]` where the source has angle brackets (`Promise[void]` is `Promise` of `void` in the real fixture). The body sanitizer eats tag-shaped fragments, backticked ones included, so the real spelling cannot be written here. The fixtures in `scripts/ts-parse.mjs` carry the real characters. ## What was wrong `scripts/ts-parse.mjs` answers one question — "could I read this tree?" — and answers it by ending the process. That is right for a tree the gate's own author controls, and the module's header argues it at length: a throw is swallowable, and `try { parse } catch { continue }` is already written in this repo. `tenant-audit-census.mjs` was asking a **different** question through that same door. It reads a receiver's declared type out of a source that parsed, stores the type's text whitespace-collapsed (`:459`, `getText(sf).replace(/\s+/g, ' ')`), and later re-parses the stored text as a synthetic type alias (`:723`, `parseSourceFile('census-receiver-type.ts', ...)`) to ask whether it declares a write door. A type literal may separate its members by a newline alone. The collapse turns that separator into nothing, the synthetic alias does not parse, and the census takes `EXIT_UNPARSEABLE` for the whole run — with a refusal naming a file that does not exist in the tree. Reproduced on this branch's base (`origin/main@07c6f82`) before any edit: ``` STORED: "{ insert(object: string, data: unknown): Promise[void] find(object: string, query: unknown): Promise[void] }" LIT CONTROL typeTextDeclaresEngineDoor = true [the semicolon spelling, from the card] about to classify the newline-only spelling x ts-parse — REFUSING to scan a source that does not parse. file census-receiver-type.ts errors 1 parse diagnostic(s) from TypeScript 6.0.3 census-receiver-type.ts:1:78 ';' expected. REPRO EXIT=3 ``` And the shape itself is legal TypeScript — so the collapse is the defect, not the receiver: | type text | alias parses? | |---|---| | authored, members separated by a newline | **yes**, 0 diagnostics | | the same text after `.replace(/\s+/g, ' ')` | **NO**, 1: `';' expected.` | | the semicolon spelling (the card's lit control) | **yes**, 0 diagnostics | The refusal's own reasoning is false for this call: *"a file the gate could not read, reported as a file with nothing to report"*. The gate read the file. What did not round-trip is the gate's own re-serialisation of a fragment of it — a fact about the synthesiser, never about the corpus. ## The repair `parseDerivedText(origin, fileName, text, scriptKind)` answers the synthesis question and hands the verdict **back**, so one unanswerable site costs one site instead of every site. The floor is held **by construction rather than by review**. The door requires an `origin` — a `ts.SourceFile` this module has already certified — and the only two doors that certify one (`parseSourceFile`, `createProgramChecked`) **exit** on a source that does not parse. So a source the gate could not read **cannot reach** the returnable door: it hits the refusal first. An uncertified origin is itself a refusal (`EXIT_DERIVED_MISUSE`, exit 4), an exit and not a throw for the reason the module already gives. Two deliberate divergences from `packages/lint/src/checked-parse.ts`, the repo's existing ruling on this same axis (it reports rather than refuses "because a `scripts/**` gate audits a tree its own author controls"), both in the strict direction: - **no tree comes back on failure** (`sourceFile: null`), where the sibling always returns the recovered one. There is no recovered wreckage to walk and no reading of the result that says "nothing to report". A caller that forgets to branch gets a TypeError — loud, non-zero. - **a rejection is counted as a rejection, never as a refusal.** `parseCensus()` gains `derived` and `rejections`. A refusal ended the run, so every later source went unread and the numerator is short by an unknown amount; a rejection left the run going. One combined total would make the report whose job is to say how much was measured say it wrong. ## ⭐ Deliverable 1 — the dependency census: who depends on the fact that it EXITS Instrument: TypeScript AST over **every** git-tracked `.mjs/.cjs/.js/.mts/.cts/.ts/.tsx` file in the repo (**6873** scanned), resolving each module specifier to an absolute path and counting **call expressions** per imported binding. ⛔ No `head -N` anywhere in the pipeline. Measured at `b09c8f550`. | population | count | |---|---| | files whose text mentions `ts-parse` at all | 45 | | **A** — a real module edge to `scripts/ts-parse.mjs` (import / re-export / dynamic) | **34** | | **B** — of A, CALL a door that ends the process ⇒ **depend on the exit** | **34** | | **C** — of A, name only non-refusing exports ⇒ do not depend on it | **0** | | **D** — text mentions with NO module edge (prose / payload literals) | **11** | | files that RE-EXPORT anything from it (a second-order importer would hide here) | **0** | | files the instrument could not parse (its own blind spot) | **0** | **Lit control, as triage required.** `scripts/tenant-audit-census.mjs` provably calls `parseSourceFile` — it is the subject of this card — and the instrument counts it, at `parseSourceFile x12`. Verified independently: `grep -n parseSourceFile` on that file shows one import at `:177` and **12** call sites (`:319 :363 :416 :723 :860 :1189 :1686 :1738 :1757 :1778 :1827 :1897`). The two numbers agree. **A second, discriminating control.** The instrument tells the three doors apart, which a text match on `parseSourceFile` cannot: `check-published-readme-exports.mjs` is counted at `createProgramChecked x1` and calls `parseSourceFile` zero times; `platform-object-tenancy-census.mjs` at `transpileChecked x1`, likewise zero. **⭐ The seat's 35 is 34.** `scripts/check-parse-guard.mjs` matches `from './ts-parse.mjs'` as **text** — at `:555` inside its own failure message, and at `:694 :697 :733` inside self-test fixture strings — and **imports nothing** from the module. A `git grep -l` on the specifier counts it; an AST edge does not. It is row D, not row B. **Reachable radius, and what is outside it.** The instrument sees static `import`/`export ... from`, namespace imports, and dynamic `import()` with a **literal** specifier, over git-tracked source files, one hop, resolved by path. ⛔ Outside it: a specifier built at runtime from a variable; a `createRequire`/`require` edge; an edge through `node_modules`; untracked files; and any second-order importer reached through a re-exporter — that last one measured to be vacuous here, because the re-exporter count is **0**. Also outside it: `scripts/pm/dispatch-gates.mjs` deliberately does **not** fan watch-hints out to the importers of the five shared utilities (`ts-parse.mjs` is one), so the derived gate list for this diff does **not** name those 34 families, by that file's own measured decision. That is why the floor below is argued mechanically and not from the gate list. ## Acceptance — triage's two inputs, both pinned Both live in `scripts/ts-parse.mjs`'s own `--self-test`, which CI runs (`.github/workflows/lint.yml:507`). 30 cases before, **48** after; five new batteries declaring **18** cases, and the roster floor rose 12 to 17 so deleting one reds. **① the newline-separated inline type literal.** The fixtures are the census's own round trip, spelled the way the census spells it — a receiver authored across lines, put through `.replace(/\s+/g, ' ')`, wrapped in the alias `:723` builds: - ⛔ the collapsed receiver **still exits** through `parseSourceFile` (the refusal is untouched); - ⭐ the same text through `parseDerivedText` returns a **located verdict** and the run continues (`status 0`, `tree null`, `failure.count 1`, `failure.line 1`); - and that verdict **names the source the text was derived from**, not only the synthetic name; - ⭐ **lit control:** the semicolon spelling — the card's own control — **parses** through the same door and returns a usable tree; - ⭐ **lit control:** so does the authored spelling. Without these two the first case is indistinguishable from a door that rejects everything. **② a genuinely unparseable input is still REFUSED.** Merge-conflict markers and a truncated body, each through both doors: `parseSourceFile` still ends the run, and `parseDerivedText` hands back **no tree** with `failure.count` at least 1. Triage's words are the reason this leg exists — without it "fixed" and "the check was switched off" are the same green. ## ⛔ Floor — nothing exits 0 where it exited non-zero **Every deleted line in the whole diff**, all 9 of them — `git diff 07c6f82..b09c8f5`, 439 insertions, 9 deletions: ``` -const SELF_TEST_BATTERY_FLOOR = 12; -/** Parses attempted, the distinct file names, and the refusals. */ -const census = { parses: 0, programs: 0, transpiles: 0, files: new Set(), refusals: 0 }; - * `parses` counts every source that reached the parser through ANY of the three - * `programs` and `transpiles` say which door they came through. - + `(${c.programs} program(s), ${c.transpiles} transpile(s)); ${c.refusals} refusal(s)\n`, - && counted.out === '{"parses":3,"programs":0,"transpiles":0,"files":2,"refusals":0}', - && countedAll.out === '{"parses":3,"programs":1,"transpiles":1,"files":3,"refusals":0}', - + `included, and a caller’s try/catch cannot swallow any of it).`, ``` ⇒ **not one of them is in a refusal path.** Every line from each door's diagnostics read through its `process.exit` is byte-identical: ``` 580: if (diagnostics.length > 0) { 671: if (rows.length > 0) { 720: if (rows.length > 0) { 581: census.refusals += 1; 672: census.refusals += 1; 721: census.refusals += 1; 582: ...refusalReport(...) 673: ...programRefusalReport 722: ...transpileRefusalReport 583: process.exit(EXIT_UNPARSEABLE); 674: process.exit(EXIT_UNPAR…) 723: process.exit(EXIT_UNPAR…) ``` The only change inside an existing door is one `vouchedSources.add(...)` on each success path — **after** the refusal has already been decided. **The covering mechanism for all 34, rather than 34 readings.** The new door has **zero** call sites anywhere in the tree outside the module that declares it (`git grep` for `parseDerivedText`, `EXIT_DERIVED_MISUSE`, `derivedMisuseReport`, `derivedFailureReport` over all tracked files: 0 files outside `scripts/ts-parse.mjs`). No importer reaches it, no refusal path changed ⇒ no importer's behaviour on an unparseable source can have moved. **Direct per-door reading at `b09c8f550`**, each fed a truncated body: | door | exit | returned? | |---|---|---| | `parseSourceFile` | **3** | no | | `createProgramChecked` | **3** | no | | `transpileChecked` | **3** | no | **The census's live behaviour is unmoved.** `node scripts/tenant-audit-census.mjs` over the live corpus: **exit 0**, 573 sources scanned, identical counts — the repair adds a capability and adopts it nowhere (see Acceptance notes). ## The new cases can fail — three ablations, each on a different limb Every mutation was confirmed on disk before the reading (anchor count to 0, injected marker counted), driven through `scripts/ablation-replace.mjs`, and every restore verified byte-identical (`blob == HEAD blob be83f4e`, `git diff HEAD` empty). | ablation | on-disk proof | self-test | which cases | |---|---|---|---| | the door EXITS instead of returning the verdict | anchor 1 to 0; `process.exit(EXIT_UNPARSEABLE)` 3 to 4 | **RED**, exit 1 | 5 — the rejection cases only. ⭐ Both lit controls stayed **green**, because they parse and never reach that path | | the origin-vouch guard deleted | `vouchedSources.has(origin)` 1 to 0 | **RED**, exit 1 | 6 — the whole unreachability battery | | the recovered tree handed back instead of `null` | `sourceFile: null,` 1 to 0 | **RED**, exit 1 | 3 — the two "no tree" acceptance legs plus the verdict case | ## Checks `scripts/pm/dispatch-gates.mjs` derived the families from the changeset itself (1 path vs merge base `07c6f822e`, three-dot). **29 derived, 29 run, 0 NOT-MEASURED, 0 UNRUN**, and the reconciliation's zero is DERIVED — every one of the 29 carries a recorded exit code and none of them is 3. Every family **exit 0**, including `check:parse-guard`, `check:nul-bytes`, `check:watch-hint-literal`, `check:scripts-symbol-anchors`, `check:self-test-wired`, `check:self-test-workflow-commands`, `check:cross-package-test-inputs` and `check:pm-dispatch-gates`. `node scripts/ts-parse.mjs --self-test` exit 0, 48 cases. Outside the derived total and therefore CI's, as that tool prints: the 53 artifact-roster families, the 11 declared wide-population families, the 14 pending-changeset families and the 1 path-scheduled job. **`pnpm lint` was narrowed, and the narrowing is a measurement.** The repo-wide `eslint . --no-inline-config` run is CI's. Three readings, all three required: ① the reachable population comes from **eslint's own config text**, not a guess — `eslint.config.mjs:328` records that "this repo runs one `eslint.config.mjs`, which never enables type-aware linting (no `parserOptions.project`, no typed `@typescript-eslint` rules) for ANY file", measured there with a positive control; ② the targeted run's file count is read from `--format json`: **1** file, 0 errors, 0 warnings, exit 0; ③ **invariance** — with no type-aware linting enabled anywhere, a one-file diff cannot move the verdict on any file it does not contain. The targeted run is at the final commit, `b09c8f550`. ## Clause ② and the changeset **`skip-changeset`, and the reading behind it.** Nothing published moves. ① the root package is `private: true`; ② of the **70** non-private workspace packages, **0** have a `files[]` entry that climbs out of its own package directory, so root `scripts/**` cannot be in any published payload; ③ the new symbols (`parseDerivedText`, `EXIT_DERIVED_MISUSE`, `derivedFailureReport`, `derivedMisuseReport`) appear in **0** files under `packages/`, `apps/` or `examples/` — with the same grep lit as a control, finding **48** files for a symbol that **is** published (`MetadataTypeSchema`, reached by `packages/spec`'s `src/**/*.zod.ts` entry). **`Clause-②: no`**, on the same three readings plus two more: the accept set of all three refusing doors is byte-identical (above), and the new door has zero adopters, so nothing in this tree accepts anything it previously refused. ⭐ The judgement left for the seat, stated rather than buried: this does add four exports to a module's surface — that surface is measured **unpublished**, which is why the answer is `no`, and the seat can overrule without re-deriving anything. ## Acceptance notes - ⚠️ **The census is NOT wired to the new door in this PR, and that is deliberate.** `scripts/tenant-audit-census.mjs` is serialised behind PR #19073 (card #18944) and the dispatch holds it: if the sound repair also has to edit the store/re-parse pair, stop and report the minimal hunk rather than edit it. It does. **The minimal hunk, for the sequenced PR:** in `typeTextDeclaresEngineDoor`, take the already-parsed `sf` of the source the type text came from as the origin, call `parseDerivedText(origin, 'census-receiver-type.ts', ...)` instead of `parseSourceFile`, and on `failure` print `failure.report` against the site and return `false`; then add one `NON_ENGINE_REASONS` arm (suggested: `type-text-not-round-trippable`) and put it in `UNDEFENDED_REASONS`, so the site is **declared** in both artefacts instead of dropping out of the population silently. Until that lands, the defect on `main` is unchanged — this PR makes the repair available and provable, it does not apply it. - **Discharge check, as triage required.** PR #19073's diff was read first. It touches `scripts/tenant-audit-census.mjs`, `content/docs/permissions/tenant-audit-census.mdx` and `docs/audits/2026-08-tenant-audit-write-call-sites.counts.md`, and it does **not** change the collapse at `:459` or the re-parse at `:723`. What it does is route four more `kind: 'other'` returns through `inlineEngineDoorOrOther`, which calls `typeTextDeclaresEngineDoor` — i.e. it **enlarges the set of type texts that reach the defect** without creating it. The seat's reading is confirmed; the finding is not discharged. - **All three exit sites, answered.** `:485` (`parseSourceFile`) gets the derived counterpart. `:575` (`createProgramChecked`) needs none: a Program is built from root **paths on disk**, and its one caller passes real paths — and its source files are certified, so a Program-based gate can use the new door for text it synthesises. `:617` (`transpileChecked`) is **not** answered here; see the next note for why, and it is a finding rather than silence. - **Out of scope, reported not fixed:** two of `transpileChecked`'s three callers are already in the same derived-text position as the census. `check-objectql-double-limit.mjs:535` and `check-where-matcher-conformance.mjs:543` both transpile a **lifted snippet**, named `${candidate.file}#L${candidate.line}.lifted.ts` — synthesised text, not a file. A legal source whose lifted span is not independently parseable would take either gate down process-wide, exactly as the census. Not fixed here: it is a different call site with a different acceptance input (that span), and adding a second new door while `check-parse-guard.mjs` — the gate that polices which doors `scripts/**` may use — is out of this card's surface is worse than adding one. (`platform-object-tenancy-census.mjs:226` transpiles a **real file's** source, so the refusal is correct there.) - **Escalation reading, taken because it fell out of the work.** `node scripts/tenant-audit-census.mjs` over the live corpus exits **0** — no such receiver exists in the tree today, so the card stays latent. ⭐ The zero is **lit** on the same subject: the run reached `typeTextDeclaresEngineDoor` and that function returned `true` for two real sites (`plugin-auth/src/audience-gate-test-support.ts:72`, `plugin-auth/src/sso-client-secret.ts:303`, both reported as door-shaped subtractions), so the instrument was not silently skipping the code path. ⛔ Grading is triage's — this changes no priority. - **No path literal was added to `scripts/ts-parse.mjs`, on purpose.** `scripts/pm/dispatch-gates.mjs` records that the five shared utilities it follows declare no path literals at all, and it follows them one hop; a literal here would fan a watch-hint into every one of the 34 importer families — the precision loss that file explicitly refuses. So the door's safety is the origin-vouch rather than a roster with a scan. `check:watch-hint-literal` exits 0. --- 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh --- _Generated by [Claude Code](https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh)_ --- _Generated by [Claude Code](https://claude.ai/code)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 9bb059d commit 956e010

1 file changed

Lines changed: 439 additions & 9 deletions

File tree

0 commit comments

Comments
 (0)