Skip to content

finding(tooling): check:spec-symbols matches by name, so a hand copy renamed away from the spec's symbol is invisible to it #4592

Description

@yinlianghui

Observation-class finding, surfaced while implementing #4588 (PR #4591). Nothing is being changed for it there — the gate is outside that card's surface, and both known instances are already fixed. Filed so the next one is a decision rather than an accident.

What was found

scripts/check-spec-symbol-derivation.mjs enforces a real and valuable rule: "A spec-named symbol must be DERIVED from @objectstack/spec, not hand-written." Its matching is by name — it looks for a local declaration whose identifier equals a @objectstack/spec export, and its allowlist is keyed package:symbol.

So the gate catches a fork that keeps the spec's name. It cannot see a fork that was renamed.

ViewNavigationConfig (packages/types/src/objectql.ts) was exactly that. A hand-written interface mirroring the spec's NavigationConfigSchema — the same six keys — that had drifted on one of them: it required mode, which the spec declares as mode: NavigationModeSchema.default('page') and therefore publishes as input-optional. Because it was not named NavigationConfig, the gate had nothing to match, and the copy passed every CI run since it was written. It was found by a manual census for #4550, not by a check.

The sibling case is worth recording next to it: @object-ui/react's NavigationConfig (#4550, PR #4586) did carry the spec's own name, but was written as a type alias over the spec type via Omit, so it was structurally derived and passed the gate while still being wrong about mode. Two instances of one defect, two different reasons the gate could not see either.

Why it may matter

The gate's own header argues the harm better than this issue can: an agent reads a canonical-sounding doc comment and builds on it, so "a wrong canonical-claim is not stale documentation — it is a planted premise for the next session."

A renamed copy carries that identical planted premise while sitting outside the check written to catch it. ViewNavigationConfig's comment read "Aligned with @objectstack/spec ListView.navigation" — canonical-sounding, and false on mode.

No user hits this today, and no metadata is refused because of it; it is a coverage observation about a guard, which is why it is filed as an observation rather than a defect.

Deliberately not established here

  • Whether name-matching is the right scope. It may well be a considered choice. The structural alternative — "does this local type mirror a spec object's key set, whatever it is called" — is a much larger instrument and would need a false-positive story (the ALLOW map already documents five same-name shapes that are divergent on purpose).
  • How many renamed copies exist today. Not measured. This finding is one instance plus its sibling, not a census.
  • Whether a cheaper approximation would pay for itself — e.g. flagging a local declaration whose doc comment claims spec alignment (Aligned with @objectstack/spec …) but which carries no structural spec reference. That is a heuristic on prose, with its own costs.

Refs #4588, #4550, PR #4586, PR #4591, objectstack#4115.


Generated by Claude Code

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions