Skip to content

Commit 92aefd3

Browse files
committed
docs(spec): the slot judges' door lists name the parse and why it is not one of them
The value-slot and text-slot judges run at registerFlow, at the validateStackExpressions rule (objectstack validate, objectstack compile, the runtime publish gate of an active save) and at every run. FlowSchema.parse runs neither, but for an end node's message through EndConfigSchema. Both module docs now say so, with the two measured reasons: a parse refusal would replace registerFlow's and the validate rule's located refusal with the parse's issue list, and would fail the row in the stored pass, whose verdict a refusal after the parse leaves unmoved. Claude-Session: https://claude.ai/code/session_01YRp5z4kK6vgYkrdH97ztsy Co-authored-by: Claude <noreply@anthropic.com>
1 parent d5b7045 commit 92aefd3

3 files changed

Lines changed: 67 additions & 4 deletions

File tree

‎packages/spec/src/automation/flow-text-slot-template.ts‎

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,31 @@
2626
* dialect: a single-brace `{…}` token is refused, and the refusal names the
2727
* `{{ }}` spelling of each token or, for a token no hole can spell, where to
2828
* compute it. The contracts compose it (`NotifyConfigSchema`,
29-
* `ScreenConfigSchema`, `EndConfigSchema`), and `AutomationEngine.registerFlow`
30-
* and `objectstack validate` call it on every slot
29+
* `HttpConfigSchema`, `ScreenConfigSchema`, `EndConfigSchema`), and
30+
* `AutomationEngine.registerFlow` and the `validateStackExpressions` rule
31+
* (`objectstack validate`, `objectstack compile`, the runtime publish gate of
32+
* an `active` metadata save) call it on every slot
3133
* {@link flowNodeTextSlotSources} finds — ⛔ never a second reading of the
32-
* dialect anywhere. Those two doors then compile each slot's `{{ }}` holes
34+
* dialect anywhere. Those doors then compile each slot's `{{ }}` holes
3335
* (`validateExpression('template', …)`), which the spec cannot do itself: the
3436
* template engine is a runtime.
3537
*
38+
* ## Not at `FlowSchema.parse`, but for an `end` node (#23320)
39+
*
40+
* The flow parse runs this judge on one slot: a top-level `end` node's
41+
* `message`, because `end` has no executor, so its contract, `EndConfigSchema`,
42+
* has no door but the parse (`parseEndNodeConfig` in `flow.zod.ts`). On every
43+
* other text slot the parse leaves the token to the doors above, as it leaves
44+
* a value-slot token to the value-slot judge's: `flowNodeConfigRefusals`'
45+
* value arm stands aside at the paths this judge refuses
46+
* (`slotJudgeRefusedPaths`, #23260), and the parse calls no slot judge of its
47+
* own. The reasons are measured once, for both judges, in
48+
* `flow-value-slot-template.ts` ("The doors that run it"): a refusal at the
49+
* parse would replace `registerFlow`'s located line and `objectstack
50+
* validate`'s `expression-invalid` finding with the parse's issue list, and
51+
* would fail the row in the stored pass (`os migrate meta --stored`), whose
52+
* verdict a refusal after the parse leaves unmoved.
53+
*
3654
* ## Refused, not converted (ADR-0087 D2)
3755
*
3856
* D2 lets a conversion rewrite only what maps LOSSLESSLY. Every token spelling

‎packages/spec/src/automation/flow-value-slot-template.ts‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,48 @@
2828
* `AutomationEngine.registerFlow`, `objectstack validate` and the executors
2929
* call it — ⛔ never a second reading of the dialect anywhere.
3030
*
31+
* ## The doors that run it — and why `FlowSchema.parse` is not one (#23320)
32+
*
33+
* The judge runs at every door that reports a flow's refusals per slot:
34+
* `AutomationEngine.registerFlow`, after its parse; the `validateStackExpressions`
35+
* rule (`expression-invalid` at `error`), which `objectstack validate`,
36+
* `objectstack compile` and the runtime publish gate of an `active` metadata
37+
* save run; and every run, through the node contracts that compose
38+
* `FlowValueSlotSchema`. The flow parse does NOT run it: `FlowSchema.parse`,
39+
* and so `defineStack`, `ObjectStackDefinitionSchema`, the `flow` metadata
40+
* type's schema and an artifact's parse, accept a value-slot token. The parse
41+
* parses the node contracts too (`flowNodeConfigRefusals`' value arm), and
42+
* that arm stands aside at exactly the paths this judge refuses
43+
* (`slotJudgeRefusedPaths`, #23260), so the parse leaves the token to the
44+
* doors above, on purpose. Two measured reasons, taken by running this judge
45+
* and the text-slot judge in `FlowSchema`'s refinement and reverting it:
46+
*
47+
* - **The located refusal.** `registerFlow` parses first, and a parse
48+
* refusal reaches its caller as the parse's own issue list. A token
49+
* refused at the parse would replace `registerFlow`'s located line — the
50+
* node's id and type, the slot's label, the config path, the remedy and the
51+
* `source:` — at registration and in the boot's log, and `objectstack
52+
* validate` and `objectstack compile`, which parse the stack before their
53+
* rules run, would stop at the parse before the `expression-invalid`
54+
* finding. Every pin of either located refusal went red.
55+
* - **The stored pass.** The parse is also the stored-flow canonicalization
56+
* (`AutomationEngine.canonicalizeStoredFlow`) that `os migrate meta
57+
* --stored` runs on each flow row, and a parse refusal reports the row
58+
* `failed`, which flips `storedMigrationClean`: a non-zero exit that no run
59+
* of the command can clear, because no conversion rewrites the token (the
60+
* next section). A refusal after the parse is listed instead, as a TODO
61+
* under the step-18 D3 entry `flow-value-slot-template-dialect-refused`
62+
* (`conversions/refused-shapes.ts`), and the verdict does not move. The
63+
* pin that a braced filter operand parses and is listed went red.
64+
*
65+
* What this leaves: a stack built by `defineStack` and handed to none of those
66+
* doors — `os dev` and `os serve` load it and boot — reaches `registerFlow` at
67+
* boot, which refuses the flow with its located line and does not register it.
68+
* The judge can join the parse once `registerFlow` reports a parse refusal as
69+
* located as its own and the stored pass lists a slot the parse refuses
70+
* rather than failing its row; the contract arm's exemption then keeps each
71+
* token reported once.
72+
*
3173
* ## Refused, not converted (ADR-0087 D2)
3274
*
3375
* D2 lets a conversion rewrite only what maps LOSSLESSLY. Every token spelling

‎packages/spec/src/automation/flow.zod.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1503,7 +1503,10 @@ export const FlowSchema = lazySchema(() => strictObject(
15031503
// value carrying a `{token}` included since #23260 (`get_record`
15041504
// `limit: '{x}'`), never a region slot, a ledger predicate or value slot,
15051505
// a value or text slot whose own judge refuses its token, or `http`'s
1506-
// run-resolved `signingSecret`. A KEY its
1506+
// run-resolved `signingSecret`. (#23320) This parse runs neither slot
1507+
// judge itself, but for an `end` node's `message` through its contract:
1508+
// the doors that do, and the measured reasons the parse is not one, are
1509+
// in `flow-value-slot-template.ts` ("The doors that run it"). A KEY its
15071510
// contract does not declare is refused too (#21982), for every builtin
15081511
// `builtinNodeConfigKeysJudged` names (every one, `try_catch` included
15091512
// since #22343), a body-less legacy `loop` included. The one plugin node contract the spec declares, `approval`

0 commit comments

Comments
 (0)