Repository navigation
assertCanonicalVitestInvocation does not cover the 17 packages carrying their own vitest.config.ts — contrary to its own docstring #5406
Description
Activity
os-support-ai commented
on Aug 20, 2026 CollaboratorAuthorMore actionsClaim: PM loop round 20
Session:session_01RV6yuVCxymHYE16PL9vQkE
Branch:claude/issue-5406-vitest-invocation-guard-coverage
Worktree:objectui-issue-5406
Domain:repo:objectui
File surface: theassertCanonicalVitestInvocationimplementation and its docstring, plus the per-packagevitest.config.tsfiles only if closing the hole genuinely requires touching them (stop on breach; explain in the report)
Container & model: M,mode:subagent,model: opus
Clause-②: no — closes a hole in an existing guard; does not widen any public surface and does not change what any suite asserts
Serial constraints cleared: ⛔ not.github/workflows/ci.yml(#5403 owns it,pm:blocked). Disjoint from #5402 (a single test + its script) and #5405 (release.yml), both claimed the same round.⚠️ This card may touch package-levelvitest.config.tsfiles; no card in flight owns those, but the dev must declare which it touched and why.
Generated by Claude Code
os-support-ai commented
on Aug 20, 2026 CollaboratorAuthorMore actions{ "issue": 5406, "status": "done", "branch": "claude/issue-5406-vitest-invocation-guard-coverage", "pr": "https://github.com/objectstack-ai/objectui/pull/5412", "premise_still_valid": true, "summary": "The defect is real and reverse-verified, but the card's numbers are not: I enumerated the configs myself rather than inheriting '17'. On origin/main @ fd227eae1 there are 19 vitest config files — the root vitest.config.mts plus 18 others (17 under packages/, plus apps/console and examples/schema-catalog). Classified by route: 8 configs IMPORT the root config (packages/core, react, types re-export it verbatim; components, fields, plugin-dashboard, apps/console merge a vite config into it; examples/schema-catalog strips one key) and importing it executes its module scope, so the guard runs — all 8 measured REFUSED before any change. Packages with no config at all (app-shell, mobile) resolve upward and were also REFUSED, so the docstring's upward-resolution claim was the one true part of it. The uncovered set is 11 STANDALONE configs: plugin-calendar/-charts/-detail/-form/-gantt/-grid/-kanban/-list/-map/-timeline/-view, each a self-contained defineConfig with happy-dom + globals + a one-line local setup and NO alias table, where the root config maps ~40 @object-ui/* specifiers at a sibling's src/. So the card is wrong on two of the three packages it names — packages/core and packages/components DO import the root config and were already covered (core's config is literally two lines re-exporting it) — and 17 is the count of ALL packages/* configs, not the uncovered ones. plugin-grid, the third named, is genuinely uncovered. The true figure is 11, exactly what #5313 measured a day earlier from the same starting point (see open_questions). Fix: the 11 now call the guard themselves via a new repoRootFrom(import.meta.url) landmark search rather than eleven hand-counted '../..' literals (a wrong count resolves to a real directory, so the guard would keep issuing verdicts against the wrong root); the guard docstring and the root config's call-site comment now state the three routes instead of asserting one; and the coverage claim is ENFORCED, not restated — the guard's own test walks every vitest.config.* in the repo and fails on any taking neither route, with liveness pinned so it cannot go vacuously green. No assertion weakened, no skip list, no exemption, no config's test semantics changed, .github/workflows/ci.yml untouched.", "tests": "All from the repo root; gate union re-run after the final commit at 33fc79109 (working tree clean). REVERSE VERIFICATION — predicted before running: plugin-grid accepted before / refused after; packages/core refused in both; packages/app-shell UNKNOWN (whether vitest walks up for a config had to be measured). BEFORE, in packages/plugin-grid: 'pnpm exec vitest run src/__tests__/ObjectGrid.exportOptionsKeys.test.ts' -> 'RUN v4.1.10 /…/packages/plugin-grid' (root is the PACKAGE) / 'Test Files 1 passed (1)' / 'Tests 5 passed (5)', exit 0, no guard output — accepted AND green, the false-green shape. AFTER, identical command: the 'vitest 调用被拒绝 … (objectui#3378)' box, exit=1. Full sweep of all 21 config-bearing directories: BEFORE 10 refused / 11 accepted; AFTER 21 refused / 0 accepted. Observed matched predicted on all three, including the unknown (app-shell and mobile were already refused). ABLATION on the new enforcement test, predicted plain RED (config texts are its only input; removing a call can only add a finding): deleting the guard call from packages/plugin-view/vitest.config.ts turned it red naming that exact file — 'Tests 1 failed | 29 passed (30)'; restored with 'git checkout HEAD -- …' (fix committed first, so a real restore point existed), git status clean afterwards, 30/30 green again. REBUILD STATEMENT, per leg: NO build artifact sits between any edit and the thing under test on either the mutation or the restore leg — the enforcement test reads the config files as TEXT from disk and vitest transpiles the test file per run; nothing on either path resolves through a dist/. (Separately confirmed the fix survives a real build: check:published-dist ran a full 275s turbo build, exit 0.) GATES: 'pnpm exec vitest run scripts/' 58 files / 1567 passed; 'pnpm exec vitest run scripts/__tests__/vitest-invocation-guard.test.ts' 30 passed (was 26); 'pnpm exec vitest run packages/plugin-grid/ packages/plugin-view/' 98 files / 906 passed (canonical path for two edited packages, unaffected); pnpm type-check:scripts exit 0 (the guard's inferred types flow into the .ts test — the scripts program is the one tsc program my edits are in); eslint over all 14 changed files 0 errors (7 pre-existing unused-'path' warnings, present on main, left alone); check:control-bytes / check:self-import / check:phantom-deps / check:skills-paths / check-changeset-presence / check-changeset-no-major / check:published-dist all exit 0. Measured that packages/*/vitest.config.ts is in NO tsc program (tsc --listFiles on both tsconfig.json and tsconfig.test.json: 0 hits), which is why the new import carries no @ts-expect-error. One near-miss worth recording: the enforcement test's first classifier spelling was text.includes('vitest.config.mts'), and all 11 standalone configs NAME the root file in the comment explaining why they do not import it — so every one classified as covered and the check went vacuously green over exactly the configs it exists to catch. Caught because the two liveness assertions went red. It now matches the import specifier, and that distinction is pinned as its own assertion. NOT run locally, left to CI per the dispatch contract: the full root suite (pnpm test, 4 shards in CI). No exemption was needed anywhere — CI runs pnpm test from the repo root only (ci.yml:435 explicitly rejects turbo run test), no workflow or root script invokes turbo run test or a package-filtered test, the 11 configs are not loaded by a root run, and the OBJECTUI_VITEST_GUARD=off escape hatch still stands down in a newly-guarded package.", "open_questions": [ { "question": "#5313 (open, pm:queue, unassigned) records THIS SAME defect with the correct count of 11, the same plugin-grid measurement, and it prescribed exactly the fix taken here ('11 个本地配置各自 import 并调用 assertCanonicalVitestInvocation'). #5406 appears to be a restatement of it that degraded the numbers — its '17' is the count of all packages/* configs, and two of the three packages it names as uncovered were already covered. PR #5412 closes #5313 in substance. What should happen to it?", "options": [ "A — PM closes #5313 as completed by PR #5412 once merged; leave the PR's Fixes line naming only #5406", "B — add 'Fixes #5313' to the PR body so the merge closes both automatically", "C — leave #5313 open for a separate follow-up" ], "recommendation": "A. I deliberately used no closing keyword on #5313 in the PR body: it is a card I was not dispatched and did not claim, and under the shared GitHub identity a state change I make on it is indistinguishable from anyone else's. B would work mechanically but takes a disposition decision that is yours. C is wrong — the PR leaves nothing of #5313 undone; I also answered the one question it explicitly left untested (a bare run in one of the 11 does NOT collect console's 22 foreign files: their configs have no `projects` array, so vitest falls back to the default include relative to the package root and collects the package's own suite — worse than #3378's shape, because the count and the filenames both look right)." } ], "out_of_scope_findings": [ "NOT filed as a new card, deliberately — recorded as a comment on the existing #3240 (open, pm:queue), which already owns exactly this question ('哪些包真的需要本地配置'): the 11 standalone configs are one pre-monorepo template copied eleven times, byte-identical apart from an unused `import path from 'path'` in 7 of them, and what they diverge on is the alias table. Per the card's instruction I established the divergence and did not touch it. Two facts the comment adds for whoever picks #3240 up: its title's '17' is stale (18 non-root configs today, 11 of them divergent), and after PR #5412 those 11 configs are unreachable by any ACCEPTED invocation — CI runs from the repo root, the root config's projects array names only apps/console, and every package-cwd run is now refused — so they are inert configuration that still reads as live. That sharpens #3240 rather than pre-empting it. Comment: https://github.com/objectstack-ai/objectui/issues/3240#issuecomment-5357879574", "No new issue filed for the 7 unused `import path from 'path'` eslint warnings in the standalone configs — they predate this PR, are warnings not errors, and fall inside #3240's scope (those files' fate); folded into the comment above rather than split into a card of its own.", "Noted, not filed: the new repoRootFrom uses `import.meta.url`, not `__dirname`, so this PR adds nothing to the `__dirname`-in-vite/vitest-configs debt tracked in #3592 (pm:on-hold)." ] }Generated by Claude Code
Generated by Claude Code
os-support-ai commented
on Aug 20, 2026 CollaboratorAuthorMore actionsos-dev-report
(Supersedes the previous comment. That one led with the HTML-comment marker, which GitHub's sanitizer stripped on write — verified by reading the stored body back, where it begins at the code fence. Same JSON, marker now as literal text so the PM's scan can see it. No tool in this session can edit an existing issue comment, hence the repost rather than an edit.)
{ "issue": 5406, "status": "done", "branch": "claude/issue-5406-vitest-invocation-guard-coverage", "pr": "https://github.com/objectstack-ai/objectui/pull/5412", "premise_still_valid": true, "summary": "The defect is real and reverse-verified, but the card's numbers are not: I enumerated the configs myself rather than inheriting '17'. On origin/main @ fd227eae1 there are 19 vitest config files — the root vitest.config.mts plus 18 others (17 under packages/, plus apps/console and examples/schema-catalog). Classified by route: 8 configs IMPORT the root config (packages/core, react, types re-export it verbatim; components, fields, plugin-dashboard, apps/console merge a vite config into it; examples/schema-catalog strips one key) and importing it executes its module scope, so the guard runs — all 8 measured REFUSED before any change. Packages with no config at all (app-shell, mobile) resolve upward and were also REFUSED, so the docstring's upward-resolution claim was the one true part of it. The uncovered set is 11 STANDALONE configs: plugin-calendar/-charts/-detail/-form/-gantt/-grid/-kanban/-list/-map/-timeline/-view, each a self-contained defineConfig with happy-dom + globals + a one-line local setup and NO alias table, where the root config maps ~40 @object-ui/* specifiers at a sibling's src/. So the card is wrong on two of the three packages it names — packages/core and packages/components DO import the root config and were already covered (core's config is literally two lines re-exporting it) — and 17 is the count of ALL packages/* configs, not the uncovered ones. plugin-grid, the third named, is genuinely uncovered. The true figure is 11, exactly what #5313 measured a day earlier from the same starting point (see open_questions). Fix: the 11 now call the guard themselves via a new repoRootFrom(import.meta.url) landmark search rather than eleven hand-counted '../..' literals (a wrong count resolves to a real directory, so the guard would keep issuing verdicts against the wrong root); the guard docstring and the root config's call-site comment now state the three routes instead of asserting one; and the coverage claim is ENFORCED, not restated — the guard's own test walks every vitest.config.* in the repo and fails on any taking neither route, with liveness pinned so it cannot go vacuously green. No assertion weakened, no skip list, no exemption, no config's test semantics changed, .github/workflows/ci.yml untouched.", "tests": "All from the repo root; gate union re-run after the final commit at 33fc79109 (working tree clean). REVERSE VERIFICATION — predicted before running: plugin-grid accepted before / refused after; packages/core refused in both; packages/app-shell UNKNOWN (whether vitest walks up for a config had to be measured). BEFORE, in packages/plugin-grid: 'pnpm exec vitest run src/__tests__/ObjectGrid.exportOptionsKeys.test.ts' produced 'RUN v4.1.10 /…/packages/plugin-grid' (root is the PACKAGE) / 'Test Files 1 passed (1)' / 'Tests 5 passed (5)', exit 0, no guard output — accepted AND green, the false-green shape. AFTER, identical command: the 'vitest 调用被拒绝 … (objectui#3378)' box, exit=1. Full sweep of all 21 config-bearing directories: BEFORE 10 refused / 11 accepted; AFTER 21 refused / 0 accepted. Observed matched predicted on all three, including the unknown (app-shell and mobile were already refused). ABLATION on the new enforcement test, predicted plain RED (config texts are its only input; removing a call can only add a finding): deleting the guard call from packages/plugin-view/vitest.config.ts turned it red naming that exact file — 'Tests 1 failed | 29 passed (30)'; restored with 'git checkout HEAD -- …' (fix committed first, so a real restore point existed), git status clean afterwards, 30/30 green again. REBUILD STATEMENT, per leg: NO build artifact sits between any edit and the thing under test on either the mutation or the restore leg — the enforcement test reads the config files as TEXT from disk and vitest transpiles the test file per run; nothing on either path resolves through a dist/. (Separately confirmed the fix survives a real build: check:published-dist ran a full 275s turbo build, exit 0.) GATES: 'pnpm exec vitest run scripts/' 58 files / 1567 passed; 'pnpm exec vitest run scripts/__tests__/vitest-invocation-guard.test.ts' 30 passed (was 26); 'pnpm exec vitest run packages/plugin-grid/ packages/plugin-view/' 98 files / 906 passed (canonical path for two edited packages, unaffected); pnpm type-check:scripts exit 0 (the guard's inferred types flow into the .ts test — the scripts program is the one tsc program my edits are in); eslint over all 14 changed files 0 errors (7 pre-existing unused-'path' warnings, present on main, left alone); check:control-bytes / check:self-import / check:phantom-deps / check:skills-paths / check-changeset-presence / check-changeset-no-major / check:published-dist all exit 0. Measured that packages/*/vitest.config.ts is in NO tsc program (tsc --listFiles on both tsconfig.json and tsconfig.test.json: 0 hits), which is why the new import carries no @ts-expect-error. One near-miss worth recording: the enforcement test's first classifier spelling was text.includes('vitest.config.mts'), and all 11 standalone configs NAME the root file in the comment explaining why they do not import it — so every one classified as covered and the check went vacuously green over exactly the configs it exists to catch. Caught because the two liveness assertions went red. It now matches the import specifier, and that distinction is pinned as its own assertion. NOT run locally, left to CI per the dispatch contract: the full root suite (pnpm test, 4 shards in CI). No exemption was needed anywhere — CI runs pnpm test from the repo root only (ci.yml:435 explicitly rejects turbo run test), no workflow or root script invokes turbo run test or a package-filtered test, the 11 configs are not loaded by a root run, and the OBJECTUI_VITEST_GUARD=off escape hatch still stands down in a newly-guarded package.", "open_questions": [ { "question": "#5313 (open, pm:queue, unassigned) records THIS SAME defect with the correct count of 11, the same plugin-grid measurement, and it prescribed exactly the fix taken here ('11 个本地配置各自 import 并调用 assertCanonicalVitestInvocation'). #5406 appears to be a restatement of it that degraded the numbers — its '17' is the count of all packages/* configs, and two of the three packages it names as uncovered were already covered. PR #5412 addresses #5313 in substance. What should happen to it?", "options": [ "A — PM disposes of #5313 as completed by PR #5412 once merged; leave the PR's Fixes line naming only #5406", "B — add a closing keyword for #5313 to the PR body so the merge disposes of both automatically", "C — leave #5313 open for a separate follow-up" ], "recommendation": "A. I deliberately used no closing keyword on #5313 in the PR body: it is a card I was not dispatched and did not claim, and under the shared GitHub identity a state change I make on it is indistinguishable from anyone else's. B would work mechanically but takes a disposition decision that is yours. C is wrong — the PR leaves nothing of #5313 undone; I also answered the one question it explicitly left untested (a bare run in one of the 11 does NOT collect console's 22 foreign files: their configs have no `projects` array, so vitest falls back to the default include relative to the package root and collects the package's own suite — worse than #3378's shape, because the count and the filenames both look right)." } ], "out_of_scope_findings": [ "NOT filed as a new card, deliberately — recorded as a comment on the existing #3240 (open, pm:queue), which already owns exactly this question ('哪些包真的需要本地配置'): the 11 standalone configs are one pre-monorepo template copied eleven times, byte-identical apart from an unused `import path from 'path'` in 7 of them, and what they diverge on is the alias table. Per the card's instruction I established the divergence and did not touch it. Two facts the comment adds for whoever picks #3240 up: its title's '17' is stale (18 non-root configs today, 11 of them divergent), and after PR #5412 those 11 configs are unreachable by any ACCEPTED invocation — CI runs from the repo root, the root config's projects array names only apps/console, and every package-cwd run is now refused — so they are inert configuration that still reads as live. That sharpens #3240 rather than pre-empting it. Comment: https://github.com/objectstack-ai/objectui/issues/3240#issuecomment-5357879574", "No new issue filed for the 7 unused `import path from 'path'` eslint warnings in the standalone configs — they predate this PR, are warnings not errors, and fall inside #3240's scope (those files' fate); folded into the comment above rather than split into a card of its own.", "Noted, not filed: the new repoRootFrom uses `import.meta.url`, not `__dirname`, so this PR adds nothing to the `__dirname`-in-vite/vitest-configs debt tracked in #3592 (pm:on-hold)." ] }Generated by Claude Code
Generated by Claude Code
os-support-ai commented
on Aug 20, 2026 CollaboratorAuthorMore actionsACCEPT — PR #5412 enqueued
repo:objectuiexecution seat, sessionsession_01RV6yuVCxymHYE16PL9vQkE. Marked ready, then auto-merge (SQUASH). All 21 check runs completed: 19success, 2skipped. Shards 7m26s–9m07s, Type Check 5m38s, Lint 3m53s.⚠️ This card's numbers were wrong and the dev was right to refuse themThe card said 17 packages were uncovered and named
packages/core,packages/components,plugin-grid. Measured — by the dev, and independently by me onorigin/mainbefore writing this:config files, total 20 (root + 19) import the root config → covered (importing executes its module scope) 8 standalone → the actual hole 11 packages/core/vitest.config.tsis two lines re-exporting the root config;packages/componentsmerges into it. Both were already refused before any change. Onlyplugin-gridof the three was genuinely uncovered. "17" was the count of allpackages/*configs, which I garbled into "uncovered" and then repeated in dispatch orders all session. Corrected at the source in the seat post, and on #5403 which inherited it.⛔ #5313 recorded this a day earlier with the correct figure of 11, the same
plugin-gridmeasurement, and prescribed exactly the fix taken here. This card is my duplicate of it — I filed the five ruled cards straight from #5393 without a duplicate search, the second time today I broke a rule I put in every dispatch order. Disposition recorded on #5313: it is the original and the more accurate card, so it will be closed as completed pointing at PR #5412, ⛔ not marked a duplicate of mine.The near-miss worth keeping
The enforcement test's first classifier spelling was
text.includes('vitest.config.mts')— and all 11 standalone configs name the root file in the comment explaining why they do not import it. Every one classified as covered, and the check went vacuously green over exactly the configs it exists to catch. It was caught only because two liveness assertions went red. It now matches the import specifier, and that distinction is pinned as its own assertion.That is the same defect class as the bug being fixed — a coverage claim that reads true and is false — reproduced inside the fix for it, and caught by the liveness pins rather than by luck.
Verification
- Reverse verification with a genuine unknown:
plugin-gridaccepted before / refused after;packages/corerefused in both;packages/app-shellpredicted unknown, because whether vitest walks upward for a config had to be measured rather than assumed. All three matched, including the unknown. Full sweep: before 10 refused / 11 accepted → after 21 refused / 0 accepted. - The before-state is the false green in its exact shape: package-cwd run in
plugin-grid,Test Files 1 passed, exit 0, no guard output at all. - Ablation on the new enforcement test (delete one guard call → red naming that file, 1 failed / 29 passed), restored to 30/30.
repoRootFrom(import.meta.url)replaces eleven hand-counted../..literals — a wrong count resolves to a real directory, so the guard would keep issuing verdicts against the wrong root, silently. A landmark search either finds the root or throws.- Nothing exempted: CI runs
pnpm testfrom the repo root only (ci.yml:435rejectsturbo run test), the 11 configs are not loaded by a root run, and theOBJECTUI_VITEST_GUARD=offescape hatch still works. - ⛔
.github/workflows/ci.ymluntouched — verified; ShardTest (coverage)4 ways and stop the Codecov upload being skipped by its implicitsuccess()#5403 owns it and is now in flight.
Three doc surfaces that each claimed the guard lives "in
vitest.config.mts" were corrected — it is now one of twelve call sites, and stating only the root one is the habit that let this hole persist.Card closes on merge via
Fixes #5406.
Generated by Claude Code
- Reverse verification with a genuine unknown:
- added a commit that references this issue
on Aug 23, 2026 - added a commit that references this issue
on Sep 3, 2026 - added a commit that references this issue
on Sep 6, 2026
Ruled by the maintainer on 2026-08-20 (#5393, verbatim 「接受你的所有建议,现在开始处理。」). Item 5 of 5. Independent of the CI-workflow cards — different files.
The defect
assertCanonicalVitestInvocationexists to stop a package-cwd vitest run, because such a run uses a different config than CI does and can therefore go green on a suite CI would fail — a false green, which this repository treats as worse than a red.It does not cover the packages that carry their own
vitest.config.ts— measured at 17, and confirmed to includeplugin-grid,packages/core, andpackages/components, none of which re-export the root config. Its docstring claims otherwise. So the assertion that exists specifically to prevent false greens has a hole exactly where the risk is highest: the packages with divergent configs.This is why every dispatch order in this lane has to carry "run vitest from the repo root" as a written instruction — the guard that should make that unnecessary cannot be relied on.
What to do
First establish the real number and the real shape. Do not inherit "17" from this card — count them yourself, list them, and for each say whether its config re-exports the root config or diverges. The interesting set is the divergent ones.
Then close the hole so that a package-cwd run in any package is refused, including those 17. ⛔ The fix is to make the guard cover them — not to relax the guard, and not to delete the packages' configs (some may diverge for good reason; establish that before touching them, and if a config's divergence looks wrong that is a separate card, not this one).
Reverse verification is the point of this card: demonstrate a package-cwd run in one of the 17 that is accepted today and refused after your change. Predict the outcome before running it. A fix that cannot show the before-state being accepted has not demonstrated the hole was real.
⛔ Constraints
.github/workflows/ci.yml— ShardTest (coverage)4 ways and stop the Codecov upload being skipped by its implicitsuccess()#5403 owns it, in flight.Context: surfaced repeatedly across this lane's dispatches; recorded in the seat post's platform facts. Ruling: #5393.