Uh oh!
There was an error while loading. Please reload this page.
test(console): gate query params inside preview page-source template literals - #6282
Conversation
…literals
`object-ui/no-unprefixed-query-params` anchors on a `CallExpression`, so the
fourth objectui#5458 site — `apps/console/src/sdui-workbench-preview.tsx`,
whose page source is a template literal — is structurally invisible to it. No
AST rule can reach inside one, and a text scan over the rule's key list would
match its own docblock (`top`, `limit`, `filter`, `sort`, `count` are ordinary
English words), which is why the rule is call-anchored in the first place.
The fix is not a different rule but a different subject: extract each preview
harness's page `source` and run the REAL rule over it, where its own anchor
works. `helpers/preview-page-sources.ts` now owns the single enumeration and
the single extractor — both tests in the family read from it, so they cannot
disagree about what a preview page is — and it returns the template literal's
COOKED value, because the raw text of `sdui-workbench-preview`'s source is not
parseable JS (`\u{1F5C2}` reads as JSX text plus an expression container).
Non-vacuity is pinned rather than argued: a control pair, and a mutation over
the real extracted source that strips the `$` off the canonical spellings taken
from the rule's own `QUERY_OPTION_SPELLINGS` (now exported for that reason) and
requires the gate to go red.
Part of #5944
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019b5UBNMtTzKbVtZZGvFuxe…needs it
`typescript-eslint` exports its parser typed as a minimal compatibility shim —
`parseForESLint(text: string): { ast: unknown }` — which drops the options
parameter, so the console's `tsc` rejected the `ecmaFeatures: { jsx: true }`
the harness parse needs (TS2554). Importing `@typescript-eslint/parser`
directly for the real declarations would be a phantom dependency, so the
signature is restated at that call and nothing downstream trusts more than
`unknown`. Also restores objectui#5470's 79-vs-95 measurement, which moved out
of the styling test with the extractor.
Part of #5944
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019b5UBNMtTzKbVtZZGvFuxe✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
yinlianghui-tw
commented
Aug 25, 2026
PM review — ACCEPT. ⭐ Best reuse shape of tonight's five, and it falsified one of my own dispatch claims — correctly.Reviewed by the ⭐ The design — run the real rule, not a copy of its knowledgeThe dispatch said "reuse the rule's own key map rather than a second copy." What landed is strictly stronger: the entire rule runs via ESLint's Migrating the styling test onto the shared helper instead of copying the extractor is the right call for the same reason — two enumerations cannot deliver "a new harness is covered automatically" — and its pinned counts (48/21) are unchanged and green, so the migration is behaviour-preserving and reversible in one file if the maintainer prefers otherwise. Flagging that as a reviewable scope note rather than burying it: correct. ⭐ You falsified my dispatch, with the gate's own output — and this correction propagatesMy dispatch said The RED demonstration — named to file, page, key, line, column
Traps hit and handled
Sequencing noteNo file overlap with #6274/#6276/#6277/#6279 — ⛔ Not armed yetCI was in_progress at report time. Mark ready → arm once every check carries a conclusion (rate-limit permitting). Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#5944
The gap
object-ui/no-unprefixed-query-params(#5458) anchors on aCallExpressionwhose callee is.find/.findOne. That anchor is load-bearing, not incidental: every spelling on its list —top,limit,filter,sort,count— is an ordinary English word, so outside a finder call the name carries no signal at all. A text scan over the same list would match the rule's own docblock, the issue, and the prose incontent/docs/guide/react-pages.md.Three of that card's four live sites are real calls and the rule reported all three. The fourth —
apps/console/src/sdui-workbench-preview.tsx— holds its page source in a template literal, which the parser sees as oneTemplateLiteraltoken and never as aCallExpression. No AST rule can reach inside one. That site was corrected by hand; nothing rejected the next one written there.The fix is a different SUBJECT, not a different rule
Pull the page
sourceout of the harness and the rule's own anchor works on it perfectly. So this PR adds a sibling ofsdui-preview-page-source-styling.test.tsthat extracts each preview harness's page source and runs the real rule over it — the sameeslint-rules/no-unprefixed-query-params.jsthateslint.config.jsloads, driven through ESLint'sLinter. Not a re-implementation and not a second key list: a copy of a rule cannot disagree with itself.One enumeration, one extractor
apps/console/src/__tests__/helpers/preview-page-sources.tsis new and now owns both theimport.meta.glob('../../*-preview.tsx', { query: '?raw' })enumeration and the page-source extractor. The styling test was migrated onto it rather than the extractor being copied. The whole value of that glob is "a new harness is covered without anyone remembering to add it", and two copies of it cannot deliver that: the day they disagree about what counts as a preview page, one silently stops covering a harness and still reports green. The styling test's assertions and its pinned className counts (48 / 21) are unchanged — see the verdict lines below.Why the extractor now parses instead of regexing
A page
sourceis a template literal, and its raw text is not the string the page receives.sdui-workbench-previewwrites its folder glyph as a\u{...}escape, which cooks to one character but, read raw, is JSX text followed by an expression container that no JS parser accepts:A consumer that parses the extracted source would therefore hit a fatal parse error on exactly the harness this gate exists for — and a gate that cannot parse its subject reports nothing. The extractor now reads the template literal off the parsed harness (
typescript-eslint's parser) and returns its cooked value. Measured: cooking is a no-op for the three sources the styling test pins (4101 / 1866 / 3074 chars, identical raw and cooked) and changes only the workbench source (3458 → 3444).Reused, and exported
QUERY_OPTION_SPELLINGSis now a named export of the rule module. The whole rule is reused for the check itself; the key map is reused for the mutation guard below, which needs the canonical$-prefixed half of it.eslint-rules/has nopackage.jsonand is not inpnpm-workspace.yaml— it is a repo-local plugin directory — so the named export widens no published surface.Non-vacuity is pinned, not argued
The tree is clean at that site, so a green run proves nothing on its own. Three guards make the green mean something, and all three are permanent:
find('showcase_project', { top: 200 })and is silent on{ $top: 200 }. (Also covers a real trap:Linter#verifycalled without a filename that matches the config'sfilesreturns[], indistinguishable from a clean source.)$off the canonical spellings taken from the rule's own map must turn the gate red. It runs against whatever the harness says today, so it cannot rot into a tautology, and it auto-covers a future harness.The one-off RED demonstration
{ $top: 200 }was reintroduced as{ top: 200 }in the harness's template literal on disk (anchor counts before:$top1, baretop0; after:$top0, baretop1;git diff --stat1 file, 1 insertion, 1 deletion), the new suite was run, and it went red naming the file, the page and the key:Two further cases went red with it (
the objectui#5458 fourth site is inside the extracted text, as a real call, andat least one enumerated page really uses a query option) — the mutation guards noticing that the site stopped being a$-spelled call.Tests 3 failed | 6 passed (9). The mutation script carried atrap … EXIT INT TERMrestore; the revert was verified withgit diff --exit-code→0, working tree clean.The enumeration really enumerates
Printed by deliberately failing the enumeration assertion, then reverted (
git diff --exit-code→0):which is exactly
ls apps/console/src/*-preview.tsx, 5 of 5. Two of them (record-header,row-actions) carry no page object, so the four checked pages arecommand_center:jsx,release_notes:html,pipeline_react:react,crm_workbench:react.Changeset
Owed and added, with an empty frontmatter.
check-changeset-presence.mjsdoes not exit 0 either way on this tree — it has no carve-out for tests undersrc/, and it failed before the declaration:and passes after it:
Nothing published changes: the new files are tests, and
@object-ui/console'sfileslist publishesdist+plugin.*+README.mdonly.Not in this PR
The
.records/ unprefixed-option misreads inskills/objectui/guides/data-integration.mdare a published-skill surface tracked separately (#5947 / #6006); untouched here.Verification — all quoted from each gate's own printed verdict, at
8821b7d9dExit codes captured by redirect before any pipe.
apps/consolevitest projectpnpm exec vitest run --maxWorkers=2 --reporter=verbose apps/console/Test Files 77 passed (77)·Tests 879 passed (879)eslint-rules/+ the script tests that consume thempnpm exec vitest run --maxWorkers=2 --reporter=verbose eslint-rules/ scripts/__tests__/{check-vi-mock-specifiers,turbo-lint-inputs,turbo-task-guard-coverage,vitest-invocation-guard,check-changeset-presence}.test.tsTest Files 13 passed (13)·Tests 349 passed (349)(52 named cases in the rule's own suite)@object-ui/consoletype-checkpnpm --workspace-concurrency=2 --filter @object-ui/console type-check0(tsc --noEmit && tsc -b tsconfig.node.json --force)lint:root, unnarrowedpnpm run lint:root✖ 28 problems (0 errors, 28 warnings)— all pre-existingno-explicit-anywarnings, none in a touched file@object-ui/consolelintpnpm --filter @object-ui/console lint✖ 203 problems (0 errors, 203 warnings)— likewiseeslint <4 files> --format json0 errors, 0 warningseach, none ignored — so the zeros above are coverage, not exclusioncheck:control-bytes✅ check-control-bytes: OK (scanned 5174 tracked text file(s); skipped 85 binary)check:phantom-deps✅ Every in-scope import is declared by the package that publishes it.check:lint-coverage✅ lint coverage: 46/46 packages linted, 0 with outstanding errors (0 total).check:type-check-coverage✅ type-check coverage: 45/46 via type-check …·✅ test type-check coverage: 41/41 packages compile their testscheck:esm-specifiersSpecifier leg: no un-ledgered package emits an extensionless relative specifier.check:vi-mock-specifiers,check:self-import✅eachThe first console
type-checkfailed with 22 errors (TS2307: Cannot find module '@object-ui/core'and friends) in files this PR does not touch — the stale-disttrap.pnpm --workspace-concurrency=2 --filter '@object-ui/console^...' buildfirst, then it is clean; the verdict above is the post-build read.One declared narrowing
The full root vitest suite did not converge in this container. It was started under the shared verify lock and killed at 21 minutes, having produced no summary — CPU-bound the whole time (
237%, 49m51s of CPU), not hung. A parallel agent's identicalpnpm exec vitest run --maxWorkers=2on another worktree ended the same way. So the run was narrowed to the two sets above, on this measurement: the diff is five files, none of them under anypackages/*/src, so the rootdom/dom-heavy/unitprojects have no changed input; the only behaviour that can move is@object-ui/console's (tests only) andeslint-rules/'s, and both were run whole. The consumers ofeslint-ruleswere derived by grep rather than recalled —scripts/__tests__/{check-vi-mock-specifiers,turbo-lint-inputs,turbo-task-guard-coverage,vitest-invocation-guard}.test.ts— and all are in the second set. CI runs the farm regardless.Generated by Claude Code