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
7 changes: 5 additions & 2 deletions packages/core/src/plugin/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,20 @@ import PROMPT_INITIALIZE from "./command/initialize.txt"
import PROMPT_REVIEW from "./command/review.txt"
import DAG_FLOW_PROMPT from "./command/dag-flow.txt"
import DAG_TEMPLATE_UPDATE_PROMPT from "./command/dag-template-update.txt"
import workflowRouting from "./command/workflow-routing.md" with { type: "text" }
import workflowBlocks from "./command/workflow-blocks.md" with { type: "text" }
import workflowContent from "./command/workflow.md" with { type: "text" }
import orchestrationPolicy from "./command/orchestration-policy.md" with { type: "text" }
import orchestrationDomains from "./command/orchestration-domains.md" with { type: "text" }

export const DagFlowDescription = "Start a dependency-graph multi-agent workflow for the supplied task"
export const DagTemplateUpdateDescription = "Update the global DAG reference templates from opencode-dag-config"
export const WorkflowFactsContent = workflowContent
export const WorkflowBlocksContent = workflowBlocks
export const OrchestrationPolicyContent = orchestrationPolicy
export const OrchestrationDomainsContent = orchestrationDomains
export const WorkflowContent = `${WorkflowFactsContent}\n\n${OrchestrationPolicyContent}\n\n${OrchestrationDomainsContent}`
export const DagFlowContent = `${DAG_FLOW_PROMPT}\n\n${WorkflowContent}`
export const WorkflowContent = workflowRouting
export const DagFlowContent = DAG_FLOW_PROMPT

export const Plugin = define({
id: "command",
Expand Down
70 changes: 33 additions & 37 deletions packages/core/src/plugin/command/dag-flow.txt
Original file line number Diff line number Diff line change
@@ -1,43 +1,39 @@
# Start a DAG Workflow

The user invoked `/dag-flow` to start a new orchestration task.

<dag-flow-task>
$ARGUMENTS
</dag-flow-task>

If the content inside `<dag-flow-task>` is empty or contains only whitespace, ask the user what task should be orchestrated. Do not call the `workflow` tool until the user provides a task.

For a non-empty task:

1. Before starting, classify the task as `brainstorm`, `review`, or `develop`, then select the closest reference topology from the workflow library. Saved workflow names resolve through three scopes (first match wins): project `.opencode/workflows/`, global `<opencode config dir>/workflows/` (curated by the `opencode-dag-config` repo), then the builtin templates embedded in release binaries.
Run `workflow(action: "list")` to see every template that actually resolves in this environment with its scope, then pick by NAME:
- design documents, requirement deep-dives, architecture decisions, or design-level debugging → saved workflow `design-decision-loop`
- end-to-end implementation with multiple modules, wiring, tests, and review → saved workflow `parallel-development-loop`
- deep review of an already-built module, subsystem, or codebase → saved workflow `deep-review-dag-module`
- a small bounded working-tree change review → saved workflow `change-review`
- none of the above names resolves (bare dev checkout without the config repo) → compose the smallest fresh graph; do not force an unrelated reference
2. Treat the selected saved spec as a reviewed topology reference, not as a script to replay blindly. Start a saved workflow by name only when its embedded target and inputs already match the request. Otherwise read the reference, derive one inline `spec`, inject the complete `/dag-flow` task into its root planning/exploration prompt, retarget its lanes, and pass it directly to `workflow(action=start)`. Do not create a transient YAML file.
3. The derived graph may expand or prune non-protected lanes. Record the selected `reference_template`, every added node, and every prune as `{node, prune_reason, replacement_coverage}` in the first planning/exploration artifact; require the next fresh review gate to audit that manifest. Missing prune evidence is fail-closed.
4. Preserve the selected reference's protected spine: fresh-context local review, deterministic/evidence verification where applicable, one final arbiter, and PASS-only finalization. Gates return `PASS | LOOP | BLOCKED` with reason, evidence, minimal `loop_scope`, and `stop_reason`. `LOOP` means pause → replan new local correction/review nodes → resume; never create a cycle or restart terminal nodes.
5. During compilation, preserve every user constraint in the graph, including named `@agent` roles, exact model selections, read-only or "Do not modify files" scope, required checks, forbidden actions, and requested deliverables.
6. Resolve capability slots against the eligible configured worker types shown in the `workflow` tool description. Do not invent a missing role or model; if a required capability cannot be resolved, do not start and report the gap.
7. Scale one consolidated graph to the task's blast radius. Related flows for this user objective become nodes and edges under the same workflow ID. A small, well-bounded target gets the smallest useful dependency graph. A large or system-level target (an entire module, subsystem, or codebase) is never satisfied by a single wave of parallel opinions: stage exploration, independent analysis, evidence verification, and synthesis as separate dependent waves. Keep independent viewpoints or work packages parallel and use real fan-in nodes for synthesis, arbitration, integration, and final reporting.
8. For a large-target review or audit, require every reviewer to cite file:line evidence and to mark claims it could not verify. Insert a verification wave between the reviewers and the arbiter that checks disputed, unverified, and uncovered scope against the actual code, so the arbiter rules on verified findings only.
9. Call the `workflow` tool with `action=start` and inline `spec` in this response. Use `spec_path` only when the selected saved workflow already matches or persistence was explicitly requested. Merely printing a plan, graph, JSON, or YAML does not mean a workflow was started.
10. Do not claim the workflow is running unless the tool call succeeds. On success, report the exact Workflow ID and initial state returned by the tool, then tell the user to run `/dag` for live inspection.
11. The workflow runs asynchronously and wakes this parent session when attention or a terminal result is ready. Do not poll it with `action=status`, sleep, retry, or loop merely to wait. End the current response after the brief success report.
12. On failure, state that the workflow was not started and report the actual error. Never invent a Workflow ID or start a replacement workflow unless the user explicitly asked for automatic retries.
13. A completed aggregate node must actually contain the requested synthesis. Never describe unresolved placeholders or an aggregate-node error message as a successful final result.

## Resume-first: continue an interrupted workflow before restarting

When the current task maps to a previously interrupted workflow (same task retried or resumed), the default instinct to "restart the whole graph" is usually wrong — completed node outputs are durable and reusable. First read `workflow(action=status)` on the prior workflow: every failed node carries `error_class` (runtime classes: timeout / exec_failed / verdict_fail) plus `error_reason` — except nodes cancelled via replan (failed with reason "cancelled via replan", no error_class) and rows written before the error_class migration — triage per the Node failure triage section in the workflow guidance, then recover in this order, and only fall back to a full restart when nothing reusable exists:

1. **Paused recovery (crash recovery)**: if the prior workflow is `paused`, never open a new one. The failed node is terminal and immutable — add a replacement node under a NEW id, rewire its pending dependents' `depends_on` to the new id, then `control(resume)`. Downstream nodes stay pending and keep their state.
2. **Continue from completed waves**: if the prior workflow is terminal (`failed` or `cancelled`) but has nodes that `completed` before the failure, their final outputs are still valid. Extract each completed node's output (its final text result, e.g. from the node session's persisted parts or any artifact it wrote) and compile a **continuation spec** that starts at the first unfinished wave. Inject the reused outputs as static context into the downstream node prompts (do not re-run them), add only the missing nodes, and `workflow(action=start)` it. Record `reused_nodes` in the manifest.
3. **Full restart**: only when no completed-node output is reusable — zero completed nodes, or their outputs are empty/irrelevant to the remaining work — re-derive the full graph and start it.

Fail-closed guard: before starting a continuation, verify every reused output is present and non-empty; if extraction is incomplete, fall back to the affected node's fresh run rather than silently continuing on empty input. Never discard completed work to rerun it from zero unless extraction genuinely fails.

Use the orchestration guidance below to design and manage the workflow.
If the task is empty, ask for it and do not start a workflow. Otherwise load
the `orchestration-router` skill and route the request through one consolidated
graph. `/dag-flow` explicitly selects DAG execution, but it does not waive a
material user decision.

When the route requires a decision checkpoint or GRILL qualification, inspect
discoverable facts first, proactively write recommended answers, surface the
compact brief in the main conversation, and ask for one combined confirmation.
Do not call `workflow(action="start")` until that confirmation arrives. Do not
put the checkpoint in a child node. If the request is already bounded and
confirmed, start without manufacturing another question.

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
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
worker types or model IDs. If a configured capability or model is unavailable,
report the actual gap and leave the workflow uncreated.

On success, report the exact Workflow ID and initial state, tell the user they
can run `/dag` for live inspection, and end the response. The workflow wakes
the parent when attention is needed. Do not poll, sleep, or loop to wait. On
failure, state that it did not start and report the real error; do not invent a
replacement run.

A final synthesis block must contain the requested result rather than a plan or
placeholder. The parent verifies that artifact, disposes of any non-ACCEPT
review verdict, and gives the user one final report.
44 changes: 28 additions & 16 deletions packages/core/src/plugin/command/orchestration-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,15 @@ Choose the smallest child execution mode that can safely complete the request:

1. Use direct execution only for conversation, trivial state inspection,
workflow control, final synthesis, or an explicit user opt-out.
2. Use one `task` subagent for one independent non-trivial leaf assignment when
no graph-level coordination is needed. The parent launches it once, consumes
its result, and does not duplicate the leaf work.
3. Use one live `workflow` DAG when one user objective contains staged
dependencies, two or more related workstreams, a quality gate, unknown-size
discovery, adaptive repair, or an explicit multi-role or multi-model
requirement.
2. Use one `task` subagent for one independent non-trivial leaf assignment
outside a project-level source or test change when no graph-level
coordination is needed. The parent launches it once, consumes its result,
and does not duplicate the leaf work.
3. Use one live `workflow` DAG for project-level source or test changes, even
when only one project file is expected, and whenever one user objective
contains staged dependencies, two or more related workstreams, a quality
gate, unknown-size discovery, adaptive repair, or an explicit multi-role or
multi-model requirement.

"Smallest" is measured against the Depth Ladder: a mode or graph that cannot
deliver the ladder's hard minimum for the target size is not safe, merely
Expand Down Expand Up @@ -99,7 +101,8 @@ graph. You MUST NOT create an admission child node, QA workflow, separate
persona, or privileged command. `GRILL-ME` selects `GRILL`; equivalent explicit
requests for adversarial qualification do the same.

Cover these six dimensions, asking only material unresolved questions:
Cover these six dimensions, resolving repository-discoverable facts before
asking the user:

1. goal;
2. scope;
Expand All @@ -108,15 +111,23 @@ Cover these six dimensions, asking only material unresolved questions:
5. evidence and review;
6. risks and failure modes.

Use one adaptive policy with bounded modes:
Use one parent-owned recommendation and confirmation interaction. Fill every
material open decision with a recommended answer based on available evidence,
show alternatives only when they change the result, then ask the user for one
combined confirmation. Do not drip questions across several turns. A user
correction creates a revised brief and one replacement confirmation; unchanged
facts are not asked again.

- `LIGHT`: at most 1 question round for a nearly complete brief.
- `STANDARD`: at most 3 question rounds and the default for deep admission.
- `GRILL`: at most 5 question rounds, probing contradictions, hidden
assumptions, evidence quality, failure modes, and falsifiers.
The modes control challenge depth, not the number of user question rounds:

Stop early as soon as the brief is ready. Exhausting a budget with unresolved
blockers yields `NOT_READY`; it never silently yields `READY`.
- `LIGHT`: validate a nearly complete brief and expose only blockers.
- `STANDARD`: test scope, acceptance evidence, dependencies, and material
delivery risks.
- `GRILL`: additionally probe contradictions, hidden assumptions, evidence
quality, failure modes, and falsifiers, while still recommending an answer
for every surfaced choice.

Unresolved blockers yield `NOT_READY`; they never silently yield `READY`.

Maintain a versioned Requirement Brief with this structure:

Expand All @@ -138,7 +149,8 @@ Maintain a versioned Requirement Brief with this structure:
}
```

Before start, show a concise brief summary and verdict:
Before start, proactively show the recommended answers, a concise brief
summary, and verdict:
`READY | NOT_READY | WAIVED`, plus QA mode, brief revision, and remaining
blockers. `READY` requires a non-empty goal, scope boundaries,
acceptance criteria, evidence obligations, review plan, and no blocking
Expand Down
110 changes: 110 additions & 0 deletions packages/core/src/plugin/command/workflow-blocks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# 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.

## Shape

Use `objective` and `blocks` inside `config` for **start**, or alongside
`blocks` for **extend**. A replan uses the same fields inside `fragment`.

```yaml
config:
name: implement-session-recovery
objective: Implement session recovery with focused tests and evidence-backed review.
blocks:
- id: map
kind: explore
instruction: Locate the ownership and persistence seams.
- id: design
kind: plan
depends_on: [map]
- id: implement
kind: coding
depends_on: [design]
skills: [tdd]
- id: checks
kind: verify
depends_on: [implement]
- id: decision
kind: review
depends_on: [checks]
skills: [code-review]
```

Each block accepts:

- `id`: unique dependency address and the ID of its compiled exit node.
- `kind`: `explore`, `plan`, `prototype`, `debug`, `coding`, `verify`,
`review`, or `synthesize`.
- `depends_on`: upstream block IDs; omitted means a root block.
- `instruction`: target-specific text added to the built-in block contract.
- `skills`: relevant skill names the child loads lazily when available.
- `worker_type`, `required`, `report_to_parent`: optional overrides.

`objective` is required and is injected into every generated node. Use blocks
or nodes, never both. Block IDs use letters, numbers, underscores, and hyphens.
Dependencies must be acyclic; they may name blocks in the submitted fragment
or existing durable node IDs during **extend** and replan.

## Block contracts

- `explore`: read-only repository mapping and evidence collection.
- `plan`: implementation-ready decomposition, seams, checks, and risks.
- `prototype`: the smallest throwaway experiment that resolves a runnable
uncertainty; it does not silently become production code.
- `debug`: expands to reproduce/evidence followed by root-cause diagnosis.
- `coding`: bounded production implementation plus focused tests and checks.
- `verify`: deterministic acceptance checks with explicit PASS/FAIL evidence.
- `review`: expands to independent standards and intent reviews, then one
structured arbiter returning `ACCEPT | REVISE | REJECT | BLOCKED`.
- `synthesize`: resolves dependency outputs into the parent-facing result.

Every compiled block is required by default. `review` and `synthesize` report
to the parent by default; other blocks stay quiet. A block immediately after a
review gate is conditioned on `ACCEPT`. Because the condition language handles
one verdict reference, fan multiple review lanes into one review block before
continuing.

## Composition routes

Choose only blocks justified by current evidence:

- Product or architecture decision: parallel `explore` lanes → `plan` options
→ `review` or `synthesize`.
- Project feature: optional `explore` → `plan` → parallel `coding` packages →
`verify` → `review`.
- Hard bug: `debug` → `coding` → `verify` → `review`.
- Runnable design uncertainty: `prototype` → `plan`; keep the prototype
disposable unless the confirmed scope explicitly promotes it.
- Existing implementation review: `explore` scope lanes → `review`; add a
separate verification block first when test evidence is required.

Do not add a phase merely because it exists. Skip exploration when repository
facts are already known, skip a prototype when ordinary inspection resolves
the question, and keep independent work parallel. Use `synthesize` only when
multiple outputs need reconciliation.

## Parent decision checkpoint

User qualification is not a DAG block. Before executable blocks start, the
parent gathers facts it can discover, creates recommended answers for every
material open decision, displays one compact decision brief, and asks for one
combined confirmation. The brief contains the recommended route, alternatives
only where they change the result, assumptions, risks, scope, and acceptance
evidence. A correction from the user updates the brief; unchanged confirmed
facts are not asked again.

After confirmation, encode the decision in `objective` and block instructions.
If the request is already fully bounded and confirmed, do not manufacture a
redundant checkpoint. Child nodes never ask the user to make product or scope
decisions.

## When to use low-level nodes

Drop to `nodes` for custom template bindings, several conditional branches,
special output schemas, exact retry/cancel/restart controls, or deep diff-review
metadata. Load `guide(topic=interface)` for the full node interface and
`guide(topic=policy)` for gate and recovery contracts. Do not poll a running
workflow; reporting blocks wake the parent when a decision is actionable.
Loading
Loading