Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions .changeset/flow-edge-condition-evaluated-slot.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ edges:
refused by `evaluateCondition` with the same sentence, instead of answering a
silent `false`. An `ast` BESIDE a string `source` is still admitted
everywhere. The whitespace-only STRING ruling on `config.condition` (#15662:
consistent `false` on both sides) is untouched.
consistent `false` on both sides) is untouched by this change, but it does not
survive the release that carries it: two sibling notes in that release refuse
the value, at `registerFlow` (#17322, `@objectstack/service-automation`) and
at `objectstack validate` (#17495, `@objectstack/lint`).
- **Three doors agree, through the spec.** `registerFlow` refuses the flow at
`FlowSchema.parse` (edge) or at its structural pass (`config.condition`);
`objectstack validate` refuses it at its `ObjectStackDefinitionSchema` parse
Expand All @@ -80,7 +83,7 @@ the refusal itself carries the prescription.

**A flow ALREADY STORED in `sys_metadata` stops running entirely — the whole
flow, not just the edge.** The paragraph above is the author's remedy, at
`objectstack validate` / `POST /flows`; a stored row has no author in front of
`objectstack validate` / `POST /api/v1/automation`; a stored row has no author in front of
it. Stored flows are deliberately NOT canonicalized by
`applyConversionsToStoredItem` (`spec/src/conversions/stored.ts`, and the same
skip in `metadata/src/loaders/database-loader.ts`'s `rowToData`) — flow-node
Expand All @@ -104,5 +107,9 @@ door, `objectstack validate`, which locates the same edge at
judgment for a consumer replaying the chain.

Not touched here: `start.config.condition` has no Zod schema to narrow (the
start node's `config` is an open record); its producer-side gate is the
structural refusal above, which this change tightens but does not type.
start node's `config` is an open record). Its producer-side gate is the
structural pass at `registerFlow` and `objectstack validate`: the shape refusal
above, which this change tightens but does not type, and after it a blank-source
check that runs this change's `EvaluatedExpressionInputSchema` on the
condition's `source` (added by #17322 at `registerFlow` and by #17495 at
`objectstack validate`).
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const entry: SemanticMigration = {
+ 'than deriving a second one; it is the same decision reaching the second slot, which is why '
+ 'it is named here instead of in an entry of its own. Reachable wherever a flow is authored '
+ 'or stored: defineStack({ flows }) sources, an exported stack passed to objectstack validate, '
+ 'a POST /flows body, and a flow row already sitting in sys_metadata',
+ 'a POST /api/v1/automation body, and a flow row already sitting in sys_metadata',
replacement:
'a non-blank `source` — `{ dialect: \'cel\', source: \'record.amount > 10\' }`, or the bare '
+ 'string `\'record.amount > 10\'` — if the edge was meant to branch; or REMOVE the '
Expand Down Expand Up @@ -60,7 +60,7 @@ export const entry: SemanticMigration = {
acceptanceCriteria:
'Grep every authored structural condition — BOTH `edges[].condition` and a node\'s '
+ '`config.condition` (a `decision` node\'s predicate, and on a `start` node the trigger '
+ 'gate) — in `defineStack({ flows })` sources, exported stacks and `POST /flows` bodies, and '
+ 'gate) — in `defineStack({ flows })` sources, exported stacks and `POST /api/v1/automation` bodies, and '
+ 'every flow row in `sys_metadata`, for an envelope with no `source` key and for a `source` '
+ '(or bare string) that is empty after trimming. ⚠️ Sweeping only the edge key leaves the '
+ 'node key unswept, and the node key is the one with no schema in front of it. For each '
Expand Down
4 changes: 2 additions & 2 deletions packages/spec/src/migrations/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9065,7 +9065,7 @@ const step18: MigrationStep = {
+ 'than deriving a second one; it is the same decision reaching the second slot, which is why '
+ 'it is named here instead of in an entry of its own. Reachable wherever a flow is authored '
+ 'or stored: defineStack({ flows }) sources, an exported stack passed to objectstack validate, '
+ 'a POST /flows body, and a flow row already sitting in sys_metadata',
+ 'a POST /api/v1/automation body, and a flow row already sitting in sys_metadata',
replacement:
'a non-blank `source` — `{ dialect: \'cel\', source: \'record.amount > 10\' }`, or the bare '
+ 'string `\'record.amount > 10\'` — if the edge was meant to branch; or REMOVE the '
Expand Down Expand Up @@ -9107,7 +9107,7 @@ const step18: MigrationStep = {
acceptanceCriteria:
'Grep every authored structural condition — BOTH `edges[].condition` and a node\'s '
+ '`config.condition` (a `decision` node\'s predicate, and on a `start` node the trigger '
+ 'gate) — in `defineStack({ flows })` sources, exported stacks and `POST /flows` bodies, and '
+ 'gate) — in `defineStack({ flows })` sources, exported stacks and `POST /api/v1/automation` bodies, and '
+ 'every flow row in `sys_metadata`, for an envelope with no `source` key and for a `source` '
+ '(or bare string) that is empty after trimming. ⚠️ Sweeping only the edge key leaves the '
+ 'node key unswept, and the node key is the one with no schema in front of it. For each '
Expand Down
Loading