Filed by the domain:spec PM seat (session session_0174WZTU6XcFcS7g2kykC53i, seat post #6017) at 2026-09-04T02:54Z as the executor half of the maintainer's ruling A on objectstack#14149 (comment 5507504961, 「同意」 2026-09-02), whose spec half landed in PR #15113 (merge f3bbbef5). Cross-lane request into the domain:services lane; domain:* is triage's to set. Provenance: the #14149 dev's read-and-report (comment 5534552929) and the seat's contract review (PR #15113 comment 5534669909); director audit 5534406255.
What the spec now declares (on origin/mainf3bbbef58)
FlowNodeExpressionRole has 'value'; FLOW_NODE_EXPRESSION_PATHS carries { nodeType: 'assignment', path: 'assignments.*', role: 'value' }; resolveFlowNodeExpressions emits only envelope-shaped objects (isExpressionEnvelopeShaped) for a value slot — a plain string stays {token} interpolation (packages/spec/src/automation/flow-node-expression-paths.ts).AssignmentConfigSchema / AssignmentValueSchema / AssignmentExpressionValueSchema (builtin-node-config.zod.ts): an assignment value is a string (interpolation), a { dialect: 'cel', source } envelope, or any other literal; a malformed envelope is refused at the variable's path with ASSIGNMENT_VALUE_ENVELOPE_REFUSAL; the map value carries .meta({ xExpression: 'value' }), exposed to the ledger ratchet through LEDGER_DECLARED_NODE_CONFIG_SCHEMAS (schemaless-node-config.zod.ts); the ratchet config-expression-ledger.test.ts already knows ROLE_BY_MARKER.value.
The ask (executor half, packages/services/service-automation)
- Validate at
registerFlow and objectstack validate: call validateExpression('value', …) on what the ledger resolves for assignment (the service-automation / lint consumers of the ledger; the engine's severity policy throws on a malformed predicate at registerFlow, the lint pass reports a located finding — apply the same policy to the value role). - Evaluate at run time: the built-in
assignment executor (logic-nodes.ts:135 via template.ts:346-368interpolate) today writes an envelope object into the variable VERBATIM (interpolate() recurses into it as a literal object) and notify renders it as JSON (engine.ts:6901 skips every non-predicate slot). Evaluate a value-role envelope with the expression engine (the CEL engine already answers joinNonEmpty(rows.map(r, r.subject), "\n") for the ruling's example — measured on built formula dist) and assign the result; a plain string keeps today's interpolation. - Decide the legacy array form before
AssignmentConfigSchema is wired into parseNodeConfig: the schema refuses assignments: [{ variable, value }] with ASSIGNMENT_ARRAY_FORM_PRESCRIPTION while the executor still normalizes that shape (flagged by the director's audit 5534406255 — nothing parses a node config against this schema today, so no flow changes behaviour yet). Either refuse it at registerFlow (breaks any existing flow authored that way — needs an ADR-0087 conversion or a ruling) or accept it as untyped legacy (do not wire the schema for that shape). State the choice on this card's PR; a refusal is Clause ② and a ruling, not a lane's call. - Pins for all three, and the liveness / ledger discipline the family uses.
packages/spec is not this card's surface — if the contract needs to move, it goes back to the spec lane.
Consumer note: objectui's Studio form affordance for the envelope value is a separate objectui card (filed by the same seat).
Generated by Claude Code
Filed by the
domain:specPM seat (sessionsession_0174WZTU6XcFcS7g2kykC53i, seat post #6017) at 2026-09-04T02:54Z as the executor half of the maintainer's ruling A on objectstack#14149 (comment 5507504961, 「同意」 2026-09-02), whose spec half landed in PR #15113 (mergef3bbbef5). Cross-lane request into thedomain:serviceslane;domain:*is triage's to set. Provenance: the #14149 dev's read-and-report (comment 5534552929) and the seat's contract review (PR #15113 comment 5534669909); director audit 5534406255.What the spec now declares (on
origin/mainf3bbbef58)FlowNodeExpressionRolehas'value';FLOW_NODE_EXPRESSION_PATHScarries{ nodeType: 'assignment', path: 'assignments.*', role: 'value' };resolveFlowNodeExpressionsemits only envelope-shaped objects (isExpressionEnvelopeShaped) for avalueslot — a plain string stays{token}interpolation (packages/spec/src/automation/flow-node-expression-paths.ts).AssignmentConfigSchema/AssignmentValueSchema/AssignmentExpressionValueSchema(builtin-node-config.zod.ts): an assignment value is a string (interpolation), a{ dialect: 'cel', source }envelope, or any other literal; a malformed envelope is refused at the variable's path withASSIGNMENT_VALUE_ENVELOPE_REFUSAL; the map value carries.meta({ xExpression: 'value' }), exposed to the ledger ratchet throughLEDGER_DECLARED_NODE_CONFIG_SCHEMAS(schemaless-node-config.zod.ts); the ratchetconfig-expression-ledger.test.tsalready knowsROLE_BY_MARKER.value.The ask (executor half,
packages/services/service-automation)registerFlowandobjectstack validate: callvalidateExpression('value', …)on what the ledger resolves forassignment(theservice-automation/lintconsumers of the ledger; the engine's severity policy throws on a malformed predicate atregisterFlow, the lint pass reports a located finding — apply the same policy to thevaluerole).assignmentexecutor (logic-nodes.ts:135viatemplate.ts:346-368interpolate) today writes an envelope object into the variable VERBATIM (interpolate()recurses into it as a literal object) andnotifyrenders it as JSON (engine.ts:6901skips every non-predicate slot). Evaluate avalue-role envelope with the expression engine (the CEL engine already answersjoinNonEmpty(rows.map(r, r.subject), "\n")for the ruling's example — measured on built formula dist) and assign the result; a plain string keeps today's interpolation.AssignmentConfigSchemais wired intoparseNodeConfig: the schema refusesassignments: [{ variable, value }]withASSIGNMENT_ARRAY_FORM_PRESCRIPTIONwhile the executor still normalizes that shape (flagged by the director's audit 5534406255 — nothing parses a node config against this schema today, so no flow changes behaviour yet). Either refuse it atregisterFlow(breaks any existing flow authored that way — needs an ADR-0087 conversion or a ruling) or accept it as untyped legacy (do not wire the schema for that shape). State the choice on this card's PR; a refusal is Clause ② and a ruling, not a lane's call.packages/specis not this card's surface — if the contract needs to move, it goes back to the spec lane.Consumer note: objectui's Studio form affordance for the envelope value is a separate objectui card (filed by the same seat).
Generated by Claude Code