Observation-class finding, split out of #7390 while implementing it. Unassigned, filed per Prime Directive #10. Nothing a user hits today — this is a drift seam, not a live defect.
What the seam is
The filter slot has four wire spellings, and they are declared in two places with different ownership:
where and filter come from RPC_QUERY_ALIAS_SLOTS (packages/spec/src/data/data-engine.zod.ts), which its own docblock calls "the ONE place the alias to canonical mapping is declared".filters and $filter are wire-only — no schema declares them. packages/metadata-protocol extends the spec table with them locally (WIRE_QUERY_ALIAS_SLOTS).
#7390 added a third reader: packages/rest now gates the filter slot's arity at the querystring ingress, and needs the same four spellings to do it. It derives where / filter from the spec table, but cannot derive filters / $filter — @objectstack/metadata-protocol is a dev-only dependency of @objectstack/rest, so no runtime import of that table exists. They are named literally in packages/rest/src/query-multiplicity.ts (FILTER_SLOT_QUERY_PARAMS).
The consequence
A new wire-only alias for the filter slot, added to metadata-protocol's table, would fold correctly in the normalizer and be silently ungated at the ingress — repetition on that spelling would fall back to the misdiagnosis #7390 exists to remove. Nothing fails: the spelling works, the gate simply does not see it.
Half of it is already pinned: rest-server-repeated-filter-param.test.ts §5 asserts the derived set composes to exactly the four spellings, so a spec-table change goes red. The wire-only half has no such backstop, because the package holding the truth is not on the runtime graph.
Symmetric note: WIRE_DOLLAR_ALIASES in the same normalizer already carries a comment about exactly this class of mistake — "Adding a $ alias in one place and not the other is exactly how a parameter ends up folded but unchecked." That sentence is now true across a package boundary as well as within one.
Options
- Hoist the wire-only spellings into
@objectstack/spec — e.g. a WIRE_ONLY_QUERY_ALIASES export beside RPC_QUERY_ALIAS_SLOTS — so both metadata-protocol and rest read one table. Cleanest, and the only option that removes the seam rather than watching it. It touches the spec's public export surface (check:api-surface regeneration), and whether the wire vocabulary belongs in the spec at all is a genuine question: these spellings exist precisely because no schema declares them. - A cross-package consistency gate — a
check: script asserting rest's literal set equals the normalizer's derived set. No dependency-graph change; adds a script whose only job is to watch two lists. - Leave it, documented. The set has been stable at four spellings for a long time, and the drift only bites if someone adds a fifth.
Recommendation is (1) if the spec seat wants the wire vocabulary declared at all, else (2). Both are cheap; neither is urgent.
Dedup
Searched open issues for WIRE_QUERY_ALIAS_SLOTS, RPC_QUERY_ALIAS_SLOTS, wire alias, $filter, filters plural, and query-alias drift. Hits: #7390 (the parent), #4436, #3774 — none covers the two-package declaration of the wire-only spellings.
Generated by Claude Code
Observation-class finding, split out of #7390 while implementing it. Unassigned, filed per Prime Directive #10. Nothing a user hits today — this is a drift seam, not a live defect.
What the seam is
The filter slot has four wire spellings, and they are declared in two places with different ownership:
whereandfiltercome fromRPC_QUERY_ALIAS_SLOTS(packages/spec/src/data/data-engine.zod.ts), which its own docblock calls "the ONE place the alias to canonical mapping is declared".filtersand$filterare wire-only — no schema declares them.packages/metadata-protocolextends the spec table with them locally (WIRE_QUERY_ALIAS_SLOTS).#7390 added a third reader:
packages/restnow gates the filter slot's arity at the querystring ingress, and needs the same four spellings to do it. It deriveswhere/filterfrom the spec table, but cannot derivefilters/$filter—@objectstack/metadata-protocolis a dev-only dependency of@objectstack/rest, so no runtime import of that table exists. They are named literally inpackages/rest/src/query-multiplicity.ts(FILTER_SLOT_QUERY_PARAMS).The consequence
A new wire-only alias for the filter slot, added to
metadata-protocol's table, would fold correctly in the normalizer and be silently ungated at the ingress — repetition on that spelling would fall back to the misdiagnosis #7390 exists to remove. Nothing fails: the spelling works, the gate simply does not see it.Half of it is already pinned:
rest-server-repeated-filter-param.test.ts§5 asserts the derived set composes to exactly the four spellings, so a spec-table change goes red. The wire-only half has no such backstop, because the package holding the truth is not on the runtime graph.Symmetric note:
WIRE_DOLLAR_ALIASESin the same normalizer already carries a comment about exactly this class of mistake — "Adding a$alias in one place and not the other is exactly how a parameter ends up folded but unchecked." That sentence is now true across a package boundary as well as within one.Options
@objectstack/spec— e.g. aWIRE_ONLY_QUERY_ALIASESexport besideRPC_QUERY_ALIAS_SLOTS— so bothmetadata-protocolandrestread one table. Cleanest, and the only option that removes the seam rather than watching it. It touches the spec's public export surface (check:api-surfaceregeneration), and whether the wire vocabulary belongs in the spec at all is a genuine question: these spellings exist precisely because no schema declares them.check:script assertingrest's literal set equals the normalizer's derived set. No dependency-graph change; adds a script whose only job is to watch two lists.Recommendation is (1) if the spec seat wants the wire vocabulary declared at all, else (2). Both are cheap; neither is urgent.
Dedup
Searched open issues for
WIRE_QUERY_ALIAS_SLOTS,RPC_QUERY_ALIAS_SLOTS, wire alias,$filter,filtersplural, and query-alias drift. Hits: #7390 (the parent), #4436, #3774 — none covers the two-package declaration of the wire-only spellings.Generated by Claude Code