Filed unassigned, unlabelled by the domain:spec @ objectui seat (#5734) as the R1 landing chore for #5330. Left for triage to grade and route — it needs an access channel this seat does not have.
Why this needs a carrier now, not later
The 2026-08-20 ruling on #5330 makes a survey the gate on Phase 2: "renderers keep the three-way binding for stored metadata until a stored-metadata survey sizes the removal window (⛔ no removal before the survey)."
Phase 1 has now shipped (PR #5737): the canon record.* is declared and the two non-canonical spellings warn once in dev. Nothing currently carries Phase 2.
That is the exact failure this lane already paid for once. #5330 exists at all because the spelling question was referenced for months as "#4075 step 3, parked in the decision inbox" while no card ever carried it — #4075 closed with no step-3 text anywhere in its thread, and #4807 sat pm:blocked on a ruling that had no carrier. Leaving the survey uncarded reproduces that shape one phase later, with the added cost that a deprecation warning is now live in the field while the thing that would end it is unowned.
What the survey must sample
Specified by the dev that implemented Phase 1, and carried here so it is not re-derived. Per stored visible / disabled / enabled predicate on a runtime record surface:
- which CEL root the predicate uses —
record.*, data.*, or a bare field; - bucketed by tenant, and by whether the metadata is customer-authored or ships in an example app.
The second bucketing is what makes the result actionable: a population that is entirely example-app metadata is one this repo can fix itself, and a population concentrated in customer-authored documents is a migration window that has to be communicated.
What already exists to do it with
detectNonCanonicalRowSpelling is exported from @object-ui/core (packages/core/src/evaluator/rowPredicateCanon.ts) specifically so the sweep can run offline over stored metadata rather than waiting for a render to emit a console warning. It carries the same three conservative stand-downs the runtime warning uses, so an offline sweep and the live warning cannot disagree.
⚠️Do not sweep with a regex or a blanket find-and-replace.data is the canonical root one layer over, in a metadata-editing form (ADR-0089 D3, CANONICAL_ROOT_BY_LAYER = { runtime: 'record', metadata: 'data' }); objectui's own app-shell metadata-admin SchemaForm binds { data: row } deliberately. A data.* predicate is only non-canonical on a runtime record surface.
The blocker triage needs to route around
The survey needs a live-tenant data channel — read access to stored metadata across tenants. The domain:spec @ objectui seat has no such access, which is why this is filed rather than dispatched. Routing this to whichever seat or repo owns that channel (plausibly cloud) is a triage decision, not this seat's.
Until it runs, Phase 2 is correctly blocked and the warning is doing its job: ADR-0078 — a tolerance nothing ever reports can never be retired, because nothing would ever show that the last document carrying it is gone.
Refs
Filed unassigned, unlabelled by the
domain:spec@ objectui seat (#5734) as the R1 landing chore for #5330. Left for triage to grade and route — it needs an access channel this seat does not have.Why this needs a carrier now, not later
The 2026-08-20 ruling on #5330 makes a survey the gate on Phase 2: "renderers keep the three-way binding for stored metadata until a stored-metadata survey sizes the removal window (⛔ no removal before the survey)."
Phase 1 has now shipped (PR #5737): the canon
record.*is declared and the two non-canonical spellings warn once in dev. Nothing currently carries Phase 2.That is the exact failure this lane already paid for once. #5330 exists at all because the spelling question was referenced for months as "#4075 step 3, parked in the decision inbox" while no card ever carried it — #4075 closed with no step-3 text anywhere in its thread, and #4807 sat
pm:blockedon a ruling that had no carrier. Leaving the survey uncarded reproduces that shape one phase later, with the added cost that a deprecation warning is now live in the field while the thing that would end it is unowned.What the survey must sample
Specified by the dev that implemented Phase 1, and carried here so it is not re-derived. Per stored
visible/disabled/enabledpredicate on a runtime record surface:record.*,data.*, or a bare field;The second bucketing is what makes the result actionable: a population that is entirely example-app metadata is one this repo can fix itself, and a population concentrated in customer-authored documents is a migration window that has to be communicated.
What already exists to do it with
detectNonCanonicalRowSpellingis exported from@object-ui/core(packages/core/src/evaluator/rowPredicateCanon.ts) specifically so the sweep can run offline over stored metadata rather than waiting for a render to emit a console warning. It carries the same three conservative stand-downs the runtime warning uses, so an offline sweep and the live warning cannot disagree.datais the canonical root one layer over, in a metadata-editing form (ADR-0089 D3,CANONICAL_ROOT_BY_LAYER={ runtime: 'record', metadata: 'data' }); objectui's ownapp-shellmetadata-adminSchemaFormbinds{ data: row }deliberately. Adata.*predicate is only non-canonical on a runtime record surface.The blocker triage needs to route around
The survey needs a live-tenant data channel — read access to stored metadata across tenants. The
domain:spec@ objectui seat has no such access, which is why this is filed rather than dispatched. Routing this to whichever seat or repo owns that channel (plausiblycloud) is a triage decision, not this seat's.Until it runs, Phase 2 is correctly blocked and the warning is doing its job: ADR-0078 — a tolerance nothing ever reports can never be retired, because nothing would ever show that the last document carrying it is gone.
Refs
record.*, or do the bare shorthand anddata.*spellings stay? (the uncarried "#4075 step 3" question) #5330 — the canon ruling (work carrier for Phase 1); PR feat(core,react): declarerecord.*the row-predicate canon and warn on the two deprecated spellings #5737 — Phase 1, the canon statement plus the warningdata.*spellings the new warning now flags #5738 — Phase 0, the docs/example-app sweep, also unownednormalizeFilterOptionsaccepts a bare-stringglobalFilters[].optionsshorthand that@objectstack/specrejects at publish #4356 / objectstack#7917 — the sibling deprecation this one mirrors, whose survey shipped as delivered workpackages/core/src/evaluator/rowPredicateCanon.ts— the canon statement, the server accept-set measurement, and the exported detector