Skip to content

gate-53 effective-manifest-crossref never joins src/registry.js — a manifest component ref and an orphaned registry entry are BOTH silent #238

Description

@rubenvdlinde

Filed alongside ConductionNL/larpingapp#286, where a registered-but-unreferenced component shipped as unreachable UI long enough for its openspec task to be ticked over it. gates 22 and 53 are the two that exist to catch manifest cross-reference defects, and neither sees this one — in either direction.

Measured — with ajv resolved

Important, because this gate has a wiring failure mode that masks its verdict entirely. Without NODE_PATH set, on ConductionNL/larpingapp@development:

[gate-22] manifest-validation: FAIL — SCHEMA VALIDATION DID NOT HAPPEN — Ajv is not resolvable ...
[gate-53] effective-manifest-crossref: FAIL — SCHEMA VALIDATION DID NOT HAPPEN — the vendored
          validator degraded to its structural lint (Ajv unresolvable mid-run)

With NODE_PATH=<app>/node_modules, same commit, both go PASS. Everything below is from the ajv-resolved runs — a red-for-wiring baseline would have told me nothing.

Direction 1 — orphaned registry entry (the #286 shape)

src/registry.js exports EventRoster: { kind: 'section', component: EventRoster }. No tabs[].component, no slots.*, no sections[].component anywhere in src/manifest.json names it. The component is resolvable and unreachable; the check-in surface it renders has no entry point.

[gate-53] effective-manifest-crossref: PASS

Direction 2 — manifest names a component that does not exist

Control on top of the #286 fix branch: changed one tab to name a component no registry exports.

{ "id": "checkin", "label": "Check-in", "icon": "AccountCheck",
  "component": "ThisComponentDoesNotExistAnywhere" }

At runtime CnObjectSidebar.resolveTabComponent() logs Tab "checkin" component "..." not found in registry or customComponents and renders nothing — a blank tab.

[gate-22] manifest-validation: PASS
[gate-53] effective-manifest-crossref: PASS

Both silent.

Cause

scripts/lib/check_manifest_crossref.js never reads src/registry.js. Its joins are manifest→manifest (menu-route → page-id, open-page targets, deepLink correspondence, ADR-044 removals) and manifest→register-JSON (register/schema slug resolution). The one place the component registry comes up, it is explicitly declined:

// The modal registry is app code (src/registry.js et al.) the gate
warn('action-target', ptr, `open-modal action '...' targets '...' — modal registry is app code, not statically checkable`)

src/registry.js is app code, but it is not opaque — it is a fixed-shape ES module whose top-level export keys are statically extractable with a regex on the export default { block, which is how the app-local test in larpingapp#288 does it (it cannot import the module either, since the file pulls in .vue SFCs).

Why it is worth closing

This is the same failure shape the gate suite already treats as first-class elsewhere: gate-56 register-handler-resolution, gate-14 route-reachability (a route pointing at a method that does not exist), gate-57 orphaned-write-capability. A manifest naming a component that does not exist is precisely gate-14's defect one layer up, and an orphaned registry entry is gate-57's.

It also has the "zero callers has two opposite fixes" property, so the gate should report rather than prescribe: an orphan is either wired or deleted, and the gate cannot know which.

Suggested shape

In check_manifest_crossref.js, when src/registry.js exists:

  1. extract top-level keys from the export default { ... } block;
  2. FAIL on any manifest component / slot-override value with no matching key (direction 2 — unambiguously broken, renders nothing);
  3. WARN on any registry key with kind in section/page/widget that no manifest position names (direction 1 — could be a legitimate deletion candidate, so not a hard fail).

Suppressible with the usual <gate> exclude <reason> for entries resolved by convention rather than by name.

Stopgap

larpingapp#288 adds tests/vitest/manifestRegistry.spec.js covering both directions app-locally, with a can-fail proof. Happy to port that logic into the gate if the shape above is acceptable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions