Skip to content

A source-text ratchet cannot see a component with no spec at all — #2918's hole is still open #2940

Description

@vybe

Filed from the 2026-09-21 merge train (#2939). Three of five members landed safety-critical .vue logic with no executing coverage, and two of them stated a false reason for it. #2922 landed on the same train and closes #2918, but its mechanism cannot catch this class — so the hole #2918's title names is still open.

The gap in one line

The source-text ratchet fails a spec that reads source text. It does nothing about a component that has no spec at all — the scanner has nothing to look at.

Evidence from this train

PR What was inert How it was proven
#2920 SkillsPanel.vue, ~60 lines of conflict UI plus the fix for the draft-wipe regression the PR itself introduced disabling the whole conflict block and reverting the fix left the suite byte-identically green (139 files / 3209 tests either way)
#2756 the entire .vue layer (6 components) mutating SystemTeardownPanel.vue so the removal checklist always arrives empty — panel's stated default broken, deploy→remove flow dead — passed 36/36 Python guards and 141 files / 3235 vitest
#2778 BaseModal.vue (fixed before this train, after two ejections) the 2026-09-20 ejection; now covered by a mounted spec and 12 mutations

Both #2920's and #2756's regressions were caught by a human clicking through a live stack, not by CI.

The false rationale is the load-bearing part

test_ent454_teardown_frontend.py and baseButtonVariantBorder.spec.js both asserted that vitest.config.js "pins environment: 'node' with no component mounting". That is false — it is the default only; a spec opts in per file with // @vitest-environment jsdom, and 22 specs already mount. #2922's own docblock says the same thing: "Every time, the stated reason was 'vitest here is node-only, nothing can mount' — false."

Corrected in both files during the train, but a corrected comment does not stop the next author writing it again.

Suggested direction (not a prescription)

  1. A coverage-side ratchet, not only a source-text one. Something shaped like: a component under an agreed safety-critical set (or any .vue carrying a v-model/watcher that decides a destructive default) must have at least one spec that mounts it. Freeze today's list, then pay down — the same shape as raw-color-baseline.json and loading-gate-baseline.json.
  2. Close refactor(tests): make the mount harness discoverable and ratchet source-text specs over components (#2918) #2922's escape-hatch hole while you are here — PIN_MARKER.exec(source) scans the whole file though the contract says "in its docblock" in four places, so sourceTextRatchet.spec.js self-exempts on its own prose (harvested reason: the literal <reason>). Its 5 reads are ungated, and any future spec quoting the failure message self-exempts the same way. loadingGateRatchet.spec.js:70 avoids this structurally by scanning src/, not the directory containing itself. Related: the committed baseline is not the fixed point of its own documented regen command (adds 1 entry, totals 271/86 → 277/88), and totals is unasserted.
  3. Put the decision where it can be executed, as feat(systems): the public half of system teardown — a structured membership read, a license-blind MCP proxy, and a gated remove panel (abilityai/trinity-enterprise#454) #2756 did. Moving the destructive default out of the watcher and into the store is why that one rule is covered today. That is a good pattern worth stating in the contract rather than rediscovering per-PR.

Why this is worth a ticket rather than three PR comments

It is one upstream habit, not three bugs. It has now produced: a pre-ticked delete confirmation (#2756), an unpinned regression fix (#2920), and two prior ejections (#2778). Each was reported to a different author on a different PR, and nobody correlated them until they arrived on one train.

Refs #2918 (closed by #2922), #2920, #2756, #2778, #2922.

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