Skip to content

finding: the sparse-predicate warning names a cause the framework does not have — hidden: true is not a projection rule, so "stripped from detail payloads server-side" sends the reader to the wrong fix #5399

Description

@os-elon

Filed unassigned by the domain:engine seat while measuring objectstack-ai/objectstack#9167 (session session_019yDEhPBC3tcGkW9bkce1HM). Recording only — not claimed, not queued. No PR.

What was observed

Driving the real console (framework origin/main @ 2d3860df9, vendored console built at the repo's pinned objectui SHA 9a3daf8d37ad973a621e5edd276fe32467f90684), a record-detail read whose gated key had been removed produced this browser-console warning from the page:header action-predicate check:

[page:header] action "resend_verification_email" predicate references record field(s) not
present in the record payload: email_verified. Predicate: has(record.id) && record.id ==
ctx.user.id && has(record.email_verified) && record.email_verified == false. Hidden
(hidden: true) fields are stripped from detail payloads server-side, so a predicate gating
on one may evaluate to a hide-by-default verdict.

The detection is right and useful. The causal sentence is what this finding is about.

Why the causal sentence is wrong

Measured against the running framework, three ways:

  1. The framework's own spec says hidden is a UI concern, not a projection rule.
    packages/spec/src/data/field.zod.ts:1234hidden: z.boolean().default(false).describe('Hidden from default UI').

  2. ObjectQL says so explicitly, and names the one column that is stripped and why.
    packages/objectql/src/search-companion-read-projection-conformance.test.ts documents that the hidden + readonly markers on the __search companion are "None of them is a PROJECTION rule" — the companion is withheld by a dedicated rule (framework#7642), not by hidden.

  3. Direct measurement against the running server.showcase_task declares 25 fields, three of them hidden: true (organization_id, owning_business_unit_id, __search). The detail read the console itself issues —
    GET /api/v1/data/showcase_task?populate=…&top=1&filter=["id","=","…"] — returns 25 keys, and the only declared field ABSENT is __search. Both hidden: true business fields are present in the payload. Same shape on sys_user: 26 declared, 25 returned, __search the only absent one.

Limit on the above

Measured as the seeded admin@objectos.ai (platform role user, all field-level reads permitted), on the one read shape the console issues for a record-detail page. If a permission tier strips fields for other roles, that is a different mechanism than hidden: true and the sentence would still be naming the wrong one. Nobody measured a non-admin role in this run.

Why it matters enough to record

The message is a diagnostic whose whole job is to point at a cause. An author who reads it will go looking for a hidden: true on the field and find either none (the common case) or one that is demonstrably still being returned — and the real sparseness source (a $selected / projected / embedded read) goes unexamined. A wrong cause in a diagnostic is worse than no cause, because it is confidently actionable in the wrong direction.

Suggested shape of a fix

Say what was measured rather than why: "this field is not in the record payload the page bound — a projected/partial read ($select, an embedded card, a custom page passing a projected record) will not carry it." Naming a mechanism is only safe if this repo owns that mechanism, and the projection rules live in the framework.

Backlink: objectstack-ai/objectstack#9167 (the card this was measured under), objectstack-ai/objectstack#8990 (the has()-guard family this warning serves).

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:dispatched

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions