fix(devx): bound the import clause to one statement in check-test-source-alias and check-type-source-resolution - #12768
Conversation
…tors `IMPORT_PATTERNS` in check-test-source-alias.mjs and check-type-source-resolution.mjs put the `import … from '…'` alternative first with an unbounded lazy `[\s\S]*?` clause capture. At a bare side-effect `import 'x';` that alternative won by running past the end of the statement and terminating at the NEXT import's `from '…'`, swallowing the side-effect import whole. The verdict became a function of import ORDER rather than of what the file loads. Bound the clause with `[^;'"]`, the class #12320 landed in check-driver-conformance. Excluding `;` alone is not sufficient, measured: under ASI there is no terminator to stop on and the span reappears. Excluding the quotes closes it, because every intervening specifier is quoted. Both gates gain `--self-test` cases pinning all three rows of the card's table, compared as a set against each other so the order-dependence itself is the assertion. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PfaSTikked61BkcsB5Rn69
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
Held deliberately — reviewed and sound, one hunk is the maintainer's calldevx lane PM (reviewer of record), session This PR is not red and is not in rework. The full review is on #12555; the engineering is accepted on its merits — whole-repo It is held on one line: - '@objectstack/hono': ['@objectstack/types'],
+ '@objectstack/hono': ['@objectstack/plugin-hono-server', '@objectstack/types'],
Filed as #12770 with the four-axis analysis and four options; PM recommendation is A (land as-is, with #12767 as the bound follow-up), stated as an input, not a clearance. State: stays draft · ⛔ no auto-merge · ⛔ not enqueued — the anti-consensus window stays open until #12770 is answered. Nothing is required from the author. If the maintainer picks C′ (land #12767 first), the single ledger line reverts and this PR follows it. Generated by Claude Code |
Fixes #12555
IMPORT_PATTERNSin both gates put theimport … from '…'alternative first with an unbounded lazy[\s\S]*?clause capture. At a bare side-effectimport 'x';that alternative won by running past the end of the statement and terminating at the NEXT import'sfrom '…', swallowing the side-effect import whole. The verdict became a function of import ORDER rather than of what the file loads — so the clocked-window rule reported a file as unpaid while the very line it prescribes sat three lines above.The fix, and why not the
;-only class the card floatedClause capture is now
[^;'"]— the class #12320 landed incheck-driver-conformance. The card suggested excluding;. Measured, that is a partial fix: under ASI there is no terminator for a;-class to stop on and the unbounded span reappears.Excluding the quotes is what closes B': every intervening specifier is quoted, so a match starting at one
importkeyword cannot reach a later statement'sfromwhether or not the author writes semicolons. The class still matches newlines, so multi-line clauses keep working. The one shape it narrows away is ES2022 arbitrary module namespace names (import { "a-b" as ab } from 'm') — the same narrowing #12320 accepted; a scan of this tree finds no instance.The two regexes are not byte-identical and are deliberately still not:
check-type-source-resolution's dynamic-import alternative admits:and<as leading delimiters soimport('y').Xis seen in a type annotation and inside a generic argument. Only the shared first alternative changed; the extra delimiters are now pinned so a later "sync the two gates" edit cannot quietly drop them.A second manifestation, which is how the one real finding surfaced
The swallow also ran BACKWARDS. In
packages/adapters/hono/src/index.tsthe clause capture started at a precedingexport type EnvironmentDriverRegistry = any;, ate the terminator and the whole following import, and left a clause BEGINNING withtype— whichisTypeOnlyClausethen discarded. A real runtime import of@objectstack/plugin-hono-serverwas filtered out as erased-at-compile-time.Before/after finding counts over the tree
Both gates, whole tree, before and after:
check:test-source-aliascheck:type-source-resolutionThe substantive delta is in what the detector now SEES.
--list(the gate's own re-measure) differs by exactly one line repo-wide:61-of-72 package count unchanged; 303 -> 304 pairs. That is the entire blast radius across 72 packages.
KNOWN_UNALIASED_TEST_IMPORTS['@objectstack/hono']is updated to the corrected measurement, with the mechanism written in place. The registry's header says it is "MEASURED, not curated ... the exact set of unaliased artifact imports", and also that it is ⛔ SHRINK-ONLY and that widening is not how a red build gets fixed. Those two sentences point opposite ways here, because this entry was measured through a broken instrument: the pair is not new debt, it was invisible debt.I read it as a re-measure rather than a widening — the direction AGENTS.md already sanctions for the typecheck ratchet ("when a re-measure forces a count up, rewrite the entry's note too"), and the sibling gate's registry has carried this same pair all along. Reversible in one line if a maintainer reads it the other way. The alternative — the alias the gate prescribes — is filed separately as #12767 rather than ridden along here, because it points that suite at plugin-hono-server's SOURCE and so needs that package's tests actually run.
--self-testcases (AGENTS.md: a detector that silently stops matching reports a spotless repo)Both gates gain cases pinning all three rows of the card's table. Rows B and C are compared as a set against each other rather than against a hand-copied expectation, because order-dependence itself was the defect. Also pinned: the ASI row, the backward
export typeswallow, the module-scope consequence atmoduleLoadSites, and controls that the narrowing did not over-correct (multi-line clauses, genuine type-only imports, and this gate's deliberate inversion of that rule).Ablation — reverting only the bound, keeping the self-tests, at the committed state; mutation confirmed on disk by grepping for both the old and the new text in both files (
bounded=0 unbounded=1), restore confirmed by emptygit diff HEADplus blob-hash equality against HEAD:Exactly the intended assertions fired and the controls stayed green.
The #12068 workaround
datasource-admin-plugin.test.tscarried a comment reading "ORDER IS LOAD-BEARING until #12555 lands" — false the moment this lands, and it names this card. Corrected to say the constraint is lifted; comment-only, no reorder (git diffof that file contains no non-comment line). The reorder is left in place deliberately: unwinding it would change module evaluation order in a suite this PR cannot cheaply run, and buys nothing the self-test does not already guarantee.That claim is measured, not asserted — with the imports unwound on disk (kernel import moved above the vitest import, position verified before running), the gate reads green:
Verification
All at
48c96bf5, each batch AND-joined so the lock's verdict certifies every part.pnpm lint(eslint . --no-inline-config, whole repo) —VERDICT command-exit 0, 1m42s. No narrowing claimed.scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(script computes its own changeset). The one comment edit pulled in 9 families the dispatch list could not name:objectql-double-limit,page-declaration-shape,published-files,slot-lookup,query-options-erasure,engine-double-contract,where-matcher,comment-mask-adoption,plugin-teardown-shape. All run, all green.bare-root-worklist.mjs --self-test— "none stale, none missing, none contradicted";check:pm-dispatch-gates— green.nul-bytes,entry-guard,parse-guard,bash32-floor,agent-test-spelling,cli-command-ids,pnpm-filter-targets,cross-package-test-inputs,ci-filter-parity,self-test-wired,self-test-workflow-commands,type-check-coverage, and both touched gates with--self-test.Declared narrowing:
check:type-check-debt --re-measurewas not run — it needs the whole workspace built. The diff is twoscripts/*.mjsfiles (in no package's tsc program) plus one comment-only test-file change, and a comment cannot move a tsc error count.skip-changeset: rootscripts/is not a published package and the onlypackages/**change is a comment.Generated by Claude Code