Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions packages/core/src/plugin/command/dag-flow.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ execution; the router still owns any material Decision Checkpoint.

Prefer composable blocks for a fresh flow. Load
`workflow(action="guide", topic="blocks")` only if the block contract is not
already in context. Use inline `spec` for one-off work; use `spec_path` only
when a saved workflow already matches or persistence was requested. Preserve
already in context. Write one-off work to a task-local YAML file and pass its
`spec_path`; a matching saved workflow name is also a valid `spec_path`. Preserve
the task, user constraints, named roles, read-only limits, acceptance checks,
and confirmed decisions in the objective and block instructions.

Call the workflow tool with `action=start` in the first response after the
route is ready. Printing a plan, JSON, or YAML does not start it. Never invent
Validate the YAML path, then call the workflow tool with `action=start` in the
first response after the route is ready. Printing a plan or YAML does not start it. Never invent
worker types or model IDs. If a configured capability or model is unavailable,
report the actual gap and leave the workflow uncreated.

Expand Down
13 changes: 6 additions & 7 deletions packages/core/src/plugin/command/workflow-blocks.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Composable Workflow Blocks

Blocks are the high-level interface for assembling a one-off workflow. The
tool compiles them into ordinary durable DAG nodes before validation and
persistence. Existing node-based YAML remains compatible.
Blocks are the high-level interface for assembling a one-off workflow YAML
file. The tool compiles them into ordinary durable DAG nodes before validation
and persistence. Existing node-based YAML remains compatible.

## Shape

Expand Down Expand Up @@ -33,10 +33,9 @@ config:
depends_on: [verify]
```

The parameter schema owns the exact block field shapes; the tool rejects
unknown or missing fields by name, and `workflow(action="validate")` reports
each field error with its path. This guide covers semantics and constraints
only — compose blocks against the schema, not against prose.
This guide owns the author-written block fields and semantics. The action
schema stays shallow and accepts only `spec_path`; the YAML validator rejects
unknown or missing graph fields by name and reports each error with its path.

`objective` is required and is injected into every generated node. Use blocks
or nodes, never both. Block IDs use letters, numbers, underscores, and hyphens.
Expand Down
16 changes: 9 additions & 7 deletions packages/core/src/plugin/command/workflow-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,13 @@ instructions specialize the task and never name external Skills.

When a saved route matches the topology, read it, retarget its objective and
block instructions, and prune or add justified blocks before starting the
edited inline spec. Start `spec_path` directly only when its target already
matches exactly. Use low-level nodes only for bindings, conditions, output
schemas, or lifecycle metadata blocks cannot express.
edited YAML file. Start the saved `spec_path` directly only when its target
already matches exactly. Use low-level nodes only for bindings, conditions,
output schemas, or lifecycle metadata blocks cannot express.

Validate the composed or edited spec before start. Fix every diagnostic and
validate again; validation creates no workflow. A successful start returns the
Write the composed or edited graph to YAML and validate that `spec_path` before
start. Fix every diagnostic in the same file and validate again; validation
creates no workflow. A successful start returns the
exact workflow ID. The parent owns the brief, graph, user interaction,
checkpoints, controls, and final report; children own bounded executable work.
End after start and let the workflow wake the parent. Do not poll merely to
Expand All @@ -74,5 +75,6 @@ wait, and never claim an unstarted graph is running.
- `guide(topic="policy")`: gates, recovery, and bounded repair.
- `guide(topic="patterns")`: larger domain playbooks.

The tool parameter schema owns required fields and exclusivity; author calls
from that schema rather than reconstructed prose.
The tool parameter schema owns action fields and requires `spec_path`; the
on-demand block/interface guides own author-written YAML fields, and validation
is the final authority for the file.
41 changes: 21 additions & 20 deletions packages/core/src/plugin/command/workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ as independent workstreams, cross-domain uncertainty, high blast radius,
conflicting constraints, evidence gathering, or multiple verification
perspectives.

For a one-off graph, pass `spec` inline on `start`, `extend`, or
`control(replan)`. This is the default: do not create a transient YAML file.
Use `spec_path` only for a saved workflow name, a reusable workflow file, or an
explicitly requested file-backed spec. Exactly one of `spec` and `spec_path` is
valid. After a validation failure, correct the same source and retry the call.
Before `start`, `extend`, `control(replan)`, or `validate`, write the graph to a
`.yaml` or `.yml` file and pass only `spec_path`. A one-off graph may use a
task-local file such as `.opencode/.dag-specs/<name>.yaml`; it does not need to
become a saved library workflow. After a validation failure, edit that same
file and retry with the same path.

Before a deep start, qualify the request interactively in the parent session.
The start spec places `mode: deep`, a versioned `READY` or informed `WAIVED`
Expand Down Expand Up @@ -94,10 +94,11 @@ Prefer a saved workflow when the user names a recurring procedure ("run the
code review workflow") and the saved target/inputs already match: starting it
is one call, and its graph has already been reviewed. When only its topology
matches, call `{ action: "read", spec_path: "code-review" }`, retarget its objective and block instructions to the current task, prune or add lanes, then
start that edited value as an inline spec. `read` never starts a workflow.
Compose a fresh inline `spec` when the task is one-off or no reference fits.
To turn a working one-off spec into a saved workflow, persist it as YAML in one
of the two workflow-library directories under a descriptive name.
write the edited value to a task-local YAML file and start its `spec_path`.
`read` never starts a workflow. Compose a fresh task-local YAML file when the
task is one-off or no reference fits. To turn a working one-off spec into a
saved workflow, move it into one of the two workflow-library directories under
a descriptive name.

## Orchestration Lifecycle

Expand Down Expand Up @@ -151,9 +152,9 @@ the workflow uncreated so the user can configure a model and retry.
## Collaboration Patterns

Four structural patterns cover the common cases. Real workflows often combine
them. Every block below shows the object shape for inline `spec`; pass the
selected shape with `{ action: "start", spec: { ... } }`. Persist it as YAML
and use `spec_path` only when the workflow itself should be saved.
them. Every block below is YAML file content. Save the selected shape, validate
it with `{ action: "validate", spec_path: "<file>.yaml" }`, then start it with
`{ action: "start", spec_path: "<file>.yaml" }`.

### 1. Staged Pipeline with Gate

Expand Down Expand Up @@ -540,10 +541,9 @@ All nodes share the same workspace. Write conflicts are an orchestration concern
### Actions

**start** — Create a workflow from `config` and optional `title`, `mode`, and
admission input. For a one-off graph call
`{ action: "start", spec: { config: { ... } } }`. Use `spec_path` for a saved
workflow name (`{ action: "start", spec_path: "code-review" }`) or an explicit
YAML path.
admission input stored in YAML. Pass a task-local YAML path for one-off work or
a saved workflow name such as
`{ action: "start", spec_path: "code-review" }`.
Returns the workflow ID. Nodes declare `depends_on` (node IDs); layers and
execution order are computed automatically.

Expand All @@ -553,14 +553,15 @@ not running workflows; use `status` for a workflow's live state.

**read** — Return one saved workflow as structured JSON without starting it.
Pass `spec_path`, then retarget generic objectives and block instructions in
the parent before using the edited result as an inline `start` spec.
the parent, write the edited result to YAML, and start that file by path.

**extend** — Add nodes to a running workflow. Existing nodes are unaffected;
new nodes are immediately eligible for scheduling if their dependencies are
met. It also accepts a genuinely additive wave after a reporting leaf
checkpoint naturally completed the current graph; an early
`control(complete)` workflow remains terminal. Put the new nodes under `spec.nodes`,
then call `{ action: "extend", workflow_id: "dag_...", spec: { nodes: [...] } }`.
`control(complete)` workflow remains terminal. Put the new nodes under `nodes`
in a YAML file, then call
`{ action: "extend", workflow_id: "dag_...", spec_path: "extend.yaml" }`.

**status** — Read the durable state of one workflow and all of its nodes. Pass `workflow_id`. Use it when the user explicitly asks for current state or once before a decision that requires fresh state, such as replan/control. Do not poll a running workflow merely to wait: node reports and terminal outcomes wake the parent session automatically.

Expand All @@ -576,7 +577,7 @@ omitted content from its preview.
- `pause` — let running nodes finish, don't spawn new ones (pause does NOT stop nodes that are already running). On a cancel/replan intent, always pause FIRST: it needs no fragment and freezes scheduling while you compose the replan, so the graph cannot terminalize under you.
- `resume` — resume scheduling
- `cancel` — cancel the entire workflow
- `replan` — pass `spec: { fragment: { ... } }` with the graph fields and node definitions; running nodes can be `restart: true` or `cancel: true`; pending nodes absent from the fragment are cancelled. Valid while paused — the pause → compose spec → replan → resume sequence is the safe path. Use `spec_path` only for a saved or explicitly file-backed fragment.
- `replan` — put `fragment: { ... }` with the graph fields and node definitions in YAML and pass its `spec_path`; running nodes can be `restart: true` or `cancel: true`; pending nodes absent from the fragment are cancelled. Valid while paused — the pause → write file → replan → resume sequence is the safe path.
- `complete` — early-complete: remaining pending nodes are skipped (non-violation)
- `step` — advance exactly one ready node (the first by node ID lexicographic order), then wait. Use for controlled debugging or staged verification of a critical path. Unlike `pause`, which freezes all scheduling, `step` advances one node and re-waits. A second `step` while the stepped node is still running is rejected. Use `resume` to return to full-speed scheduling. Nodes are selected in lexicographic ID order for determinism.

Expand Down
15 changes: 8 additions & 7 deletions packages/core/test/plugin/command.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,20 +107,20 @@ describe("CommandPlugin.Plugin", () => {
}),
)

it.effect("uses inline specs for one-off graphs without removing saved workflows", () =>
it.effect("uses file-backed specs for one-off and saved workflows", () =>
Effect.sync(() => {
expect(CommandPlugin.WorkflowFactsContent).toContain("For a one-off graph, pass `spec` inline")
expect(CommandPlugin.WorkflowFactsContent).toContain("Use `spec_path` only")
expect(CommandPlugin.WorkflowFactsContent).toContain("write the graph to a")
expect(CommandPlugin.WorkflowFactsContent).toContain("task-local file")
// The resident description keeps tool selection and the progressive
// guide index only; per-action field semantics live in the parameter
// schema (change repair-workflow-authoring-validation).
expect(CommandPlugin.WorkflowContent).not.toContain("## Actions")
expect(CommandPlugin.WorkflowContent).toContain("parameter schema")
expect(CommandPlugin.WorkflowFactsContent).toContain('{ action: "read", spec_path: "code-review" }')
expect(CommandPlugin.WorkflowFactsContent).toContain("retarget its objective and block instructions")
expect(CommandPlugin.WorkflowFactsContent).not.toContain("Never inline graph nodes")
expect(CommandPlugin.WorkflowFactsContent).not.toContain("Before any graph-carrying action")
expect(CommandPlugin.DagFlowContent).toContain("inline `spec`")
expect(CommandPlugin.WorkflowFactsContent).not.toContain("pass `spec` inline")
expect(CommandPlugin.DagFlowContent).toContain("task-local YAML file")
expect(CommandPlugin.DagFlowContent).toContain("`spec_path`")
}),
)

Expand Down Expand Up @@ -346,7 +346,8 @@ describe("CommandPlugin.Plugin", () => {
expect(CommandPlugin.WorkflowFactsContent).toContain(
"the workflow boundary owns `protocol_version`, `state`, and\n`fingerprint`",
)
expect(CommandPlugin.WorkflowFactsContent).toContain("For a one-off graph, pass `spec` inline")
expect(CommandPlugin.WorkflowFactsContent).toContain("A one-off graph may use a")
expect(CommandPlugin.WorkflowFactsContent).toContain("task-local file")
expect(CommandPlugin.WorkflowFactsContent).not.toContain("`config.mode`")
}),
)
Expand Down
5 changes: 3 additions & 2 deletions packages/opencode/src/dag/CONTEXT.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Workflow Orchestration Context

Workflow Orchestration turns one user objective into one durable DAG. It supports saved or inline custom workflows and recommends heuristic composition from reusable Blocks. Low-level Nodes remain available when a Block route cannot express the objective.
Workflow Orchestration turns one user objective into one durable DAG. Its model-facing tool accepts saved or file-backed custom workflows and recommends heuristic composition from reusable Blocks. Low-level Nodes remain available when a Block route cannot express the objective.

## Glossary

| Term | Meaning |
| --- | --- |
| Workflow Source | An inline object or YAML document supplied to start, extend, replan, read, validate, or release tooling. |
| Workflow Source | An in-memory object used by trusted internal callers or a YAML document supplied to runtime and release tooling. Model-authored graph actions use YAML through `spec_path`. |
| Workflow Authoring Check | The side-effect-free source-to-graph boundary that parses, normalizes file compatibility, decodes the action shape, compiles Blocks, applies the selected validation profile, and returns diagnostics or a Prepared Workflow Graph. |
| Prepared Workflow Graph | A strictly decoded and compiled graph that passed the requested authoring checks and is ready for a runtime mutation. |
| Workflow Route | A complete Block or Node composition selected for one objective. It may be custom, saved, or assembled heuristically. |
Expand All @@ -28,6 +28,7 @@ Workflow Orchestration turns one user objective into one durable DAG. It support
- `portable` validation does not load user environment catalogs. `environment` validation reads current catalogs and verifies actual model availability.
- No workflow event or durable mutation occurs before a valid Prepared Workflow Graph exists.
- The model-facing schema contains fields the model owns. Session/Project identity, admission audit state, model assignment, and other runtime-derived fields remain hidden.
- Model-facing graph actions expose only `spec_path`; graph fields live in YAML so provider tool-call serialization cannot turn a nested graph into a string.
- Legacy YAML may be adapted at the file boundary without making legacy fields valid inline input.
- Runtime Admission and Workflow Authoring Check have separate names, state, and responsibilities.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

## Context

Workflow input was interpreted independently by the provider-facing tool schema, start, validate, list/read, replan, CLI, generation, and packaging. Hidden YAML authoring removed the model's accidental examples while leaving it unable to infer required fields. Later patches added validators at individual callers, so accepted shapes and diagnostics drifted and some paths reached durable DAG operations before equivalent checks had run.
Workflow input was interpreted independently by the provider-facing tool schema, start, validate, list/read, replan, CLI, generation, and packaging. Earlier hidden YAML authoring left the model unable to infer required fields. Exposing the complete graph as an inline tool argument fixed discoverability but introduced another failure mode: providers or models could double-serialize the nested `spec` object into a JSON string before validation. Field guidance now belongs to the on-demand workflow guides, while the model-facing action remains shallow and file-backed.

The product supports a single custom workflow, saved workflows, and heuristic Block composition. Those are source choices for one orchestration product, not separate validation systems.

Expand All @@ -15,13 +15,18 @@ The product supports a single custom workflow, saved workflows, and heuristic Bl

All tool graph actions and offline config/release consumers call this boundary. Callers may authorize and read files or perform durable DAG mutations, but they do not reinterpret source shape or decide graph validity.

Model-facing `start`, `extend`, `control(replan)`, and `validate` accept only
`spec_path`. One-off graphs use task-local YAML files; saved workflow names use
the same field. Trusted internal consumers may still pass an in-memory source
directly to `WorkflowAuthoring` without creating a second validation path.

The provider schema exposes only author-owned fields. Runtime identity, model assignment, and persisted admission audit fields are derived or adapted behind the boundary. Portable checks are environment-free; environment checks resolve live catalogs and are not cached as content-only facts.

## Consequences

- A valid source has one compiled meaning across validate, start, extend, replan, read/list diagnostics, CI, generation, and packaging.
- Provider schema is sufficient for model authoring without exposing runtime-owned fields.
- Legacy YAML remains readable while new inline input stays strict.
- Provider schema stays shallow; on-demand guides describe author-owned YAML fields without exposing runtime-owned fields.
- File-backed custom and saved workflows share one YAML validation path; internal in-memory input stays strict.
- Environment changes are observed on the next environment check.
- Durable DAG methods retain lifecycle validation as defense in depth, but do not become a second raw-source validator.

Expand Down
Loading
Loading