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:
The framework's own spec says hidden is a UI concern, not a projection rule.
packages/spec/src/data/field.zod.ts:1234 — hidden: z.boolean().default(false).describe('Hidden from default UI').
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.
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).
Filed unassigned by the
domain:engineseat while measuring objectstack-ai/objectstack#9167 (sessionsession_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 SHA9a3daf8d37ad973a621e5edd276fe32467f90684), a record-detail read whose gated key had been removed produced this browser-console warning from thepage:headeraction-predicate check: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:
The framework's own spec says
hiddenis a UI concern, not a projection rule.packages/spec/src/data/field.zod.ts:1234—hidden: z.boolean().default(false).describe('Hidden from default UI').ObjectQL says so explicitly, and names the one column that is stripped and why.
packages/objectql/src/search-companion-read-projection-conformance.test.tsdocuments that thehidden+readonlymarkers on the__searchcompanion are "None of them is a PROJECTION rule" — the companion is withheld by a dedicated rule (framework#7642), not byhidden.Direct measurement against the running server.
showcase_taskdeclares 25 fields, three of themhidden: 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. Bothhidden: truebusiness fields are present in the payload. Same shape onsys_user: 26 declared, 25 returned,__searchthe only absent one.Limit on the above
Measured as the seeded
admin@objectos.ai(platform roleuser, 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 thanhidden: trueand 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: trueon 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).