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(lint): read a flow's node lists through recordsOf, not an inline cast (#16765)
`validateStackExpressions` threw on a non-record entry of a flow's `nodes`
list: `Array.isArray` proves the LIST, never its MEMBERS, and an empty item
in a YAML `nodes:` list deserialises to `null`. The next statement read
`.type` off it, so the linter crashed instead of reporting a finding.
Both inline casts now read through `recordsOf` — the one home of this
coercion, so no copy is added for `collection-coercion-single-copy.test.ts`
to count. The coerced array is also what is handed to `collectFlowGraphs`:
that function declares already-parsed `FlowNodeParsed[]` and forwards members
untouched, so passing the raw flow was calling it out of contract, and
coercing only the local variable relocated the crash into `packages/spec`
rather than removing it. Measured both ways.
The pin is a new addressing mode in `non-record-object-entry.test.ts` rather
than a local test. The sweep drove collections only, which is why this class
was closed three times without reaching these two lines; it now addresses a
flow's own node list and a nested region's sub-graph. Both arms found more of
the same class on their first run, recorded in `RESIDUAL_THROWS` and filed as
#16751 and #16752.
Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU
Co-authored-by: Claude <noreply@anthropic.com>
`validateStackExpressions` no longer throws on a non-record entry of a flow's `nodes` list.
6
+
7
+
An empty item in a YAML `nodes:` list deserialises to `null`, so this is an authorable shape — the same one #15552, #15636 and #15742 closed for stack collections and for `objects[].fields`. Here it crashed the linter instead of producing a finding: `flow.nodes: [null, ...]` threw `Cannot read properties of null (reading 'type')`, which presents to an author as a broken tool rather than as a problem with their metadata.
8
+
9
+
Both of the file's inline casts now read through `recordsOf`, the one home of this coercion, instead of asserting that `Array.isArray` proves anything about a list's MEMBERS:
10
+
11
+
- The flow walk reads `flow.nodes` through `recordsOf`, and — the half that actually removes the crash — hands that coerced array to `collectFlowGraphs` rather than the raw flow. `collectFlowGraphs` declares its input as already-parsed `FlowNodeParsed[]` and is transparent about members, so passing raw authored metadata was calling it out of contract; coercing only the local variable relocated the throw into `@objectstack/spec` instead of ending it. The producer's contract is unchanged, deliberately: widening it to tolerate malformed members is the wrong direction.
12
+
- The per-graph walk reads `graph.nodes` through `recordsOf` in place of an `as unknown as` double cast. A nested region's node list is only `Array.isArray`-checked before it becomes a graph, so that list carries the producer's word about its members and not a check.
13
+
14
+
A non-record member is dropped whole and in silence, exactly as the file's sibling field readers already did; a flow standing beside the junk entry is still judged, and a `nodes` list holding a plain string still reports exactly what it reported before.
0 commit comments