You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
finding(tooling): check-spec-symbol-derivation.mjs skips every non-exported declaration, so the #5652 mirrors it was cited to catch were invisible to both rules #5899
Found while implementing #5652. Filed unassigned as an observation, for PM triage.
The claim the card made, and what actually happened
#5652 described three hand-written mirrors of the FormViewSchema contract in packages/react/src/spec-bridge/bridges/form-view.ts and said:
That is the mirror shape scripts/check-spec-symbol-derivation.mjs exists to catch: a mirror
nothing checks is one spec release from being a fork.
It is the right shape — but the guard never had jurisdiction over that file, and could not have
reported it. Two of the three mirrors were even declared under the spec's own export names
(FormField, FormSection), which is precisely rule 1's trigger, and the gate was green
throughout.
Mechanism — both rules skip non-exported declarations
scripts/check-spec-symbol-derivation.mjs collects declarations behind the same guard in both
scanners:
consthasExportModifier=(node)=>node.modifiers?.some((m)=>m.kind===ts.SyntaxKind.ExportKeyword)??false;
...
if(!hasExportModifier(stmt))continue;// line 827 — rule 1 (spec-named symbol)if(!hasExportModifier(stmt))continue;// line 923 — rule 2 (alignment claim)
The three mirrors were module-local interfaces, so neither scanner saw them. Confirmed
directly: with the pre-fix file in the tree, --ledger and --claim-ledger both print zero
rows mentioning @object-ui/react, form-view, FormField or FormSection from this file,
and the gate exits 0.
Why the exclusion is not obviously wrong — the part that needs a decision
The export filter is defensible on the script's own terms: an unexported type cannot be imported
by another package under the spec's name, so it cannot become "a planted premise" through the
package's public surface. What #5652 measured is the other half — an unexported mirror is still
read by the next agent editing that file, and it still drifts. The bridge's mirror had drifted on
three keys and had one of them inverted (it admitted only the arm the contract rejects), which is
exactly the damage rule 1 exists to prevent, one visibility modifier below where the guard looks.
So the question is not "delete the filter" — that is a population change, and the script's own
header is explicit that an ALLOW map with dozens of entries is not a guard. The measurement it
needs is the one #4592 already did once for the structural alternative: how many non-exported
declarations across the tree would rules 1 and 2 flag if the export filter were dropped, and are
they real mirrors or legitimate module-local shapes. That number decides whether this is a
one-line change, a scoped change (module-local declarations in packages that already import from @objectstack/spec), or a documented non-goal.
Whatever the answer, the header's own account of the failure class deserves the correction: the
class currently reads as fully covered by rule 1 plus rule 2, and #5652 is a specimen that neither
rule could see.
Boundary
Searched the open issues before filing (check-spec-symbol-derivation, derivation, guard,
mirror, spec symbol phrasings, over all 231 open issues — this session cannot reach the search
API, so the list was fetched and filtered locally). Nothing open covers it. ObjectTrigger and ObjectRelationship are orphaned hand-written exports after the ObjectSchemaMetadata derivation (#5362) #5859 touches the
same script's subject matter (orphaned hand-written exports after a derivation) but is about
exported symbols, which the guard does see.
Found while implementing #5652. Filed unassigned as an observation, for PM triage.
The claim the card made, and what actually happened
#5652 described three hand-written mirrors of the
FormViewSchemacontract inpackages/react/src/spec-bridge/bridges/form-view.tsand said:It is the right shape — but the guard never had jurisdiction over that file, and could not have
reported it. Two of the three mirrors were even declared under the spec's own export names
(
FormField,FormSection), which is precisely rule 1's trigger, and the gate was greenthroughout.
Mechanism — both rules skip non-exported declarations
scripts/check-spec-symbol-derivation.mjscollects declarations behind the same guard in bothscanners:
The three mirrors were module-local
interfaces, so neither scanner saw them. Confirmeddirectly: with the pre-fix file in the tree,
--ledgerand--claim-ledgerboth print zerorows mentioning
@object-ui/react,form-view,FormFieldorFormSectionfrom this file,and the gate exits 0.
Why the exclusion is not obviously wrong — the part that needs a decision
The export filter is defensible on the script's own terms: an unexported type cannot be imported
by another package under the spec's name, so it cannot become "a planted premise" through the
package's public surface. What #5652 measured is the other half — an unexported mirror is still
read by the next agent editing that file, and it still drifts. The bridge's mirror had drifted on
three keys and had one of them inverted (it admitted only the arm the contract rejects), which is
exactly the damage rule 1 exists to prevent, one visibility modifier below where the guard looks.
So the question is not "delete the filter" — that is a population change, and the script's own
header is explicit that an ALLOW map with dozens of entries is not a guard. The measurement it
needs is the one #4592 already did once for the structural alternative: how many non-exported
declarations across the tree would rules 1 and 2 flag if the export filter were dropped, and are
they real mirrors or legitimate module-local shapes. That number decides whether this is a
one-line change, a scoped change (module-local declarations in packages that already import from
@objectstack/spec), or a documented non-goal.Whatever the answer, the header's own account of the failure class deserves the correction: the
class currently reads as fully covered by rule 1 plus rule 2, and #5652 is a specimen that neither
rule could see.
Boundary
check-spec-symbol-derivation, derivation, guard,mirror, spec symbol phrasings, over all 231 open issues — this session cannot reach the search
API, so the list was fetched and filtered locally). Nothing open covers it.
ObjectTriggerandObjectRelationshipare orphaned hand-written exports after the ObjectSchemaMetadata derivation (#5362) #5859 touches thesame script's subject matter (orphaned hand-written exports after a derivation) but is about
exported symbols, which the guard does see.
tightenings of it), [finding] A THIRD hand-written FormViewSchema mirror survives in
packages/react's spec bridge, and it has drifted on three keys #5596 just measured #5652 (the specimen).packages/react's spec bridge, and it has drifted on three keys #5596 just measured #5652 are fixed and pinned, so this is not a liveregression — it is a hole in the instrument, and its price is that the next file-local mirror
is found by a human reading the file, which is how this one was found.
Generated by Claude Code