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
4 changes: 4 additions & 0 deletions packages/core/src/plugin/command/workflow-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ Top level is `title`/`mode`/`admission` (optional) and `config` (required);
`kind` (required), `depends_on`, `instruction`, `worker_type`, `required`,
`report_to_parent` — never `worker`, `prompt`, or `agent`.

A `report_to_parent` node with dependents is a reporting checkpoint: gate each
dependent on its output via `condition`, keep it a reporting leaf, or drop
`report_to_parent`.

Validate that `spec_path` before start. Fix every diagnostic in the same file
and revalidate; validation creates no workflow. A successful start returns the
exact workflow ID. The parent owns the graph, controls, and final report;
Expand Down
4 changes: 4 additions & 0 deletions packages/opencode/src/dag/CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Workflow Orchestration turns one user objective into one durable DAG. Its model-
| Orchestration Router | The product-owned parent guidance that qualifies an objective and selects one Workflow Route without external Skill discovery. |
| Block Composer | The Orchestration Router decision that selects the smallest Block graph justified by current evidence. |
| Decision Checkpoint | One parent-owned confirmation for unresolved user choices that materially change behavior, scope, acceptance, or an irreversible boundary. |
| Reporting Checkpoint | A `report_to_parent: true` node with dependents; its dependents must gate on its output via `condition`, or it must be a reporting leaf. |
| Workflow Brief | The recommended route, scope, acceptance evidence, assumptions, risks, and material alternatives presented at a Decision Checkpoint. |
| Block | A reusable high-level orchestration capability such as explore, plan, debug, coding, verify, or review. Blocks compile into Nodes. |
| Node | A low-level durable unit of child-agent work with dependencies, prompt input, policy, and output contract. |
Expand All @@ -31,6 +32,7 @@ Workflow Orchestration turns one user objective into one durable DAG. Its model-
- 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.
- Dependents of a reporting checkpoint must be gated on its output; authoring rejects ungated shapes at start/validate (enforcement point: authoring boundary only, runtime create deliberately unchanged).

## Boundaries

Expand All @@ -43,3 +45,5 @@ Workflow Orchestration turns one user objective into one durable DAG. Its model-
## Decisions

- [ADR-0001: One Workflow Authoring Check authority](docs/adr/0001-workflow-authoring-check.md)
- [ADR-0002: Parallel workspace writers with an implementation aggregator](docs/adr/0002-parallel-writers-aggregator.md)
- [ADR-0003: Reporting checkpoint gating at the authoring boundary](docs/adr/0003-reporting-checkpoint-gating.md)
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# ADR-0003: Reporting checkpoint gating at the authoring boundary

- Status: Accepted
- Date: 2026-08-17

## Context

On 2026-08-17 a hand-authored 15-node workflow (issue #320) ran 75 minutes to
`completed` although every one of its five decision checkpoints returned
`verdict: replan`. The checkpoint nodes carried `report_to_parent: true` but
their stage dependents declared only `depends_on`, no `condition`. The engine
spawns a dependent the moment all of its dependencies complete, so each next
stage started milliseconds (≈12ms) after its checkpoint settled; the wake to
the parent was terminal-only advisory signal, never a gate. The authoring
model ignored warning-level feedback, which is how the ungated shape shipped.

Block-compiled graphs already gate dependents on checkpoint verdicts (the
issue #294 REJECT-checkpoint shape); hand-built node graphs had no equivalent
check.

## Decision

A `report_to_parent: true` node with dependents is a **reporting checkpoint**.
Each dependent must gate on the checkpoint's output via `condition`
(`input_mapping` does not count — it feeds data, it does not gate), or the
checkpoint must be a reporting leaf, or the node must drop `report_to_parent`.
`node_defaults.report_to_parent` is honored: a node inheriting the default
reports the same way.

Enforcement lives in `checkpointGateDiagnostics`, wired only into
`validatePostCompile`'s structural branch — the authoring start/validate path.
Every ungated dependent emits one error-severity `dag.invalid` diagnostic in
both `portable` and `environment` profiles, so `start` and `validate` reject
the shape before any durable graph exists.

Enforcement is authoring-only by design. `Dag.create` and the replan/extend
fragment paths stay untouched: the verdict vocabulary is open, the ACCEPT path
must not wait for the parent, and runtime enforcement would change the
semantics of every existing graph, including issue #294's wake-chain and
reopen-extend behavior.

## Consequences

- Ungated reporting checkpoints fail fast at start/validate with a diagnostic
naming the checkpoint, the dependent, and the three legal fixes.
- Runtime create, wake chains, and reopen-extend semantics are unchanged;
trusted internal callers retain full runtime flexibility.
- Saved and curated workflows were audited: 14 curated block workflows are
unaffected; only `ultra-flow-route.yaml` and `release-route.yaml` trip the
new check and are tracked in opencode-dag-config#14.

## Alternatives Considered

- Runtime enforcement at `Dag.create`: rejected — the verdict vocabulary is
open-ended, the ACCEPT path must not block waiting for the parent, and it
would change the behavior of every existing graph.
- Warning-severity diagnostic: rejected — the authoring model ignores
warnings; that is precisely how the incident happened.
- A new explicit `gate` field on dependents: rejected — `condition` already
expresses output gating and the block compiler already emits it; a second
mechanism would split the gating vocabulary.

## Deferred

- Replan/extend fragments are not checkpoint-gate-checked (coverage gap; no
date). Runtime flexibility was prioritized; fragment authoring remains
advisory.
- Deprecation of advisory wake chains (no date): `report_to_parent` without
gated dependents stays legal but is a smell worth revisiting once fragment
coverage exists.
Loading
Loading