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(spec): let a schemaless node own an expression-ledger entry (objectstack-ai#4439)
`FLOW_NODE_EXPRESSION_PATHS` (objectstack-ai#4027) tells `registerFlow` and `objectstack
validate` which config keys hold expressions and in which dialect. Its
ratchet derives what it expects from descriptor `configSchema` markers and
fails both ways — an undeclared marker, and a ledger entry nothing declares.
`decision` / `script` / `subflow` publish no descriptor `configSchema` on
purpose (a partial one would drop the editors their hand-written Studio
forms need — the objectstack-ai#4210 incident). Those two rules compose into a hole: an
expression slot on a schemaless node is unreachable by the ratchet, and the
reverse direction then refuses to let it be entered by hand either.
`decision.conditions[].expression` sat in that hole. Its own schema calls it
a bare CEL predicate and its own comment names `{…}` as the objectstack-ai#1491 trap, and
no validator walked it — so a braced predicate passed tsc, passed validate,
passed registration. objectstack-ai#4414 made that loud at run time; this makes it a build
failure, which is the delay objectstack-ai#4027 exists to remove.
The ratchet now reads both declaration channels. Spec hands the schemaless
one over as JSON Schema (`getSchemalessNodeConfigJsonSchemas()`, memoized,
input mode — the shape a descriptor's `configSchema` already is), so both are
walked by the same function: no second notion of "a declared expression
property", and no `zod` dependency added to service-automation. Each channel
is separately asserted non-empty so a broken derivation cannot hide behind
the other's results.
Swept the rest of the class and recorded the result in the ledger header:
`script.template` is a template id, not a body; `script.inputs` /
`script.variables` / `subflow.input` interpolate `{token}` like essentially
every node config string and are covered generically. The decision predicate
is the only genuinely declared expression slot there.
Docs: the flows guide taught the wrong dialect for decision predicates in
three places, plus a warning that inverted after objectstack-ai#4414 — and FlowNodeSchema's
own `@example` did the same. Corrected to bare CEL with the history stated,
so an author whose build now fails knows what changed. The dialect table goes
from three dialects to two: predicates never take braces, values always do.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q8as8yR67v41xEdomiTba9
* docs(automation): state the stored-flow impact, and pin the shape Studio emits
Two gaps found while verifying objectstack-ai#4439.
The objectstack-ai#4414 changeset described the mechanism but not who it moves. `objectui`'s
FlowEdgeInspector has always written `isDefault: true` when an out-edge is
bound to a decision's default/else branch — into a key with zero readers, so
that edge ran unconditionally alongside whichever branch matched. Enforcement
therefore changes STORED flows, and mostly Studio's own: they now take exactly
one branch. That is the fix, but it lands on existing data, which a reader of
the release notes needs to know before upgrading.
The same inspector also copies each branch's expression and label onto the
edge it wires, so Studio emits the very double declaration the authoring guide
told hand-writers to avoid. It routes correctly — the designer keeps the two
sides in sync by construction — so the guide now says that plainly instead of
forbidding a shape our own tool produces: redundant, not wrong, and dangerous
only when the two disagree, which is the whole of objectstack-ai#4414.
Pins that exact emitted shape as a regression test, both directions.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q8as8yR67v41xEdomiTba9
---------
Co-authored-by: Claude <noreply@anthropic.com>
0 commit comments