refactor: remove placeholder join step
This commit is contained in:
+3
-2
@@ -895,8 +895,9 @@ _Avoid_: Job, invocation
|
|||||||
- Missing reducers mean replace only within a single serial lineage. At a
|
- Missing reducers mean replace only within a single serial lineage. At a
|
||||||
**Barrier**, multiple writes to the same path without a reducer are conflicts,
|
**Barrier**, multiple writes to the same path without a reducer are conflicts,
|
||||||
not last-writer-wins replacements.
|
not last-writer-wins replacements.
|
||||||
- Current `JoinNode` should not silently become a **Barrier**. It may be
|
- The pass-through `JoinNode` was removed. Future **Gather** behavior starts
|
||||||
repurposed later only through an explicit design pass.
|
from an explicit barrier contract rather than inheriting placeholder
|
||||||
|
semantics.
|
||||||
- The first **Scheduler Foundation** implementation should create ready/block/wake
|
- The first **Scheduler Foundation** implementation should create ready/block/wake
|
||||||
seams only. **Lineage Isolation** and **Barrier** merge behavior are future
|
seams only. **Lineage Isolation** and **Barrier** merge behavior are future
|
||||||
concurrent semantics, not first-pass behavior.
|
concurrent semantics, not first-pass behavior.
|
||||||
|
|||||||
@@ -1312,17 +1312,6 @@
|
|||||||
],
|
],
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
"DraftJoinStep": {
|
|
||||||
"additionalProperties": false,
|
|
||||||
"description": "Draft step that emits the current core join node.",
|
|
||||||
"properties": {
|
|
||||||
"join": {
|
|
||||||
"additionalProperties": true,
|
|
||||||
"type": "object"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"DraftMatchCase": {
|
"DraftMatchCase": {
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"description": "One ordered equality case in a draft match decision.",
|
"description": "One ordered equality case in a draft match decision.",
|
||||||
@@ -6209,9 +6198,6 @@
|
|||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/DraftInterruptStep"
|
"$ref": "#/components/schemas/DraftInterruptStep"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"$ref": "#/components/schemas/DraftJoinStep"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/DraftEndStep"
|
"$ref": "#/components/schemas/DraftEndStep"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -14,9 +14,10 @@ without making multiple matching edges silently mean broadcast.
|
|||||||
|
|
||||||
The scheduler, ready queue, blocked frames, lineage-local state views, and
|
The scheduler, ready queue, blocked frames, lineage-local state views, and
|
||||||
reducer-aware barrier commits already support concurrent foreach and native
|
reducer-aware barrier commits already support concurrent foreach and native
|
||||||
subgraphs. They do not yet define general graph-level fork/gather. The existing
|
subgraphs. They do not yet define general graph-level fork/gather. A former
|
||||||
`JoinNode` is only a day-one marker: it immediately emits `done` and neither
|
`JoinNode` day-one marker immediately emitted `done`; it was removed after a
|
||||||
waits nor merges. Renaming it would falsely preserve semantics it never had.
|
census found no real persisted workflows using it. `GatherNode` therefore does
|
||||||
|
not inherit placeholder semantics or a compatibility burden.
|
||||||
|
|
||||||
A cross-system semantics review supported keeping `NodeResult.output` separate
|
A cross-system semantics review supported keeping `NodeResult.output` separate
|
||||||
from its named domain `outcome`, keeping operational failure outside that
|
from its named domain `outcome`, keeping operational failure outside that
|
||||||
@@ -135,11 +136,10 @@ feature would require an operational signal to the owning foreach and an
|
|||||||
explicit policy for already admitted concurrent items. No `BreakNode` is added
|
explicit policy for already admitted concurrent items. No `BreakNode` is added
|
||||||
without that use case and policy.
|
without that use case and policy.
|
||||||
|
|
||||||
The current `JoinNode` will not be silently upgraded. Before implementation we
|
The placeholder `JoinNode` was removed rather than silently upgraded. The
|
||||||
will verify whether real persisted artifacts use it. With no real compatibility
|
repository has no real persisted artifacts outside tests, so `GatherNode` can
|
||||||
obligation, remove it and introduce `GatherNode` cleanly. If persisted callers
|
be introduced with its actual rendezvous and merge contract and no legacy wire
|
||||||
exist, define an explicit migration rather than assigning barrier semantics to
|
alias.
|
||||||
old `join` payloads.
|
|
||||||
|
|
||||||
## Considered Options
|
## Considered Options
|
||||||
|
|
||||||
@@ -169,8 +169,8 @@ ordinary back-edge to the owning foreach already expresses item return in the
|
|||||||
canonical graph. Break and race semantics remain separate future policies
|
canonical graph. Break and race semantics remain separate future policies
|
||||||
rather than additional meanings assigned to ordinary outcomes.
|
rather than additional meanings assigned to ordinary outcomes.
|
||||||
|
|
||||||
**Reuse or rename `JoinNode`.** Rejected as the default because the existing
|
**Reuse or rename the placeholder `JoinNode`.** Rejected and removed because
|
||||||
node is a pass-through marker with no barrier contract.
|
the node was a pass-through marker with no barrier contract.
|
||||||
|
|
||||||
## Consequences
|
## Consequences
|
||||||
|
|
||||||
@@ -206,7 +206,6 @@ node is a pass-through marker with no barrier contract.
|
|||||||
continuation frame in each topology shape.
|
continuation frame in each topology shape.
|
||||||
- The trace representation for waiting and merging without excessive internal
|
- The trace representation for waiting and merging without excessive internal
|
||||||
scheduler noise.
|
scheduler noise.
|
||||||
- Whether any real persisted artifact requires migration from `JoinNode`.
|
|
||||||
|
|
||||||
This ADR extends the lineage and barrier direction established by
|
This ADR extends the lineage and barrier direction established by
|
||||||
[ADR-0002](0002-concurrent-foreach-policy-and-barrier-commits.md). It remains
|
[ADR-0002](0002-concurrent-foreach-policy-and-barrier-commits.md). It remains
|
||||||
|
|||||||
+17
-15
@@ -40,23 +40,12 @@ author -> validate -> save artifact -> deploy -> run -> inspect or resume
|
|||||||
|
|
||||||
## Active runtime sequence
|
## Active runtime sequence
|
||||||
|
|
||||||
The next three slices build on the foreach control-region, scheduler, lineage,
|
The next three slices build on the foreach control-region, structured-context,
|
||||||
and barrier foundations in this order.
|
scheduler, lineage, and barrier foundations in this order.
|
||||||
|
|
||||||
### 1. Review and merge structured runtime context
|
### 1. Add a persisted run step budget
|
||||||
|
|
||||||
The implementation plan is ready and its feature branch is under review:
|
Implement the proposed run-wide limit:
|
||||||
|
|
||||||
- [`structured runtime context design`](superpowers/specs/2026-09-04-structured-runtime-context-design.md)
|
|
||||||
- [`structured runtime context implementation plan`](historical/superpowers/plans/2026-09-04-structured-runtime-context.md)
|
|
||||||
|
|
||||||
This slice gives runtime code, expressions, validation, and authoring references
|
|
||||||
one model for run data and same-scope foreach activations. Subgraphs continue to
|
|
||||||
cross an explicit input boundary rather than inheriting a parent's context.
|
|
||||||
|
|
||||||
### 2. Add a persisted run step budget
|
|
||||||
|
|
||||||
After structured context is stable, implement the proposed run-wide limit:
|
|
||||||
|
|
||||||
- [`run step budget design`](superpowers/specs/2026-09-04-run-step-budget-design.md)
|
- [`run step budget design`](superpowers/specs/2026-09-04-run-step-budget-design.md)
|
||||||
|
|
||||||
@@ -64,6 +53,13 @@ The budget must cover every frame and subgraph scope in one run, survive
|
|||||||
checkpoint and resume, and stop valid but non-terminating graph cycles with a
|
checkpoint and resume, and stop valid but non-terminating graph cycles with a
|
||||||
clear runtime failure.
|
clear runtime failure.
|
||||||
|
|
||||||
|
### 2. Consolidate runtime identity resolution
|
||||||
|
|
||||||
|
Introduce one internal resolver for a frame, lineage, runtime scope, and
|
||||||
|
foreach activation environment. The resolver should validate the canonical
|
||||||
|
identity chain once so fork/gather code does not pass related identifiers
|
||||||
|
independently or repeat ownership walks.
|
||||||
|
|
||||||
### 3. Implement explicit fork and gather
|
### 3. Implement explicit fork and gather
|
||||||
|
|
||||||
Reuse the scheduler, activation, lineage, and reducer-aware barrier machinery:
|
Reuse the scheduler, activation, lineage, and reducer-aware barrier machinery:
|
||||||
@@ -140,12 +136,18 @@ The active sequence can assume these foundations:
|
|||||||
- Native subgraph scopes and durable return to the parent node
|
- Native subgraph scopes and durable return to the parent node
|
||||||
- Concurrent foreach with activation barriers and reducer-aware lineage merges
|
- Concurrent foreach with activation barriers and reducer-aware lineage merges
|
||||||
- Validated foreach back-edges with one static control region per node use
|
- Validated foreach back-edges with one static control region per node use
|
||||||
|
- Structured runtime context shared by execution, expressions, validation, and
|
||||||
|
authoring references
|
||||||
|
- Removal of the pass-through `JoinNode`; future `GatherNode` starts with its
|
||||||
|
actual synchronization contract and no placeholder compatibility
|
||||||
- Durable stopped-run inspection and resume
|
- Durable stopped-run inspection and resume
|
||||||
- Python client reconstruction of capabilities, artifacts, deployments, and
|
- Python client reconstruction of capabilities, artifacts, deployments, and
|
||||||
runs through the API
|
runs through the API
|
||||||
|
|
||||||
The current foreach return contract is
|
The current foreach return contract is
|
||||||
[`foreach back-edge design`](superpowers/specs/2026-09-04-foreach-back-edge-design.md).
|
[`foreach back-edge design`](superpowers/specs/2026-09-04-foreach-back-edge-design.md).
|
||||||
|
The current context contract is
|
||||||
|
[`structured runtime context`](superpowers/specs/2026-09-04-structured-runtime-context-design.md).
|
||||||
|
|
||||||
## Historical entry points
|
## Historical entry points
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,6 @@ Exactly one step-kind key must be present. Allowed step-kind keys are:
|
|||||||
- `use`
|
- `use`
|
||||||
- `foreach`
|
- `foreach`
|
||||||
- `interrupt`
|
- `interrupt`
|
||||||
- `join`
|
|
||||||
|
|
||||||
Zero kind keys or multiple kind keys are validation errors.
|
Zero kind keys or multiple kind keys are validation errors.
|
||||||
|
|
||||||
@@ -243,19 +242,6 @@ This lowers to the current core `ForeachNode`.
|
|||||||
|
|
||||||
This lowers to the current core `InterruptNode`.
|
This lowers to the current core `InterruptNode`.
|
||||||
|
|
||||||
### `join`
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"join": {}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This lowers to the current core `JoinNode`.
|
|
||||||
|
|
||||||
`join` is not a reverse branch. It remains reserved for actual join/frame
|
|
||||||
semantics.
|
|
||||||
|
|
||||||
## Routes
|
## Routes
|
||||||
|
|
||||||
Most ordinary edges should be authored through `routes`:
|
Most ordinary edges should be authored through `routes`:
|
||||||
@@ -337,7 +323,8 @@ node_c.unreachable
|
|||||||
-> runtime_error
|
-> runtime_error
|
||||||
```
|
```
|
||||||
|
|
||||||
This is not `join`. It is compressed declaration of several ordinary edges.
|
This is not synchronization. It is compressed declaration of several ordinary
|
||||||
|
edges.
|
||||||
|
|
||||||
Possible later surfaces:
|
Possible later surfaces:
|
||||||
|
|
||||||
@@ -366,7 +353,7 @@ That should be handled as its own pass, not smuggled into this MCP draft change.
|
|||||||
Potential later core work:
|
Potential later core work:
|
||||||
|
|
||||||
- true graph-as-node / subgraph support
|
- true graph-as-node / subgraph support
|
||||||
- meaningful join semantics
|
- explicit fork/gather semantics
|
||||||
- future START-edge support if `Workflow.start` changes
|
- future START-edge support if `Workflow.start` changes
|
||||||
|
|
||||||
### Draft `route` Sugar
|
### Draft `route` Sugar
|
||||||
|
|||||||
@@ -157,7 +157,6 @@ Example:
|
|||||||
"SubgraphNode",
|
"SubgraphNode",
|
||||||
"ConditionNode",
|
"ConditionNode",
|
||||||
"ForeachNode",
|
"ForeachNode",
|
||||||
"JoinNode",
|
|
||||||
"EndNode",
|
"EndNode",
|
||||||
"InterruptNode"
|
"InterruptNode"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -85,8 +85,8 @@ semantic operation produces one patch and consumes one revision.
|
|||||||
clients do not need to know about the internal service split.
|
clients do not need to know about the internal service split.
|
||||||
|
|
||||||
The service boundary is intentionally not capability-only. The current draft
|
The service boundary is intentionally not capability-only. The current draft
|
||||||
model also represents `end`, `condition`, `interrupt`, `foreach`, `join`,
|
model also represents `end`, `condition`, `interrupt`, `foreach`, `when`,
|
||||||
`when`, `choose`, and `match` steps, and core may gain more step kinds. This
|
`choose`, `match`, and subgraph steps, and core may gain more step kinds. This
|
||||||
slice adds semantic operations only where required, but new step-kind helpers
|
slice adds semantic operations only where required, but new step-kind helpers
|
||||||
belong in `WorkflowDraftAuthoringApi` rather than a parallel authoring system.
|
belong in `WorkflowDraftAuthoringApi` rather than a parallel authoring system.
|
||||||
|
|
||||||
|
|||||||
@@ -132,7 +132,6 @@ Node presentation distinguishes the existing core node kinds:
|
|||||||
- condition;
|
- condition;
|
||||||
- interrupt;
|
- interrupt;
|
||||||
- foreach;
|
- foreach;
|
||||||
- join;
|
|
||||||
- end and other control nodes.
|
- end and other control nodes.
|
||||||
|
|
||||||
Each node shows its stable id and concise semantic label. Selecting a node opens
|
Each node shows its stable id and concise semantic label. Selecting a node opens
|
||||||
@@ -226,4 +225,3 @@ The slice is complete when:
|
|||||||
7. stale requests and partial failures cannot corrupt newer selections;
|
7. stale requests and partial failures cannot corrupt newer selections;
|
||||||
8. the `lda_report_workflow` lifecycle is readable without scrolling raw JSON;
|
8. the `lda_report_workflow` lifecycle is readable without scrolling raw JSON;
|
||||||
9. frontend tests, typecheck, production build, and optional live smoke pass.
|
9. frontend tests, typecheck, production build, and optional live smoke pass.
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ deliberate authoring vocabulary that is later lowered by
|
|||||||
- `DraftUseStep`
|
- `DraftUseStep`
|
||||||
- `DraftForeachStep`
|
- `DraftForeachStep`
|
||||||
- `DraftInterruptStep`
|
- `DraftInterruptStep`
|
||||||
- `DraftJoinStep`
|
|
||||||
- `DraftEndStep`
|
- `DraftEndStep`
|
||||||
- `DraftWhenStep`
|
- `DraftWhenStep`
|
||||||
- `DraftChooseStep`
|
- `DraftChooseStep`
|
||||||
@@ -149,7 +148,6 @@ Declared top-level outcomes are:
|
|||||||
- `foreach`: `loop`, `done`, plus `completed_with_errors` when the item-error
|
- `foreach`: `loop`, `done`, plus `completed_with_errors` when the item-error
|
||||||
policy is `skip` or `collect`;
|
policy is `skip` or `collect`;
|
||||||
- `interrupt`: `interrupt.outcomes`;
|
- `interrupt`: `interrupt.outcomes`;
|
||||||
- `join`: `done`;
|
|
||||||
- `subgraph`: `subgraph.outcomes`.
|
- `subgraph`: `subgraph.outcomes`.
|
||||||
|
|
||||||
The capability helper remains distinct because it resolves a capability,
|
The capability helper remains distinct because it resolves a capability,
|
||||||
@@ -175,7 +173,7 @@ reject malformed or ambiguous step objects before dispatching to the API.
|
|||||||
|
|
||||||
The Python RPC client implements the same method on `WorkflowApi`. Client and
|
The Python RPC client implements the same method on `WorkflowApi`. Client and
|
||||||
server serialize steps with aliases so fields such as foreach `as` and when
|
server serialize steps with aliases so fields such as foreach `as` and when
|
||||||
`if` retain their canonical wire names. Round-trip tests cover all nine step
|
`if` retain their canonical wire names. Round-trip tests cover all eight step
|
||||||
variants, including interrupt schemas and subgraph workflow references.
|
variants, including interrupt schemas and subgraph workflow references.
|
||||||
|
|
||||||
## CLI Shape
|
## CLI Shape
|
||||||
@@ -186,7 +184,6 @@ Register a focused Typer application beneath `wf draft`:
|
|||||||
wf draft add capability
|
wf draft add capability
|
||||||
wf draft add interrupt
|
wf draft add interrupt
|
||||||
wf draft add foreach
|
wf draft add foreach
|
||||||
wf draft add join
|
|
||||||
wf draft add end
|
wf draft add end
|
||||||
wf draft add when
|
wf draft add when
|
||||||
wf draft add choose
|
wf draft add choose
|
||||||
@@ -210,7 +207,6 @@ Variant-specific options are:
|
|||||||
`--resume LOCAL_SOURCE=STATE_TARGET`, and repeatable `--outcome`;
|
`--resume LOCAL_SOURCE=STATE_TARGET`, and repeatable `--outcome`;
|
||||||
- `foreach`: `--over`, `--as`, `--mode`, `--item-error`, optional
|
- `foreach`: `--over`, `--as`, `--mode`, `--item-error`, optional
|
||||||
`--collect-to`, `--max-active`, and `--max-outstanding`;
|
`--collect-to`, `--max-active`, and `--max-outstanding`;
|
||||||
- `join`: no variant-specific options;
|
|
||||||
- `end`: `--outcome` and no `--route`;
|
- `end`: `--outcome` and no `--route`;
|
||||||
- `when`: `--condition-file`, `--then`, and `--otherwise`;
|
- `when`: `--condition-file`, `--then`, and `--otherwise`;
|
||||||
- `choose`: `--clauses-file` containing the ordered clause array and
|
- `choose`: `--clauses-file` containing the ordered clause array and
|
||||||
@@ -269,7 +265,7 @@ modules need them; avoid a broad CLI refactor.
|
|||||||
|
|
||||||
### CLI
|
### CLI
|
||||||
|
|
||||||
- `wf draft add --help` lists all nine commands.
|
- `wf draft add --help` lists all eight commands.
|
||||||
- Per-command help exposes only relevant options.
|
- Per-command help exposes only relevant options.
|
||||||
- Every command builds the expected `DraftStep`, incoming source, and routes.
|
- Every command builds the expected `DraftStep`, incoming source, and routes.
|
||||||
- Invalid flag combinations fail before calling the API.
|
- Invalid flag combinations fail before calling the API.
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ The remaining graph-authoring work is split into independently useful slices:
|
|||||||
1. selected-step input and output dataflow;
|
1. selected-step input and output dataflow;
|
||||||
2. workflow Input, State, and Outcomes contract projections;
|
2. workflow Input, State, and Outcomes contract projections;
|
||||||
3. explicit End authoring and a typed Add step palette;
|
3. explicit End authoring and a typed Add step palette;
|
||||||
4. typed interrupt, control, subgraph, foreach, and join forms; and
|
4. typed interrupt, control, subgraph, and foreach forms; and
|
||||||
5. direct graph gestures lowered through the same canonical mutations.
|
5. direct graph gestures lowered through the same canonical mutations.
|
||||||
|
|
||||||
This document specifies only the first item.
|
This document specifies only the first item.
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ Slice 6 includes:
|
|||||||
Slice 6 excludes:
|
Slice 6 excludes:
|
||||||
|
|
||||||
- explicit End-node creation;
|
- explicit End-node creation;
|
||||||
- typed creation forms for interrupt, condition, subgraph, foreach, or join;
|
- typed creation forms for interrupt, condition, subgraph, or foreach;
|
||||||
- graph gesture binding by drawing edges;
|
- graph gesture binding by drawing edges;
|
||||||
- arbitrary schema inference from runtime values;
|
- arbitrary schema inference from runtime values;
|
||||||
- renaming existing step ids;
|
- renaming existing step ids;
|
||||||
@@ -140,7 +140,7 @@ independent source of truth once the operation is available.
|
|||||||
Standard frame context currently includes values such as prior outcome, active
|
Standard frame context currently includes values such as prior outcome, active
|
||||||
incoming edge, scope id, lineage id, and parent lineage id. Foreach iteration
|
incoming edge, scope id, lineage id, and parent lineage id. Foreach iteration
|
||||||
frames additionally expose loop item, loop index, and the configured foreach
|
frames additionally expose loop item, loop index, and the configured foreach
|
||||||
alias. Future fork/join features may add branch-scoped context through the same
|
alias. Future fork/gather features may add branch-scoped context through the same
|
||||||
inventory without changing high-level clients.
|
inventory without changing high-level clients.
|
||||||
|
|
||||||
Context availability is computed by core/API code using workflow graph and
|
Context availability is computed by core/API code using workflow graph and
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ They are runtime policy, not workflow graph semantics.
|
|||||||
|
|
||||||
**Step Attempt** is one admitted attempt to execute a selected workflow `Step`
|
**Step Attempt** is one admitted attempt to execute a selected workflow `Step`
|
||||||
in one frame. Node uses, conditions, foreach controllers, subgraph boundaries,
|
in one frame. Node uses, conditions, foreach controllers, subgraph boundaries,
|
||||||
interrupt nodes, joins, and explicit end nodes all count.
|
interrupt nodes, and explicit end nodes all count.
|
||||||
|
|
||||||
**Step Number** is the one-based ordinal assigned to an admitted step attempt
|
**Step Number** is the one-based ordinal assigned to an admitted step attempt
|
||||||
within a run.
|
within a run.
|
||||||
@@ -256,7 +256,7 @@ with the run.
|
|||||||
### Core counting
|
### Core counting
|
||||||
|
|
||||||
- A budget of one admits exactly one step and denies the second.
|
- A budget of one admits exactly one step and denies the second.
|
||||||
- Node, condition, foreach, subgraph, interrupt, join, and explicit end steps
|
- Node, condition, foreach, subgraph, interrupt, and explicit end steps
|
||||||
count.
|
count.
|
||||||
- A transition to legacy `END` does not create an extra attempt.
|
- A transition to legacy `END` does not create an extra attempt.
|
||||||
- Handler failure still consumes its admitted step.
|
- Handler failure still consumes its admitted step.
|
||||||
|
|||||||
+1
-1
@@ -444,7 +444,7 @@ Use `set-route` separately for outcome routing.
|
|||||||
Use `wf draft add` to add one typed step to an existing draft:
|
Use `wf draft add` to add one typed step to an existing draft:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
capability interrupt foreach join end
|
capability interrupt foreach end
|
||||||
when choose match subgraph
|
when choose match subgraph
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+5
-16
@@ -399,8 +399,8 @@ are part of the graph definition:
|
|||||||
```
|
```
|
||||||
|
|
||||||
Static values are not path mappings. Use `{"target": ..., "value": ...}` for
|
Static values are not path mappings. Use `{"target": ..., "value": ...}` for
|
||||||
literal JSON values. Invalid draft step shapes are rejected instead of silently
|
literal JSON values. Invalid draft step shapes are rejected instead of being
|
||||||
compiling to `join`.
|
silently replaced with a placeholder step.
|
||||||
|
|
||||||
Generated MCP tool wrappers are intentionally naive. They normally expose both
|
Generated MCP tool wrappers are intentionally naive. They normally expose both
|
||||||
`ok` and `error` outcomes, because MCP tool calls can report transport/provider
|
`ok` and `error` outcomes, because MCP tool calls can report transport/provider
|
||||||
@@ -513,16 +513,6 @@ Declares an explicit workflow terminal outcome.
|
|||||||
Use explicit `end` steps for non-`ok` workflow outcomes. The legacy `__end__`
|
Use explicit `end` steps for non-`ok` workflow outcomes. The legacy `__end__`
|
||||||
destination remains the shorthand for public workflow outcome `ok`.
|
destination remains the shorthand for public workflow outcome `ok`.
|
||||||
|
|
||||||
### `join`
|
|
||||||
|
|
||||||
Joins control flow.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"join": {}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### `when`
|
### `when`
|
||||||
|
|
||||||
Creates one boolean decision step. The condition uses the same JSON shape as
|
Creates one boolean decision step. The condition uses the same JSON shape as
|
||||||
@@ -643,10 +633,9 @@ interrupt, an end step, or a subgraph rather than a capability:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
wf draft create report_ws --name report_workflow
|
wf draft create report_ws --name report_workflow
|
||||||
wf draft add join report_ws --revision 1 --step gate --route done=finish
|
wf draft add end report_ws --revision 1 --step finish --outcome error
|
||||||
wf draft set-start report_ws --revision 2 --step gate
|
wf draft set-start report_ws --revision 2 --step finish
|
||||||
wf draft add end report_ws --revision 3 --step finish --outcome error
|
wf draft set-contract report_ws --revision 3 --outcome error
|
||||||
wf draft set-contract report_ws --revision 4 --outcome error
|
|
||||||
wf draft validate report_ws
|
wf draft validate report_ws
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ an unexpected extra argument because it is not attached to its own flag.
|
|||||||
|
|
||||||
Adds any typed `DraftStep` with optional incoming and outgoing route wiring in
|
Adds any typed `DraftStep` with optional incoming and outgoing route wiring in
|
||||||
one revision. The CLI exposes one command per kind under `wf draft add`:
|
one revision. The CLI exposes one command per kind under `wf draft add`:
|
||||||
`interrupt`, `foreach`, `join`, `end`, `when`, `choose`, `match`, and
|
`interrupt`, `foreach`, `end`, `when`, `choose`, `match`, and
|
||||||
`subgraph`. Decision targets are embedded and reject `--route`. Interrupts
|
`subgraph`. Decision targets are embedded and reject `--route`. Interrupts
|
||||||
and subgraphs preserve JSON Schema boundary contracts. Invalid intermediate
|
and subgraphs preserve JSON Schema boundary contracts. Invalid intermediate
|
||||||
drafts remain saveable in the workspace but must pass `wf draft validate`
|
drafts remain saveable in the workspace but must pass `wf draft validate`
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ from wf_artifacts.drafts.models import (
|
|||||||
DraftEndStep,
|
DraftEndStep,
|
||||||
DraftForeachStep,
|
DraftForeachStep,
|
||||||
DraftInterruptStep,
|
DraftInterruptStep,
|
||||||
DraftJoinStep,
|
|
||||||
DraftMatchStep,
|
DraftMatchStep,
|
||||||
DraftStep,
|
DraftStep,
|
||||||
DraftSubgraphStep,
|
DraftSubgraphStep,
|
||||||
@@ -353,8 +352,6 @@ class WorkflowDraftAuthoringApi:
|
|||||||
return outcomes
|
return outcomes
|
||||||
if isinstance(step, DraftInterruptStep):
|
if isinstance(step, DraftInterruptStep):
|
||||||
return set(step.interrupt.outcomes)
|
return set(step.interrupt.outcomes)
|
||||||
if isinstance(step, DraftJoinStep):
|
|
||||||
return {"done"}
|
|
||||||
if isinstance(step, DraftSubgraphStep):
|
if isinstance(step, DraftSubgraphStep):
|
||||||
return set(step.subgraph.outcomes)
|
return set(step.subgraph.outcomes)
|
||||||
if isinstance(
|
if isinstance(
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ from .models import (
|
|||||||
DraftEndStep,
|
DraftEndStep,
|
||||||
DraftForeachStep,
|
DraftForeachStep,
|
||||||
DraftInterruptStep,
|
DraftInterruptStep,
|
||||||
DraftJoinStep,
|
|
||||||
DraftMatchCase,
|
DraftMatchCase,
|
||||||
DraftMatchStep,
|
DraftMatchStep,
|
||||||
DraftSubgraphStep,
|
DraftSubgraphStep,
|
||||||
@@ -27,7 +26,6 @@ __all__ = [
|
|||||||
"DraftEndStep",
|
"DraftEndStep",
|
||||||
"DraftForeachStep",
|
"DraftForeachStep",
|
||||||
"DraftInterruptStep",
|
"DraftInterruptStep",
|
||||||
"DraftJoinStep",
|
|
||||||
"DraftMatchCase",
|
"DraftMatchCase",
|
||||||
"DraftMatchStep",
|
"DraftMatchStep",
|
||||||
"DraftSubgraphStep",
|
"DraftSubgraphStep",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from typing import Any
|
|||||||
|
|
||||||
from wf_authoring import WorkflowBuilder
|
from wf_authoring import WorkflowBuilder
|
||||||
from wf_authoring.dsl import PathExpr
|
from wf_authoring.dsl import PathExpr
|
||||||
from wf_core import JoinNode, SubgraphNode, Workflow
|
from wf_core import SubgraphNode, Workflow
|
||||||
from wf_core.paths import GraphSourcePath
|
from wf_core.paths import GraphSourcePath
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
@@ -12,7 +12,6 @@ from .models import (
|
|||||||
DraftEndStep,
|
DraftEndStep,
|
||||||
DraftForeachStep,
|
DraftForeachStep,
|
||||||
DraftInterruptStep,
|
DraftInterruptStep,
|
||||||
DraftJoinStep,
|
|
||||||
DraftMatchStep,
|
DraftMatchStep,
|
||||||
DraftStep,
|
DraftStep,
|
||||||
DraftSubgraphStep,
|
DraftSubgraphStep,
|
||||||
@@ -84,10 +83,6 @@ def _add_step(builder: WorkflowBuilder, step_id: str, step: DraftStep):
|
|||||||
return builder.interrupt(
|
return builder.interrupt(
|
||||||
**interrupt_kwargs,
|
**interrupt_kwargs,
|
||||||
)
|
)
|
||||||
if isinstance(step, DraftJoinStep):
|
|
||||||
node = JoinNode(id=step_id, type="join")
|
|
||||||
builder.nodes.append(node)
|
|
||||||
return node
|
|
||||||
if isinstance(step, DraftEndStep):
|
if isinstance(step, DraftEndStep):
|
||||||
return builder.end(step.end.outcome, id=step_id)
|
return builder.end(step.end.outcome, id=step_id)
|
||||||
if isinstance(step, DraftWhenStep):
|
if isinstance(step, DraftWhenStep):
|
||||||
|
|||||||
@@ -220,14 +220,6 @@ class DraftSubgraphStep(BaseModel):
|
|||||||
subgraph: DraftSubgraphPayload
|
subgraph: DraftSubgraphPayload
|
||||||
|
|
||||||
|
|
||||||
class DraftJoinStep(BaseModel):
|
|
||||||
"""Draft step that emits the current core join node."""
|
|
||||||
|
|
||||||
model_config = ConfigDict(extra="forbid")
|
|
||||||
|
|
||||||
join: JsonObject = Field(default_factory=dict)
|
|
||||||
|
|
||||||
|
|
||||||
class DraftEndPayload(BaseModel):
|
class DraftEndPayload(BaseModel):
|
||||||
"""Payload for one explicit workflow terminal outcome."""
|
"""Payload for one explicit workflow terminal outcome."""
|
||||||
|
|
||||||
@@ -323,7 +315,6 @@ DraftStep = (
|
|||||||
DraftUseStep
|
DraftUseStep
|
||||||
| DraftForeachStep
|
| DraftForeachStep
|
||||||
| DraftInterruptStep
|
| DraftInterruptStep
|
||||||
| DraftJoinStep
|
|
||||||
| DraftEndStep
|
| DraftEndStep
|
||||||
| DraftWhenStep
|
| DraftWhenStep
|
||||||
| DraftChooseStep
|
| DraftChooseStep
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ from wf_core import (
|
|||||||
EndNode,
|
EndNode,
|
||||||
ForeachNode,
|
ForeachNode,
|
||||||
InterruptNode,
|
InterruptNode,
|
||||||
JoinNode,
|
|
||||||
NodeUse,
|
NodeUse,
|
||||||
SubgraphNode,
|
SubgraphNode,
|
||||||
)
|
)
|
||||||
@@ -16,14 +15,7 @@ from wf_core import (
|
|||||||
from ..nodes import NodeSpec
|
from ..nodes import NodeSpec
|
||||||
|
|
||||||
StepRef: TypeAlias = (
|
StepRef: TypeAlias = (
|
||||||
str
|
str | NodeUse | SubgraphNode | ConditionNode | ForeachNode | InterruptNode | EndNode
|
||||||
| NodeUse
|
|
||||||
| SubgraphNode
|
|
||||||
| ConditionNode
|
|
||||||
| ForeachNode
|
|
||||||
| InterruptNode
|
|
||||||
| JoinNode
|
|
||||||
| EndNode
|
|
||||||
)
|
)
|
||||||
"""A reference to a step, which can be either a string id or a node object
|
"""A reference to a step, which can be either a string id or a node object
|
||||||
that should be auto-used."""
|
that should be auto-used."""
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ from wf_artifacts.drafts.models import (
|
|||||||
DraftForeachStep,
|
DraftForeachStep,
|
||||||
DraftInterruptPayload,
|
DraftInterruptPayload,
|
||||||
DraftInterruptStep,
|
DraftInterruptStep,
|
||||||
DraftJoinStep,
|
|
||||||
DraftMatchCase,
|
DraftMatchCase,
|
||||||
DraftMatchPayload,
|
DraftMatchPayload,
|
||||||
DraftMatchStep,
|
DraftMatchStep,
|
||||||
@@ -425,43 +424,6 @@ def add_foreach_step(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.command("join")
|
|
||||||
def add_join_step(
|
|
||||||
ctx: typer.Context,
|
|
||||||
workspace_id: Annotated[str, typer.Argument(help="Draft workspace id.")],
|
|
||||||
revision: Annotated[
|
|
||||||
int, typer.Option("--revision", min=1, help="Expected workspace revision.")
|
|
||||||
],
|
|
||||||
step_id: Annotated[str, typer.Option("--step", help="New draft step id.")],
|
|
||||||
from_step: Annotated[
|
|
||||||
str | None, typer.Option("--from-step", help="Incoming step id.")
|
|
||||||
] = None,
|
|
||||||
from_outcome: Annotated[
|
|
||||||
str | None,
|
|
||||||
typer.Option("--from-outcome", help="Outcome on --from-step (default: ok)."),
|
|
||||||
] = None,
|
|
||||||
route: Annotated[
|
|
||||||
list[str] | None,
|
|
||||||
typer.Option("--route", help="Route mapping OUTCOME=TARGET. Repeat as needed."),
|
|
||||||
] = None,
|
|
||||||
) -> None:
|
|
||||||
"""Add a join step.
|
|
||||||
|
|
||||||
Example: `wf draft add join WS --revision 1 --step joined --route done=__end__`.
|
|
||||||
Run `wf draft validate WS` after editing.
|
|
||||||
"""
|
|
||||||
_submit_step(
|
|
||||||
ctx,
|
|
||||||
workspace_id=workspace_id,
|
|
||||||
revision=revision,
|
|
||||||
step_id=step_id,
|
|
||||||
step=DraftJoinStep(join={}),
|
|
||||||
from_step=from_step,
|
|
||||||
from_outcome=from_outcome,
|
|
||||||
routes=_parse_route_flags(route) or None,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@app.command("end")
|
@app.command("end")
|
||||||
def add_end_step(
|
def add_end_step(
|
||||||
ctx: typer.Context,
|
ctx: typer.Context,
|
||||||
|
|||||||
+1
-1
@@ -56,7 +56,7 @@ def write_json_file(path: Path, payload: Any, *, force: bool) -> None:
|
|||||||
output.write("\n")
|
output.write("\n")
|
||||||
except FileExistsError as exc:
|
except FileExistsError as exc:
|
||||||
raise CliInputError(
|
raise CliInputError(
|
||||||
f"file {path!s} already exists; use --force to replace it"
|
f"file already exists: {path!s}; use --force to replace it"
|
||||||
) from exc
|
) from exc
|
||||||
except OSError as exc:
|
except OSError as exc:
|
||||||
raise CliInputError(f"could not write file {path!s}: {exc}") from exc
|
raise CliInputError(f"could not write file {path!s}: {exc}") from exc
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ from .models import (
|
|||||||
InputPathBinding,
|
InputPathBinding,
|
||||||
InputValueBinding,
|
InputValueBinding,
|
||||||
InterruptNode,
|
InterruptNode,
|
||||||
JoinNode,
|
|
||||||
JsonValue,
|
JsonValue,
|
||||||
LiteralExpression,
|
LiteralExpression,
|
||||||
NodeDef,
|
NodeDef,
|
||||||
@@ -89,7 +88,6 @@ __all__ = [
|
|||||||
"InputValueBinding",
|
"InputValueBinding",
|
||||||
"InterruptRequest",
|
"InterruptRequest",
|
||||||
"InterruptRoute",
|
"InterruptRoute",
|
||||||
"JoinNode",
|
|
||||||
"JsonValue",
|
"JsonValue",
|
||||||
"LiteralExpression",
|
"LiteralExpression",
|
||||||
"NodeDef",
|
"NodeDef",
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ from wf_core.models.steps import (
|
|||||||
ForeachItemErrorPolicy,
|
ForeachItemErrorPolicy,
|
||||||
ForeachNode,
|
ForeachNode,
|
||||||
InterruptNode,
|
InterruptNode,
|
||||||
JoinNode,
|
|
||||||
NodeUse,
|
NodeUse,
|
||||||
Step,
|
Step,
|
||||||
SubgraphNode,
|
SubgraphNode,
|
||||||
@@ -54,7 +53,6 @@ __all__ = [
|
|||||||
"InputExpressionBinding",
|
"InputExpressionBinding",
|
||||||
"InputPathBinding",
|
"InputPathBinding",
|
||||||
"InputValueBinding",
|
"InputValueBinding",
|
||||||
"JoinNode",
|
|
||||||
"JsonValue",
|
"JsonValue",
|
||||||
"LiteralExpression",
|
"LiteralExpression",
|
||||||
"LiteralOperand",
|
"LiteralOperand",
|
||||||
|
|||||||
@@ -293,13 +293,6 @@ class ForeachNode(BaseModel):
|
|||||||
return GraphSourcePath("context", ("foreach", self.id, "index"))
|
return GraphSourcePath("context", ("foreach", self.id, "index"))
|
||||||
|
|
||||||
|
|
||||||
class JoinNode(BaseModel):
|
|
||||||
"""Control-flow step that marks a branch or frame as joined."""
|
|
||||||
|
|
||||||
id: str
|
|
||||||
type: Literal["join"]
|
|
||||||
|
|
||||||
|
|
||||||
class EndNode(BaseModel):
|
class EndNode(BaseModel):
|
||||||
"""Explicit workflow terminal that sets the workflow-level outcome.
|
"""Explicit workflow terminal that sets the workflow-level outcome.
|
||||||
|
|
||||||
@@ -411,13 +404,7 @@ class InterruptNode(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
Step = Annotated[
|
Step = Annotated[
|
||||||
NodeUse
|
NodeUse | SubgraphNode | ConditionNode | ForeachNode | EndNode | InterruptNode,
|
||||||
| SubgraphNode
|
|
||||||
| ConditionNode
|
|
||||||
| ForeachNode
|
|
||||||
| JoinNode
|
|
||||||
| EndNode
|
|
||||||
| InterruptNode,
|
|
||||||
Field(discriminator="type"),
|
Field(discriminator="type"),
|
||||||
]
|
]
|
||||||
"""Discriminated union of all executable workflow graph steps."""
|
"""Discriminated union of all executable workflow graph steps."""
|
||||||
|
|||||||
@@ -41,15 +41,6 @@ def handle_condition_step(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def handle_join_step() -> StepExecutionResult:
|
|
||||||
return StepExecutionResult(
|
|
||||||
outcome="done",
|
|
||||||
resolved_input={},
|
|
||||||
output={},
|
|
||||||
state_changes={},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def handle_interrupt_step(
|
def handle_interrupt_step(
|
||||||
run: RunState,
|
run: RunState,
|
||||||
step: InterruptNode,
|
step: InterruptNode,
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ from wf_core.models.steps import (
|
|||||||
EndNode,
|
EndNode,
|
||||||
ForeachNode,
|
ForeachNode,
|
||||||
InterruptNode,
|
InterruptNode,
|
||||||
JoinNode,
|
|
||||||
NodeUse,
|
NodeUse,
|
||||||
SubgraphNode,
|
SubgraphNode,
|
||||||
)
|
)
|
||||||
@@ -22,7 +21,6 @@ from wf_core.runtime.ops.foreach import step_foreach
|
|||||||
from wf_core.runtime.ops.handlers import (
|
from wf_core.runtime.ops.handlers import (
|
||||||
handle_condition_step,
|
handle_condition_step,
|
||||||
handle_interrupt_step,
|
handle_interrupt_step,
|
||||||
handle_join_step,
|
|
||||||
)
|
)
|
||||||
from wf_core.runtime.ops.index import WorkflowIndex, build_workflow_index
|
from wf_core.runtime.ops.index import WorkflowIndex, build_workflow_index
|
||||||
from wf_core.runtime.ops.merges import ReducerDefinition
|
from wf_core.runtime.ops.merges import ReducerDefinition
|
||||||
@@ -149,8 +147,6 @@ def step_workflow(
|
|||||||
raise
|
raise
|
||||||
elif isinstance(step, ConditionNode):
|
elif isinstance(step, ConditionNode):
|
||||||
step_result = handle_condition_step(run, step)
|
step_result = handle_condition_step(run, step)
|
||||||
elif isinstance(step, JoinNode):
|
|
||||||
step_result = handle_join_step()
|
|
||||||
elif isinstance(step, EndNode):
|
elif isinstance(step, EndNode):
|
||||||
return complete_end_step(
|
return complete_end_step(
|
||||||
run=run,
|
run=run,
|
||||||
@@ -264,8 +260,6 @@ async def step_workflow_async(
|
|||||||
raise
|
raise
|
||||||
elif isinstance(step, ConditionNode):
|
elif isinstance(step, ConditionNode):
|
||||||
step_result = handle_condition_step(run, step)
|
step_result = handle_condition_step(run, step)
|
||||||
elif isinstance(step, JoinNode):
|
|
||||||
step_result = handle_join_step()
|
|
||||||
elif isinstance(step, EndNode):
|
elif isinstance(step, EndNode):
|
||||||
return complete_end_step(
|
return complete_end_step(
|
||||||
run=run,
|
run=run,
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ def declared_outcomes_for_step(step: Step, node_defs: dict[str, NodeDef]) -> set
|
|||||||
if step.item_error.action in {"skip", "collect"}:
|
if step.item_error.action in {"skip", "collect"}:
|
||||||
outcomes.add("completed_with_errors")
|
outcomes.add("completed_with_errors")
|
||||||
return outcomes
|
return outcomes
|
||||||
if step.type == "join":
|
|
||||||
return {"done"}
|
|
||||||
if isinstance(step, EndNode):
|
if isinstance(step, EndNode):
|
||||||
return set()
|
return set()
|
||||||
if isinstance(step, InterruptNode):
|
if isinstance(step, InterruptNode):
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ def test_draft_step_requires_exactly_one_kind_key() -> None:
|
|||||||
assert isinstance(steps, dict)
|
assert isinstance(steps, dict)
|
||||||
echo = steps["echo"]
|
echo = steps["echo"]
|
||||||
assert isinstance(echo, dict)
|
assert isinstance(echo, dict)
|
||||||
echo["join"] = {}
|
echo["end"] = {"outcome": "ok"}
|
||||||
|
|
||||||
with pytest.raises(ValidationError) as exc_info:
|
with pytest.raises(ValidationError) as exc_info:
|
||||||
WorkflowDraft.model_validate(draft)
|
WorkflowDraft.model_validate(draft)
|
||||||
|
|||||||
@@ -675,7 +675,7 @@ async def test_update_capability_step_rejects_wrong_kind_and_invalid_input_atomi
|
|||||||
register_echo=True,
|
register_echo=True,
|
||||||
)
|
)
|
||||||
draft = _echo_draft()
|
draft = _echo_draft()
|
||||||
draft["steps"]["joined"] = {"join": ["echo"]}
|
draft["steps"]["joined"] = {"end": {}}
|
||||||
await draft_api.create_draft_workspace(workspace_id="echo", draft=draft)
|
await draft_api.create_draft_workspace(workspace_id="echo", draft=draft)
|
||||||
before = await draft_api.get_draft_workspace(
|
before = await draft_api.get_draft_workspace(
|
||||||
workspace_id="echo",
|
workspace_id="echo",
|
||||||
@@ -3136,7 +3136,7 @@ async def test_set_step_output_bindings_rejects_non_capability_step_without_muta
|
|||||||
await draft_api.patch_draft_workspace(
|
await draft_api.patch_draft_workspace(
|
||||||
workspace_id="non-capability-output-step",
|
workspace_id="non-capability-output-step",
|
||||||
revision=1,
|
revision=1,
|
||||||
patch=[{"op": "replace", "path": "/steps/render", "value": {"join": {}}}],
|
patch=[{"op": "replace", "path": "/steps/render", "value": {"end": {}}}],
|
||||||
)
|
)
|
||||||
before = await draft_api.get_draft_workspace(
|
before = await draft_api.get_draft_workspace(
|
||||||
workspace_id="non-capability-output-step",
|
workspace_id="non-capability-output-step",
|
||||||
@@ -3246,7 +3246,7 @@ async def test_set_step_output_bindings_stale_revision_precedes_non_capability_s
|
|||||||
await draft_api.patch_draft_workspace(
|
await draft_api.patch_draft_workspace(
|
||||||
workspace_id="stale-output-non-capability",
|
workspace_id="stale-output-non-capability",
|
||||||
revision=1,
|
revision=1,
|
||||||
patch=[{"op": "replace", "path": "/steps/render", "value": {"join": {}}}],
|
patch=[{"op": "replace", "path": "/steps/render", "value": {"end": {}}}],
|
||||||
)
|
)
|
||||||
before = await draft_api.get_draft_workspace(
|
before = await draft_api.get_draft_workspace(
|
||||||
workspace_id="stale-output-non-capability",
|
workspace_id="stale-output-non-capability",
|
||||||
@@ -3504,7 +3504,7 @@ async def test_set_step_input_bindings_rejects_non_capability_step_without_mutat
|
|||||||
register_echo=True,
|
register_echo=True,
|
||||||
)
|
)
|
||||||
draft = _structured_report_draft()
|
draft = _structured_report_draft()
|
||||||
draft["steps"]["report"] = {"join": {}}
|
draft["steps"]["report"] = {"end": {}}
|
||||||
await draft_api.create_draft_workspace(workspace_id="non_capability", draft=draft)
|
await draft_api.create_draft_workspace(workspace_id="non_capability", draft=draft)
|
||||||
api = WorkflowApi(authoring.context, drafts=True)
|
api = WorkflowApi(authoring.context, drafts=True)
|
||||||
before = await draft_api.get_draft_workspace(
|
before = await draft_api.get_draft_workspace(
|
||||||
@@ -3956,7 +3956,6 @@ async def test_add_step_from_capability_rejects_existing_step_id(
|
|||||||
"interrupt",
|
"interrupt",
|
||||||
{"interrupt": {"kind": "review", "outcomes": ["submitted"]}},
|
{"interrupt": {"kind": "review", "outcomes": ["submitted"]}},
|
||||||
),
|
),
|
||||||
("join", {"join": {}}),
|
|
||||||
("end", {"end": {"outcome": "ok"}}),
|
("end", {"end": {"outcome": "ok"}}),
|
||||||
(
|
(
|
||||||
"when",
|
"when",
|
||||||
@@ -4067,7 +4066,7 @@ async def test_add_step_stale_revision_wins_over_content_preflight(
|
|||||||
before = await draft_api.get_draft_workspace(
|
before = await draft_api.get_draft_workspace(
|
||||||
workspace_id="draft_ws", include_draft=True
|
workspace_id="draft_ws", include_draft=True
|
||||||
)
|
)
|
||||||
step = TypeAdapter(DraftStep).validate_python({"join": {}})
|
step = TypeAdapter(DraftStep).validate_python({"end": {}})
|
||||||
|
|
||||||
result = await api.add_step(
|
result = await api.add_step(
|
||||||
workspace_id="draft_ws",
|
workspace_id="draft_ws",
|
||||||
|
|||||||
@@ -824,7 +824,6 @@ def test_wf_draft_add_help_lists_typed_step_commands_and_removes_flat_command()
|
|||||||
"capability",
|
"capability",
|
||||||
"interrupt",
|
"interrupt",
|
||||||
"foreach",
|
"foreach",
|
||||||
"join",
|
|
||||||
"end",
|
"end",
|
||||||
"when",
|
"when",
|
||||||
"choose",
|
"choose",
|
||||||
@@ -1531,7 +1530,7 @@ def test_wf_draft_add_foreach_builds_concurrent_policy(monkeypatch) -> None:
|
|||||||
assert calls[1]["step"].foreach.concurrent.max_outstanding == 20
|
assert calls[1]["step"].foreach.concurrent.max_outstanding == 20
|
||||||
|
|
||||||
|
|
||||||
def test_wf_draft_add_join_and_end_build_concrete_steps(monkeypatch) -> None:
|
def test_wf_draft_add_end_builds_concrete_step(monkeypatch) -> None:
|
||||||
calls: list[dict[str, Any]] = []
|
calls: list[dict[str, Any]] = []
|
||||||
|
|
||||||
class FakeHandlers:
|
class FakeHandlers:
|
||||||
@@ -1544,25 +1543,6 @@ def test_wf_draft_add_join_and_end_build_concrete_steps(monkeypatch) -> None:
|
|||||||
"wf_cli.commands.draft_add.load_cli_context", lambda _ctx: context
|
"wf_cli.commands.draft_add.load_cli_context", lambda _ctx: context
|
||||||
)
|
)
|
||||||
|
|
||||||
join_result = runner.invoke(
|
|
||||||
app,
|
|
||||||
[
|
|
||||||
"draft",
|
|
||||||
"add",
|
|
||||||
"join",
|
|
||||||
"workspace",
|
|
||||||
"--revision",
|
|
||||||
"1",
|
|
||||||
"--step",
|
|
||||||
"joined",
|
|
||||||
"--from-step",
|
|
||||||
"each_issue",
|
|
||||||
"--from-outcome",
|
|
||||||
"done",
|
|
||||||
"--route",
|
|
||||||
"done=finish",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
end_result = runner.invoke(
|
end_result = runner.invoke(
|
||||||
app,
|
app,
|
||||||
[
|
[
|
||||||
@@ -1571,7 +1551,7 @@ def test_wf_draft_add_join_and_end_build_concrete_steps(monkeypatch) -> None:
|
|||||||
"end",
|
"end",
|
||||||
"workspace",
|
"workspace",
|
||||||
"--revision",
|
"--revision",
|
||||||
"2",
|
"1",
|
||||||
"--step",
|
"--step",
|
||||||
"finish",
|
"finish",
|
||||||
"--outcome",
|
"--outcome",
|
||||||
@@ -1579,12 +1559,9 @@ def test_wf_draft_add_join_and_end_build_concrete_steps(monkeypatch) -> None:
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
assert join_result.exit_code == 0, join_result.output
|
|
||||||
assert end_result.exit_code == 0, end_result.output
|
assert end_result.exit_code == 0, end_result.output
|
||||||
assert calls[0]["step"].model_dump(mode="json") == {"join": {}}
|
assert calls[0]["step"].model_dump(mode="json") == {"end": {"outcome": "completed"}}
|
||||||
assert calls[0]["routes"] == {"done": "finish"}
|
assert calls[0]["routes"] is None
|
||||||
assert calls[1]["step"].model_dump(mode="json") == {"end": {"outcome": "completed"}}
|
|
||||||
assert calls[1]["routes"] is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_wf_draft_add_control_commands_reject_invalid_input_before_api_call(
|
def test_wf_draft_add_control_commands_reject_invalid_input_before_api_call(
|
||||||
@@ -1788,12 +1765,9 @@ def test_wf_draft_add_control_commands_reject_invalid_input_before_api_call(
|
|||||||
def test_wf_draft_add_control_command_help_is_type_specific() -> None:
|
def test_wf_draft_add_control_command_help_is_type_specific() -> None:
|
||||||
interrupt = runner.invoke(app, ["draft", "add", "interrupt", "--help"])
|
interrupt = runner.invoke(app, ["draft", "add", "interrupt", "--help"])
|
||||||
foreach = runner.invoke(app, ["draft", "add", "foreach", "--help"])
|
foreach = runner.invoke(app, ["draft", "add", "foreach", "--help"])
|
||||||
join = runner.invoke(app, ["draft", "add", "join", "--help"])
|
|
||||||
end = runner.invoke(app, ["draft", "add", "end", "--help"])
|
end = runner.invoke(app, ["draft", "add", "end", "--help"])
|
||||||
|
|
||||||
assert (
|
assert interrupt.exit_code == foreach.exit_code == end.exit_code == 0
|
||||||
interrupt.exit_code == foreach.exit_code == join.exit_code == end.exit_code == 0
|
|
||||||
)
|
|
||||||
assert "--request-schema-file" in interrupt.output
|
assert "--request-schema-file" in interrupt.output
|
||||||
assert "--resume-schema-file" in interrupt.output
|
assert "--resume-schema-file" in interrupt.output
|
||||||
assert "--request" in interrupt.output
|
assert "--request" in interrupt.output
|
||||||
@@ -1806,9 +1780,6 @@ def test_wf_draft_add_control_command_help_is_type_specific() -> None:
|
|||||||
assert "--item-error" in foreach.output
|
assert "--item-error" in foreach.output
|
||||||
assert "--collect-to" in foreach.output
|
assert "--collect-to" in foreach.output
|
||||||
assert "--request-schema-file" not in foreach.output
|
assert "--request-schema-file" not in foreach.output
|
||||||
assert "--from-step" in join.output
|
|
||||||
assert "--route" in join.output
|
|
||||||
assert "--request-schema-file" not in join.output
|
|
||||||
assert "--outcome" in end.output
|
assert "--outcome" in end.output
|
||||||
assert "--route" not in end.output
|
assert "--route" not in end.output
|
||||||
|
|
||||||
|
|||||||
@@ -771,14 +771,14 @@ def test_wf_remote_capability_free_draft_lifecycle(monkeypatch, tmp_path) -> Non
|
|||||||
[
|
[
|
||||||
"draft",
|
"draft",
|
||||||
"add",
|
"add",
|
||||||
"join",
|
"end",
|
||||||
"control_ws",
|
"control_ws",
|
||||||
"--revision",
|
"--revision",
|
||||||
"1",
|
"1",
|
||||||
"--step",
|
"--step",
|
||||||
"gate",
|
"finish",
|
||||||
"--route",
|
"--outcome",
|
||||||
"done=finish",
|
"error",
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"draft",
|
"draft",
|
||||||
@@ -787,26 +787,14 @@ def test_wf_remote_capability_free_draft_lifecycle(monkeypatch, tmp_path) -> Non
|
|||||||
"--revision",
|
"--revision",
|
||||||
"2",
|
"2",
|
||||||
"--step",
|
"--step",
|
||||||
"gate",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"draft",
|
|
||||||
"add",
|
|
||||||
"end",
|
|
||||||
"control_ws",
|
|
||||||
"--revision",
|
|
||||||
"3",
|
|
||||||
"--step",
|
|
||||||
"finish",
|
"finish",
|
||||||
"--outcome",
|
|
||||||
"error",
|
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"draft",
|
"draft",
|
||||||
"set-contract",
|
"set-contract",
|
||||||
"control_ws",
|
"control_ws",
|
||||||
"--revision",
|
"--revision",
|
||||||
"4",
|
"3",
|
||||||
"--outcome",
|
"--outcome",
|
||||||
"error",
|
"error",
|
||||||
],
|
],
|
||||||
@@ -824,10 +812,10 @@ def test_wf_remote_capability_free_draft_lifecycle(monkeypatch, tmp_path) -> Non
|
|||||||
assert '"status": "valid"' in results[-1].output
|
assert '"status": "valid"' in results[-1].output
|
||||||
assert inspected.exit_code == 0, inspected.output
|
assert inspected.exit_code == 0, inspected.output
|
||||||
payload = json.loads(inspected.output)
|
payload = json.loads(inspected.output)
|
||||||
assert payload["revision"] == 5
|
assert payload["revision"] == 4
|
||||||
assert payload["draft"]["start"] == "gate"
|
assert payload["draft"]["start"] == "finish"
|
||||||
assert payload["draft"]["outcomes"] == ["error"]
|
assert payload["draft"]["outcomes"] == ["error"]
|
||||||
assert set(payload["draft"]["steps"]) == {"gate", "finish"}
|
assert set(payload["draft"]["steps"]) == {"finish"}
|
||||||
|
|
||||||
|
|
||||||
def test_wf_draft_export_uses_remote_get_and_writes_only_draft(
|
def test_wf_draft_export_uses_remote_get_and_writes_only_draft(
|
||||||
@@ -2409,7 +2397,6 @@ def test_wf_draft_add_control_steps_use_generic_rpc_target(
|
|||||||
"completed_with_errors": "__end__",
|
"completed_with_errors": "__end__",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
("join", ["--route", "done=__end__"], {}, {"done": "__end__"}),
|
|
||||||
("end", ["--outcome", "ok"], {"outcome": "ok"}, None),
|
("end", ["--outcome", "ok"], {"outcome": "ok"}, None),
|
||||||
(
|
(
|
||||||
"when",
|
"when",
|
||||||
|
|||||||
@@ -67,7 +67,6 @@ def test_schema_compact_alias_is_json_outline_without_refs() -> None:
|
|||||||
"SubgraphNode",
|
"SubgraphNode",
|
||||||
"ConditionNode",
|
"ConditionNode",
|
||||||
"ForeachNode",
|
"ForeachNode",
|
||||||
"JoinNode",
|
|
||||||
"EndNode",
|
"EndNode",
|
||||||
"InterruptNode",
|
"InterruptNode",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ def test_generates_the_complete_real_workflow_contract() -> None:
|
|||||||
|
|
||||||
assert len(manifest["operations"]) == 72
|
assert len(manifest["operations"]) == 72
|
||||||
assert len({operation["method"] for operation in manifest["operations"]}) == 72
|
assert len({operation["method"] for operation in manifest["operations"]}) == 72
|
||||||
assert len(schemas) == 142
|
assert len(schemas) == 141
|
||||||
assert len(manifest["components"]["errors"]) == 1
|
assert len(manifest["components"]["errors"]) == 1
|
||||||
assert all(
|
assert all(
|
||||||
set(operation["result"]["schema"]) == {"$ref"}
|
set(operation["result"]["schema"]) == {"$ref"}
|
||||||
|
|||||||
@@ -2090,7 +2090,7 @@ def test_add_draft_step_params_reject_invalid_kind_and_route_source() -> None:
|
|||||||
"workspace_id": "ws",
|
"workspace_id": "ws",
|
||||||
"revision": 1,
|
"revision": 1,
|
||||||
"step_id": "bad",
|
"step_id": "bad",
|
||||||
"step": {"use": "demo.echo", "join": {}},
|
"step": {"use": "demo.echo", "end": {}},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -2104,7 +2104,7 @@ def test_add_draft_step_params_reject_invalid_kind_and_route_source() -> None:
|
|||||||
"workspace_id": "ws",
|
"workspace_id": "ws",
|
||||||
"revision": 1,
|
"revision": 1,
|
||||||
"step_id": "new",
|
"step_id": "new",
|
||||||
"step": {"join": {}},
|
"step": {"end": {}},
|
||||||
"incoming": incoming,
|
"incoming": incoming,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ from wf_api.surface import RouteSource, WorkflowDraftSurface
|
|||||||
from wf_artifacts.drafts.models import (
|
from wf_artifacts.drafts.models import (
|
||||||
DraftEndPayload,
|
DraftEndPayload,
|
||||||
DraftEndStep,
|
DraftEndStep,
|
||||||
DraftJoinStep,
|
|
||||||
DraftStep,
|
DraftStep,
|
||||||
)
|
)
|
||||||
from wf_core import END
|
from wf_core import END
|
||||||
@@ -573,32 +572,25 @@ async def test_rpc_client_builds_capability_free_draft_lifecycle(tmp_path) -> No
|
|||||||
workspace_id="control_first",
|
workspace_id="control_first",
|
||||||
name="control_first",
|
name="control_first",
|
||||||
)
|
)
|
||||||
joined = await client.add_step(
|
|
||||||
workspace_id="control_first",
|
|
||||||
revision=created["revision"],
|
|
||||||
step_id="gate",
|
|
||||||
step=DraftJoinStep(join={}),
|
|
||||||
routes={"done": "finish"},
|
|
||||||
)
|
|
||||||
started = await client.set_draft_start(
|
|
||||||
workspace_id="control_first",
|
|
||||||
revision=joined["revision"],
|
|
||||||
step_id="gate",
|
|
||||||
)
|
|
||||||
ended = await client.add_step(
|
ended = await client.add_step(
|
||||||
workspace_id="control_first",
|
workspace_id="control_first",
|
||||||
revision=started["revision"],
|
revision=created["revision"],
|
||||||
step_id="finish",
|
step_id="finish",
|
||||||
step=DraftEndStep(end=DraftEndPayload(outcome="error")),
|
step=DraftEndStep(end=DraftEndPayload(outcome="error")),
|
||||||
)
|
)
|
||||||
contracted = await client.set_draft_contract(
|
started = await client.set_draft_start(
|
||||||
workspace_id="control_first",
|
workspace_id="control_first",
|
||||||
revision=ended["revision"],
|
revision=ended["revision"],
|
||||||
|
step_id="finish",
|
||||||
|
)
|
||||||
|
contracted = await client.set_draft_contract(
|
||||||
|
workspace_id="control_first",
|
||||||
|
revision=started["revision"],
|
||||||
outcomes=("error",),
|
outcomes=("error",),
|
||||||
)
|
)
|
||||||
stale = await client.set_draft_start(
|
stale = await client.set_draft_start(
|
||||||
workspace_id="control_first",
|
workspace_id="control_first",
|
||||||
revision=ended["revision"],
|
revision=started["revision"],
|
||||||
step_id="finish",
|
step_id="finish",
|
||||||
)
|
)
|
||||||
validated = await client.validate_draft_workspace(workspace_id="control_first")
|
validated = await client.validate_draft_workspace(workspace_id="control_first")
|
||||||
@@ -609,22 +601,18 @@ async def test_rpc_client_builds_capability_free_draft_lifecycle(tmp_path) -> No
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert created["revision"] == 1
|
assert created["revision"] == 1
|
||||||
assert joined["revision"] == 2
|
assert ended["revision"] == 2
|
||||||
assert started["revision"] == 3
|
assert started["revision"] == 3
|
||||||
assert ended["revision"] == 4
|
assert contracted["revision"] == 4
|
||||||
assert contracted["revision"] == 5
|
|
||||||
assert stale["status"] == "conflict"
|
assert stale["status"] == "conflict"
|
||||||
assert stale["diagnostics"][0]["code"] == "revision_conflict"
|
assert stale["diagnostics"][0]["code"] == "revision_conflict"
|
||||||
assert validated["status"] == "valid"
|
assert validated["status"] == "valid"
|
||||||
assert "compiled_plan" in compiled
|
assert "compiled_plan" in compiled
|
||||||
assert compiled["compiled_plan"]["start"] == "gate"
|
assert compiled["compiled_plan"]["start"] == "finish"
|
||||||
draft = inspected.get("draft")
|
draft = inspected.get("draft")
|
||||||
assert draft is not None
|
assert draft is not None
|
||||||
assert draft["start"] == "gate"
|
assert draft["start"] == "finish"
|
||||||
assert draft["steps"] == {
|
assert draft["steps"] == {"finish": {"end": {"outcome": "error"}}}
|
||||||
"gate": {"join": {}},
|
|
||||||
"finish": {"end": {"outcome": "error"}},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def test_rpc_client_satisfies_draft_surface_static_shape() -> None:
|
def test_rpc_client_satisfies_draft_surface_static_shape() -> None:
|
||||||
@@ -1226,7 +1214,6 @@ async def test_rpc_client_preserves_nested_local_path_strings() -> None:
|
|||||||
"outcomes": ["submitted"],
|
"outcomes": ["submitted"],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
("join", TypeAdapter(DraftStep).validate_python({"join": {}}), {}),
|
|
||||||
("end", TypeAdapter(DraftStep).validate_python({"end": {}}), {"outcome": "ok"}),
|
("end", TypeAdapter(DraftStep).validate_python({"end": {}}), {"outcome": "ok"}),
|
||||||
(
|
(
|
||||||
"when",
|
"when",
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ const nodeColor = (data: WorkflowGraphNodeData): string => {
|
|||||||
return "#ef4444";
|
return "#ef4444";
|
||||||
case "foreach":
|
case "foreach":
|
||||||
return "#8b5cf6";
|
return "#8b5cf6";
|
||||||
case "join":
|
|
||||||
return "#10b981";
|
|
||||||
case "end":
|
case "end":
|
||||||
return "#6b7280";
|
return "#6b7280";
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ export type WorkflowGraphNodeKind =
|
|||||||
| "condition"
|
| "condition"
|
||||||
| "interrupt"
|
| "interrupt"
|
||||||
| "foreach"
|
| "foreach"
|
||||||
| "join"
|
|
||||||
| "end"
|
| "end"
|
||||||
| "unsupported";
|
| "unsupported";
|
||||||
|
|
||||||
@@ -94,8 +93,6 @@ const mapNodeKind = (type: unknown): WorkflowGraphNodeKind => {
|
|||||||
return "interrupt";
|
return "interrupt";
|
||||||
case "foreach":
|
case "foreach":
|
||||||
return "foreach";
|
return "foreach";
|
||||||
case "join":
|
|
||||||
return "join";
|
|
||||||
case "end":
|
case "end":
|
||||||
return "end";
|
return "end";
|
||||||
default:
|
default:
|
||||||
@@ -119,7 +116,6 @@ const buildLabel = (
|
|||||||
return typeof node.kind === "string" ? node.kind : "Interrupt";
|
return typeof node.kind === "string" ? node.kind : "Interrupt";
|
||||||
}
|
}
|
||||||
if (type === "foreach") return "For Each";
|
if (type === "foreach") return "For Each";
|
||||||
if (type === "join") return "Join";
|
|
||||||
if (type === "subgraph") {
|
if (type === "subgraph") {
|
||||||
const workflowRef = typeof node.workflow === "string" ? node.workflow : undefined;
|
const workflowRef = typeof node.workflow === "string" ? node.workflow : undefined;
|
||||||
if (workflowRef) {
|
if (workflowRef) {
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ describe("architectureCatalog", () => {
|
|||||||
"NodeUse",
|
"NodeUse",
|
||||||
"Condition",
|
"Condition",
|
||||||
"Foreach",
|
"Foreach",
|
||||||
"Join",
|
|
||||||
"Subgraph",
|
"Subgraph",
|
||||||
"Interrupt",
|
"Interrupt",
|
||||||
"End",
|
"End",
|
||||||
|
|||||||
@@ -524,7 +524,6 @@ export const architectureCatalog: FigureCatalogDefinition = defineFigureCatalog(
|
|||||||
"node-use": { x: 0, y: 0 },
|
"node-use": { x: 0, y: 0 },
|
||||||
"condition": { x: 300, y: 0 },
|
"condition": { x: 300, y: 0 },
|
||||||
"foreach": { x: 600, y: 0 },
|
"foreach": { x: 600, y: 0 },
|
||||||
"join": { x: 900, y: 0 },
|
|
||||||
"subgraph": { x: 150, y: 210 },
|
"subgraph": { x: 150, y: 210 },
|
||||||
"interrupt": { x: 450, y: 210 },
|
"interrupt": { x: 450, y: 210 },
|
||||||
"end": { x: 750, y: 210 },
|
"end": { x: 750, y: 210 },
|
||||||
@@ -542,7 +541,6 @@ export const architectureCatalog: FigureCatalogDefinition = defineFigureCatalog(
|
|||||||
},
|
},
|
||||||
{ id: "condition", label: "Condition", summary: "Route true or false", kind: "decision", icon: "branch", evidencePointer: "src/wf_core/models/steps.py" },
|
{ id: "condition", label: "Condition", summary: "Route true or false", kind: "decision", icon: "branch", evidencePointer: "src/wf_core/models/steps.py" },
|
||||||
{ id: "foreach", label: "Foreach", summary: "Create item frames and barrier", kind: "loop", icon: "repeat", evidencePointer: "src/wf_core/models/steps.py" },
|
{ id: "foreach", label: "Foreach", summary: "Create item frames and barrier", kind: "loop", icon: "repeat", evidencePointer: "src/wf_core/models/steps.py" },
|
||||||
{ id: "join", label: "Join", summary: "Close a branch or frame", kind: "operation", icon: "workflow", evidencePointer: "src/wf_core/models/steps.py" },
|
|
||||||
{ id: "subgraph", label: "Subgraph", summary: "Enter a prepared child workflow", kind: "boundary", icon: "layers", evidencePointer: "src/wf_core/models/steps.py" },
|
{ id: "subgraph", label: "Subgraph", summary: "Enter a prepared child workflow", kind: "boundary", icon: "layers", evidencePointer: "src/wf_core/models/steps.py" },
|
||||||
{ id: "interrupt", label: "Interrupt", summary: "Persist request and wait", kind: "boundary", icon: "pause", evidencePointer: "src/wf_core/models/steps.py", childFigureId: "interrupt-contract-detail" },
|
{ id: "interrupt", label: "Interrupt", summary: "Persist request and wait", kind: "boundary", icon: "pause", evidencePointer: "src/wf_core/models/steps.py", childFigureId: "interrupt-contract-detail" },
|
||||||
{ id: "end", label: "End", summary: "Project a terminal outcome", kind: "terminal", icon: "stop", evidencePointer: "src/wf_core/models/steps.py" },
|
{ id: "end", label: "End", summary: "Project a terminal outcome", kind: "terminal", icon: "stop", evidencePointer: "src/wf_core/models/steps.py" },
|
||||||
|
|||||||
@@ -57,7 +57,6 @@ const stepKind = (step: JsonRecord): string => {
|
|||||||
"choose",
|
"choose",
|
||||||
"match",
|
"match",
|
||||||
"foreach",
|
"foreach",
|
||||||
"join",
|
|
||||||
"end",
|
"end",
|
||||||
]) {
|
]) {
|
||||||
if (kind in step) return kind;
|
if (kind in step) return kind;
|
||||||
|
|||||||
@@ -464,7 +464,6 @@ export interface WorkflowContractMap {
|
|||||||
| DraftUseStep
|
| DraftUseStep
|
||||||
| DraftForeachStep
|
| DraftForeachStep
|
||||||
| DraftInterruptStep
|
| DraftInterruptStep
|
||||||
| DraftJoinStep
|
|
||||||
| DraftEndStep
|
| DraftEndStep
|
||||||
| DraftWhenStep
|
| DraftWhenStep
|
||||||
| DraftChooseStep
|
| DraftChooseStep
|
||||||
@@ -1867,17 +1866,6 @@ export interface SchemaRef {
|
|||||||
type?: string | string[] | null;
|
type?: string | string[] | null;
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Draft step that emits the current core join node.
|
|
||||||
*
|
|
||||||
* This interface was referenced by `WorkflowContractMap`'s JSON-Schema
|
|
||||||
* via the `definition` "DraftJoinStep".
|
|
||||||
*/
|
|
||||||
export interface DraftJoinStep {
|
|
||||||
join?: {
|
|
||||||
[k: string]: unknown;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Draft step that lowers to core `EndNode`.
|
* Draft step that lowers to core `EndNode`.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user