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
{{ message }}
Repository navigation
Commit 059b422
Browse filesBrowse the repository at this point in the historyBrowse files
feat(spec)!: hold a predicate to what the engine can run; declare its fault semantics
`PredicateSchema` / `PredicateInputSchema` composed the PERSISTENCE contract
(`source` OR `ast`) while a predicate exists to be evaluated and the CEL engine
reads `source` alone. An `ast`-only envelope and a `source` blank after trimming
parsed, registered, passed `objectstack validate`, then faulted or
short-circuited at evaluation time. They now compose the evaluated rule, and the
field-rule triad on `FieldSchema` binds to them.
The predicate contract's docblock states the fault semantics the consumers are
held to: a faulting field-rule predicate refuses the SUBMIT naming the field and
the rule; visibility stays fail-open at RENDER; a blank or faulting gate
predicate is diagnosed, never a silent true. The evaluation helper's fallback
stays freely specifiable.
Claude-Session: https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh
Co-authored-by: Claude <noreply@anthropic.com>
* state live as the record changes (UX), and the server enforces
1640
1640
* `requiredWhen` and ignores writes to a field whose `readonlyWhen` is TRUE
1641
1641
* (so the rule can't be bypassed). e.g. `P\`record.status == 'paid'\``.
1642
+
*
1643
+
* These three are THE field-rule triad, and they are EVALUATED slots: they
1644
+
* compose `PredicateInputSchema` (ADR-0136 D1), not the persistence
1645
+
* `ExpressionInputSchema`, so an envelope carrying only an `ast` and a
1646
+
* `source` that is blank after trimming are refused HERE, at authoring,
1647
+
* instead of faulting at evaluation. A blank predicate is the triad's third
1648
+
* state — not "no rule" and not "engine fault" — and refusing it at
1649
+
* authoring is what keeps those two apart at runtime.
1650
+
*
1651
+
* What happens when one of these DOES fault (a stored rule, a column renamed
1652
+
* out from under it) is contract, not renderer choice: the submit is refused
1653
+
* loudly and names the field and the rule (ADR-0136 D2), while visibility
1654
+
* stays fail-OPEN at render so a rule that could not run never hides a
1655
+
* control and writes `null` over a column the user never saw (D3). The two
1656
+
* directions are a pair; neither is safe alone.
1642
1657
*/
1643
-
visibleWhen: ExpressionInputSchema.optional().describe("Predicate (CEL) — field is shown only when TRUE (else hidden). e.g. P`record.type == 'invoice'`"),
1644
-
readonlyWhen: ExpressionInputSchema.optional().describe("Predicate (CEL) — field is read-only when TRUE. e.g. P`record.status == 'paid'`"),
1645
-
requiredWhen: ExpressionInputSchema.optional().describe("Predicate (CEL) — field is required when TRUE. A TRANSITION GATE, not an invariant: the write is refused only when the merged record violates the requirement AND the pre-write record complied — so the write that flips the predicate TRUE, an INSERT born inside the gate, and a write that clears the cell are all refused, while a row that was already missing the value keeps passing unrelated edits and state moves that stay inside the gate (ADR-0113 non-regression: adding the rule to a deployed object never bricks existing rows). Need an invariant every write must satisfy instead ('X may never exceed Y') — declare a `validations[]` `script` rule, which re-checks the merged record with no exemption. Enforced by `evaluateValidationRules`. The only slot; the `conditionalRequired` alias was removed in protocol 17."),
1658
+
visibleWhen: PredicateInputSchema.optional().describe("Predicate (CEL) — field is shown only when TRUE (else hidden). Needs a non-blank `source`: an evaluated slot is held to what the engine can run (ADR-0136 D1). A fault at RENDER is fail-open (the field shows) and refuses the SUBMIT, naming the field and this rule (D2/D3). e.g. P`record.type == 'invoice'`"),
1659
+
readonlyWhen: PredicateInputSchema.optional().describe("Predicate (CEL) — field is read-only when TRUE. Needs a non-blank `source` (ADR-0136 D1); a fault refuses the submit and names the field and this rule (D2). e.g. P`record.status == 'paid'`"),
1660
+
requiredWhen: PredicateInputSchema.optional().describe("Predicate (CEL) — field is required when TRUE. Needs a non-blank `source` (ADR-0136 D1); a fault refuses the submit and names the field and this rule (D2). A TRANSITION GATE, not an invariant: the write is refused only when the merged record violates the requirement AND the pre-write record complied — so the write that flips the predicate TRUE, an INSERT born inside the gate, and a write that clears the cell are all refused, while a row that was already missing the value keeps passing unrelated edits and state moves that stay inside the gate (ADR-0113 non-regression: adding the rule to a deployed object never bricks existing rows). Need an invariant every write must satisfy instead ('X may never exceed Y') — declare a `validations[]` `script` rule, which re-checks the merged record with no exemption. Enforced by `evaluateValidationRules`. The only slot; the `conditionalRequired` alias was removed in protocol 17."),
1646
1661
1647
1662
/**
1648
1663
* [REMOVED in protocol 17 — #3855] The deprecated alias of `requiredWhen`.
0 commit comments