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
|
||||
**Barrier**, multiple writes to the same path without a reducer are conflicts,
|
||||
not last-writer-wins replacements.
|
||||
- Current `JoinNode` should not silently become a **Barrier**. It may be
|
||||
repurposed later only through an explicit design pass.
|
||||
- The pass-through `JoinNode` was removed. Future **Gather** behavior starts
|
||||
from an explicit barrier contract rather than inheriting placeholder
|
||||
semantics.
|
||||
- The first **Scheduler Foundation** implementation should create ready/block/wake
|
||||
seams only. **Lineage Isolation** and **Barrier** merge behavior are future
|
||||
concurrent semantics, not first-pass behavior.
|
||||
|
||||
@@ -1312,17 +1312,6 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"DraftJoinStep": {
|
||||
"additionalProperties": false,
|
||||
"description": "Draft step that emits the current core join node.",
|
||||
"properties": {
|
||||
"join": {
|
||||
"additionalProperties": true,
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"DraftMatchCase": {
|
||||
"additionalProperties": false,
|
||||
"description": "One ordered equality case in a draft match decision.",
|
||||
@@ -6209,9 +6198,6 @@
|
||||
{
|
||||
"$ref": "#/components/schemas/DraftInterruptStep"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/DraftJoinStep"
|
||||
},
|
||||
{
|
||||
"$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
|
||||
reducer-aware barrier commits already support concurrent foreach and native
|
||||
subgraphs. They do not yet define general graph-level fork/gather. The existing
|
||||
`JoinNode` is only a day-one marker: it immediately emits `done` and neither
|
||||
waits nor merges. Renaming it would falsely preserve semantics it never had.
|
||||
subgraphs. They do not yet define general graph-level fork/gather. A former
|
||||
`JoinNode` day-one marker immediately emitted `done`; it was removed after a
|
||||
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
|
||||
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
|
||||
without that use case and policy.
|
||||
|
||||
The current `JoinNode` will not be silently upgraded. Before implementation we
|
||||
will verify whether real persisted artifacts use it. With no real compatibility
|
||||
obligation, remove it and introduce `GatherNode` cleanly. If persisted callers
|
||||
exist, define an explicit migration rather than assigning barrier semantics to
|
||||
old `join` payloads.
|
||||
The placeholder `JoinNode` was removed rather than silently upgraded. The
|
||||
repository has no real persisted artifacts outside tests, so `GatherNode` can
|
||||
be introduced with its actual rendezvous and merge contract and no legacy wire
|
||||
alias.
|
||||
|
||||
## 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
|
||||
rather than additional meanings assigned to ordinary outcomes.
|
||||
|
||||
**Reuse or rename `JoinNode`.** Rejected as the default because the existing
|
||||
node is a pass-through marker with no barrier contract.
|
||||
**Reuse or rename the placeholder `JoinNode`.** Rejected and removed because
|
||||
the node was a pass-through marker with no barrier contract.
|
||||
|
||||
## Consequences
|
||||
|
||||
@@ -206,7 +206,6 @@ node is a pass-through marker with no barrier contract.
|
||||
continuation frame in each topology shape.
|
||||
- The trace representation for waiting and merging without excessive internal
|
||||
scheduler noise.
|
||||
- Whether any real persisted artifact requires migration from `JoinNode`.
|
||||
|
||||
This ADR extends the lineage and barrier direction established by
|
||||
[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
|
||||
|
||||
The next three slices build on the foreach control-region, scheduler, lineage,
|
||||
and barrier foundations in this order.
|
||||
The next three slices build on the foreach control-region, structured-context,
|
||||
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:
|
||||
|
||||
- [`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:
|
||||
Implement the proposed run-wide limit:
|
||||
|
||||
- [`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
|
||||
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
|
||||
|
||||
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
|
||||
- Concurrent foreach with activation barriers and reducer-aware lineage merges
|
||||
- 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
|
||||
- Python client reconstruction of capabilities, artifacts, deployments, and
|
||||
runs through the API
|
||||
|
||||
The current foreach return contract is
|
||||
[`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
|
||||
|
||||
|
||||
@@ -112,7 +112,6 @@ Exactly one step-kind key must be present. Allowed step-kind keys are:
|
||||
- `use`
|
||||
- `foreach`
|
||||
- `interrupt`
|
||||
- `join`
|
||||
|
||||
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`.
|
||||
|
||||
### `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
|
||||
|
||||
Most ordinary edges should be authored through `routes`:
|
||||
@@ -337,7 +323,8 @@ node_c.unreachable
|
||||
-> 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:
|
||||
|
||||
@@ -366,7 +353,7 @@ That should be handled as its own pass, not smuggled into this MCP draft change.
|
||||
Potential later core work:
|
||||
|
||||
- true graph-as-node / subgraph support
|
||||
- meaningful join semantics
|
||||
- explicit fork/gather semantics
|
||||
- future START-edge support if `Workflow.start` changes
|
||||
|
||||
### Draft `route` Sugar
|
||||
|
||||
@@ -157,7 +157,6 @@ Example:
|
||||
"SubgraphNode",
|
||||
"ConditionNode",
|
||||
"ForeachNode",
|
||||
"JoinNode",
|
||||
"EndNode",
|
||||
"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.
|
||||
|
||||
The service boundary is intentionally not capability-only. The current draft
|
||||
model also represents `end`, `condition`, `interrupt`, `foreach`, `join`,
|
||||
`when`, `choose`, and `match` steps, and core may gain more step kinds. This
|
||||
model also represents `end`, `condition`, `interrupt`, `foreach`, `when`,
|
||||
`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
|
||||
belong in `WorkflowDraftAuthoringApi` rather than a parallel authoring system.
|
||||
|
||||
|
||||
@@ -132,7 +132,6 @@ Node presentation distinguishes the existing core node kinds:
|
||||
- condition;
|
||||
- interrupt;
|
||||
- foreach;
|
||||
- join;
|
||||
- end and other control nodes.
|
||||
|
||||
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;
|
||||
8. the `lda_report_workflow` lifecycle is readable without scrolling raw JSON;
|
||||
9. frontend tests, typecheck, production build, and optional live smoke pass.
|
||||
|
||||
|
||||
@@ -56,7 +56,6 @@ deliberate authoring vocabulary that is later lowered by
|
||||
- `DraftUseStep`
|
||||
- `DraftForeachStep`
|
||||
- `DraftInterruptStep`
|
||||
- `DraftJoinStep`
|
||||
- `DraftEndStep`
|
||||
- `DraftWhenStep`
|
||||
- `DraftChooseStep`
|
||||
@@ -149,7 +148,6 @@ Declared top-level outcomes are:
|
||||
- `foreach`: `loop`, `done`, plus `completed_with_errors` when the item-error
|
||||
policy is `skip` or `collect`;
|
||||
- `interrupt`: `interrupt.outcomes`;
|
||||
- `join`: `done`;
|
||||
- `subgraph`: `subgraph.outcomes`.
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
## CLI Shape
|
||||
@@ -186,7 +184,6 @@ Register a focused Typer application beneath `wf draft`:
|
||||
wf draft add capability
|
||||
wf draft add interrupt
|
||||
wf draft add foreach
|
||||
wf draft add join
|
||||
wf draft add end
|
||||
wf draft add when
|
||||
wf draft add choose
|
||||
@@ -210,7 +207,6 @@ Variant-specific options are:
|
||||
`--resume LOCAL_SOURCE=STATE_TARGET`, and repeatable `--outcome`;
|
||||
- `foreach`: `--over`, `--as`, `--mode`, `--item-error`, optional
|
||||
`--collect-to`, `--max-active`, and `--max-outstanding`;
|
||||
- `join`: no variant-specific options;
|
||||
- `end`: `--outcome` and no `--route`;
|
||||
- `when`: `--condition-file`, `--then`, and `--otherwise`;
|
||||
- `choose`: `--clauses-file` containing the ordered clause array and
|
||||
@@ -269,7 +265,7 @@ modules need them; avoid a broad CLI refactor.
|
||||
|
||||
### CLI
|
||||
|
||||
- `wf draft add --help` lists all nine commands.
|
||||
- `wf draft add --help` lists all eight commands.
|
||||
- Per-command help exposes only relevant options.
|
||||
- Every command builds the expected `DraftStep`, incoming source, and routes.
|
||||
- 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;
|
||||
2. workflow Input, State, and Outcomes contract projections;
|
||||
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.
|
||||
|
||||
This document specifies only the first item.
|
||||
|
||||
@@ -56,7 +56,7 @@ Slice 6 includes:
|
||||
Slice 6 excludes:
|
||||
|
||||
- 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;
|
||||
- arbitrary schema inference from runtime values;
|
||||
- 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
|
||||
incoming edge, scope id, lineage id, and parent lineage id. Foreach iteration
|
||||
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.
|
||||
|
||||
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`
|
||||
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
|
||||
within a run.
|
||||
@@ -256,7 +256,7 @@ with the run.
|
||||
### Core counting
|
||||
|
||||
- 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.
|
||||
- A transition to legacy `END` does not create an extra attempt.
|
||||
- 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:
|
||||
|
||||
```text
|
||||
capability interrupt foreach join end
|
||||
capability interrupt foreach end
|
||||
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
|
||||
literal JSON values. Invalid draft step shapes are rejected instead of silently
|
||||
compiling to `join`.
|
||||
literal JSON values. Invalid draft step shapes are rejected instead of being
|
||||
silently replaced with a placeholder step.
|
||||
|
||||
Generated MCP tool wrappers are intentionally naive. They normally expose both
|
||||
`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__`
|
||||
destination remains the shorthand for public workflow outcome `ok`.
|
||||
|
||||
### `join`
|
||||
|
||||
Joins control flow.
|
||||
|
||||
```json
|
||||
{
|
||||
"join": {}
|
||||
}
|
||||
```
|
||||
|
||||
### `when`
|
||||
|
||||
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
|
||||
wf draft create report_ws --name report_workflow
|
||||
wf draft add join report_ws --revision 1 --step gate --route done=finish
|
||||
wf draft set-start report_ws --revision 2 --step gate
|
||||
wf draft add end report_ws --revision 3 --step finish --outcome error
|
||||
wf draft set-contract report_ws --revision 4 --outcome error
|
||||
wf draft add end report_ws --revision 1 --step finish --outcome error
|
||||
wf draft set-start report_ws --revision 2 --step finish
|
||||
wf draft set-contract report_ws --revision 3 --outcome error
|
||||
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
|
||||
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
|
||||
and subgraphs preserve JSON Schema boundary contracts. Invalid intermediate
|
||||
drafts remain saveable in the workspace but must pass `wf draft validate`
|
||||
|
||||
@@ -16,7 +16,6 @@ from wf_artifacts.drafts.models import (
|
||||
DraftEndStep,
|
||||
DraftForeachStep,
|
||||
DraftInterruptStep,
|
||||
DraftJoinStep,
|
||||
DraftMatchStep,
|
||||
DraftStep,
|
||||
DraftSubgraphStep,
|
||||
@@ -353,8 +352,6 @@ class WorkflowDraftAuthoringApi:
|
||||
return outcomes
|
||||
if isinstance(step, DraftInterruptStep):
|
||||
return set(step.interrupt.outcomes)
|
||||
if isinstance(step, DraftJoinStep):
|
||||
return {"done"}
|
||||
if isinstance(step, DraftSubgraphStep):
|
||||
return set(step.subgraph.outcomes)
|
||||
if isinstance(
|
||||
|
||||
@@ -11,7 +11,6 @@ from .models import (
|
||||
DraftEndStep,
|
||||
DraftForeachStep,
|
||||
DraftInterruptStep,
|
||||
DraftJoinStep,
|
||||
DraftMatchCase,
|
||||
DraftMatchStep,
|
||||
DraftSubgraphStep,
|
||||
@@ -27,7 +26,6 @@ __all__ = [
|
||||
"DraftEndStep",
|
||||
"DraftForeachStep",
|
||||
"DraftInterruptStep",
|
||||
"DraftJoinStep",
|
||||
"DraftMatchCase",
|
||||
"DraftMatchStep",
|
||||
"DraftSubgraphStep",
|
||||
|
||||
@@ -4,7 +4,7 @@ from typing import Any
|
||||
|
||||
from wf_authoring import WorkflowBuilder
|
||||
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 .models import (
|
||||
@@ -12,7 +12,6 @@ from .models import (
|
||||
DraftEndStep,
|
||||
DraftForeachStep,
|
||||
DraftInterruptStep,
|
||||
DraftJoinStep,
|
||||
DraftMatchStep,
|
||||
DraftStep,
|
||||
DraftSubgraphStep,
|
||||
@@ -84,10 +83,6 @@ def _add_step(builder: WorkflowBuilder, step_id: str, step: DraftStep):
|
||||
return builder.interrupt(
|
||||
**interrupt_kwargs,
|
||||
)
|
||||
if isinstance(step, DraftJoinStep):
|
||||
node = JoinNode(id=step_id, type="join")
|
||||
builder.nodes.append(node)
|
||||
return node
|
||||
if isinstance(step, DraftEndStep):
|
||||
return builder.end(step.end.outcome, id=step_id)
|
||||
if isinstance(step, DraftWhenStep):
|
||||
|
||||
@@ -220,14 +220,6 @@ class DraftSubgraphStep(BaseModel):
|
||||
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):
|
||||
"""Payload for one explicit workflow terminal outcome."""
|
||||
|
||||
@@ -323,7 +315,6 @@ DraftStep = (
|
||||
DraftUseStep
|
||||
| DraftForeachStep
|
||||
| DraftInterruptStep
|
||||
| DraftJoinStep
|
||||
| DraftEndStep
|
||||
| DraftWhenStep
|
||||
| DraftChooseStep
|
||||
|
||||
@@ -8,7 +8,6 @@ from wf_core import (
|
||||
EndNode,
|
||||
ForeachNode,
|
||||
InterruptNode,
|
||||
JoinNode,
|
||||
NodeUse,
|
||||
SubgraphNode,
|
||||
)
|
||||
@@ -16,14 +15,7 @@ from wf_core import (
|
||||
from ..nodes import NodeSpec
|
||||
|
||||
StepRef: TypeAlias = (
|
||||
str
|
||||
| NodeUse
|
||||
| SubgraphNode
|
||||
| ConditionNode
|
||||
| ForeachNode
|
||||
| InterruptNode
|
||||
| JoinNode
|
||||
| EndNode
|
||||
str | NodeUse | SubgraphNode | ConditionNode | ForeachNode | InterruptNode | EndNode
|
||||
)
|
||||
"""A reference to a step, which can be either a string id or a node object
|
||||
that should be auto-used."""
|
||||
|
||||
@@ -16,7 +16,6 @@ from wf_artifacts.drafts.models import (
|
||||
DraftForeachStep,
|
||||
DraftInterruptPayload,
|
||||
DraftInterruptStep,
|
||||
DraftJoinStep,
|
||||
DraftMatchCase,
|
||||
DraftMatchPayload,
|
||||
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")
|
||||
def add_end_step(
|
||||
ctx: typer.Context,
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ def write_json_file(path: Path, payload: Any, *, force: bool) -> None:
|
||||
output.write("\n")
|
||||
except FileExistsError as exc:
|
||||
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
|
||||
except OSError as exc:
|
||||
raise CliInputError(f"could not write file {path!s}: {exc}") from exc
|
||||
|
||||
@@ -11,7 +11,6 @@ from .models import (
|
||||
InputPathBinding,
|
||||
InputValueBinding,
|
||||
InterruptNode,
|
||||
JoinNode,
|
||||
JsonValue,
|
||||
LiteralExpression,
|
||||
NodeDef,
|
||||
@@ -89,7 +88,6 @@ __all__ = [
|
||||
"InputValueBinding",
|
||||
"InterruptRequest",
|
||||
"InterruptRoute",
|
||||
"JoinNode",
|
||||
"JsonValue",
|
||||
"LiteralExpression",
|
||||
"NodeDef",
|
||||
|
||||
@@ -30,7 +30,6 @@ from wf_core.models.steps import (
|
||||
ForeachItemErrorPolicy,
|
||||
ForeachNode,
|
||||
InterruptNode,
|
||||
JoinNode,
|
||||
NodeUse,
|
||||
Step,
|
||||
SubgraphNode,
|
||||
@@ -54,7 +53,6 @@ __all__ = [
|
||||
"InputExpressionBinding",
|
||||
"InputPathBinding",
|
||||
"InputValueBinding",
|
||||
"JoinNode",
|
||||
"JsonValue",
|
||||
"LiteralExpression",
|
||||
"LiteralOperand",
|
||||
|
||||
@@ -293,13 +293,6 @@ class ForeachNode(BaseModel):
|
||||
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):
|
||||
"""Explicit workflow terminal that sets the workflow-level outcome.
|
||||
|
||||
@@ -411,13 +404,7 @@ class InterruptNode(BaseModel):
|
||||
|
||||
|
||||
Step = Annotated[
|
||||
NodeUse
|
||||
| SubgraphNode
|
||||
| ConditionNode
|
||||
| ForeachNode
|
||||
| JoinNode
|
||||
| EndNode
|
||||
| InterruptNode,
|
||||
NodeUse | SubgraphNode | ConditionNode | ForeachNode | EndNode | InterruptNode,
|
||||
Field(discriminator="type"),
|
||||
]
|
||||
"""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(
|
||||
run: RunState,
|
||||
step: InterruptNode,
|
||||
|
||||
@@ -10,7 +10,6 @@ from wf_core.models.steps import (
|
||||
EndNode,
|
||||
ForeachNode,
|
||||
InterruptNode,
|
||||
JoinNode,
|
||||
NodeUse,
|
||||
SubgraphNode,
|
||||
)
|
||||
@@ -22,7 +21,6 @@ from wf_core.runtime.ops.foreach import step_foreach
|
||||
from wf_core.runtime.ops.handlers import (
|
||||
handle_condition_step,
|
||||
handle_interrupt_step,
|
||||
handle_join_step,
|
||||
)
|
||||
from wf_core.runtime.ops.index import WorkflowIndex, build_workflow_index
|
||||
from wf_core.runtime.ops.merges import ReducerDefinition
|
||||
@@ -149,8 +147,6 @@ def step_workflow(
|
||||
raise
|
||||
elif isinstance(step, ConditionNode):
|
||||
step_result = handle_condition_step(run, step)
|
||||
elif isinstance(step, JoinNode):
|
||||
step_result = handle_join_step()
|
||||
elif isinstance(step, EndNode):
|
||||
return complete_end_step(
|
||||
run=run,
|
||||
@@ -264,8 +260,6 @@ async def step_workflow_async(
|
||||
raise
|
||||
elif isinstance(step, ConditionNode):
|
||||
step_result = handle_condition_step(run, step)
|
||||
elif isinstance(step, JoinNode):
|
||||
step_result = handle_join_step()
|
||||
elif isinstance(step, EndNode):
|
||||
return complete_end_step(
|
||||
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"}:
|
||||
outcomes.add("completed_with_errors")
|
||||
return outcomes
|
||||
if step.type == "join":
|
||||
return {"done"}
|
||||
if isinstance(step, EndNode):
|
||||
return set()
|
||||
if isinstance(step, InterruptNode):
|
||||
|
||||
@@ -226,7 +226,7 @@ def test_draft_step_requires_exactly_one_kind_key() -> None:
|
||||
assert isinstance(steps, dict)
|
||||
echo = steps["echo"]
|
||||
assert isinstance(echo, dict)
|
||||
echo["join"] = {}
|
||||
echo["end"] = {"outcome": "ok"}
|
||||
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
WorkflowDraft.model_validate(draft)
|
||||
|
||||
@@ -675,7 +675,7 @@ async def test_update_capability_step_rejects_wrong_kind_and_invalid_input_atomi
|
||||
register_echo=True,
|
||||
)
|
||||
draft = _echo_draft()
|
||||
draft["steps"]["joined"] = {"join": ["echo"]}
|
||||
draft["steps"]["joined"] = {"end": {}}
|
||||
await draft_api.create_draft_workspace(workspace_id="echo", draft=draft)
|
||||
before = await draft_api.get_draft_workspace(
|
||||
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(
|
||||
workspace_id="non-capability-output-step",
|
||||
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(
|
||||
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(
|
||||
workspace_id="stale-output-non-capability",
|
||||
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(
|
||||
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,
|
||||
)
|
||||
draft = _structured_report_draft()
|
||||
draft["steps"]["report"] = {"join": {}}
|
||||
draft["steps"]["report"] = {"end": {}}
|
||||
await draft_api.create_draft_workspace(workspace_id="non_capability", draft=draft)
|
||||
api = WorkflowApi(authoring.context, drafts=True)
|
||||
before = await draft_api.get_draft_workspace(
|
||||
@@ -3956,7 +3956,6 @@ async def test_add_step_from_capability_rejects_existing_step_id(
|
||||
"interrupt",
|
||||
{"interrupt": {"kind": "review", "outcomes": ["submitted"]}},
|
||||
),
|
||||
("join", {"join": {}}),
|
||||
("end", {"end": {"outcome": "ok"}}),
|
||||
(
|
||||
"when",
|
||||
@@ -4067,7 +4066,7 @@ async def test_add_step_stale_revision_wins_over_content_preflight(
|
||||
before = await draft_api.get_draft_workspace(
|
||||
workspace_id="draft_ws", include_draft=True
|
||||
)
|
||||
step = TypeAdapter(DraftStep).validate_python({"join": {}})
|
||||
step = TypeAdapter(DraftStep).validate_python({"end": {}})
|
||||
|
||||
result = await api.add_step(
|
||||
workspace_id="draft_ws",
|
||||
|
||||
@@ -824,7 +824,6 @@ def test_wf_draft_add_help_lists_typed_step_commands_and_removes_flat_command()
|
||||
"capability",
|
||||
"interrupt",
|
||||
"foreach",
|
||||
"join",
|
||||
"end",
|
||||
"when",
|
||||
"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
|
||||
|
||||
|
||||
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]] = []
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
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(
|
||||
app,
|
||||
[
|
||||
@@ -1571,7 +1551,7 @@ def test_wf_draft_add_join_and_end_build_concrete_steps(monkeypatch) -> None:
|
||||
"end",
|
||||
"workspace",
|
||||
"--revision",
|
||||
"2",
|
||||
"1",
|
||||
"--step",
|
||||
"finish",
|
||||
"--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 calls[0]["step"].model_dump(mode="json") == {"join": {}}
|
||||
assert calls[0]["routes"] == {"done": "finish"}
|
||||
assert calls[1]["step"].model_dump(mode="json") == {"end": {"outcome": "completed"}}
|
||||
assert calls[1]["routes"] is None
|
||||
assert calls[0]["step"].model_dump(mode="json") == {"end": {"outcome": "completed"}}
|
||||
assert calls[0]["routes"] is None
|
||||
|
||||
|
||||
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:
|
||||
interrupt = runner.invoke(app, ["draft", "add", "interrupt", "--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"])
|
||||
|
||||
assert (
|
||||
interrupt.exit_code == foreach.exit_code == join.exit_code == end.exit_code == 0
|
||||
)
|
||||
assert interrupt.exit_code == foreach.exit_code == end.exit_code == 0
|
||||
assert "--request-schema-file" in interrupt.output
|
||||
assert "--resume-schema-file" 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 "--collect-to" 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 "--route" not in end.output
|
||||
|
||||
|
||||
@@ -771,14 +771,14 @@ def test_wf_remote_capability_free_draft_lifecycle(monkeypatch, tmp_path) -> Non
|
||||
[
|
||||
"draft",
|
||||
"add",
|
||||
"join",
|
||||
"end",
|
||||
"control_ws",
|
||||
"--revision",
|
||||
"1",
|
||||
"--step",
|
||||
"gate",
|
||||
"--route",
|
||||
"done=finish",
|
||||
"finish",
|
||||
"--outcome",
|
||||
"error",
|
||||
],
|
||||
[
|
||||
"draft",
|
||||
@@ -787,26 +787,14 @@ def test_wf_remote_capability_free_draft_lifecycle(monkeypatch, tmp_path) -> Non
|
||||
"--revision",
|
||||
"2",
|
||||
"--step",
|
||||
"gate",
|
||||
],
|
||||
[
|
||||
"draft",
|
||||
"add",
|
||||
"end",
|
||||
"control_ws",
|
||||
"--revision",
|
||||
"3",
|
||||
"--step",
|
||||
"finish",
|
||||
"--outcome",
|
||||
"error",
|
||||
],
|
||||
[
|
||||
"draft",
|
||||
"set-contract",
|
||||
"control_ws",
|
||||
"--revision",
|
||||
"4",
|
||||
"3",
|
||||
"--outcome",
|
||||
"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 inspected.exit_code == 0, inspected.output
|
||||
payload = json.loads(inspected.output)
|
||||
assert payload["revision"] == 5
|
||||
assert payload["draft"]["start"] == "gate"
|
||||
assert payload["revision"] == 4
|
||||
assert payload["draft"]["start"] == "finish"
|
||||
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(
|
||||
@@ -2409,7 +2397,6 @@ def test_wf_draft_add_control_steps_use_generic_rpc_target(
|
||||
"completed_with_errors": "__end__",
|
||||
},
|
||||
),
|
||||
("join", ["--route", "done=__end__"], {}, {"done": "__end__"}),
|
||||
("end", ["--outcome", "ok"], {"outcome": "ok"}, None),
|
||||
(
|
||||
"when",
|
||||
|
||||
@@ -67,7 +67,6 @@ def test_schema_compact_alias_is_json_outline_without_refs() -> None:
|
||||
"SubgraphNode",
|
||||
"ConditionNode",
|
||||
"ForeachNode",
|
||||
"JoinNode",
|
||||
"EndNode",
|
||||
"InterruptNode",
|
||||
]
|
||||
|
||||
@@ -92,7 +92,7 @@ def test_generates_the_complete_real_workflow_contract() -> None:
|
||||
|
||||
assert len(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 all(
|
||||
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",
|
||||
"revision": 1,
|
||||
"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",
|
||||
"revision": 1,
|
||||
"step_id": "new",
|
||||
"step": {"join": {}},
|
||||
"step": {"end": {}},
|
||||
"incoming": incoming,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -13,7 +13,6 @@ from wf_api.surface import RouteSource, WorkflowDraftSurface
|
||||
from wf_artifacts.drafts.models import (
|
||||
DraftEndPayload,
|
||||
DraftEndStep,
|
||||
DraftJoinStep,
|
||||
DraftStep,
|
||||
)
|
||||
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",
|
||||
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(
|
||||
workspace_id="control_first",
|
||||
revision=started["revision"],
|
||||
revision=created["revision"],
|
||||
step_id="finish",
|
||||
step=DraftEndStep(end=DraftEndPayload(outcome="error")),
|
||||
)
|
||||
contracted = await client.set_draft_contract(
|
||||
started = await client.set_draft_start(
|
||||
workspace_id="control_first",
|
||||
revision=ended["revision"],
|
||||
step_id="finish",
|
||||
)
|
||||
contracted = await client.set_draft_contract(
|
||||
workspace_id="control_first",
|
||||
revision=started["revision"],
|
||||
outcomes=("error",),
|
||||
)
|
||||
stale = await client.set_draft_start(
|
||||
workspace_id="control_first",
|
||||
revision=ended["revision"],
|
||||
revision=started["revision"],
|
||||
step_id="finish",
|
||||
)
|
||||
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 joined["revision"] == 2
|
||||
assert ended["revision"] == 2
|
||||
assert started["revision"] == 3
|
||||
assert ended["revision"] == 4
|
||||
assert contracted["revision"] == 5
|
||||
assert contracted["revision"] == 4
|
||||
assert stale["status"] == "conflict"
|
||||
assert stale["diagnostics"][0]["code"] == "revision_conflict"
|
||||
assert validated["status"] == "valid"
|
||||
assert "compiled_plan" in compiled
|
||||
assert compiled["compiled_plan"]["start"] == "gate"
|
||||
assert compiled["compiled_plan"]["start"] == "finish"
|
||||
draft = inspected.get("draft")
|
||||
assert draft is not None
|
||||
assert draft["start"] == "gate"
|
||||
assert draft["steps"] == {
|
||||
"gate": {"join": {}},
|
||||
"finish": {"end": {"outcome": "error"}},
|
||||
}
|
||||
assert draft["start"] == "finish"
|
||||
assert draft["steps"] == {"finish": {"end": {"outcome": "error"}}}
|
||||
|
||||
|
||||
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"],
|
||||
},
|
||||
),
|
||||
("join", TypeAdapter(DraftStep).validate_python({"join": {}}), {}),
|
||||
("end", TypeAdapter(DraftStep).validate_python({"end": {}}), {"outcome": "ok"}),
|
||||
(
|
||||
"when",
|
||||
|
||||
@@ -31,8 +31,6 @@ const nodeColor = (data: WorkflowGraphNodeData): string => {
|
||||
return "#ef4444";
|
||||
case "foreach":
|
||||
return "#8b5cf6";
|
||||
case "join":
|
||||
return "#10b981";
|
||||
case "end":
|
||||
return "#6b7280";
|
||||
default:
|
||||
|
||||
@@ -7,7 +7,6 @@ export type WorkflowGraphNodeKind =
|
||||
| "condition"
|
||||
| "interrupt"
|
||||
| "foreach"
|
||||
| "join"
|
||||
| "end"
|
||||
| "unsupported";
|
||||
|
||||
@@ -94,8 +93,6 @@ const mapNodeKind = (type: unknown): WorkflowGraphNodeKind => {
|
||||
return "interrupt";
|
||||
case "foreach":
|
||||
return "foreach";
|
||||
case "join":
|
||||
return "join";
|
||||
case "end":
|
||||
return "end";
|
||||
default:
|
||||
@@ -119,7 +116,6 @@ const buildLabel = (
|
||||
return typeof node.kind === "string" ? node.kind : "Interrupt";
|
||||
}
|
||||
if (type === "foreach") return "For Each";
|
||||
if (type === "join") return "Join";
|
||||
if (type === "subgraph") {
|
||||
const workflowRef = typeof node.workflow === "string" ? node.workflow : undefined;
|
||||
if (workflowRef) {
|
||||
|
||||
@@ -46,7 +46,6 @@ describe("architectureCatalog", () => {
|
||||
"NodeUse",
|
||||
"Condition",
|
||||
"Foreach",
|
||||
"Join",
|
||||
"Subgraph",
|
||||
"Interrupt",
|
||||
"End",
|
||||
|
||||
@@ -524,7 +524,6 @@ export const architectureCatalog: FigureCatalogDefinition = defineFigureCatalog(
|
||||
"node-use": { x: 0, y: 0 },
|
||||
"condition": { x: 300, y: 0 },
|
||||
"foreach": { x: 600, y: 0 },
|
||||
"join": { x: 900, y: 0 },
|
||||
"subgraph": { x: 150, y: 210 },
|
||||
"interrupt": { x: 450, 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: "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: "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" },
|
||||
|
||||
@@ -57,7 +57,6 @@ const stepKind = (step: JsonRecord): string => {
|
||||
"choose",
|
||||
"match",
|
||||
"foreach",
|
||||
"join",
|
||||
"end",
|
||||
]) {
|
||||
if (kind in step) return kind;
|
||||
|
||||
@@ -464,7 +464,6 @@ export interface WorkflowContractMap {
|
||||
| DraftUseStep
|
||||
| DraftForeachStep
|
||||
| DraftInterruptStep
|
||||
| DraftJoinStep
|
||||
| DraftEndStep
|
||||
| DraftWhenStep
|
||||
| DraftChooseStep
|
||||
@@ -1867,17 +1866,6 @@ export interface SchemaRef {
|
||||
type?: string | string[] | null;
|
||||
[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`.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user