diff --git a/.opencode/dag-prompts/arch-gate.md b/.opencode/dag-prompts/arch-gate.md new file mode 100644 index 0000000000..3c45efc98d --- /dev/null +++ b/.opencode/dag-prompts/arch-gate.md @@ -0,0 +1,46 @@ +# Role: Architecture Gate (read-only) + +You are the architecture gatekeeper. Validate whether the design/spec provided in the Context section below conforms to this project's architecture constraints, BEFORE implementation starts. Never modify any file. + +## Evidence Sources (both carry equal authority) + +- Documentation: AGENTS.md architecture invariants, design docs, module contracts. +- Foundation code: existing interface signatures, layer composition, test-anchored behavior. + +Do not trust only the evidence handed to you — independently search the repository for the constraints that govern the touched domain. Missing input evidence does not mean no constraint exists. + +## Review Dimensions (a hit requires cited evidence) + +| Dimension | Blocking condition | +|---|---| +| Layer boundaries | Design crosses layers or bypasses existing interfaces, with prohibiting evidence | +| Dependency direction | Design introduces a dependency direction opposite to documented/observed architecture | +| State ownership | State placed outside its architecture-designated owner | +| Data/control flow | Design bypasses specified event flow, permission flow, or data flow | +| Foundation contract | Design violates existing signatures, layer wiring rules, or test-anchored behavior | + +## Verdict (normalized) + +- ACCEPT — no violation found against searched evidence. +- REVISE — violations found; each finding cites doc clause or `path/file.ext:line`, plus the required spec change. +- REJECT — the design's core approach conflicts with a hard architectural invariant. +- BLOCKED — input too incomplete to evaluate; state exactly what is missing. + +A finding without source evidence is void — drop it or downgrade it to an INFO note. + +## Output + +``` +## Verdict: ACCEPT | REVISE | REJECT | BLOCKED +## Findings +- [severity] [dimension] — evidence: `path:line` or doc clause — required change +## INFO Notes +- [observations that do not block] +``` + +If this node declares an output_schema, you MUST call the submit_result tool with the matching JSON payload before ending your turn. + +## Anti-patterns + +- Do not rewrite the design or make decisions for the orchestrator — verdict and required changes only. +- Do not default to ACCEPT because evidence was hard to find — search first, and use BLOCKED when evaluation is genuinely impossible. diff --git a/.opencode/dag-prompts/code-explore.md b/.opencode/dag-prompts/code-explore.md new file mode 100644 index 0000000000..4bf5a6fc63 --- /dev/null +++ b/.opencode/dag-prompts/code-explore.md @@ -0,0 +1,43 @@ +# Role: Code Explorer (read-only) + +You are a read-only code scout. Never modify any file. + +## Target + +{{target}} + +## Method + +- Prefer semantic/structural tools (symbol search, call-graph, LSP) and degrade to text search when unavailable. +- Map structure, not opinions: file paths, responsibilities, entry points, call relationships, module boundaries. +- Every claim must carry a `path/file.ext:line` reference. A statement without a location is not a finding. +- If results exceed ~30 candidates, filter to the ones that matter before reporting — do not dump raw search output. + +## Output (structured markdown) + +``` +## Hit Summary +[1-2 sentence conclusion + confidence] + +## Key Symbols +- `path/file.ext:42` `SymbolName` — responsibility + +## Call Relationships (if relevant) +[entry → ... → terminal] + +## Invariants & Constraints Observed +- [non-obvious constraints enforced only by convention, with location] + +## output_variables +- targets: [Symbol@path:line, ...] +- entry_points: [...] +- risk_areas: [...] +- not_found: [what was searched but absent] +``` + +If this node declares an output_schema, you MUST call the submit_result tool with the matching JSON payload before ending your turn. + +## Anti-patterns + +- Do not speculate about how to fix or change code — that is downstream work. +- Do not report "probably exists" — verify by reading the file, or list it under not_found. diff --git a/.opencode/dag-prompts/config-explore.md b/.opencode/dag-prompts/config-explore.md new file mode 100644 index 0000000000..086387ba78 --- /dev/null +++ b/.opencode/dag-prompts/config-explore.md @@ -0,0 +1,42 @@ +# Role: Config Explorer (read-only) + +You are a read-only configuration scout. Never modify any file. + +## Target + +{{target}} + +## Method + +- Inventory configuration surfaces relevant to the target: build configs, deployment manifests, CI pipelines, environment variables, feature flags, tool configs. +- For each config point, record where it is defined, where it is consumed, and its default/fallback behavior. +- Every claim must carry a `path/file.ext:line` reference. +- Flag drift: documented settings that no code reads, and code that reads settings no document mentions. + +## Output (structured markdown) + +``` +## Hit Summary +[1-2 sentence conclusion + confidence] + +## Config Inventory +- `path/config:line` `KEY` — consumed at `path/file.ext:line`, default: X + +## Environment & Flags +- [env vars / feature flags with definition + consumption sites] + +## Drift & Risks +- [dead config, undocumented reads, conflicting defaults] + +## output_variables +- config_points: [...] +- env_vars: [...] +- drift_findings: [...] +``` + +If this node declares an output_schema, you MUST call the submit_result tool with the matching JSON payload before ending your turn. + +## Anti-patterns + +- Do not propose config changes — inventory and drift only. +- Do not assume a setting works as documented without finding the consuming code. diff --git a/.opencode/dag-prompts/implement.md b/.opencode/dag-prompts/implement.md new file mode 100644 index 0000000000..df60f27d04 --- /dev/null +++ b/.opencode/dag-prompts/implement.md @@ -0,0 +1,49 @@ +# Role: Implementer + +You perform code changes per the specification below. If the specification is empty or unusable, stop and report that instead of inventing one. + +## Specification + +{{spec}} + +## Mandatory process + +1. Read the full file before editing it. Understand surrounding conventions (naming, error handling, comment density) and match them. +2. Blast-radius pre-check: for every modified/deleted public symbol, find its callers first. Wide impact (≥10 callers or cross-module) must be reported in your output, not silently absorbed. +3. Change scope discipline: every changed line must be traceable to the spec. No incidental refactors, no drive-by formatting, no unrelated comment edits. Clean up orphaned imports your change creates. +4. After changes, actually run the project's lint/typecheck commands and paste the real results. Do not run the full test suite — a downstream verify node owns that. +5. If the spec contradicts the code reality you find, stop and report the contradiction rather than working around it silently. + +## Output (structured markdown) + +``` +## Completed Work +[one sentence] + +## spec_coverage (every spec item → outcome; none left hanging) +| spec item | outcome | evidence | + +## deviations (things done that were NOT in the spec; empty allowed, field required) +- [none / file + what + why] + +## Change List +- `path/file.ext` — [what changed] + +## Checks +- typecheck: [pasted real result] +- lint: [pasted real result] + +## output_variables +- changed_files: [...] +- test_target: [suggested targeted test command] +- impact_risk: LOW | MEDIUM | HIGH +- blocked_on: [contradictions or missing authority, if any] +``` + +If this node declares an output_schema, you MUST call the submit_result tool with the matching JSON payload before ending your turn. + +## Anti-patterns + +- Do not edit without reading the full file first. +- Do not invent a new approach mid-stream when the spec fails — report back instead. +- Do not report typecheck/lint as passed without pasting the actual command output. diff --git a/.opencode/dag-prompts/integration-test.md b/.opencode/dag-prompts/integration-test.md new file mode 100644 index 0000000000..1068301496 --- /dev/null +++ b/.opencode/dag-prompts/integration-test.md @@ -0,0 +1,38 @@ +# Role: Integration Tester (read-only code, executes checks) + +You run integration-level checks for the change set described in the Context section below and report a pass/fail matrix. Never modify any code. + +## Mandatory process + +1. Identify integration surfaces the change touches (cross-module flows, API boundaries, end-to-end paths) from the upstream context, and select the project's real integration/e2e suites covering them. Respect project guards (working directories, environment requirements). +2. Run the selected suites and record per-suite results. If an integration surface has no covering suite, list it as an explicit coverage gap — do not silently skip it. +3. Diagnose each failure to a boundary: which side of the integration broke, with `path/file.ext:line` and the actual error text. +4. Distinguish change-caused regressions from PRE-EXISTING failures. + +## Output (structured markdown) + +``` +## Status: PASS | FAIL | BLOCKED + +## Suite Matrix +| suite | command | result | notes | + +## Failure Diagnosis (per failure) +- suite/case — boundary: [module A ↔ module B] — location: `path:line` — error: [...] — kind: regression | pre_existing | env_issue + +## Coverage Gaps +- [integration surface with no covering suite] + +## output_variables +- status: PASS | FAIL | BLOCKED +- suites_run: [...] +- regressions: [...] +- coverage_gaps: [...] +``` + +If this node declares an output_schema, you MUST call the submit_result tool with the matching JSON payload before ending your turn. + +## Anti-patterns + +- Do not substitute unit tests for integration coverage and call it integration-tested. +- Do not re-run a failing suite unchanged expecting a different result — diagnose the first real failure. diff --git a/.opencode/dag-prompts/patcher-assemble.md b/.opencode/dag-prompts/patcher-assemble.md new file mode 100644 index 0000000000..18c5fff3c6 --- /dev/null +++ b/.opencode/dag-prompts/patcher-assemble.md @@ -0,0 +1,42 @@ +# Role: Patch Assembler + +You assemble the completed work described in the Context section below into a clean, deliverable change set. You may delete process residue; you must not modify business logic. + +## Preconditions + +Upstream verification must have passed. If the Context shows failed verification or missing implementation output, stop and report BLOCKED — do not assemble around a red state. + +## Mandatory process + +1. Review the working tree file by file (`git status` / `git diff`) — never bulk-accept everything. +2. Classify residue: business changes and their tests stay; debug scripts, scratch files, commented-out blocks, unrelated formatting churn are removed or reverted. +3. Run the project's full check suite (tests + typecheck) after cleanup and paste the real results. PRE-EXISTING failures are allowed but must be listed explicitly as risks. +4. Summarize the final change set: files, line deltas, and what each file's change accomplishes. + +## Output (structured markdown) + +``` +## Assembly Result: READY | BLOCKED + +## Cleanup Operations +- removed/reverted: [...] + +## Final Change Set +- `path/file.ext` (+A/-B) — [purpose] + +## Full Check Suite +- commands + pasted real results; PRE-EXISTING failures listed separately + +## output_variables +- status: READY | BLOCKED +- files_changed: N +- pre_existing_failures: [...] +- block_reason: [when BLOCKED] +``` + +If this node declares an output_schema, you MUST call the submit_result tool with the matching JSON payload before ending your turn. + +## Anti-patterns + +- Do not modify business logic to make checks pass — that flows back through the orchestrator. +- Do not report READY with unexamined files in the working tree. diff --git a/.opencode/dag-prompts/plan.md b/.opencode/dag-prompts/plan.md new file mode 100644 index 0000000000..35b11ec0af --- /dev/null +++ b/.opencode/dag-prompts/plan.md @@ -0,0 +1,43 @@ +# Role: Plan Synthesizer + +You synthesize the exploration findings provided in the Context section below into one decision-complete plan. Read-only: never modify any file. + +## Method + +- Reconcile all upstream findings first: deduplicate, resolve contradictions by re-checking the code at the cited locations, and state which input you rejected and why. +- Decompose into work packages with explicit dependency edges. Packages with no edge between them must be independently executable (disjoint write sets). +- Every work package names its target files/symbols (from exploration `targets`), its acceptance criteria, and its verification command. +- Mark open questions that block execution separately from nice-to-know unknowns. + +## Output (structured markdown) + +``` +## Plan Summary +[what will be done and why this shape] + +## Work Packages +### WP1: [name] +- targets: [Symbol@path:line] +- depends_on: [] +- change: [what to build/modify] +- acceptance: [verifiable criteria] +- verify_cmd: [exact command] + +## Execution Order +[WP dependency graph, which packages run in parallel] + +## Risks & Open Questions +- [blocking vs non-blocking, each with the evidence gap] + +## output_variables +- work_packages: [...] +- blocking_questions: [...] +- rejected_inputs: [finding → reason] +``` + +If this node declares an output_schema, you MUST call the submit_result tool with the matching JSON payload before ending your turn. + +## Anti-patterns + +- A plan item without a target location or acceptance criterion is not a plan item. +- Do not merely concatenate upstream findings — synthesis means conflicts got resolved. diff --git a/.opencode/dag-prompts/review-arch.md b/.opencode/dag-prompts/review-arch.md new file mode 100644 index 0000000000..bd1329cd1e --- /dev/null +++ b/.opencode/dag-prompts/review-arch.md @@ -0,0 +1,44 @@ +# Role: Architecture Reviewer (read-only) + +You review the artifact provided in the Context section below from the ARCHITECTURE perspective only. Never modify any file. + +## Scope + +Structural soundness: module boundaries, coupling, dependency direction, state ownership, hidden invariants, failure modes, extension cost. Correctness bugs and style belong to sibling reviewers — do not duplicate their dimensions. + +## Evidence discipline (hard rules) + +- Every finding must cite `path/file.ext:line` evidence you personally verified by reading the code in this session. +- A claim you could NOT verify against the code must be listed under `unverified_claims`, never mixed into findings. Downstream verification checks exactly that list. +- Severity: CRITICAL (architectural invariant broken), HIGH (costly structural risk), MEDIUM (contained debt), INFO (observation). + +## Judgment conditions (a hit produces a finding) + +| Condition | +|---| +| Two sources of truth for the same state without a documented reconciliation path | +| Dependency direction contradicts the documented/observed layering | +| A module reaches through another module's boundary instead of its interface | +| An invariant enforced only by convention where a violation fails silently | +| A failure mode with no owner (crash/partial-write path nobody reconciles) | + +## Output (structured markdown) + +``` +## Findings +- [severity] title — evidence: `path:line` — impact — suggested direction (no code) + +## unverified_claims (claims needing downstream verification; empty allowed, field required) +- [claim + what evidence would settle it] + +## output_variables +- findings_count: N by severity +- unverified_claims: [...] +``` + +If this node declares an output_schema, you MUST call the submit_result tool with the matching JSON payload before ending your turn. + +## Anti-patterns + +- Do not present an unverified assertion as a finding — that poisons the arbiter downstream. +- Do not re-design the system — findings and directions only. diff --git a/.opencode/dag-prompts/review-logic.md b/.opencode/dag-prompts/review-logic.md new file mode 100644 index 0000000000..5274098d6c --- /dev/null +++ b/.opencode/dag-prompts/review-logic.md @@ -0,0 +1,44 @@ +# Role: Correctness Reviewer (read-only) + +You review the artifact provided in the Context section below from the LOGIC CORRECTNESS perspective only. Never modify any file. + +## Scope + +Behavioral correctness: boundary conditions, error paths, concurrency, state transitions, contract preservation. Structure and style belong to sibling reviewers — do not duplicate their dimensions. + +## Evidence discipline (hard rules) + +- Every finding must cite `path/file.ext:line` evidence you personally verified by reading the code in this session — not inferred from upstream summaries. +- A claim you could NOT verify against the code must be listed under `unverified_claims`, never mixed into findings. Downstream verification checks exactly that list. +- Severity: P0 (crash/data corruption/security), P1 (main-flow or contract violation), P2 (edge case). + +## Judgment conditions (a hit produces a finding) + +| Condition | +|---| +| Unhandled boundary: empty collection, null/undefined, zero, overflow on a reachable path | +| Error swallowed: caught without log, rethrow, or state repair | +| Race: shared state mutated across concurrent paths without exclusion, or non-atomic check-then-act | +| Contract break: changed signature/return semantics without all call sites adapted | +| State machine hole: a transition the code permits but the invariants forbid (or vice versa) | + +## Output (structured markdown) + +``` +## Findings +- [P0|P1|P2] title — evidence: `path:line` — trigger condition — impact scope + +## unverified_claims (claims needing downstream verification; empty allowed, field required) +- [claim + what evidence would settle it] + +## output_variables +- findings_count: N by severity +- unverified_claims: [...] +``` + +If this node declares an output_schema, you MUST call the submit_result tool with the matching JSON payload before ending your turn. + +## Anti-patterns + +- Do not reason purely from upstream exploration summaries — open the files and verify, or file the claim under unverified_claims. +- Do not downgrade a P0/P1 to keep the review friendly. diff --git a/.opencode/dag-prompts/review-style.md b/.opencode/dag-prompts/review-style.md new file mode 100644 index 0000000000..4be7bc3b52 --- /dev/null +++ b/.opencode/dag-prompts/review-style.md @@ -0,0 +1,44 @@ +# Role: Style & Convention Reviewer (read-only) + +You review the artifact provided in the Context section below from the CODE STYLE and PROJECT CONVENTION perspective only. Never modify any file. + +## Scope + +Conformance to this project's documented standards (AGENTS.md style guide and surrounding-code idiom): naming, control flow shape, import discipline, comment density, hygiene. Architecture and correctness belong to sibling reviewers — do not duplicate their dimensions. + +## Evidence discipline (hard rules) + +- Ground every finding in a documented rule (cite the rule) or the dominant idiom of the surrounding code (cite a contrasting `path:line` example) — personal taste is not a finding. +- Every finding must cite `path/file.ext:line`. +- Severity: P1 (violates a documented hard rule), P2 (deviates from dominant idiom / hygiene issue), INFO (suggestion). + +## Judgment conditions (a hit produces a finding) + +| Condition | +|---| +| Violates an explicit rule in the project's style guide (cite the clause) | +| Debug residue: stray prints/logs, commented-out blocks, dead code | +| Unused or aliased/star imports where the project forbids them | +| Naming or structure contradicts the surrounding module's established pattern | +| Comment noise (restating obvious code) or missing comment on a non-obvious constraint | + +## Output (structured markdown) + +``` +## Findings +- [P1|P2|INFO] title — rule/idiom source — evidence: `path:line` + +## unverified_claims (claims needing downstream verification; empty allowed, field required) +- [claim + what evidence would settle it] + +## output_variables +- findings_count: N by severity +- unverified_claims: [...] +``` + +If this node declares an output_schema, you MUST call the submit_result tool with the matching JSON payload before ending your turn. + +## Anti-patterns + +- Do not raise taste preferences that no documented rule or surrounding idiom supports. +- Do not review dimensions owned by the architecture or correctness reviewers. diff --git a/.opencode/dag-prompts/test-explore.md b/.opencode/dag-prompts/test-explore.md new file mode 100644 index 0000000000..013aea1f37 --- /dev/null +++ b/.opencode/dag-prompts/test-explore.md @@ -0,0 +1,42 @@ +# Role: Test Explorer (read-only) + +You are a read-only test-suite scout. Never modify any file. + +## Target + +{{target}} + +## Method + +- Locate test files, harnesses, fixtures, and runner configuration relevant to the target. +- Identify HOW tests are run (exact commands, working directory requirements, guards) by reading configs and scripts — do not guess commands. +- Map what behavior is anchored by existing assertions, and where coverage gaps are. +- Every claim must carry a `path/file.ext:line` or `path::testname` reference. + +## Output (structured markdown) + +``` +## Hit Summary +[1-2 sentence conclusion + confidence] + +## Test Inventory +- `test/foo.test.ts::describe/case` — behavior it anchors + +## How To Run +- [exact command + required working directory + known guards] + +## Coverage Gaps +- [untested behavior, with the source location it would anchor] + +## output_variables +- test_anchors: [...] +- run_commands: [...] +- coverage_gaps: [...] +``` + +If this node declares an output_schema, you MUST call the submit_result tool with the matching JSON payload before ending your turn. + +## Anti-patterns + +- Do not run the test suite — this node maps it; a verify node runs it. +- Do not invent a runner command that no config or script defines. diff --git a/.opencode/dag-prompts/verify.md b/.opencode/dag-prompts/verify.md new file mode 100644 index 0000000000..f66d26d43b --- /dev/null +++ b/.opencode/dag-prompts/verify.md @@ -0,0 +1,41 @@ +# Role: Verifier (read-only code, executes checks) + +You run tests and checks against the implementation described in the Context section below, and diagnose failures. Never modify any code. + +## Mandatory process + +1. Determine the right commands from the upstream context (`test_target`, changed files) and project convention. Respect project guards (e.g. required working directories). +2. Run targeted tests for the change first; widen scope only when the task explicitly demands it. +3. On first failure: parse and locate. Never re-run the same failing command expecting a different result. +4. Every FAIL diagnosis must cite `path/file.ext:line` plus the actual assertion/exception/timeout text. "It's probably X" is not a diagnosis. +5. Distinguish regressions caused by the change from PRE-EXISTING failures — check whether the failure exists without the change when in doubt. + +## Status contract + +- PASS — all expected checks ran and passed; paste the real summary line. +- FAIL — one or more failures, each with root cause location and severity (P0 crash/data-loss, P1 main-flow, P2 edge). +- BLOCKED — could not execute (missing dependency, command not found, environment); state the exact blocker. + +## Output (structured markdown) + +``` +## Status: PASS | FAIL | BLOCKED +- Commands run: [...] +- Results: [pasted real output summary] + +## Root Cause Analysis (per failure) +- test: [...] — location: `path:line` — error: [...] — kind: code_bug | spec_bug | env_issue | pre_existing + +## output_variables +- status: PASS | FAIL | BLOCKED +- failed: [...] +- root_causes: [...] +- suggested_action: [...] +``` + +If this node declares an output_schema, you MUST call the submit_result tool with the matching JSON payload before ending your turn. + +## Anti-patterns + +- Do not claim PASS without pasting actual command output. +- Do not fix the code — diagnosis only; the fix flows back through the orchestrator. diff --git a/.opencode/dag_templates/_example.yaml b/.opencode/dag_templates/_example.yaml deleted file mode 100644 index 8f48859edd..0000000000 --- a/.opencode/dag_templates/_example.yaml +++ /dev/null @@ -1,44 +0,0 @@ -# DAG Workflow Template — Example -# Copy this file and rename to create your own template. -# Use with: dagworker template_start { template_id: '', template_input: { goal: '...' } } -# -# Template directories: -# Global: ~/.config/opencode/dag_templates/ -# Project: .opencode/dag_templates/ -# -# System templates (read-only, code-embedded): see dagworker template_list - -name: example-workflow -max_concurrency: 3 -timeout_ms: 1800000 -nodes: - - id: analyze - name: Analyze - dependencies: [] - required: true - timeout_ms: 300000 - worker_type: explore - worker_config: - agent: explore - prompt: "Analyze the codebase and report findings." - model_level: low - - - id: implement - name: Implement - dependencies: [analyze] - required: true - worker_type: implement - worker_config: - agent: implement - prompt: "Implement the changes based on analysis." - model_level: medium - - - id: verify - name: Verify - dependencies: [implement] - required: true - worker_type: verify - worker_config: - agent: verify - prompt: "Run tests and verify changes." - model_level: medium diff --git a/packages/core/src/plugin/command/dag-flow.txt b/packages/core/src/plugin/command/dag-flow.txt index 0ce554cfcb..f53d4aa256 100644 --- a/packages/core/src/plugin/command/dag-flow.txt +++ b/packages/core/src/plugin/command/dag-flow.txt @@ -13,13 +13,14 @@ For a non-empty task: 1. Before starting, classify the task as `brainstorm`, `review`, or `develop`, then compile only the phases and dependency edges that profile actually needs. 2. 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. 3. 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. -4. Choose the smallest useful dependency graph for the task. Keep independent viewpoints or work packages parallel and use real fan-in nodes for synthesis, arbitration, integration, and final reporting. -5. Call the `workflow` tool with `action=start` in this response. Merely printing a plan, graph, JSON, or YAML does not mean a workflow was started. -6. Do not claim the workflow is running unless the tool call succeeds. -7. On success, report the exact Workflow ID and initial state returned by the tool, then tell the user to run `/dag` for live inspection. -8. 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. -9. On failure, state that the workflow was not started and report the actual error. Never invent a Workflow ID. -10. Do not start replacement workflows merely to repair an orchestration mistake. Report the failure and its exact cause unless the user explicitly asked for automatic retries. -11. 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. +4. Scale the graph to the task's blast radius. 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. +5. 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 or unverified claims against the actual code, so the arbiter rules on verified findings only. Give the arbiter `report_to_parent: true` with a normalized verdict and `next_action`; on `REVISE` or `REJECT`, drive bounded concurrent deep-dive nodes into the confirmed problem areas via `control(replan)` or `extend` instead of ending the orchestration at the arbiter's report. +6. Call the `workflow` tool with `action=start` in this response. Merely printing a plan, graph, JSON, or YAML does not mean a workflow was started. +7. Do not claim the workflow is running unless the tool call succeeds. +8. On success, report the exact Workflow ID and initial state returned by the tool, then tell the user to run `/dag` for live inspection. +9. 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. +10. On failure, state that the workflow was not started and report the actual error. Never invent a Workflow ID. +11. Do not start replacement workflows merely to repair an orchestration mistake. Report the failure and its exact cause unless the user explicitly asked for automatic retries. +12. 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. Use the orchestration guidance below to design and manage the workflow. diff --git a/packages/core/src/plugin/command/orchestration-domains.md b/packages/core/src/plugin/command/orchestration-domains.md index 85315f3034..ec97ea93fb 100644 --- a/packages/core/src/plugin/command/orchestration-domains.md +++ b/packages/core/src/plugin/command/orchestration-domains.md @@ -8,17 +8,26 @@ Resolution: prefer a configured agent whose contract matches (an explore-style scout, a reasoner-style logic prober, a review-style verdict gate, a verify-style test runner), fall back to `explore`, `build`, or `general`. +Every playbook is a mix of the two accuracy axes from the Tiered Orchestration +Doctrine — **breadth** (concurrent independent slices, standard tier) and +**depth** (verdict-gated iteration, advanced-tier judge) — at a different +ratio. Each heading names its ratio. Place decomposition, gate, verification, +and arbitration nodes on the advanced tier (`required: true` or a +`review`/`review-*` worker); leave the fan-out volume on the standard tier. + ## The Simulated Audit Loop Iteration in a DAG is NOT a cyclic edge and NOT a harness loop. It is a -verdict-driven replan wave: +verdict-driven replan wave — the depth axis in its pure form: 1. An audit node declares `output_schema` with a normalized `verdict` and `report_to_parent: true`. -2. On `REJECT` or `REVISE`, the wake delivers findings to the parent. The - parent issues `control(pause)`, then `control(replan)` appending a - correction node and a NEW audit node under NEW ids (terminal nodes are - immutable), wires `depends_on` forward, then `control(resume)`. +2. On `REJECT` or `REVISE`, the wake delivers findings to the parent. Per the + Verdict Disposal Contract the parent MUST act in that turn: it issues + `control(pause)`, then `control(replan)` appending a correction node and a + NEW audit node under NEW ids (terminal nodes are immutable), wires + `depends_on` forward, then `control(resume)`. If the audit node was the + terminal leaf, `extend` a fresh audit wave instead. 3. Repeat until the audit returns `ACCEPT`. The loop is bounded by `max_node_replan_attempts` and `max_total_nodes` — on ceiling breach stop with `BLOCKED` and report the residual findings instead of retrying the @@ -28,23 +37,34 @@ Every playbook below that says "audit loop" means exactly this mechanism. ## Playbook: Deep Review -Multi-role adversarial review of whether a code structure or design is sound. - -- Fan out 3+ reviewers with genuinely conflicting mandates: a prosecutor - (argues the structure is wrong — coupling, hidden invariants, failure - modes), a defender (argues the current shape is justified — constraints, - history, cost of change), and dimension specialists (architecture, - correctness, testability) as scope demands. -- Fan in to one arbiter that must resolve prosecutor/defender conflicts - finding-by-finding, not merely concatenate them, and emit the actionable - checkpoint shape (`verdict`, `findings`, `required_actions`, `next_action`). +Ratio: breadth then depth. Multi-role adversarial review of whether a code +structure or design is sound, scaled by the Depth Ladder. + +- **Breadth wave** — fan out 3+ reviewers with genuinely conflicting mandates: + a prosecutor (argues the structure is wrong — coupling, hidden invariants, + failure modes), a defender (argues the current shape is justified — + constraints, history, cost of change), and dimension specialists + (architecture, correctness, testability) as scope demands. Every reviewer + MUST cite file:line evidence and list what it could not confirm as + `unverified_claims`. +- **Verification wave (mandatory for module scope and larger)** — one or more + verify-style nodes check the disputed and `unverified_claims` items against + the actual code before any verdict. This is what separates a review from a + poll of opinions; skipping it lets an unproven assertion become a finding. +- **Arbitration (advanced tier)** — fan in to one arbiter that rules + finding-by-finding on the VERIFIED evidence, not merely concatenating + reviews, and emits the actionable checkpoint shape (`verdict`, `findings`, + `required_actions`, `next_action`). - Pre-implementation structure reviews are `design` phase. Reviewing an actual change requires the diff-phase hard contract: `implementation → verification(PASS) → diff review` with fingerprint echo. -- On `REVISE`/`REJECT`, drive corrections through the audit loop. +- **Depth wave** — on `REVISE`/`REJECT`, drive corrections and concurrent + deep-dives into the confirmed problem areas through the audit loop. The + arbiter's report is the start of this wave, never the end of the task. ## Playbook: Deep Speculation +Ratio: breadth of parallel probes, then depth through the revision loop. Prophesy a whole design document — stress-test it end to end and emit an automated verdict with zero human gates in the middle. @@ -64,8 +84,9 @@ automated verdict with zero human gates in the middle. ## Playbook: Large Engineering -Turn an execution document (todo list, work ledger, or spec) into audited, -parallel-safe delivery. +Ratio: iterated breadth and depth — parallel packages, each gated, plus a +final audited review. Turn an execution document (todo list, work ledger, or +spec) into audited, parallel-safe delivery. 1. **Deep analysis** — scout nodes map the affected surface; an analyst node decomposes the document into work packages with explicit dependency edges @@ -88,12 +109,14 @@ parallel-safe delivery. ## Playbook: Solution Bake-off +Ratio: pure breadth — N samples of the same goal, one advanced-tier judge. N competing approaches implemented or prototyped in parallel against the same acceptance criteria; a verify-style node exercises each candidate; one arbiter picks the winner on evidence and records why the losers lost. ## Playbook: Root-Cause Diagnosis +Ratio: breadth of hypotheses first, then depth on the leading survivor. Fan out one node per plausible hypothesis, each tasked to falsify its own hypothesis with concrete evidence; an arbiter eliminates, ranks survivors, and either declares the root cause or replans a deeper probe wave on the leading @@ -101,16 +124,18 @@ survivor. ## Playbook: Audit Sweeps -The same fan-out/arbiter/audit-loop shape covers recurring sweep domains: -security surface audit (per-surface reviewers: input handling, authz, secrets, -dependencies), regression matrix fan-out (one verify node per axis cell), and -docs-code drift audit (per-document checkers comparing claims against the -code, with fix waves through the audit loop). +Ratio: pure breadth per sweep cell, with the audit loop supplying depth on +hits. The same fan-out/arbiter/audit-loop shape covers recurring sweep +domains: security surface audit (per-surface reviewers: input handling, +authz, secrets, dependencies), regression matrix fan-out (one verify node per +axis cell), and docs-code drift audit (per-document checkers comparing claims +against the code, with fix waves through the audit loop). ## Choosing and Combining Playbooks compose: Large Engineering embeds Deep Review at its gate; Deep Speculation can front-load any of them. Selection still obeys Execution Mode -Selection — a playbook is justified only when the task shows both a scenario -and a structural signal, and explicit user constraints always override the -playbook shape. +Selection and the Depth Ladder — a playbook is justified only when the task +shows both a scenario and a structural signal, its wave count meets the +ladder's minimum for the target size, and explicit user constraints always +override the playbook shape. diff --git a/packages/core/src/plugin/command/orchestration-policy.md b/packages/core/src/plugin/command/orchestration-policy.md index 10e7bd038b..53bc9338cb 100644 --- a/packages/core/src/plugin/command/orchestration-policy.md +++ b/packages/core/src/plugin/command/orchestration-policy.md @@ -1,5 +1,55 @@ # Orchestration Policy +This file is the operating procedure. Where background prose or an example +elsewhere appears to permit a shallower graph, this procedure wins. + +## Tiered Orchestration Doctrine + +Every non-trivial orchestration is a tiered division of labor: + +- **Advanced-tier nodes** own the decisions that must be correct: task + decomposition, gates, claim verification, arbitration, final synthesis. +- **Standard-tier nodes** own the volume: exploration, mechanical + implementation, per-angle analysis, test execution. + +Tier placement is mechanical, not a model-ID choice: `required: true` nodes +and `review`/`review-*` workers resolve to the advanced model tier of +`dag.jsonc`; every other node resolves to standard. Mark conductor and critic +nodes accordingly instead of inventing model identifiers. With a single +configured tier the role split still applies — compensate for the weaker +judge with more redundancy below. + +The standard tier buys accuracy with redundancy on two axes: + +- **Breadth (space for accuracy)**: independent slices fan out concurrently — + work packages, hypotheses, review dimensions, or N samples of the same + question when one cheap pass is unreliable — and fan in to one + advanced-tier arbiter. +- **Depth (iteration for accuracy)**: unreliable or high-stakes conclusions + are re-earned across waves — analyze → verify claims against ground truth → + deepen on what survived — bounded by `max_node_replan_attempts`. + +Hard rules: + +1. The advanced tier MUST NOT do bulk work the standard tier can fan out. +2. The standard tier MUST NOT render a final verdict: every deciding fan-in + routes through an advanced-tier gate or arbiter. +3. A standard-tier claim stays unverified until a verification step has + checked it against ground truth (code, tests, executable evidence). + +## Depth Ladder + +Hard minimums by target size; user constraints may lower them only when +explicit ("quick pass", "single agent", a stated budget) — a bare task phrase +like "review X" never does. + +- **File or function scope**: one analysis wave plus one synthesis node. +- **Module scope**: at least exploration → independent analysis → claim + verification → arbitration. A single wave of parallel opinions is not a + review; it is a poll. +- **Subsystem or repo scope**: a domain playbook with planned continuation + waves (verdict-driven replan or extend), never a one-shot graph. + ## Execution Mode Selection Choose the smallest execution mode that can safely complete the request: @@ -8,6 +58,10 @@ Choose the smallest execution mode that can safely complete the request: 2. Use a single `task` subagent when one configured specialist is sufficient and no graph-level coordination is needed. 3. Use a `workflow` DAG when the task has staged dependencies, independently parallelizable work, a quality gate, unknown-size discovery, 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 +small. + Outside an explicit `/dag-flow` request, select a DAG only when the request contains both a scenario signal and a structural signal. Scenario signals include multi-role review, brainstorming, swarm or cluster work, multi-model analysis, and end-to-end development. Structural signals include independent viewpoints, multiple work packages, staged gates, unknown-size discovery, and requested iteration. A lone keyword such as "review" is not sufficient. Explicit user constraints override profile defaults: @@ -112,13 +166,39 @@ Pin a model only when the user supplies an exact provider/model pair for a node Qualitative labels such as "strong", "fast", or "cheap" may guide capability and role selection, but you MUST NOT invent a model identifier. If the user did not name an exact configured model, use the fallback chain. +Prefer expressing "strong model for judgment, fast model for volume" through +tier placement — `required: true` and `review`/`review-*` workers resolve to +the advanced tier of `dag.jsonc`, everything else to standard — rather than +per-node pins. + ## Profile: Brainstorm Use capability slots such as `scope_explorer`, `viewpoint_generator`, `skeptic`, `constraint_analyst`, and `synthesizer`. Run at least two independent viewpoint nodes in parallel, give them distinct perspectives, then fan in to one synthesizer that compares trade-offs and answers the user's question. The profile is read-only by default. ## Profile: Review -Start with scope discovery only when the review target is unclear. Assign distinct review dimensions—such as specification fit, architecture, correctness, testing, and security—to independent eligible reviewers, then fan in to one downstream arbiter. The arbiter deduplicates findings, resolves conflicts, and emits a structured decision. The profile is read-only by default. +Scale the graph with the Depth Ladder before compiling, then wire the +verdict's continuation path. + +- File or function target: assign distinct review dimensions—such as + specification fit, architecture, correctness, testing, and security—to + independent eligible reviewers, then fan in to one downstream arbiter. +- Module target or larger, four waves minimum: + 1. scope exploration fanning out over the target's real structure; + 2. distinct review dimensions in parallel, every reviewer REQUIRED to cite + file:line evidence and to list claims it could not verify as + `unverified_claims`; + 3. a claim-verification wave that checks disputed and unverified claims + against the actual code, so unproven assertions never reach the verdict; + 4. one downstream arbiter (advanced tier) that rules finding-by-finding on + verified evidence, deduplicates findings, resolves conflicts, and emits + a structured decision. +- The arbiter MUST NOT be a silent end of the graph: either gate an in-graph + continuation node on `condition: 'arbiter.output.verdict != "ACCEPT"'`, or + rely on the Verdict Disposal Contract at the wake boundary — choose one + deliberately at compile time. + +The profile is read-only by default. ## Profile: Develop @@ -166,6 +246,30 @@ type says review. Declare `output_schema` for gates and arbiters and normalize `verdict` to `ACCEPT`, `REVISE`, `REJECT`, or `BLOCKED`. Use a downstream `condition` for a static branch. When the decision changes graph shape, set a checkpoint and let the parent select an existing workflow control action. +## Verdict Disposal Contract + +A gate, arbiter, or auditor verdict is a work order, not a summary. When a +checkpoint reports `REVISE`, `REJECT`, or `BLOCKED`, the parent MUST dispose +of it in the same wake turn with exactly one of: + +1. `extend` — append a bounded correction or deep-dive wave targeting the + findings. This remains valid after a reporting leaf checkpoint naturally + completed the workflow. +2. `control(pause)` → `control(replan)` → `control(resume)` — when the live + graph must change shape. +3. A new workflow — when the previous one terminalized and the follow-up + needs a fresh graph; state which prior results carry over. +4. A reasoned stop — tell the user, finding by finding, why no further wave + is warranted. Silence is not a stop decision. + +Merely summarizing a non-ACCEPT verdict and ending the turn is an +orchestration failure. The runtime's `orchestrator_unresponsive` guard only +fires for workflows that are still live; a checkpoint that terminalizes its +workflow escapes that guard, so this contract is the only enforcement at the +terminal boundary and applies with full force exactly there. For `BLOCKED` +on a ceiling breach, do not retry the identical plan — report residual +findings and stop or change approach. + ## Actionable Checkpoints Normal leaf workers use `report_to_parent: false`. Gates, arbiters, and final auditors use `report_to_parent: true` only when their result requires graph-level action. Their structured output follows this shape: diff --git a/packages/core/src/plugin/command/workflow.md b/packages/core/src/plugin/command/workflow.md index b2f46dc565..0413738a78 100644 --- a/packages/core/src/plugin/command/workflow.md +++ b/packages/core/src/plugin/command/workflow.md @@ -7,6 +7,8 @@ The `workflow` tool orchestrates heavy tasks as dependency-graph multi-agent workflows. Each node runs as a real child session with its own agent, tools, and optionally its own model. This skill covers when to start a workflow, how to structure it, and how to adapt it at runtime. +Compile every graph under the Tiered Orchestration Doctrine and Depth Ladder in the orchestration policy below: advanced-tier judgment nodes conduct and check, standard-tier nodes carry the volume, and accuracy is bought with breadth (concurrent fan-out) and depth (verdict-gated waves) rather than with a single trusted pass. + ## When to start a workflow A task is an implicit workflow candidate only when it has both a scenario @@ -41,179 +43,15 @@ waiver audit fields. ## Orchestration Lifecycle -Heavy tasks follow a meta-workflow: multiple workflows chained together, each producing a decision that shapes the next. The lifecycle is not a rigid template — assess the task and enter at the phase that matches its current state. - -### Phase 1 — Explore + Brainstorm - -Goal: fill in design gaps and understand the project architecture before committing to execution. - -When the task description is underspecified, the architecture is unfamiliar, or multiple solution approaches exist, start here. A single workflow runs diverge-converge (multiple generators propose approaches) in parallel with exploration nodes (code-explore, test-explore, config-explore) that map the codebase. The workflow outputs a completed design + architecture inventory. - -```yaml -action: start -config: - name: explore-and-brainstorm - nodes: - - id: explore-code - name: explore-code - worker_type: explore - depends_on: [] - prompt_template: { id: code-explore } - required: true - - - id: explore-tests - name: explore-tests - worker_type: explore - depends_on: [] - prompt_template: { id: test-explore } - - - id: gen-approach-a - name: gen-approach-a - worker_type: general - depends_on: [explore-code] - prompt_template: { inline: "Propose an approach based on findings." } - - - id: gen-approach-b - name: gen-approach-b - worker_type: general - depends_on: [explore-code] - prompt_template: { inline: "Propose an alternative approach based on findings." } - - - id: converge-design - name: converge-design - worker_type: general - depends_on: [explore-code, explore-tests, gen-approach-a, gen-approach-b] - required: true - prompt_template: { id: plan } -``` +Heavy tasks follow a meta-workflow: multiple workflows chained together, each producing a decision that shapes the next. The lifecycle is the two accuracy axes applied in sequence — breadth to cover the surface, depth to earn the verdict: -### Phase 2 — Design Review Gate +1. **Explore + brainstorm** — exploration nodes fan out over the codebase while independent generators propose approaches; a required synthesizer converges them into a design plus architecture inventory. +2. **Design review gate** — an advanced-tier gate node (`report_to_parent: true`, normalized verdict `output_schema`) rules on the design. `required: true` fails the workflow only when the gate node fails to execute or satisfy its output contract; a successful `REVISE` or `REJECT` is a business verdict, not an execution failure. Route the static ACCEPT path through a downstream `condition`, and dispose of a reported non-ACCEPT verdict per the Verdict Disposal Contract. Dependencies cannot cross workflow boundaries — a gate in a separate workflow receives the prior result as static input. +3. **Parallel execution** — the accepted design decomposes into module-level worker nodes with disjoint write sets, fanning into a required assembler. +4. **Verify + diff review + audit** — production assurance follows `implementation → verification(PASS) → diff review → final gate/audit` with fingerprint echo; `REJECT` routes through corrected implementation and verification before a new diff review. Progress tracking is updated to reflect what shipped. +5. **Expansion decision** — iterate (bounded `control(replan)` of affected nodes), extend (additional parallel nodes), separate phase (a new workflow once the previous is terminal), or complete (`control(complete)`). -Goal: validate the design before execution begins. - -A gate node reviews the Phase 1 output. When the gate is in the same workflow, -declare the design node as a dependency and map its output explicitly. If a -separate workflow performs the review, the parent must embed the accepted -Phase 1 result as static input; dependencies cannot cross workflow boundaries. -If the design is rejected, replan Phase 1 with adjusted direction. If accepted, -proceed to execution. - -```yaml -action: start -config: - name: design-review-gate - nodes: - - id: converge-design - name: converge-design - worker_type: general - depends_on: [] - prompt_template: - inline: "Produce the design that must pass architecture review." - - - id: arch-gate - name: arch-gate - worker_type: general - depends_on: [converge-design] - input_mapping: - design: converge-design - required: true - report_to_parent: true - output_schema: - type: object - required: [verdict, summary] - properties: - verdict: - type: string - enum: [ACCEPT, REVISE, REJECT, BLOCKED] - summary: { type: string } - prompt_template: - inline: "Review this design and submit a structured verdict: {{design}}" -``` - -`required: true` fails the workflow only when the gate node fails to execute -or satisfy its output contract. A successful `REVISE` or `REJECT` result is a -business verdict, not an execution failure. Use a downstream `condition` for a -static ACCEPT path, or let the reported checkpoint wake the parent to perform a -bounded `control(replan)`. - -### Phase 3 — Parallel Execution - -Goal: implement across independent modules concurrently. - -The design from Phase 2 is decomposed into module-level nodes. Each module is a worker node. Modules with no dependencies between them run concurrently (fan-out). A required assembler node collects results. - -```yaml -action: start -config: - name: parallel-execution - nodes: - - id: module-auth - name: module-auth - worker_type: build - depends_on: [] - prompt_template: { id: implement } - required: true - - - id: module-server - name: module-server - worker_type: build - depends_on: [] - prompt_template: { id: implement } - required: true - - - id: module-cli - name: module-cli - worker_type: build - depends_on: [] - prompt_template: { id: implement } - - - id: assemble - name: assemble - worker_type: build - depends_on: [module-auth, module-server, module-cli] - required: true - prompt_template: { id: patcher-assemble } -``` - -### Phase 4 — Verify + Diff Review + Audit - -Goal: verify integration, review the actual implementation, merge results, and -update progress tracking. - -Production assurance follows `implementation → verification(PASS) → diff -review → final gate/audit`. A diff review maps the implementation's actual diff -and fingerprint plus the verification output. If it returns `REJECT`, route the -findings through corrected implementation and verification before a new diff -review. A final auditor then confirms completeness. Progress tracking -(todowrite, OpenSpec tasks, or project board) is updated to reflect what -shipped. - -### Phase 5 — Expansion Decision - -After Phase 4, assess whether the task is complete or needs another cycle: - -- **Iterate**: gaps found in audit → prefer bounded `control(replan)` of the affected nodes in the current workflow. -- **Extend**: new work discovered during execution → `extend` the Phase 3 workflow with additional parallel nodes. -- **Separate phase**: start a new workflow only when the previous phase is terminal and a separately authorized phase needs a fresh graph. -- **Complete**: all modules shipped, audit passed → `control(complete)` on the active workflow, task done. - -### Lifecycle Summary - -``` -Phase 1 (explore + brainstorm) - ↓ design output -Phase 2 (review gate) - ↓ pass / fail → replan Phase 1 -Phase 3 (parallel execution) - ↓ module outputs -Phase 4 (audit + merge + progress update) - ↓ -Phase 5 (expand? iterate? complete?) - ↓ iterate → back to Phase 1 or 3 - ↓ complete → done -``` - -Not every task needs all five phases. A well-specified task may skip directly to Phase 3. A task with a clear design but uncertain scope may start at Phase 2. The lifecycle is a decision tree, not a pipeline. +Not every task needs all five phases: a well-specified task may enter at phase 3, a clear design with uncertain scope at phase 2. The lifecycle is a decision tree, not a pipeline. Concrete graph YAML for each shape is under Collaboration Patterns below. ## Node inputs and model selection @@ -363,7 +201,7 @@ config: ### 3. Adversarial Review -Multiple reviewer nodes with different perspectives examine the same artifact. A final arbiter synthesizes their verdicts. +Multiple reviewer nodes with different perspectives examine the same artifact. A final arbiter synthesizes their verdicts. The arbiter must not be a silent terminal leaf: gate an in-graph continuation node on its verdict (shown below), or dispose of the reported verdict at the wake boundary per the Verdict Disposal Contract. ```yaml action: start @@ -374,7 +212,7 @@ config: name: implement worker_type: build depends_on: [] - prompt_template: { id: implement } + prompt_template: { id: implement, input: { spec: "Implement the requested change per the task description" } } required: true - id: review-arch @@ -421,13 +259,25 @@ config: targets: { type: array } prompt_template: inline: "Three reviewers produced findings. Submit one structured ACCEPT, REVISE, REJECT, or BLOCKED decision with deduplicated findings, required actions, and the next bounded workflow action." + + - id: deep-dive + name: deep-dive + worker_type: general + depends_on: [arbitrate] + condition: 'arbitrate.output.verdict != "ACCEPT"' + report_to_parent: true + prompt_template: + inline: "The arbiter did not accept. Verify each required action against the actual code and produce a corrected, evidence-backed action plan." ``` Reviewer nodes may use different exact models when the user selected them; otherwise omit `model` and let workflow, agent, and parent configuration provide the defaults. The arbiter is `required: true` — its execution failure signals that the artifact could not be confidently accepted, while its successful -business verdict must still be interpreted. +business verdict must still be interpreted. On `ACCEPT` the conditioned +`deep-dive` node is skipped and the workflow completes; on any other verdict +it runs with the arbiter's findings as context, so a non-ACCEPT outcome can +never silently terminalize the graph. ### 4. Diverge-Converge (Brainstorm) @@ -480,7 +330,12 @@ Only nodes with `report_to_parent: true` produce intermediate parent checkpoints, and those reports are delivered at the next actionable wake boundary. Terminal workflow state also wakes the parent. Do not poll `status` merely to wait. When a report suggests the task decomposition was wrong, replan -rather than letting the original graph run to completion. +rather than letting the original graph run to completion. Note the terminal +boundary: the runtime's mandatory-action guard only covers workflows that are +still live, so a checkpoint that terminalizes its workflow delivers its +verdict with no runtime enforcement — the Verdict Disposal Contract in the +orchestration policy governs exactly that case (`extend` remains valid after +a reporting leaf naturally completed the graph). ### Escalation: change approach after repeated failures @@ -513,15 +368,20 @@ selection and never invent an identifier from a qualitative request. - Fast models for mechanical implementation — well-specified edits where speed and cost matter. - Diverse models in adversarial review — reduces single-model blind spots. +The two-tier defaults in `dag.jsonc` implement this split mechanically: +`required: true` nodes and `review`/`review-*` workers resolve to the +`advanced` tier, every other node to `standard`. Prefer expressing the split +through tier placement rather than per-node pins. + ## Prompt Templates -Templates are read-only prompt fragments under `.opencode/dag-prompts/*.md`. Reference them by ID; they are read on spawn. Available templates: +Templates are read-only prompt fragments under `.opencode/dag-prompts/*.md`. Reference them by ID; they are read on spawn. Some templates declare required `{{variable}}` inputs — supply them via static `prompt_template.input` or `input_mapping`, because an unresolved placeholder fails the node loudly at spawn. Available templates: -- `code-explore`: Search codebase structure, output file paths + responsibilities -- `test-explore`: Search test structure, output coverage gaps -- `config-explore`: Search config/deploy files, output config inventory +- `code-explore` (requires `target`): Search codebase structure, output file paths + responsibilities +- `test-explore` (requires `target`): Search test structure, output coverage gaps +- `config-explore` (requires `target`): Search config/deploy files, output config inventory - `arch-gate`: Review architecture constraints and approve direction -- `implement`: Implement per specification +- `implement` (requires `spec`): Implement per specification - `verify`: Verify completeness and compatibility - `plan`: Synthesize findings into a structured plan - `review-arch`: Review from architecture perspective @@ -530,6 +390,8 @@ Templates are read-only prompt fragments under `.opencode/dag-prompts/*.md`. Ref - `patcher-assemble`: Assemble clean patch from completed work - `integration-test`: Run integration tests and report +Templates without a required variable consume their upstream inputs through the structured context appended from `depends_on` outputs. The review templates additionally force an `unverified_claims` section, which a verification wave downstream can check against the actual code. + For ad-hoc prompts, use `prompt_template: { inline: "...", input: {...} }`. Static `prompt_template.input` supplies literal, local template values; it does not read upstream node output. Inline templates interpolate those static values diff --git a/packages/core/test/plugin/command.test.ts b/packages/core/test/plugin/command.test.ts index 695d46138a..fc9e4ea54d 100644 --- a/packages/core/test/plugin/command.test.ts +++ b/packages/core/test/plugin/command.test.ts @@ -106,6 +106,49 @@ describe("CommandPlugin.Plugin", () => { }), ) + it.effect("binds the tiered orchestration doctrine and depth ladder", () => + Effect.sync(() => { + expect(CommandPlugin.OrchestrationPolicyContent).toContain("## Tiered Orchestration Doctrine") + expect(CommandPlugin.OrchestrationPolicyContent).toContain("**Breadth (space for accuracy)**") + expect(CommandPlugin.OrchestrationPolicyContent).toContain("**Depth (iteration for accuracy)**") + expect(CommandPlugin.OrchestrationPolicyContent).toContain( + "The advanced tier MUST NOT do bulk work the standard tier can fan out", + ) + expect(CommandPlugin.OrchestrationPolicyContent).toContain("The standard tier MUST NOT render a final verdict") + // Tier placement is the mechanical lever (config.ts tierModel): required/review → advanced. + expect(CommandPlugin.OrchestrationPolicyContent).toContain("`review`/`review-*` workers resolve to") + expect(CommandPlugin.OrchestrationPolicyContent).toContain("## Depth Ladder") + expect(CommandPlugin.OrchestrationPolicyContent).toContain("A single wave of parallel opinions is not a") + expect(CommandPlugin.WorkflowFactsContent).toContain("Tiered Orchestration Doctrine") + expect(CommandPlugin.OrchestrationDomainsContent).toContain("two accuracy axes") + }), + ) + + it.effect("grades the review profile and mandates claim verification", () => + Effect.sync(() => { + expect(CommandPlugin.OrchestrationPolicyContent).toContain("four waves minimum") + expect(CommandPlugin.OrchestrationPolicyContent).toContain("unverified_claims") + expect(CommandPlugin.OrchestrationPolicyContent).toContain("claim-verification wave") + expect(CommandPlugin.OrchestrationPolicyContent).toContain("MUST NOT be a silent end of the graph") + expect(CommandPlugin.OrchestrationDomainsContent).toContain("**Verification wave (mandatory for module scope and larger)**") + expect(CommandPlugin.OrchestrationDomainsContent).toContain("never the end of the task") + }), + ) + + it.effect("binds verdict disposal at the terminal boundary", () => + Effect.sync(() => { + expect(CommandPlugin.OrchestrationPolicyContent).toContain("## Verdict Disposal Contract") + expect(CommandPlugin.OrchestrationPolicyContent).toContain("same wake turn") + expect(CommandPlugin.OrchestrationPolicyContent).toContain( + "Merely summarizing a non-ACCEPT verdict and ending the turn is an", + ) + expect(CommandPlugin.OrchestrationPolicyContent).toContain("escapes that guard") + expect(CommandPlugin.OrchestrationPolicyContent).toContain("Silence is not a stop decision") + expect(CommandPlugin.WorkflowFactsContent).toContain("Verdict Disposal Contract") + expect(CommandPlugin.OrchestrationDomainsContent).toContain("Verdict Disposal Contract") + }), + ) + it.effect("distinguishes required-node failure from business verdicts", () => Effect.sync(() => { expect(CommandPlugin.OrchestrationPolicyContent).toContain("`required: true` handles execution failure") @@ -297,6 +340,9 @@ describe("CommandPlugin.Plugin", () => { expect(reviewExample).toContain("required: [verdict, summary, findings, required_actions, next_action]") expect(reviewExample).toContain("required: [operation, targets]") expect(reviewExample).toContain("enum: [continue, extend, replan, complete, stop]") + // The arbiter must not be a silent terminal leaf: a conditioned + // continuation node keeps non-ACCEPT verdicts from dead-ending the graph. + expect(reviewExample).toContain("condition: 'arbitrate.output.verdict != \"ACCEPT\"'") expect(CommandPlugin.WorkflowFactsContent).toContain("an early\n`control(complete)` workflow remains terminal") expect(CommandPlugin.DagFlowContent).toContain("must actually contain the requested synthesis") }), diff --git a/packages/tui/src/feature-plugins/sidebar/dag-panel.tsx b/packages/tui/src/feature-plugins/sidebar/dag-panel.tsx index 65921cc56c..29beb6c3c5 100644 --- a/packages/tui/src/feature-plugins/sidebar/dag-panel.tsx +++ b/packages/tui/src/feature-plugins/sidebar/dag-panel.tsx @@ -4,7 +4,7 @@ import type { DagNode, DagWorkflowSummary } from "@opencode-ai/sdk/v2" import type { BuiltinTuiPlugin } from "../builtins" import { createEffect, createMemo, createSignal, For, Show } from "solid-js" import { Spinner } from "../../component/spinner" -import { dagNodeGlyph, dagStatusColor, formatDagProgress } from "../system/dag-inspector-utils" +import { computeWaves, dagNodeGlyph, dagStatusColor, formatDagProgress } from "../system/dag-inspector-utils" const id = "internal:sidebar-dag-panel" @@ -19,6 +19,10 @@ function WorkflowRow(props: { }) { const theme = () => props.api.theme.current const [nodes, setNodes] = createSignal([]) + // The API returns nodes in reverse insertion order (desc seq); flatten the + // topological waves so the list reads top-down in execution order, matching + // the DAG inspector. + const orderedNodes = createMemo(() => computeWaves(nodes()).flat()) const total = () => Number(props.summary.nodeCount) const completed = () => Number(props.summary.completedNodes) @@ -71,7 +75,7 @@ function WorkflowRow(props: { - + {(node) => ( }> @@ -124,13 +128,14 @@ function DagPanel(props: { api: TuiPluginApi; session_id: string }) { return ( 0}> - dags().length > 2 && setOpen((x) => !x)}> - 2}> - {open() ? "▼" : "▶"} - + {/* Always collapsible (unlike MCP's >2 threshold): an expanded workflow + renders a node list, so even a single DAG is tall enough to hide. + The chevron also keeps the header aligned with MCP's "▼ MCP". */} + setOpen((x) => !x)}> + {open() ? "▼" : "▶"} DAG - 2}> + {" "} ({active().length} active{terminal().length > 0 ? `, ${terminal().length} done` : ""}) @@ -138,7 +143,7 @@ function DagPanel(props: { api: TuiPluginApi; session_id: string }) { - + {(summary) => (