Recorded while implementing #9257 (PR adding validateSortableFields). Not claimed. Filed as a sub-issue of #4716 because it is one concrete item of that card's "全量 runtime-safe 规则快照" half, not a separate direction.
What is measured
REFERENCE_INTEGRITY_RULES is one entry in the authoring-rule registry, and that entry declares:
surfaces: CLI_AND_RUNTIME,
runtimeTypes: ['flow'],
So on the runtime publish gate the suite runs only for a flow snapshot. Two of its members judge a list view's field references and neither can fire there:
Both surfaces they walk are reachable through the metadata door. A standalone list view is written as ViewMetadataSchema's flattened list-overlay member (packages/spec/src/ui/view.zod.ts, ListViewOverlayWireSchema — ListViewSchema.extend(flattenedViewOverlayFields()).strip(), with object + viewKind required since #7741), which carries sort and searchableFields at its top level. That door is PUT /api/v1/meta/view, i.e. the only door a Studio tenant or an MCP/AI author has.
Net effect: an author who writes the exact declaration both rules exist to refuse gets it accepted, because the author who would have been stopped is the one who never runs a CLI command. That is #4463's own founding shape ("the runtime write path, the only door a Studio tenant or an MCP/AI author has, ran none of the 26 rules at all"), surviving on this one registry entry.
Note the two halves are independent: even with runtimeTypes widened to view, the metadata walk in validateSearchableFields / validateSortableFields reads objects[].listViews.* and views[].list / views[].listViews.* — it does not read a top-level flattened overlay. So this needs both a dispatch widening and a rung in the walk, and doing only the first would be a silent no-op that reads as coverage.
Why it was not fixed in the #9257 PR
authoring-rules.ts records the decision boundary in the suite's own entry — widening the gate's dispatch surface "would widen the gate's dispatch surface on this rule's authority, which is P2's decision". A new rule joining the suite inherits the entry; it does not get to change it. The #9257 PR therefore documents the gap in its module docblock and leaves it here.
The false-positive budget applies unchanged
#4716's ruling that this is not a one-line runtimeTypes edit holds here too: view writes are hot (every console column-sort PUT is one), and both rules gate at error. A tenant-metadata replay is owed before either reaches the runtime door — with the added wrinkle that a console sort PUT persists sort: [{ id, field, order }] rows carrying objectui's crypto.randomUUID() row id (view.zod.ts's #5074 note), so the replay must run against the shape saveMetaItem really stores, not the authoring shape.
Refs
#4716 (parent, P2), #4463 (P1), #9257 (the SORT gate), #6674 (the SEARCH gate), #7741 (the overlay's required pair), #7220 and #6715 (the same shape for two other rule families).
Recorded while implementing #9257 (PR adding
validateSortableFields). Not claimed. Filed as a sub-issue of #4716 because it is one concrete item of that card's "全量 runtime-safe 规则快照" half, not a separate direction.What is measured
REFERENCE_INTEGRITY_RULESis one entry in the authoring-rule registry, and that entry declares:So on the runtime publish gate the suite runs only for a
flowsnapshot. Two of its members judge a list view's field references and neither can fire there:validateSearchableFields—searchable-field-unknown/-unsearchable/-unprovisioned(A virtual formula field declared in searchableFields clears lint and the #4254 gate, then silently matches nothing — the same fail-open shape #4254 closed one axis over #6674, compiler/validate: view searchableFields not validated against searchable field types — a declaration typo 400s the whole list at runtime #4830, lint:searchableFieldsis a FIFTH blanket-SYSTEM_FIELDS read site — a declared anchor on an external object becomes a$searchFieldsnarrowing over a column with no storage #8404);validateSortableFields—sort-field-unknown/-unsortable(A view's declaredsortnaming aformulafield clears lint, then 400s on every load — the SORT axis has no authoring gate, unlike SEARCH (#6674) #9257, landing now).Both surfaces they walk are reachable through the metadata door. A standalone list view is written as
ViewMetadataSchema's flattened list-overlay member (packages/spec/src/ui/view.zod.ts,ListViewOverlayWireSchema—ListViewSchema.extend(flattenedViewOverlayFields()).strip(), withobject+viewKindrequired since #7741), which carriessortandsearchableFieldsat its top level. That door isPUT /api/v1/meta/view, i.e. the only door a Studio tenant or an MCP/AI author has.Net effect: an author who writes the exact declaration both rules exist to refuse gets it accepted, because the author who would have been stopped is the one who never runs a CLI command. That is #4463's own founding shape ("the runtime write path, the only door a Studio tenant or an MCP/AI author has, ran none of the 26 rules at all"), surviving on this one registry entry.
Note the two halves are independent: even with
runtimeTypeswidened toview, the metadata walk invalidateSearchableFields/validateSortableFieldsreadsobjects[].listViews.*andviews[].list/views[].listViews.*— it does not read a top-level flattened overlay. So this needs both a dispatch widening and a rung in the walk, and doing only the first would be a silent no-op that reads as coverage.Why it was not fixed in the #9257 PR
authoring-rules.tsrecords the decision boundary in the suite's own entry — widening the gate's dispatch surface "would widen the gate's dispatch surface on this rule's authority, which is P2's decision". A new rule joining the suite inherits the entry; it does not get to change it. The #9257 PR therefore documents the gap in its module docblock and leaves it here.The false-positive budget applies unchanged
#4716's ruling that this is not a one-line
runtimeTypesedit holds here too:viewwrites are hot (every console column-sort PUT is one), and both rules gate aterror. A tenant-metadata replay is owed before either reaches the runtime door — with the added wrinkle that a console sort PUT persistssort: [{ id, field, order }]rows carrying objectui'scrypto.randomUUID()rowid(view.zod.ts's#5074note), so the replay must run against the shapesaveMetaItemreally stores, not the authoring shape.Refs
#4716 (parent, P2), #4463 (P1), #9257 (the SORT gate), #6674 (the SEARCH gate), #7741 (the overlay's required pair), #7220 and #6715 (the same shape for two other rule families).