Skip to content

spec: the evaluated-slot rule of #15430 reaches only the flow-node ledger — every other ExpressionInputSchema slot an engine evaluates (formula expression, validation / hook / sharing condition, visibleWhen…) still accepts an ast-only or blank-source envelope #15811

Description

@claude

Found while implementing #15430 (PR #15810), outside that card's dispatched surface and deliberately not fixed there. Filed by the os-dev seat, PM session session_01M59rPZZFzqhfMUPFqqZTkf; no assignee; domain:*, type and priority are triage's.

The gap

#15430's fix scopes the evaluated-slot rule to the flow-node expression ledger (FLOW_NODE_EXPRESSION_PATHS): the assignment value envelope now composes EvaluatedExpressionSchema (packages/spec/src/shared/expression.zod.ts) — source required and non-blank after trimming, one rule, one message — and the edge condition is #15807. ExpressionSchema / ExpressionInputSchema themselves stay the persistence contract (source OR ast; min(1) on source), by design.

The same seam exists on every OTHER slot that ExpressionInputSchema types and some engine evaluates. Measured consumer map on origin/main 8e500f2 (git grep of ExpressionInputSchema in packages/spec/src, non-test):

For each, { dialect: 'cel', ast: {...} } and { dialect: 'cel', source: ' ' } parse today. What happens next differs per evaluator and is the part that needs measuring before any narrowing: objectql/src/validation/rule-validator.ts documents that an AST-only rule condition answers the LOUD branch (its readsUserRoot note: "anything this reader cannot see is a predicate the evaluator could not have run either"); ADR-0078 pins rejects an AST-only or non-CEL visible loudly (kernel/public-auth-features.test.ts, ui/action.test.ts); ui/view-form-features-root.test.ts pins "an AST-only envelope is opaque at this layer and passes (documented boundary)"; metadata-core/src/form-predicate-root-policy.ts passes an opaque predicate. So some of these are already loud, some documented-opaque, and some (formula expression, the *When field predicates, sharing condition) unmeasured.

What a fix would be

Not a blanket swap of ExpressionInputSchema — that is the persistence contract and its M9.2 note. Per slot: census which engine reads it and how an ast-only / blank source envelope fails today (loud, silent, or documented-opaque); where the answer is "silent" or "faults at run time", compose the evaluated form (an EvaluatedExpressionInputSchema sibling — bare string → { dialect: 'cel', source }, envelope → EvaluatedExpressionSchema — is the one-spelling way; #15807 needs the same sibling). Each slot narrowed is an accept-set change on a published schema (Clause ②), so this is a spec-lane decision per slot family rather than one sweep.

Back-link: #15430, PR #15810 (the rule and its H1 measurement), #15807 (the edge-condition half), #15028 (adjacent: the dialect is fixed only on the bare-string arm of CronExpressionInputSchema / TemplateExpressionInputSchema).


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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