Skip to content

Four *When doc examples are authored in shapes that carry no layer, so the new #11407 gate must skip them — a docs-only re-authoring would put them under the gate #11651

Description

@os-steve

Filed unassigned by the os-dev seat while implementing #11407 (session session_015ahemw8RcTgqtxrj15PEZx). Recording only — not claimed, not queued, not graded.

What the new gate measured

#11407 added a third scan surface to packages/lint/scripts/check-doc-formula-expressions.mjs: field-level visibleWhen / readonlyWhen / requiredWhen in the docs corpus, judged only where the enclosing structure identifies the layer. Its run on origin/main accounts for every *When occurrence in the corpus:

count
text-level *When: occurrences in TS/TSX fences23
— the ADR quoting field.zod.ts's schema (visibleWhen: ExpressionInputSchema.optional()), not predicates3
admitted and judged (layer determinable)13
skipped and listed (layer not determinable)7

The gate prints all 7 skips with a reason on every run, green ones included. This card is about the subset of those 7 that are skipped for an authoring reason rather than a real one.

The split inside the 7

Three are correctly skipped forever — they genuinely are other layers, and judging them by the field-level rule would be a false red:

  • content/docs/automation/flows.mdx:320 — a flow-screen field. visibleWhen: 'createOpportunity == true' is a bare reference, and correct: a screen flattens its own field names to top level. The record-scoped verdict would report it as the Formula guardrail: cel-js arithmetic silently returns null (double × int + bare identifiers) #1928 defect.
  • content/docs/ui/pages.mdx:165 — a page component. 'sales_manager' in current_user.positions is correct there; current_user is unbound at the field level, so the field-level rule would false-red it.
  • content/docs/ui/views.mdx:415 — a form-view field in a fields:array, the shape form views, flow screens and action params all share.

Four are skipped only because of how the fragment is written, and a docs-only edit would move them under the gate:

  • content/docs/data-modeling/formulas.mdx:279{ name: 'rating', type: 'select', visibleWhen: P + "record.status == 'qualified'" + }. It sits under the heading "Predicate (visibility, read-only, conditional required)", directly beside a Field.text({ … }) example that is admitted. It is a field example; it just carries no fields: map and no Field.* factory, so nothing in the tree says so.
  • content/docs/protocol/objectui/layout-dsl.mdx:821, :824, :863 — bare visibleWhen: "…" lines at statement position. TypeScript reads those as labelled statements, not property assignments, so they are invisible to an AST walk entirely. (The os:check fence type-checks a visibleWhen CEL string as string — any predicate text passes, including calls to functions that exist nowhere #11407 added a text-level tripwire so they are listed rather than dropped silently — before it, they produced zero sites and printed nothing.) Their layer is stated only in a prose comment above each line: // e.g. on a PageComponent, // e.g. on a FormSection / FormField.

Why this is a finding and not a defect

Every one of the four examples is correct as documentation — a reader gets the right answer. Nothing is broken. The observation is only that the gate cannot reach them, so a future edit that broke one would not be caught, and the corpus's covered fraction is 13/20 rather than 17/20.

Two ways it could go, neither decided here

  1. Re-author the four fragments into shapes the existing discriminator already admits (wrap in the Field.* factory, or in a fields: { … } map). Docs-only, no gate change, and it would make the layer explicit to human readers too — which is arguably the better outcome for layout-dsl.mdx, where the layer currently lives in a comment.
  2. Widen the discriminator.The os:check fence type-checks a visibleWhen CEL string as string — any predicate text passes, including calls to functions that exist nowhere #11407's dispatch was explicit that the selector must not be widened until something matches, and each widening has to answer "which layer" without guessing. A { name, type } object literal at fragment top level is probably a field def — "probably" is exactly what the surface refuses.

Option 1 needs no new judgement and is the cheaper half; option 2 is a real design question. Recording both rather than picking.

Related, and distinct

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions