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
fix(service-automation): evaluation refuses a malformed condition shape with registration's own refusal (#16438)
`evaluateCondition` derived `exprStr` with a `typeof` guard covering only the
bare-string arm, so an envelope whose `source` was present and not a string
became that value and `.trim()` threw a bare `TypeError` naming no flow, no
node and no expression. Two sibling arms shared the same unguarded read: a
value that is neither text nor envelope-shaped read as an EMPTY condition and
answered a silent `false` — on the same key a start node's trigger gate is read
from — and a malformed envelope under a non-predicate dialect answered `false`
one statement earlier still, at the dialect check.
#15662 closed this reject set at the producer; the evaluator was left
disagreeing with it in a different vocabulary. Per the maintainer's ruling
(decision batch #57, option A) evaluation now calls `structuralConditionRefusal`
— the SAME constructor `registerFlow` calls, not a second hand-written envelope
that could drift — as the method's first statement, above the dialect check so
all three arms are covered.
Controls pinned alongside: bare CEL text and both envelope spellings still
evaluate; an `ast`-only envelope still answers `false` (that population is
#15430/#15807's); a well-formed `cron` envelope still answers `false` rather
than being refused; absent/`null`/empty/whitespace conditions are still "not
authored"; and a malformed STRING still earns the brace trap or the §1c CEL
fault, never the shape refusal.
Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
Co-authored-by: Claude <noreply@anthropic.com>
`evaluateCondition` now refuses a malformed condition shape with the same `STRUCTURAL_CONDITION_SHAPE_REFUSAL` registration already raises — evaluation and registration share one refusal, so a shape that slipped past registration can never surface as a raw `TypeError` or as a silent `false`.
6
+
7
+
#15662 closed the reject set at the producer: `registerFlow` refuses a structural condition (`config.condition` on a node, `edge.condition`) that is neither CEL text nor an expression envelope. The evaluator was left saying the opposite thing in a different vocabulary, and that half matters because `evaluateCondition` is a **public method on an exported class** — a plugin reaches it directly regardless of what `registerFlow` admits, and a flow stored before that gate landed replays through it.
8
+
9
+
The unguarded read had three arms, all of them now refused by the shared `structuralConditionRefusal` — the same call `registerFlow` makes, not a second hand-written envelope that could drift from it:
10
+
11
+
- an envelope whose `source` is present and **not a string** (`{ source: 1 }`, `{ dialect: 'cel', source: 1 }`) reached `.trim()` and threw `TypeError: exprStr.trim is not a function`, naming no flow, no node and no expression;
12
+
- a value that is neither text nor envelope-shaped (`42`, `true`, `['a']`, `{}`, `{ dialect: 'cel' }`) was read as an **empty condition** and answered `false` — the "an unauthored branch must not open" rule applied to a value that was very much authored, on the same key a start node's **trigger gate** is read from;
13
+
- a malformed envelope carrying a non-predicate dialect (`{ dialect: 'cron', source: 1 }`) answered `false` one statement earlier still, at the dialect check, never reaching the source derivation at all.
14
+
15
+
**What still evaluates is unchanged, and is pinned as controls.** Bare CEL text and both envelope spellings evaluate exactly as before; an `ast`-only envelope still answers `false`; a well-formed non-predicate dialect (`{ dialect: 'cron', source: '0 0 * * *' }`) still answers `false` rather than being refused; absent, `null`, empty and whitespace-only conditions are still "not authored", not malformed. A malformed **string** still earns its own verdict — the brace trap or the ADR-0032 §1c CEL fault — never the shape refusal.
16
+
17
+
An app whose stored flow carries one of the refused shapes in a node or edge `condition` now fails that run loudly with a message carrying the rule, instead of skipping a branch in silence or faulting unattributed; the fix is to write the condition as bare CEL text (`record.rating >= 4`) or as an expression envelope.
0 commit comments