fix(#643): replace regex comment stripping with parser-backed architecture source contracts - #671
Conversation
…cture source contracts tests/unit/side-panel-source-contract.test.ts and tests/unit/surface-lifecycle-arch.test.ts preprocessed source with a two-pass regex comment stripper before applying their own textual assertions. The block-comment pass ran before line-comment removal, so a `/*`-shaped substring sitting inside a real `//` comment could make the block pass consume real code through the next genuine `*/`, hiding a real violation before either suite's assertions ever ran. Both suites now call two new named analyzers in build/lib/check-legacy-owners.mjs (findSidePanelSourceContractViolations / findSurfaceLifecycleSourceContractViolations), backed by the same real- TypeScript-parser infrastructure the #630/#642 checks already use. The module's single-source `withParsedSource` is refactored into a thin wrapper over a new internal `withParsedSources` batch primitive, so the surface scan shares one native parser process across the whole `src/**` tree (measured ~250-310ms for 221 files) instead of spawning one per file. A new build/lib/check-legacy-owners.d.mts gives the two strict-.ts test files a plain-data declaration boundary with no SourceFile/Node/SyntaxKind crossing it. Several rules deliberately gained precision along the way: the exact-value panel-id/label checks no longer false-positive on a longer literal merely containing a protected id, while gaining multi-quote-style coverage (including type-position literals, e.g. `type Pref = 'library'`) for the actual protected value; the history/sidePanel.value comparison rules support both operand orders; the surface ordering scopes now recognize return-annotated function declarations (a real gap in the retired textual opener) while explicitly preserving its accidental treatment of parenthesized control-flow blocks as independent ordering scopes; and `currentWorkspace = null ?? fallback` is now deliberately treated as clean. Verified the real src/** tree produces zero findings under the new analyzers, and manually confirmed both analyzers go red against temporarily-sabotaged real production files (workbench-session.ts, app.ts), then restored the originals from saved bytes. No production src/** code, dependency, or runtime behavior changed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwFPT465eDJqYcRa8HGNLz
Independent review found nearly every sabotage case in side-panel-source-contract.test.ts asserted only .toHaveLength(1)/ .toEqual([]), never which .rule code was returned. Several guarded files (app-shell.ts most notably, with app-shell-panel-def/ app-shell-panel-id/app-shell-host-accessor) dispatch to multiple distinct rule codes from the same call, so a bug that swapped two rule-code strings in the dispatch table would still pass. Added a rulesOf() helper mirroring surface-lifecycle-arch.test.ts's own precedent and tightened every under-specified assertion to pin the exact expected rule code(s) via a single rulesOf(...).toEqual([...]) assertion. No production logic changed; tightening revealed no latent rule-code bug (today's dispatch table is correct).
ChatGPT review pass 1Reviewed head: Major issues
Focused checks
VERDICT: REVISE |
- protectedDeclarationViolations() now requires a const-flagged VariableDeclarationList, not any VariableDeclaration: a let/var rewrite of disposeShell/disposeCurrentSurface/committedWorkspaceSignal/ mainSurfaceSignal now fails the guard instead of passing undetected. - Its checked range now anchors at the declaration list's own start (the const/let/var keyword's position) instead of the VariableDeclaration node's start (the binding identifier) — a straddle whose keyword sits outside the coordinator and whose binding sits inside is now correctly classified as a straddle violation rather than misread as fully "inside". - sidePanelsTypeAliasViolations() now also walks each TypeAliasDeclaration's typeParameters, not just its .type RHS, so a protected literal confined to a generic constraint/default (`type Probe<T extends 'databases'> = T;`) is caught the same way a literal in the RHS already was. - Added table-driven let/var sabotage tests (all four protected names, both keywords), a keyword-before-marker/binding-after-marker straddle test, and rule-code-pinned extends-constraint/default-clause sabotage tests for the type-alias check. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwFPT465eDJqYcRa8HGNLz
ChatGPT review pass 2Previously reviewed head: Pass-1 findings
Focused re-review
Full updated PR / validationThe PR still changes the same five files; the new head is exactly one commit ahead of pass 1 and that commit changes only PR CI for this head's merge commit is green: No new actionable findings. VERDICT: SHIP |
What & why
tests/unit/side-panel-source-contract.test.tsandtests/unit/surface-lifecycle-arch.test.tsboth preprocessed TypeScript with the same unsound two-pass regex comment stripper
(block comments removed before line comments) ahead of their own textual assertions —
unsound in the direction that matters most for an architecture guard: a
/*-shapedsubstring sitting inside a real
//comment could make the block-comment pass consumereal code through the next genuine
*/, hiding a violation before either suite'sassertions ever ran.
Following the same lesson already applied to production architecture checks in #630/#642
(don't patch one more regex — move to the real TypeScript-parser-backed machinery), both
suites now call two new named analyzers in
build/lib/check-legacy-owners.mjs(
findSidePanelSourceContractViolations/findSurfaceLifecycleSourceContractViolations),sharing the same parser infrastructure #630/#642 already use, extended with an internal
withParsedSourcesbatch primitive so the whole-tree surface scan shares one nativeparser process rather than spawning one per file. A new
.d.mtsplain-data declarationboundary keeps compiler internals (
SourceFile/Node/SyntaxKind) from ever crossinginto the strict-
.tstest files.Several rules deliberately gained precision along the way (documented in each rule's own
test comments and the CHANGELOG): the exact-value panel-id/label checks no longer
false-positive on a longer literal merely containing a protected id, while gaining
multi-quote-style coverage (including type-position literals) for the actual protected
value; the
history/sidePanel.valuecomparison rules now support both operand orders;currentWorkspace = null ?? fallbackis now deliberately treated as clean.The plan-author/review loop (ChatGPT-author, Fable/high-review) hit its 5-pass cap without
a final approval — every prior round's findings were accepted and incorporated, and the
final round's 4 residual findings were minor/fixable precision issues. The human owner
reviewed them directly and approved folding them in as mandatory implementation
requirements rather than spending a 6th review round; all four are implemented and tested
(see commit
ce96363's message and the CHANGELOG entry). An internal Sonnet review passthen found one real test-quality gap (most side-panel sabotage assertions only checked
violation count, not the exact
.rulecode, which could mask a rule-code mislabelingbug for files dispatching multiple distinct codes) — fixed in the second commit
4e0ae49.No
src/**production code, dependency, or runtime behavior changed.Closes #643
Checklist
npm testpasses (the per-file coverage gate is non-negotiable)npm run buildsucceeds (single-filedist/sql.html)CHANGELOG.md([Unreleased]) updated🤖 Generated with Claude Code
https://claude.ai/code/session_01LwFPT465eDJqYcRa8HGNLz