You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A page authored as a raw Page object literal serves every ExpressionInputSchema key BARE, and the console silently routes bare predicates to its legacy evaluator #11255
Filed unassigned by the domain:engine seat while implementing #9167 round 2 (session session_01RfyXxZ2WPjcjhuXpiQQc3y, PR #11254). Recording the class; the one live instance is fixed in that PR. Not claimed, not queued.
The shape
PageComponentSchema.visibleWhen is an ExpressionInputSchema (packages/spec/src/ui/page.zod.ts:189), whose transform normalizes a bare string into the canonical { dialect: 'cel', source } envelope. That transform only runs if something parses the page. A page authored as
exportconstSysUserDetailPage: Page={ … }
is a plain typed object literal — the type is checked, nothing is parsed — so whatever the author wrote reaches /api/v1/meta/page verbatim. A page authored through definePage({ … }) is parsed and serves the envelope.
Measured on origin/main @ 824a996c0 with a real dev server, the two authored record:alert gates side by side after the #9167 migration text was applied literally to both:
objectui's ExpressionEvaluator.evaluateCondition at the pinned SHA (190fbd01d) routes by shape, deliberately:
Bare strings and ${…} templates stay on the legacy path (back-compat deprecation window); only an explicit { dialect: 'cel' } envelope is rerouted.
The legacy JS evaluator has no has(), and the component-visibility surface is fail-soft, so a guarded predicate throws and resolves to SHOWN. Measured in the real console, on the bare form:
run
probe
verdict
expected
B2
sys_user self, email_verified forced true
VISIBLE
hidden ❌
B3
another user's profile (control: that page's own title)
VISIBLE
hidden ❌
Isolated to the envelope alone — one page, one predicate source, one rewrite path:
run
probe
verdict
P1
node visibleWhen rewritten to the bare string
VISIBLE ❌
P2
node visibleWhen rewritten to the envelope, same source
hidden ✅
And in a production console bundle the fault is completely silent: SchemaRenderer's evaluateVisibilityPredicate short-circuits on if (!__DEV__) before the diagnostic probe, so nothing is logged. The whole failure is "a declared gate stops gating, with no output anywhere".
Current blast radius — one live instance, already fixed; the rest is latent
Swept over every *.page.ts in the repo (export form classified from code with comments stripped):
So today nothing else is broken. What makes this worth a card is that the hazard is invisible at authoring time: the type accepts the bare string, tsc is green, every test is green, the gate farm is green, and the only symptom is a banner that stops gating in a shipped platform page. The next predicate added to a raw-literal platform page reproduces it exactly.
Options, not a recommendation to implement blind
A. A gate. Assert that every page reachable from a platform package serves canonical envelopes for its ExpressionInputSchema keys — e.g. parse each exported Page through PageSchema in a test and compare, which also closes the general "raw literal was never parsed" hole rather than the predicate half of it.
B. Route platform pages through definePage(). Removes the fork at the source, but it changes the export shape of shipped platform pages and a strict parse may reject unrelated pre-existing looseness — needs its own measurement before anyone commits to it.
Related, upstream and separate: the renderer's bare-string back-compat window is objectui's, and closing it there would change verdicts for every un-normalized predicate in the wild — not something to ask for from this side.
Filed unassigned by the
domain:engineseat while implementing #9167 round 2 (sessionsession_01RfyXxZ2WPjcjhuXpiQQc3y, PR #11254). Recording the class; the one live instance is fixed in that PR. Not claimed, not queued.The shape
PageComponentSchema.visibleWhenis anExpressionInputSchema(packages/spec/src/ui/page.zod.ts:189), whose transform normalizes a bare string into the canonical{ dialect: 'cel', source }envelope. That transform only runs if something parses the page. A page authored asis a plain typed object literal — the type is checked, nothing is parsed — so whatever the author wrote reaches
/api/v1/meta/pageverbatim. A page authored throughdefinePage({ … })is parsed and serves the envelope.Measured on
origin/main@824a996c0with a real dev server, the two authoredrecord:alertgates side by side after the #9167 migration text was applied literally to both:visibleWhenon the wireshowcase_task_detail(definePage){"dialect":"cel","source":"has(record.status) && …"}sys_user_detail(rawPageliteral)"has(record.id) && …"— bareWhy bare is not merely cosmetic
objectui's
ExpressionEvaluator.evaluateConditionat the pinned SHA (190fbd01d) routes by shape, deliberately:The legacy JS evaluator has no
has(), and the component-visibility surface is fail-soft, so a guarded predicate throws and resolves to SHOWN. Measured in the real console, on the bare form:sys_userself,email_verifiedforcedtrueIsolated to the envelope alone — one page, one predicate source, one rewrite path:
visibleWhenrewritten to the bare stringvisibleWhenrewritten to the envelope, same sourceAnd in a production console bundle the fault is completely silent:
SchemaRenderer'sevaluateVisibilityPredicateshort-circuits onif (!__DEV__)before the diagnostic probe, so nothing is logged. The whole failure is "a declared gate stops gating, with no output anywhere".Current blast radius — one live instance, already fixed; the rest is latent
Swept over every
*.page.tsin the repo (export form classified from code with comments stripped):visibleWhenpackages/platform-objects/src/pages/sys-user.page.tsPpackages/platform-objects/src/pages/sys-organization.page.tspackages/platform-objects/src/pages/sys-position.page.tspackages/qa/downstream-contract/src/welcome.page.tsexamples/**definePage()So today nothing else is broken. What makes this worth a card is that the hazard is invisible at authoring time: the type accepts the bare string,
tscis green, every test is green, the gate farm is green, and the only symptom is a banner that stops gating in a shipped platform page. The next predicate added to a raw-literal platform page reproduces it exactly.Options, not a recommendation to implement blind
ExpressionInputSchemakeys — e.g. parse each exportedPagethroughPageSchemain a test and compare, which also closes the general "raw literal was never parsed" hole rather than the predicate half of it.definePage(). Removes the fork at the source, but it changes the export shape of shipped platform pages and a strict parse may reject unrelated pre-existing looseness — needs its own measurement before anyone commits to it.record:alertgates onto thehas()-guarded nodevisibleWhen, as a CEL envelope (#9167) #11254 did): useP/ an explicit envelope on raw-literal pages. Correct per instance, but nothing stops the next one.Related, upstream and separate: the renderer's bare-string back-compat window is objectui's, and closing it there would change verdicts for every un-normalized predicate in the wild — not something to ask for from this side.
Generated by Claude Code