Skip to content

finding(test-infra): check-vi-mock-inherit's covered set is exact-match, so a SUBPATH of a swept package is never judged #8141

Description

@claude

Surfaced while sweeping @object-ui/components for objectui#6892 (slice 6). Deliberately not fixed there — that PR sweeps one specifier and adds one line to COVERED_SPECIFIERS; changing what the gate's scope RESOLVER accepts is a different decision.

What was measured

scripts/check-vi-mock-inherit.mjs decides scope by EXACT string equality against COVERED_SPECIFIERS:

coveredSet.has(specifier)
  ? 'covered'
  : specifier.startsWith('@object-ui/')
    ? 'workspace'   // counted, never judged
    : 'external'

So a SUBPATH of an already-swept package is not covered. Measured on 214d5d5a6 (the slice-6 head, eleven members in the constant), by running the gate's own exported scan():

COVERED_SPECIFIERS members                          : 11
vi.mock call sites naming a SUBPATH of one of them  : 2
  @object-ui/components/ui/sonner
    packages/plugin-form/src/MasterDetailForm.outcomeToastSupersede.test.tsx:99
    packages/plugin-form/src/WizardForm.outcomeToastSupersede.test.tsx:96
judged with those two subpaths added to `covered`   : 2 inherit, 0 frozen

Both already inherit, so tightening this today is free — the same "the tree is at zero, so the gate starts as a ratchet" argument that scoped objectui#6849.

Why it matters

A package joins COVERED_SPECIFIERS because its frozen factories were swept to zero and the gate now holds it there. That guarantee silently does not extend to its own subpaths: a factory written tomorrow as a frozen double of @object-ui/components/ui/sonner (or any future subpath of any of the eleven) is counted in the "other workspace" bucket and never judged, while the maintainer's reading of the verdict line is that the package is covered. The defect the gate exists to catch — a module-scope read of an export the frozen stand-in never listed, killing the file during COLLECTION with zero failed assertions — does not care whether the specifier was the package root or a subpath of it.

This is not an exemption and not a recogniser bug: the resolver is doing exactly what it says. It is a scope boundary that was drawn when the covered set had one member and no one had a subpath.

Options, not a recommendation

  1. Prefix match — treat spec === c || spec.startsWith(c + '/') as covered. Smallest change; makes every future subpath of a swept package covered by construction. Needs the population re-measured first, because a subpath can have a genuinely different export surface.
  2. Enumerate subpaths in the constant — keep exact matching, add @object-ui/components/ui/sonner as its own member. Honest and free today, but the constant then has to grow once per subpath and the gap reopens for every new one.
  3. Leave it and say so in the header — record the boundary explicitly so the verdict line is not read as a claim about subpaths.

Option 1 is the only one that closes the class rather than the two instances; option 2 is the only one that keeps the "a member joins only by sweep" invariant literally true. Both are free at today's numbers.

Reproduce

node -e "import('./scripts/check-vi-mock-inherit.mjs').then(m => { const r = m.scan(process.cwd(), { covered: [], floors: {} }); console.log(r.sites.filter(s => s.scope === 'workspace' && m.COVERED_SPECIFIERS.some(c => s.specifier.startsWith(c + '/')))); })"

Related: objectui#6849 (the gate), objectui#6892 (the per-specifier worklist), objectui#8117 (a different pin-versus-prose issue in the same file).


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions