From 4d2ad6ce695c32b63b5f43031895877516552e7a Mon Sep 17 00:00:00 2001 From: Lex Date: Thu, 13 Aug 2026 20:18:37 +0800 Subject: [PATCH 1/3] chore(repo): remove local workflow artifacts --- .../.dag-specs/closing-audit-cygpath-fix.yaml | 141 ----- ...deep-review-diff-error-class-continue.yaml | 229 -------- .../deep-review-diff-error-class-loop1.yaml | 176 ------ .../deep-review-diff-error-class.yaml | 502 ----------------- .../deep-review-pr167-continue.yaml | 217 -------- .opencode/.dag-specs/deep-review-pr167.yaml | 424 --------------- .opencode/.dag-specs/deep-review-round2.yaml | 513 ------------------ .../deep-review-round3-continue.yaml | 177 ------ .../.dag-specs/deep-review-round3-final.yaml | 223 -------- .../final-confirmation-continue.yaml | 203 ------- .../final-confirmation-three-pr-stack.yaml | 291 ---------- .../review-parts-diff/review-contract.md | 1 - .../review-parts-diff/review-dataflow.md | 1 - .../review-parts-diff/review-prompts.md | 1 - .../review-parts-diff/review-runtime.md | 1 - .../review-parts-diff/review-style.md | 1 - .../review-parts-diff/review-tests.md | 1 - .../review-parts-diff/scope-diff.md | 1 - .../review-parts-diff/verify-suite.md | 1 - .../review-parts-final/review-config-repo.md | 1 - .../review-parts-final/review-stack.md | 1 - .../review-parts-final/verify-suite.md | 1 - .../review-parts-round3/final-audit-report.md | 73 --- .../review-parts-round3/verify-suite.md | 1 - .../.dag-specs/review-parts/explore-build.md | 110 ---- .../.dag-specs/review-parts/explore-ci.md | 117 ---- .../review-parts/explore-runtime.md | 125 ----- .../review-parts/review-architecture.md | 75 --- .../.dag-specs/review-parts/review-logic.md | 79 --- .../review-parts/review-robustness.md | 50 -- .../.dag-specs/review-parts/review-style.md | 41 -- .../review-parts/review-testability.md | 42 -- .opencode/batch-a-implement-manifest.md | 57 -- .opencode/dag-prompts/arch-gate.md | 46 -- .opencode/dag-prompts/code-explore.md | 43 -- .opencode/dag-prompts/config-explore.md | 42 -- .opencode/dag-prompts/implement.md | 49 -- .opencode/dag-prompts/integration-test.md | 38 -- .opencode/dag-prompts/patcher-assemble.md | 42 -- .opencode/dag-prompts/plan.md | 43 -- .opencode/dag-prompts/review-arch.md | 44 -- .opencode/dag-prompts/review-logic.md | 44 -- .opencode/dag-prompts/review-style.md | 44 -- .opencode/dag-prompts/test-explore.md | 42 -- .opencode/dag-prompts/verify.md | 41 -- .opencode/grill-batch-a/CONTEXT.md | 50 -- .../ADR-0001-escalation-pending-semantics.md | 25 - .../adr/ADR-0002-delivery-gated-retime.md | 69 --- .../ADR-0003-node-deadline-extended-event.md | 107 ---- .../adr/ADR-0004-lock-timeout-occams.md | 35 -- .../node-lifecycle-transitions.md | 60 -- .opencode/handoff-batch-b.md | 49 -- .opencode/opencode.jsonc | 2 +- .opencode/workflows/GRAPH-ENGINEERING.md | 41 -- .../workflows/algo-complexity-review.yaml | 246 --------- .opencode/workflows/dag-module-review-v2.yaml | 255 --------- .opencode/workflows/dag-module-review.yaml | 235 -------- .opencode/workflows/dag-review.yaml | 166 ------ .opencode/workflows/deep-perf-review.yaml | 346 ------------ .../full-codebase-critical-review.yaml | 251 --------- .opencode/workflows/perf-deep-review.yaml | 376 ------------- .opencode/workflows/review-dag-subsystem.yaml | 206 ------- .../01-q1-escalation-pending-lifecycle.md | 17 - .../issues/02-q2-delivery-gated-retime.md | 17 - .../03-q3-node-deadline-extended-event.md | 18 - .../issues/04-q3-sdk-regen-consumers.md | 15 - .../issues/05-s5-workflow-lock-timeout.md | 17 - .../issues/06-flaky-stdout-pollution.md | 16 - .../issues/07-flaky-sharenext-timing.md | 15 - .../issues/08-flaky-workspace-timing.md | 15 - .../batch-a/issues/09-promote-dev-to-main.md | 14 - .../10-backlog-phantom-cancelled-state.md | 28 - .../issues/11-backlog-spurious-t8-budget.md | 27 - .scratch/batch-b/README.md | 23 - .scratch/batch-b/abort-path-contracts.md | 53 -- .scratch/batch-b/config-lkg-spec.md | 269 --------- .scratch/batch-b/evidence.md | 61 --- .../batch-b/issues/01-u1-fork-rollback.md | 23 - .../issues/02-u2-transport-timeout-abort.md | 24 - .../issues/03-transport-midstream-stall.md | 24 - .../issues/04-f3-subscription-readiness.md | 22 - .../05-f4-type-safe-dag-store-fixtures.md | 21 - .scratch/batch-b/issues/06-o1-lkg-spec.md | 22 - .../batch-b/issues/07-o1-lkg-implement.md | 45 -- .../08-s7-recovery-invented-diagnosis.md | 23 - .../issues/09-promote-dev-main-release.md | 12 - .scratch/batch-b/s7-diagnosis.md | 53 -- .../issues/01-p8-spawn-ready-observation.md | 30 - AGENTS.md | 6 + CLAUDE.md | 178 ++++++ 90 files changed, 185 insertions(+), 7817 deletions(-) delete mode 100644 .opencode/.dag-specs/closing-audit-cygpath-fix.yaml delete mode 100644 .opencode/.dag-specs/deep-review-diff-error-class-continue.yaml delete mode 100644 .opencode/.dag-specs/deep-review-diff-error-class-loop1.yaml delete mode 100644 .opencode/.dag-specs/deep-review-diff-error-class.yaml delete mode 100644 .opencode/.dag-specs/deep-review-pr167-continue.yaml delete mode 100644 .opencode/.dag-specs/deep-review-pr167.yaml delete mode 100644 .opencode/.dag-specs/deep-review-round2.yaml delete mode 100644 .opencode/.dag-specs/deep-review-round3-continue.yaml delete mode 100644 .opencode/.dag-specs/deep-review-round3-final.yaml delete mode 100644 .opencode/.dag-specs/final-confirmation-continue.yaml delete mode 100644 .opencode/.dag-specs/final-confirmation-three-pr-stack.yaml delete mode 100644 .opencode/.dag-specs/review-parts-diff/review-contract.md delete mode 100644 .opencode/.dag-specs/review-parts-diff/review-dataflow.md delete mode 100644 .opencode/.dag-specs/review-parts-diff/review-prompts.md delete mode 100644 .opencode/.dag-specs/review-parts-diff/review-runtime.md delete mode 100644 .opencode/.dag-specs/review-parts-diff/review-style.md delete mode 100644 .opencode/.dag-specs/review-parts-diff/review-tests.md delete mode 100644 .opencode/.dag-specs/review-parts-diff/scope-diff.md delete mode 100644 .opencode/.dag-specs/review-parts-diff/verify-suite.md delete mode 100644 .opencode/.dag-specs/review-parts-final/review-config-repo.md delete mode 100644 .opencode/.dag-specs/review-parts-final/review-stack.md delete mode 100644 .opencode/.dag-specs/review-parts-final/verify-suite.md delete mode 100644 .opencode/.dag-specs/review-parts-round3/final-audit-report.md delete mode 100644 .opencode/.dag-specs/review-parts-round3/verify-suite.md delete mode 100644 .opencode/.dag-specs/review-parts/explore-build.md delete mode 100644 .opencode/.dag-specs/review-parts/explore-ci.md delete mode 100644 .opencode/.dag-specs/review-parts/explore-runtime.md delete mode 100644 .opencode/.dag-specs/review-parts/review-architecture.md delete mode 100644 .opencode/.dag-specs/review-parts/review-logic.md delete mode 100644 .opencode/.dag-specs/review-parts/review-robustness.md delete mode 100644 .opencode/.dag-specs/review-parts/review-style.md delete mode 100644 .opencode/.dag-specs/review-parts/review-testability.md delete mode 100644 .opencode/batch-a-implement-manifest.md delete mode 100644 .opencode/dag-prompts/arch-gate.md delete mode 100644 .opencode/dag-prompts/code-explore.md delete mode 100644 .opencode/dag-prompts/config-explore.md delete mode 100644 .opencode/dag-prompts/implement.md delete mode 100644 .opencode/dag-prompts/integration-test.md delete mode 100644 .opencode/dag-prompts/patcher-assemble.md delete mode 100644 .opencode/dag-prompts/plan.md delete mode 100644 .opencode/dag-prompts/review-arch.md delete mode 100644 .opencode/dag-prompts/review-logic.md delete mode 100644 .opencode/dag-prompts/review-style.md delete mode 100644 .opencode/dag-prompts/test-explore.md delete mode 100644 .opencode/dag-prompts/verify.md delete mode 100644 .opencode/grill-batch-a/CONTEXT.md delete mode 100644 .opencode/grill-batch-a/adr/ADR-0001-escalation-pending-semantics.md delete mode 100644 .opencode/grill-batch-a/adr/ADR-0002-delivery-gated-retime.md delete mode 100644 .opencode/grill-batch-a/adr/ADR-0003-node-deadline-extended-event.md delete mode 100644 .opencode/grill-batch-a/adr/ADR-0004-lock-timeout-occams.md delete mode 100644 .opencode/grill-batch-a/node-lifecycle-transitions.md delete mode 100644 .opencode/handoff-batch-b.md delete mode 100644 .opencode/workflows/GRAPH-ENGINEERING.md delete mode 100644 .opencode/workflows/algo-complexity-review.yaml delete mode 100644 .opencode/workflows/dag-module-review-v2.yaml delete mode 100644 .opencode/workflows/dag-module-review.yaml delete mode 100644 .opencode/workflows/dag-review.yaml delete mode 100644 .opencode/workflows/deep-perf-review.yaml delete mode 100644 .opencode/workflows/full-codebase-critical-review.yaml delete mode 100644 .opencode/workflows/perf-deep-review.yaml delete mode 100644 .opencode/workflows/review-dag-subsystem.yaml delete mode 100644 .scratch/batch-a/issues/01-q1-escalation-pending-lifecycle.md delete mode 100644 .scratch/batch-a/issues/02-q2-delivery-gated-retime.md delete mode 100644 .scratch/batch-a/issues/03-q3-node-deadline-extended-event.md delete mode 100644 .scratch/batch-a/issues/04-q3-sdk-regen-consumers.md delete mode 100644 .scratch/batch-a/issues/05-s5-workflow-lock-timeout.md delete mode 100644 .scratch/batch-a/issues/06-flaky-stdout-pollution.md delete mode 100644 .scratch/batch-a/issues/07-flaky-sharenext-timing.md delete mode 100644 .scratch/batch-a/issues/08-flaky-workspace-timing.md delete mode 100644 .scratch/batch-a/issues/09-promote-dev-to-main.md delete mode 100644 .scratch/batch-a/issues/10-backlog-phantom-cancelled-state.md delete mode 100644 .scratch/batch-a/issues/11-backlog-spurious-t8-budget.md delete mode 100644 .scratch/batch-b/README.md delete mode 100644 .scratch/batch-b/abort-path-contracts.md delete mode 100644 .scratch/batch-b/config-lkg-spec.md delete mode 100644 .scratch/batch-b/evidence.md delete mode 100644 .scratch/batch-b/issues/01-u1-fork-rollback.md delete mode 100644 .scratch/batch-b/issues/02-u2-transport-timeout-abort.md delete mode 100644 .scratch/batch-b/issues/03-transport-midstream-stall.md delete mode 100644 .scratch/batch-b/issues/04-f3-subscription-readiness.md delete mode 100644 .scratch/batch-b/issues/05-f4-type-safe-dag-store-fixtures.md delete mode 100644 .scratch/batch-b/issues/06-o1-lkg-spec.md delete mode 100644 .scratch/batch-b/issues/07-o1-lkg-implement.md delete mode 100644 .scratch/batch-b/issues/08-s7-recovery-invented-diagnosis.md delete mode 100644 .scratch/batch-b/issues/09-promote-dev-main-release.md delete mode 100644 .scratch/batch-b/s7-diagnosis.md delete mode 100644 .scratch/batch-c/issues/01-p8-spawn-ready-observation.md create mode 100644 CLAUDE.md diff --git a/.opencode/.dag-specs/closing-audit-cygpath-fix.yaml b/.opencode/.dag-specs/closing-audit-cygpath-fix.yaml deleted file mode 100644 index 72799b1b31..0000000000 --- a/.opencode/.dag-specs/closing-audit-cygpath-fix.yaml +++ /dev/null @@ -1,141 +0,0 @@ -title: "Closing audit: cygpath HIGH fix verification" -mode: deep -admission: - brief_revision: 1 - qa_mode: LIGHT - verdict: READY - brief: - goal: "Close the final-confirmation audit loop: verify the sole HIGH (missing cygpath conversion in release-fork.yml Extract Templates) is fixed by commit 434a4cbca on feat/dag-config-repo, per the arbiter's bounded LOOP scope" - scope: - in: - - "release-fork.yml Extract Templates step: cygpath -m guard now mirrors the models.dev step" - - "packages/opencode/script/generate.ts: contextual error for set-but-missing DAG_TEMPLATES_DIR" - - "affected gates only: typecheck opencode, SDK regen determinism (zero diff), generate.ts error/happy paths" - - "fix is committed and pushed to feat/dag-config-repo (PR #171)" - out: - - "the 3 MEDIUM + 10 LOW deferred follow-ups (arbiter excluded them from the loop)" - - "re-running the 9/9 gate suite or re-reviewing lanes (arbiter forbade both)" - constraints: - - "read-only verification" - assumptions: - - "arbiter verdict and loop_scope from dag_035a2534affe21Pdb2mCgFs6a6 are the binding contract" - acceptance_criteria: - - "Extract Templates step contains the cygpath guard pattern identical in shape to the models.dev step" - - "generate.ts throws a contextual error for missing dir and loads a real dir correctly" - - "typecheck PASS; SDK regen produces zero diff; fix commit present on feat/dag-config-repo" - evidence_required: - - "file:line citations + executed checks" - risks: - - "none material; mechanical one-pattern mirror" - review_plan: - - "one verifier node (fix existence + gates), one arbiter (final PASS/LOOP/BLOCKED)" - open_questions: [] - blocking_questions: [] -config: - name: closing-audit-cygpath-fix - max_concurrency: 2 - max_node_replan_attempts: 1 - max_total_nodes: 4 - node_defaults: - required: false - report_to_parent: false - worker_config: - timeout_ms: 600000 - nodes: - - id: verify-fix - name: "Verify: cygpath fix + affected gates" - worker_type: general - depends_on: [] - required: true - output_schema: - type: object - required: [verdict, results, anomalies] - properties: - verdict: - type: string - enum: [PASS, FAIL, BLOCKED] - results: - type: array - items: - type: object - required: [gate, command, outcome, detail] - properties: - gate: { type: string } - command: { type: string } - outcome: { type: string, enum: [PASS, FAIL, SKIPPED] } - detail: { type: string } - anomalies: { type: array, items: { type: string } } - prompt_template: - inline: | - You are an OBJECTIVE VERIFIER. Read-only (no file modifications). Current - branch: feat/dag-config-repo. Verify the HIGH-fix closure: - 1. Fix presence: .github/workflows/release-fork.yml "Extract Templates" step - contains the `command -v cygpath` guard and writes the converted - templates_dir to GITHUB_ENV; compare its shape against the models.dev - step in the same file (quote both hunks, file:line). - 2. Fix committed: `git log --oneline -3` shows the cygpath fix commit on - feat/dag-config-repo; `git diff HEAD~1 HEAD --stat` covers exactly - release-fork.yml + script/generate.ts. - 3. generate.ts contextual error: run - DAG_TEMPLATES_DIR=/nonexistent-probe bun -e "await import('./packages/opencode/script/generate.ts').then(()=>console.log('NO-THROW'),(e)=>console.log('THREW:',e.message.slice(0,120)))" - — expect THREW with the contextual message. - 4. generate.ts happy path: run the same import with - DAG_TEMPLATES_DIR pointing at ~/.config/opencode/workflows — expect the - "Loaded dag templates snapshot" log with a positive template count. - 5. typecheck: bun run typecheck (packages/opencode) - 6. SDK regen determinism: ./packages/sdk/js/script/build.ts then - `git status --short packages/sdk/js/src/v2/gen` — expect zero diff. - Verdict PASS only if all pass. Report decisive fragments. - - - id: arbitrate - name: "Arbiter: Closure Verdict" - worker_type: general - depends_on: [verify-fix] - required: true - report_to_parent: true - output_schema: - type: object - required: [verdict, reason, evidence, findings, stop_reason, next_action] - properties: - verdict: - type: string - enum: [PASS, LOOP, BLOCKED] - reason: { type: string } - evidence: - type: array - items: { type: string } - findings: - type: array - items: - type: object - required: [severity, title, evidence, status] - properties: - severity: { type: string, enum: [CRITICAL, HIGH, MEDIUM, LOW] } - title: { type: string } - description: { type: string } - evidence: { type: string } - status: { type: string, enum: [CONFIRMED, REFUTED, PARTIALLY_CONFIRMED] } - recommendation: { type: string } - stop_reason: - type: string - enum: [goal_met, correction_required, evidence_missing, no_progress, round_cap, budget_cap] - next_action: - type: string - enum: [finalize, replan, stop] - prompt_template: - inline: | - You are the ARBITER closing the final-confirmation audit loop for the - three-PR stack (#171/#169/#170). The parent loop verdict - (dag_035a2534affe21Pdb2mCgFs6a6) demanded exactly one bounded fix: the - cygpath HIGH. Judge ONLY whether verify-fix proves that closure: - - PASS: fix present + committed + contextual error works + gates green; - declare the full stack merge-ready, listing the deferred follow-ups as - accepted residuals (3 MEDIUM + 10 LOW from the parent arbiter: version - pinning, template retry, builtin third-scope docs, cascade wording, - goal route optional schema, dagFailNode union, hydration race, stack base - drift, artifact commit intent note, builtin scope tests, embed - determinism, template-update lock semantics, chore message wording) - - LOOP: only if the fix itself is wrong/incomplete (name it) - - BLOCKED: evidence missing - State reason, evidence, stop_reason, next_action explicitly. Submit via - submit_result. diff --git a/.opencode/.dag-specs/deep-review-diff-error-class-continue.yaml b/.opencode/.dag-specs/deep-review-diff-error-class-continue.yaml deleted file mode 100644 index f9cb5e4d1e..0000000000 --- a/.opencode/.dag-specs/deep-review-diff-error-class-continue.yaml +++ /dev/null @@ -1,229 +0,0 @@ -title: "Deep diff review (continuation): reuse 8 completed waves, re-run timed-out verify-claims" -mode: deep -admission: - brief_revision: 1 - qa_mode: LIGHT - verdict: READY - brief: - goal: "Continue the failed deep diff review workflow dag_039602a9fffesslSA479Re5hEt: verify-claims timed out (environmental 'timeout' class, not a task error). Reuse all 8 completed node outputs on disk; re-run only verify-claims with a larger budget, then arbitrate and finalize." - scope: - in: - - "verify-claims over the 8 persisted upstream reports in .opencode/.dag-specs/review-parts-diff/ (fresh-context claim verification against actual code)" - - "arbitrate: PASS/LOOP/BLOCKED on verified evidence incl. verify-suite gate results" - - "continuation: deep-dive (LOOP) or finalize-review (PASS)" - out: - - "re-running scope-diff or any reviewer wave (outputs reused, never re-executed)" - constraints: - - "read-only: reviewers/verifiers must not modify any file" - - "upstream report files are read-only inputs" - assumptions: - - "the 8 persisted reports are complete and trustworthy (extracted from completed nodes of the failed workflow)" - - "reused_nodes: scope-diff, review-dataflow, review-runtime, review-contract, review-prompts, review-tests, review-style, verify-suite (verdict PASS)" - - "verify-claims timeout at 600000ms was environmental (reviewer-volume saturation); budget raised to 1800000ms" - acceptance_criteria: - - "every material claim verified with CONFIRMED/REFUTED/PARTIALLY_CONFIRMED/UNRESOLVABLE against file:line" - - "no unresolved CRITICAL/HIGH" - - "arbiter emits structured PASS/LOOP/BLOCKED verdict" - - "reference prune manifest audited" - evidence_required: - - "file:line citations verified against source" - - "persisted verify-suite gate results (all PASS) as objective evidence" - risks: - - "upstream reports may contain reviewer misreadings — verify wave must check independently" - - "verify-claims may time out again despite larger budget" - review_plan: - - "verify-claims reads the 8 persisted reports + checks claims against code" - - "arbitrate rules on verified evidence (PASS/LOOP/BLOCKED, report_to_parent)" - - "LOOP -> deep-dive replan proposal; PASS -> finalize-review publishes the report" - open_questions: [] - blocking_questions: [] -config: - name: deep-review-diff-error-class-continue - max_concurrency: 2 - max_node_replan_attempts: 2 - max_total_nodes: 10 - node_defaults: - required: false - report_to_parent: false - worker_config: - timeout_ms: 900000 - nodes: - # Continuation graph: scope-diff + 6 reviewers + verify-suite COMPLETED in - # workflow dag_039602a9fffesslSA479Re5hEt; outputs persisted at - # .opencode/.dag-specs/review-parts-diff/. Only verify-claims (which timed - # out) re-runs, with a 30-minute budget. - - id: verify-claims - name: "Verify Disputed Claims (from persisted reports)" - worker_type: general - depends_on: [] - required: true - worker_config: - timeout_ms: 1800000 - output_schema: - type: object - required: [verdict, verified_claims, disputed_findings_resolution, critical_findings_status, coverage_gaps, evidence_quality, prune_audit] - properties: - verdict: - type: string - enum: [VERIFIED, GAPS, BLOCKED] - verified_claims: { type: array, items: { type: object } } - disputed_findings_resolution: { type: array, items: { type: object } } - critical_findings_status: { type: array, items: { type: object } } - coverage_gaps: { type: array, items: { type: object } } - evidence_quality: { type: string } - prune_audit: { type: array, items: { type: object } } - prompt_template: - inline: | - You are a CLAIM VERIFIER (fresh context). Read-only — do not modify any file. - - The scope wave (1 node), review wave (6 nodes), and objective gate runner - (verify-suite) already COMPLETED in the prior workflow; their reports are - persisted. Read them ALL first: - - .opencode/.dag-specs/review-parts-diff/scope-diff.md (change map + REFERENCE MANIFEST with prune decisions) - - .opencode/.dag-specs/review-parts-diff/review-dataflow.md - - .opencode/.dag-specs/review-parts-diff/review-runtime.md - - .opencode/.dag-specs/review-parts-diff/review-contract.md - - .opencode/.dag-specs/review-parts-diff/review-prompts.md - - .opencode/.dag-specs/review-parts-diff/review-tests.md - - .opencode/.dag-specs/review-parts-diff/review-style.md - - .opencode/.dag-specs/review-parts-diff/verify-suite.md (objective gates: verdict PASS) - - The review target is the uncommitted working-tree diff (`git diff HEAD`). - Your job: - 1. Extract every unverified_claims item, every reviewer disagreement, and - ALL CRITICAL/HIGH findings from the six reviewer reports. - 2. Verify each against the actual source at the cited file:line: - CONFIRMED / REFUTED / PARTIALLY_CONFIRMED / UNRESOLVABLE (cite exact line). - 3. Every factual claim in review-prompts about runtime semantics MUST be - checked against code — those prompts steer parent-agent repair decisions. - 4. Sample MEDIUM/LOW claims instead of trusting self-report. - 5. Audit the reference manifest from scope-diff.md: each prune must carry - prune_reason + replacement_coverage; record results in prune_audit. - 6. Audit scope coverage: any diff area with no evidence-bearing report is a gap. - - Verdict: - - VERIFIED: every material scope/criterion covered, no material claim unresolved - - GAPS: a bounded fresh review can close named gaps - - BLOCKED: required evidence cannot be obtained - - Submit the structured result via submit_result. coverage_gaps must name the - missing scope, evidence, and the smallest reviewer lane to add in a LOOP. - - - id: arbitrate - name: "Arbiter: Final Verdict" - worker_type: general - depends_on: [verify-claims] - required: true - report_to_parent: true - output_schema: - type: object - required: [verdict, reason, evidence, findings, loop_scope, stop_reason, next_action, prune_decisions] - properties: - verdict: - type: string - enum: [PASS, LOOP, BLOCKED] - reason: { type: string } - evidence: - type: array - items: { type: string } - findings: - type: array - items: - type: object - required: [severity, title, evidence, status] - properties: - severity: - type: string - enum: [CRITICAL, HIGH, MEDIUM, LOW] - title: { type: string } - description: { type: string } - evidence: { type: string } - status: - type: string - enum: [CONFIRMED, REFUTED, PARTIALLY_CONFIRMED] - recommendation: { type: string } - loop_scope: - type: array - items: { type: string } - stop_reason: - type: string - enum: [goal_met, correction_required, evidence_missing, no_progress, round_cap, budget_cap] - next_action: - type: string - enum: [finalize, replan, stop] - prune_decisions: - type: array - items: - type: object - required: [node, prune_reason, replacement_coverage] - properties: - node: { type: string } - prune_reason: { type: string } - replacement_coverage: { type: string } - prompt_template: - inline: | - You are the ARBITER for this deep review of the uncommitted diff. You rule on - VERIFIED evidence only. - - Evidence base: - - The verify-claims structured output (primary — it verified reviewer claims - against code in fresh context). - - Persisted upstream reports in .opencode/.dag-specs/review-parts-diff/ - (read verify-suite.md for the objective gate results — verdict PASS — and - any reviewer report needed for context). - - Your job: - 1. For each CONFIRMED finding, assess true severity (reviewers may over/under-rate) - 2. Discard REFUTED claims; correct PARTIALLY_CONFIRMED descriptions - 3. Deduplicate findings sharing a root cause; rank by impact - 4. A broken prompt-guidance factual claim is at least HIGH (it mis-steers - future parent-agent repair decisions); any real objective-gate failure is - at least HIGH (verify-suite reported PASS — treat contradictions with it - as disputes to resolve from evidence) - 5. Audit prune_decisions from the scope manifest (via verify-claims prune_audit); - missing prune_reason/replacement_coverage forbids PASS - 6. Verdict: - - PASS: no unresolved material finding; scope and evidence coverage complete - - LOOP: a bounded targeted review can resolve specific omissions/disputes - - BLOCKED: evidence insufficient, critical contradiction unresolved, or ceiling reached - 7. LOOP names the minimal new scope in loop_scope; never rerun completed waves - 8. State reason, evidence, stop_reason, next_action explicitly - - Parent disposal contract: PASS -> finalize; LOOP -> pause/replan/resume fresh - targeted nodes; BLOCKED -> stop. Submit via submit_result. - - - id: deep-dive - name: "Plan the bounded fresh review loop" - worker_type: general - depends_on: [arbitrate] - condition: 'arbitrate.output.verdict == "LOOP"' - required: true - report_to_parent: true - prompt_template: - inline: | - The arbiter required LOOP. Produce a minimal replan fragment proposal for a - NEW local review wave. Include only the missing or disputed scope from the - arbiter's loop_scope, assign NEW node IDs, preserve real artifact - dependencies, add a fresh verifier and a new arbiter, and stay within the - workflow caps. Read-only: do not fix code. Reused completed outputs live in - .opencode/.dag-specs/review-parts-diff/ — never propose re-running them. - - Return the loop reason, new nodes, dependencies, evidence each node must - collect, acceptance condition, and stop reason. - - - id: finalize-review - name: "Publish the accepted deep-review report" - worker_type: general - depends_on: [arbitrate] - condition: 'arbitrate.output.verdict == "PASS"' - required: true - report_to_parent: true - prompt_template: - inline: | - Publish the final evidence-backed review report for the uncommitted diff. - Include: scope coverage, confirmed findings (severity-ranked with file:line - evidence), discarded/refuted claims, objective gate results (verify-suite), - residual low-risk issues, and the final PASS reason. Source material: - arbiter verdict, verify-claims output, and the persisted reports in - .opencode/.dag-specs/review-parts-diff/. Do not introduce new findings or - claims that were not verified upstream. diff --git a/.opencode/.dag-specs/deep-review-diff-error-class-loop1.yaml b/.opencode/.dag-specs/deep-review-diff-error-class-loop1.yaml deleted file mode 100644 index 246cb581b4..0000000000 --- a/.opencode/.dag-specs/deep-review-diff-error-class-loop1.yaml +++ /dev/null @@ -1,176 +0,0 @@ -# Loop-1 extension fragment for workflow deep-review-diff-error-class-continue. -# Scope is EXACTLY arbitrate.loop_scope: one bounded repair-spec author for the -# five adjudicated guidance-doc defects, one fresh verifier, one new arbiter. -# Read-only everywhere. The 8 persisted reports in .opencode/.dag-specs/review-parts-diff/ -# are read-only inputs and are NEVER re-executed. Caps check: 4 existing + 3 = 7 nodes -# <= max_total_nodes 10; linear chain fits max_concurrency 2; single bounded wave fits -# max_node_replan_attempts 2. -nodes: - - id: loop1-repair-spec - name: "Deep-dive: corrected repair spec for the 5 adjudicated guidance-doc defects" - worker_type: general - depends_on: [arbitrate] - required: true - worker_config: - timeout_ms: 900000 - output_schema: - type: object - required: [defects, corrections_incorporated, optional_test_plan] - properties: - defects: - type: array - items: - type: object - required: [finding_id, target, current_text, replacement_text, justification] - properties: - finding_id: { type: string } - target: { type: string } - current_text: { type: string } - replacement_text: { type: string } - justification: { type: string } - corrections_incorporated: - type: array - items: { type: string } - optional_test_plan: - type: array - items: { type: string } - prompt_template: - inline: | - You are a READ-ONLY repair-spec author. Do not modify any file. The upstream - deep review's arbiter ruled LOOP; its adjudicated findings ({{arbitrate}}) are - your sole mandate. Persisted upstream reports are read-only context in - .opencode/.dag-specs/review-parts-diff/. Never propose re-running any wave. - - For each defect below produce EXACT replacement wording (verbatim markdown) for - the diff author, anchored to the cited file:line, and justify each line against - the adjudicated evidence: - - 1. F1 HIGH — packages/core/src/plugin/command/workflow.md:412 dependency-cascade - triage row: rewrite detection to the two REAL cascade signals — - (a) required-root failure leaves transitive dependents durably pending while - the workflow is failed (scheduling.ts:108-120,162); (b) optional-root failure - places 'Dependency "X" failed/skipped' placeholder text inside dependents' - PROMPT CONTEXT only (loop.ts:132-136; dag-wake-integration.test.ts:438). - Never claim that prefix appears in error_reason (19-site producer inventory + - dag.ts:488-494 refute it). Fix the response: repair root X; for (a) continue - with root replaced — pending dependents resume naturally and must NOT be - replaced; for (b) re-run dependents that consumed placeholder input. - 2. F2 MEDIUM — workflow.md:409 exec_failed row: add ownership-loss - (recovery.ts:141) and workflow-collateral reasons (dag.ts:487-495; - loop.ts:272,830: orchestrator_unresponsive / required node(s) failed / - workflow_failed) plus replan-ceiling (dag.ts:599), condition-eval (loop.ts:114), - template-resolution (loop.ts:173); gate the response on the reason (config - fixes only for model/auth/provider/template/condition; replace+rerun for - ownership loss; sibling root-cause for collateral kills). - 3. F3 MEDIUM — workflow.md:410 verdict_fail row: split ran-but-broke-contract - (submit_result/schema/fingerprint) vs never-ran (unresolved placeholders - loop.ts:186 / review input contract loop.ts:153 → fix template or input - mapping, then rerun). - 4. F4 MEDIUM — workflow.md:404 + dag-flow.txt:37 'every failed node carries an - error_class': qualify — cancelled-via-replan rows project status=failed, - error_reason 'cancelled via replan', NO error_class (projector.ts:320-323; - excluded by digest filter loop.ts:856); same for pre-migration rows; add the - one-line 'no repair needed' note. - 5. F7 LOW — value-set enumeration: mark workflow.md:409-412 + dag-flow.txt:37 - lists non-exhaustive ('e.g.') or add push_exhausted with a 'reserved, never - emitted' note; align dag-event.ts:253 / sql.ts:65 / groups/dag.ts:42-44 - comments. Bundle with F1 wording. - - Mandatory corrections to incorporate (from arbitration; list each in - corrections_incorporated): - - 'required node(s) failed: X' is ephemeral WorkflowFailed-event data only — - never claim it is visible in wake digest or status output. - - Cancelled-via-replan rows carry no error_class (projector.ts:320-323). - - The loop.ts:857 Effect.catch arm does not catch defects (they are orDie'd); - actual DB-error behavior is abort + guarded() log + retry. - - Digest filter is loop.ts:856; 'every failed node' sentence is workflow.md:404; - exec_failed tally 10/5/4. - - Read the live files verbatim before writing replacements: - packages/core/src/plugin/command/workflow.md (esp. :401-427) and - packages/core/src/plugin/command/dag-flow.txt (:34-46), plus the runtime anchors - above. Optional: append F5 test-invariant assertions (cancelled row keeps - errorClass===null; cancelled node absent from wake 'Failed nodes:') as - optional_test_plan. Submit via submit_result. - - - id: loop1-verify-repair - name: "Fresh verification of the repair spec against source" - worker_type: general - depends_on: [loop1-repair-spec] - required: true - worker_config: - timeout_ms: 900000 - output_schema: - type: object - required: [verdict, claim_checks] - properties: - verdict: - type: string - enum: [VERIFIED, GAPS, BLOCKED] - claim_checks: - type: array - items: - type: object - required: [finding_id, status, evidence] - properties: - finding_id: { type: string } - status: - type: string - enum: [CONFIRMED, REFUTED, PARTIALLY_CONFIRMED, UNRESOLVABLE] - evidence: { type: string } - prompt_template: - inline: | - You are a fresh CLAIM VERIFIER. Read-only — do not modify any file. - Input: the repair spec ({{loop1-repair-spec}}) plus the persisted upstream - reports in .opencode/.dag-specs/review-parts-diff/ (read-only; do not re-run - any wave). - - 1. For each of the 5 replacement blocks: re-read the target file:line verbatim - (workflow.md, dag-flow.txt) and every runtime anchor the spec cites - (loop.ts:132-136,855-862; projector.ts:320-323; dag.ts:487-495,599; - scheduling.ts:108-120,162; recovery.ts:79-141; sql.ts:65; dag-event.ts:253; - groups/dag.ts:42-44). - 2. Assert each replacement makes NO claim already refuted upstream: the - 'Dependency "X" failed/skipped' prefix must never be described as an - error_reason value; workflow-collateral reasons must not be claimed visible - in wake/status; error_class claims must exclude cancelled-via-replan rows. - 3. Confirm the three mandatory arbitration corrections are present and accurate. - 4. Verdict: VERIFIED only if every block is CONFIRMED; otherwise GAPS naming the - exact block and defect. Submit via submit_result. - - - id: loop1-arbitrate - name: "Loop-1 arbiter: accept repair spec or stop" - worker_type: general - depends_on: [loop1-verify-repair] - required: true - report_to_parent: true - output_schema: - type: object - required: [verdict, reason, loop_scope, stop_reason, next_action] - properties: - verdict: - type: string - enum: [PASS, LOOP, BLOCKED] - reason: { type: string } - loop_scope: { type: array, items: { type: string } } - stop_reason: - type: string - enum: [goal_met, correction_required, evidence_missing, no_progress, round_cap, budget_cap] - next_action: - type: string - enum: [finalize, replan, stop] - prompt_template: - inline: | - You are the LOOP-1 ARBITER. Read-only. Rule on the repair spec - ({{loop1-repair-spec}}) as checked by the fresh verifier - ({{loop1-verify-repair}}); upstream adjudication context is in the persisted - reports under .opencode/.dag-specs/review-parts-diff/ (verify-suite gates all - PASS — objective evidence stands). - - PASS: all 5 defects covered by exact replacement wording, all verifier - claim_checks CONFIRMED, mandatory corrections incorporated, no new CRITICAL/HIGH. - Then stop_reason=goal_met, next_action=finalize; the repair spec is the final - deliverable for the diff author. - LOOP: allowed at most once more and only for a named defect in the spec itself; - this is the bounded final wave (max_node_replan_attempts=2) — on any further - non-PASS report BLOCKED/round_cap/stop with residual findings instead of looping. diff --git a/.opencode/.dag-specs/deep-review-diff-error-class.yaml b/.opencode/.dag-specs/deep-review-diff-error-class.yaml deleted file mode 100644 index b75202b05d..0000000000 --- a/.opencode/.dag-specs/deep-review-diff-error-class.yaml +++ /dev/null @@ -1,502 +0,0 @@ -title: "Deep diff review: error_class exposure + wake attribution + failure triage guidance" -mode: deep -admission: - brief_revision: 1 - qa_mode: LIGHT - verdict: READY - brief: - goal: "Jointly deep-review the uncommitted working-tree diff: failure-class exposure (error_class persistence, status/wake/httpapi/SDK surfaces), wake attribution semantics, and the failure-triage guidance prompts — plus the earlier Resume-first optimization in dag-flow.txt" - scope: - in: - - "git diff HEAD across packages/core (migration, sql, store, projector, workflow.md, dag-flow.txt, schema.json, migration.gen.ts, schema.gen.ts)" - - "packages/opencode (runtime/loop.ts wake summary, tool/workflow.ts status, httpapi groups+handlers, test/dag edits)" - - "packages/sdk/js/src/v2/gen/types.gen.ts regenerated field" - - "cross-cutting consistency: docs vs runtime semantics, schema vs SDK vs httpapi, test coverage of new behavior" - out: - - "unchanged DAG module behavior outside the diff" - - "untracked .opencode/workflows/*.yaml experiment files" - - "TUI rendering changes (no TUI files were modified)" - constraints: - - "reviewers are read-only; do not modify any file" - - "every material finding must cite file:line evidence" - assumptions: - - "the working tree diff against HEAD is the complete review target (uncommitted; includes the pre-existing dag-flow.txt Resume-first section)" - - "migration/schema.gen/registry files were generated by bun script/migration.ts" - acceptance_criteria: - - "no unresolved CRITICAL/HIGH finding on verified evidence" - - "error_class value set is consistent end-to-end (event trigger enum -> projector -> read surfaces)" - - "workflow.md/dag-flow.txt guidance matches actual runtime semantics" - - "regenerated SDK diff is exactly the intended field; repo contract obligations (check:generated, httpapi exercise) satisfied" - evidence_required: - - "file:line citations for static claims" - - "executed test-suite and typecheck results for runtime claims" - risks: - - "prompt guidance diverging from runtime semantics (e.g. timeout handling, terminal irreversibility)" - - "wake-path races or ordering regressions in loop.ts delivery" - - "documentation drift between the new triage section and existing guidance sections" - review_plan: - - "wave 1: consolidated diff scope map + reference manifest" - - "wave 2: six parallel dimension reviewers (dataflow integrity, runtime semantics, httpapi/SDK contract, prompt accuracy, test coverage, style)" - - "wave 3: claim verification + objective test-suite execution" - - "wave 4: advanced-tier arbiter with PASS/LOOP/BLOCKED verdict and bounded-loop continuation" - open_questions: [] - blocking_questions: [] -config: - name: deep-review-diff-error-class - max_concurrency: 8 - max_node_replan_attempts: 3 - max_total_nodes: 25 - node_defaults: - required: false - report_to_parent: false - worker_config: - timeout_ms: 600000 - nodes: - - id: scope-diff - name: "Scope: consolidated diff map + manifest" - worker_type: explore - depends_on: [] - required: true - prompt_template: - inline: | - You are the SCOPE MAPPER for a deep review of the uncommitted working-tree diff - in this repository. Read-only — do not modify any file. - - Run `git diff HEAD --stat` and `git diff HEAD` (split by file if large; also - `git status --short` for untracked context) and produce: - - 1. Consolidated change map: every changed file grouped by area: - - persistence: packages/core/src/database/migration/20260803073521_workflow_node_error_class.ts, sql.ts, store.ts, projector.ts, migration.gen.ts, schema.gen.ts, schema.json - - read surfaces: packages/opencode/src/tool/workflow.ts (status output), src/dag/runtime/loop.ts (wake summary), src/server/routes/instance/httpapi/groups/dag.ts + handlers/dag.ts - - SDK: packages/sdk/js/src/v2/gen/types.gen.ts - - guidance docs: packages/core/src/plugin/command/workflow.md, dag-flow.txt - - tests: packages/opencode/test/dag/{fixtures.ts,workflow-tool.test.ts,dag-wake-integration.test.ts} - 2. For each area, the intent of the change (infer from content; the stated - purpose: persist the node failure class that previously existed only in - dag.node.failed events, expose it on every agent-visible surface, and add - failure-triage guidance so the parent agent repairs the failed node instead - of restarting the workflow). - 3. Verify and emit the reference manifest exactly as follows, correcting only - factual errors: - - reference_template: deep-review-dag-module - - added nodes: scope-diff, review-dataflow, review-runtime, review-contract, review-prompts, review-tests, review-style, verify-suite - - pruned lanes: {node: explore-core, prune_reason: target is a bounded 16-file diff, not the whole module, replacement_coverage: scope-diff consolidated change map injected into every reviewer lane} and the same prune_reason/replacement_coverage for explore-runtime, explore-templates, explore-integrations - 4. List any changed file whose hunks appear UNRELATED to the stated purpose - (these become arbiter audit items). - 5. Note pre-existing uncommitted content: the dag-flow.txt "Resume-first" - section predates this session's triage sentence — review it as part of the - joint diff per the user's request. - - Output the full map; downstream reviewers receive it as context. - - - id: review-dataflow - name: "Review: error_class dataflow integrity" - worker_type: general - depends_on: [scope-diff] - prompt_template: - inline: | - You are a DATAFLOW INTEGRITY REVIEWER. Read-only — do not modify any file. - Review target: the uncommitted working-tree diff (`git diff HEAD`). - Upstream scope map is provided as context; verify against actual code. - - Trace the error_class value end to end: - - Producer: packages/schema/src/dag-event.ts NodeFailed trigger literals - (exec_failed/push_exhausted/verdict_fail/timeout) and every - dag.nodeFailed call site (packages/opencode/src/dag/{dag.ts,runtime/loop.ts,runtime/spawn.ts,runtime/recovery.ts}) — which triggers are actually produced? Note any literal with no producer (push_exhausted) and whether that matters. - - Persistence: migration 20260803073521_workflow_node_error_class, sql.ts - column, store.ts NodeRow.errorClass + mapNode, projector.ts NodeFailed - projection — check the projector sets error_class from event.data.trigger, - that NodeCancelled/NodeSkipped leave it null deliberately, and that the - projector race from-guards are untouched/correct. - - Read surfaces: tool/workflow.ts status output, loop.ts wake node-line - suffix and failed-workflow digest filter (status failed && errorClass != - null), httpapi NodeResponse optional field + handler mapper, SDK - types.gen.ts DagNode.error_class. - - Consistency: same value vocabulary on every surface; any consumer that - assumes values the producer never emits; any surface that strips or - renames the field inconsistently. - - MANDATORY output format: - 1. findings: array of {severity: CRITICAL|HIGH|MEDIUM|LOW, title, description, evidence: "file:line", recommendation} - 2. unverified_claims: array of strings - 3. summary: 2-3 sentences - - - id: review-runtime - name: "Review: wake-loop runtime semantics" - worker_type: general - depends_on: [scope-diff] - prompt_template: - inline: | - You are a RUNTIME SEMANTICS REVIEWER. Read-only — do not modify any file. - Review target: packages/opencode/src/dag/runtime/loop.ts uncommitted hunks - (the new failed-workflow attribution read and the node-line class suffix) - plus their interaction with surrounding delivery logic. - - Review criteria: - - The new failuresByWorkflow read happens inside the wake delivery generator: - correctness of the Effect.catch fallback to [], lock/ordering hazards vs - the existing evalLock/workflowLock discipline, and whether the extra store - read can delay or reorder wake delivery or wake_reported persistence. - - Double reporting: a failed wake-eligible node can appear both in the node - line (with class suffix) and in its workflow's Failed nodes digest — assess - whether that is acceptable, confusing, or harmful to the parent's decision. - - Truncation: per-line slice(0, 300) and output slice(0, 500) — multibyte - safety, worst-case summary size for a workflow with many failed nodes. - - Interaction with orchestrator_unresponsive and the actionableDagIDs - mandatory-action line; any path where a failed workflow now delivers - attribution but the mandatory-action guidance contradicts it. - - Regression risk for unchanged paths: completed/cancelled workflows must - produce exactly the old terminal line; skipped-node wakes unchanged. - - MANDATORY output format: - 1. findings: array of {severity: CRITICAL|HIGH|MEDIUM|LOW, title, description, evidence: "file:line", recommendation} - 2. unverified_claims: array of strings - 3. failure_scenarios: array of {scenario, impact, likelihood: HIGH|MEDIUM|LOW} - 4. summary: 2-3 sentences - - - id: review-contract - name: "Review: HTTP API + SDK contract" - worker_type: general - depends_on: [scope-diff] - prompt_template: - inline: | - You are an API CONTRACT REVIEWER. Read-only — do not modify any file. - Review target: the uncommitted httpapi + SDK hunks plus the repo's contract - obligations documented in AGENTS.md (repo root and packages/opencode). - - Review criteria: - - NodeResponse schema (packages/opencode/src/server/routes/instance/httpapi/groups/dag.ts) - vs handler mapper (handlers/dag.ts) vs regenerated SDK type - (packages/sdk/js/src/v2/gen/types.gen.ts DagNode): exact field-name and - optionality alignment; encoder strips undeclared fields — is anything - emitted but undeclared or declared but never emitted? - - Regeneration freshness: run `git diff HEAD -- packages/sdk/js/src/v2/gen` - and confirm the ONLY change is the optional error_class field; check no - other generated file drifted. - - AGENTS.md obligations: route response shape changes require updating - test/server/httpapi-exercise scenarios — inspect the dag scenarios under - packages/opencode/test/server/httpapi-exercise/ and determine whether an - optional response-field addition needs scenario updates (check how the - scenario asserts responses). Also check whether packages/client duplicates - DagNode anywhere that now drifts. - - TUI consumers of DagNode (packages/tui dag-inspector): any consumer that - could display the new field or breaks on it. - - MANDATORY output format: - 1. findings: array of {severity: CRITICAL|HIGH|MEDIUM|LOW, title, description, evidence: "file:line", recommendation} - 2. unverified_claims: array of strings - 3. summary: 2-3 sentences - - - id: review-prompts - name: "Review: guidance accuracy vs runtime semantics" - worker_type: general - depends_on: [scope-diff] - prompt_template: - inline: | - You are a PROMPT-ACCURACY REVIEWER. Read-only — do not modify any file. - Review target: packages/core/src/plugin/command/workflow.md new "Node - failure triage" section and the dag-flow.txt Resume-first triage sentence, - checked against ACTUAL runtime behavior. These prompts steer the parent - agent's wake-time repair decisions; any semantic drift is a bug. - - Verify every factual claim against code: - - "timeout: the runtime cancelled its child session at the deadline" — check - packages/opencode/src/dag/runtime/spawn.ts timeout path (promptSvc.cancel) - and recovery.ts deadline handling. Is "Check its child_session_id for - partial artifacts" accurate (session/messages persist after cancel)? - - "exec_failed: unknown/wrong model, auth, rate-limit, connection errors, or - recovery reasons ('no child session on recovery', 'child session failed - (recovered)')" — confirm these exact reason strings/classes exist - (recovery.ts ~79/108, spawn.ts ~300 Cause.pretty path) and that a wrong - model-config error actually surfaces as exec_failed with discoverable text. - - "verdict_fail: missing submit_result, schema rejection, review fingerprint - mismatch" — check capture.ts settleCapturedOutput reasons plus - loop.ts unresolved-placeholder/review-input-contract paths. - - "Budget exhaustion: replan attempt ceiling exceeded / Total node ceiling - exceeded" — confirm exact strings (dag.ts) and that the Escalation - cross-reference exists. - - "Workflow still live: control(pause) -> control(replan) with replacement - node under NEW id ... extend also works" — verify against dag.ts _extend/_replan. - - "Workflow terminal failed: terminal status is irreversible — you cannot - replan it" — verify the _replan terminal guard and - getValidNextWorkflowStatuses FAILED -> [ARCHIVED]; verify the continuation - workflow advice (reuse completed outputs as static input) is feasible. - - Cross-doc consistency: no contradiction with the Crash recovery section, - Verdict Disposal Contract, Bounded Repair, Adaptive Replanning, or the - pre-existing dag-flow.txt Resume-first steps; both docs agree on the - replacement-node-under-new-id mechanic. - - Triage table row "reason starts with Dependency X failed/skipped" — is - that how cascade actually appears to the agent (check loop.ts dependency - interpolation and scheduler behavior for failed deps)? - - MANDATORY output format: - 1. findings: array of {severity: CRITICAL|HIGH|MEDIUM|LOW, title, description, evidence: "file:line", recommendation} - 2. unverified_claims: array of strings - 3. summary: 2-3 sentences - - - id: review-tests - name: "Review: test coverage of the diff" - worker_type: general - depends_on: [scope-diff] - prompt_template: - inline: | - You are a TEST COVERAGE REVIEWER. Read-only — do not modify any file. - Review target: the uncommitted test hunks (packages/opencode/test/dag/ - fixtures.ts, workflow-tool.test.ts, dag-wake-integration.test.ts) versus the - behaviors the diff introduces. - - Review criteria: - - Do the new assertions actually cover: projector persistence of error_class - (asserted via store.getNode), status-tool error_class output, wake - node-line class suffix, wake failed-workflow Failed nodes digest - (exact string assertions — are they too brittle or exactly right)? - - Missing coverage candidates — assess each: recovery.ts failure paths - (timeout/exec_failed/verdict_fail on recovery), NodeCancelled rows keeping - errorClass null, multiple failed nodes digest ordering/truncation, empty - errorReason fallback "unknown error", httpapi node endpoint returning the - field. - - Test hygiene: fixture defaults keep old tests valid; added mock node in - workflow-tool.test.ts is realistic (status transition, seq, timestamps); - no duplicated logic into tests. - - Do existing suites elsewhere depend on NodeRow shape and were they all - updated (search for NodeRow literals in packages/core tests too)? - - MANDATORY output format: - 1. findings: array of {severity: CRITICAL|HIGH|MEDIUM|LOW, title, description, evidence: "file:line or test path", recommendation} - 2. unverified_claims: array of strings - 3. coverage_gaps: array of {path, untested_scenarios: string[]} - 4. summary: 2-3 sentences - - - id: review-style - name: "Review: style & repo conventions" - worker_type: general - depends_on: [scope-diff] - prompt_template: - inline: | - You are a STYLE & CONVENTIONS REVIEWER. Read-only — do not modify any file. - Review target: all uncommitted code hunks (`git diff HEAD`). - - Review against the root AGENTS.md Style Guide and package AGENTS.md files: - - Comments: allowed only for non-obvious constraints — judge the new - comments (sql.ts column comment, groups/dag.ts field comment, migration - none, loop.ts none) against that rule. - - const over let, no else, no unnecessary destructuring, no import aliases, - functional style; Effect conventions from packages/opencode/AGENTS.md - (Effect.fn naming, catch usage, no nested service yields). - - Migration conventions: file naming/id format vs existing migrations; - drizzle snake_case column rule; registry/schema.gen regenerated properly. - - Doc style: workflow.md section placement/heading level consistency with - neighbors; table formatting; dag-flow.txt sentence integration. - - Test style: matches existing assertion patterns in the touched files. - - MANDATORY output format: - 1. findings: array of {severity: CRITICAL|HIGH|MEDIUM|LOW, title, description, evidence: "file:line", recommendation} - 2. unverified_claims: array of strings - 3. summary: 2-3 sentences - - - id: verify-claims - name: "Verify Disputed Claims" - worker_type: general - depends_on: [review-dataflow, review-runtime, review-contract, review-prompts, review-tests, review-style] - required: true - output_schema: - type: object - required: [verdict, verified_claims, disputed_findings_resolution, critical_findings_status, coverage_gaps, evidence_quality] - properties: - verdict: - type: string - enum: [VERIFIED, GAPS, BLOCKED] - verified_claims: { type: array, items: { type: object } } - disputed_findings_resolution: { type: array, items: { type: object } } - critical_findings_status: { type: array, items: { type: object } } - coverage_gaps: { type: array, items: { type: object } } - evidence_quality: { type: string } - prompt_template: - inline: | - You are a CLAIM VERIFIER. Read-only — do not modify any file. - - Six reviewers produced findings and unverified_claims about the uncommitted - diff. You are the fresh-context verification wave. Check every - unverified/disputed/CRITICAL/HIGH claim against the actual code; sample - MEDIUM/LOW claims instead of trusting self-report. - - Upstream context contains all 6 reviewer outputs. Extract: - 1. All unverified_claims items - 2. Findings where reviewers disagree - 3. All CRITICAL/HIGH findings (must be verified regardless) - 4. Any diff area with no evidence-bearing reviewer output - 5. Any prompt-guidance factual claim (review-prompts) — these ALL need code - confirmation because the prompts steer parent-agent repair decisions - - For each claim, read the actual source at the cited location and determine - CONFIRMED / REFUTED / PARTIALLY_CONFIRMED / UNRESOLVABLE with the exact line. - - Verdict: - - VERIFIED: every material scope/criterion covered, no material claim unresolved - - GAPS: a bounded fresh review can close named gaps - - BLOCKED: required evidence cannot be obtained - - Submit the structured result. coverage_gaps must name the missing scope, - evidence, and the smallest reviewer lane that should be added in a LOOP. - - - id: verify-suite - name: "Verify: execute tests and gates" - worker_type: general - depends_on: [scope-diff] - required: true - worker_config: - timeout_ms: 900000 - output_schema: - type: object - required: [verdict, results, anomalies] - properties: - verdict: - type: string - enum: [PASS, FAIL, BLOCKED] - results: - type: array - items: - type: object - required: [gate, command, outcome, detail] - properties: - gate: { type: string } - command: { type: string } - outcome: { type: string, enum: [PASS, FAIL, SKIPPED] } - detail: { type: string } - anomalies: { type: array, items: { type: string } } - prompt_template: - inline: | - You are an OBJECTIVE GATE RUNNER. You execute commands and report results - faithfully. Do NOT modify any file. Do not fix failures — report them. - - Run these gates in order (each from the stated directory): - 1. typecheck core: bun run typecheck (packages/core) - 2. typecheck opencode: bun run typecheck (packages/opencode) - 3. opencode DAG suites: bun test test/dag (packages/opencode) - 4. core DAG suites: bun test test/dag-core.test.ts test/dag-projector-drift.test.ts test/dag-store-summaries.test.ts test/plugin/command.test.ts (packages/core) - 5. migration check: bun script/migration.ts --check (packages/core) - 6. HttpAPI contract: bun run test:httpapi --fail-on-missing (packages/opencode) - 7. SDK freshness: in packages/sdk/js run bun run build, then - `git diff -- packages/sdk/js/src/v2/gen`. IMPORTANT interpretation: this - working tree intentionally adds DagNode.error_class uncommitted — the gate - PASSES iff the ONLY diff lines in src/v2/gen are the added error_class - field (one insertion in types.gen.ts) and the regeneration produced no - OTHER drift. Do not fail on the intended field itself. - - For each gate record outcome + the decisive output fragment. Verdict PASS - only if all gates pass under that interpretation; FAIL lists each failing - gate with evidence; BLOCKED if a gate cannot run (state why). - - - id: arbitrate - name: "Arbiter: Final Verdict" - worker_type: general - depends_on: [verify-claims, verify-suite] - required: true - report_to_parent: true - output_schema: - type: object - required: [verdict, reason, evidence, findings, loop_scope, stop_reason, next_action, prune_decisions] - properties: - verdict: - type: string - enum: [PASS, LOOP, BLOCKED] - reason: { type: string } - evidence: - type: array - items: { type: string } - findings: - type: array - items: - type: object - required: [severity, title, evidence, status] - properties: - severity: - type: string - enum: [CRITICAL, HIGH, MEDIUM, LOW] - title: { type: string } - description: { type: string } - evidence: { type: string } - status: - type: string - enum: [CONFIRMED, REFUTED, PARTIALLY_CONFIRMED] - recommendation: { type: string } - loop_scope: - type: array - items: { type: string } - stop_reason: - type: string - enum: [goal_met, correction_required, evidence_missing, no_progress, round_cap, budget_cap] - next_action: - type: string - enum: [finalize, replan, stop] - prune_decisions: - type: array - items: - type: object - required: [node, prune_reason, replacement_coverage] - properties: - node: { type: string } - prune_reason: { type: string } - replacement_coverage: { type: string } - prompt_template: - inline: | - You are the ARBITER for this deep review of the uncommitted diff. You rule on - VERIFIED evidence only. The verification wave (verify-claims) and the - objective gate runner (verify-suite) outputs are your primary evidence base; - the 6 reviewer outputs are context. - - Your job: - 1. For each CONFIRMED finding, assess true severity (reviewers may over/under-rate) - 2. Discard REFUTED claims; correct PARTIALLY_CONFIRMED descriptions - 3. Deduplicate findings sharing a root cause; rank by impact - 4. Weight verify-suite gate failures: any real gate failure under the stated - interpretation is at least HIGH; a broken prompt-guidance factual claim is - at least HIGH because it mis-steers future parent-agent repair decisions - 5. Audit the reference manifest from scope-diff: prune decisions must carry - prune_reason + replacement_coverage; flag any changed file unrelated to the - stated purpose - 6. Verdict: - - PASS: no unresolved material finding; scope and evidence coverage complete - - LOOP: a bounded targeted review can resolve specific omissions/disputes - - BLOCKED: evidence insufficient, critical contradiction unresolved, or ceiling reached - 7. LOOP names the minimal new scope; never rerun the whole graph - 8. State reason, evidence, stop_reason, next_action explicitly - - Parent disposal contract: PASS -> finalize; LOOP -> pause/replan/resume fresh - targeted review + verification + arbiter nodes; BLOCKED -> stop. - - Submit your structured verdict via submit_result. - - - id: deep-dive - name: "Plan the bounded fresh review loop" - worker_type: general - depends_on: [arbitrate] - condition: 'arbitrate.output.verdict == "LOOP"' - required: true - report_to_parent: true - prompt_template: - inline: | - The arbiter required LOOP. Produce a minimal replan fragment proposal for a - NEW local review wave. Include only the missing or disputed scope from the - arbiter's loop_scope, assign NEW node IDs, preserve real artifact - dependencies, add a fresh verifier and a new arbiter, and stay within the - workflow caps. Read-only: do not fix code. - - Return the loop reason, new nodes, dependencies, evidence each node must - collect, acceptance condition, and stop reason. The parent must pause, replan, - and resume; it must never restart completed nodes or create a cycle. - - - id: finalize-review - name: "Publish the accepted deep-review report" - worker_type: general - depends_on: [arbitrate] - condition: 'arbitrate.output.verdict == "PASS"' - required: true - report_to_parent: true - prompt_template: - inline: | - Publish the final evidence-backed review report for the uncommitted diff. - Include: scope coverage, confirmed findings (severity-ranked with file:line - evidence), discarded/refuted claims, gate-runner results, residual low-risk - issues, and the final PASS reason. Do not introduce new findings or claims - that were not verified upstream. diff --git a/.opencode/.dag-specs/deep-review-pr167-continue.yaml b/.opencode/.dag-specs/deep-review-pr167-continue.yaml deleted file mode 100644 index a0b7d202f5..0000000000 --- a/.opencode/.dag-specs/deep-review-pr167-continue.yaml +++ /dev/null @@ -1,217 +0,0 @@ -title: "Deep Review: PR #167 dag-config-repo (continue from completed waves)" -mode: deep -admission: - brief_revision: 1 - qa_mode: LIGHT - verdict: READY - brief: - goal: "从已完成的探索(3)/审查(5)波续跑 PR #167 深度 review:仅执行声明验证 → 仲裁 → 续波,上游输出已落盘在 .opencode/.dag-specs/review-parts/" - scope: - in: - - "verify-claims:对 .opencode/.dag-specs/review-parts/*.md 中的 8 份探索/审查输出做 fresh-context 声明验证(含 CRITICAL/HIGH、unverified_claims、争议、prune manifest、测试执行)" - - "arbitrate:基于验证结果给出 PASS/LOOP/BLOCKED" - - "continuation:deep-dive(LOOP 时)或 finalize-review(PASS 时)" - out: - - "重新运行探索/审查波(其结果已存在且被直接复用)" - constraints: - - "只读:不修改任何代码文件" - - "verify 必须对照实际代码(file:line)检查声明,禁止基于审查者自报通过" - - "上游报告文件只读,不改写" - assumptions: - - "8 份上游报告完整且可信(已提取自上一轮 workflow 的 completed 节点)" - - "模型 400 为偶发,重试即可" - acceptance_criteria: - - "所有 material 声明被验证并给出 CONFIRMED/REFUTED/PARTIALLY_CONFIRMED/UNRESOLVABLE" - - "无未解析的 CRITICAL/HIGH(除非证据确实无法获取且已指名)" - - "仲裁给出结构化 PASS/LOOP/BLOCKED 判决" - - "prune manifest 被审计" - evidence_required: - - "file:line 引用(verify 对照实际源码)" - - "测试执行结果(bun test dag-workflows / workflow-tool)" - risks: - - "上游报告可能含审查者误读,verify 波必须独立对照源码" - - "模型 400 偶发可能导致本节点再次失败" - review_plan: - - "verify-claims(读取 8 份落盘报告 + 对照源码验证)" - - "arbitrate(PASS/LOOP/BLOCKED,report_to_parent)" - - "continuation:LOOP → deep-dive 提案 replan;PASS → finalize-review 发布报告" - open_questions: [] - blocking_questions: [] -config: - name: deep-review-pr167-continue - max_concurrency: 2 - max_node_replan_attempts: 2 - max_total_nodes: 10 - node_defaults: - required: false - report_to_parent: false - worker_config: - timeout_ms: 900000 - nodes: - # Continuation graph: explore(3) + review(5) were COMPLETED in workflow - # dag_03996cfb; their outputs are on disk at .opencode/.dag-specs/review-parts/. - # Wave 3: Claim Verification (reads the persisted upstream reports) - - id: verify-claims - name: "Verify Disputed Claims (from persisted reports)" - worker_type: general - depends_on: [] - required: true - worker_config: - timeout_ms: 1800000 - output_schema: - type: object - required: [verdict, verified_claims, disputed_findings_resolution, critical_findings_status, coverage_gaps, evidence_quality, prune_audit] - properties: - verdict: - type: string - enum: [VERIFIED, GAPS, BLOCKED] - verified_claims: { type: array, items: { type: object } } - disputed_findings_resolution: { type: array, items: { type: object } } - critical_findings_status: { type: array, items: { type: object } } - coverage_gaps: { type: array, items: { type: object } } - evidence_quality: { type: string } - prune_audit: { type: array, items: { type: object } } - prompt_template: - inline: | - 你是声明验证者(fresh-context)。只读,禁止修改任何文件。工作目录:本仓库。 - - 探索波(3 节点)与审查波(5 节点)已在上一轮 workflow 中完成,其报告已落盘,你必须先读取它们: - - .opencode/.dag-specs/review-parts/explore-runtime.md(内置 scope 解析 + PRUNE MANIFEST) - - .opencode/.dag-specs/review-parts/explore-build.md(构建注入 + 命令) - - .opencode/.dag-specs/review-parts/explore-ci.md(release 流水线 + 测试) - - .opencode/.dag-specs/review-parts/review-architecture.md - - .opencode/.dag-specs/review-parts/review-logic.md - - .opencode/.dag-specs/review-parts/review-style.md - - .opencode/.dag-specs/review-parts/review-testability.md - - .opencode/.dag-specs/review-parts/review-robustness.md - - 审查目标仍是 git diff origin/dev...HEAD(PR #167,4 commits)。你负责对整个本地审查波的 fresh-context 复核。 - - 时间预算(30 分钟硬上限):按下列优先级推进,不要在单个声明上耗尽预算: - 1. 所有 CRITICAL/HIGH —— 逐一对照 file:line 验证(各报告内已给出 evidence,务必读实际源码确认) - 2. 所有审查者 unverified_claims —— 对照实际代码验证 - 3. 审查者之间冲突的 findings - 4. 抽样 3-5 条 MEDIUM/LOW 声明 - 5. PRUNE MANIFEST 审计(来自 explore-runtime.md) - 6. 执行测试/类型检查证据:cd packages/opencode && bun test test/dag/dag-workflows.test.ts(应 14 pass)&& bun test test/dag/workflow-tool.test.ts(应 26 pass)&& bun run typecheck。 - 若时间不足,以 GAPS 判决结束并指名未验证的剩余部分——不要含糊带过。 - - 特别留意审查波已经标注的候选阻塞项,逐一核实其真实性: - - review-architecture:/dag-template-update 命令只注册了 core plugin draft 一处,未接入应用 Command 服务,运行时可能不可达 - - review-logic H1:Windows 发布构建 DAG_TEMPLATES_DIR 路径未做 cygpath 转换 - - review-logic H2 / review-testability:builtin scope 零测试覆盖,被删的仓库自检无替代 - 对每条声明,读实际源码(file:line)判定: - - CONFIRMED: 代码确如审查者所述(引用确切行) - - REFUTED: 代码并非审查者所述(解释原因) - - PARTIALLY_CONFIRMED: 方向正确但表述不精确 - - UNRESOLVABLE: 静态分析无法确定 - - 判决: - - VERIFIED: 每个 material 范围/准则都被覆盖且无 material 声明悬而未决 - - GAPS: 有界的 fresh review 可关闭已命名的覆盖/证据缺口 - - BLOCKED: 无法获得所需证据或审查波不可审计 - - 通过 submit_result 提交结构化结果。coverage_gaps 必须命名缺失的范围、证据、以及 LOOP 时应新增的最小审查 lane。 - # ---- Wave 4: Arbitration ---- - - id: arbitrate - name: "Arbiter: Final Verdict" - worker_type: general - depends_on: [verify-claims] - required: true - report_to_parent: true - output_schema: - type: object - required: [verdict, reason, evidence, findings, loop_scope, stop_reason, next_action, prune_decisions] - properties: - verdict: - type: string - enum: [PASS, LOOP, BLOCKED] - reason: { type: string } - evidence: - type: array - items: { type: string } - findings: - type: array - items: - type: object - required: [severity, title, evidence, status] - properties: - severity: - type: string - enum: [CRITICAL, HIGH, MEDIUM, LOW] - title: - type: string - description: - type: string - evidence: - type: string - status: - type: string - enum: [CONFIRMED, REFUTED, PARTIALLY_CONFIRMED] - recommendation: - type: string - loop_scope: - type: array - items: - type: string - stop_reason: - type: string - enum: [goal_met, correction_required, evidence_missing, no_progress, round_cap, budget_cap] - next_action: - type: string - enum: [finalize, replan, stop] - prune_decisions: - type: array - items: - type: object - required: [node, prune_reason, replacement_coverage] - properties: - node: { type: string } - prune_reason: { type: string } - replacement_coverage: { type: string } - prompt_template: - inline: | - 你是 PR #167 深度 review 的仲裁者。你只对已验证的证据下结论。 - - 验证波已检查所有 unverified/disputed 声明,其输出是你的主要证据基础(verify-claims 的 submit_result 输出)。5 个审查者报告在 .opencode/.dag-specs/review-parts/review-*.md,可作为补充上下文。 - - 你的职责: - 1. 对每个 CONFIRMED finding 评估真实 severity(审查者可能高估/低估) - 2. REFUTED 声明直接丢弃,不进 findings - 3. PARTIALLY_CONFIRMED 以修正后的描述纳入 - 4. 去重描述同一根因的 findings - 5. 按影响排序 - 6. 给出 fail-closed 门禁判决: - - PASS: 无未解决的 material finding;范围与证据覆盖完整 - - LOOP: 有界定向审查可解决特定遗漏或争议 - - BLOCKED: 证据不足、关键矛盾未解决、进度停滞、或图上限耗尽 - 7. LOOP 必须命名最小新审查/验证范围。绝不意味着重跑整个图或重启已完成节点。 - 8. 审计每个父声明的 prune:缺失 prune_reason 或 replacement_coverage 禁止 PASS。 - 9. 陈述 reason、evidence、stop_reason 与确切 next_action。裸结论不是有效判决。 - - 父会话处置契约:PASS → finalize;LOOP → pause/replan/resume 全新定向审查+验证+仲裁节点;BLOCKED → stop。父会话不得把 LOOP 重新解释为建议性接受。 - - 通过 submit_result 提交结构化判决。 - # ---- Continuation ---- - - id: deep-dive - name: "Plan the bounded fresh review loop" - worker_type: general - depends_on: [arbitrate] - condition: 'arbitrate.output.verdict == "LOOP"' - required: true - report_to_parent: true - prompt_template: - inline: | - 仲裁者要求 LOOP。为新的定向审查波产出最小 replan fragment 提案:只包含缺失或有争议的范围,分配新节点 ID,保持真实工件依赖,新增 fresh 验证者与新仲裁者,并保持在图上限内。只读:不要修代码。 - - 返回 loop 原因、新节点、依赖、每个节点必须收集的证据、验收条件与 stop reason。父会话必须 pause → replan → resume;绝不重启已完成节点或创建环。 - - id: finalize-review - name: "Publish the accepted deep-review report" - worker_type: general - depends_on: [arbitrate] - condition: 'arbitrate.output.verdict == "PASS"' - required: true - report_to_parent: true - prompt_template: - inline: | - 发布最终证据驱动的 review 报告:范围覆盖、确认的 findings、丢弃/反驳的声明、验证证据、残余低风险项、最终 PASS 理由。不引入上游未验证的新 findings 或声明。报告应直接写入 .opencode/.dag-specs/review-parts/FINAL-REPORT.md,并在会话最终文本中给出完整摘要。 diff --git a/.opencode/.dag-specs/deep-review-pr167.yaml b/.opencode/.dag-specs/deep-review-pr167.yaml deleted file mode 100644 index 055db0f6ff..0000000000 --- a/.opencode/.dag-specs/deep-review-pr167.yaml +++ /dev/null @@ -1,424 +0,0 @@ -title: "Deep Review: PR #167 dag-config-repo" -mode: deep -admission: - brief_revision: 1 - qa_mode: LIGHT - verdict: READY - brief: - goal: "深度 review PR #167(feat/dag-config-repo → dev)的全部变更:DAG 参考模板抽离到独立配置仓库 + /dag-template-update 命令 + release 资产打包 + 二进制内置模板(builtin 三级 scope)" - scope: - in: - - "git diff origin/dev...HEAD 的所有变更:packages/opencode/src/dag/workflows.ts、packages/opencode/src/tool/workflow.ts、packages/opencode/script/generate.ts、packages/opencode/script/build.ts、packages/core/src/plugin/command.ts、packages/core/src/plugin/command/dag-flow.txt、packages/core/src/plugin/command/dag-template-update.txt、.github/workflows/release-fork.yml、测试文件变更(dag-workflows.test.ts)" - - "相关集成上下文:opencode-dag-config 仓库(唯一权威源)、全局 workflows 目录、测试覆盖" - out: - - "本 PR 未改动的 DAG 运行时核心(scheduler/recovery/loop/状态机)" - - "TUI 深挖" - - "8 个未跟踪 yaml 模板内容逐行审查(非主对象,仅作上下文)" - constraints: - - "只读:不修改任何文件" - - "每个 reviewer 必须引用 file:line 证据" - - "无法静态确认的声明必须列入 unverified_claims" - - "verify 波必须对照实际代码检查所有未验证/争议声明,禁止基于 reviewer 自报通过" - assumptions: - - "diff 基准为 origin/dev...HEAD(4 个 commit:676e0463e/6ffc7a712/98e4c0624/2ee59d874)" - - "已修复项(ReferenceError 守卫、Entry 去重、M4 空 glob、重复下载删除)属于审查对象的一部分,需确认修复正确而非回退" - acceptance_criteria: - - "所有 material findings 有 file:line 证据" - - "无未解析的 CRITICAL/HIGH 发现" - - "测试执行(bun test dag-workflows/workflow-tool)与类型检查结果作为证据" - - "prune manifest 被 verify 波审计" - evidence_required: - - "file:line 引用" - - "测试执行结果" - - "release-fork.yml 静态行为分析(job 图、环境变量注入链)" - risks: - - "release 流水线/配置仓库生态的实际行为无法在本地完全验证,只能静态审查" - - "builtin 注入仅在构建时生效,运行时行为需靠代码审查推断" - review_plan: - - "波1 探索:3 个并行 lane(运行时解析/构建注入/CI 流水线)" - - "波2 审查:5 个并行维度(架构/逻辑/风格/测试/健壮性)" - - "波3 验证:fresh-context 对照实际代码检查所有 unverified/disputed/CRITICAL/HIGH 声明" - - "波4 仲裁:PASS/LOOP/BLOCKED,report_to_parent" - - "续波:LOOP → deep-dive 提案 replan fragment;PASS → finalize-review 发布报告" - open_questions: [] - blocking_questions: [] -config: - name: deep-review-pr167 - max_concurrency: 5 - max_node_replan_attempts: 3 - max_total_nodes: 30 - node_defaults: - required: false - report_to_parent: false - worker_config: - timeout_ms: 600000 - nodes: - # ---- Wave 1: Exploration (parallel) ---- - # Derived from reference_template: deep-review-dag-module.yaml. - # PRUNE manifest (audited by verify-claims): - # - explore-core -> replaced by explore-runtime (targets workflows.ts/workflow.ts instead of dag lifecycle core) - # prune_reason: review target is the PR diff, not the untouched dag lifecycle module - # replacement_coverage: review-logic/architecture cover the changed runtime surface - # - explore-templates -> folded into explore-build (prompt .txt + command registration + generate/build injection) - # prune_reason: template rendering pipeline untouched by this PR; only builtin data injection changed - # replacement_coverage: explore-build covers generate.ts/build.ts/command.ts/prompt files - # - explore-integrations -> pruned; TUI/schema/SDK untouched by this PR - # prune_reason: out of scope per admission brief - # replacement_coverage: explore-ci covers the release/artifact integration surface - # - explore-runtime -> retargeted to explore-runtime (builtin scope resolution) with new scope - # prune_reason: original lane targets dag/runtime/ which this PR does not modify - # replacement_coverage: workflows.ts resolve/list/readWorkflowSpec fully covered by explore-runtime - - id: explore-runtime - name: "Explore: builtin scope resolution" - worker_type: explore - depends_on: [] - prompt_template: - inline: | - TASK CONTEXT: 深度 review PR #167。diff 命令:git diff origin/dev...HEAD(工作目录:本仓库)。变更集中在 DAG 模板库三级 scope(project/global/builtin)。 - - MANIFEST(派生自 reference deep-review-dag-module.yaml,需记录输出供 verify 审计): - pruned: explore-core (targeted dag lifecycle core, untouched by PR), explore-templates (folded into explore-build), explore-integrations (TUI/schema/SDK untouched); retargeted: explore-runtime (was dag/runtime/, now builtin scope resolution) - - Explore the changed runtime resolution surface: - - packages/opencode/src/dag/workflows.ts (full file): Entry interface, scopes(), resolve(), list(), searchPaths(), isBuiltinPath(), builtinName(), builtinEntry(), parseMeta(), describe(), builtinTemplates() with the typeof OPENCODE_DAG_TEMPLATES guard - - packages/opencode/src/tool/workflow.ts: readWorkflowSpec() builtin branch, resolveSpecPath() bare-name resolution, searchedScopes() helper, list action empty-library message - - Output: - 1. Resolution order and shadowing semantics (project > global > builtin) with line refs - 2. The builtin data flow: where OPENCODE_DAG_TEMPLATES is declared/read, what happens when undefined (dev/test) vs injected (release build) - 3. readWorkflowSpec builtin path: how builtin:// paths are parsed, error handling when content missing, YAML parse failure paths - 4. list() behavior: dedup, sorting, metadata extraction (parseMeta tolerance for malformed specs) - 5. searchedScopes() and not-found/empty messages: what scopes are named, when builtin is mentioned - 6. Any boundary/edge cases: empty builtin map, builtin name shadowed by project, malformed builtin content - - id: explore-build - name: "Explore: build injection & commands" - worker_type: explore - depends_on: [] - prompt_template: - inline: | - TASK CONTEXT: 深度 review PR #167。diff 命令:git diff origin/dev...HEAD。变更集中在模板内嵌构建注入 + /dag-template-update 命令。 - - Explore: - - packages/opencode/script/generate.ts: loadDagTemplatesData() (DAG_TEMPLATES_DIR env contract, Glob scan, name extraction, JSON.stringify output), dagTemplatesData export, comparison with loadModelsData() pattern - - packages/opencode/script/build.ts: the define injection of OPENCODE_DAG_TEMPLATES, how generated outputs are consumed - - packages/core/src/plugin/command.ts: the /dag-template-update registration (draft.update pattern), how .txt content is compiled in - - packages/core/src/plugin/command/dag-template-update.txt (full): zip download flow, dry-run classification (NEW/UNCHANGED/UPDATE), QA options, timestamped backup, concurrency lock (mkdir atomicity, retry, cleanup), verify content comparison, failure handling (download/extract/backup abort), config dir resolution order - - packages/core/src/plugin/command/dag-flow.txt: two-scope (+builtin?) description consistency with the actual three-tier resolution - - Output: - 1. The full build-time data path: DAG_TEMPLATES_DIR → generate.ts → dagTemplatesData → build.ts define → binary constant - 2. What happens in dev when DAG_TEMPLATES_DIR is unset (both generate.ts behavior and the builtinTemplates() guard) - 3. The command registration pattern (how dag-flow and dag-template-update compare) - 4. The prompt file's lock/backup/verify/failure semantics line by line (M1/M2/M3 requirements: lock exists+retry+cleanup, backup-failure aborts overwrite, verify compares content not just list) - 5. Any inconsistency between dag-flow.txt's described scopes and the actual three-tier runtime - 6. Edge cases: empty template dir, non-yaml files in dir, name collision with project/global templates - - id: explore-ci - name: "Explore: release pipeline & tests" - worker_type: explore - depends_on: [] - prompt_template: - inline: | - TASK CONTEXT: 深度 review PR #167。diff 命令:git diff origin/dev...HEAD。变更集中在 release 流水线 + 测试。 - - Explore: - - .github/workflows/release-fork.yml (full): package-templates job (clone opencode-dag-config, nullglob guard, cp inside non-empty branch, tar packaging), Upload Templates Artifact, build-cli job (needs package-templates, download artifact, extract, DAG_TEMPLATES_DIR via GITHUB_ENV), Build CLI step env inheritance, release job (needs build-cli + package-templates, --target github.sha, download-artifact merge-multiple, dag-templates.tar.gz as release asset, SHA256SUMS) - - packages/opencode/test/dag/dag-workflows.test.ts (full): what the 14 tests cover (resolve/list/shadowing/parse tolerance), what was deleted (change-review test) and why that is consistent - - packages/opencode/test/dag/workflow-tool.test.ts: spot-check coverage of the workflow tool list/resolve behavior - - packages/opencode/test/dag/dag-workflow-lock.test.ts: concurrency lock tests (what lock semantics are exercised) - - Output: - 1. Job graph: package-templates → build-cli → release, with needs chains and if conditions - 2. The env injection chain: artifact download → tar extraction → GITHUB_ENV DAG_TEMPLATES_DIR → Build CLI step (verify env inheritance without explicit env:) - 3. Empty-glob behavior: what happens when opencode-dag-config has no yaml (warning + empty archive vs fail) - 4. Release asset flow: which artifacts land in the release, --target github.sha semantics - 5. Test coverage inventory: what dag-workflows.test.ts tests, what the deleted change-review test verified, coverage of builtin scope (note: no injection in test env — how is builtin tested, if at all?) - 6. Any CI regression risk: job-level if conditions, checkout behavior on push vs workflow_dispatch - # ---- Wave 2: Review (parallel, 5 dimensions) ---- - - id: review-architecture - name: "Review: Architecture" - worker_type: general - depends_on: [explore-runtime, explore-build, explore-ci] - prompt_template: - inline: | - 你是 PR #167 的架构审查者。只读,禁止修改任何文件。工作目录:本仓库。 - - 审查目标:git diff origin/dev...HEAD 的全部变更(4 commits:676e0463e / 6ffc7a712 / 98e4c0624 / 2ee59d874)。先运行 git diff origin/dev...HEAD 获取完整变更,再结合上游探索结果(三个 explore 输出已提供)对照实际代码验证。 - - 设计背景:DAG 参考模板从主仓库抽离到独立配置仓库(LeWxDeX/opencode-dag-config)成为唯一权威源;/dag-template-update 命令做日常更新(zip 下载、预演分类、备份 QA);每次 release 打包 dag-templates.tar.gz 资产 + 通过 DAG_TEMPLATES_DIR 构建注入把模板内嵌进二进制(builtin 三级 scope:project > global > builtin),封闭网络可用;主仓库不再 tracked 模板。 - - 审查准则: - - 三级 scope 设计的合理性:项目 > 全局 > builtin 的优先级/遮蔽语义是否清晰且与文档一致(dag-flow.txt / workflow.md / README) - - 单一权威源迁移是否完整:主仓库删除模板后,测试/文档/命令引用是否还有残留指向已删除的模板 - - 构建注入契约:DAG_TEMPLATES_DIR → generate.ts → define 注入的架构边界(与 OPENCODE_MODELS_DEV 先例对比);dev 环境(未注入)与 release 环境(注入)的双模式是否正确 - - 发布流水线架构:资产打包 + 二进制内嵌双渠道的职责划分;package-templates job 与 build-cli 的耦合度是否合理 - - 模块边界:模板解析(workflows.ts)与命令层(workflow.ts)的职责切分;generate.ts 的数据注入是否保持了 generate 脚本的单一职责 - - 与仓库既有模式的一致性(AGENTS.md 扩展不变量:自包含 layer、服务注入等与本 PR 相关的部分) - - 输出(强制格式): - 1. findings: [{severity: CRITICAL|HIGH|MEDIUM|LOW, title, description, evidence: "file:line", recommendation}] - 2. unverified_claims: string[] —— 无法用 file:line 确认的断言 - 3. summary: 2-3 句总体评估 - - id: review-logic - name: "Review: Logic Correctness" - worker_type: general - depends_on: [explore-runtime, explore-build, explore-ci] - prompt_template: - inline: | - 你是 PR #167 的逻辑正确性审查者。只读,禁止修改任何文件。工作目录:本仓库。 - - 审查目标:git diff origin/dev...HEAD 全部变更。先运行 git diff origin/dev...HEAD,再对照实际代码。 - - 审查准则: - - resolve() 三级回退:项目文件 → 全局文件 → builtin map;extension 循环与 scope 循环的嵌套顺序;describe()/parseMeta() 对坏 YAML 的容错 - - list() 去重/排序:project 遮蔽 global 遮蔽 builtin 的顺序一致性;builtin 条目 path(builtin://name)与文件条目的混合排序 - - readWorkflowSpec() builtin 分支:builtinName() 解析、content 缺失的报错、YAML 解析失败路径(workflowSpecParseError)是否与文件路径分支一致 - - builtinTemplates() 的 typeof 守卫:dev/test(无全局绑定)与 release(define 注入)两种环境的行为;`declare const` + typeof 守卫的 TS 语义 - - searchedScopes():builtin 存在与否条件下的消息构造;searchPaths 可变性 - - generate.ts loadDagTemplatesData():Glob 扫描、文件名 → 名称提取(.yaml/.yml)、路径拼接安全、JSON.stringify 输出;DAG_TEMPLATES_DIR 未设置时返回 "undefined" 字符串的语义 - - dag-template-update.txt 提示词逻辑(M1/M2/M3):mkdir 锁的原子性/重试/清理、备份失败中止、verify 内容对比、下载/解压失败处理、配置目录解析顺序(OPENCODE_CONFIG_DIR / XDG_CONFIG_HOME) - - release-fork.yml:nullglob + 空数组 cp 守卫、GITHUB_ENV 注入时机、--target github.sha、job needs 链 - - 边界条件:空 builtin map、builtin 被项目同名遮蔽、builtin 内容坏 YAML、DAG_TEMPLATES_DIR 指向空目录、模板名含特殊字符 - - 输出(强制格式): - 1. findings: [{severity, title, description, evidence: "file:line", recommendation}] - 2. unverified_claims: string[] - 3. summary: 2-3 句总体评估 - - id: review-style - name: "Review: Code Style & Conventions" - worker_type: general - depends_on: [explore-runtime, explore-build, explore-ci] - prompt_template: - inline: | - 你是 PR #167 的风格与惯例审查者。只读。工作目录:本仓库。 - - 审查目标:git diff origin/dev...HEAD 中所有 TS/TSX 与 YAML 变更。 - - 对照 AGENTS.md Style Guide: - - 无多余解构(用点号访问) - - 无 import 别名 / star import - - const 优先于 let;三元/早退优先于重赋值 - - 无 else(早退) - - 不预提取单次 helper(除非复用或命名真实概念) - - Effect 生成器:服务绑定到命名变量 - - 非显而易见的约束加注释,明显赋值不加 - - 动态 import 用于启动敏感路径的重模块 - - 提示词 .txt 文件的可读性与结构一致性(dag-template-update.txt 与 dag-flow.txt 的风格对齐) - - 也检查:命名一致性(builtinTemplates/builtinEntry/builtinName/isBuiltinPath)、类型标注纪律(依赖推断)、文件组织与导出模式(workflows.ts 顶层导出 + namespace 投影)。 - - 输出(强制格式): - 1. findings: [{severity, title, description, evidence: "file:line", recommendation}] - 2. unverified_claims: string[] - 3. summary: 2-3 句总体评估 - - id: review-testability - name: "Review: Testability & Coverage" - worker_type: general - depends_on: [explore-runtime, explore-build, explore-ci] - prompt_template: - inline: | - 你是 PR #167 的测试与覆盖审查者。只读。工作目录:本仓库。 - - 审查目标:git diff origin/dev...HEAD 的变更及对应测试。运行相关测试确认状态: - - cd packages/opencode && bun test test/dag/dag-workflows.test.ts(应 14 pass 0 fail) - - bun test test/dag/workflow-tool.test.ts(应 26 pass 0 fail) - - bun run typecheck - - 审查准则: - - 已删测试的合理性:change-review 测试删除后,"主仓库不再 ship 模板"的行为是否仍有测试锚点?删除是否导致某行为失去回归保护? - - builtin scope 的测试覆盖:测试环境无 OPENCODE_DAG_TEMPLATES 注入(typeof 守卫返回 {}),builtin 路径(resolve builtin 分支、readWorkflowSpec builtin 分支、list builtin 条目)实际**没有**被测试——这是覆盖缺口还是可接受? - - 测试是否测试真实实现而非复制逻辑(AGENTS.md 测试纪律) - - M1 并发锁是否有测试(dag-workflow-lock.test.ts 覆盖什么) - - release-fork.yml 的 bash 逻辑(nullglob 守卫、GITHUB_ENV)是否有任何形式的验证(或只能靠静态审查) - - generate.ts 的 DAG_TEMPLATES_DIR 加载是否有测试 - - 输出(强制格式): - 1. findings: [{severity, title, description, evidence: "file:line 或 test 路径", recommendation}] - 2. unverified_claims: string[] - 3. coverage_gaps: [{path, untested_scenarios[]}] - 4. summary: 2-3 句总体评估 - - id: review-robustness - name: "Review: Runtime Robustness" - worker_type: general - depends_on: [explore-runtime, explore-build, explore-ci] - prompt_template: - inline: | - 你是 PR #167 的运行时健壮性审查者。只读。工作目录:本仓库。 - - 审查目标:git diff origin/dev...HEAD 变更在真实环境下的失败模式。 - - 审查准则: - - 封闭网络场景:二进制内嵌模板(builtin)离线可用性;无全局目录、无项目目录、无内置注入的"全空"场景行为链 - - 发布失败模式:opencode-dag-config clone 失败 / 空模板 / tar 失败 / artifact 上传失败 → 各 job 的失败传播(needs 链是否会导致 release 中止) - - 环境变量注入:DAG_TEMPLATES_DIR 经 GITHUB_ENV 的跨步骤可见性;如果 build-cli 在某 runner 上无该变量,构建是否静默无内置模板(静默降级 vs 显式报错) - - /dag-template-update 的并发:mkdir 锁在进程崩溃后残留(孤儿锁)的处理;锁重试次数;备份文件累积(.bak 文件是否清理) - - 下载/解压失败:codeload 404/网络错误/坏 zip → 提示词是否强制原文报错且不伪造成功 - - 模板内容信任边界:内置模板与配置仓库同信任级(说明文档是否明确);模板内容中的恶意 YAML 是否只影响元数据解析(parseMeta 容错) - - 路径安全:Glob 扫描结果与 path.join 的路径穿越风险(模板名 ../ 等) - - 输出(强制格式): - 1. findings: [{severity, title, description, evidence: "file:line", recommendation}] - 2. unverified_claims: string[] - 3. failure_scenarios: [{scenario, impact, likelihood: HIGH|MEDIUM|LOW}] - 4. summary: 2-3 句总体评估 - # ---- Wave 3: Claim Verification ---- - - id: verify-claims - name: "Verify Disputed Claims" - worker_type: general - depends_on: [review-architecture, review-logic, review-style, review-testability, review-robustness] - required: true - worker_config: - timeout_ms: 1800000 - output_schema: - type: object - required: [verdict, verified_claims, disputed_findings_resolution, critical_findings_status, coverage_gaps, evidence_quality, prune_audit] - properties: - verdict: - type: string - enum: [VERIFIED, GAPS, BLOCKED] - verified_claims: { type: array, items: { type: object } } - disputed_findings_resolution: { type: array, items: { type: object } } - critical_findings_status: { type: array, items: { type: object } } - coverage_gaps: { type: array, items: { type: object } } - evidence_quality: { type: string } - prune_audit: { type: array, items: { type: object } } - prompt_template: - inline: | - 你是声明验证者(fresh-context)。只读,禁止修改任何文件。工作目录:本仓库。 - - 五个审查者对 PR #167(git diff origin/dev...HEAD)产出 findings 与 unverified_claims。你是对整个本地审查波的 fresh-context 复核。任务:检查每个 unverified/disputed/CRITICAL/HIGH 声明,并审计请求的范围/验收标准是否真的被覆盖。抽样检查 MEDIUM/LOW 声明,不要信任审查者自报。 - - 时间预算(30 分钟硬上限):按下列优先级推进,不要在单个声明上耗尽预算: - 1. 所有 CRITICAL/HIGH —— 逐一对照 file:line 验证 - 2. 所有审查者 unverified_claims —— 对照实际代码验证 - 3. 审查者之间冲突的 findings - 4. 抽样 3-5 条 MEDIUM/LOW 声明 - 5. PRUNE MANIFEST 审计(来自 explore-runtime 输出) - 6. 执行测试/类型检查证据:cd packages/opencode && bun test test/dag/dag-workflows.test.ts(应 14 pass)&& bun test test/dag/workflow-tool.test.ts(应 26 pass)&& bun run typecheck。除非审查者点名,否则不跑完整 dag-workflow-lock 套件。 - 若时间不足,以 GAPS 判决结束并指名未验证的剩余部分——不要含糊带过。 - - 上游上下文包含 3 个探索 + 5 个审查者输出。提取: - 1. 每个审查者 unverified_claims 数组的所有条目 - 2. 审查者之间冲突的 findings(severity 或结论矛盾) - 3. 所有 CRITICAL/HIGH —— 必须逐一验证 - 4. 没有任何证据性输出的请求文件区域/集成/风险/审查准则 - 5. explore-runtime 输出中的 PRUNE MANIFEST:每个 prune 必须有 prune_reason + replacement_coverage,缺失即审计失败 - - 对每条声明,读实际源码(file:line)判定: - - CONFIRMED: 代码确如审查者所述(引用确切行) - - REFUTED: 代码并非审查者所述(解释原因) - - PARTIALLY_CONFIRMED: 方向正确但表述不精确 - - UNRESOLVABLE: 静态分析无法确定 - - 实际执行验证(允许运行命令,仅在审查者点名或时间允许时): - - cd packages/opencode && bun test test/dag/dag-workflows.test.ts 与 workflow-tool.test.ts 确认测试状态 - - DAG_TEMPLATES_DIR= bun script/generate.ts 验证加载路径(如果审查涉及) - - 判决: - - VERIFIED: 每个 material 范围/准则都被覆盖且无 material 声明悬而未决 - - GAPS: 有界的 fresh review 可关闭已命名的覆盖/证据缺口 - - BLOCKED: 无法获得所需证据或审查波不可审计 - - 通过 submit_result 提交结构化结果。coverage_gaps 必须命名缺失的范围、证据、以及 LOOP 时应新增的最小审查 lane。 - # ---- Wave 4: Arbitration ---- - - id: arbitrate - name: "Arbiter: Final Verdict" - worker_type: general - depends_on: [verify-claims] - required: true - report_to_parent: true - output_schema: - type: object - required: [verdict, reason, evidence, findings, loop_scope, stop_reason, next_action, prune_decisions] - properties: - verdict: - type: string - enum: [PASS, LOOP, BLOCKED] - reason: { type: string } - evidence: - type: array - items: { type: string } - findings: - type: array - items: - type: object - required: [severity, title, evidence, status] - properties: - severity: - type: string - enum: [CRITICAL, HIGH, MEDIUM, LOW] - title: - type: string - description: - type: string - evidence: - type: string - status: - type: string - enum: [CONFIRMED, REFUTED, PARTIALLY_CONFIRMED] - recommendation: - type: string - loop_scope: - type: array - items: - type: string - stop_reason: - type: string - enum: [goal_met, correction_required, evidence_missing, no_progress, round_cap, budget_cap] - next_action: - type: string - enum: [finalize, replan, stop] - prune_decisions: - type: array - items: - type: object - required: [node, prune_reason, replacement_coverage] - properties: - node: { type: string } - prune_reason: { type: string } - replacement_coverage: { type: string } - prompt_template: - inline: | - 你是 PR #167 深度 review 的仲裁者。你只对已验证的证据下结论。 - - 验证波已检查所有 unverified/disputed 声明,其输出是你的主要证据基础。5 个审查者输出作为上下文。 - - 你的职责: - 1. 对每个 CONFIRMED finding 评估真实 severity(审查者可能高估/低估) - 2. REFUTED 声明直接丢弃,不进 findings - 3. PARTIALLY_CONFIRMED 以修正后的描述纳入 - 4. 去重描述同一根因的 findings - 5. 按影响排序 - 6. 给出 fail-closed 门禁判决: - - PASS: 无未解决的 material finding;范围与证据覆盖完整 - - LOOP: 有界定向审查可解决特定遗漏或争议 - - BLOCKED: 证据不足、关键矛盾未解决、进度停滞、或图上限耗尽 - 7. LOOP 必须命名最小新审查/验证范围。绝不意味着重跑整个图或重启已完成节点。 - 8. 审计每个父声明的 prune:缺失 prune_reason 或 replacement_coverage 禁止 PASS。 - 9. 陈述 reason、evidence、stop_reason 与确切 next_action。裸结论不是有效判决。 - - 父会话处置契约:PASS → finalize;LOOP → pause/replan/resume 全新定向审查+验证+仲裁节点;BLOCKED → stop。父会话不得把 LOOP 重新解释为建议性接受。 - - 通过 submit_result 提交结构化判决。 - # ---- Continuation ---- - - id: deep-dive - name: "Plan the bounded fresh review loop" - worker_type: general - depends_on: [arbitrate] - condition: 'arbitrate.output.verdict == "LOOP"' - required: true - report_to_parent: true - prompt_template: - inline: | - 仲裁者要求 LOOP。为新的定向审查波产出最小 replan fragment 提案:只包含缺失或有争议的范围,分配新节点 ID,保持真实工件依赖,新增 fresh 验证者与新仲裁者,并保持在图上限内。只读:不要修代码。 - - 返回 loop 原因、新节点、依赖、每个节点必须收集的证据、验收条件与 stop reason。父会话必须 pause → replan → resume;绝不重启已完成节点或创建环。 - - id: finalize-review - name: "Publish the accepted deep-review report" - worker_type: general - depends_on: [arbitrate] - condition: 'arbitrate.output.verdict == "PASS"' - required: true - report_to_parent: true - prompt_template: - inline: | - 发布最终证据驱动的 review 报告:范围覆盖、确认的 findings、丢弃/反驳的声明、验证证据、残余低风险项、最终 PASS 理由。不引入上游未验证的新 findings 或声明。 diff --git a/.opencode/.dag-specs/deep-review-round2.yaml b/.opencode/.dag-specs/deep-review-round2.yaml deleted file mode 100644 index 90bf3c3514..0000000000 --- a/.opencode/.dag-specs/deep-review-round2.yaml +++ /dev/null @@ -1,513 +0,0 @@ -title: "Deep review round 2: joint re-review of error_class exposure, /goal restoration, and remediation wave" -mode: deep -admission: - brief_revision: 1 - qa_mode: LIGHT - verdict: READY - brief: - goal: "Jointly re-review the entire uncommitted working-tree diff: (1) DAG error_class exposure + wake attribution + failure-triage guidance, (2) /goal feature restoration, (3) the remediation wave that fixed round-1 findings — including cross-bundle consistency" - scope: - in: - - "git diff HEAD across packages/core (dag persistence, goal sql, migrations, workflow.md, dag-flow.txt, schema/migration registries)" - - "packages/opencode (goal module restore, session/prompt/system/command wiring, tool registry, dag runtime loop.ts, httpapi groups/handlers, tests)" - - "packages/plugin + packages/tui (goal sidebar, sync slice, adapters, builtins)" - - "packages/sdk/js regenerated surface (goal + error_class fields)" - - "cross-bundle consistency: guidance docs vs runtime semantics, schema vs SDK vs httpapi, test coverage of all new behavior" - out: - - "committed baseline behavior unchanged by the diff" - - "untracked .opencode/workflows/*.yaml experiment files and .opencode/.dag-specs/ artifacts" - constraints: - - "reviewers are read-only; do not modify any file" - - "every material finding must cite file:line evidence" - assumptions: - - "the working tree diff against HEAD is the complete review target" - - "migration/baseline/registry files were generated by bun script/migration.ts; SDK gen files were generated by packages/sdk/js/script/build.ts" - - "intentional adaptations to accept, not flag: goal restoration without the migration-window /goal deprecation scaffolding; IF NOT EXISTS hardening in restore_goal_state migration; serviceOption/deferred-resolution layer patterns introduced after goal retirement; Pick in plugin/tui.ts" - acceptance_criteria: - - "no unresolved CRITICAL/HIGH finding on verified evidence" - - "error_class value set consistent end-to-end; wake attribution and status surfaces correct" - - "workflow.md/dag-flow.txt guidance matches actual runtime semantics after the remediation wave" - - "goal restoration functionally complete vs pre-retire baseline (state management, tool registration, dispatch, judge loop, TUI, HTTP API)" - - "regenerated SDK diff limited to intended additions; repo contract obligations satisfied" - evidence_required: - - "file:line citations for static claims" - - "executed test-suite, typecheck, migration-check, and httpapi contract results" - risks: - - "cross-bundle interference (goal wiring vs dag changes in shared files: app-runtime, bootstrap, registry, prompt.ts, httpapi session group)" - - "guidance docs drifting from runtime semantics again after remediation edits" - - "layer-wiring regressions from restoring heavyweight GoalLoop into bootstrap/app layers" - review_plan: - - "wave 1: consolidated diff scope map with per-bundle change inventory + reference manifest" - - "wave 2: six parallel dimension reviewers" - - "wave 3: claim verification + objective gate execution" - - "wave 4: advanced-tier arbiter (PASS/LOOP/BLOCKED) with bounded-loop continuation" - open_questions: [] - blocking_questions: [] -config: - name: deep-review-round2 - max_concurrency: 8 - max_node_replan_attempts: 3 - max_total_nodes: 20 - node_defaults: - required: false - report_to_parent: false - worker_config: - timeout_ms: 900000 - nodes: - - id: scope-diff - name: "Scope: consolidated diff map + manifest" - worker_type: explore - depends_on: [] - required: true - prompt_template: - inline: | - You are the SCOPE MAPPER for a joint deep review of the uncommitted - working-tree diff in this repository. Read-only — do not modify any file. - - Run `git diff HEAD --stat`, `git diff HEAD` per area, and - `git status --short` (untracked files included) and produce: - - 1. Consolidated change map grouping EVERY changed/new file into one of - these bundles (flag any file fitting none as AUDIT-FLAG): - - BUNDLE A (error_class exposure + wake attribution): core/src/dag/{sql,store,projector}.ts, migrations 20260803073521_*, tool/workflow.ts status, dag/runtime/loop.ts wake digest, httpapi dag group/handler, sdk DagNode.error_class, related tests - - BUNDLE B (/goal restoration): core/src/goal/sql.ts + migration 20260803083938_*, opencode/src/goal/*, session/prompt/goal.txt, tool/goal.{ts,txt}, command/index.ts GOAL/SUBGOAL, session/{prompt,session,system}.ts wiring, effect/{app-runtime,bootstrap-runtime}.ts + project/bootstrap.ts GoalLoop wiring, tool/registry.ts, httpapi session goal route + handler + server Goal.node, plugin/tui.ts goal types, tui sidebar/goal.tsx + sync + builtins + adapters, test fixture/goal tests/httpapi-exercise goal scenario, sdk Goal surface - - BUNDLE C (remediation wave from prior review): core/src/plugin/command/workflow.md triage section rewrite, dag-flow.txt triage sentence, loop.ts catchCause/store/predicate fixes, new test assertions (cancelled-errorClass-null, recovery errorClass), packages/sdk/js/.gitignore - - GENERATED: schema.json, migration.gen.ts, schema.gen.ts, sdk gen files (verify they contain ONLY goal + error_class related additions) - 2. For each bundle, the stated intent (A: persist/expose node failure - class so the parent agent repairs failed nodes instead of restarting; - B: restore the retired /goal feature faithfully incl. state management - (goal_state table, goal.updated/cleared events) and tool registration - (GoalTool status/complete); C: fix guidance-doc defects found by the - prior review's arbiter). - 3. Verify and emit the reference manifest: - - reference_template: deep-review-dag-module - - added nodes: scope-diff, review-error-class, review-guidance, review-goal-core, review-goal-surface, review-tests, review-style, verify-suite - - pruned lanes: explore-core, explore-runtime, explore-templates, explore-integrations — prune_reason: target is a bounded working-tree diff, not the whole module; replacement_coverage: this scope-diff consolidated change map feeds every reviewer lane - 4. List every changed file once, with bundle tag; AUDIT-FLAG anything unclassifiable. - - Downstream reviewers receive this map as context. - - - id: review-error-class - name: "Review: error_class dataflow + wake semantics" - worker_type: general - depends_on: [scope-diff] - prompt_template: - inline: | - You are a DATAFLOW & RUNTIME REVIEWER. Read-only — do not modify any file. - Review target: BUNDLE A + its BUNDLE C remediation hunks in the - uncommitted diff (`git diff HEAD`), per the upstream scope map. - - Criteria: - - error_class end-to-end: schema trigger literals -> projector -> store -> - status tool output -> wake node-line suffix -> wake failed-workflow digest - -> httpapi NodeResponse -> SDK DagNode. Same vocabulary everywhere. - - loop.ts wake digest after remediation: bound `store.getNodes`, - Effect.catchCause + logWarning fallback, type-predicate filter; verify no - lock/ordering hazards vs wake_reported persistence and no behavior - regression for completed/cancelled workflows (old terminal line intact). - - Double reporting (failed node in both node line and workflow digest): - acceptable or harmful for parent triage? - - Truncation (300/500 slices), digest size for many failed nodes. - - push_exhausted: reserved, never produced — confirm no producer exists and - surfaces handle its absence. - - Test pins: cancelled-node errorClass-null assertion, wake digest - attribution test, recovery errorClass assertions — adequate? - - MANDATORY output format: - 1. findings: array of {severity: CRITICAL|HIGH|MEDIUM|LOW, title, description, evidence: "file:line", recommendation} - 2. unverified_claims: array of strings - 3. summary: 2-3 sentences - - - id: review-guidance - name: "Review: guidance docs vs runtime semantics" - worker_type: general - depends_on: [scope-diff] - prompt_template: - inline: | - You are a PROMPT-ACCURACY REVIEWER. Read-only — do not modify any file. - Review target: BUNDLE C guidance hunks — packages/core/src/plugin/command/ - workflow.md "Node failure triage" section (rewritten in the remediation - wave) + dag-flow.txt triage sentence + Budget Declaration wording — - checked against ACTUAL runtime behavior. These prompts steer parent-agent - repair decisions; any semantic drift is a bug. - - Verify every factual claim against code: - - timeout row: runtime cancels child session at deadline (spawn.ts); - partial-artifact advice accurate. - - exec_failed row (a)(b)(c) gating: do the cited reason strings/classes - exist exactly as written (recovery.ts, dag.ts terminateNonTerminalNodes - failReason propagation `required node(s) failed: ...`, loop.ts - orchestrator_unresponsive, template/condition errors)? Is the - per-reason response correct? - - verdict_fail row two shapes: ran-but-broke-contract vs never-ran pre-spawn - (unresolved placeholders loop.ts, review input contract) — confirm both - paths and that the fixes advised actually address them. - - cascade detection two shapes: required-failure leaves dependents pending - (check scheduler/transitions: do dependents stay pending or become - skipped?); optional-failure interpolation text `Dependency "X" failed:` - lands in dependent PROMPT text not error_reason (loop.ts resolveInputMapping). - - Qualifier "Every node failed via dag.node.failed carries an error_class" - + replan-cancel exception (projector NodeCancelled keeps error_class - null) — accurate? - - Value-set sentence (push_exhausted reserved) — matches schema + producers? - - Budget Declaration paragraph (defaults are floors; verifier/aggregator - lanes need 20-30min) — consistent with node_defaults usage elsewhere? - - Cross-doc consistency: Crash recovery / Escalation / Verdict Disposal - Contract / Bounded Repair / Resume-first — any contradiction? - - MANDATORY output format: - 1. findings: array of {severity: CRITICAL|HIGH|MEDIUM|LOW, title, description, evidence: "file:line", recommendation} - 2. unverified_claims: array of strings - 3. summary: 2-3 sentences - - - id: review-goal-core - name: "Review: goal state management + loop correctness" - worker_type: general - depends_on: [scope-diff] - prompt_template: - inline: | - You are a CORE-CORRECTNESS REVIEWER for the restored /goal feature. - Read-only — do not modify any file. Review target: BUNDLE B core hunks: - packages/opencode/src/goal/* (goal.ts state service, state.ts, events.ts, - judge.ts, loop.ts GoalLoop, prompts.ts), core/src/goal/sql.ts + restore - migration, session/{prompt,session,system}.ts wiring, effect layer wiring - (app-runtime, bootstrap-runtime, project/bootstrap), tool/{registry,goal}.ts. - - Criteria: - - Goal state service: load/set/pause/resume/clear/markDone/subgoals - correctness; DB row shape vs GoalStateTable; event emission matches - schema SessionGoal.Definitions (goal.updated/goal.cleared) with correct - payload mapping. - - GoalLoop: idle-event subscription lifecycle (init via serviceOption in - bootstrap), continuation prompt flow, judge transport failure budget, - kick dispatch, turn budget/pause semantics; fiber cleanup on session end - (session.ts clear on remove). - - Layer wiring: Goal.defaultLayer placement (mergeAll group1), - GoalLoop.defaultLayer via provideMerge with self-provided deps (loop.ts - defaultLayer) — verify self-containment and memoMap dedup claims; - BootstrapLayer inclusion consistency with the bootstrap.ts lazy comment. - - prompt.ts dispatch: ordering (early return before commands.get), error - branch, announce/kick handling, startContext drain; no leftover - deprecation scaffolding. - - system.ts goal injection: degradation note vs active goal block; prompt - bloat behavior. - - Migration IF NOT EXISTS correctness for upgrade/fresh-DB paths. - - Compatibility with post-retire architecture (V2 session paths, hook - deferred-import cycle discipline). - - MANDATORY output format: - 1. findings: array of {severity: CRITICAL|HIGH|MEDIUM|LOW, title, description, evidence: "file:line", recommendation} - 2. unverified_claims: array of strings - 3. summary: 2-3 sentences - - - id: review-goal-surface - name: "Review: goal HTTP/SDK/TUI surface" - worker_type: general - depends_on: [scope-diff] - prompt_template: - inline: | - You are an API/UI-SURFACE REVIEWER for the restored /goal feature. - Read-only — do not modify any file. Review target: BUNDLE B surface hunks: - httpapi groups/session.ts goal endpoint + handlers/session.ts goal handler - + server.ts Goal.node, packages/sdk/js gen additions (Goal type + - session.goal method), packages/plugin/src/tui.ts TuiSidebarGoalItem - (Pick) + TuiState.goal, packages/tui sidebar/goal.tsx + - sync.tsx goal slice/reducer/hydration + builtins + adapters, command/index.ts - GOAL/SUBGOAL entries, httpapi-exercise goal scenario + runner/runtime/types. - - Criteria: - - Schema vs handler vs SDK alignment for session.goal (optionality, field - names goal/status/turnsUsed/maxTurns/subgoals/pausedReason). - - TUI: sync reducer cases for goal.updated/goal.cleared; hydration fetch - with catch fallback; adapters Number() coercion vs SDK JSON-Schema number - union; builtins registration order; sidebar widget renders only when goal - present. - - plugin tui.ts Pick — does the adapter still satisfy the type - (finite numbers assignable)? - - Exercise scenario assertions match handler behavior; runner goal helper + - runtime Goal module wiring (service resolvable at runtime — flag if the - scenario layer lacks Goal provision). - - Command entries: descriptions accurate, dispatch handled in prompt.ts - (template "" never reaches LLM path). - - SDK gen delta limited to goal additions (no unrelated drift). - - MANDATORY output format: - 1. findings: array of {severity: CRITICAL|HIGH|MEDIUM|LOW, title, description, evidence: "file:line", recommendation} - 2. unverified_claims: array of strings - 3. summary: 2-3 sentences - - - id: review-tests - name: "Review: test coverage across all bundles" - worker_type: general - depends_on: [scope-diff] - prompt_template: - inline: | - You are a TEST-COVERAGE REVIEWER. Read-only — do not modify any file. - Review target: all test hunks in the uncommitted diff across BUNDLES A/B/C: - test/dag/{fixtures.ts,workflow-tool.test.ts,dag-wake-integration.test.ts, - dag-replan-stale-nodefailed.test.ts,dag-loop-recovery-integration.test.ts}, - test/goal/*, test/tool/goal-tool.test.ts, test/event-manifest.test.ts, - test/fixture/tui-plugin.ts, test/server/httpapi-exercise/*. - - Criteria: - - Bundle A pins: projector persistence, status output, wake digest - attribution (exact strings), cancelled-null invariant, recovery classes — - adequate and non-brittle? - - Bundle B: restored suites cover state service, judge parse/budget, - loop lifecycle (continue/done, dispatch-failure pause/resume), tool - status/complete; what CURRENT-architecture risks are untested (e.g. - /goal dispatch ordering vs /trust, bootstrap serviceOption absence path, - TUI reducer)? - - Bundle C: cancelled-null pin placement correct; recovery errorClass pin; - guidance-doc behavior has no executable guard (expected — note only). - - Hygiene: no duplicated logic into tests, mocks realistic, fixtures - representative, no tests asserting on unreachable states. - - Cross-bundle: any shared fixture change (fixtures.ts errorClass) breaking - other suites? - - MANDATORY output format: - 1. findings: array of {severity: CRITICAL|HIGH|MEDIUM|LOW, title, description, evidence: "file:line or test path", recommendation} - 2. unverified_claims: array of strings - 3. coverage_gaps: array of {path, untested_scenarios: string[]} - 4. summary: 2-3 sentences - - - id: review-style - name: "Review: style & repo conventions" - worker_type: general - depends_on: [scope-diff] - prompt_template: - inline: | - You are a STYLE & CONVENTIONS REVIEWER. Read-only — do not modify any file. - Review target: ALL uncommitted code hunks across BUNDLES A/B/C - (`git diff HEAD` + new files). - - Review against root AGENTS.md Style Guide and package AGENTS.md files: - - Comments: only non-obvious constraints; judge every new/changed comment. - - const over let, no else, no unnecessary destructuring, no import aliases, - functional style; Effect conventions (Effect.fn naming, catchCause vs - catch, no nested service yields). - - Layer invariants: self-contained defaultLayers, provideMerge placements, - LayerNode .node lists complete for every consumer of Goal/GoalLoop. - - Migrations: naming, id format, drizzle snake_case, registry freshness. - - Docs: workflow.md section placement/heading consistency, table - formatting; dag-flow.txt sentence integration; budget paragraph wording. - - Module shape: restored goal files put self-reexport at file top vs repo - convention (bottom) — flag with severity per repo rule, noting these are - faithful pre-retire restorations. - - httpapi: declared errors present, optional fields declared iff emitted. - - MANDATORY output format: - 1. findings: array of {severity: CRITICAL|HIGH|MEDIUM|LOW, title, description, evidence: "file:line", recommendation} - 2. unverified_claims: array of strings - 3. summary: 2-3 sentences - - - id: verify-claims - name: "Verify Disputed Claims" - worker_type: general - depends_on: [review-error-class, review-guidance, review-goal-core, review-goal-surface, review-tests, review-style] - required: true - worker_config: - timeout_ms: 1800000 - output_schema: - type: object - required: [verdict, verified_claims, disputed_findings_resolution, critical_findings_status, coverage_gaps, evidence_quality, prune_audit] - properties: - verdict: - type: string - enum: [VERIFIED, GAPS, BLOCKED] - verified_claims: { type: array, items: { type: object } } - disputed_findings_resolution: { type: array, items: { type: object } } - critical_findings_status: { type: array, items: { type: object } } - coverage_gaps: { type: array, items: { type: object } } - evidence_quality: { type: string } - prune_audit: { type: array, items: { type: object } } - prompt_template: - inline: | - You are a CLAIM VERIFIER. Read-only — do not modify any file. - - Six reviewers produced findings and unverified_claims about the uncommitted - diff. You are the fresh-context verification wave. Check every - unverified/disputed/CRITICAL/HIGH claim against the actual code; sample - MEDIUM/LOW claims instead of trusting self-report. - - Upstream context contains all 6 reviewer outputs + the scope map. Extract: - 1. All unverified_claims items - 2. Findings where reviewers disagree (especially cross-bundle claims about - shared files: app-runtime, bootstrap, prompt.ts, registry, httpapi session group) - 3. All CRITICAL/HIGH findings (must be verified regardless) - 4. Every factual claim in review-guidance — these ALL need code confirmation - 5. Any diff area with no evidence-bearing reviewer output (AUDIT-FLAG files - from the scope map must be resolved) - - For each claim, read the actual source at the cited location and determine - CONFIRMED / REFUTED / PARTIALLY_CONFIRMED / UNRESOLVABLE with the exact line. - Record prune_audit verdicts for the scope manifest. - - Verdict: - - VERIFIED: every material scope/criterion covered, no material claim unresolved - - GAPS: a bounded fresh review can close named gaps - - BLOCKED: required evidence cannot be obtained - - Submit via submit_result. coverage_gaps must name the missing scope, - evidence, and the smallest reviewer lane to add in a LOOP. - - - id: verify-suite - name: "Verify: execute tests and gates" - worker_type: general - depends_on: [scope-diff] - required: true - output_schema: - type: object - required: [verdict, results, anomalies] - properties: - verdict: - type: string - enum: [PASS, FAIL, BLOCKED] - results: - type: array - items: - type: object - required: [gate, command, outcome, detail] - properties: - gate: { type: string } - command: { type: string } - outcome: { type: string, enum: [PASS, FAIL, SKIPPED] } - detail: { type: string } - anomalies: { type: array, items: { type: string } } - prompt_template: - inline: | - You are an OBJECTIVE GATE RUNNER. Execute commands and report faithfully. - Do NOT modify any file. Do not fix failures — report them. - - Gates (each from the stated directory): - 1. typecheck core: bun run typecheck (packages/core) - 2. typecheck opencode: bun run typecheck (packages/opencode) - 3. opencode DAG suites: bun test test/dag (packages/opencode) - 4. goal suites: bun test test/goal test/tool/goal-tool.test.ts (packages/opencode) - 5. core DAG suites: bun test test/dag-core.test.ts test/dag-projector-drift.test.ts test/dag-store-summaries.test.ts test/plugin/command.test.ts (packages/core) - 6. migration check: bun script/migration.ts --check (packages/core) - 7. HttpAPI contract: bun run test:httpapi --fail-on-missing (packages/opencode) - 8. SDK freshness: in packages/sdk/js run bun run build, then - `git diff -- packages/sdk/js/src/v2/gen`. Interpretation: this working - tree intentionally adds (uncommitted) Goal types + session.goal method + - DagNode.error_class — PASS iff the gen diff contains ONLY those intended - additions and no other drift. - - Record outcome + decisive output fragment per gate. Verdict PASS only if all - gates pass under that interpretation; FAIL lists each failing gate with - evidence; BLOCKED states why a gate cannot run. - - - id: arbitrate - name: "Arbiter: Final Verdict" - worker_type: general - depends_on: [verify-claims, verify-suite] - required: true - report_to_parent: true - worker_config: - timeout_ms: 1200000 - output_schema: - type: object - required: [verdict, reason, evidence, findings, loop_scope, stop_reason, next_action, prune_decisions] - properties: - verdict: - type: string - enum: [PASS, LOOP, BLOCKED] - reason: { type: string } - evidence: - type: array - items: { type: string } - findings: - type: array - items: - type: object - required: [severity, title, evidence, status] - properties: - severity: - type: string - enum: [CRITICAL, HIGH, MEDIUM, LOW] - title: { type: string } - description: { type: string } - evidence: { type: string } - status: - type: string - enum: [CONFIRMED, REFUTED, PARTIALLY_CONFIRMED] - recommendation: { type: string } - loop_scope: - type: array - items: { type: string } - stop_reason: - type: string - enum: [goal_met, correction_required, evidence_missing, no_progress, round_cap, budget_cap] - next_action: - type: string - enum: [finalize, replan, stop] - prune_decisions: - type: array - items: - type: object - required: [node, prune_reason, replacement_coverage] - properties: - node: { type: string } - prune_reason: { type: string } - replacement_coverage: { type: string } - prompt_template: - inline: | - You are the ARBITER for this joint deep review of the uncommitted diff. - You rule on VERIFIED evidence only. verify-claims + verify-suite outputs - are your primary evidence base; the 6 reviewer outputs and the scope map - are context. - - Your job: - 1. For each CONFIRMED finding, assess true severity - 2. Discard REFUTED claims; correct PARTIALLY_CONFIRMED descriptions - 3. Deduplicate findings sharing a root cause; rank by impact - 4. Weight guidance-doc inaccuracies as at least HIGH (they steer - parent-agent repair decisions); any objective-gate failure at least HIGH - 5. Accept (do not penalize) the admission-listed intentional adaptations; - resolve all AUDIT-FLAG files from the scope map - 6. Audit prune_decisions from the scope manifest (via prune_audit); missing - prune_reason/replacement_coverage forbids PASS - 7. Verdict: - - PASS: no unresolved material finding; scope and evidence coverage complete - - LOOP: a bounded targeted review can resolve specific omissions/disputes - - BLOCKED: evidence insufficient, critical contradiction unresolved, or ceiling reached - 8. LOOP names the minimal new scope in loop_scope; never rerun completed waves - 9. State reason, evidence, stop_reason, next_action explicitly - - Parent disposal contract: PASS -> finalize; LOOP -> pause/replan/resume - fresh targeted nodes; BLOCKED -> stop. Submit via submit_result. - - - id: deep-dive - name: "Plan the bounded fresh review loop" - worker_type: general - depends_on: [arbitrate] - condition: 'arbitrate.output.verdict == "LOOP"' - required: true - report_to_parent: true - prompt_template: - inline: | - The arbiter required LOOP. Produce a minimal replan fragment proposal for a - NEW local review wave. Include only the missing or disputed scope from the - arbiter's loop_scope, assign NEW node IDs, preserve real artifact - dependencies, add a fresh verifier and a new arbiter, stay within caps. - Read-only: do not fix code. Keep proposals compact and copy-paste-ready as a - replan fragment — the parent applies it via pause/replan/resume. - - Return the loop reason, new nodes, dependencies, evidence each node must - collect, acceptance condition, and stop reason. - - - id: finalize-review - name: "Publish the accepted deep-review report" - worker_type: general - depends_on: [arbitrate] - condition: 'arbitrate.output.verdict == "PASS"' - required: true - report_to_parent: true - prompt_template: - inline: | - Publish the final evidence-backed joint review report for the uncommitted - diff. Include: scope coverage per bundle (A error_class, B goal restoration, - C remediation), confirmed findings (severity-ranked with file:line - evidence), discarded/refuted claims, objective gate results, residual - low-risk issues, and the final PASS reason. Do not introduce new findings - or claims not verified upstream. diff --git a/.opencode/.dag-specs/deep-review-round3-continue.yaml b/.opencode/.dag-specs/deep-review-round3-continue.yaml deleted file mode 100644 index f03105cba7..0000000000 --- a/.opencode/.dag-specs/deep-review-round3-continue.yaml +++ /dev/null @@ -1,177 +0,0 @@ -title: "Round 3 final audit (continuation): re-run timed-out closure review, reuse green gate results" -mode: deep -admission: - brief_revision: 1 - qa_mode: LIGHT - verdict: READY - brief: - goal: "Continue the failed round-3 audit (dag_038dbcba0ffeAZRBDq2cwyIPr6): review-final timed out at 900s (environmental timeout class). Reuse the completed verify-suite result (8/8 PASS, persisted on disk); re-run only the closure review with a 30-minute budget, then arbitrate." - scope: - in: - - "review-final-2: closure verification of the five round-2 HIGH findings + regression scan (same mission, larger budget)" - - "arbitrate: final PASS/LOOP/BLOCKED on review-final-2 + persisted verify-suite gate results" - - "finalize-review on PASS" - out: - - "re-running the objective gate suite (verify-suite completed: 8/8 PASS)" - constraints: - - "reviewers are read-only; do not modify any file" - - "upstream gate result file is read-only input" - assumptions: - - "verify-suite result at .opencode/.dag-specs/review-parts-round3/verify-suite.md is complete and trustworthy (verdict PASS, 8/8 gates)" - - "review-final timeout was environmental (budget too small for the closure scan over ~40 files), fixed by raising timeout_ms to 1800000" - - "reused_nodes: verify-suite" - acceptance_criteria: - - "each round-2 HIGH finding verifiably closed with file:line evidence" - - "no NEW CRITICAL/HIGH on verified evidence" - - "arbiter emits structured PASS/LOOP/BLOCKED" - evidence_required: - - "file:line citations for closure claims" - - "persisted gate results as objective evidence" - risks: - - "review-final-2 could time out again despite 30-min budget" - review_plan: - - "review-final-2 (fresh context, reads working tree + gate result file)" - - "arbitrate (advanced tier, report_to_parent)" - - "PASS -> finalize-review" - open_questions: [] - blocking_questions: [] -config: - name: deep-review-round3-continue - max_concurrency: 2 - max_node_replan_attempts: 2 - max_total_nodes: 8 - node_defaults: - required: false - report_to_parent: false - worker_config: - timeout_ms: 900000 - nodes: - - id: review-final - name: "Review: remediation closure + regression scan (30min)" - worker_type: general - depends_on: [] - required: true - worker_config: - timeout_ms: 1800000 - prompt_template: - inline: | - You are a FRESH-CONTEXT FINAL REVIEWER. Read-only — do not modify any file. - Work efficiently: targeted reads/greps first, full reads only where needed. - - The repo's uncommitted working-tree diff contains a joint change set (DAG - error_class exposure, /goal restoration, two remediation waves). A prior - review round returned LOOP with five HIGH findings; a remediation wave claims - closure. Objective gates already ran: read - .opencode/.dag-specs/review-parts-round3/verify-suite.md (8/8 PASS). - - Verify each round-2 HIGH is closed (cite fixing lines): - 1. workflow.md "Cascade detection" section: required-failure shape must state - dependents are terminalized to `skipped` with error_reason - `workflow_failed` (pending only while paused) — cross-check against - dag.ts terminateNonTerminalNodes + loop.ts dag.fail wiring. - 2. workflow.md exec_failed row (c): must NOT rely on a surfaced workflow-level - reason or universal primary-node attribution; must carry an - orchestrator_unresponsive recipe (zero attribution; use status; - extend/replan or change approach). Cross-check dag.ts fail(), - loop.ts `required node(s) failed:` producer, wake digest visibility. - 3. dag-flow.txt: error_class sentence carries replan-cancel + pre-migration - exceptions. - 4. packages/opencode/test/session/prompt.test.ts: tests exist and meaningfully - cover /goal set+kick, /goal status, /subgoal, Goal-absent fallthrough - (judge assertion strength, not just existence). - 5. packages/opencode/src/session/system.ts: Goal.defaultLayer provided + - Goal.node in the LayerNode deps (goal block reachable); no import cycle - hazard vs the deferred SettingsHook pattern. - - Regression spot-checks (brief, evidence-based): - - error_class pipeline intact (projector, store, tool status, wake digest, - httpapi NodeResponse, SDK) - - app-runtime provideMerge comment accurate vs loop.ts/settings.ts - self-provides - - GOAL command description includes done; dispatch handles done (goal.ts) - - Known deferred follow-ups (non-blocking unless clearly material): GET - /session/:id/goal 200-null vs SDK Goal typing; httpapi error_class - field-level fixture; TUI sync reducer tests; GoalLoop e2e fixed sleeps. - - MANDATORY output format: - 1. closure: array of {round2_finding, status: CLOSED|PARTIAL|OPEN, evidence: "file:line", note} - 2. findings: array of {severity: CRITICAL|HIGH|MEDIUM|LOW, title, description, evidence: "file:line", recommendation} (NEW issues only) - 3. unverified_claims: array of strings - 4. summary: 2-3 sentences - - - id: arbitrate - name: "Arbiter: Final Verdict" - worker_type: general - depends_on: [review-final] - required: true - report_to_parent: true - worker_config: - timeout_ms: 1200000 - output_schema: - type: object - required: [verdict, reason, evidence, findings, loop_scope, stop_reason, next_action] - properties: - verdict: - type: string - enum: [PASS, LOOP, BLOCKED] - reason: { type: string } - evidence: - type: array - items: { type: string } - findings: - type: array - items: - type: object - required: [severity, title, evidence, status] - properties: - severity: - type: string - enum: [CRITICAL, HIGH, MEDIUM, LOW] - title: { type: string } - description: { type: string } - evidence: { type: string } - status: - type: string - enum: [CONFIRMED, REFUTED, PARTIALLY_CONFIRMED] - recommendation: { type: string } - loop_scope: - type: array - items: { type: string } - stop_reason: - type: string - enum: [goal_met, correction_required, evidence_missing, no_progress, round_cap, budget_cap] - next_action: - type: string - enum: [finalize, replan, stop] - prompt_template: - inline: | - You are the ARBITER for the FINAL audit round of the joint uncommitted diff. - Rule on VERIFIED evidence only: review-final's closure verification + - the persisted gate results in - .opencode/.dag-specs/review-parts-round3/verify-suite.md (8/8 PASS). - - This is round 3 of a bounded loop (max_node_replan_attempts: 2). Judgment: - - PASS: all five round-2 HIGH findings CLOSED, gates PASS, no new - CRITICAL/HIGH; the documented deferred follow-ups remain explicitly - non-blocking (list them in evidence as accepted residual items) - - LOOP: at most ONE more bounded wave, only for a NEW or reopened HIGH with - concrete loop_scope; do not loop on deferred follow-ups or cosmetics - - BLOCKED: evidence missing or ceiling reached; report residual findings - - Deduplicate, discard REFUTED, rank by impact. State reason, evidence, - stop_reason, next_action explicitly. Submit via submit_result. - - - id: finalize-review - name: "Publish the final audit report" - worker_type: general - depends_on: [arbitrate] - condition: 'arbitrate.output.verdict == "PASS"' - required: true - report_to_parent: true - prompt_template: - inline: | - Publish the final audit report for the joint uncommitted diff: closure table - for the round-2 HIGH findings (with fix evidence), gate results, confirmed - residual items with their accepted non-blocking status, and the PASS reason. - Do not introduce new findings. diff --git a/.opencode/.dag-specs/deep-review-round3-final.yaml b/.opencode/.dag-specs/deep-review-round3-final.yaml deleted file mode 100644 index 68117d7d84..0000000000 --- a/.opencode/.dag-specs/deep-review-round3-final.yaml +++ /dev/null @@ -1,223 +0,0 @@ -title: "Round 3 final audit: confirm remediation closure on the joint diff" -mode: deep -admission: - brief_revision: 1 - qa_mode: LIGHT - verdict: READY - brief: - goal: "Final audit round of the uncommitted joint diff: verify every round-2 LOOP finding is closed by in-tree remediation, re-run all objective gates, and confirm no unresolved material bug remains" - scope: - in: - - "Round-2 HIGH cluster resolutions: workflow.md cascade rewrite (skipped terminalization + paused-pending nuance), exec_failed (c) row no-attribution rewrite + orchestrator_unresponsive recipe, dag-flow.txt exception clause" - - "Round-2 test item: new prompt.test.ts goal/subgoal dispatch tests (set+kick loop, status, subgoal, Goal-absent fallthrough)" - - "Round-2 MEDIUM follow-up pulled into the fix wave: SystemPrompt Goal.node + Goal.defaultLayer wiring so the goal system block renders" - - "LOW cleanups: app-runtime provideMerge comment correction, GOAL command description 'done'" - - "Regression spot-check over previously reviewed areas (error_class pipeline, wake digest, goal restoration surfaces)" - out: - - "Arbiter-deferred follow-ups explicitly accepted as non-blocking: GET /session/:id/goal null-vs-Goal typing, httpapi error_class field-level fixture, TUI sync reducer tests, GoalLoop e2e fixed sleeps" - - "committed baseline behavior outside the diff" - constraints: - - "reviewers are read-only; do not modify any file" - - "every material finding must cite file:line evidence" - assumptions: - - "the remediation wave is complete: docs rewritten, SystemPrompt wired, 4 dispatch tests added and passing locally" - - "deferred follow-ups are documented findings, not unresolved bugs, unless re-graded material by this round" - acceptance_criteria: - - "each round-2 HIGH finding verifiably closed (quote the fixing code/doc line)" - - "no NEW CRITICAL/HIGH finding on verified evidence" - - "all objective gates PASS under the SDK-freshness interpretation (intended uncommitted additions allowed)" - evidence_required: - - "file:line citations for closure claims" - - "executed gate results" - risks: - - "remediation wording introducing fresh guidance-vs-runtime drift" - - "SystemPrompt wiring changing layer construction for existing consumers" - review_plan: - - "wave 1: fresh-context remediation review (closure verification + regression scan)" - - "wave 2: objective gate runner" - - "wave 3: advanced-tier arbiter PASS/LOOP/BLOCKED" - open_questions: [] - blocking_questions: [] -config: - name: deep-review-round3-final - max_concurrency: 4 - max_node_replan_attempts: 2 - max_total_nodes: 10 - node_defaults: - required: false - report_to_parent: false - worker_config: - timeout_ms: 900000 - nodes: - - id: review-final - name: "Review: remediation closure + regression scan" - worker_type: general - depends_on: [] - required: true - prompt_template: - inline: | - You are a FRESH-CONTEXT FINAL REVIEWER. Read-only — do not modify any file. - The repo's uncommitted working-tree diff contains a joint change set (DAG - error_class exposure, /goal restoration, two remediation waves). A prior - review round returned LOOP with five HIGH findings; a remediation wave claims - they are closed. Your job is closure verification + regression scan. - - Verify each round-2 HIGH is actually closed (cite the fixing lines): - 1. workflow.md "Cascade detection" section: the required-failure shape must - state dependents are terminalized to `skipped` with error_reason - `workflow_failed` (pending only while paused) — check this matches - dag.ts terminateNonTerminalNodes + loop.ts dag.fail wiring. - 2. workflow.md exec_failed row (c): must NOT reference a surfaced - workflow-level reason or a universally-available primary-node attribution; - must carry an orchestrator_unresponsive recipe (zero attribution; use - status; extend/replan or change approach). Cross-check against - dag.ts fail() + loop.ts:272 `required node(s) failed:` producer and the - wake digest surface (loop.ts failuresByWorkflow) to confirm what IS visible. - 3. dag-flow.txt: the error_class sentence must carry the replan-cancel and - pre-migration exceptions. - 4. packages/opencode/test/session/prompt.test.ts: new tests must exist and - meaningfully cover /goal set+kick, /goal status, /subgoal, and Goal-absent - fallthrough (read the tests; judge assertion strength, not just existence). - 5. SystemPrompt wiring: packages/opencode/src/session/system.ts must provide - Goal.defaultLayer + Goal.node so the goal block is reachable; verify no - layer-cycle or construction-order hazard (goal imports must not close a - TDZ cycle like the deferred SettingsHook pattern guards against). - - Regression scan (spot-check, evidence-based): - - error_class pipeline untouched by the remediation wave? (projector, store, - tool status, wake digest, httpapi NodeResponse, SDK) - - app-runtime provideMerge comment now accurate vs loop.ts/settings.ts - self-provides? - - GOAL command description includes done; dispatch handles done (goal.ts)? - - Known deferred follow-ups (do NOT re-grade unless clearly material): - GET /session/:id/goal returns 200 JSON null vs SDK Goal (non-null); httpapi - error_class field-level fixture absent; TUI sync reducer untested; GoalLoop - e2e fixed sleeps. - - MANDATORY output format: - 1. closure: array of {round2_finding, status: CLOSED|PARTIAL|OPEN, evidence: "file:line", note} - 2. findings: array of {severity: CRITICAL|HIGH|MEDIUM|LOW, title, description, evidence: "file:line", recommendation} (NEW issues only) - 3. unverified_claims: array of strings - 4. summary: 2-3 sentences - - - id: verify-suite - name: "Verify: execute all gates" - worker_type: general - depends_on: [] - required: true - output_schema: - type: object - required: [verdict, results, anomalies] - properties: - verdict: - type: string - enum: [PASS, FAIL, BLOCKED] - results: - type: array - items: - type: object - required: [gate, command, outcome, detail] - properties: - gate: { type: string } - command: { type: string } - outcome: { type: string, enum: [PASS, FAIL, SKIPPED] } - detail: { type: string } - anomalies: { type: array, items: { type: string } } - prompt_template: - inline: | - You are an OBJECTIVE GATE RUNNER. Execute commands and report faithfully. - Do NOT modify any file. Do not fix failures — report them. - - Gates (each from the stated directory): - 1. typecheck core: bun run typecheck (packages/core) - 2. typecheck opencode: bun run typecheck (packages/opencode) - 3. opencode DAG suites: bun test test/dag (packages/opencode) - 4. goal + dispatch suites: bun test test/goal test/tool/goal-tool.test.ts test/session/prompt.test.ts (packages/opencode) - 5. core suites: bun test test/dag-core.test.ts test/dag-projector-drift.test.ts test/dag-store-summaries.test.ts test/plugin/command.test.ts (packages/core) - 6. migration check: bun script/migration.ts --check (packages/core) - 7. HttpAPI contract: bun run test:httpapi --fail-on-missing (packages/opencode) - 8. SDK freshness: in packages/sdk/js run bun run build, then - `git diff -- packages/sdk/js/src/v2/gen`. Interpretation: the working - tree intentionally adds (uncommitted) Goal types + session.goal method + - DagNode.error_class — PASS iff the gen diff contains ONLY those intended - additions and no other drift. - - Record outcome + decisive output fragment per gate. Verdict PASS only if all - gates pass under the interpretation; FAIL lists failing gates with evidence; - BLOCKED states why. - - - id: arbitrate - name: "Arbiter: Final Verdict" - worker_type: general - depends_on: [review-final, verify-suite] - required: true - report_to_parent: true - worker_config: - timeout_ms: 1200000 - output_schema: - type: object - required: [verdict, reason, evidence, findings, loop_scope, stop_reason, next_action] - properties: - verdict: - type: string - enum: [PASS, LOOP, BLOCKED] - reason: { type: string } - evidence: - type: array - items: { type: string } - findings: - type: array - items: - type: object - required: [severity, title, evidence, status] - properties: - severity: - type: string - enum: [CRITICAL, HIGH, MEDIUM, LOW] - title: { type: string } - description: { type: string } - evidence: { type: string } - status: - type: string - enum: [CONFIRMED, REFUTED, PARTIALLY_CONFIRMED] - recommendation: { type: string } - loop_scope: - type: array - items: { type: string } - stop_reason: - type: string - enum: [goal_met, correction_required, evidence_missing, no_progress, round_cap, budget_cap] - next_action: - type: string - enum: [finalize, replan, stop] - prompt_template: - inline: | - You are the ARBITER for the FINAL audit round of the joint uncommitted diff. - Rule on VERIFIED evidence only: review-final's closure verification + - verify-suite's gate results. - - This is round 3 of a bounded loop (max_node_replan_attempts: 2). Judgment: - - PASS: all five round-2 HIGH findings CLOSED, all gates PASS, no new - CRITICAL/HIGH; the documented deferred follow-ups remain explicitly - non-blocking (list them in evidence as accepted residual items) - - LOOP: at most ONE more bounded wave, only for a NEW or reopened HIGH with - concrete loop_scope; do not loop on deferred follow-ups or cosmetics - - BLOCKED: evidence missing or ceiling reached; report residual findings - - Deduplicate, discard REFUTED, rank by impact. State reason, evidence, - stop_reason, next_action explicitly. Submit via submit_result. - - - id: finalize-review - name: "Publish the final audit report" - worker_type: general - depends_on: [arbitrate] - condition: 'arbitrate.output.verdict == "PASS"' - required: true - report_to_parent: true - prompt_template: - inline: | - Publish the final audit report for the joint uncommitted diff: closure table - for the round-2 HIGH findings (with fix evidence), gate results, confirmed - residual items with their accepted non-blocking status, and the PASS reason. - Do not introduce new findings. diff --git a/.opencode/.dag-specs/final-confirmation-continue.yaml b/.opencode/.dag-specs/final-confirmation-continue.yaml deleted file mode 100644 index 949732e703..0000000000 --- a/.opencode/.dag-specs/final-confirmation-continue.yaml +++ /dev/null @@ -1,203 +0,0 @@ -title: "Final confirmation (continuation): split post-remediation review, reuse 3 completed lanes" -mode: deep -admission: - brief_revision: 1 - qa_mode: LIGHT - verdict: READY - brief: - goal: "Continue the failed final-confirmation workflow (dag_035b6a590ffeyPe2ofvgYNTwXR): the monolithic post-remediation reviewer timed out at 1200s for the third consecutive time — CHANGED APPROACH per Escalation: split it into two small parallel lanes instead of retrying a bigger monolith. Reuse the 3 completed lane outputs (config-repo review, stack integrity, 9/9 gate results) persisted on disk." - scope: - in: - - "review-remediation-docs: workflow.md cascade wording, prompt.test.ts fall-through positive assertions, dag-replan paused required-failure test, e2e-loop SUBSCRIPTION_SETTLE_MS" - - "review-remediation-contract: goal 404 handler + route error declaration + consumer tolerance audit (sync.tsx hydration + any session.goal callers), httpapi-exercise goal.absent scenario + dagFailNode fixture + wire assertions, sync-goal reducer tests" - - "arbitrate over all five lane outputs (2 new + 3 persisted)" - out: - - "re-running gates (9/9 PASS persisted), re-reviewing config-repo/stack lanes (completed)" - constraints: - - "reviewers are read-only; do not modify any file" - - "persisted upstream lane outputs are read-only inputs" - assumptions: - - "persisted outputs complete: .opencode/.dag-specs/review-parts-final/{review-config-repo.md, review-stack.md, verify-suite.md}" - - "reused_nodes: review-config-repo, review-stack, verify-suite" - - "timeout root cause was monolithic lane size, not budget alone; splitting is the approach change" - acceptance_criteria: - - "every R1-R10 remediation item reviewed by one of the two new lanes with file:line evidence" - - "no NEW CRITICAL/HIGH on verified evidence" - - "arbiter emits PASS/LOOP/BLOCKED" - evidence_required: - - "file:line citations" - - "persisted gate results (9/9 PASS)" - risks: - - "split lanes missing an item at the boundary between docs/tests and contract/wiring" - review_plan: - - "two parallel small reviewers (explicit per-item assignment, boundary covered twice where cheap)" - - "arbiter with persisted upstream context" - - "finalize on PASS" - open_questions: [] - blocking_questions: [] -config: - name: final-confirmation-continue - max_concurrency: 4 - max_node_replan_attempts: 2 - max_total_nodes: 8 - node_defaults: - required: false - report_to_parent: false - worker_config: - timeout_ms: 900000 - nodes: - - id: review-remediation-docs - name: "Review: remediation docs + test assertions" - worker_type: general - depends_on: [] - required: true - prompt_template: - inline: | - You are a FRESH-CONTEXT REVIEWER. Read-only — do not modify any file. - Branch feat/goal-restore holds the final state. Review FOUR remediation - items (post-round-3 fixes, gate-verified but never agent-reviewed): - - 1. packages/core/src/plugin/command/workflow.md "Cascade detection": - required-failure shape says pending/queued/paused dependents are - terminalized to `skipped` with error_reason workflow_failed, untouched - only while the workflow stays paused. Cross-check against - packages/opencode/src/dag/dag.ts terminateNonTerminalNodes (failRunning - branch fails running nodes, else-branch skips every other non-terminal) - and the PAUSED->FAILED guard rejection. - 2. packages/opencode/test/session/prompt.test.ts goal-absent fall-through - test: positive assertions ("/goal orphan request" persisted + exactly 1 - LLM input). Verify they cannot pass vacuously: trace the registry path - in prompt.ts (empty template -> text part + single prompt/loop run). - 3. packages/opencode/test/dag/dag-replan-stale-nodefailed.test.ts paused - required-failure test: scenario constructs the paused window correctly - (pause before settlement; empty reply fails the required node), and the - assertions prove pending-during-pause then skipped(workflow_failed) - after resume. - 4. packages/opencode/test/goal/e2e-loop.test.ts SUBSCRIPTION_SETTLE_MS: - constant used at all three former sleep sites; comment claims match - loop.ts forkScoped subscription wiring and the absence of a readiness - latch. - - MANDATORY output format: - 1. findings: array of {severity: CRITICAL|HIGH|MEDIUM|LOW, title, description, evidence: "file:line", recommendation} - 2. unverified_claims: array of strings - 3. summary: 2-3 sentences - - - id: review-remediation-contract - name: "Review: remediation contract + fixture wiring" - worker_type: general - depends_on: [] - required: true - prompt_template: - inline: | - You are a FRESH-CONTEXT REVIEWER. Read-only — do not modify any file. - Branch feat/goal-restore holds the final state. Review FOUR remediation - items (post-round-3 fixes, gate-verified but never agent-reviewed): - - 1. Goal endpoint 404 contract: packages/opencode/src/server/.../handlers/ - session.ts goal handler fails with notFound(...) when goalless; verify - `notFound` import, ApiNotFoundError declared in the goal route's error - list (groups/session.ts), wire shape honesty (200 always carries Goal). - CONSUMER AUDIT: find every session.goal call site (SDK client usage in - packages/tui — sync.tsx hydration .catch fallback — and anywhere else) - and verify each tolerates 404. - 2. httpapi-exercise session.goal.absent scenario: 404 expectation matches - handler behavior; seeded goalless session. - 3. httpapi-exercise error_class fixture: runner.ts failDagNodeFixture - (Dag.Service.nodeFailed via run() wrapper — check run() provides the app - layer so no residual requirement leaks), types.ts dagFailNode signature, - index.ts dag.nodes scenario seeds a timeout failure and asserts - error_class present on failed node / absent on pristine node. - 4. packages/tui/test/cli/cmd/tui/sync-goal.test.tsx: emitted events match - the schema definitions (goal.updated carries goal Info object; - goal.cleared carries sessionID only); reducer assertions cover write / - replace / clear transitions. - - MANDATORY output format: - 1. findings: array of {severity: CRITICAL|HIGH|MEDIUM|LOW, title, description, evidence: "file:line", recommendation} - 2. unverified_claims: array of strings - 3. summary: 2-3 sentences - - - id: arbitrate - name: "Arbiter: Final Verdict" - worker_type: general - depends_on: [review-remediation-docs, review-remediation-contract] - required: true - report_to_parent: true - worker_config: - timeout_ms: 1200000 - output_schema: - type: object - required: [verdict, reason, evidence, findings, loop_scope, stop_reason, next_action] - properties: - verdict: - type: string - enum: [PASS, LOOP, BLOCKED] - reason: { type: string } - evidence: - type: array - items: { type: string } - findings: - type: array - items: - type: object - required: [severity, title, evidence, status] - properties: - severity: - type: string - enum: [CRITICAL, HIGH, MEDIUM, LOW] - title: { type: string } - description: { type: string } - evidence: { type: string } - status: - type: string - enum: [CONFIRMED, REFUTED, PARTIALLY_CONFIRMED] - recommendation: { type: string } - loop_scope: - type: array - items: { type: string } - stop_reason: - type: string - enum: [goal_met, correction_required, evidence_missing, no_progress, round_cap, budget_cap] - next_action: - type: string - enum: [finalize, replan, stop] - prompt_template: - inline: | - You are the ARBITER for the final confirmation review of the three-PR stack - (#171 config-repo, #169 error_class, #170 goal restore). - - Evidence base — read all five inputs: - - Two new reviewer outputs (upstream context): review-remediation-docs, - review-remediation-contract - - Three persisted completed lanes from the interrupted workflow: - .opencode/.dag-specs/review-parts-final/review-config-repo.md - .opencode/.dag-specs/review-parts-final/review-stack.md - .opencode/.dag-specs/review-parts-final/verify-suite.md (9/9 gates PASS, - including SDK regen zero-diff determinism) - - Judgment: - - PASS: no CRITICAL/HIGH on verified evidence across all five lanes; gates - PASS; stack attribution sound; config-repo mechanism sound - - LOOP: at most ONE bounded wave for concrete HIGH findings (name loop_scope); - do NOT loop on MEDIUM/LOW or deferred follow-ups - - BLOCKED: evidence missing or ceiling reached; report residuals - - Deduplicate, discard REFUTED, rank by impact. State reason, evidence, - stop_reason, next_action explicitly. Submit via submit_result. - - - id: finalize-review - name: "Publish the final confirmation report" - worker_type: general - depends_on: [arbitrate] - condition: 'arbitrate.output.verdict == "PASS"' - required: true - report_to_parent: true - prompt_template: - inline: | - Publish the final confirmation report for the three-PR stack. Include: - per-PR review coverage, the five lane conclusions, gate results (9/9), - confirmed residual items with accepted non-blocking status, and the PASS - reason declaring the stack merge-ready. Write the report to - .opencode/.dag-specs/review-parts-final/final-confirmation-report.md and - submit a concise summary as your final output. Do not introduce new findings. diff --git a/.opencode/.dag-specs/final-confirmation-three-pr-stack.yaml b/.opencode/.dag-specs/final-confirmation-three-pr-stack.yaml deleted file mode 100644 index f293b6836c..0000000000 --- a/.opencode/.dag-specs/final-confirmation-three-pr-stack.yaml +++ /dev/null @@ -1,291 +0,0 @@ -title: "Final confirmation review: three-PR stack (config-repo, error_class, goal restore)" -mode: deep -admission: - brief_revision: 1 - qa_mode: LIGHT - verdict: READY - brief: - goal: "Final confirmation review closing the last two review gaps before merge: (1) the post-round-3 remediation wave (R1-R10 fixes) which was gate-verified but never agent-reviewed, and (2) the config-repo template mechanism (#171) whose original deep review was interrupted and never reached a verdict" - scope: - in: - - "Post-remediation delta: workflow.md paused/queued/paused wording; prompt.test.ts positive fall-through assertions; dag-replan-stale-nodefailed.test.ts paused required-failure test; handlers/session.ts goal 404 + session.goal.absent scenario; httpapi-exercise dagFailNode fixture (runner/types/index); tui sync-goal.test.tsx; e2e-loop.test.ts SUBSCRIPTION_SETTLE_MS" - - "Config-repo mechanism commits 676e0463e, 6ffc7a712, 98e4c0624, 2ee59d874: release-fork.yml package-templates job, dag-flow.txt library scope text, /dag-template-update command template + registration, builtin template embedding (workflows.ts + build injection)" - - "Stack integrity: per-branch content attribution (feat/dag-config-repo / feat/dag-error-class / feat/goal-restore), generated-file coherence per branch, committed .opencode artifacts hygiene (no secrets)" - - "Objective gates at feat/goal-restore HEAD (full tree): typecheck, test suites, httpapi contract, migration check, SDK regen determinism" - out: - - "Re-reviewing already-adjudicated round-1/2/3 findings (closure already PASS-adjudicated)" - - "opencode-dag-config remote repo content itself" - constraints: - - "reviewers are read-only; do not modify any file" - - "every material finding must cite file:line evidence" - assumptions: - - "current branch feat/goal-restore contains the complete stacked final state" - - "prior adjudications stand: round-3 PASS on the pre-remediation state; R1-R10 argumentations recorded in-session" - acceptance_criteria: - - "no NEW CRITICAL/HIGH on verified evidence across remediation delta and config-repo mechanism" - - "config-repo mechanism verified sound: release job, update command, library resolution tier" - - "all objective gates PASS at HEAD" - - "stack branches carry exactly their attributed bundles" - evidence_required: - - "file:line citations" - - "executed gate results" - risks: - - "remediation fixes introducing subtle contract drift (goal 404 vs consumers)" - - "release-template job failure modes never exercised" - review_plan: - - "wave 1: three parallel reviewers (post-remediation delta, config-repo mechanism, stack integrity)" - - "wave 2: objective gate runner" - - "wave 3: arbiter PASS/LOOP/BLOCKED with bounded continuation" - open_questions: [] - blocking_questions: [] -config: - name: final-confirmation-three-pr-stack - max_concurrency: 6 - max_node_replan_attempts: 2 - max_total_nodes: 12 - node_defaults: - required: false - report_to_parent: false - worker_config: - timeout_ms: 900000 - nodes: - - id: review-post-remediation - name: "Review: post-round-3 remediation delta" - worker_type: general - depends_on: [] - required: true - worker_config: - timeout_ms: 1200000 - prompt_template: - inline: | - You are a FRESH-CONTEXT REVIEWER. Read-only — do not modify any file. - Target: the remediation wave applied AFTER the round-3 PASS adjudication of - the joint diff. These fixes were gate-verified but never agent-reviewed. - Current branch (feat/goal-restore) contains the final committed state. - - Review each fix for correctness, regressions, and contract drift: - 1. packages/core/src/plugin/command/workflow.md "Cascade detection": - required-failure shape says dependents pending/queued/PAUSED are skipped - with error_reason workflow_failed (untouched only while workflow paused). - Cross-check dag.ts terminateNonTerminalNodes (failRunning branch vs - NodeSkipped branch) and the PAUSED->FAILED guard. - 2. packages/opencode/test/session/prompt.test.ts goal-absent fall-through - test: positive assertions (/goal orphan request persisted + exactly 1 LLM - input). Check they cannot pass vacuously and match the registry path - behavior in prompt.ts (empty template expansion). - 3. packages/opencode/test/dag/dag-replan-stale-nodefailed.test.ts paused - required-failure test: scenario valid (pause before failure settlement, - empty-reply failing the node), assertions prove the paused nuance. - 4. packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts - goal handler returns notFound when goalless (was 200+null): check error - import, ApiNotFoundError declared on the route (groups/session.ts), and - EVERY consumer tolerates 404 — audit packages/tui/src/context/sync.tsx - hydration (.catch fallback) and any other session.goal call sites. - 5. httpapi-exercise: session.goal.absent scenario (404 expectation), - dag.nodes scenario seeded failure (failDagNodeFixture in runner.ts via - Dag.Service.nodeFailed + run() wrapper), wire assertions - (error_class present on failed, absent on pristine). - 6. packages/tui/test/cli/cmd/tui/sync-goal.test.tsx: events match schema - (goal.updated properties goal object; goal.cleared), reducer assertions - cover write/replace/clear. - 7. packages/opencode/test/goal/e2e-loop.test.ts SUBSCRIPTION_SETTLE_MS - constant: comment accuracy vs loop.ts forkScoped; all three waits use it. - - MANDATORY output format: - 1. findings: array of {severity: CRITICAL|HIGH|MEDIUM|LOW, title, description, evidence: "file:line", recommendation} - 2. unverified_claims: array of strings - 3. summary: 2-3 sentences - - - id: review-config-repo - name: "Review: config-repo template mechanism (#171)" - worker_type: general - depends_on: [] - required: true - prompt_template: - inline: | - You are a FRESH-CONTEXT REVIEWER. Read-only — do not modify any file. - Target: the config-repo reference-template mechanism on branch - feat/dag-config-repo (commits 676e0463e, 6ffc7a712, 98e4c0624, 2ee59d874, - plus chore commit 494b3b463 artifacts). Its original deep review was - interrupted; this is the first completed review. Use `git show ` - and read current files on this branch. - - Review dimensions: - 1. .github/workflows/release-fork.yml package-templates/sync job: clone of - LeXwDeX/opencode-dag-config at release time, packaging dag-templates - asset, write-safety (must not touch main repo contents outside the - asset), failure behavior (job failure vs release), pinning (branch/tag/ - SHA?) and supply-chain trust implications. - 2. /dag-template-update command: packages/core/src/plugin/command.ts - registration + packages/core/src/plugin/command/dag-template-update.txt - template — the update flow (first-run clone, pull --ff-only, or zip - download per commit message), preview classification - (NEW/UNCHANGED/UPDATE), backup-before-overwrite, QA decision gate, - directory assumptions (/workflows), failure modes (network, - dirty target dir, divergence). - 3. Library resolution: builtin scope as third tier in command/workflows.ts - (project overrides global overrides builtin), list/resolve coverage, - dag-flow.txt guidance text consistency with actual resolution order. - 4. Build injection: how OPENCODE_DAG_TEMPLATES is injected (generate/build - scripts touched by 98e4c0624) — determinism, embedding correctness. - 5. Fix commit 2ee59d874: what review findings did it address, are they - actually resolved? - 6. Chore artifacts (494b3b463 .opencode/.dag-specs + workflows yamls): - YAML sanity scan + NO secrets/credentials/absolute-path leakage - (grep for tokens, keys, ~/.local/share paths that would break for others). - - MANDATORY output format: - 1. findings: array of {severity: CRITICAL|HIGH|MEDIUM|LOW, title, description, evidence: "file:line", recommendation} - 2. unverified_claims: array of strings - 3. summary: 2-3 sentences - - - id: review-stack - name: "Review: three-PR stack integrity" - worker_type: general - depends_on: [] - prompt_template: - inline: | - You are a FRESH-CONTEXT STACK-INTEGRITY REVIEWER. Read-only — do not - modify any file. Three stacked PR branches carry the joint change set: - - feat/dag-config-repo (PR #171, base dev): config-repo mechanism + - workflow library + review artifacts - - feat/dag-error-class (PR #169, base feat/dag-config-repo): error_class - exposure + triage guidance + its remediation - - feat/goal-restore (PR #170, base feat/dag-error-class): /goal restoration - + its remediation - - Verify (git commands: git log/diff between branches): - 1. Attribution: diff(feat/dag-config-repo -> feat/dag-error-class) contains - ONLY error_class-bundle files; diff(feat/dag-error-class -> - feat/goal-restore) contains ONLY goal-bundle files. List any file that - appears in the wrong layer (generated files included — migration registry, - schema baseline, SDK gen must be layered correctly: error_class-only - additions before goal additions). - 2. Independence: feat/dag-error-class compiles conceptually without goal - (no dangling goal imports in its tree — grep for @/goal references in - files attributed to it). - 3. Generated coherence: migration.gen.ts on feat/dag-error-class lists only - the error_class migration; on feat/goal-restore both; schema baseline and - SDK gen likewise layered. - 4. No cross-branch leftovers: nothing from the wip snapshot committed by - accident (compare feat/goal-restore against expectations). - - MANDATORY output format: - 1. findings: array of {severity: CRITICAL|HIGH|MEDIUM|LOW, title, description, evidence: "file:line or git range", recommendation} - 2. unverified_claims: array of strings - 3. summary: 2-3 sentences - - - id: verify-suite - name: "Verify: execute all gates at HEAD" - worker_type: general - depends_on: [] - required: true - output_schema: - type: object - required: [verdict, results, anomalies] - properties: - verdict: - type: string - enum: [PASS, FAIL, BLOCKED] - results: - type: array - items: - type: object - required: [gate, command, outcome, detail] - properties: - gate: { type: string } - command: { type: string } - outcome: { type: string, enum: [PASS, FAIL, SKIPPED] } - detail: { type: string } - anomalies: { type: array, items: { type: string } } - prompt_template: - inline: | - You are an OBJECTIVE GATE RUNNER. Execute and report faithfully. Do NOT - modify any file. Current branch feat/goal-restore holds the full stacked - state. Gates (from the stated directories): - 1. typecheck core: bun run typecheck (packages/core) - 2. typecheck opencode: bun run typecheck (packages/opencode) - 3. typecheck tui: bun run typecheck (packages/tui) - 4. opencode suites: bun test test/dag test/goal test/tool/goal-tool.test.ts test/session/prompt.test.ts (packages/opencode) - 5. core suites: bun test test/dag-core.test.ts test/dag-projector-drift.test.ts test/dag-store-summaries.test.ts test/plugin/command.test.ts (packages/core) - 6. tui sync suites: bun test test/cli/cmd/tui/sync-goal.test.tsx test/cli/cmd/tui/sync-dag.test.tsx (packages/tui) - 7. migration check: bun script/migration.ts --check (packages/core) - 8. HttpAPI contract: bun run test:httpapi --fail-on-missing (packages/opencode) - 9. SDK regen determinism: in packages/sdk/js run bun run build, then - `git diff -- packages/sdk/js/src/v2/gen` — PASS iff no diff at all (the - committed gen files must be exactly reproduced). - Record outcome + decisive fragment per gate. Verdict PASS only if all pass; - FAIL lists failing gates; BLOCKED states why. - - - id: arbitrate - name: "Arbiter: Final Verdict" - worker_type: general - depends_on: [review-post-remediation, review-config-repo, review-stack, verify-suite] - required: true - report_to_parent: true - worker_config: - timeout_ms: 1200000 - output_schema: - type: object - required: [verdict, reason, evidence, findings, loop_scope, stop_reason, next_action] - properties: - verdict: - type: string - enum: [PASS, LOOP, BLOCKED] - reason: { type: string } - evidence: - type: array - items: { type: string } - findings: - type: array - items: - type: object - required: [severity, title, evidence, status] - properties: - severity: - type: string - enum: [CRITICAL, HIGH, MEDIUM, LOW] - title: { type: string } - description: { type: string } - evidence: { type: string } - status: - type: string - enum: [CONFIRMED, REFUTED, PARTIALLY_CONFIRMED] - recommendation: { type: string } - loop_scope: - type: array - items: { type: string } - stop_reason: - type: string - enum: [goal_met, correction_required, evidence_missing, no_progress, round_cap, budget_cap] - next_action: - type: string - enum: [finalize, replan, stop] - prompt_template: - inline: | - You are the ARBITER for the final confirmation review of the three-PR stack. - Rule on VERIFIED evidence only: three reviewer outputs + gate results. - - Judgment: - - PASS: no CRITICAL/HIGH on verified evidence; gates PASS; stack layers - correctly attributed; config-repo mechanism sound (release job safe, - update command robust, resolution tiers correct, no secrets in artifacts) - - LOOP: at most ONE bounded wave for concrete HIGH findings (name loop_scope) - - BLOCKED: evidence missing or ceiling reached; report residuals - - Deduplicate, discard REFUTED, rank by impact. State reason, evidence, - stop_reason, next_action explicitly. Submit via submit_result. - - - id: finalize-review - name: "Publish the final confirmation report" - worker_type: general - depends_on: [arbitrate] - condition: 'arbitrate.output.verdict == "PASS"' - required: true - report_to_parent: true - prompt_template: - inline: | - Publish the final confirmation report for the three-PR stack: per-PR - review coverage status, gate results, confirmed residual items (accepted - non-blocking), and the PASS reason declaring the stack merge-ready. Do not - introduce new findings. diff --git a/.opencode/.dag-specs/review-parts-diff/review-contract.md b/.opencode/.dag-specs/review-parts-diff/review-contract.md deleted file mode 100644 index 8a16b8c6dc..0000000000 --- a/.opencode/.dag-specs/review-parts-diff/review-contract.md +++ /dev/null @@ -1 +0,0 @@ -"# API Contract Review — httpapi + SDK + contract obligations\n\n## 1. findings\n\n- **{severity: LOW, title: \"httpapi-exercise scenarios not updated for the NodeResponse shape change — functionally inert, but the AGENTS.md contract letter is unmet\"}**\n - description: AGENTS.md obliges scenario updates when a route response shape changes. The dag scenarios under `packages/opencode/test/server/httpapi-exercise/index.ts:1807-1850` assert with spot checks only (`check(typeof n.status === \"string\")`, `Array.isArray(n.depends_on)`, `check(body.id === \"n1\")`), never exact-shape equality — an additive optional field cannot break them. Route-coverage enforcement (`--fail-on-missing`/`--fail-on-skip`, routing.ts:50-51; the package.json script uses `--fail-on-skip`) only guards route presence, not field shape. Functionally CI stays green; but the new persisted field is never exercised over HTTP (fixtures create nodes that never fail, so `error_class` is always null on the wire in tests).\n - evidence: \"packages/opencode/test/server/httpapi-exercise/index.ts:1822-1832\", \"packages/opencode/test/server/httpapi-exercise/index.ts:1845-1849\", \"packages/opencode/package.json test:httpapi\", \"AGENTS.md (repo root, Extending the Codebase)\"\n - recommendation: add a `check(typeof n.error_class === \"string\")`-style assertion only if a failing-node fixture is feasible; otherwise document (in the scenario comment) that optional failure-class fields are covered by the schema-level contract and the SDK typecheck. No CI risk either way.\n\n- **{severity: LOW, title: \"error_class value set is unconstrained on every read surface — wire contract enforces no enum\"}**\n - description: The 4-value set (`exec_failed`/`push_exhausted`/`verdict_fail`/`timeout`) exists only at the event source `packages/schema/src/dag-event.ts:253` (Schema.Literals) and in comments (sql.ts:65, groups/dag.ts:42-43). NodeResponse declares `Schema.optional(Schema.String)` (groups/dag.ts:44) and the SDK type is plain `error_class?: string` (types.gen.ts:3909). Field-level alignment is exact (see summary), but any malformed/stale persisted value would pass through projector → store → handler → HTTP/SDK unvalidated, and guidance docs enumerate only 3 of 4 values.\n - evidence: \"packages/opencode/src/server/routes/instance/httpapi/groups/dag.ts:44\", \"packages/sdk/js/src/v2/gen/types.gen.ts:3909\", \"packages/schema/src/dag-event.ts:253\"\n - recommendation: optionally derive the HTTP field from the shared trigger literal (export a `FailedTrigger` union from packages/schema and reuse in NodeResponse + `error_class` comments) so the wire type constrains the set; acceptable to leave as `string` since pass-through persistence is intentional.\n\n- **{severity: LOW, title: \"TUI consumers neither break on nor display error_class — triage field invisible in the inspector\"}**\n - description: All DagNode consumers were checked: `dag-inspector.tsx:659-661` renders only `error_reason` via `formatDagError` (dag-inspector-utils.ts:64-66, verbatim string munging); `dag-panel.tsx` uses status/glyph only (lines 81-83); `dag-inspector-utils.ts:18-61` (computeWaves/computeNodeRowIndex) is field-agnostic. The additive optional field breaks nothing (no exhaustive destructuring, no `satisfies`). However the new field — the primary triage input for the workflow.md guidance — cannot be shown by the failure pane until a TUI change lands; consistent with the diff's scope-out (no TUI files modified).\n - evidence: \"packages/tui/src/feature-plugins/system/dag-inspector.tsx:659-661\", \"packages/tui/src/feature-plugins/sidebar/dag-panel.tsx:81-83\"\n - recommendation: none required for this diff; note as follow-up: the inspector's failure pane could display `error_class` alongside the reason string once TUI changes are in scope.\n\n- **{severity: LOW, title: \"Untracked regenerated openapi.json build artifact visible in git status\"}**\n - description: `packages/sdk/js/openapi.json` was removed from tracking by commit 881ca8643 (\"chore: generate\", pure deletion) and is not covered by any .gitignore; each SDK build regenerates it, so it perpetually shows as `??` and an accidental `git add -A` would re-add it. It is inert for CI: `check:generated` diffs only `src/v2/gen` (packages/sdk/js/package.json:10).\n - evidence: \"packages/sdk/js/openapi.json (untracked)\", \"packages/sdk/js/package.json:10\", \"git show 881ca8643\"\n - recommendation: add `openapi.json` to `packages/sdk/js/.gitignore` to prevent accidental commits.\n\n## 2. unverified_claims\n\n- The claim that httpapi-exercise scenarios remain green with the new optional field is inferred from reading the assertion logic (spot checks cannot fail on an additive optional field), not from executing `bun run test:httpapi`.\n- \"packages/client has no dag surface, hence nothing to drift\" — verified by grep (zero case-insensitive `dag` matches across `packages/client/src`), not by running the client generator; the client `check:generated` guards `src/generated src/generated-effect` (package.json:13), both untouched.\n- I did not execute `bun typecheck` in packages/opencode; the handler/schema alignment claim rests on static comparison of the mapper object literal against the Schema.Struct declaration.\n\n## 3. summary\n\nField-level alignment is exact end-to-end: `NodeRow.errorClass` (store.ts:42) → handler emits `error_class` only when non-null (handlers/dag.ts:65) → `Schema.optional(Schema.String)` declared (groups/dag.ts:44) → SDK `error_class?: string` (types.gen.ts:3909) — all 16 NodeResponse fields match the mapper 1:1, nothing emitted-but-undeclared and nothing declared-but-never-emitted, so the encoder strips nothing unintended. Regeneration freshness holds: `git diff HEAD -- packages/sdk/js/src/v2/gen` shows exactly one added line, no other generated file drifted, and packages/client has no dag types at all. No CRITICAL/HIGH findings; the four LOW items are contract-convention (scenario update per AGENTS.md), wire-level enum enforcement, TUI invisibility of the new field, and the untracked openapi.json artifact." diff --git a/.opencode/.dag-specs/review-parts-diff/review-dataflow.md b/.opencode/.dag-specs/review-parts-diff/review-dataflow.md deleted file mode 100644 index e42c889e1b..0000000000 --- a/.opencode/.dag-specs/review-parts-diff/review-dataflow.md +++ /dev/null @@ -1 +0,0 @@ -"Verification complete: 22 + 26 + 17 tests pass, typecheck clean. Final review follows.\n\n---\n\n# DATAFLOW INTEGRITY REVIEW — error_class end-to-end\n\n**Trace verified end to end**: schema literal → projector → column → all three read surfaces, plus the dead `push_exhausted` value and the wake-digest race window.\n\n## findings\n\n```json\n[\n {\n \"severity\": \"MEDIUM\",\n \"title\": \"workflow.md claims every failed node carries error_class, but cancelled-via-replan and pre-migration nodes have it null\",\n \"description\": \"The triage section states 'Every failed node carries an error_class in status output and in the wake summary.' Two classes of failed nodes have error_class = null: (1) nodes cancelled via replan — NodeCancelled projects status='failed' with error_reason 'cancelled via replan' and deliberately does NOT set error_class; (2) failed rows written before this migration. The read surfaces correctly tolerate null (truthiness guards in tool/workflow.ts:204, handlers/dag.ts:65, loop.ts failureClass), and the wake digest's `errorClass !== null` filter is precisely what excludes cancelled-via-replan nodes — so the runtime is right and the doc is overbroad. An agent triaging a failed node with no error_class finds no matching table row and no fallback guidance.\",\n \"evidence\": \"packages/core/src/plugin/command/workflow.md:400; packages/core/src/dag/projector.ts:320-327 (NodeCancelled set: no error_class); packages/opencode/src/dag/runtime/loop.ts:859 (filter `status === \\\"failed\\\" && errorClass !== null`)\",\n \"recommendation\": \"Qualify the sentence, e.g. 'Every node failed via dag.node.failed carries an error_class...' and add one line: nodes cancelled via replan are status failed with error_reason 'cancelled via replan' and no error_class.\"\n },\n {\n \"severity\": \"LOW\",\n \"title\": \"push_exhausted is a dead value: schema/comments advertise 4 classes, runtime emits 3, docs say 3 (docs are correct)\",\n \"description\": \"All 19 producer call sites emit only exec_failed (11), verdict_fail (5), or timeout (4). push_exhausted appears only in the schema Literals (dag-event.ts:253), the FallbackTrigger enum (core/types.ts:43, unused member — only EXEC_FAILED is referenced as DEFAULT_FALLBACK_TRIGGER), and the sql.ts:65 / groups/dag.ts:43 comments. Pipeline is pure string pass-through (schema → projector → column → surfaces), so the dead value breaks nothing; if a future producer emits it, persistence and every surface work unchanged. The docs listing 3 values exactly match observable behavior; the comments listing 4 are aspirational. Impact: negligible, but the vocabulary surfaces disagree with each other.\",\n \"evidence\": \"packages/schema/src/dag-event.ts:253; packages/core/src/dag/core/types.ts:43; packages/core/src/dag/sql.ts:65; packages/opencode/src/server/routes/instance/httpapi/groups/dag.ts:43; all nodeFailed call sites: packages/opencode/src/dag/dag.ts:488,599 / runtime/recovery.ts:79,99,108,130,138 / runtime/spawn.ts:76,163,176,231,248,258,300 / runtime/loop.ts:114,153,173,186,245\",\n \"recommendation\": \"Align the two comments to the 3 produced values, or document push_exhausted as reserved in the FallbackTrigger enum. No code change required.\"\n },\n {\n \"severity\": \"LOW\",\n \"title\": \"Wake digest has a narrow torn-read window for running nodes killed by dag.fail; miss is not re-delivered\",\n \"description\": \"dag.fail publishes WorkflowFailed (dag.ts:525) BEFORE terminateNonTerminalNodes publishes NodeFailed (dag.ts:526 → 488), so projection order commits the workflow row 'failed' before the killed running-node rows become 'failed'. The digest's getNodes (loop.ts:852-860) is a second read after the snapshot transaction and can transiently miss those node rows, yielding '[DAG Workflow failed] ...' with no 'Failed nodes:' attribution. Terminal workflows are marked wakeReported after delivery, so the miss is not re-delivered; recovery is via the status tool. The node-line path (batch.nodes) is unaffected for wake-eligible nodes; the digest was the only surface covering non-wake-eligible killed nodes. The codebase already tolerates this torn-read class elsewhere (loop.ts:794-795 'A terminal event can commit between either query'), and the digest degrades silently by design (Effect.catch → []). Impact: cosmetic, one wake, narrow window.\",\n \"evidence\": \"packages/opencode/src/dag/dag.ts:524-527; packages/opencode/src/dag/runtime/loop.ts:852-862, 859\",\n \"recommendation\": \"Accept as-is (documented degradation) or, for strictness, re-read the failed workflows' nodes inside the same transaction the snapshot uses, or treat an empty digest on a failed workflow as 'attribution pending' and allow one coalesced re-read. LOW because consequence is cosmetic.\"\n },\n {\n \"severity\": \"LOW\",\n \"title\": \"Workflow-collateral kills are stamped exec_failed with the fail reason — doc's exec_failed row doesn't mention them\",\n \"description\": \"terminateNonTerminalNodes emits trigger 'exec_failed' for running nodes killed when the workflow fails (reason e.g. 'orchestrator_unresponsive'), and for the cancel path these are NodeSkipped (no error_class). These rows are not 'child session or provider error' — the doc's exec_failed row — but a workflow-level cascade. They flow correctly into the digest and surfaces, and the doc's advice ('read error_reason; infra/config errors; don't restart') fits the common case, but 'orchestrator_unresponsive' and similar fail reasons are absent from the doc's example list, so the mapping is incomplete.\",\n \"evidence\": \"packages/opencode/src/dag/dag.ts:487-495, 524-527; packages/core/src/plugin/command/workflow.md:406-410\",\n \"recommendation\": \"Add one line to the exec_failed row: 'reason is the workflow's fail reason (e.g. orchestrator_unresponsive) when the node was killed by the workflow failing' — or map collateral kills to their own treatment in the table.\"\n },\n {\n \"severity\": \"LOW\",\n \"title\": \"workflow-tool.test.ts fixture indentation anomaly + no test guards the null-error_class filter\",\n \"description\": \"The new mock node is formatted `}, {` at odd indentation (workflow-tool.test.ts:189-212) — cosmetic only. More materially: no test asserts that a cancelled-via-replan node (status failed, error_class null) is EXCLUDED from the wake digest, which is the exact behavior the `errorClass !== null` filter exists for; a future refactor could drop the filter and no test would catch it.\",\n \"evidence\": \"packages/opencode/test/dag/workflow-tool.test.ts:189; packages/opencode/src/dag/runtime/loop.ts:859\",\n \"recommendation\": \"Fix indentation; optionally add a digest assertion with a null-error_class failed node.\"\n }\n]\n```\n\n## unverified_claims\n\n- Projection order follows publish order (WorkflowFailed seq < NodeFailed seq in the `dag.fail` path) — inferred from the publish sequence at dag.ts:524-527, not verified against the event-pipeline internals; the LOW torn-read finding stands only if this ordering holds.\n- `check:generated` not executed locally (would regenerate files; CI enforces the tracked `src/v2/gen` diff).\n- No test covers the digest on a workflow failed via `dag.fail` with a running non-wake-eligible node (the kill path); the tested path is required-node-failure → terminal.\n- No test covers pre-migration legacy failed rows (error_class null) through the read surfaces.\n- The httpapi-exercise `dag.nodes`/`dag.nodeDetail` scenarios assert only `length >= N`, so the additive optional field cannot break them — verified by inspection, not by running the exercise suite.\n\n## summary\n\nThe error_class dataflow is clean end to end: all four surfaces (tool status, wake summaries, HttpAPI, SDK) are string pass-throughs of the projector's `event.data.trigger` write, the `errorClass !== null` filters correctly exclude cancelled-via-replan rows, and the migration/schema/SDK regenerations are exactly the one intended field (tests 22+26+17 pass, typecheck clean). No CRITICAL/HIGH findings. The material issues are one overbroad doc claim (MEDIUM) — \"every failed node carries an error_class\" is false for cancelled-via-replan and pre-migration rows — plus four LOWs: the dead `push_exhausted` value (docs correctly list 3, comments advertise 4), a narrow cosmetic torn-read window in the wake digest for nodes killed by `dag.fail`, the unlisted workflow-collateral `exec_failed` stamping, and a missing test guard on the null-filter behavior." diff --git a/.opencode/.dag-specs/review-parts-diff/review-prompts.md b/.opencode/.dag-specs/review-parts-diff/review-prompts.md deleted file mode 100644 index 6628ea1a0c..0000000000 --- a/.opencode/.dag-specs/review-parts-diff/review-prompts.md +++ /dev/null @@ -1 +0,0 @@ -"Verification complete. All claims checked against code. Here is the review:\n\n---\n\n## 1. findings\n\n| # | Severity | Title | Description | Evidence | Recommendation |\n|---|----------|-------|-------------|----------|----------------|\n| 1 | **HIGH** | \"reason starts with `Dependency \"X\" failed/skipped`\" never appears as an error_reason — the row's detection criterion cannot match anything in status/wake | The strings `Dependency \"X\" failed: ...` / `Dependency \"X\" skipped: ...` are input-mapping **placeholder values** injected into the dependent node's PROMPT when resolving `{{dep}}` (loop.ts:133/136). They are never an error_reason: an exhaustive inventory of all 20+ `dag.nodeFailed` call sites (spawn.ts, loop.ts, recovery.ts, dag.ts) contains no path producing this prefix. Actual cascade semantics: (a) **required** root fails → `cascadeUnsatisfied` marks all transitive dependents unsatisfied, they never spawn and stay durable `pending` with **no error_reason at all** (scheduling.ts:109-121); the wake shows only the root in \"Failed nodes\". (b) **optional** root fails → dependents DO run, receiving the `Dependency \"X\" failed:` text inside their prompt Context (verified by the repo's own test asserting the text in the *arbitrate child's prompt input*, dag-wake-integration.test.ts:438). The row's response \"replace the cascaded subtree\" is also wrong for case (a) — dependents are untouched pending nodes, not failed ones. | workflow.md:412; loop.ts:133,136; scheduling.ts:109-121,162-174; dag-wake-integration.test.ts:438 | Rewrite the row: cascade is detected by (a) required-root failure leaving dependents `pending` in `status` while the workflow is `failed` (wake shows \"required node(s) failed: X\", loop.ts:272), or (b) the `Dependency \"X\" failed:` text inside a *running/completed* dependent's prompt context — never in error_reason. Response: fix root X; in case (a) simply resume/continue with the root replaced; in case (b) re-run dependents that consumed placeholder input. |\n| 2 | **MEDIUM** | exec_failed row's reason list omits major sub-causes and its \"fix the config first\" response misdirects for them | Real exec_failed reasons beyond those listed: `execution ownership lost on recovery` (recovery.ts:141 — the dominant crash-recovery reason, and the exact string the doc's own Crash recovery section at workflow.md:390 tells the agent to expect), workflow-level collateral kills `workflow_failed`/`orchestrator_unresponsive`/`required node(s) failed: ...` published to still-running nodes via terminateNonTerminalNodes with hardcoded trigger `exec_failed` (dag.ts:492, loop.ts:272,830), `replan attempt ceiling exceeded` (dag.ts:599), condition-eval errors (loop.ts:114), `Template resolution failed` (loop.ts:173). For ownership-loss and collateral-kill reasons, \"Fix the config first (dag.jsonc tier, provider credentials, model id), then replace and rerun ONLY that node\" is wrong advice — no config is broken, and the collateral-killed node's failure is a symptom of a sibling's root cause. Also `\"no child session on recovery\"` is a substring of the actual `\"node was running but had no child session on recovery\"` (recovery.ts:79) — fine for substring matching but not an exact string as quoted. | workflow.md:409; recovery.ts:79,108,141; dag.ts:492,599; loop.ts:272,830 | Expand the exec_failed row: list ownership-loss and workflow-collateral reasons explicitly, and gate the response on the reason: config fixes only for model/auth/provider/template/condition reasons; replace+rerun for ownership loss; look at sibling root causes for collateral-killed nodes. |\n| 3 | **MEDIUM** | verdict_fail row claims \"The node ran\" — false for pre-spawn verdict_fail failures, whose fix is not \"state the contract explicitly\" | Two verdict_fail reasons fire **before the child session exists**: `Unresolved template placeholders: ...` (loop.ts:186 — the node never spawned; the aggregate-node test at dag-wake-integration.test.ts:693-731 confirms it) and `Review input contract failed: ...` (loop.ts:153). For these, \"Rerun the node with the contract stated explicitly\" cannot fix a template with unresolved `{{placeholder}}` or a review node whose input mapping lacks implementation evidence — the template/mapping must be fixed. Listed reasons themselves are accurate (missing submit_result → \"output_schema declared but submit_result was never successfully called\", capture.ts:140-141; schema rejection → child gets \"Validation failed\" and captured stays null, submit_result.ts:49; fingerprint → \"Review result contract failed: ...\", capture.ts:143; plus \"provider returned empty output\", spawn.ts:258). | workflow.md:410; loop.ts:153,186; capture.ts:140-145; spawn.ts:258; dag-wake-integration.test.ts:706-709 | Split verdict_fail into ran-but-broke-contract (submit_result/schema/fingerprint → rerun with contract explicit) vs never-ran (unresolved placeholder/input-contract → fix template/mapping, then rerun). |\n| 4 | **LOW** | timeout row's \"Check its child_session_id for partial artifacts\" holds only for the main timeout path | Only `node exceeded timeout of {X}ms` (spawn.ts:231) has a cancellable child session whose messages persist (SessionPrompt.cancel only aborts the in-flight prompt; the NodeFailed projection keeps child_session_id on the row — projector.ts:279-291). The pre-permit (spawn.ts:163) and permit-wait (spawn.ts:176) timeout reasons have **no child session at all** (child_session_id null), and `deadline exceeded on recovery` (recovery.ts:130) cancels the session during recovery, not \"at the deadline\". So \"the runtime cancelled its child session at the deadline\" is accurate only for the dominant path. | workflow.md:408; spawn.ts:163,176,230-231; recovery.ts:130; projector.ts:279-291 | Note in the row that child_session_id may be null for permit-queue timeout variants; partial-artifact checking applies to the running-timeout case. |\n| 5 | **LOW** | \"Every failed node carries an error_class in ... the wake summary\" — wake attribution silently drops null-class rows | The wake \"Failed nodes\" block filters `errorClass !== null` (loop.ts:857), and the node line shows the class only when set (loop.ts:868). Fresh runs always carry a trigger (every nodeFailed publishes one), so the doc holds in practice; only pre-migration rows are invisible. Related: both docs enumerate 3 classes omitting `push_exhausted` (dag-flow.txt:37, workflow.md:407-412) — harmless because push_exhausted exists only in the schema enum and comments (dag-event.ts:253, sql.ts:65, groups/dag.ts:43) and is **never emitted by any runtime path** (exhaustive nodeFailed grep). | workflow.md:404; loop.ts:857,868; dag-flow.txt:37; dag-event.ts:253 | Optionally add \"every failed node carries an error_class (null only on pre-migration rows)\" and a note that push_exhausted is reserved/unused; not a behavior gap. |\n\n**Verified accurate (no finding):** timeout cancel at deadline (spawn.ts:230-231); wrong-model/auth/rate-limit/connection → exec_failed with discoverable Cause.pretty text (spawn.ts:78 \"unknown worker_type\"/\"no model configured for agent\", spawn.ts:300, loop.ts:245); budget strings exact — `replan attempt ceiling exceeded` (dag.ts:599, as a node-failed event with class exec_failed) and `Total node ceiling exceeded` (dag.ts:354,577, as a create/replan tool error) with working `(see Escalation)` cross-ref (workflow.md:382 mentions \"replan-attempt ceiling rejection\"); live repair path — pause valid from running/stepping (types.ts:198-207), replan valid while paused (dag.ts:542 terminal-guard only), `restart: true` semantics match tool schema (workflow.ts:53), `extend` works on live workflows (_extend → _replan, dag.ts:682-722), completed siblings preserved; terminal-failed irreversibility — `TerminalViolationError` on replan (dag.ts:542-546) and `getValidNextWorkflowStatuses(FAILED) → [ARCHIVED]` (types.ts:209-211); continuation workflow feasible — child sessions/messages durable after cancel, child_session_id exposed in status (workflow.ts:204), `reused_nodes`/manifest convention exists (dag-flow.txt:23,40); both docs agree on replacement-under-new-id mechanic with the Crash recovery section (workflow.md:386-399) and dag-flow.txt:39; no contradiction with Verdict Disposal Contract/Bounded Repair/Adaptive Replanning (workflow.md:106,249,363). Runtime check: `bun test test/dag/workflow-tool.test.ts` → 26 pass, incl. the new error_class-in-status assertion.\n\n## 2. unverified_claims\n\n- dag-wake-integration.test.ts:693-731 wake-text assertions (verdict_fail + \"Failed nodes:\" attribution) not executed — live integration harness, not run in this read-only review; statically consistent.\n- That a real wrong-model/provider auth error lands in error_reason via Cause.pretty — path verified statically (spawn.ts:300), not exercised against a live provider.\n- Session-part durability after promptSvc.cancel — inferred from SessionPrompt.cancel only aborting the in-flight prompt (prompt.ts:165-167); not executed end-to-end.\n- Replan-from-STEPPING (in \"still live (running/paused/stepping)\") — allowed by the status table (types.ts:204), not exercised at runtime.\n- `push_exhausted` truly never emitted — based on exhaustive grep of all nodeFailed call sites (no emitter found), not on runtime observation.\n\n## 3. summary\n\nThe triage section is substantially runtime-accurate: timeout-cancel, exec_failed surfaces, budget strings, live-vs-terminal repair paths, and cross-doc consistency (including the replacement-under-new-id mechanic) all verified against code, with workflow-tool tests passing. One HIGH drift: the Dependency-cascade row's \"reason starts with `Dependency \"X\" failed/skipped`\" criterion does not exist in any error_reason — cascade appears as pending dependents (required root) or as placeholder text inside dependents' prompts (optional root), so the parent would search for a signal that never occurs. Two MEDIUM gaps: exec_failed and verdict_fail rows under-describe their real reason sets and prescribe fixes (config-first; \"state the contract\") that misdirect for ownership-loss, collateral-kill, and pre-spawn verdict_fail failures." diff --git a/.opencode/.dag-specs/review-parts-diff/review-runtime.md b/.opencode/.dag-specs/review-parts-diff/review-runtime.md deleted file mode 100644 index 956e68ff19..0000000000 --- a/.opencode/.dag-specs/review-parts-diff/review-runtime.md +++ /dev/null @@ -1 +0,0 @@ -"All tests pass. Here is my review.\n\n---\n\n# RUNTIME SEMANTICS REVIEW — `packages/opencode/src/dag/runtime/loop.ts` (uncommitted hunks)\n\n## 1. findings\n\n**f-1 | MEDIUM | Collateral `exec_failed` attribution is indistinguishable from genuine node failures in the digest — mis-triage risk on the orchestrator_unresponsive path**\n`Dag.fail` (packages/opencode/src/dag/dag.ts:523-527) publishes `WorkflowFailed` then `terminateNonTerminalNodes` publishes `NodeFailed` with a hardcoded `trigger: \"exec_failed\"` (dag.ts:492) for every running node killed by the workflow's failure. The new digest (loop.ts:855-864) renders these as `(exec_failed)` alongside genuinely failed nodes, and the workflow row carries no failure reason (projector.ts:117-123 sets only status/seq/completed_at), so the digest alone cannot distinguish \"node's own execution failed\" from \"node killed by workflow failure\". The `workflow.md` triage table (workflow.md:410) instructs `exec_failed` → \"fix config first … replan the graph only when the reason shows the task itself failed\" — a parent following the table on a workflow failed by the unresponsive net (nodes show reason \"orchestrator_unresponsive\", class \"exec_failed\") gets table guidance that fits the class but mislabels the mechanism. The mandatory-action line (loop.ts:882-884) and the prior wake's threat make it recoverable for an attentive parent, but the new digest amplifies the mislabeling rather than mitigating it.\nRecommendation: render collateral kills distinctly in the digest (e.g., a `(killed by workflow failure)` marker derived from the node's errorReason matching the workflow-fail reason, or pass the real trigger through `terminateNonTerminalNodes`) — or at minimum document in the triage table that `exec_failed` with reason `orchestrator_unresponsive` means collateral.\n\n**f-2 | LOW | Double reporting of every failed node — same-wake redundancy plus full-history repetition**\nThe batch composition guarantees the same failed node appears twice in one wake when a node failure and its workflow terminalization land in the same batch: once as `[DAG Node Result] Node \"x\" failed (exec_failed): …` (from `batch.nodes`, loop.ts:867-873) and again inside `Failed nodes:\\n- \"x\" (exec_failed): …` (loop.ts:874-878). `getNodes` (loop.ts:855) reads the full history with no `wake_reported` filter, so nodes whose failures were delivered in earlier batches also reappear in the terminal digest; a node that failed between the batch snapshot and the digest read is included too (digest is a superset of the batch — never a missing-node case, verified by construction). The new test exercises exactly this double report (dag-wake-integration.test.ts:729-730 asserts the digest while the same wake contains the node line). Impact: ~300 chars duplicated per failed node; information is consistent, not contradictory — acceptable, but redundant.\nRecommendation: acceptable as-is; if prompt budget matters, drop the node line for failed nodes already covered by a workflow digest in the same batch, or cap digest entries to nodes not already reported this batch.\n\n**f-3 | LOW | Silent `Effect.catch` fallback diverges from the file's failure-handling convention and can silently drop attribution**\n`getNodes` uses `Effect.orDie` (packages/core/src/dag/store.ts:241), so its errors surface as defects — caught by `Effect.catch` (v4 unified channel) and replaced with `[]` with no logging (loop.ts:857). Every other store read in this delivery path uses `Effect.catchCause` + `Effect.logWarning` (loop.ts:945-946, 958-961, 969-974). A busy/locked DB at delivery time silently degrades the digest to the old text with zero trace, which is exactly the class of silent failure the surrounding code deliberately logs.\nRecommendation: `Effect.catchCause((cause) => Effect.logWarning(\"DagLoop failed to read failed nodes for digest\", { workflowID, cause }).pipe(Effect.as([])))` to match local convention.\n\n**f-4 | LOW | Multibyte truncation can split surrogate pairs**\n`slice(0, 300)` (loop.ts:862) and the pre-existing `slice(0, 500)` (loop.ts:869-870) count UTF-16 code units; an astral character (emoji in node name/reason) at the cut boundary yields a lone surrogate embedded in the prompt part, rendering as U+FFFD in most parsers/encoders. Cosmetic; pre-existing pattern, now also applied to digest lines.\nRecommendation: use `Intl.Segmenter`/`Array.from`-based truncation or leave as-is (documented known wart).\n\n**f-5 | LOW | Digest size introduces a full-history size class (bounded but non-trivial)**\nThe digest is a full-history read of all failed nodes per failed workflow, each line capped at 300 chars; node count is bounded by the declared graph (default budget `max_total_nodes: 100`, packages/opencode/src/tool/workflow.ts:82) → worst case ≈ 30KB per failed workflow in one prompt part, multiplied across multiple failed workflows in one session batch. Pre-existing node lines were also per-node (500×N) but only for unreported rows in the terminal batch; the digest adds another full-history copy. Bounded, not a new order of magnitude, but the largest single-prompt-part regression this diff introduces.\nRecommendation: acceptable; consider a digest entry cap (e.g., first 20 failed nodes + \"… N more\") for large graphs.\n\n**f-6 | LOW | Digest reads through `dag.store` — a second DagStore instance — instead of the loop's local `store`**\nloop.ts:855 uses `dag.store.getNodes` while every other read in the delivery path uses the local `store` (DagStore.Service, loop.ts:52). `Dag.Service.store` (dag.ts:243) is bound to the Dag layer's own `Layer.provide(DagStore.defaultLayer)` (dag.ts:790), so it is a different service instance/connection than the one used for `getWakeSnapshot`/`markWakeBatchReported`. Both point at the same DB file under the same InstanceRef, so committed-row consistency holds (verified: tests pass), and the fresh-read-on-another-connection can only add rows beyond the batch snapshot (benign superset). No functional defect — a consistency/style issue on the most-sensitive delivery path.\nRecommendation: use `store.getNodes` for uniformity with the rest of the generator.\n\n**No CRITICAL/HIGH findings.** Verified non-issues: (1) lock/ordering — the digest reads acquire no locks and don't touch the evalLock/workflowLock discipline (which governs runtime/fibers state, not store rows; the torn-read comment at loop.ts:807-819 is unaffected); `failuresByWorkflow` is scoped per batch iteration (recreated at loop.ts:852 inside `for(;;)`), no stale-map leak. (2) `wake_reported` persistence ordering unchanged — batch rows are still marked only after successful `promptIfIdle` (loop.ts:894-915); the digest's already-reported rows need no marking; a failed delivery leaves everything unreported for retry. (3) Preemption semantics preserved — `promptIfIdle` atomically claims idle via `startIfIdle` (packages/opencode/src/session/prompt.ts:1394); the new reads only widen the existing guard→admission window. (4) `actionableDagIDs` excludes terminal workflows (loop.ts:760-762), so a failed workflow never sits in both the digest and the mandatory-action line of one wake. (5) When the unresponsive net fails a workflow inside this delivery (loop.ts:830), the terminal handler's fork → `wakePending` → retry (loop.ts:918-921, 707-708) delivers the digest in a fresh call. (6) Regression paths — completed/cancelled workflow lines are byte-identical (`attribution` is `\"\"` since only `status === \"failed\"` is processed), node lines identical for non-failed nodes and for legacy failed nodes with `errorClass === null`, skipped-node lines untouched. Tests executed: `dag-wake-integration.test.ts` (new aggregate-failure test PASS, exec_failed persistence PASS), `workflow-tool.test.ts` 26/26 PASS.\n\n## 2. unverified_claims\n\n- The same-wake double report (f-2) is inferred from `readWakeBatch` composition (loop.ts:721-775), not asserted by the test — the test checks only the digest substring, so the node-line half of the double report is unobserved.\n- `Effect.catch` in this repo's Effect v4 vendored build catches `orDie` defects — high confidence (unified channel semantics) but not verified against the vendored version.\n- SQLite journal mode (WAL vs DELETE) for the cross-connection read in f-6 — not verified; DELETE mode would serialize readers behind writers, making f-3's silent-fallback path more reachable.\n- `max_total_nodes` (100) is a tool-level budget; whether the store can hold a graph with more declared nodes than the budget (which would raise f-5's worst case) — not verified.\n- Digest behavior when the parent session was busy across the entire failure window (digest delivered from the restart scan, loop.ts:957-978, with the batch rows still unreported) — path traced, not executed.\n\n## 3. failure_scenarios\n\n- {scenario: getNodes hits a busy/locked DB at delivery time → digest silently degraded to old text, no log trace, impact: lost attribution for triage, likelihood: LOW}\n- {scenario: workflow failed by orchestrator_unresponsive → digest shows all collateral nodes as (exec_failed); parent follows the triage table and replans/repairs the wrong thing or starts an unnecessary continuation, impact: wasted provider work / wrong repair, likelihood: MEDIUM (fires whenever the unresponsive net kills a running workflow)}\n- {scenario: large failed workflow (≈100 nodes) terminalizes → single prompt part ≈30KB digest + node lines, impact: context dilution in the parent session, likelihood: LOW}\n- {scenario: node name/errorReason contains astral chars at the 300-char cut → lone surrogate in wake text, impact: cosmetic garbling, likelihood: LOW}\n- {scenario: legacy rows (pre-migration) with error_class NULL → failed nodes omitted from digest while still listed as individual node lines, impact: digest appears incomplete, likelihood: LOW (one-time migration window)}\n\n## 4. summary\n\nThe new digest read is safe under the existing delivery discipline — no lock interaction, no `wake_reported` reorder, terminal/actionable sets never overlap, and completed/cancelled/skipped paths are byte-identical regressions (all executed tests pass). The material risks are semantic, not mechanical: collateral `exec_failed` kills from the orchestrator-unresponsive net are presented in the digest indistinguishably from genuine node failures (f-1, MEDIUM — the only finding that could steer a parent wrong), and the digest doubles reported content and a silent-fallback path that break the file's own conventions. No CRITICAL/HIGH; recommended fixes are small (mark collateral kills, use `store` + `catchCause` with logging, cap digest entries)." diff --git a/.opencode/.dag-specs/review-parts-diff/review-style.md b/.opencode/.dag-specs/review-parts-diff/review-style.md deleted file mode 100644 index 27bc3fee2c..0000000000 --- a/.opencode/.dag-specs/review-parts-diff/review-style.md +++ /dev/null @@ -1 +0,0 @@ -"# STYLE & CONVENTIONS REVIEW — error_class diff\n\n审查基线:`git diff HEAD` 全部 16 个已跟踪 hunk + 未跟踪迁移文件。对照 root AGENTS.md Style Guide、packages/opencode/AGENTS.md、test/AGENTS.md。\n\n## 通过项(无 finding)\n\n- **注释合规**:`sql.ts:65` 与 `groups/dag.ts:42-44` 两条新注释均解释非显而易见语义(值域 + 事件来源),且分别匹配文件内既有注释约定(`sql.ts` 的 captured_output/deadline_ms/wake_eligible 均为解释型单行注释;`groups/dag.ts` 的 deadline_ms 同为双行注释)。迁移与 loop.ts 无注释,符合\"仅非显然约束加注释\"规则。\n- **迁移约定**:`20260803073521_workflow_node_error_class.ts` 的 id=文件名、时间戳格式与近半年描述性 snake_case 命名(`20260720013828_dag-workflow-node-identity`)一致;`migration.gen.ts:50` 按时序末位注册;`schema.gen.ts:86` 列序(error_reason→error_class→captured_output)与 `sql.ts` 定义序一致;`schema.json` id 轮换、实体插序正确;`version` 保持 \"7\" 与最近四个迁移提交一致;列名 `error_class` 直接 snake_case 无字符串覆盖(drizzle 规则 ✓)。\n- **Effect 约定**:无新增 `Effect.fn`;无嵌套 service yield;`Effect.catch` 用法与同文件 line 841 形状一致;未引入 `any`。\n- **测试风格**:`dag-wake-integration.test.ts:726,727-731` 完整沿用既有 parent-wake 模式(`takeWithin` + `promptText` + `Deferred.succeed(parent.release, \"success\")`,对齐 line 514-522 先例);`:448` 的 `?.errorClass` 断言镜像 `:447` 的 `?.status`;`fixtures.ts:18` 与 mock 默认值补齐符合文件模式。\n\n## findings\n\n```json\n[\n {\n \"severity\": \"LOW\",\n \"title\": \"loop.ts 新代码绕过已绑定的 store 变量改用 dag.store\",\n \"description\": \"文件顶部(loop.ts:52)已绑定 `const store = yield* DagStore.Service`,同文件其余 7 处 getNodes 调用(97/261/367/411/660 等)全部使用 `store`。新代码 loop.ts:855 却写 `dag.store.getNodes(...)`,绕过了已绑定的命名变量。已核验 dag.ts:284+765-767:`dag.store` 由同一 `DagStore.Service` yield 构造,功能上等价,纯约定偏差。\",\n \"evidence\": \"packages/opencode/src/dag/runtime/loop.ts:855(对比 loop.ts:52, 97, 261, 367, 411, 660)\",\n \"recommendation\": \"改为 `store.getNodes(workflow.id)`,与文件内既有调用点一致。\"\n },\n {\n \"severity\": \"LOW\",\n \"title\": \"errorClass 过滤缺类型谓词,下游类型未收窄\",\n \"description\": \"loop.ts:856 的 `nodes.filter((node) => node.status === \\\"failed\\\" && node.errorClass !== null)` 无类型谓词,`map` 中 `node.errorClass` 类型仍为 `string | null`,模板 `(${node.errorClass})`(:862)仅靠运行时过滤保证非空,typecheck 无法证明。root AGENTS.md 明确要求 \\\"use type guards on filter to maintain type inference downstream\\\",且同文件 loop.ts:736 已有 `(workflow): workflow is DagStore.WorkflowRow` 谓词先例。\",\n \"evidence\": \"packages/opencode/src/dag/runtime/loop.ts:856-862(对照 loop.ts:736)\",\n \"recommendation\": \"给 filter 加类型谓词(如 `(node): node is DagStore.NodeRow & { errorClass: string; status: \\\"failed\\\" }`),使下游 `node.errorClass` 收窄为 string。\"\n },\n {\n \"severity\": \"LOW\",\n \"title\": \"冗余的 `as DagStore.NodeRow[]` 类型断言,与文件既有模式不一致\",\n \"description\": \"loop.ts:857 `Effect.catch(() => Effect.succeed([] as DagStore.NodeRow[]))` — `[]` 推断为 `never[]` 可赋值给 `NodeRow[]`,断言大概率多余;同文件 :841 的同类 catch 写作 `Effect.succeed([])` 无断言。\",\n \"evidence\": \"packages/opencode/src/dag/runtime/loop.ts:857(对照 loop.ts:841)\",\n \"recommendation\": \"去掉断言,与 :841 保持一致(需 typecheck 复核)。\"\n },\n {\n \"severity\": \"LOW\",\n \"title\": \"workflow-tool.test.ts mock 数组分隔符缩进错位\",\n \"description\": \"新增的 `}, {`(:190)与 `}]`(:214)缩进 10 空格,而数组元素对象(含既有 node_running)为 8 空格,新增行与文件自身约定错位 2 格。\",\n \"evidence\": \"packages/opencode/test/dag/workflow-tool.test.ts:190,214(对比 :167, :189)\",\n \"recommendation\": \"将 `}, {` 与 `}]` 对齐到 8 空格。\"\n },\n {\n \"severity\": \"LOW\",\n \"title\": \"两处文档将 3/4 的 error_class 值列举为完整集合\",\n \"description\": \"workflow.md:409-412 分诊表与 dag-flow.txt:37 的括号列举均只含 timeout/exec_failed/verdict_fail,且无 \\\"e.g.\\\" 或省略标记,读作穷举;而 schema 枚举(dag-event.ts:253)含第 4 值 push_exhausted,同一变更内两处代码注释(sql.ts:65、groups/dag.ts:42-44)均列全 4 值。文档与代码注释在同一 diff 内已出现值集不一致,存在漂移风险。遗漏是否构成行为指导缺口属 review-prompts/仲裁范畴,此处仅指风格一致性。\",\n \"evidence\": \"packages/core/src/plugin/command/workflow.md:409-412, packages/core/src/plugin/command/dag-flow.txt:37(对照 packages/schema/src/dag-event.ts:253, packages/core/src/dag/sql.ts:65, packages/opencode/src/server/routes/instance/httpapi/groups/dag.ts:43-44)\",\n \"recommendation\": \"补全 push_exhausted(或明确标注非穷举),使三处枚举一致。\"\n },\n {\n \"severity\": \"LOW\",\n \"title\": \"dag-flow.txt 收尾过渡句被新章节隔断\",\n \"description\": \"文件原为编号指令块(1-13)后直接接收尾过渡句 \\\"Use the orchestration guidance below to design and manage the workflow.\\\"。新 \\\"## Resume-first\\\" 节插在两者之间,使过渡句读作新章节的尾部而非指令块的收尾,编号块与指向语之间的衔接被破坏。\",\n \"evidence\": \"packages/core/src/plugin/command/dag-flow.txt:35-46(新节插于 :34 与收尾句之间)\",\n \"recommendation\": \"将收尾过渡句移回编号块正后方(新节之前),或若有意为之,确认章节顺序的语义(Resume-first 属启动前决策,可考虑放在编号块前)。\"\n }\n]\n```\n\n## unverified_claims\n\n- \"loop.ts:857 的 `as DagStore.NodeRow[]` 断言删去后 typecheck 仍通过\" — 依据 `never[]` 可赋值性推断,未实际运行 `bun typecheck` 验证。\n- \"`dag.store` 与绑定 `store` 在含 LayerNode/dag.jsonc 组合路径下恒等\" — 仅核验了 `Dag.layer` 构造路径(dag.ts:284,765-767),未追踪 `LayerNode.make` 组合(dag.ts:768)。\n- \"workflow.md:412 的 `Dependency \\\"X\\\" failed/skipped` 前缀真实存在于生产的 error_reason 文本\" — 仅由既有测试断言 dag-wake-integration.test.ts:438 佐证,未查证产生该字符串的运行时代码。\n- \"schema.json `version` 保持 7 符合生成脚本约定\" — 核验最近四个迁移提交均为 7,未检查 `script/migration.ts` 的版本递增逻辑。\n- \"新增 wake 文本断言、errorClass 持久化断言在测试套件中实际通过\" — 未执行测试(属 verify 通道)。\n\n## summary\n\n该 diff 风格上整体干净:两条新注释均满足\"仅非显然约束\"规则且贴合文件本地约定,迁移命名/id/注册/生成物(schema.gen.ts、schema.json、types.gen.ts)完全合规,测试新增均镜像既有断言模式,全 diff 无 `let`、`else`、冗余解构、import 别名或 `any`。共 6 个 LOW finding:loop.ts 绕过已绑定 `store` 变量、过滤缺类型谓词(违反 root AGENTS.md 显式规则且有同文件先例)、冗余 cast、测试 mock 缩进错位、两文档漏列第 4 枚举值且读作穷举、dag-flow.txt 过渡句被新节隔断。无 CRITICAL/HIGH;push_exhausted 遗漏的内容定性已移交 prompts/仲裁通道。" diff --git a/.opencode/.dag-specs/review-parts-diff/review-tests.md b/.opencode/.dag-specs/review-parts-diff/review-tests.md deleted file mode 100644 index c795a0cb74..0000000000 --- a/.opencode/.dag-specs/review-parts-diff/review-tests.md +++ /dev/null @@ -1 +0,0 @@ -"All evidence gathered. Both modified suites pass (26 + 22 tests), the fixture-dependent recovery/guard suites pass (38 tests), and `bun typecheck` is green. Here is the review.\n\n---\n\n## TEST COVERAGE REVIEW — error_class test hunks\n\n### 1. Findings\n\n| # | Severity | Title | Description | Evidence | Recommendation |\n|---|----------|-------|-------------|----------|-----------------|\n| F1 | MEDIUM | httpapi `error_class` surface has zero coverage | The handler (non-null → `error_class`) and the new `NodeResponse` optional field are never exercised: `httpapi-exercise` seeds only fresh pending nodes and asserts loose fields (`id`, `status`, `depends_on`, `replan_attempts`); no scenario seeds a failed node, so neither the presence of `error_class` on a failed node nor its absence on a healthy node is checked. The loose checks mean CI does not break — but the field ships untested to the SDK/TUI consumers. | packages/opencode/test/server/httpapi-exercise/index.ts:1807-1850; handler map at packages/opencode/src/server/routes/instance/httpapi/handlers/dag.ts:65 | Extend the `dag.nodeDetail`/`dag.nodes` scenarios to seed a failed node (insert a `dag.node.failed` event or DB row) and assert `error_class` present with the expected value; optionally a negative check on a completed node. |\n| F2 | MEDIUM | Recovery-invented failure classes never asserted | recovery.ts emits `NodeFailed` with `timeout`/`exec_failed`/`verdict_fail` triggers (recovery.ts:79, 99, 108, 130, 142), but neither `dag-recovery.test.ts` nor `dag-loop-recovery-integration.test.ts` asserts the resulting `errorClass` — recovery tests track events/outcomes, not the new column. These paths are exactly the \"recovered workflow failure\" cases the triage docs tell agents to read (`error_class` on recovery failures). | packages/opencode/src/dag/runtime/recovery.ts:79-145; dag-recovery.test.ts (event assertions only) | In a recovery integration test, assert `store.getNode(...).errorClass` for a deadline-exceeded node (`\"timeout\"`) and a no-child-session node (`\"exec_failed\"`), and assert the wake digest attribution when the recovered workflow terminalizes failed. |\n| F3 | MEDIUM | NodeCancelled null-`errorClass` invariant untested | NodeCancelled projection sets `status: \"failed\"` + `error_reason: \"cancelled via replan\"` but deliberately leaves `error_class` null (projector.ts:320-323); the wake digest filter `status === \"failed\" && errorClass !== null` (loop.ts:856) excludes exactly those rows. Neither side is pinned by a test — a future change setting the class in NodeCancelled (or dropping the filter) would silently change digest contents. | packages/core/src/dag/projector.ts:320-323; packages/opencode/src/dag/runtime/loop.ts:856 | In an existing replan-cancel test, assert the cancelled row has `errorClass === null`; in a wake test with a cancelled-then-terminal workflow, assert the cancelled node is absent from `Failed nodes:`. |\n| F4 | MEDIUM | Multi-failed-node digest ordering and truncation untested | `getNodes` orders `desc(seq)` (store.ts:239) so the digest lists newest failure first; each line is `.slice(0, 300)` (loop.ts:862). Only a single-failed-node digest is asserted; the workflow-level cascading fail (`dag.ts:492`, hardcoded `exec_failed`) would put collateral failed nodes in the digest alongside the primary — semantics no test documents. | packages/core/src/dag/store.ts:234-243; packages/opencode/src/dag/runtime/loop.ts:852-865 | Add a scenario with ≥2 failed nodes (primary + collateral from workflow fail) asserting both appear, order (desc seq), and a >300-char reason truncates. |\n| F5 | LOW | Wake node-line class suffix exercised but unasserted | In the aggregate-placeholder test the failed `summary` node is also in `batch.nodes` (registered `report_to_parent: true` → `wake_eligible` never cleared by NodeFailed projection; `getWakeSnapshot` selects `status IN (completed, failed)`), so the text contains `[DAG Node Result] Node \"summary\" failed (verdict_fail): …` — but the test asserts only the workflow line and the digest (line 729-730), leaving loop.ts:871's node-line suffix logic unasserted. | packages/opencode/test/dag/dag-wake-integration.test.ts:727-731; loop.ts:867-873 | Add one `toContain('Node \"summary\" failed (verdict_fail):')` to the existing wake assertions. |\n| F6 | LOW | Mock failed node encodes an impossible runtime state | `node_failed` has `dependsOn: [\"node_running\"]` while `node_running` is still `status: \"running\"` — a dependent can never fail before its dependency completes in the real runtime. Acceptable for a rendering unit test, but the fixture models an unreachable state; also no negative assertion that the running node's JSON omits `error_class`. | packages/opencode/test/dag/workflow-tool.test.ts:166-214, 409-414 | Use `dependsOn: []` (or a completed dependency) for the failed node; add `expect(result.output).not.toContain('\"error_class\"')` for the running node. |\n| F7 | LOW | Indentation drift in the mock array | `}, {` at lines 190/214 sits at 10-space indent vs the 8-space array style around it. | packages/opencode/test/dag/workflow-tool.test.ts:190, 214 | Run the formatter (or align to 8 spaces). |\n| F8 | LOW | `\"unknown error\"` fallback untested | `node.errorReason ?? \"unknown error\"` (loop.ts:862) is unreachable through any current producer (every NodeFailed carries a reason); only DB tampering reaches it. Deferrable, but worth a one-line assertion if a fixture ever sets `failed + class + null reason`. | packages/opencode/src/dag/runtime/loop.ts:862 | Optional: seed a row with `errorClass` set and `errorReason` null in the digest test. |\n\nNo CRITICAL/HIGH: every primary behavior (projector persistence, status tool output, wake digest attribution) has at least one passing assertion, and hygiene checks pass.\n\n### 2. unverified_claims\n\n- The aggregate test's wake text contains the node-line `[DAG Node Result] Node \"summary\" failed (verdict_fail): …` — derived from static analysis (registration `wake_eligible: true` via `report_to_parent`, NodeFailed projection never clears `wake_eligible`, `getWakeSnapshot` includes `status IN (\"completed\",\"failed\")` rows, terminal workflows are delivery boundaries), not from an executed assertion (F5).\n- `push_exhausted` has no runtime producer — verified by `rg` over `packages/opencode/src` + `packages/core/src` (only the `types.ts` enum and comments); hence its absence from tests is by-design, and its absence from the triage docs is a docs nit, not a coverage gap.\n- The workflow-tool mock's `node_failed` depends-on-running-dependency state is unreachable in the real runtime — static inference from the scheduling rule that dependents spawn only after dependencies settle (F6).\n- The digest's per-line 300-char truncation and desc-seq ordering follow directly from store.ts:239 + loop.ts:862 (read, not executed in any test).\n\n### 3. coverage_gaps\n\n| path | untested_scenarios |\n|------|--------------------|\n| packages/opencode/src/server/routes/instance/httpapi/handlers/dag.ts (error_class mapping) | node endpoint returns `error_class` for a failed node; field absent for non-failed nodes; SDK type carries it through |\n| packages/opencode/src/dag/runtime/recovery.ts | `errorClass` persisted as `timeout` (deadline on recovery), `exec_failed` (no child session / failed child / ownership loss), `verdict_fail` (recovered settlement gate); wake digest attribution after a recovery-failed terminal workflow |\n| packages/opencode/src/dag/runtime/loop.ts (failed-workflow digest) | ≥2 failed nodes ordering (desc seq) and per-line 300-char truncation; `\"unknown error\"` fallback; exclusion of `errorClass === null` failed rows (cancelled via replan) |\n| packages/core/src/dag/projector.ts (NodeCancelled projection) | cancelled-via-replan row keeps `error_class` null while `status` becomes \"failed\" |\n| packages/opencode/test/dag/dag-wake-integration.test.ts | node-line class suffix (`Node \"summary\" failed (verdict_fail):`) — present in the text but unasserted (F5) |\n| packages/opencode/src/tool/workflow.ts (status action) | negative case: `error_class` omitted when null (running/healthy nodes) |\n\n### 4. summary\n\nThe new assertions correctly cover the three primary surfaces — projector persistence via `store.getNode` (`exec_failed` at :448, `verdict_fail` at :726), status-tool output (`\"error_class\": \"timeout\"` at workflow-tool.test.ts:414), and the wake `Failed nodes:` digest — and the exact-string digest assertions are appropriately pinned (contract-level, reason-prefix matched so wording changes don't flake); suites (26+22+38 tests) and typecheck are green, and the `makeNodeRow`/mock-store `errorClass: null` defaults keep all pre-existing suites valid (no other NodeRow literal exists outside the dag test dir; TUI/SDK consume SDK types, not NodeRow). No CRITICAL/HIGH findings; the real coverage debt is on secondary surfaces: the httpapi endpoint, recovery-invented failure classes, the NodeCancelled null-class invariant, multi-node digest ordering/truncation, and the node-line class suffix that the aggregate test already exercises but never asserts." diff --git a/.opencode/.dag-specs/review-parts-diff/scope-diff.md b/.opencode/.dag-specs/review-parts-diff/scope-diff.md deleted file mode 100644 index aa65f3f348..0000000000 --- a/.opencode/.dag-specs/review-parts-diff/scope-diff.md +++ /dev/null @@ -1 +0,0 @@ -"# Diff 范围地图 — failure-class 持久化 + 暴露 + 分诊指导\n\n## 1. 变更全景图(git diff HEAD:16 tracked 文件,+118/-8;另 1 个未跟踪新迁移文件)\n\n变更主线单一:**`dag.node.failed` 事件的 `trigger`(error_class)从瞬态事件数据 → 持久化列 → 全部 agent 可见读面 → 分诊提示词**。无跨主线 hunk。\n\n### 区域 A — 持久化层(packages/core,7 文件 + 1 新文件)\n| 文件 | 变更 |\n|---|---|\n| `src/database/migration/20260803073521_workflow_node_error_class.ts` | **[未跟踪新文件]** `ALTER TABLE workflow_node ADD error_class text`(可空、无默认值),id 与文件名一致 |\n| `src/dag/sql.ts:65` | 新增 `error_class: text()` 列,注释内联枚举四值 timeout/exec_failed/verdict_fail/push_exhausted |\n| `src/dag/projector.ts:285` | `dag.node.failed` 投影处新增 `error_class: event.data.trigger`(与 `error_reason` 同 set 块) |\n| `src/dag/store.ts:42,104` | `NodeRow.errorClass: string \\| null` + `mapNode` 映射 |\n| `src/database/migration.gen.ts:50` | 注册新迁移(时序末位) |\n| `src/database/schema.gen.ts:86` | DDL 快照加列 |\n| `schema.json:4-7,597-606` | 版本 id 轮换 + 新列实体(生成物) |\n\n**意图**:error_class 落库。枚举源头已核验:`packages/schema/src/dag-event.ts:253` — `trigger: Schema.Literals([\"exec_failed\",\"push_exhausted\",\"verdict_fail\",\"timeout\"])`,共 **4 值**;projector 直接透传 `event.data.trigger`,无转换层。\n\n### 区域 B — 读面(packages/opencode,4 文件)\n| 文件 | 变更 |\n|---|---|\n| `src/tool/workflow.ts:204` | `status` action 的节点 JSON 增加 `error_class`(非空才输出) |\n| `src/dag/runtime/loop.ts:852-879` | Wake 摘要两处:① 节点行 `Node \"X\" failed (errorClass): reason`;② **新增**:batch 内每个 status===\"failed\" 的 workflow 调 `store.getNodes` 重查失败节点,workflow 终止行追加 `Failed nodes:\\n- \"name\" (class): reason`(单行截断 300 字符,getNodes 失败静默降级为空) |\n| `src/server/routes/instance/httpapi/groups/dag.ts:42-44` | `NodeResponse` 新增可选 `error_class`(注释列四值) |\n| `src/server/routes/instance/httpapi/handlers/dag.ts:65` | handler 映射 `errorClass → error_class`(非空才输出) |\n\n**意图**:三条 agent/客户端可见路径(workflow tool status、父会话 wake prompt、HttpAPI/TUI SDK)统一暴露 error_class。loop.ts 是唯一有**运行时行为变化**的文件(新增一次 store 读 + 摘要格式变化),wake-path 竞态/顺序审查重点在 `loop.ts:839-849` 的 idle/抢占守卫与新 getNodes 调用之间。\n\n### 区域 C — SDK(packages/sdk/js,1 文件)\n`src/v2/gen/types.gen.ts:3909`:`DagNode.error_class?: string`,位置紧随 `error_reason`,**恰好一个字段**。\n**意图**:OpenAPI → SDK 再生成的产物。**佐证物**:未跟踪的 `packages/sdk/js/openapi.json`(今日 15:44 重新生成;该文件已于 commit `881ca8643` 移出 git 跟踪,属构建产物——见第 4 节审计项)。\n\n### 区域 D — 指导文档(packages/core/src/plugin/command,2 文件)\n| 文件 | 变更 |\n|---|---|\n| `workflow.md:401-427` | 新增 \"Node failure triage\" 节:error_class → 响应动作映射表(timeout/exec_failed/verdict_fail 三行 + Dependency 级联行)、budget 耗尽单列、live vs terminal 两种修复路径(pause→replan→resume / continuation workflow)、硬规则\"环境性单节点失败不得整图重启\" |\n| `dag-flow.txt:35-44` | 新增 \"Resume-first\" 节:中断工作流优先续跑(status 读取→暂停恢复→续跑已完成波→最后才重启),含 fail-closed 守卫 |\n\n**意图**:把新暴露的 error_class 转化为父 agent 的行为指导。**注意**:两文档的枚举列举均只写 3 值(timeout/exec_failed/verdict_fail),**遗漏 schema 枚举的第 4 值 `push_exhausted`**(见第 4 节审计项)。\n\n### 区域 E — 测试(packages/opencode/test/dag,3 文件)\n| 文件 | 变更 |\n|---|---|\n| `fixtures.ts:18` | `makeNodeRow` 默认 `errorClass: null` |\n| `dag-wake-integration.test.ts:448,695-731` | ① exec_failed 场景断言持久化 `errorClass`;② 聚合节点占位符失败场景:断言 `errorClass===\"verdict_fail\"` + **新增 wake 文本断言**(`[DAG Workflow failed]...` 与 `Failed nodes:\\n- \"summary\" (verdict_fail):`),并补 `parent.release` 放行 |\n| `workflow-tool.test.ts:189-212,413-414` | mock store 新增 `node_failed`(errorClass:\"timeout\")节点,status 输出断言 `\"error_class\": \"timeout\"` |\n\n**意图**:覆盖持久化、wake 归因文本、tool status 三条新路径。\n\n## 2. 参考清单(Reference Manifest,按要求格式输出,含一处事实更正)\n\n- **reference_template**: `deep-review-dag-module`\n- **added nodes**: `scope-diff`, `review-dataflow`, `review-runtime`, `review-contract`, `review-prompts`, `review-tests`, `review-style`, `verify-suite`\n- **pruned lanes**:\n - `{node: explore-core, prune_reason: \"target is a bounded 17-file change (16 tracked + 1 untracked new migration), not the whole module\", replacement_coverage: \"scope-diff consolidated change map injected into every reviewer lane\"}`\n - `{node: explore-runtime, prune_reason: 同上, replacement_coverage: 同上}`\n - `{node: explore-templates, prune_reason: 同上, replacement_coverage: 同上}`\n - `{node: explore-integrations, prune_reason: 同上, replacement_coverage: 同上}`\n\n> **事实更正说明**:原稿 prune_reason 写 \"16-file diff\"。`git diff HEAD --stat` 确实是 16 个已跟踪文件,但变更集还包含 1 个**未跟踪新迁移文件**(`20260803073521_workflow_node_error_class.ts`,migration.gen.ts 引用它),完整变更集为 17 文件。已更正计数,其余原样保留。\n\n## 3. 与声明目的无关的 hunk → **无**\n\n逐文件核对:16 个已跟踪文件的所有 hunk 均服务于\"持久化 error_class / 暴露到读面 / 分诊指导\"三目的之一,**未发现无关改动**。以下 4 项为**仲裁审计项**(非无关 hunk,但需裁定):\n\n1. **[审计·提示词一致性]** `workflow.md` 分诊表与 `dag-flow.txt:37` 的枚举列举均缺 `push_exhausted`(schema 枚举第 4 值,`dag-event.ts:253`;sql.ts 与 groups/dag.ts 注释均列齐 4 值)。review-prompts 裁定:遗漏是否构成指导缺口。\n2. **[审计·归因语义]** `dag.ts:492`(**diff 外既有代码**):workflow 级 fail/cancel 终止 running 节点时硬编码 `trigger: \"exec_failed\"` 发 NodeFailed——此类节点也会落 error_class,但其语义是\"被工作流连坐\"而非节点自身执行失败;且 `terminateNonTerminalNodes` 的 skip 分支(NodeSkipped)不产生 error_class。review-runtime 评估新指导文档对这类节点的表述是否准确。\n3. **[审计·仓库契约]** 未跟踪 `packages/sdk/js/openapi.json`(今日 15:44 生成,commit `881ca8643` 已将其移出跟踪,当前也未被 .gitignore 忽略)——确认为构建产物即可,但 review-contract 应核验 `check:generated` 与 httpapi exercise 契约(AGENTS.md:路由形状变更需更新 `test/server/httpapi-exercise`;本次 NodeResponse 新增了可选字段)。\n4. **[审计·测试覆盖]** 无任何测试断言 `push_exhausted` 类的端到端路径;workflow-tool.test.ts 新增节点的缩进格式异常(`}, {` 行,`workflow-tool.test.ts:189-212`)交 review-style。\n\n## 4. 既有未提交内容声明\n\n`dag-flow.txt` 的 \"Resume-first\" 整节(+35-44)**早于本会话的分诊句子**撰写,属同一未提交工作树的既有内容;按用户要求作为联合 diff 的一部分审查。其与 workflow.md 新分诊节存在**交叉引用**(\"triage per the Node failure triage section in the workflow guidance\")——两节必须作为整体审查一致性(续跑顺序、fail-closed 守卫、与 error_class 值集的吻合)。\n\n## output_variables\n- **targets**: [\n error_class 列@packages/core/src/dag/sql.ts:65,\n NodeFailed 投影@packages/core/src/dag/projector.ts:285,\n NodeRow.errorClass@packages/core/src/dag/store.ts:42,\n 迁移@packages/core/src/database/migration/20260803073521_workflow_node_error_class.ts,\n wake 摘要归因@packages/opencode/src/dag/runtime/loop.ts:852-879,\n status 输出@packages/opencode/src/tool/workflow.ts:204,\n NodeResponse.error_class@packages/opencode/src/server/routes/instance/httpapi/groups/dag.ts:42,\n handler 映射@packages/opencode/src/server/routes/instance/httpapi/handlers/dag.ts:65,\n DagNode.error_class@packages/sdk/js/src/v2/gen/types.gen.ts:3909,\n 分诊节@packages/core/src/plugin/command/workflow.md:401-427,\n Resume-first 节@packages/core/src/plugin/command/dag-flow.txt:35-44,\n trigger 枚举源@packages/schema/src/dag-event.ts:253\n]\n- **impacted_processes**: [NodeFailed 投影落库, 父会话 wake 投递(loop.ts idle/抢占守卫后的摘要构建), workflow tool status, HttpAPI dag 节点查询, SDK 生成链(openapi → types.gen)]\n- **test_anchors**: [\n packages/opencode/test/dag/dag-wake-integration.test.ts:448(exec_failed 持久化),\n packages/opencode/test/dag/dag-wake-integration.test.ts:693-731(verdict_fail + wake 文本),\n packages/opencode/test/dag/workflow-tool.test.ts:413-414(status error_class),\n packages/opencode/test/dag/fixtures.ts:18\n]\n- **arbiter_audit_items**: [push_exhausted 文档遗漏, dag.ts:492 连坐节点 error_class 语义, sdk/openapi.json 产物与 check:generated/httpapi-exercise 契约, push_exhausted 无测试覆盖 + 测试缩进]\n- **ast_available**: true(codebase-memory 图谱可用;本次以 git diff 原始 hunk + rg 交叉核验为准,未依赖图谱推断)" diff --git a/.opencode/.dag-specs/review-parts-diff/verify-suite.md b/.opencode/.dag-specs/review-parts-diff/verify-suite.md deleted file mode 100644 index f38486d23e..0000000000 --- a/.opencode/.dag-specs/review-parts-diff/verify-suite.md +++ /dev/null @@ -1 +0,0 @@ -{"verdict":"PASS","results":[{"gate":"typecheck core","command":"bun run typecheck (packages/core)","outcome":"PASS","detail":"tsgo --noEmit exited 0 with no diagnostics (only '$ tsgo --noEmit' in output)."},{"gate":"typecheck opencode","command":"bun run typecheck (packages/opencode)","outcome":"PASS","detail":"tsgo --noEmit exited 0 with no diagnostics."},{"gate":"opencode DAG suites","command":"bun test test/dag (packages/opencode)","outcome":"PASS","detail":"324 pass / 0 fail, 841 expect() calls, 26 files (incl. dag-wake-integration.test.ts and workflow-tool.test.ts which carry the new error_class assertions). Decisive fragment: '324 pass\\n 0 fail ... Ran 324 tests across 26 files'. WARN log lines during run are intentional negative-path scenarios (e.g. 'DAG wake delivery failed', 'paused workflow after recovery invented node failures') whose paired tests all passed."},{"gate":"core DAG suites","command":"bun test test/dag-core.test.ts test/dag-projector-drift.test.ts test/dag-store-summaries.test.ts test/plugin/command.test.ts (packages/core)","outcome":"PASS","detail":"101 pass / 0 fail, 493 expect() calls across 4 files. Decisive fragment: '101 pass\\n 0 fail ... Ran 101 tests across 4 files'."},{"gate":"migration check","command":"bun script/migration.ts --check (packages/core)","outcome":"PASS","detail":"Incremental check printed 'No schema changes, nothing to migrate 😴' (schema.gen/migrations in sync with drizzle schema incl. the new error_class column); full-migration generation succeeded ('[✓] Your SQL migration ➜ .../full/20260803080041_schema/migration.sql'), exit 0."},{"gate":"HttpAPI contract","command":"bun run test:httpapi --fail-on-missing (packages/opencode)","outcome":"PASS","detail":"Both runs (mode=auth and mode=effect, selected=225, effectRoutes=192) ended with 'summary pass=225 fail=0 skip=0 missing=0 extra=0'. All /dag routes incl. dag.nodes/dag.nodeDetail passed; missing=0 satisfies --fail-on-missing."},{"gate":"SDK freshness","command":"bun run build (packages/sdk/js) then git diff -- packages/sdk/js/src/v2/gen","outcome":"PASS","detail":"Regeneration succeeded (@hey-api v0.90.10, '[Job 1] ✅ Done!'). Post-build diff is exactly the intended field: types.gen.ts DagNode gains one line '+ error_class?: string' after error_reason. git diff --stat: '1 file changed, 1 insertion(+)'; git status --porcelain shows only 'M packages/sdk/js/src/v2/gen/types.gen.ts' — no other drift, no untracked gen files. Matches the stated pass interpretation."}],"anomalies":["Test-suite WARN logs in gates 3/4 are expected negative-path coverage (delivery failure, ownership-loss recovery), not defects — all associated tests passed.","packages/sdk/js/openapi.json is untracked (build artifact per commit 881ca8643, not .gitignored); it was used as the regeneration input and produced zero drift beyond the intended field — no gate impact.","Gate 7 build output lists src/v2/gen files without the '(unchanged)' marker (rewritten), but byte-level git diff confirms the sole change is the intended error_class insertion."]} diff --git a/.opencode/.dag-specs/review-parts-final/review-config-repo.md b/.opencode/.dag-specs/review-parts-final/review-config-repo.md deleted file mode 100644 index 5e73e3b6e0..0000000000 --- a/.opencode/.dag-specs/review-parts-final/review-config-repo.md +++ /dev/null @@ -1 +0,0 @@ -"## 配置仓库引用模板机制评审(feat/dag-config-repo @ 494b3b463)\n\n**评审环境说明**:评审过程中工作区被并行会话从 `feat/dag-config-repo` 切换到 `feat/goal-restore`;此后所有文件证据均通过 `git show feat/dag-config-repo:` 固定在该分支 HEAD(494b3b463)读取,未修改任何文件。跨分支比对确认 `workflows.ts / generate.ts / build.ts / command.ts / dag-template-update.txt` 在两个堆叠分支上逐字一致。\n\n```json\n{\n \"findings\": [\n {\n \"severity\": \"HIGH\",\n \"title\": \"Windows 构建的 DAG_TEMPLATES_DIR 缺少 cygpath 路径转换,与同文件 models.dev 步骤的处理不对称\",\n \"description\": \"build-cli 在 3-OS 矩阵上运行。Git Bash (windows-latest) 中 $GITHUB_WORKSPACE 会被 MSYS 转换为 POSIX 形式(/d/a/...),Extract Templates 步骤把该原始值直接写入 GITHUB_ENV。同文件上方 models.dev 步骤正是因为同样问题显式使用 cygpath -m 转回 Windows 形式后才写入 GITHUB_ENV,证明 POSIX 形式确实会到达原生消费者。原生 bun 收到 /d/... 路径后,generate.ts 的 Bun.Glob.scan({cwd}) 对不存在目录会抛 ENOENT(本地实测确认),导致 Windows release 构建失败(或退化为静默丢失 builtin 模板)。\",\n \"evidence\": \".github/workflows/release-fork.yml:170-175 vs .github/workflows/release-fork.yml:145-152; packages/opencode/script/generate.ts:51\",\n \"recommendation\": \"与 models.dev 步骤对齐:`if command -v cygpath; then DAG_TEMPLATES_DIR=$(cygpath -m \\\"$GITHUB_WORKSPACE/dag-templates-src\\\"); fi` 后再写入 GITHUB_ENV;或在 generate.ts 中对目录不存在给出带上下文的可读错误。\"\n },\n {\n \"severity\": \"MEDIUM\",\n \"title\": \"配置仓库引用完全浮动(默认分支 HEAD / refs/heads/main),无版本钉扎与完整性校验\",\n \"description\": \"package-templates 的 checkout 未指定 ref,release 时取 opencode-dag-config 默认分支 HEAD;/dag-template-update 下载 refs/heads/main 的 zip,且文案称其为 “pinned repository URL”(钉住的是仓库,不是版本)。两个消费点都无校验和/签名验证,配置仓库上的任意新提交(含误推的 WIP)会自动进入下一个 release 的二进制内嵌模板、release 资产和用户全局配置目录(与 dag.jsonc 同信任级,可被 agent 直接执行)。同一所有者的仓库降低了攻击可能性,但该设计为有意“取最新”,故评为 MEDIUM。\",\n \"evidence\": \".github/workflows/release-fork.yml:72-77(with: repository,无 ref); packages/core/src/plugin/command/dag-template-update.txt:27-31\",\n \"recommendation\": \"为 workflow_dispatch 增加可选的 config-repo ref/tag 输入(默认 main)并在 release notes 记录实际解析的 SHA;修正 dag-template-update.txt 中 “pinned” 措辞。\"\n },\n {\n \"severity\": \"MEDIUM\",\n \"title\": \"模板打包位于 release 关键路径且无重试,失败模式不自洽\",\n \"description\": \"build-cli/release 均 needs: package-templates,配置仓库 checkout 的一次瞬时故障(网络抖动、仓库改名/转私有)会阻塞整个二进制发布。同文件 models.dev 下载有 3 次重试 + 优雅降级,模板步骤没有任何重试。且语义不对称:配置仓库为空 → 仅 ::warning:: 并继续发布(零 builtin);clone 失败 → 整个发布硬失败。\",\n \"evidence\": \".github/workflows/release-fork.yml:101,220(needs); :81-89(空 glob 仅告警); :143-158(models.dev 的 3 次重试对照)\",\n \"recommendation\": \"给 Clone Config Repo 加有限重试;或 package-templates continue-on-error + 在 build-cli 中容忍缺失(退化为无 builtin),使模板故障与二进制发布解耦。\"\n },\n {\n \"severity\": \"MEDIUM\",\n \"title\": \"指导文本仍描述两级 scope,与运行时三级解析(含 builtin)漂移\",\n \"description\": \"workflows.ts 的解析序为 project > global > builtin,但 workflow.md(workflow 工具自身描述)只列两级;dag-flow.txt 明确写 “two scopes”。release 二进制在干净环境下四个推荐模板全部只能从 builtin 解析,agent 按文本去 /workflows 会看到空目录(workflow(action:\\\"list\\\") 指令可兜底)。此外 list 输出展示 builtin://name 路径,若 agent 照抄作为 spec_path,isName 因含 “/” 拒绝、扩展名检查报 “must be a .yaml or .yml file”——一个由文档诱导的可恢复陷阱。2ee59d874 只更新了运行时 hint,未更新静态文本。\",\n \"evidence\": \"packages/core/src/plugin/command/workflow.md:84-88; packages/core/src/plugin/command/dag-flow.txt:13-16; packages/opencode/src/dag/workflows.ts:9-14; packages/opencode/src/tool/workflow.ts:153-160,401-417\",\n \"recommendation\": \"workflow.md 与 dag-flow.txt 增补 builtin 第三级说明(release 内嵌、无磁盘文件、按名称启动);list 对 builtin 条目提示“仅可按名称启动”,或让 resolveSpecPath 直接接受 builtin:// 形式。\"\n },\n {\n \"severity\": \"LOW\",\n \"title\": \"builtin 层零自动化测试覆盖\",\n \"description\": \"2ee59d874 删除了旧的 change-review 规格测试后,没有任何测试替代:resolve/list 的 builtin 优先级与遮蔽、isBuiltinPath/builtinName、parseMeta 的容错路径(2ee59d874 新增的 catch)均未被测试。测试可通过 globalThis.OPENCODE_DAG_TEMPLATES 注入实现,成本低。\",\n \"evidence\": \"packages/opencode/test/dag/dag-workflows.test.ts(全文无 builtin 引用); 2ee59d874 删除段落\",\n \"recommendation\": \"补充注入式单测:builtin 被 project/global 同名遮蔽、builtin 单独命中、malformed builtin 内容在 list 时不抛错。\"\n },\n {\n \"severity\": \"LOW\",\n \"title\": \"嵌入确定性弱:模板按文件系统扫描顺序注入,且仅 *.yaml\",\n \"description\": \"loadDagTemplatesData 按 Bun.Glob.scan 的未排序顺序插入键,3-OS 矩阵产出的二进制内嵌 JSON 键序可能不同(功能无影响:按键查找、list 已排序,但字节级不确定)。另外 glob 只取 *.yaml,而 workflows.ts EXTENSIONS 同时接受 .yml——手工设置 DAG_TEMPLATES_DIR 指向含 .yml 的目录会静默丢模板;tar 打包也只复制 *.yaml,行为一致但未文档化。\",\n \"evidence\": \"packages/opencode/script/generate.ts:50-55; packages/opencode/src/dag/workflows.ts:33; .github/workflows/release-fork.yml:82-86\",\n \"recommendation\": \"对扫描结果排序后再写入 Record;在注释或文档中声明“仅根目录 *.yaml 被打包/内嵌”。\"\n },\n {\n \"severity\": \"LOW\",\n \"title\": \"/dag-template-update 锁语义存在两处含糊\",\n \"description\": \"1) 指令要求“下载或合并前”先 mkdir 锁目录,但当 /workflows 尚不存在时 mkdir 以 ENOENT 失败(非 EEXIST),agent 可能误判为“锁被占用”;创建目录的指令放在文末 Failure handling,顺序含糊。2) 崩溃遗留的陈旧锁没有 TTL/接管指引,会无限期阻塞后续更新,唯一恢复路径是用户手工删除。预览分类、QA 决策门、备份失败即中止、内容级验证等核心流程设计合理;备份文件 .bak- 后缀不会被 list() 误收(仅 .yaml/.yml),无库污染。\",\n \"evidence\": \"packages/core/src/plugin/command/dag-template-update.txt:65-74(锁),86-88(目录创建),56-62(备份)\",\n \"recommendation\": \"把“目录不存在则先创建”移到锁步骤之前;锁目录内写一个时间戳文件,锁龄超过阈值(如 30 分钟)即视为陈旧并接管。\"\n },\n {\n \"severity\": \"LOW\",\n \"title\": \"chore 提交与前一提交的设计声明存在表述张力(行为无问题)\",\n \"description\": \"98e4c0624 以 “config repo is the sole source; builtin + global scopes cover runtime” 为由删除受跟踪模板,随后 494b3b463 又向 .opencode/workflows/ 添加 8 个项目级工作流 yaml。项目 scope 容纳项目专属规格在语义上合法(project > global > builtin 已文档化),功能无冲突,但两次提交的声明读起来相互矛盾,易误导后续维护者。8 个 yaml 全部通过解析、depends_on 闭合、prompt_template.id 均存在于 .opencode/dag-prompts。\",\n \"evidence\": \"98e4c0624 commit message; 494b3b463 新增 .opencode/workflows/*.yaml(8 个文件)\",\n \"recommendation\": \"无需代码改动;如保留这批文件,在 PR 描述中说明“reference 模板归配置仓库,项目级工作流仍可在仓库内维护”的边界。\"\n }\n ],\n \"unverified_claims\": [\n \"finding 1 的最终形态依赖两点本机无法验证的事实:windows-latest Git Bash 中 $GITHUB_WORKSPACE 的实际形式,以及原生 Windows bun 对 /d/... 路径的解析行为(若 bun 恰好能解析则影响消失,但 :146-147 的 cygpath 不对称仍成立)\",\n \"CI 管道未实际执行:actions/checkout@v4 以调用方令牌拉取外部公开仓库(contents: read)的实际行为、artifact 在 job 间的传递均未实测,release-fork.yml 全部结论为静态分析\",\n \"opencode-dag-config 仓库内容不在评审范围:无法确认 design-decision-loop / parallel-development-loop / deep-review-dag-module / change-review 四个名字确实存在于该仓库根目录(dag-flow.txt 按名引用它们;若缺失,release builtin 不会提供)\",\n \"无法确认 opencode-dag-config 是否公开、默认分支是否有保护/CI 门禁(供应链缓解措施评估的前提)\",\n \"本轮为只读评审,未运行 typecheck/测试/httpapi 门禁(客观门禁由独立的 gate-runner 波次执行)\",\n \"release job 在 7 天 artifact 保留期后被单独重跑的边缘场景(dag-templates retention-days:7 vs 二进制 artifact 默认保留期)未评估\"\n ],\n \"summary\": \"机制整体结构健全:package-templates 写安全已验证(contents: read、不触碰主仓库内容、无 push),builtin 嵌入忠实镜像 OPENCODE_MODELS_DEV 模式且带 typeof 防护,三级解析与 list/resolve 的遮蔽语义一致,2ee59d874 确实闭合了其声称的全部缺陷(含“删除冗余下载”经验证安全——无名 download-artifact + merge-multiple 仍会把 dag-templates.tar.gz 带入 release 资产与 SHA256SUMS),chore 产物 16 个 yaml 全部合法、未发现密钥或本机路径泄漏。首要风险是 release-fork.yml 中 Windows 路径形式缺陷(HIGH,同文件已有现成的 cygpath 解法可照搬),其次是浮动引用无钉扎、发布关键路径无重试与静态指导文本滞后于三级解析(各 MEDIUM),这些都不阻塞合并方向但建议在发布前修复 HIGH 项。\"\n}\n```\n\n**D5 专项结论**:fix commit 2ee59d874 处理的审查发现均真实解决——ReferenceError 防护(workflows.ts:51-56)、malformed builtin 容错(parseMeta catch,workflows.ts:158-161)、重复 Entry 接口去重、not-found hint 含 builtin(workflow.ts:393-398)、空 glob cp 崩溃修复(release-fork.yml:81-89)、release job 冗余下载移除(验证安全)。唯一残留是它没触及的静态文档漂移(上文 MEDIUM finding 4)。" diff --git a/.opencode/.dag-specs/review-parts-final/review-stack.md b/.opencode/.dag-specs/review-parts-final/review-stack.md deleted file mode 100644 index 48717f2a11..0000000000 --- a/.opencode/.dag-specs/review-parts-final/review-stack.md +++ /dev/null @@ -1 +0,0 @@ -"All checks complete. Here is the stack-integrity verdict.\n\n## 1. Findings\n\n```json\n[\n {\n \"severity\": \"LOW\",\n \"title\": \"Stack base drift: 494b3b463 on feat/dag-config-repo is not an ancestor of the upper stack branches\",\n \"description\": \"The stack is not perfectly linear. feat/dag-config-repo HEAD is 494b3b463 (workflow library specs + deep-review artifacts, pushed to origin), but the merge-base of feat/dag-config-repo with both upper branches is 2ee59d874. feat/dag-error-class and feat/goal-restore were cut from the 4-commit config-repo mechanism before the artifacts commit landed, so PR #169/#170 bases point at the pre-artifact state. No content is lost (the artifacts merge via PR #171) and no conflict is possible (494b3b463 touches only .opencode/, disjoint from both bundles), but 'feat/goal-restore contains the complete stacked final state' holds only modulo the artifacts, which live solely on the config-repo branch.\",\n \"evidence\": \"git merge-base feat/dag-config-repo feat/dag-error-class = 2ee59d874; remote tip refs/heads/feat/dag-config-repo = 494b3b463; git ls-tree shows .opencode/.dag-specs/* and .opencode/workflows/* present on feat/dag-config-repo but absent on feat/dag-error-class and feat/goal-restore\",\n \"recommendation\": \"Either rebase feat/dag-error-class (and goal-restore) onto 494b3b463 for a linear stack, or explicitly enforce merge order (config-repo/PR #171 first) in the merge plan. Optionally confirm the artifacts commit is intentional PR content, not a stray local push.\"\n },\n {\n \"severity\": \"LOW\",\n \"title\": \"Review artifacts (.opencode/.dag-specs) committed to the repo\",\n \"description\": \"The 494b3b463 commit deliberately tracks ~26 deep-review session artifacts (admission briefs, manifests, gate results, final audit report) plus 8 project workflow library specs under .opencode/. The commit message states this is intentional evidence-keeping. Hygiene scan found no real secrets (only innocent prose hits: 'key token' in glossary/tr.md and a Chinese sentence containing 'token' in algo-complexity-review.yaml). Flagged only so the team can confirm these belong in the public repo's merge path.\",\n \"evidence\": \"git show --stat 494b3b463 (.opencode/.dag-specs/*, .opencode/workflows/*); secret-pattern grep over 494b3b463 -- .opencode/ produced only glossary/prose matches\",\n \"recommendation\": \"Confirm intent; if these are evidence only, consider a private branch or .gitignore'd location instead of the merge path.\"\n }\n]\n```\n\nNo wrong-layer files, no reverts, no dangling imports, no wip leftovers, no secrets — all four verification axes pass (details in summary).\n\n## 2. Unverified claims\n\n- Objective gates (typecheck, test suites, httpapi, migration `--check`, SDK regen determinism) at `feat/goal-restore` HEAD were not re-executed in this session; per prior adjudications they pass, and this review scope was branch topology, not execution.\n- GitHub PR #169/#170/#171 base/target settings (that the diff each PR will show equals the branch-vs-branch diffs computed here) not confirmed via `gh` — verified against local refs whose tips match origin exactly (`4e8eb7e68`, `ca085b0d9`, `494b3b463` all confirmed on origin).\n- Whether merge order will actually be enforced (config-repo/PR #171 before the upper two) — no evidence; depends on the merge plan.\n- `packages/sdk/js/.gitignore` (`openapi.json`) newly added in the error_class bundle is presumed intentional (SDK regen artifact hygiene), not a stray file.\n\n## 3. Summary\n\nStack attribution is clean: diff(`2ee59d874`→`4e8eb7e68`) is exactly the 23-file error_class bundle with zero `goal` references and no goal module in its tree; diff(`4e8eb7e68`→`ca085b0d9`) is exactly the 47-file goal bundle with purely additive edits to the six shared files. Generated artifacts layer correctly at each branch — `migration.gen.ts` lists only `20260803073521_workflow_node_error_class` on the error-class branch and adds `20260803083938_restore_goal_state` on goal-restore; schema baseline, `types.gen.ts`, `sdk.gen.ts` (goal-only, correct since error_class is a field not a route), and `schema.json` follow the same ordering. The wip snapshot (`1b53e9e9c`) decomposes exactly into the stack: its only delta against `feat/goal-restore` is the 34 `.opencode/` artifact files, all carried by `494b3b463` on the config-repo branch — nothing leaked and nothing is missing. The single actionable note is the LOW stack-base drift (upper branches cut before the artifacts commit), which is risk-free content-wise but must be reflected in merge order." diff --git a/.opencode/.dag-specs/review-parts-final/verify-suite.md b/.opencode/.dag-specs/review-parts-final/verify-suite.md deleted file mode 100644 index 04cfa6fa17..0000000000 --- a/.opencode/.dag-specs/review-parts-final/verify-suite.md +++ /dev/null @@ -1 +0,0 @@ -{"verdict":"PASS","results":[{"gate":"1. typecheck core","command":"bun run typecheck (packages/core)","outcome":"PASS","detail":"tsgo --noEmit exit 0, no errors"},{"gate":"2. typecheck opencode","command":"bun run typecheck (packages/opencode)","outcome":"PASS","detail":"tsgo --noEmit exit 0, no errors"},{"gate":"3. typecheck tui","command":"bun run typecheck (packages/tui)","outcome":"PASS","detail":"tsgo --noEmit exit 0, no errors"},{"gate":"4. opencode suites","command":"bun test test/dag test/goal test/tool/goal-tool.test.ts test/session/prompt.test.ts (packages/opencode)","outcome":"PASS","detail":"468 pass, 1 skip, 0 fail across 33 files (1298 expect() calls, 48.00s); skip is pre-existing 'v2 projector disabled'"},{"gate":"5. core suites","command":"bun test test/dag-core.test.ts test/dag-projector-drift.test.ts test/dag-store-summaries.test.ts test/plugin/command.test.ts (packages/core)","outcome":"PASS","detail":"101 pass, 0 fail across 4 files (493 expect() calls)"},{"gate":"6. tui sync suites","command":"bun test test/cli/cmd/tui/sync-goal.test.tsx test/cli/cmd/tui/sync-dag.test.tsx (packages/tui)","outcome":"PASS","detail":"8 pass, 0 fail across 2 files incl. goal.updated/cleared slice assertions"},{"gate":"7. migration check","command":"bun script/migration.ts --check (packages/core)","outcome":"PASS","detail":"incremental-vs-full schema comparison clean: 'No schema changes, nothing to migrate'; full migration regenerated in temp dir; exit 0"},{"gate":"8. HttpAPI contract","command":"bun run test:httpapi --fail-on-missing (packages/opencode)","outcome":"PASS","detail":"summary pass=227 fail=0 skip=0 missing=0 extra=0; exit 0; includes dag.* and session.goal scenarios"},{"gate":"9. SDK regen determinism","command":"bun run build then git diff -- packages/sdk/js/src/v2/gen (packages/sdk/js)","outcome":"PASS","detail":"build exit 0 (sdk.gen.ts/types.gen.ts regenerated); git diff and git status on packages/sdk/js/src/v2/gen both empty — committed gen files exactly reproduced"}],"anomalies":["Repo was found on feat/dag-config-repo, not feat/goal-restore as the brief assumes; checked out local feat/goal-restore (ca085b0d9, in sync with origin) to run all gates, then restored feat/dag-config-repo; only pre-existing untracked file .opencode/.dag-specs/final-confirmation-three-pr-stack.yaml remained","Gate 4 has 1 skipped test ('prompt emits v2 prompted and synthetic events (v2 projector disabled)') — pre-existing deliberate skip, not a failure","Migration check wrote generated SQL only to a temp dir (/var/folders/.../opencode-core-migration-check-*); no repo files modified"]} diff --git a/.opencode/.dag-specs/review-parts-round3/final-audit-report.md b/.opencode/.dag-specs/review-parts-round3/final-audit-report.md deleted file mode 100644 index 548164e8e8..0000000000 --- a/.opencode/.dag-specs/review-parts-round3/final-audit-report.md +++ /dev/null @@ -1,73 +0,0 @@ -# Final Audit Report — Joint Uncommitted Diff (Round 3) - -- **Verdict**: PASS -- **Bounded loop**: round 3 of max 2 replans — goal met, no further loop warranted -- **Scope**: uncommitted working-tree diff (DAG `error_class` exposure, `/goal` restoration, two remediation waves; ~40 modified files) -- **Objective gates**: `.opencode/.dag-specs/review-parts-round3/verify-suite.md` — verdict PASS, 8/8 gates PASS (read directly from disk; primary evidence) -- **Closure review**: review-final-2 (fresh context, 30-min budget) over working tree + gate result file - -## 1. Closure table — round-2 HIGH findings - -All five round-2 HIGH findings are CLOSED with file:line fix evidence. - -| # | Round-2 HIGH finding | Status | Fix evidence (file:line) | -|---|---|---|---| -| F1 | workflow.md "Cascade detection": required-failure shape must state dependents are terminalized to `skipped` with error_reason `workflow_failed` (pending only while paused) | CLOSED | `packages/core/src/plugin/command/workflow.md:419` matches runtime `packages/opencode/src/dag/dag.ts:482-504,523-527` and `packages/opencode/src/dag/loop.ts:271-273`; paused parenthetical accurate per `packages/core/src/dag/core/types.ts:204-205`, `dag.ts:290-297`, `loop.ts:638` | -| F2 | workflow.md exec_failed row (c): must gate on `error_reason`, not rely on surfaced workflow-level reason or universal primary-node attribution; must carry an `orchestrator_unresponsive` recipe | CLOSED | `workflow.md:413` row (c) gates on `error_reason`; zero-attribution recipe at `workflow.md:424-428`; matches `loop.ts:879-883,852-869,272,830` and `packages/opencode/src/dag/scheduling.ts:215-217` | -| F3 | dag-flow.txt: error_class sentence must carry replan-cancel + pre-migration exceptions | CLOSED | `packages/core/src/plugin/command/dag-flow.txt:37` carries both exceptions; consistent with `packages/core/src/dag/projector.ts:320-323` | -| F4 | prompt.test.ts: meaningful coverage of `/goal set+kick`, `/goal status`, `/subgoal`, Goal-absent fall-through (assertion strength, not just existence) | CLOSED | `packages/opencode/test/session/prompt.test.ts:2286-2378` — set+kick asserts loop result/echo/persisted state/exactly-1 LLM call; status and subgoal assert rendered/persisted state + 0 LLM calls; fall-through asserts negative marker only (recorded as residual R2 below) | -| F5 | system.ts: Goal.defaultLayer provided + Goal.node in LayerNode deps (goal block reachable); no import-cycle hazard vs deferred SettingsHook pattern | CLOSED | `packages/opencode/src/session/system.ts:181,187` — Goal.defaultLayer provided, Goal.node in node deps; production reachability via `packages/opencode/src/effect/app-runtime.ts:100` → `packages/opencode/src/session/prompt.ts:2101` and `prompt.ts:2256`; lazy `serviceOption` resolution at `system.ts:69` with no import cycle | - -Regression spot-checks (all pass): - -- error_class pipeline intact: projector → store → tool status → wake digest → httpapi NodeResponse → SDK -- app-runtime provideMerge comment accurate vs `packages/opencode/src/dag/loop.ts:373-380` and `packages/opencode/src/hook/settings.ts:2174-2176` self-provides -- GOAL command description includes `done`; dispatch handles `done` at `packages/opencode/src/command/index.ts:98` and `packages/opencode/src/goal.ts:584-590` - -## 2. Gate results (objective evidence, persisted on disk) - -Source: `.opencode/.dag-specs/review-parts-round3/verify-suite.md` — `{"verdict":"PASS"}`, 8/8 PASS. - -| Gate | Command | Outcome | Detail | -|---|---|---|---| -| 1. typecheck core | `bun run typecheck` (packages/core) | PASS | tsgo --noEmit exit 0, no diagnostics | -| 2. typecheck opencode | `bun run typecheck` (packages/opencode) | PASS | tsgo --noEmit exit 0, no diagnostics | -| 3. DAG suites | `bun test test/dag` (packages/opencode) | PASS | 324 pass / 0 fail, 845 expect() calls across 26 files | -| 4. goal + dispatch suites | `bun test test/goal test/tool/goal-tool.test.ts test/session/prompt.test.ts` | PASS | 143 pass / 0 fail, 1 pre-existing marked skip across 7 files | -| 5. core suites | `bun test` (4 core test files) | PASS | 101 pass / 0 fail, 493 expect() calls across 4 files | -| 6. migration check | `bun script/migration.ts --check` | PASS | EXIT=0; "No schema changes, nothing to migrate" | -| 7. HttpAPI contract | `bun run test:httpapi --fail-on-missing` | PASS | pass=226 fail=0 skip=0 missing=0 extra=0, EXIT=0 | -| 8. SDK freshness | `bun run build` (packages/sdk/js) + git diff gen | PASS | EXIT=0; pure additive gen diff (69+/0-) matching intended set exactly | - -Gate anomalies (recorded, non-failures): - -- Gate 4: 1 pre-existing marked skip ("v2 projector disabled"), not a failure — accepted residual R5 -- Gate 8: gen diff is entirely uncommitted working-tree additions, consistent with the stated interpretation — accepted residual R6 - -## 3. Confirmed residual items (accepted, non-blocking) - -Newly confirmed observations from the closure review — all LOW, none loop-worthy: - -| # | Severity | Status | Item | Evidence | Disposition | -|---|---|---|---|---|---| -| R1 | LOW | CONFIRMED | Cascade doc lists only pending/queued; `terminateNonTerminalNodes` terminalizes all non-terminal rows (incl. node-level paused). Non-exhaustive, not wrong. Optional doc polish. | `workflow.md:419`; `dag.ts:485` | Accepted, non-blocking | -| R2 | LOW | CONFIRMED | Goal-absent fall-through test asserts only absence of "目标已设定"; a silent no-op would also pass. Optional: assert a positive fall-through outcome later. | `prompt.test.ts:2360-2378` | Accepted, non-blocking | -| R3 | LOW | PARTIALLY_CONFIRMED | Paused-workflow required-failure closure verified by code-path reasoning, not scenario execution. Guard logic explicit and simple; reasoning sound. Optional: add an executed scenario in a follow-up. | `dag.ts:290-297`; `loop.ts:638` | Accepted as verified-closed, non-blocking | - -Documented deferred follow-ups — explicitly accepted as non-blocking residual items: - -| # | Item | Note | -|---|---|---| -| R4 | Deferred SettingsHook wiring pattern | goal.ts mirrors it via `serviceOption`; intentionally deferred | -| R5 | Gate-4 pre-existing marked skip | v2 projector disabled; pre-existing, unrelated to this diff | -| R6 | Gate-8 SDK gen diff uncommitted | intentionally uncommitted working-tree additions matching the intended set | -| R7 | GET /session/:id/goal 200-null vs SDK Goal typing | documented deferred follow-up | -| R8 | httpapi error_class field-level fixture | documented deferred follow-up | -| R9 | TUI sync reducer tests | documented deferred follow-up | -| R10 | GoalLoop e2e fixed sleeps | documented deferred follow-up | - -## 4. PASS reason - -All five round-2 HIGH findings are CLOSED with file:line evidence from review-final-2's closure verification, and the objective gate suite is persisted as 8/8 PASS (verdict PASS, verified by direct read of the gate result file — primary evidence, not hearsay). Only two new LOW-severity observations emerged (plus one transparency record); no new CRITICAL/HIGH on verified evidence. Documented deferred follow-ups remain explicitly non-blocking residual items. Bounded loop round 3 of max 2 replans: goal met, no loop warranted. - -**Next action**: finalize. No remediation wave required. diff --git a/.opencode/.dag-specs/review-parts-round3/verify-suite.md b/.opencode/.dag-specs/review-parts-round3/verify-suite.md deleted file mode 100644 index 4f6721b996..0000000000 --- a/.opencode/.dag-specs/review-parts-round3/verify-suite.md +++ /dev/null @@ -1 +0,0 @@ -{"verdict":"PASS","results":[{"gate":"1. typecheck core","command":"bun run typecheck (packages/core)","outcome":"PASS","detail":"tsgo --noEmit exited 0, no diagnostics"},{"gate":"2. typecheck opencode","command":"bun run typecheck (packages/opencode)","outcome":"PASS","detail":"tsgo --noEmit exited 0, no diagnostics"},{"gate":"3. opencode DAG suites","command":"bun test test/dag (packages/opencode)","outcome":"PASS","detail":"324 pass, 0 fail, 845 expect() calls across 26 files"},{"gate":"4. goal + dispatch suites","command":"bun test test/goal test/tool/goal-tool.test.ts test/session/prompt.test.ts (packages/opencode)","outcome":"PASS","detail":"143 pass, 0 fail, 1 skip across 7 files; skip is pre-existing marked test 'prompt emits v2 prompted and synthetic events (v2 projector disabled)'"},{"gate":"5. core suites","command":"bun test test/dag-core.test.ts test/dag-projector-drift.test.ts test/dag-store-summaries.test.ts test/plugin/command.test.ts (packages/core)","outcome":"PASS","detail":"101 pass, 0 fail, 493 expect() calls across 4 files"},{"gate":"6. migration check","command":"bun script/migration.ts --check (packages/core)","outcome":"PASS","detail":"EXIT=0; incremental: 'No schema changes, nothing to migrate'; full migration.sql generated cleanly"},{"gate":"7. HttpAPI contract","command":"bun run test:httpapi --fail-on-missing (packages/opencode)","outcome":"PASS","detail":"summary pass=226 fail=0 skip=0 missing=0 extra=0, EXIT=0"},{"gate":"8. SDK freshness","command":"bun run build (packages/sdk/js) + git diff -- packages/sdk/js/src/v2/gen","outcome":"PASS","detail":"Build EXIT=0. Diff is pure additions (69 insertions, 0 deletions, only sdk.gen.ts + types.gen.ts) and matches the intended set exactly: SessionGoalData/Errors/Responses types referencing pre-existing committed 'Goal' type, Session2.goal() method for GET /session/{sessionID}/goal, and DagNode.error_class?: string. No other drift."}],"anomalies":["Gate 4 contains 1 pre-existing marked skip (v2 projector disabled), not a failure","Gate 8 gen diff is entirely uncommitted working-tree additions, consistent with the stated interpretation (Goal route types + session.goal + error_class intentionally uncommitted)"]} diff --git a/.opencode/.dag-specs/review-parts/explore-build.md b/.opencode/.dag-specs/review-parts/explore-build.md deleted file mode 100644 index f4d5c582f9..0000000000 --- a/.opencode/.dag-specs/review-parts/explore-build.md +++ /dev/null @@ -1,110 +0,0 @@ -探索完成。所有目标文件已读取,关键声明已对运行时源码逐条核验。以下为探索报告。 - ---- - -## Hit Summary - -PR #167 的构建注入链、三级 scope 解析、命令注册、release 流水线在代码层面自洽:`typeof` 守卫(dev 无 define 时不抛 ReferenceError)、`seen` 去重、空 glob 均修复正确,prompt 的配置目录解析顺序与运行时(`flag.ts:63-64` + `global.ts:3,13` + xdg-basedir)**逐字一致**。主要问题集中在:**dag-flow.txt 描述"two scopes"与运行时三级不符**、**builtin 新功能零测试覆盖**、**README 死链/过时声明**、**prompt 锁语义两个缺口(父目录前置条件、陈旧锁无恢复)**。置信度:高(静态证据充分;仅 node 构建分发路径与 Windows xdg 行为留待 verify 波确认)。 - ---- - -## Key Symbols - -- `packages/opencode/script/generate.ts:44` `loadDagTemplatesData()` — 读 `DAG_TEMPLATES_DIR` env,`Bun.Glob("*.yaml")` 根级扫描,`file.replace(/\.ya?ml$/, "")` 取名字,`JSON.stringify(templates)` 输出(unset 时返回字符串 `"undefined"`);与 `loadModelsData()`(generate.ts:12-33)同构:同是"env 快照 → 字符串(JSON 或 `"undefined"`)→ 导出 → define 注入"模式 -- `packages/opencode/script/generate.ts:59` `dagTemplatesData` — 模块顶层 await 导出;`build.ts:16` `await import("./generate.ts")`,`build.ts:203` 注入 `OPENCODE_DAG_TEMPLATES: generated.dagTemplatesData`(裸文本替换:JSON 字面量或 `undefined` 关键字,均合法表达式) -- `packages/opencode/src/dag/workflows.ts:30` `declare const OPENCODE_DAG_TEMPLATES: Record | undefined` — 与 `packages/core/src/models-dev.ts:114,185-187` 的 `OPENCODE_MODELS_DEV` 守卫模式完全一致 -- `packages/opencode/src/dag/workflows.ts:51` `builtinTemplates()` — `typeof` 守卫(未绑定标识符在 typeof 下安全,不抛 ReferenceError — 修复正确) -- `packages/opencode/src/dag/workflows.ts:77` `resolve()` / `:102` `list()` / `:135` `builtinEntry()` / `:126-133` `isBuiltinPath`/`builtinName`(`BUILTIN_PREFIX = "builtin://"` 在 :58) -- `packages/opencode/src/tool/workflow.ts:359-370` — `readWorkflowSpec` 的 builtin 分支(`isBuiltinPath` → 查 map → `Bun.YAML.parse(content)`) -- `packages/opencode/src/tool/workflow.ts:395-399` `searchedScopes()` — 空库/未找到提示附加 "the release's builtin templates" -- `packages/core/src/plugin/command.ts:41-45` — `draft.update("dag-template-update", ...)` 注册(与 dag-flow 的 :37-40 同模式) -- `.github/workflows/release-fork.yml:66-97` `package-templates` job、`:163-175` 下载/解压/`DAG_TEMPLATES_DIR` 环境注入 - ---- - -## Call Relationships - -- **构建期**:`release-fork.yml:80-89`(打包 `dag-config/*.yaml`)→ `DAG_TEMPLATES_DIR` env(:175)→ `generate.ts:44` → `build.ts:16,203` define → 二进制常量 → `workflows.ts:51 builtinTemplates()` → `resolve()`(:86-88 兜底层)/ `list()`(:117-120)/ `tool/workflow.ts:360-364` -- **运行时名字解析**:`workflow(action:start)` → `tool/workflow.ts:408 resolveSpecPath`(isName 分支)→ `workflows.ts:77 resolve`(project → global → builtin)→ 返回 `builtin://name` 路径 → `:360` builtin 分支解析内容 -- **命令面**:`command.ts:41-45` 注册 `/dag-template-update`(draft.update + `.txt` import,:11);`/dag-flow` 同模式(:37-40);`command/index.ts:51` 有 `Default.DAG_FLOW` 常量但**无** `DAG_TEMPLATE_UPDATE` 条目(是否必需 → unverified) -- **release 流水线**:`package-templates`(仅 workflow_dispatch,:68)→ `build-cli`(needs :101 + 自身 `if: workflow_dispatch` :105)→ `release`(needs 两者 + `create_release` 门,:220-221);push 触发时三者全跳过、仅 `register` 跑 — **needs 链不因 push 中断** ✓ - ---- - -## 1. 完整构建期数据路径 - -`release-fork.yml:163-175`(download-artifact `dag-templates` → `tar -xzf` → `DAG_TEMPLATES_DIR=$GITHUB_WORKSPACE/dag-templates-src` 写入 GITHUB_ENV)→ `generate.ts:45` 读 env → `:51` `Bun.Glob("*.yaml").scan({cwd})`(仅根级,非递归)→ `:52` 名字去扩展名 → `:53` `Bun.file().text()` → `:56` `JSON.stringify(templates)`(值为 JSON 文本,esbuild define 裸粘贴为对象字面量;模板内引号/反斜杠由 stringify 正确转义,无注入面)→ `:59` 导出 → `build.ts:203` define → 编译进单文件二进制 → 运行时 `workflows.ts:51-56` 守卫后按名取用。**注**:`build-node.ts:23` 与 `packages/cli/script/build.ts:92` 只注入 `OPENCODE_MODELS_DEV`、未注入 `OPENCODE_DAG_TEMPLATES` → node 目标构建无 builtin(守卫优雅降级为两级);"air-gapped installs ship the curated templates"(workflows.ts:12-14)仅对 bun 单文件构建成立(UNVERIFIED:node 构建是否属用户分发路径)。 - -## 2. dev 下 DAG_TEMPLATES_DIR unset 的行为 - -- `generate.ts:46-49`:打日志并返回字符串 `"undefined"` → define 注入裸 `undefined` 关键字 → `workflows.ts:54` `typeof ... === "undefined"` → 返回 `{}`。 -- 源码 dev 运行(bun dev 无 define):标识符未绑定,`typeof` 对未声明标识符不抛错 → 同路径返回 `{}`。**ReferenceError 守卫修复正确**(typeof 是唯一安全探测方式)。 -- 后果链:`resolve` 返回 undefined("not found"提示不含 builtin)→ `list` 空 → `searchedScopes`(tool/workflow.ts:397)不附加 builtin 提及。**全部优雅降级** ✓ - -## 3. 命令注册模式对比 - -`command.ts:41-45`:`draft.update("dag-template-update", (command) => { command.template = DAG_TEMPLATE_UPDATE_PROMPT; command.description = DagTemplateUpdateDescription })` — 与 dag-flow(:37-40)逐行同构(import 于 :11)。唯一不对称:`command/index.ts:51` 的 `Default` 枚举只有 `DAG_FLOW`,无 `dag-template-update`(该枚举用途未确认 — unverified;若 TUI/命令面依赖它,新命令可能不完整)。 - -## 4. dag-template-update.txt 逐节语义(M1/M2/M3) - -- **配置目录(L16-24)**:`OPENCODE_CONFIG_DIR` env → xdg 平台目录(XDG_CONFIG_HOME,兜底 `~/.config/opencode`)。**与运行时逐字一致**:`flag.ts:63-64`(读 env)+ `global.ts:3,13`(xdg-basedir;macOS 默认 `~/.config/opencode`,非 Library/Application Support)✓ 已验证 -- **下载(L26-37)**:固定 `codeload.github.com/LeXwDeX/opencode-dag-config/zip/refs/heads/main` — 未 pin tag/commit(与 release clone 同为 HEAD,可复现性弱,LOW) -- **干跑分类(L39-49)**:NEW/UNCHANGED/UPDATE + local-only 保留 ✓ -- **合并 QA(L51-61)**:无 UPDATE 直合;有 UPDATE 时三选项(全覆盖先备份/全跳过/逐文件)+ 拒绝只加 NEW ✓ -- **备份(L62-65)**:`.yaml.bak-` 放原文件旁;备份失败 → **中止该文件覆盖并报告,绝不无备份覆盖** ✓ **M2 满足**。备份文件 extname 为 `.bak-*`,被 `list()` 的 EXTENSIONS 过滤(workflows.ts:109-110)不会污染库列表 ✓ -- **锁(L67-78)**:`mkdir .dag-update.lock` 原子判定(L72-74)、短暂等待重试数次(L75-76)、合并结束含失败时 `rmdir`(L77-78)✓ **M1 满足(存在+重试+清理)**。锁目录被 `list()` 的 `isFile()` 过滤(:108)✓ - - **缺口 A(M2/M3)**:锁在"下载前"创建(L70),但 `/workflows` 不存在时 `mkdir` 失败是 **ENOENT 而非 EEXIST**;提示词只在 L97(Failure handling)说"applying 前创建目录" — 锁步骤的父目录前置条件缺失,顺序歧义,agent 可能误判 ENOENT - - **缺口 B(M2/M3)**:陈旧锁无恢复路径 — agent 崩溃/被杀后 `.dag-update.lock` 永久残留,后续更新永远停在"another update is already running";无 mtime/age 检测、无强制覆盖或提示手工清除 -- **验证(L80-90)**:重读每个更新文件与归档副本**逐内容比对**(L85-86)+ `workflow(action:list)` 计数与变更名单(L87-89)+ 报告备份位置 ✓ **M3 满足(内容比较,非仅列表)**;并正确提示项目级 shadow 影响列表可见性 - - **缺口 C(LOW)**:内容比对不一致时无恢复动作(未提回滚备份/重试) -- **失败处理(L92-97)**:下载失败 verbatim 报告并停(L94-95)✓;解压失败报告并停(L96)✓;合并中途复制失败未明确覆盖(LOW);锁清理覆盖"包括失败" ✓ - -## 5. dag-flow.txt 与运行时不一致(确认存在) - -- **dag-flow.txt:13**:"Reference templates are installed in **two scopes** (project overrides global...)" — 运行时是**三级**(project → global → builtin,workflows.ts:9-14)。builtin 层在 dag-flow.txt 中**完全缺失**;对开箱即用(未跑 update、无 config repo)用户,curated 模板恰恰只存在于 builtin 层,提示词描述的 global 层是空的 -- **dag-flow.txt:14**:"global ... curated by the `opencode-dag-config` repo" — 只有跑过 `/dag-template-update` 才成立;curated 快照的实际载体是二进制内置层 -- **dag-flow.txt:17-20**:指名 4 个 saved workflow(design-decision-loop 等)— 这些是本次**删除**的提交模板(`git diff` 872 行删除);dev checkout 无 config repo 时这些名字解析不到,agent 只能走 :21 的 "no close match" 兜底。提示词假设模板常驻,与实际可用性脱节 -- **dag-flow.txt:16** "pick by name or path" — `list()` 对 builtin 项展示的路径是 `builtin://`(workflows.ts:58,131-133),该路径**不能**作为 spec_path 回传:`resolveSpecPath` 走 path 分支后在 extname 检查失败(tool/workflow.ts:416-418,报 "must be a .yaml or .yml file")— 误导性报错。应只按名选 - -## 6. 边界情况 - -- **空模板目录**:Glob 零匹配 → `{}` → `"{}"` → define 注入 `{}` → `typeof {}` 为 "object" → `builtinTemplates()` 返回 `{}` → 无 builtin 项 ✓(release 空 tar 时 `release-fork.yml:87` 打 warning,同样优雅) -- **非 yaml 文件**:`generate.ts:51` glob 过滤 ✓;release 打包 `dag-config/*.yaml`(release-fork.yml:83)✓;update prompt 指示取 `*.yaml` ✓ — 三层一致 -- **名称冲突**:`resolve` 顺序 project→global→builtin(:79-89)+ `list` seen-map(:104-120)优先级一致,listing 不会广告 resolve 选不中的项 ✓;`dag-template-update.txt:101-103` 正确提示 shadow -- **`.yml` 漂移(LOW)**:运行时 `EXTENSIONS` 含 `.yml`(workflows.ts:33),但 generate.ts glob、release 打包、update prompt 全只认 `.yaml` → config repo 若未来放 `.yml` 模板,对 binary 和打包不可见 -- **`generate.ts:52` 的 `\.ya?ml$` 中 `?` 是死代码**(glob 只匹配 `.yaml`)— 风格 nit - -## 其他发现(供 review/verify 波) - -1. **零新增测试(MEDIUM)**:`dag-workflows.test.ts` 无任何 builtin 测试(仅 project/global fixture);`workflow-tool.test.ts` 无 builtin 引用(:1039 只有 global scope env 重定向)。新功能面(三级优先级、builtinEntry、parseMeta 重构、typeof 守卫、searchedScopes)全部无测试。被删的 "repository's own workflow library" 测试是唯一真实 spec 校验 -2. **README 死链/过时(MEDIUM, docs)**:`README.md:253` 引用已删除的 `./.opencode/workflows/change-review.yaml`;`README.zh.md:27` "仓库已经附带三类强约束参考图"(已不成立)、`:66-67` 两 scope 表缺 builtin 层 -3. **`Entry.content` 死字段(LOW)**:`builtinEntry`(workflows.ts:135-137)设置,但 `readWorkflowSpec` 重新查 `builtinTemplates()`(tool/workflow.ts:360-364),全仓库无消费方 -4. **release-fork.yml**:job 图静态分析通过(push 触发不断链、`download-artifact` merge-multiple 将 `dag-templates.tar.gz` 带入 release 资产 — 与头部注释 :24-25 一致,属意图);config repo 未 pin tag(LOW);`tar -xzf` 在 windows-latest runner 可用(bsdtar) -5. **已修复项确认**:ReferenceError 守卫 ✓(typeof 语义安全)、Entry 去重 ✓(seen-map)、M4 空 glob ✓(Array.fromAsync 空集安全)、重复下载删除 ✓(最终 diff 中 build-cli 仅一个下载 step) - -## Related Test Files - -- `packages/opencode/test/dag/dag-workflows.test.ts` — resolve/list 仅 project/global;**builtin 无覆盖**(移除的 library 测试是唯一真实 spec 校验) -- `packages/opencode/test/dag/workflow-tool.test.ts:1039` — global scope 经 OPENCODE_CONFIG_DIR 重定向的集成测试(可作为 builtin 测试锚点) -- `packages/opencode/test/dag/dag-config.test.ts` — config dir env 重定向模式参考 - -## output_variables - -- targets: - - `loadDagTemplatesData@packages/opencode/script/generate.ts:44` - - `dagTemplatesData@packages/opencode/script/generate.ts:59` - - `builtinTemplates@packages/opencode/src/dag/workflows.ts:51` - - `resolve@packages/opencode/src/dag/workflows.ts:77` - - `list@packages/opencode/src/dag/workflows.ts:102` - - `builtinEntry@packages/opencode/src/dag/workflows.ts:135` - - `readWorkflowSpec@packages/opencode/src/tool/workflow.ts:350` - - `searchedScopes@packages/opencode/src/tool/workflow.ts:395` - - `command.ts:41` `/dag-template-update` 注册 - - `dag-template-update.txt`(L16-103 全文件) - - `dag-flow.txt:13-21` - - `release-fork.yml:66-97,163-175` -- impacted_processes: [WorkflowLibrary 名字解析(resolve/list/start)、/dag-template-update 命令、release 流水线模板打包与注入] -- test_anchors: [dag-workflows.test.ts, workflow-tool.test.ts:1039, dag-config.test.ts] -- ast_available: true(codebase-memory-mcp 已索引本 repo,head 2ee59d874;本次探索以 git diff 为准,未依赖图查询) - -**留给 verify 波的 unverified_claims**:① `build-node.ts`(target: node, src/node.ts)与 `packages/cli/script/build.ts` 产物是否为用户分发路径(决定缺 define 的严重性);② `command/index.ts:51` Default 枚举是否需要 dag-template-update 条目;③ xdg-basedir 在 Windows 的实际回退路径(prompt 仅声明 macOS/Linux);④ 本 repo 8 个未跟踪模板与 builtin 名是否冲突(本地 shadow);⑤ `bun test dag-workflows/workflow-tool` 实测通过性。 \ No newline at end of file diff --git a/.opencode/.dag-specs/review-parts/explore-ci.md b/.opencode/.dag-specs/review-parts/explore-ci.md deleted file mode 100644 index e440defda7..0000000000 --- a/.opencode/.dag-specs/review-parts/explore-ci.md +++ /dev/null @@ -1,117 +0,0 @@ -All exploration complete. Here's the report. - ---- - -## Hit Summary - -PR #167 的 4 个 commit(676e0463e → 2ee59d874)将 DAG 参考模板从仓库内 `.opencode/workflows/` 抽离到独立配置仓库,新增 builtin 三级 scope(project > global > builtin)编译进二进制,新增 `/dag-template-update` 命令,并扩展 release 流水线打包模板资产。release-fork.yml 的 job 图、env 注入链、空 glob 守卫均已静态验证;builtin scope 在测试环境中**完全无覆盖**(无注入钩子),Windows 构建的 DAG_TEMPLATES_DIR 路径转换存在未决风险点。信心:高(源码侧),中(Windows runner 行为需 verify 波确认)。 - ---- - -## 1. Job Graph(release-fork.yml) - -``` -push(main/dev, paths=release-fork.yml) ──► register (if: push, no-op) -workflow_dispatch ──► package-templates (if: workflow_dispatch, ubuntu, contents: read) - │ - ├──needs──► build-cli (if: workflow_dispatch, 3-OS matrix ×3) - │ └─ 每步再叠加 if: platforms=='' || contains(platforms, matrix.name) - └──needs──► release (if: inputs.create_release) - └─ needs: [build-cli, package-templates] -``` - -- **if 条件**:`release-fork.yml:68` package-templates、`:105` build-cli 均门控 `github.event_name == 'workflow_dispatch'`(push 注册模式下全 skip,skip→needs 视为 success,不会级联失败);`:221` release 门控 `inputs.create_release`(push 时 inputs 为空 → falsy → skip)。`:271` register 门控 `push`。 -- **平台过滤在 step 级**(`:123/129/143/164/171/178/187/191/210` 每步重复 `if: inputs.platforms == '' || contains(...)`),job 级无法引用 matrix —— 注释已说明(`:102-104`)。selected-platforms 时被过滤的 matrix job 仍会跑但所有 step skip,不上传 artifact。 - -## 2. Env 注入链(验证通过) - -``` -package-templates: dist/*.yaml → tar -czf dag-templates.tar.gz → upload-artifact name=dag-templates (retention 7d) - build-cli (needs package-templates): - step "Download Templates Artifact" (:163-168) → dag-templates-artifact/dag-templates.tar.gz - step "Extract Templates" (:170-175): tar -xzf -C dag-templates-src - → echo "DAG_TEMPLATES_DIR=$GITHUB_WORKSPACE/dag-templates-src" >> $GITHUB_ENV - step "Build CLI" (:177-182): env 块只有 OPENCODE_CHANNEL/OPENCODE_VERSION - → DAG_TEMPLATES_DIR 经 GITHUB_ENV 自动继承,无需显式 env: ✓ -``` - -build.ts:16 `await import("./generate.ts")` → generate.ts:44-60 读 `process.env.DAG_TEMPLATES_DIR` → `Bun.Glob("*.yaml").scan` → `JSON.stringify(templates)` → build.ts:203 `define: OPENCODE_DAG_TEMPLATES: generated.dagTemplatesData`。`DAG_TEMPLATES_DIR` 未设置时 generate.ts 返回字符串 `"undefined"`(generate.ts:52),经 esbuild/Bun define 原样注入为 `undefined` 关键字 → `typeof` 守卫命中 → 空 map。此 round-trip 与 `OPENCODE_MODELS_DEV`(generate.ts:33,models-dev.ts:185-187 同款 typeof+Object.keys 守卫)完全同构,是已验证的生产模式 ✓。 - -## 3. Empty-Glob 行为(M4 修复确认正确) - -- `release-fork.yml:82-90`:`shopt -s nullglob` + 数组长度守卫,空 glob → `::warning::No templates found...` + **不执行 cp** → `tar -czf` 打空目录(`mkdir -p dist` 先于判断,tar 空目录合法)→ **warning + 空 archive,不失败** ✓。修复前的 bug 版本(2ee59d874 之前)是 `cp "${files[@]}" dist/` 无条件执行,空数组时 `cp dist/` 报 missing operand 使 step 失败。 -- 空 archive → generate.ts glob 0 个文件 → `{}` → builtin scope 为空,二进制静默降级为无 builtin ✓。 - -## 4. Release 资产流 + --target 语义 - -- release job(`:227-231`)`download-artifact@v4 + merge-multiple: true` 拉取**本 run 全部 4 个 artifact**(opencode-{linux,macos,windows} + dag-templates),无文件名冲突。 -- `SHA256SUMS`(`:243-248`)glob `*` 覆盖 4 个二进制 + dag-templates.tar.gz;`shasum || sha256sum` 双 fallback。 -- `gh release create ... --target "${{ github.sha }}"`(`:264`)→ 手动触发时 github.sha = 触发瞬间分支 HEAD commit,tag 精确锚定该 commit;`--prerelease` 当 `github.ref_name != "main"`(`:258-260`)。上传 `release-assets/*` 含 SHA256SUMS 本身 ✓。 -- **风险点**:build-cli 的 checkout(`:124-126`)未指定 `ref: ${{ github.sha }}`,workflow_dispatch 下默认检出分支 tip —— 若构建期间分支有新 push,二进制可能来自更新的 commit 而 release tag 锚定旧 sha(低概率、构建-标签不一致)。release job 自身 checkout 同理(仅作 gh 上下文,无害)。 -- **Windows 路径风险(候选 HIGH)**:models.dev step 对 `$RUNNER_TEMP` 显式做 `cygpath -m` 转换(`:145-147`),而 Extract Templates 的 `$GITHUB_WORKSPACE/dag-templates-src`(`:175`)**无任何转换**。Windows runner 的 bash 下 GITHUB_WORKSPACE 为 POSIX 形(`/d/a/...`),写入 GITHUB_ENV 后原生 Bun 进程读取该路径可能无法解析 → glob 0 模板 → **Windows 发布版静默丢失 builtin 模板**。作者在同一文件里已证明知道此坑(cygpath 行),此处遗漏高度可疑。无法本地验证 Bun@Windows 路径处理 → 列入 unverified,交 verify 波。 - -## 5. Test Coverage Inventory - -**dag-workflows.test.ts(163 行,14 个测试,全部通过 diff 确认保留)**: - -| describe | 数量 | 覆盖 | -|---|---|---| -| `isName` | 3 | 裸标识符 true;path 形/扩展名/`.`/`..`/空串 false;控制字符(NUL、换行)false | -| `resolve` | 7 | project 命中;global 回退;project 遮蔽 global;`.yml` 扩展;同 scope `.yaml` 优先于 `.yml`;未知名 undefined + searchPaths 顺序断言;**不可解析 spec 仍返回 entry**(title/nodes undefined) | -| `list` | 4 | 双 scope 皆空 → [];合并排序 + 遮蔽(project 优先);忽略非 spec 文件与目录;无 title 时 title undefined、nodes=0 | - -**删除的测试**(dag-workflows.test.ts 旧 165-187 行):`change-review as valid start spec` —— 验证 `resolve("change-review", repoRoot)` 命中 project scope、`StartSpec` decode 通过、每个 `prompt_template.id` 存在于 `.opencode/dag-prompts/`、`depends_on` 引用有效。**删除一致性确认**:98e4c0624 删除了 4 个 tracked 模板(change-review.yaml / deep-review-dag-module.yaml / design-decision-loop.yaml / parallel-development-loop.yaml),测试会因 `entry!.path` 直接抛错而必挂 —— 删除是必要条件而非清理。但代价:模板间的 `depends_on`/prompt-template 引用完整性校验**从此没有任何自动化守卫**(配置仓库在 repo 外,无 CI 挂钩)。 - -**builtin scope 覆盖:无。** `builtinTemplates()`(workflows.ts:51-55)在测试环境 typeof 守卫返回 `{}`,`declare const` 无任何注入钩子(不像 config 可用 OPENCODE_CONFIG_DIR 重定向)。resolve 的 builtin fallback、list 的 builtin 合并/去重、`isBuiltinPath`/`builtinName`、tool/workflow.ts:360-370 的 builtin 读谱分支、`searchedScopes` 提示 —— 全部零测试。要测只能通过真实构建注入,测试基建缺失。 - -**workflow-tool.test.ts**(未改动文件,spot-check):saved-workflows 段 6 个测试覆盖 tool 级 list/resolve 行为 —— start-by-name(project、global 均无外部目录权限询问)、未知名错误信息含两个搜索目录、list 双 scope 遮蔽输出(`shared [project] — project-shared title`)、list 空库提示。schema 负例段(:291-331)覆盖 action/operation 枚举。**builtin 相关消息分支(searchedScopes 拼接)同样零覆盖。** - -**dag-workflow-lock.test.ts**(1 测试,未改动):mock DagStore.getWorkflow 内 25ms sleep + activeReads 计数,unbounded 并发 2 次 `dag.extend("wf1")`,断言 `maxActiveReads === 1` —— 验证 extend 同 workflow 串行化锁语义。与本 PR 无交集,属上下文佐证。 - -## 6. CI 回归风险 - -- **现有 CI(ci-typecheck/ci-test)零改动**;本 PR 只动 release-fork.yml,push 到 main/dev 仅触发 `register`(path-filtered),无新负担。 -- 唯一共享面:`setup-bun` macOS cache saver(`:137`)—— 未改动,既有行为。 -- job 级 skip 语义无级联失败风险(skip→needs success→下游同条件 skip)。 -- **风险清单**:① Windows DAG_TEMPLATES_DIR 路径转换缺失(静默空 builtin,候选 HIGH);② build-cli checkout 未 pin `github.sha` vs release `--target github.sha` 竞态(低概率 MEDIUM-LOW);③ 配置仓库为空/不可达时 package-templates 行为(空=降级 ✓,克隆失败=job 失败 fail-loud,可接受);④ `shasum -a 256 *` 在 release-assets 恒非空(dag-templates 必然存在)前提下安全。 - -## 其他值得 reviewer 关注的证据点 - -- `parseMeta` async + `.catch(() => undefined)`(workflows.ts:155-158)修复了旧 `describeContent` 同步 `Bun.YAML.parse` 对 malformed builtin 抛异常的路径 —— 现在 resolve/list 容忍、start 时 fail-loud(tool/workflow.ts:364-368 `Effect.try`),与文件路径行为一致 ✓。 -- list 去重(workflows.ts:117-120 `seen.set` + `if (seen.has(name)) continue`)保证 builtin 被 project/global 遮蔽 ✓,与 resolve 优先级一致。 -- `/dag-template-update`(103 行 prompt):`mkdir` 原子锁 + 失败回退、备份失败即中止、内容级 verify、local-only 保留 —— 全部是 prompt 级契约,**无代码强制**(该命令完全由 agent 依 prompt 执行),属设计权衡而非 bug。 -- dag-flow.txt 已更新为库引用(saved workflow 名字)+ 移除仓库内路径,与模板迁移一致 ✓。 - ---- - -## output_variables - -- targets: - - `package-templates job@.github/workflows/release-fork.yml:66-97` - - `build-cli job@.github/workflows/release-fork.yml:99-216` - - `release job@.github/workflows/release-fork.yml:218-266` - - `builtinTemplates@packages/opencode/src/dag/workflows.ts:51-55` - - `resolve@packages/opencode/src/dag/workflows.ts:73-89` - - `list@packages/opencode/src/dag/workflows.ts:100-122` - - `isBuiltinPath/builtinName/builtinEntry/parseMeta@packages/opencode/src/dag/workflows.ts:125-158` - - `loadDagTemplatesData@packages/opencode/script/generate.ts:39-60` - - `readWorkflowSpec@packages/opencode/src/tool/workflow.ts:350-391` - - `searchedScopes@packages/opencode/src/tool/workflow.ts:393-398` - - `DagTemplateUpdateDescription + command registration@packages/core/src/plugin/command.ts:15,42-44` - - `dag-template-update.txt@packages/core/src/plugin/command/dag-template-update.txt:1-103` - - `dag-flow.txt@packages/core/src/plugin/command/dag-flow.txt` - - `OPENCODE_DAG_TEMPLATES define@packages/opencode/script/build.ts:203` - - `dag-workflows.test.ts@packages/opencode/test/dag/dag-workflows.test.ts`(14 测试 + 1 删除) - - `dag-workflow-lock.test.ts@packages/opencode/test/dag/dag-workflow-lock.test.ts:8-58` - - `workflow-tool.test.ts saved-workflows@packages/opencode/test/dag/workflow-tool.test.ts:1038-1195` -- impacted_processes: [ReleaseForkPipeline, DagWorkflowResolution, DagTemplateUpdate] -- test_anchors: - - `packages/opencode/test/dag/dag-workflows.test.ts::isName/resolve/list` - - `packages/opencode/test/dag/workflow-tool.test.ts::workflow tool saved workflows` - - `packages/opencode/test/dag/dag-workflow-lock.test.ts::Dag.Service workflow lock` -- ast_available: true - -**unverified_claims(交 verify 波)**: -1. Bun@Windows 对 POSIX 形 `DAG_TEMPLATES_DIR`(`/d/a/...`)路径的解析行为 —— 决定 Windows 发布版是否静默丢失 builtin 模板(release-fork.yml:175 vs :145-147 的 cygpath 不对称)。 -2. GitHub Actions windows runner bash 下 `$GITHUB_WORKSPACE` 的实际形态(/d/a/... vs D:\a\...)。 -3. `"undefined"` 字符串经 Bun.build define 注入为 `undefined` 关键字的假设(models 同款已在生产证明,风险低)。 \ No newline at end of file diff --git a/.opencode/.dag-specs/review-parts/explore-runtime.md b/.opencode/.dag-specs/review-parts/explore-runtime.md deleted file mode 100644 index fed6cdc080..0000000000 --- a/.opencode/.dag-specs/review-parts/explore-runtime.md +++ /dev/null @@ -1,125 +0,0 @@ -## Hit Summary - -builtin 三级 scope 解析面已完整映射:`workflows.ts` 的解析/列举逻辑(project > global > builtin,first-match-wins)与 `tool/workflow.ts` 的消费路径(`readWorkflowSpec` builtin 分支、`searchedScopes` 提示)实现正确且自洽;esbuild define 注入机制已实测验证可用(JSON 对象值被正确包裹)。主要风险集中在**零测试覆盖**:builtin scope 无任何测试(`rg builtin` 在测试目录零命中),且被删除的仓库自检测试(旧 spec 解码校验)没有替代,导致 config repo 模板完全脱离本仓库测试套件。另有 2 个值得上报的边界问题(`list()` 展示的 `builtin://` 路径不可作为 `spec_path` 回填;空库消息中 builtin 提及是不可达死代码)。置信度:代码路径理解高,release 流水线实际产物中高(静态审查)。 - -## Key Symbols - -- `packages/opencode/src/dag/workflows.ts:35` `export type Scope = "project" | "global" | "builtin"` — 新增第三级 scope -- `packages/opencode/src/dag/workflows.ts:50-56` `builtinTemplates()` — `typeof OPENCODE_DAG_TEMPLATES === "undefined"` 守卫,undefined(dev/test)→ `{}` -- `packages/opencode/src/dag/workflows.ts:77-90` `resolve()` — project/global 循环后 builtin 兜底 -- `packages/opencode/src/dag/workflows.ts:102-123` `list()` — seen Map 去重,builtin 合并,`localeCompare` 排序 -- `packages/opencode/src/dag/workflows.ts:126-133` `isBuiltinPath()` / `builtinName()` — `builtin://` 前缀解析 -- `packages/opencode/src/dag/workflows.ts:158-167` `parseMeta()` — YAML 解析容错 + title/nodes 提取 -- `packages/opencode/src/tool/workflow.ts:359-370` `readWorkflowSpec()` builtin 分支 — 内容缺失报错、YAML 解析失败路径 -- `packages/opencode/src/tool/workflow.ts:395-399` `searchedScopes()` — 目录 + 条件性 builtin 提及 -- `packages/opencode/script/generate.ts:36-59` `loadDagTemplatesData()` / `dagTemplatesData` — 构建期从 `DAG_TEMPLATES_DIR` 读快照 -- `packages/opencode/script/build.ts:203` `OPENCODE_DAG_TEMPLATES: generated.dagTemplatesData` — define 注入点 -- `packages/core/src/models-dev.ts:114,185-187` — `OPENCODE_MODELS_DEV` 既有同构先例 - -## Call Relationships - -``` -tool/workflow.ts resolveSpecPath (L401) - └─ isName(specPath)? (L408) - ├─ resolve(name, dir) → workflows.ts:77 → scopes() (project L144 → global L145) → builtinTemplates() (L86-88) - │ └─ builtinEntry() (L135-137) → parseMeta() (L158) - └─ path branch (L415-424) — 永不产生 builtin:// 路径(实测:// 被 path 规范化折叠) -readWorkflowSpec (L350) - ├─ isBuiltinPath(filepath)? (L360) → builtinTemplates()[builtinName()] → YAML.parse (L365-368) - │ └─ 缺失 → fail "Workflow spec not found: builtin://name" (L363) - └─ 文件分支 (L372-389) — 含 1MB size 检查 (L376),builtin 分支绕过 -build 链: release-fork.yml (clone opencode-dag-config → tar *.yaml → artifact) - → build-cli (下载解包 → DAG_TEMPLATES_DIR env, L160-175) - → generate.ts:45 (env 读取) → generate.ts:59 (dagTemplatesData) - → build.ts:203 (define) → workflows.ts:30 (declare const) → workflows.ts:54 (typeof 守卫) -``` - -## Execution Flows Involved - -- `Flow: BuiltinResolution` — bare name → `resolve()` 三级查找 → builtin 兜底 → `readWorkflowSpec` builtin 分支 → YAML.parse → start/extend/replan 解码 (workflows.ts:86-88 → workflow.ts:360-369) -- `Flow: BuiltinInjection` — 构建期:`DAG_TEMPLATES_DIR` env → glob `*.yaml` → `JSON.stringify` → esbuild define → 编译进二进制;dev/test 下返回字面量 `"undefined"` → 守卫生效返回 `{}`(实测验证对象注入路径:`{"a":"b"}` 被正确编译为对象字面量) -- `Flow: LibraryListing` — `list()` 三级合并去重排序 → `[name] [scope] — title (N nodes)\n path` 输出(workflow.ts:149-162),builtin 条目路径显示为 `builtin://name` - -## Related Test Files - -- `packages/opencode/test/dag/dag-workflows.test.ts` — **无 builtin 覆盖**(isName/resolve 投影/全局/list 测试均不触达 builtin 分支);已删除旧测试"the repository's own workflow library"(旧 L163-189,校验 change-review 可解码) -- `packages/opencode/test/dag/workflow-tool.test.ts:1038-1160` — saved workflows 段:bare name 解析、全局 scope 无外目录提示、not-found 消息(`toContain` 子串断言,容忍 builtin 提及追加)、list 双 scope 投影;同样无 builtin 用例 - ---- - -## 1. 解析顺序与遮蔽语义(含行号) - -- **顺序**:`scopes()`(workflows.ts:142-147)= project(`.opencode/workflows`,L144)→ global(`Flag.OPENCODE_CONFIG_DIR ?? Global.Path.config` + workflows,L145);`resolve()` 先遍历两 scope 的 `.yaml`/`.yml`(L79-85,EXTENSIONS 顺序 L33 保证同 scope 内 `.yaml` 优先),builtin 仅在前两者均未命中时兜底(L86-88)。头注释 L9-14 一致。 -- **list() 遮蔽一致性**:`seen` Map key=name、first-wins(L104),builtin 只在 `!seen.has(name)` 时并入(L117-120),与 resolve() 优先级严格一致——不会列出 resolve() 选不中的文件。 -- **验证状态**:project>global 遮蔽有测试(dag-workflows.test.ts:88-94、workflow-tool.test.ts 双 scope 用例);**project/global 遮蔽 builtin 零测试**。 - -## 2. builtin 数据流 - -- **声明**:`declare const OPENCODE_DAG_TEMPLATES`(workflows.ts:30),仅构建期存在;`builtinTemplates()` 用 `typeof` 守卫(L54)防 ReferenceError——dev/test 裸跑返回 `{}`。 -- **注入链**:`generate.ts:45` 读 `DAG_TEMPLATES_DIR` env → 无 env 返回字符串 `"undefined"`(L47-48,define 后成为字面量 `undefined` → 守卫触发);有 env 则 `new Bun.Glob("*.yaml").scan({cwd})`(L49)收集 → name 去扩展名(L50)→ `JSON.stringify`(L57)→ `build.ts:203` 注入 define。**已实测**:Bun build 对 `{"a":"b"}` 值正确编译为对象字面量(非 block statement),typeof 返回 "object"。 -- **release 侧**:`release-fork.yml` package-templates job(clone `LeXwDeX/opencode-dag-config` → 仅打包根目录 `*.yaml` → tar artifact,L68-96)→ build-cli job 下载解包并写入 `DAG_TEMPLATES_DIR` env(L160-175)→ 与 `--compile` 定义值配套。 -- **与先例同构**:`models-dev.ts:114,185-187` 的 `OPENCODE_MODELS_DEV` 完全相同模式(含 length 检查),生产已验证——机制可信。 -- **dev/test 差异**:无 env → `"undefined"` 字面量 → 空 map → builtin scope 静默关闭,所有现有测试实际都跑在守卫分支上(隐式覆盖了守卫本身)。 - -## 3. readWorkflowSpec builtin 路径 - -- **解析**:`isBuiltinPath`(workflows.ts:126-128,`startsWith("builtin://")`)+ `builtinName`(L131-133,slice 前缀)。 -- **内容缺失**:`content === undefined` → `Effect.fail("Workflow spec not found: builtin://name")`(workflow.ts:362-364)——与文件分支的 not-found 同文案风格。 -- **YAML 失败**:`Effect.try` 包裹 `Bun.YAML.parse`(L365-368)→ `workflowSpecParseError`(L428-430)→ `Invalid workflow YAML builtin://name: `,与文件分支(L385-388)同形。 -- **可达性**:builtin 分支只能由 `resolve()` 返回的路径触发。**实测**:用户直接传 `builtin://code-review.yaml` 作 spec_path 时,`path.resolve` 把 `//` 折叠成 `/`(POSIX `/abs/dir/builtin:/code-review.yaml`,win32 `C:\dir\builtin:\code-review.yaml`),`isBuiltinPath` 恒 false → 落入路径分支报"must be .yaml/.yml"或 not-found——**不可伪造,也不可达**。 -- **绕过项**:builtin 内容跳过 1MB size 检查(L376-380 仅文件分支)与外目录权限提示——内容为构建期策展,可接受,但属隐式信任面。 - -## 4. list() 行为 - -- **去重**:seen Map 三级 first-wins(L104-120);**排序**:`a.name.localeCompare(b.name)`(L121,unchanged)。 -- **parseMeta 容错**(L158-167):YAML parse 失败 catch → `{}`(L159-161);title 需顶层字符串(L164);nodes = `config.nodes` 数组长度(L165-166);畸形 spec 照常列出(测试 dag-workflows.test.ts:117-123、156-161 覆盖文件侧;builtin 侧同函数未测)。 -- **describe() 重构**(L149-153):`.text().catch(() => undefined)` 拆出与 pre-PR 行为等价(pre-PR 的 catch 在 `.then(YAML.parse)` 之后,同样吞掉 unreadable 与 parse 错误)——**非行为修复,纯为共享 parseMeta**。 -- **builtin 条目**:scope="builtin"、path=`builtin://name`、携带 content(L135-137)。`Entry.content` **当前无任何消费者**(start 路径重查 `builtinTemplates()[name]`,list 输出不含 content)——死字段,为未来 TUI/预览预留。 - -## 5. searchedScopes() 与消息 - -- `searchedScopes()`(workflow.ts:395-399):`searchPaths()` 两目录 + builtin map 非空时追加 `"the release's builtin templates"`。 -- **not-found 消息**(L411-413):`Saved workflow not found: "x". Searched [ and builtin]...` —— builtin 提及仅在 map 非空时出现,语义正确。 -- **空库消息**(L142-147):`The workflow library is empty. Searched ...` —— **builtin 提及不可达死代码**:map 非空时 list() 必然包含 builtin 条目、`entries.length === 0` 不可能成立,而 map 空时 searchedScopes 不加 builtin 文案。 -- **dag-flow.txt 与内置 scope 不一致**(上下文):prompt 只描述两 scope(project/global),未提 builtin——agent 若回填 list() 展示的 `builtin://` 路径会失败(见 §3),但 prompt 引导使用裸名,实际可用。 - -## 6. 边界/边缘情况 - -| 边界 | 行为 | 行号 | 状态 | -|---|---|---|---| -| 空 builtin map(dev/test) | 守卫返回 `{}`,resolve 落 undefined,消息只提目录 | workflows.ts:54 | ✓ 隐式覆盖 | -| project/global 遮蔽 builtin | resolve L79-88 / list L117-119 一致 first-wins | — | ✓ 逻辑正确,无测试 | -| 畸形 builtin 内容 | list 仍列出(parseMeta catch);start 报清晰 parse error | workflows.ts:159-161 / workflow.ts:365-368 | ✓ | -| `builtin://` 伪造 | 路径规范化折叠 `//`,不可达 builtin 分支(实测) | workflow.ts:360 | ✓ | -| list 展示路径不可回填 | `builtin://name` 含 `/` → isName=false → 路径分支报错 | workflow.ts:408-418 | ⚠️ UX 缺陷,非崩溃 | -| 1MB size 检查绕过 | builtin 内容不检查 | workflow.ts:376 vs 359-370 | ⚠️ 低风险(构建期信任) | -| `.yml`/嵌套目录静默丢弃 | glob 仅 `*.yaml` 顶层(generate.ts:49;release-fork L73-76 同);L50 正则容忍 `.yml` 是死代码 | — | ⚠️ 依赖 config repo 布局 | -| `Entry.content` 无消费者 | 死字段 | workflows.ts:42-43 | ℹ️ 预留面 | -| **builtin 零测试覆盖** | 全新增面无测试;旧仓库自检测试删除后 config repo 模板完全脱离本仓库 CI 校验 | 测试文件 diff | 🔴 测试缺口 | - -## unverified_claims(供 verify 波核对) - -- **U1**:release 产出的二进制实际包含模板——仅静态验证 define 机制 + CI job 图;`--version` smoke test(build.ts)不触达 builtin 解析,无法本地跑通 release 构建(config repo 私有/外网)。 -- **U2**:opencode-dag-config 仓库根目录只含 `*.yaml`(顶层)——若含 `.yml` 或子目录会被 glob/打包脚本静默丢弃。 -- **U3**:`/dag-template-update` 写入目录与运行时读取目录一致(`OPENCODE_CONFIG_DIR` → 平台默认)——命令是 prompt 无代码路径,运行时用 `Flag.OPENCODE_CONFIG_DIR ?? Global.Path.config`(workflows.ts:145)。 -- **U4**:`build-cli` job 仅 `workflow_dispatch` 触发(release-fork.yml:106),push 触发只注册不构建——builtin 注入只存在于手动 release 产物,非 dev 构建。属设计意图,非缺陷。 - -## output_variables - -- targets: - - `builtinTemplates@packages/opencode/src/dag/workflows.ts:51` - - `resolve@packages/opencode/src/dag/workflows.ts:77` - - `list@packages/opencode/src/dag/workflows.ts:102` - - `isBuiltinPath@packages/opencode/src/dag/workflows.ts:126` - - `builtinName@packages/opencode/src/dag/workflows.ts:131` - - `parseMeta@packages/opencode/src/dag/workflows.ts:158` - - `readWorkflowSpec@packages/opencode/src/tool/workflow.ts:350`(builtin 分支 359-370) - - `searchedScopes@packages/opencode/src/tool/workflow.ts:395` - - `loadDagTemplatesData@packages/opencode/script/generate.ts:36` -- impacted_processes: [BuiltinResolution, BuiltinInjection, LibraryListing] -- test_anchors: - - [dag-workflows.test.ts::DagWorkflows.resolve(无 builtin 用例)] - - [dag-workflows.test.ts::DagWorkflows.list(无 builtin 用例)] - - [workflow-tool.test.ts::workflow tool saved workflows@1038(无 builtin 用例)] - - 已删除:dag-workflows.test.ts 旧 "repository's own workflow library"(spec 解码校验,无替代) -- ast_available: true(全文件直接精读 + git diff 比对 + 2 项运行时实测验证,未依赖图索引) \ No newline at end of file diff --git a/.opencode/.dag-specs/review-parts/review-architecture.md b/.opencode/.dag-specs/review-parts/review-architecture.md deleted file mode 100644 index 4a2dcae38c..0000000000 --- a/.opencode/.dag-specs/review-parts/review-architecture.md +++ /dev/null @@ -1,75 +0,0 @@ -# PR #167 架构审查报告(只读) - -审查基准:`git diff origin/dev...HEAD`(4 commits,13 文件:+286/−926)。已实测运行 `bun test dag-workflows/workflow-tool`(40 pass / 0 fail)作为测试证据。 - -## findings - -**1. HIGH — `/dag-template-update` 运行时不可达:仅注册了 core plugin draft,未注册进应用 Command 服务** - -命令只加在 `packages/core/src/plugin/command.ts:42-45` 的 `draft.update`,该 draft 落入 core v2 Command catalog(`packages/core/src/command.ts`),消费方只有 core plugin host(`packages/core/src/plugin/host.ts:95-97`),**不接入 TUI/会话命令面**。TUI 命令列表(`sync.tsx` → `GET /command`,`packages/opencode/src/server/routes/instance/httpapi/handlers/instance.ts:76-77`)与 slash 命令执行(`session/prompt.ts:1867-1871`,`commands.get` 未命中 → "Command not found")都走 `packages/opencode/src/command/index.ts` 的 Command 服务。该服务只注册 `Default` 枚举内的命令(`index.ts:48-54`、dag-flow 注册于 `:94-100`)。dag-flow 先例(commit e14fdcdec)是**双注册**:core plugin draft + 应用 Default 枚举 + `command.test.ts:46-52` 注册断言;本 PR 只做了前者。后果:用户输 `/dag-template-update` 直接 "Command not found",命令功能整体死代码。 - -推荐:仿 DAG_FLOW 在 `packages/opencode/src/command/index.ts` 的 `Default` + commands map 补注册,并在 `packages/opencode/test/command/command.test.ts` 加断言。 - -**2. MEDIUM — builtin scope 零测试覆盖,且删除的仓库自检测试无替代** - -`packages/opencode/test/dag/dag-workflows.test.ts` 删除了唯一的真实 spec 校验(旧 L163-189:StartSpec 解码 + prompt_template.id/depends_on 引用完整性),全部保留测试(isName/resolve/list 14 个)不触达 builtin 分支;`workflow-tool.test.ts` 亦无 builtin 用例。新增面(`workflows.ts:51-56,86-88,117-120`、`workflow.ts:361,395-397`)零覆盖。`declare const` 无注入钩子,测试基建缺失是根因。结果:config repo 模板完全脱离本仓库 CI,模板间引用错误不再有任何自动守卫。 - -推荐:为 `builtinTemplates()` 提供可注入测试源(如可选参数/全局 hook),覆盖 resolve builtin 兜底、project/global 遮蔽 builtin、list 合并去重、`isBuiltinPath`/`builtinName`、searchedScopes 拼接;`generate.ts:44-57` 的 glob→map 逻辑可抽纯函数用临时 `DAG_TEMPLATES_DIR` 单测。 - -**3. MEDIUM — 文档与运行时三级 scope 不一致,dev 环境 /dag-flow 首步即失败** - -- `dag-flow.txt:13` 仍写 "two scopes (project overrides global)"——运行时是三级(`workflows.ts:9-14`);`:14` 称 global "curated by the opencode-dag-config repo",实际 curated 快照的载体是 builtin 层(global 需先跑 update 才存在)。 -- `workflow.md:84-85,483-484`(追加到每次 dag-flow 调用的 WorkflowFactsContent)同样只描述两 scope。 -- `dag-flow.txt:17-20` 点名 4 个 saved workflow(design-decision-loop 等)——dev checkout(无 builtin、无 config repo)下解析不到,agent 按提示第一步就 "Saved workflow not found: ..."(`workflow.ts:412`),主流程在 dev 与 release 体验割裂。 -- README 死链/过时:`README.md:253` 指向已删除的 `change-review.yaml`;`README.md:30`、`README.zh.md:27` "仓库附带三类参考图"已不成立;`README.zh.md:66-67` 两 scope 表缺 builtin 层。 - -推荐:三处文档统一为三级描述并注明 builtin 仅存在于 release 构建;README 死链改为指向配置仓库。 - -**4. MEDIUM — Windows runner `DAG_TEMPLATES_DIR` 路径未做 cygpath 转换(同文件不对称)** - -`release-fork.yml:175` 写 `DAG_TEMPLATES_DIR=$GITHUB_WORKSPACE/dag-templates-src`,无任何转换;同 job 的 models.dev step(`:145-147`)对 `$RUNNER_TEMP` 显式做了 `cygpath -m`——作者在同一文件证明知道此坑。若 Windows bash 下 `GITHUB_WORKSPACE` 呈 MSYS 形(`/d/a/...`)或 Bun 无法解析混合分隔符路径,glob 零匹配 → **Windows 发布版静默丢失 builtin 模板**(generate.ts 空集不报错,`workflows.ts:51-56` 优雅降级为两级)。 - -推荐:与 models.dev 一致加 `cygpath -m`(保留 POSIX 与 Windows 双形态兼容写法),并在 build 步骤后加一步生成物校验(如 `bun -e` 检查二进制含模板名),防静默降级。 - -**5. MEDIUM — `/dag-template-update` prompt 锁语义两个缺口(纯 prompt 契约,无代码兜底)** - -- 父目录前置缺失:锁创建 `mkdir /workflows/.dag-update.lock`(`dag-template-update.txt:72-74`)在 `/workflows` 不存在时失败是 **ENOENT 而非 EEXIST**;"创建目录"指令在 L97 Failure handling 才出现,且措辞是 "before applying",锁在下载前——agent 可能把 ENOENT 误判为"有并发更新"。 -- 陈旧锁无恢复:agent 崩溃/被杀后 `.dag-update.lock` 永久残留,`L75-76` 只说"wait and retry, then stop"——此后该命令永远不可用,无 mtime/age 检测、无强制覆盖、无提示手工清除。 - -推荐:锁步骤前明确先 `mkdir -p /workflows`;增加"若锁已存在,检查其 mtime 超时(如 >30min)则视为陈旧、报告并提示删除后重试"。 - -**6. LOW — `workflows.ts:16` 头注释残留 "same two-level scope"** - -三级已落地但注释未更新(:9-14 已改为三级,:16 与之一句之隔自相矛盾)。推荐改为 "same multi-level scope" 或移除。 - -**7. LOW — 空库消息的 builtin 提及是不可达死代码** - -`tool/workflow.ts:145` 的 empty-library 分支:builtin map 非空时 `list()` 必含 builtin 条目(`workflows.ts:117-120`)→ `entries.length === 0` 不成立;map 空时 `searchedScopes` 不追加 builtin 文案。分支只在"builtin 存在"时命中,文案永不出现。 - -**8. LOW — `list()` 展示的 `builtin://name` 路径不可回填 `spec_path`,与 dag-flow.txt 指引冲突** - -`dag-flow.txt:16` 说 "pick by name or path";builtin 条目路径(`workflows.ts:58,131-133`)经 `resolveSpecPath` 路径分支(`tool/workflow.ts:415-424`)会被 `path.resolve` 折叠 `//` 后报 "must be a .yaml or .yml file"。用户按文档回填即报误导性错误(正确用法是裸名,但文档未说清)。 - -**9. LOW — `.yml` 漂移:三层只认 `.yaml`,运行时认 `.yml`,`generate.ts:52` 的 `?` 是死代码** - -运行时 `EXTENSIONS` 含 `.yml`(`workflows.ts:33`),但 generate.ts glob(`generate.ts:51`)、release 打包(`release-fork.yml:83`)、update prompt 全只匹配 `.yaml`;`/\.ya?ml$/` 中的 `?` 无输入可达。config repo 若放入 `.yml` 会被静默丢弃——布局契约全靠隐式约定,建议文档化或在 glob 统一。 - -**10. LOW — build-cli checkout 未 pin `ref: ${{ github.sha }}`,release tag 锚定旧 sha 竞态** - -`release-fork.yml:122-126` checkout 默认分支 tip;`release` job `--target "${{ github.sha }}"`(`:264`)锚定触发瞬间的 commit。构建期间若分支有 push,二进制与 tag 指向不同 commit。低概率但存在;成本极低(加一行 `ref`)。 - -**11. LOW — `Entry.content` 死字段** - -`workflows.ts:42-43,135-137` 设置 content,全仓库无消费者(start 路径重查 `builtinTemplates()`,`tool/workflow.ts:361`;list 输出不含)。注释标注为预留,可接受,但建议注明驱动场景(TUI 预览)避免误以为已接通。 - -## unverified_claims - -- Windows runner bash 下 `$GITHUB_WORKSPACE` 的实际形态(`D:\a\...` vs `/d/a/...`)及 Bun 对混合分隔符路径的解析行为——决定 finding 4 是否实际触发;本地无法复现 windows-latest 环境。 -- opencode-dag-config 仓库根目录只含顶层 `*.yaml`(无 `.yml`/子目录)——glob、release 打包、update prompt 三层假设成立与否无法本地验证。 -- release 产物二进制确实包含模板(define round-trip 端到端)——仅静态验证 `build.ts:203` + `generate.ts:44-57`,与 `OPENCODE_MODELS_DEV` 先例同构,风险低。 -- core v2 Command catalog(plugin draft 落点)是否被任何 TUI/会话表面消费——静态追踪显示无(`command.list` 路由与 session 均走应用 Command 服务),未做运行时验证。 -- `"undefined"` 字符串经 Bun.build define 注入为 `undefined` 关键字语义——models 生产先例已证明,风险低。 - -## summary - -总体架构方向正确:三级 scope(project > global > builtin)的解析/遮蔽/去重语义在代码层自洽,define 注入复用了 `OPENCODE_MODELS_DEV` 成熟先例(typeof 守卫、`"undefined"` 字面量降级均正确),release 双渠道(资产 + 内嵌)职责清晰,`package-templates` 与 build-cli 的耦合度合理,已修复项(ReferenceError 守卫、Entry 去重、空 glob、重复下载)均验证为正确修复而非回退。阻塞性问题只有一个但致命:`/dag-template-update` 命令只注册了 core plugin draft 一处,未按 dag-flow 先例接入应用 Command 服务,运行时不可达——发布前必须补注册。次要面(builtin 零测试、文档三级不一致、Windows 路径转换、锁语义缺口)应在合并前或紧随其后处理,其中 finding 4 若成立会导致 Windows 发布版静默丢失该 PR 的核心价值。 \ No newline at end of file diff --git a/.opencode/.dag-specs/review-parts/review-logic.md b/.opencode/.dag-specs/review-parts/review-logic.md deleted file mode 100644 index 17bddc0065..0000000000 --- a/.opencode/.dag-specs/review-parts/review-logic.md +++ /dev/null @@ -1,79 +0,0 @@ -审查完成。以下为逻辑正确性审查报告。 - -## 1. findings - -### CRITICAL -无。 - -### HIGH - -**H1. Windows 发布构建的 `DAG_TEMPLATES_DIR` 路径未做 cygpath 转换(与同文件 models 步骤不对称)** -- description: Extract Templates 把 `$GITHUB_WORKSPACE/dag-templates-src` 原样写入 GITHUB_ENV;而同一文件 15 行之上的 models 步骤对 `$RUNNER_TEMP` 路径显式做了 `cygpath -m` 转换——作者已知 Windows runner 的 bash 下路径形态陷阱,此处遗漏。若 bash 中 `GITHUB_WORKSPACE` 为 MSYS/POSIX 形(`/d/a/...`),原生 Bun 进程无法解析该 cwd:本地实测 `Bun.Glob.scan({cwd: 不存在目录})` 抛 ENOENT(fail-loud,build 失败);若 scan 恰好返回空则 builtin 静默缺失。无法本地验证 Windows 侧行为(见 U1/U2)。 -- evidence: `.github/workflows/release-fork.yml:175` vs `.github/workflows/release-fork.yml:145-147` -- recommendation: 与 models 步骤同构,写入 GITHUB_ENV 前 `cygpath -m`(cygpath 存在时):`echo "DAG_TEMPLATES_DIR=$(cygpath -m "$GITHUB_WORKSPACE/dag-templates-src")" >> "$GITHUB_ENV"`。 - -**H2. builtin scope 零测试覆盖,且被删的仓库自检无替代** -- description: 新增面全部无测试:`builtinTemplates()` 守卫、resolve builtin 兜底(workflows.ts:86-88)、list builtin 合并(117-120)、`isBuiltinPath`/`builtinName`、`readWorkflowSpec` builtin 分支(workflow.ts:359-370)、`searchedScopes` 拼接。全仓 test 目录 `rg builtin` 零命中。删除的 "repository's own workflow library" 测试是唯一对真实 spec 做 `StartSpec` 解码 + prompt_template/depends_on 引用完整性校验的守卫;删除后 config repo 模板完全脱离本仓库任何 CI 校验(该仓库无 CI 挂钩)。删除本身必要(4 个模板已移出),但无替代校验。 -- evidence: `packages/opencode/test/dag/dag-workflows.test.ts`(diff 删除 describe 块);`packages/opencode/src/dag/workflows.ts:51-56,86-88,117-120` -- recommendation: 为 builtinTemplates/resolve/list 补单测(测试环境无法注入 define——可把 builtin map 改为可注入来源,或在测试内直接断言守卫分支 + 用 `Object.defineProperty(globalThis, ...)` 模拟注入);同时考虑在 opencode-dag-config 仓库加模板解码 CI。 - -### MEDIUM - -**M3. `list()` 展示的 `builtin://name` 路径不可回填为 spec_path(工具契约断裂)** -- description: project/global 条目展示的路径可直接回传;builtin 条目路径 `builtin://name`(无扩展名)回传时 `isName` 因含 `/` 为 false → 落入 path 分支 → `path.resolve` 折叠 `//`、extname 为空 → 报误导性错误 "must be a .yaml or .yml file"。结合 dag-flow.txt:16 的 "pick by name or path",agent 按 list 输出回填必失败。 -- evidence: `packages/opencode/src/dag/workflows.ts:136`(path 生成);`packages/opencode/src/tool/workflow.ts:408`(isName 拒绝)、`415-418`(extname 检查);`packages/core/src/plugin/command/dag-flow.txt:16` -- recommendation: 在 resolveSpecPath 的 path 分支前特判 `builtin://` 前缀直接走 builtin 解析;或在 list 输出中为 builtin 条目注明 "start by name only"。 - -**M4. dag-flow.txt 描述 "two scopes" 与运行时三级不符;4 个指名模板在 dev checkout 下不可解析** -- description: 运行时是 project → global → builtin 三级(workflows.ts:9-14),提示词只字未提 builtin——而对开箱即用用户,curated 模板恰恰只存在于 builtin 层,global 层为空。design-decision-loop / parallel-development-loop / deep-review-dag-module / change-review 四个名字对应本 PR 删除的模板(diff 872 行删除),dev checkout(无 builtin 注入、未跑 update)下全部解析失败。"run list first" 是兜底,但指名具体模板会误导。 -- evidence: `packages/core/src/plugin/command/dag-flow.txt:13-19`;`packages/opencode/src/dag/workflows.ts:9-14,35` -- recommendation: 更新为三级描述;对 4 个模板名标注"需已安装(builtin/已跑 update)"。 - -**M5. /dag-template-update 锁语义两个缺口:父目录 ENOENT 歧义 + 陈旧锁无恢复** -- description: (a) `mkdir /workflows/.dag-update.lock` 在 `/workflows` 不存在时失败是 ENOENT 而非 EEXIST,而提示词的唯一诊断是"fails because the directory exists → another update is in progress"(L72-74),agent 可能误报并发;"create it before applying"(L97)位置在锁章节之后,且锁章节说 "before downloading or merging"(L70)与段落线性顺序(下载→干跑→合并→锁)矛盾。(b) agent 崩溃/被杀后锁目录永久残留,后续更新永远停在"already running",无 mtime/age 检测、无强制清理或手工清除指引。 -- evidence: `packages/core/src/plugin/command/dag-template-update.txt:67-78,97` -- recommendation: 明确"先 `mkdir -p /workflows` 再取锁";增加陈旧锁恢复路径(检测锁目录 mtime 超阈值视为陈旧,或提示用户手工 rmdir 后再试)。 - -**M6. README 死链与过时声明(模板删除的连带损伤)** -- description: README.md:227 链接已删除的 `./.opencode/workflows/change-review.yaml`(404);README.md:30 / README.zh.md:27 "仓库附带三类参考图"已不成立;README.zh.md:66-67 两级 scope 表缺 builtin 层。 -- evidence: `README.md:227,30`;`README.zh.md:27,66-67` -- recommendation: 随本 PR 同步更新(本 PR 未触碰 README,属遗漏)。 - -### LOW - -**L7. `.ya?ml$` 的 `?` 是死代码;`.yml` 被三层一致丢弃** -- description: glob 只匹配 `*.yaml`,正则中 `?` 永不生效;运行时 EXTENSIONS 含 `.yml`(workflows.ts:33),但 generate.ts glob、release 打包(release-fork.yml:83)、update 提示三层都只认 `.yaml`——config repo 若放 `.yml` 模板会被静默丢弃。 -- evidence: `packages/opencode/script/generate.ts:51-52`;`.github/workflows/release-fork.yml:83` -- recommendation: 要么统一支持 `.yml`,要么去掉 `?` 并把"仅 .yaml"写成显式约束。 - -**L8. 空库消息的 builtin 提及是不可达死代码** -- description: builtinTemplates 非空 → list() 必有 ≥1 条目 → `entries.length === 0` 分支不可能执行;map 空 → searchedScopes 不追加 builtin 文案。两条件互斥,builtin 文案永不出现于空库消息。not-found 消息中的 builtin 提及(map 非空但该名缺失)可达且正确。 -- evidence: `packages/opencode/src/tool/workflow.ts:145` vs `395-399`;`packages/opencode/src/dag/workflows.ts:117-120` -- recommendation: 无害,但应移除或重构消息构造,避免维护者误读。 - -**L9. `Entry.content` 死字段** -- description: builtinEntry 写入 content(workflows.ts:135-137),但 readWorkflowSpec 重新查 `builtinTemplates()`(workflow.ts:360-364),全仓无 `.content` 消费者。 -- evidence: `packages/opencode/src/dag/workflows.ts:42-43,135-137`;全仓 grep 无消费者 -- recommendation: 为 TUI/预览预留可保留,建议加注释说明意图。 - -**L10. builtin 内容绕过 1MB size 检查** -- description: size 检查仅文件分支(workflow.ts:376),builtin 分支(359-370)跳过——内容为构建期策展,风险可接受,属隐式信任面。 -- evidence: `packages/opencode/src/tool/workflow.ts:376` vs `359-370` -- recommendation: 可接受,建议注释注明设计意图。 - -**L11. opencode-dag-config 未 pin tag/commit** -- description: update 命令下载 `refs/heads/main`(L31)、release clone 同取 HEAD(release-fork.yml:74-77)——同一 URL 两处 HEAD,可复现性弱,模板变更不可追踪。 -- evidence: `packages/core/src/plugin/command/dag-template-update.txt:31`;`.github/workflows/release-fork.yml:74-77` -- recommendation: 考虑 pin 到 tag;属设计权衡。 - -## 2. unverified_claims - -- U1: windows-latest runner 的 bash 中 `$GITHUB_WORKSPACE` 的实际形态(POSIX `/d/a/...` 还是原生 `D:\a\...`)——决定 H1 是否触发,本地不可验证 -- U2: Bun@Windows 对 MSYS/POSIX 形 cwd 的 `Glob.scan` 行为(本地仅在 macOS 实测:缺失 cwd 抛 ENOENT) -- U3: release 流水线端到端产物实际包含 builtin 模板(package-templates → build-cli → 二进制)——仅静态分析,config repo 私有/无法本地跑通 -- U4: opencode-dag-config 仓库根目录仅含 `*.yaml`(若含 `.yml`/子目录会被 glob 与打包三层一致静默丢弃,见 L7) -- U5: Windows 上 xdg-basedir 对 `Global.Path.config` 的实际回退(提示词仅声明 macOS/Linux 行为,运行时代码静态一致但 Windows 分支未验证) - -## 3. summary - -核心逻辑正确:resolve 三级回退嵌套(scope 外循环 × extension 内循环)、list 去重/排序与 resolve 优先级严格一致、readWorkflowSpec builtin 分支(缺失报错/坏 YAML → `workflowSpecParseError`)与文件分支同形、typeof 守卫在 dev/release 双环境语义正确、generate.ts 路径拼接与 JSON.stringify 无注入面、release-fork.yml 的 nullglob 守卫与 needs 链(push 触发不中断)均确认无误;40 项相关测试与 typecheck 全绿,无 CRITICAL。已修复项(ReferenceError 守卫、Entry 去重、M4 空 glob、重复下载删除)确认修复正确无回退,且探索波声称的 `command/index.ts` Default 枚举缺口经查该文件不存在,已证伪。主要风险集中在 H1(Windows 路径形态,同文件有 cygpath 先例)、H2(builtin 零测试 + 被删自检无替代)与 M3/M4/M5(builtin 路径不可回填、提示词两级表述、锁的 ENOENT/陈旧锁缺口);建议合并前至少处理 H1、H2 与 M3,其余为文档与打磨项。 \ No newline at end of file diff --git a/.opencode/.dag-specs/review-parts/review-robustness.md b/.opencode/.dag-specs/review-parts/review-robustness.md deleted file mode 100644 index 6bd1cea978..0000000000 --- a/.opencode/.dag-specs/review-parts/review-robustness.md +++ /dev/null @@ -1,50 +0,0 @@ -所有关键证据已收集并验证(测试 14/14 通过、resolve 调用面确认、命令注册链确认、draft.update 创建语义确认)。以下是运行时健壮性审查报告。 - ---- - -# PR #167 运行时健壮性审查 - -## 1. Findings - -| # | Severity | Title | Description | Evidence | Recommendation | -|---|----------|-------|-------------|----------|----------------| -| F1 | **HIGH** (unverified 行为) | Windows 构建 `DAG_TEMPLATES_DIR` 缺 cygpath 转换,1/3 平台发布版可能静默无内置模板 | `Extract Templates` 将 `$GITHUB_WORKSPACE/dag-templates-src` 裸写入 GITHUB_ENV;同一文件 22 行前的 models.dev step 对 `$RUNNER_TEMP` 显式 `cygpath -m`(说明作者已知 MSYS/原生路径混用坑),此步却无转换。若 Windows runner 下 Bun 无法解析该路径 → glob 0 模板 → 构建成功、发布照发、builtin 静默为空,仅 build 日志一行 "0 templates" | `.github/workflows/release-fork.yml:175` vs `:145-147` | 与 models.dev 同款防御:`DAG_TEMPLATES_DIR="$(cygpath -m "$GITHUB_WORKSPACE/dag-templates-src")"`,或改用原生 `${{ github.workspace }}` 上下文 | -| F2 | MEDIUM | builtin 注入缺失仅静默降级,无任何显式失败门禁 | env 存在但 glob 0(Windows 路径 bug、config repo 无 yaml、tar 空)→ `JSON.stringify({})` → 二进制无 builtin,release 照常成功。`generate.ts:55` 日志不参与 CI 判定。审查准则 3 的答案是:**静默降级,无显式报错路径**(与 models.dev `:158` warning 同构,属团队既有模式) | `script/generate.ts:51-56`、`release-fork.yml:87` | 在 package-templates job 或 build-cli 增加显式校验 step:模板数 > 0 否则 fail-loud(空 config repo 时发布中止而非静默发无 builtin 版) | -| F3 | MEDIUM | `/dag-template-update` 孤儿锁无恢复路径 | 锁用 `mkdir .dag-update.lock` 原子判定,但 prompt 无 mtime/age 检测、无强制清除、无"锁陈旧则接管"路径。agent 进程崩溃/被杀后锁永久残留,后续更新永远停在"another update is already running",恢复只能靠用户手动 rmdir(提示词未指导) | `dag-template-update.txt:67-78` | 补充锁龄检测(如超过 N 分钟视为陈旧,允许接管)或明确指导用户删除锁目录的恢复步骤 | -| F4 | MEDIUM | 锁的父目录前置条件缺失:全新机器 ENOENT 误判 | 锁路径为 `/workflows/.dag-update.lock`,目录创建指令(`:97`)位于 Failure handling 段,晚于锁步骤(`:70`"before downloading or merging")。全新机器 `/workflows` 不存在时 `mkdir` 报 ENOENT 而非 EEXIST,agent 可能误判"另一更新进行中"而停止 | `dag-template-update.txt:70,72,97` | 明确顺序:先 `mkdir -p /workflows` 再取锁;并将 ENOENT 与 EEXIST 的判别写进提示词 | -| F5 | MEDIUM | builtin 新功能零测试覆盖,模板契约校验随旧测试删除而消失 | `builtinTemplates()`、resolve builtin 兜底、list builtin 合并、`isBuiltinPath/builtinName`、`readWorkflowSpec` builtin 分支、`searchedScopes` 提示全部无测试(测试环境守卫返回 `{}`,无注入钩子)。旧 "repository's own workflow library" 测试(模板 `depends_on`/`prompt_template.id` 完整性校验)删除后,config repo 模板与本仓库 CI 完全脱钩 | `test/dag/dag-workflows.test.ts`(14 测全过但无 builtin 用例)、`dag-workflows.test.ts:161-187` 删除块 | 在 config repo 加独立 CI 校验 spec 结构;本仓库可加 `DAG_TEMPLATES_DIR` 指向 fixture 的构建期注入测试(generate.ts 可直接单测) | -| F6 | MEDIUM | dag-flow.txt 与运行时三级 scope 及实际模板名漂移 | prompt 只描述 two scopes(`:13`)、称 global "curated by the opencode-dag-config repo"(`:14`)、指名 4 个**本 PR 已删除**的提交模板(`:17-20`)。开箱即用用户(未跑 update、无全局目录)的模板只在 builtin 层,但 prompt 引导的名字与二进制实际内容(构建时从 config repo HEAD 动态取)**零同步校验**——config repo 改名即解析不到 | `dag-flow.txt:13-20` | 补 builtin 层描述;删除/弱化固定模板名(改为"按 list() 输出选择");构建时校验 dag-flow.txt 提及名 ⊆ builtin 名或删名 | -| F7 | MEDIUM | 供应链 pin 缺失:release 与 update 均取 config repo HEAD | `actions/checkout` 无 `ref`(release-fork.yml:73-77)、codeload URL 固定 `refs/heads/main`(dag-template-update.txt:32)。任何推送到 config repo 的提交(包括恶意或意外)立即进入所有新发布二进制,且内置模板信任级声明弱(workflows.ts:12-14 仅说 "curated") | `release-fork.yml:76`、`dag-template-update.txt:32`、`workflows.ts:12-14` | 至少 pin tag/commit 并在 workflow 注明;信任边界文档(builtin 与 dag.jsonc 同级)显式写入说明 | -| F8 | LOW | 备份文件 `.bak-*` 永久累积 | 每次 overwrite 生成一个带时间戳备份,无清理策略、无数量上限。已被 `list()` 的 EXTENSIONS 过滤不污染库列表,但长期更新磁盘持续膨胀 | `dag-template-update.txt:62-65` | 补充保留策略(如保留最近 N 份)或提示用户清理 | -| F9 | LOW | 锁重试次数未具体化 | "wait briefly and retry a few times"(`:75`)由 agent 自由裁量,无明确次数/间隔,行为不可复现 | `dag-template-update.txt:75-76` | 给出具体值(如 3 次 × 2 秒) | -| F10 | LOW | list 展示的 `builtin://name` 路径不可回填且报误导性错误 | 用户把列表路径回填 spec_path → `path.resolve` 折叠 `//` → 非 builtin 分支 → 报 "must be a .yaml or .yml file"(:416-417),而非提示用裸名 | `workflows.ts:58,131-133`、`tool/workflow.ts:415-418` | 路径分支对 `builtin://` 前缀给出"请用裸名"提示 | -| F11 | LOW | builtin 内容绕过 1MB 大小检查 | `MAX_WORKFLOW_SPEC_BYTES` 检查仅文件分支(:376-380),builtin 分支(:360-370)直接解析。构建期信任可接受,但 config repo 若被塞大文件 → 二进制膨胀 + 无防护 | `tool/workflow.ts:360-370` vs `:376-380` | 构建期 generate.ts 对模板体积设上限即可 | -| F12 | LOW | 空库消息中 builtin 提及为死代码 | `searchedScopes` 在 builtin map 非空时附加 builtin 文案,但 map 非空时 `list()` 必然含 builtin 条目 → `entries.length === 0` 永不成立 | `tool/workflow.ts:142-147,395-399` | 删除条件或接受为无害冗余 | -| F13 | LOW | build-cli checkout 未 pin `github.sha` 而 release `--target` 锚定 sha | workflow_dispatch 下默认检出分支 tip,构建期间新 push → 二进制与 tag 锚点不一致(低概率) | `release-fork.yml:124-126` vs `:264` | checkout 加 `ref: ${{ github.sha }}` | -| F14 | LOW | `.yml` 三处不一致:运行时支持、构建/打包/更新只认 `.yaml` | `EXTENSIONS` 含 `.yml`(workflows.ts:33),但 generate.ts glob(:51)、release 打包(:83)、update prompt 全只取 `*.yaml`;`\\.ya?ml$` 的 `?` 是死代码 | `workflows.ts:33`、`generate.ts:51-52` | 统一为 `.yaml` 或补齐 `.yml` | - -**已修复项确认**(无回退):ReferenceError 守卫正确(`typeof` 语义,workflows.ts:54);Entry 去重正确(seen-map,:104-120);M4 空 glob 正确(`shopt -s nullglob` + 数组守卫,release-fork.yml:82-88);重复下载 step 已删。**路径穿越检查**:`resolve()` 唯一调用方经 `isName`(拦截 `/`、`\`、extname、控制字符、`.` 开头)→ `path.join` 无穿越面;builtin map key 来自构建期 glob 文件名(非递归)→ 安全;`builtinName()` 仅作 map 索引与错误文案,无 fs 操作 → 安全。**parseMeta 容错**:恶意 YAML 只影响 title/nodes 元数据显示(类型守卫,:162-165),start 路径 fail-loud(workflow.ts:364-368)→ 满足"恶意 YAML 只影响元数据解析"。**下载/解压失败**:prompt 强制原文报错且停(:94-96),内容级 verify 兜底(:80-90)→ 满足准则 5。**失败传播**:package-templates 任一失败(clone/tar/upload)→ build-cli、release 全部 skip → run failed、无 release 产出 → fail-loud 中止正确。 - -## 2. Unverified Claims - -1. **Bun@Windows 对 `D:\a\...` 风格 `DAG_TEMPLATES_DIR` 的 `Bun.Glob.scan({cwd})` 解析行为** —— 决定 F1 实际影响;无本地环境可验证 -2. **GitHub Actions windows runner 的 bash 中 `$GITHUB_WORKSPACE` 实际形态**(`D:\a\...` 原样 vs MSYS 转换)—— F1 前提 -3. **opencode-dag-config 仓库根目录布局**:顶层 `*.yaml` 假设、模板名集合、与 dag-flow.txt 所列 4 个名字(design-decision-loop 等)是否匹配 —— F6 实害 -4. **node 构建产物(build-node.ts:23 未注入 define)是否属用户分发路径** —— 若 npm 分发则这些用户无 builtin -5. **release 二进制实际包含 builtin 模板** —— 无法本地构建验证(config repo 私有/外网),仅静态确认 define 链 -6. **xdg-basedir 在 Windows 的实际回退路径**(dag-template-update.txt:22-24 仅声明 macOS/Linux)—— update 命令写入位置与运行时读取位置(workflows.ts:145)一致性 - -## 3. Failure Scenarios - -| Scenario | Impact | Likelihood | -|----------|--------|------------| -| Windows runner `DAG_TEMPLATES_DIR` 不可解析 → 3 平台中 windows 版静默无内置模板,发布照常成功 | Windows 用户开箱即用无 curated 模板,无任何失败信号 | MEDIUM(cygpath 不对称是强信号,但 Bun@Windows 行为未证) | -| config repo 克隆失败 / tar 失败 / artifact 上传失败 | 整条 needs 链(build-cli + release)skip,run 失败、无 release 产出 —— 失败传播正确、fail-loud | LOW(网络/瞬时故障,手动重试即可) | -| config repo 空或无 `*.yaml` | warning + 空 tar → 全平台二进制无 builtin,release 照发(仅日志可见) | LOW(作者自控仓库) | -| `/dag-template-update` 执行中 agent 进程崩溃 → 孤儿锁 | 所有后续更新永久阻塞,需用户手动 rmdir 恢复;无提示指导 | MEDIUM(长任务中断概率非零) | -| 全新机器执行 `/dag-template-update` | 锁 mkdir ENOENT 被误判为并发冲突而中止更新 | MEDIUM(prompt 顺序歧义) | -| config repo 模板改名/损坏 YAML | 损坏 → list 容错、start 报清晰错误(✓);改名 → dag-flow.txt 引导的名字解析不到,agent 走兜底多耗一轮 | MEDIUM(跨仓库契约无门禁) | - -## 4. Summary - -代码层健壮性整体良好:运行时三级解析/去重/容错/路径安全全部验证通过,release 失败传播是 fail-loud 中止(正确行为),已修复的 4 项确认无回退。主要风险集中在**边界静默性**:Windows 路径转换缺失(F1,修复成本一行)与 builtin 注入缺失无 CI 门禁(F2)会让发布版无声降级;`/dag-template-update` 作为纯 prompt 命令缺乏孤儿锁恢复与目录前置处理(F3/F4),崩溃恢复体验脆弱。测试缺口(F5)与 dag-flow.txt 模板名漂移(F6)是中等风险,需 config repo 侧 CI 或构建期校验补齐。无 CRITICAL 项,建议 F1/F2 修复后合并。 \ No newline at end of file diff --git a/.opencode/.dag-specs/review-parts/review-style.md b/.opencode/.dag-specs/review-parts/review-style.md deleted file mode 100644 index 96cb91c804..0000000000 --- a/.opencode/.dag-specs/review-parts/review-style.md +++ /dev/null @@ -1,41 +0,0 @@ -## 风格与惯例审查报告(PR #167) - -审查范围:`git diff origin/dev...HEAD`(4 commits)全部 TS/TSX 与 YAML 变更。已核实:40 个 dag 相关测试全部通过(`bun test dag-workflows workflow-tool`,0 fail);验证了 explore 波的关键声明。 - -### 1. findings - -| # | severity | 标题 | 说明 | evidence | 建议 | -|---|----------|------|------|----------|------| -| 1 | **HIGH** | `DAG_TEMPLATES_DIR` Windows 路径未转换,同文件内 cygpath 先例被遗漏 | build-cli 的 Extract Templates 步骤把 POSIX 形 `$GITHUB_WORKSPACE/dag-templates-src` 直接写入 `GITHUB_ENV`;同一文件 30 行前 models.dev 步骤对 `$RUNNER_TEMP` 显式做了 `cygpath -m` 转换(作者已证明知道此坑)。Windows runner 的 bash 下 `GITHUB_WORKSPACE` 为 `/d/a/...` 形态,原生 Bun 进程读取该路径若失败 → glob 0 个模板 → **Windows 发布版静默丢失全部 builtin 模板**(降级为两级 scope,无任何报错) | `.github/workflows/release-fork.yml:175` vs `:146-147` | 与 models.dev 步骤对齐,写 env 前对路径做 `cygpath -m` 转换(非 Windows 下幂等) | -| 2 | **MEDIUM** | `parseMeta` 假 async + 冗余 Promise 链包裹同步解析 | `Bun.YAML.parse` 是同步操作,却被 `Promise.resolve(text).then((value) => Bun.YAML.parse(value)).catch(() => undefined)` 包进 Promise 链(pre-PR 的 `.text().then(parse)` 形状遗留物)。违反 AGENTS.md「同步解析、校验应保持同步」精神,并强制 `builtinEntry`/`resolve`/`list` 无谓地携带 async/await。全 src 无此包裹同步解析的先例(`Promise.resolve` 在 src 中仅用于 sync-callback 桥接) | `packages/opencode/src/dag/workflows.ts:158-161` | parseMeta 改为同步函数(`Effect.try` 或 `Promise.resolve().then(...).catch(...)` 单链),builtinEntry 随之同步,调用点去掉 `await` | -| 3 | **MEDIUM** | `/dag-flow` 提示词停留在"两 scope",与本次 PR 的核心新增(builtin 第三级)脱节 | dag-flow.txt 本次 PR 内被修改(11 行 diff),但 :13 仍写"installed in **two scopes**";全局/项目顺序反列(L14-15 global 在前,实际解析 project 优先);builtin 层完全缺失——而**全新安装(未跑 update)时 curated 模板恰恰只存在于 builtin 层**。:16 "pick by name or path" 也有误导:`list()` 对 builtin 项展示的 `builtin://name` 路径含 `/` → `isName` 为 false → 落入 path 分支报 "must be a .yaml or .yml file"(workflow.ts:416-418),路径回填必然失败 | `packages/core/src/plugin/command/dag-flow.txt:13-16` | 补第三级 scope 说明(builtin 编译进二进制);路径选择措辞改为"按名选择",删除对 builtin 路径回填的暗示 | -| 4 | **MEDIUM** | builtin 全新增面零测试覆盖,且唯一真实 spec 校验测试被删除 | `resolve` 的 builtin 兜底、`list` 的 builtin 合并去重、`isBuiltinPath`/`builtinName`、`readWorkflowSpec` builtin 分支、`searchedScopes` 提示——全部无测试。测试环境 `typeof` 守卫恒返回 `{}`,无任何注入钩子。被删的 "repository's own workflow library" 测试是唯一的 `depends_on`/`prompt_template.id` 完整性守卫,删除后 config repo 模板完全脱离本仓库 CI | `packages/opencode/test/dag/dag-workflows.test.ts`(删除段);`workflows.ts:86-88,117-120` | 为 builtinTemplates 增加可注入的测试钩子(如通过 env/参数重载),补 resolve 兜底、list 遮蔽合并、searchedScopes 拼接的最小用例 | -| 5 | **LOW** | 过期注释:header 仍称"same two-level scope" | 本次 PR 把查找顺序改为三级(并更新了上方 bullet 列表),但 `Mirrors config.ts: same two-level scope` 未同步 | `packages/opencode/src/dag/workflows.ts:16-17` | 改为 three-level 或删除该句 | -| 6 | **LOW** | `Entry.content` 死字段 | `builtinEntry`(:135-137)设置 content,但 `readWorkflowSpec` 重新查询 `builtinTemplates()`(workflow.ts:361),全仓库无任何消费者;`list()` 输出也不含它 | `packages/opencode/src/dag/workflows.ts:43,135-137` | 要么让 readWorkflowSpec 消费 entry.content,要么删字段(或加注释明确为预留面) | -| 7 | **LOW** | README 死链/过时声明(PR 删文件未同步文档) | README.md:253、README.zh.md:227 指向已删除的 `.opencode/workflows/change-review.yaml`(404);README.zh.md:27 "仓库已经附带三类强约束参考图" 已不成立;:66-67 scope 表缺 builtin 层 | `README.md:253`、`README.zh.md:27,66-67,227` | 更新为 config repo 引用 + 三级 scope 表;删除死链接 | -| 8 | **LOW** | 锁语义两个 prompt 级缺口 | ① `mkdir /workflows/.dag-update.lock`:首次运行时父目录不存在 → ENOENT,提示词将其归因为"目录已存在=他人持锁",agent 会误判;② 陈旧锁无恢复路径(崩溃后 `.dag-update.lock` 永久残留,无 age 检测/手工清除提示) | `packages/core/src/plugin/command/dag-template-update.txt:67-78,97` | 锁步骤前明确"先创建 workflows 目录";补充陈旧锁的处理指引 | -| 9 | **LOW** | "pinned repository URL" 措辞不实 | 提示词称 URL 为 "pinned/fixed",但 `codeload.../zip/refs/heads/main` 未 pin tag/commit(release 流水线同),可复现性弱 | `dag-template-update.txt:28-29`;`release-fork.yml:83` | 措辞改为"固定分支 URL"或真的 pin commit | -| 10 | **NIT** | `\.ya?ml$` 中的 `?` 是死代码 | glob 只匹配 `*.yaml`(generate.ts:51,release 打包、update prompt 亦只认 `.yaml`),正则暗示 `.yml` 支持并不存在——三层 `.yml` 漂移是既有事实 | `packages/opencode/script/generate.ts:52` | 去掉 `?` 或统一 `.yml` 支持 | -| 11 | **NIT** | 私有 helper 重复显式返回类型标注 | `describe` 与 `parseMeta` 各自标注相同的内联返回类型 `Promise<{ title?: string; nodes?: number }>`;repo 风格倾向依赖推断 | `workflows.ts:150,158` | 删除显式标注(或提取共享 type alias) | - -**核查通过项**(非 finding): -- 命名一致性:`builtinTemplates`/`builtinEntry`/`builtinName`/`isBuiltinPath`/`BUILTIN_PREFIX` 前缀统一 ✓;`DAG_TEMPLATE_UPDATE_PROMPT`/`DagTemplateUpdateDescription` 与 dag-flow 同构 ✓ -- 导出模式:`export * as DagWorkflows`(workflows.ts:20)沿用既有 module shape;新 helper 全部 namespace-private 不导出 ✓ -- 类型纪律:导出函数显式返回类型、私有函数推断,边界正确;无 `any` ✓ -- 控制流:全部早退、无 else、const 优先 ✓;`searchedScopes` 对 `searchPaths()` 返回的新数组 push 安全 ✓ -- Effect 纪律:builtin 分支的 `Effect.fail(new Error(...))` 与文件分支(workflow.ts:374,377,412)逐字同形,遵循文件内既有惯例 ✓ -- `Array.fromAsync` 为仓库既有模式(5+ 处使用)✓;动态 import 无新增需求 ✓ -- dag-template-update.txt 结构(`##` 分节 + 加粗术语 + 失败处理段)与 review.txt/initialize.txt 房风一致,比 dag-flow.txt 的数字列表更贴近主流结构 ✓ -- 已修复项确认:typeof 守卫(workflows.ts:54)、seen 去重(:117-120)、空 glob 守卫(release-fork.yml:84-89)均修复正确;测试全部通过 ✓ - -### 2. unverified_claims - -- **U1**:Bun@Windows 对 POSIX 形 `DAG_TEMPLATES_DIR`(`/d/a/...`)路径的解析行为——决定 finding #1 是否真致 Windows 版静默丢模板(本地无法验证 Windows runner,需 CI 实测或 Windows 环境确认) -- **U2**:release 产出的二进制实际包含 builtin 模板——define 注入链已静态验证与 `OPENCODE_MODELS_DEV` 同构(生产先例),但本 PR 产物未做 `--version`/运行时 smoke 验证 -- **U3**:opencode-dag-config 仓库根目录只含 `*.yaml`(顶层)——若含 `.yml` 或子目录会被 glob/打包/提示词三层静默丢弃 -- **U4**:`/dag-template-update` 的写入目录(prompt 描述)与运行时读取目录(workflows.ts:145 `Flag.OPENCODE_CONFIG_DIR ?? Global.Path.config`)逐字一致——已对 flag.ts/global.ts 静态核对,但未实测 XDG_CONFIG_HOME 全路径矩阵 -- **已消解**:explore 波声称的 `command/index.ts:51` Default 枚举缺 `DAG_TEMPLATE_UPDATE` 条目——该文件不存在(`packages/core/src/plugin/command/index.ts` IO error),`Default.DAG_FLOW` 全仓库零命中,此声明为误报,不构成 finding - -### 3. summary - -整体风格纪律良好:命名前缀统一、导出模式与模块形状合规、早退/const/推断类型纪律一致、Effect 错误处理与文件内既有惯例同构,dag-template-update.txt 的 `##` 分节结构与仓库 prompt 房风吻合;40 个 dag 测试全部通过。主要问题在**一致性**而非风格本身:dag-flow.txt 未同步 builtin 第三级(其 "two scopes" 描述在全新安装下直接误导 agent)、`parseMeta` 的假 async 是 pre-PR 形状的遗留物、README 死链未随模板删除更新。唯一 HIGH 是 release-fork.yml 的 Windows 路径转换遗漏——同一文件内已有 cygpath 先例,属可低成本修复的静默失败风险;builtin 零测试覆盖与 config repo 脱离 CI 校验是测试面最大缺口。 \ No newline at end of file diff --git a/.opencode/.dag-specs/review-parts/review-testability.md b/.opencode/.dag-specs/review-parts/review-testability.md deleted file mode 100644 index 12e53ad980..0000000000 --- a/.opencode/.dag-specs/review-parts/review-testability.md +++ /dev/null @@ -1,42 +0,0 @@ -# PR #167 测试与覆盖审查报告 - -**测试执行证据**(全部在 `packages/opencode` 内): -- `bun test test/dag/dag-workflows.test.ts` → **14 pass / 0 fail** ✓(34 expect 调用) -- `bun test test/dag/workflow-tool.test.ts` → **26 pass / 0 fail** ✓(80 expect 调用) -- `bun run typecheck`(tsgo --noEmit)→ **通过,零错误** ✓ -- 额外:本地实测执行 release-fork.yml 的 nullglob 打包片段(空 glob → warning + rc=0 + 合法空 tar,M4 修复行为确认) - -## 1. findings - -| # | severity | title | description | evidence | recommendation | -|---|----------|-------|-------------|----------|----------------| -| F1 | **MEDIUM** | 删除仓库自检测试后,模板完整性校验失去全部回归保护 | 被删测试(旧 L163-189)是唯一锚点:`resolve("change-review")` → StartSpec decode → 每个 `prompt_template.id` 存在 → `depends_on` 引用有效。删除是**必要**的(98e4c0624 删除 4 个模板后旧测试会因 `entry!.path` 抛错),但**无替代**:config 仓库在 repo 外且无 CI 挂钩,"air-gapped installs ship the curated templates"(workflows.ts:12-14)的声明没有任何自动化校验 | `git diff` dag-workflows.test.ts 旧 L163-189(仅删除,无新增);`rg builtin packages/opencode/test` 零命中 | 在两个仓库之一补锚点:config repo 加 CI(对每个 `*.yaml` 跑 StartSpec decode + prompt-template 引用校验),或本 repo 用 fixture spec 跑一次真实 decode 冒烟 | -| F2 | **MEDIUM** | builtin 三级 scope 全分支零测试覆盖——无注入钩子 | `builtinTemplates()`(workflows.ts:51-56)typeof 守卫在测试环境恒返回 `{}`,`declare const` 运行期无法注入。resolve builtin 兜底(workflows.ts:86-88)、list builtin 合并/遮蔽(workflows.ts:117-120)、`readWorkflowSpec` builtin 分支(workflow.ts:359-370,含缺失 fail 与 YAML 错误两路径)、`searchedScopes` builtin 提及(workflow.ts:395-399)在 40 个测试中全部不可达。project>global 遮蔽有测试(dag-workflows.test.ts:88-94),第三级的优先级契约零断言。**部分缓解**:14+26 个测试全部经守卫分支运行,隐式证明了无 ReferenceError | dag-workflows.test.ts:48-162 无任何 builtin 用例;workflow-tool.test.ts:1038-1184 无 builtin 引用 | 抽取注入面:将 `builtinTemplates()` 改为可注入/可 mock(或单测纯函数 `isBuiltinPath`/`builtinName`/`builtinEntry` + 带 stub map 的 resolve/list 用例),中成本关闭主要缺口 | -| F3 | **LOW/MEDIUM** | generate.ts 的 DAG_TEMPLATES_DIR 加载零测试,且存在测试约束 | `loadDagTemplatesData`(generate.ts:44-57)两个分支(env 未设→`"undefined"` 字面量;env 设→glob `*.yaml`→JSON)均无测试(`rg DAG_TEMPLATES_DIR|dagTemplatesData` 在 test/ 零命中)。关键路径"`"undefined"` 字面量 → define → typeof 守卫"(dev 运行依赖它)无回归保护。约束:import generate.ts 会触发顶层 `fetch(models.dev/api.json)`(generate.ts:24-26),测试需同时设 `MODELS_DEV_API_JSON` + `DAG_TEMPLATES_DIR` + 动态 import——可行但脆弱,解释了缺失 | generate.ts:44-59;`rg -n "DAG_TEMPLATES_DIR" packages --include='*.test.ts'` → 0 | 补一个双 env + 动态 import 的往返测试;或至少在 review 记录中显式接受该风险 | -| F4 | **LOW** | release-fork.yml bash 逻辑:nullglob 守卫**本地实测验证**,GITHUB_ENV 注入链有 Windows 转换缺失风险 | nullglob 片段(release-fork.yml:82-90)本地实测:有文件 → cp 执行 rc=0;空 glob → warning + 不执行 cp + tar 空目录 rc=0(M4 修复确认无 missing-operand 失败)。但 `DAG_TEMPLATES_DIR=$GITHUB_WORKSPACE/dag-templates-src`(release-fork.yml:172-175)无 `cygpath -m` 转换,而同文件 models.dev 步骤(release-fork.yml:145-147)显式转换——windows runner bash 下 GITHUB_WORKSPACE 为 POSIX 形 `/d/a/...`,原生 Bun 进程可能无法解析 → **Windows 发布版静默丢失 builtin 模板**(glob 空不报错)。本机无 Windows 无法验证 | release-fork.yml:82-90(实测)/ 172-175(静态);本地 bash 复现输出 rc=0 无报错 | 与 models.dev 同款加 `cygpath -m` 转换(或用 `${{ github.workspace }}` 原生形变量);最低限度在 release 后 smoke 断言二进制内置模板数 | -| F5 | **LOW** | M1 并发锁:现有锁测试与 `/dag-template-update` 的锁无关,update 锁结构性不可测 | dag-workflow-lock.test.ts:8-58 覆盖的是 `Dag.Service.extend` 同 workflow 串行化(mock DagStore 25ms sleep + `maxActiveReads===1`),是运行时锁,本 PR 未动。update 命令的 `.dag-update.lock`(dag-template-update.txt:67-78)是 **prompt 级契约**——无代码执行它,测试环境没有任何自动化手段验证(mkdir 原子性、重试、清理、陈旧锁无恢复均为 agent 行为) | dag-workflow-lock.test.ts 全文(未变更文件);dag-template-update.txt:67-78 | 接受现状并显式声明:该锁只有靠 agent 按 prompt 执行 + 人工 review;或未来把锁逻辑下沉为可测代码 | -| F6 | **LOW** | 测试纪律:符合"测真实实现"要求,无逻辑复制 | resolve/list 测试用真实 tmpdir fs fixture + 真实函数(无复制查找逻辑);workflow-tool 用真实 tool execute + 真实文件;锁测试用 `Layer.mock`(AGENTS.md 认可模式)。fixture 辅助函数(spec/savedSpec)是输入构造,非实现副本 | dag-workflows.test.ts:13-30, 71-162;workflow-tool.test.ts:1058-1059 | 无动作 | -| F7 | **LOW** | generate.ts:52 `\.ya?ml$` 的 `?` 是死代码;`.yml` 在打包链被静默丢弃 | glob 仅 `*.yaml`(generate.ts:51),`?` 分支不可达;release 打包 `dag-config/*.yaml`(release-fork.yml:83)与 update prompt 同。而运行时 `EXTENSIONS` 含 `.yml`(workflows.ts:33)——config repo 若放 `.yml`,对二进制与打包均不可见,依赖 repo 布局约定(U2) | generate.ts:51-52 | 三处统一为 `.ya?ml` 或显式文档化"config repo 只接受 .yaml" | -| F8 | **LOW** | builtin 内容绕过 1MB size 检查 | 文件分支有 size 检查(workflow.ts:376-380),builtin 分支无(workflow.ts:360-369)——构建期策展内容,风险低但属隐式信任面 | workflow.ts:359-370 vs 376-380 | 可接受,记录即可 | - -## 2. unverified_claims - -- **U1**:Windows runner 上 POSIX 形 `DAG_TEMPLATES_DIR`(`/d/a/...`)在原生 Bun 进程能否解析——若不能,Windows 发布版静默丢失 builtin 模板(release-fork.yml:172-175 vs :145-147 的 cygpath 不对称)。无 Windows 环境,无法本地验证。 -- **U2**:opencode-dag-config 仓库根目录只含 `*.yaml`(顶层)——若有 `.yml` 或子目录会被打包与注入链静默丢弃(仓库私有,无法查看)。 -- **U3**:`generate.ts` 返回的 `"undefined"` 字符串经 Bun.build define 注入后成为 `undefined` 关键字、触发 typeof 守卫——按 `OPENCODE_MODELS_DEV` 同构模式推断(生产已验证该机制),本地未跑 release 构建。 -- **U4**:release 产出的二进制实际包含模板——仅静态验证 define 注入链 + CI job 图(config repo 私有,本地无法复现 release 构建)。 -- **U5**:`/dag-template-update` 提示词行为(下载/合并/备份/锁/验证)无法自动化验证——纯 prompt 契约,只能靠 agent 执行后人工审计。 - -## 3. coverage_gaps - -| path | untested_scenarios[] | -|------|---------------------| -| `packages/opencode/src/dag/workflows.ts` | resolve 的 builtin 兜底命中(L86-88);list 的 builtin 条目合并/排序/被 project 与 global 遮蔽(L117-120);`builtinTemplates()` 守卫的"有值"正分支(L51-56,测试只走空分支);`isBuiltinPath`/`builtinName`(L126-133);`builtinEntry`/`parseMeta` 经 builtin 路径(L135-137, 158-167) | -| `packages/opencode/src/tool/workflow.ts` | `readWorkflowSpec` builtin 分支两条失败路径:内容缺失 fail(L362-364)、YAML 解析错误(L365-368);`searchedScopes` 追加 "the release's builtin templates" 的分支(L395-399,当前测试 env 下恒不触发) | -| `packages/opencode/script/generate.ts` | `loadDagTemplatesData` 两分支(L44-57):env 未设返回 `"undefined"`;env 设时 glob 收集 → name 去扩展名 → JSON 序列化往返 | -| `.github/workflows/release-fork.yml` | package-templates 空 glob 路径(本地 bash 实测过,非 CI 实测);Extract Templates 的 GITHUB_ENV 注入在 windows runner 的行为(U1) | -| 已删除测试的覆盖面(无替代锚点) | StartSpec decode 有效性;`prompt_template.id` 引用存在性;`depends_on` 完整性——模板迁到 config repo 后**任何地方**都没有自动化校验 | - -## 4. summary - -测试状态完全符合预期(14+26 pass、typecheck 干净),且测试纪律合规(测真实实现、Layer.mock 模式、无逻辑复制);nullglob 空 glob 修复经本地实测确认行为正确。主要问题是 F1/F2:第三级 builtin scope 是新行为契约却零测试覆盖,且删除仓库自检测试后模板完整性校验在两侧仓库都没有锚点——这是 PR 最大的测试缺口,建议优先补注入面测试 + config repo CI。release 流水线(F4/U1/U4)只能静态审查 + 依赖 Windows runner 与 config repo 的后续实证,属已知不可本地验证面,其中 Windows 路径转换缺失是最值得在合并前修复的低成本风险点。 \ No newline at end of file diff --git a/.opencode/batch-a-implement-manifest.md b/.opencode/batch-a-implement-manifest.md deleted file mode 100644 index d1cfc19fb8..0000000000 --- a/.opencode/batch-a-implement-manifest.md +++ /dev/null @@ -1,57 +0,0 @@ -# Batch A Implement — 图编排 Manifest - -## reference_template -`parallel-development-loop`(global,13 节点)——保护脊柱保留: -audit-module-wave(模块波本地审查,PASS|LOOP|BLOCKED)→ wire-modules(单一集成/提交所有者)→ simulate-wired-system(reasoner 纯逻辑推演)→ verify-wired-system(确定性验证)→ 三路 fresh review → arbitrate-final-review(唯一终审)→ finalize-delivery(仅 PASS 条件放行)。 - -## 任务注入 -`.scratch/batch-a/issues/01-08`(09 为 dev→main 收束票,CI 全绿后在图外执行)。 -规格依据:`.opencode/grill-batch-a/`(CONTEXT.md + ADR-0001~0004 + node-lifecycle-transitions.md v2,Round 2 doc 审核 PASS 冻结版,基线 dev@5330b15a9)。 - -## 展开(expand) -参考图的 develop-core / develop-adapters / develop-tests 三个泛模块槽替换为 8 个票据实现节点: - -| 节点 | 票据 | 依赖 | -|---|---|---| -| impl-q1 | 01 裁决旗清旗 | freeze-contract | -| impl-q2 | 02 送达门控 re-time | freeze-contract | -| impl-s5 | 05 锁一行超时 | freeze-contract | -| impl-flaky-stdout | 06 stdout 污染族 | freeze-contract | -| impl-flaky-ws | 08 workspace 计时 | freeze-contract | -| impl-q3 | 03 事件+guard 前移 | impl-q1(projector 写集串行) | -| impl-sdk | 04 SDK 再生 | impl-q3 | -| impl-flaky-share | 07 ShareNext 计时 | impl-flaky-stdout(同文件串行) | - -## 剪裁(prune_decisions) -| node | prune_reason | replacement_coverage | -|---|---|---| -| develop-core | 泛槽与已审计的票据分解不匹配 | 8 个票据节点按审计后写集分工,含测试切片(各票 TDD 自带) | -| develop-adapters | 同上 | 同上(03/04 覆盖 schema/SDK 适配面) | -| develop-tests | 测试切片并入各票 TDD | 每票先写失败测试再实现;review-tests 终审覆盖矩阵 | -| freeze-design | 设计已在图外冻结(两轮 doc 审核 PASS) | 改为 freeze-contract:只核验票据写集互斥并产出结构化契约,不重做设计 | - -## 写集互斥表 -- q1:projector 折叠侧 + dag 测试(清旗族) -- q2:runtime/loop.ts re-time 发起点 + 门控测试 -- q3:schema 事件定义 + EventManifest + dag.ts 命令路径 + projector handler + 测试 -- sdk:packages/sdk/js 生成物 + 消费者类型对齐 -- s5:dag.ts withWorkflowLock 包装层(唯一区域)+ 测试 -- flaky-stdout:test/cli/run + src/share/share-next.ts + 相关 fixture -- flaky-share:test/share 计时部分(06 已合入其依赖报告) -- flaky-ws:workspace sync 测试(+ 根因所需最小 src,须记录) -已知同文件异区:s5 与 q3 同 dag.ts(区域互斥:锁包装层 vs 命令路径);q1 与 q3 同 projector(已串行)。 - -## Git 纪律 -- 所有 impl 节点禁止任何 git 操作(add/commit/stash/branch/push) -- wire-modules 是唯一提交所有者(typecheck + 套件 + lint 全绿后单 commit) -- PR 由父会话在终审 PASS 后开(分支 feat/batch-a → dev) - -## 审查门禁义务 -arbitrate-final-review 必须审计本 manifest:每个 prune 有 prune_reason + replacement_coverage,缺任一禁止 PASS(fail-closed)。 - -## 续作记录(Continuation) -原图 dag_024a09546ffevmMvS3M6v0I1Av 于 audit PASS、wire-modules 提交 17f10f0ce 之后 terminal failed——两个 spawn 期配置错误:impl-q3({{freeze-contract}} 非直接依赖,已由 impl-q3b 替换并完成)与 verify-wired-system(replan 片段遗漏 input: repo)。 -终态不可逆 → 按续作合约起新图 batch-a-continue: -- reused_nodes:freeze-contract、impl-q1/q2/q3b/s5/flaky-stdout/flaky-ws/flaky-share/sdk、audit-module-wave(PASS)、wire-modules(提交 17f10f0ce)——全部完成且经审计,不重跑 -- 续跑尾部:verify-wired(修复 input 绑定)→ simulate-wired + 三路 review → arbitrate-final → finalize -- 尾部节点一律从真实仓库状态(git show HEAD + 票据 + grill 文档)取证,不注入可能为空的旧输出(fail-closed) diff --git a/.opencode/dag-prompts/arch-gate.md b/.opencode/dag-prompts/arch-gate.md deleted file mode 100644 index 3c45efc98d..0000000000 --- a/.opencode/dag-prompts/arch-gate.md +++ /dev/null @@ -1,46 +0,0 @@ -# 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 deleted file mode 100644 index 4bf5a6fc63..0000000000 --- a/.opencode/dag-prompts/code-explore.md +++ /dev/null @@ -1,43 +0,0 @@ -# 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 deleted file mode 100644 index 086387ba78..0000000000 --- a/.opencode/dag-prompts/config-explore.md +++ /dev/null @@ -1,42 +0,0 @@ -# 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 deleted file mode 100644 index df60f27d04..0000000000 --- a/.opencode/dag-prompts/implement.md +++ /dev/null @@ -1,49 +0,0 @@ -# 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 deleted file mode 100644 index 1068301496..0000000000 --- a/.opencode/dag-prompts/integration-test.md +++ /dev/null @@ -1,38 +0,0 @@ -# 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 deleted file mode 100644 index 18c5fff3c6..0000000000 --- a/.opencode/dag-prompts/patcher-assemble.md +++ /dev/null @@ -1,42 +0,0 @@ -# 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 deleted file mode 100644 index 35b11ec0af..0000000000 --- a/.opencode/dag-prompts/plan.md +++ /dev/null @@ -1,43 +0,0 @@ -# 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 deleted file mode 100644 index bd1329cd1e..0000000000 --- a/.opencode/dag-prompts/review-arch.md +++ /dev/null @@ -1,44 +0,0 @@ -# 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 deleted file mode 100644 index 5274098d6c..0000000000 --- a/.opencode/dag-prompts/review-logic.md +++ /dev/null @@ -1,44 +0,0 @@ -# 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 deleted file mode 100644 index 4be7bc3b52..0000000000 --- a/.opencode/dag-prompts/review-style.md +++ /dev/null @@ -1,44 +0,0 @@ -# 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 deleted file mode 100644 index 013aea1f37..0000000000 --- a/.opencode/dag-prompts/test-explore.md +++ /dev/null @@ -1,42 +0,0 @@ -# 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 deleted file mode 100644 index f66d26d43b..0000000000 --- a/.opencode/dag-prompts/verify.md +++ /dev/null @@ -1,41 +0,0 @@ -# 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/grill-batch-a/CONTEXT.md b/.opencode/grill-batch-a/CONTEXT.md deleted file mode 100644 index 50e96f9d01..0000000000 --- a/.opencode/grill-batch-a/CONTEXT.md +++ /dev/null @@ -1,50 +0,0 @@ -# CONTEXT — 批次 A 设计 grilling(D2/D3 + escalation_pending 生命周期 / S5 锁超时) - -状态记录文件:术语表 + 决策树 + 已定/未定。随 grilling 更新。 - -## 术语表(domain glossary) - -| 术语 | 当前定义(代码事实) | 问题 | -|---|---|---| -| `escalation_pending` | node 列。escalate projector 置 true;NodeStarted/NodeRestarted 清 false;updateNodeDeadline 清 false;**终态不清** | 语义未定:是"有未送达的 wake"还是"等待裁决"?(Q1) | -| `wake_reported` | node 列。wake 送达后 true;escalate 时 re-arm false | 与 escalation_pending 职责边界模糊(D2 根因) | -| adjudication(裁决) | 主 agent 对升级节点的处置:extend(replan 带新 timeout)/ restart / cancel | extend 写入在事件日志之外(D3) | -| re-time | extend 落地动作:nodeExtendTimeout 重算死线(now + new timeout) | 门控见 loop.ts:800(A1 cap gate) | -| delivery boundary(交付边界) | wake 投递条件:`escalationPending ∨ (timeoutExtensions>0 ∧ terminal)` | 依赖 escalation_pending 语义(Q1 决定后复查) | -| 升级循环 | watchdog 超时 → nodeTimeoutEscalated(count+1, pending=true, wake re-arm)→ 主 agent 裁决 | 每轮消耗一个 count,21×cap 兜底 | -| workflow lock | KeyedMutex per dagID,单许可、不可重入、**无超时**(S5) | 静默死锁风险 | - -## 设计公理(用户宏观原则,2026-08-07 确立,永久约束) - -1. **状态流转优先**:节点生命周期以显式状态机为真相源;轮询/watchdog 只能是「转移提议者」,不得充当监督权威或直接写状态。 -2. **错误即状态**:错误类别(error_class/trigger)是状态机的输入,由状态决定后续动作与 agent 的判断/处置依据(wake 文案承载)。 -3. **奥卡姆剃刀**:解法需要复杂策略(新错误类族、per-caller 语义分支、特殊化处理)= 重新思考的信号;优先砍机制而非加机制。 -4. **单一写权威**:节点状态一切变更走「dag 命令 → durable 事件 → projector」;直写行 = 破窗(现存唯一破窗 updateNodeDeadline,Q3 已决废除)。 - -## 决策树 - -- **Q1(根):escalation_pending 的生命周期契约** → ✅ **已定:(b) 裁决状态旗**。「节点正在等待主 agent 裁决」;由裁决写动作清(extend / restart / cancel)**或**由终态清(NodeCompleted/NodeFailed 清旗——死掉的节点无需裁决,结果走终态交付臂)。投递是 wake_reported 的本职,两旗职责正交(D2 病灶即职责混用)。→ 落 ADR-0001 -- **Q2:wake 未送达时 re-time 放不放行(D2)** → ✅ **已定:(a) 送达门控**(Round 2 机制修正:skip 合取项,非放行析取项)。`loop.ts:800` A1 skip 之外新增 Q2 skip 合取项 `(escalationPending && !wakeReported)`——已升级但未送达一律跳过 re-time(节点保留过期死线,watchdog `spawn.ts:111` 自续再升级,wake 照常投递,裁决必发生在送达之后)。放行条件等价于 `deadline≤now ∨ deadline=null ∨ (escalationPending ∧ wakeReported)`;不可写成放行析取项(会被 deadlineElapsed 析取吞没,对公共路径无效,cons-F1 旧病)。updateNodeDeadline 的 wake_reported:true 退化为无害 no-op。restart/cancel 不加门控(不改死线,不受 D2 威胁)。→ 落 ADR-0002(v Round 2) -- **Q3:deadline 变更入事件日志(D3)** → ✅ **已定:(a) 全量入日志**(Round 2 机制修正:guard 前移到命令层)。新增 durable 事件 `NodeDeadlineExtended`(nodeID + 新死线 + 裁决时 extension 计数),workflow 锁内发布,projector 幂等投影(event id 去重、replay-safe);`nodeExtendTimeout` 改为标准「命令 → 事件 → 投影」形态,直写废除。guard(running-guard + Q2 送达门控)在命令层、`events.publish` 之前持锁同步判,`0/1` 是命令同步 Effect 返回(不经 publish 链——projector 返回值在 `event.ts:256-258` 被丢弃,imp-F1);编排器经状态(终态 / 持续 escalation_pending)+ wake 观察拒绝(公理 ②)。成本:schema dag-event + DurableDefinitions + projector handler + dag.ts + 测试 + SDK event union 再生;无路由变化。→ 落 ADR-0003(v Round 2) -- **Q4+Q5(S5):锁超时语义与参数** → ✅ **已定:被 Q6 奥卡姆重构收编**。原案(类型化错误类 + per-caller 语义)被否——违反公理 3;最终形态:withLock 外层一行 `Effect.timeout("30 seconds")`,复用 TimeoutException,零新错误类、零 per-caller 改动、watchdog 零特殊化(自续间隔天然重试,计数只在成功时 +1)。→ 并入 ADR-0004 -- **Q6(收口):批次 A 最终采纳范围** → ✅ **已定:(A)**。Q1-Q3 照旧 + S5 一行超时 + **节点生命周期转移表**作为权威审查基准(此后引擎改版先对照表审)。入表既有小疵:watchdog 强杀时 promptSvc.cancel 先于 nodeFailed 事件(应改为事件后处置,不另开工单)。→ 落 ADR-0004/0005 - -**决策树状态:全部闭合(Q1✅ Q2✅ Q3✅ Q4/Q5→Q6 收编✅ Q6✅)。grilling 完成,共识达成。** - -## 交付物清单 - -- ADR-0001 escalation_pending 裁决状态旗契约 -- ADR-0002 送达门控 re-time(D2)—— **Round 2 修正**:skip 合取项(`loop.ts:800`),语义保留 -- ADR-0003 NodeDeadlineExtended 入事件日志(D3)—— **Round 2 修正**:guard 前移到命令层(非 projector 返回值),保留 durable 事件/schema/SDK 再生/回放一致性 -- ADR-0004 S5 奥卡姆版:一行超时(收编 Q4/Q5) -- ADR-0005 转移表基准(node-lifecycle-transitions.md **v2**:G1 skip 合取项 + T9 命令层 guard + guard 拒绝非转移说明) -- 实施顺序建议:Q1+Q2+Q3 一个 PR(escalation 生命周期闭环);**Q3 的 SDK event union 再生(`./packages/sdk/js/script/build.ts`)为强制伴随步骤**;S5 一行超时并入或单独小 PR;转移表随实施落地后从 grill-batch-a 晋级至 .dag-specs - -## 证据锚点 - -- D2:store.ts updateNodeDeadline(set escalation_pending:false + wake_reported:true);loop.ts:800 re-time gate -- D3:dag.ts nodeExtendTimeout 无 events.publish / seq bump;投影重建恢复旧死线 -- 终态不清:projector.ts escalate 置 true / NodeStarted:243、NodeRestarted:362 清 / NodeCompleted、NodeFailed 无清理 -- 边界谓词:节点级 `loop.ts:949-953`(`escalationPending ∨ (timeoutExtensions>0 ∧ terminal)`)/ 工作流级决策 `loop.ts:960-967`;summary 谓词:`store.ts:245-260`(`escalatedRows`:`escalation_pending=true ∧ status='running'`)。注:`loop.ts:906-912` 实为 workflow-terminal 清理,非交付边界谓词;DAG 运行时无 3 分钟阈值(watchdog 自续间隔 = `Math.max(1_000, timeoutMs)`,`spawn.ts:111`) -- S5:dag.ts:298-305(KeyedMutex 注释:单许可不可重入,持锁者崩溃/挂起 = 静默死锁);历史发现 S7 recovery INVENTED 推断同域 -- 五轮 review 归档:.opencode/promotion-review-round1/arbitrate.md diff --git a/.opencode/grill-batch-a/adr/ADR-0001-escalation-pending-semantics.md b/.opencode/grill-batch-a/adr/ADR-0001-escalation-pending-semantics.md deleted file mode 100644 index 4def8fc24f..0000000000 --- a/.opencode/grill-batch-a/adr/ADR-0001-escalation-pending-semantics.md +++ /dev/null @@ -1,25 +0,0 @@ -# ADR-0001: escalation_pending 是裁决状态旗(Q1) - -- 状态:已接受(批次 A grilling,2026-08-07,决策 (b)) -- 上游公理:设计公理 ①②④(CONTEXT.md) - -## 背景 - -`escalation_pending` 曾是无契约的混合旗:escalate 置 true、NodeStarted/Restarted 清、updateNodeDeadline 清、终态不清。被三个消费者依赖(交付边界、summary escalatedNodes、re-time 门控)。D2(wake 被 extend 偷吃)与「终态永挂旗」陷阱同源于语义未定义——投递状态与裁决状态共用一旗。 - -## 决策 - -`escalation_pending` 的唯一语义:**该节点正在等待主 agent 裁决**。 - -清除时机(仅两种): -1. **裁决写动作**:extend(NodeDeadlineExtended 投影)/ restart(NodeRestarted)/ cancel(NodeCancelled) -2. **终态**:NodeCompleted / NodeFailed 清旗——死掉的节点无裁决对象,其结果由交付边界终态臂 `(extensions>0 ∧ terminal)` 保证送达 - -投递(送达)是 `wake_reported` 的专职,两旗职责正交。 - -## 后果 - -- projector:NodeCompleted/NodeFailed handler 增补清旗(修复终态挂旗) -- NodeCancelled handler 清旗(cancel 即裁决) -- summary 谓词与边界谓词不变(语义对齐后自然正确) -- 测试:终态清旗回归测试 + cancel 清旗测试 diff --git a/.opencode/grill-batch-a/adr/ADR-0002-delivery-gated-retime.md b/.opencode/grill-batch-a/adr/ADR-0002-delivery-gated-retime.md deleted file mode 100644 index fac050b5b3..0000000000 --- a/.opencode/grill-batch-a/adr/ADR-0002-delivery-gated-retime.md +++ /dev/null @@ -1,69 +0,0 @@ -# ADR-0002: 送达门控的 re-time(Q2,D2 的修复) - -- 状态:已接受(批次 A grilling,2026-08-07,决策 (a);Round 2 机制修正,语义保留) -- 依赖:ADR-0001(旗子语义) - -## 背景 - -re-time 门控(loop.ts:800 的 A1 cap gate)的 pending 臂只认 `escalationPending`,不认送达状态。主 agent 因不相干原因 replan 时若修改了升级节点的 timeout,会在 agent **从未见过该次升级**的情况下完成裁决并消费未送达的 wake(`store.updateNodeDeadline` 置 `wake_reported:true`,`store.ts:331`)——违反 §5-3「wake 主 agent」。 - -## 决策(Q2,机制修正后) - -re-time 门控新增一个 **skip 合取项**:已升级但 wake 未送达的节点一律跳过 re-time——**裁决必发生在送达之后**成为结构不变式(G1)。 - -- **未送达的升级**(`escalationPending=true ∧ wakeReported=false`)→ 跳过 re-time:节点保留过期死线,watchdog 自续下个间隔再升级(计数照常爬向 G2 cap,偏安全侧),wake 照常投递 -- restart/cancel **不加**送达门控(不改死线,不受 D2 威胁) -- `store.updateNodeDeadline` 的 `wake_reported:true` 写入在门控生效后退化为无害 no-op(送达早已 true)——D2 被结构性消灭,无需改写入逻辑 - -## 机制(Round 2 修正:skip 合取项,非放行析取项) - -### re-time 是单路径,单门控点全覆盖 - -re-time 在整仓只有**一条触发路径**:`loop.ts:818` replan handler → `dag.nodeExtendTimeout`(`dag.ts:869-871`,全仓唯一调用点)→ `store.updateNodeDeadline`(`store.ts:321-343`,唯一的 deadline **延长**写)。`nodeExtendTimeout` 的全仓调用点仅 `loop.ts:818`;`deadline_ms` 的延长写点仅 `store.ts:331`(外加两个初始投影 `projector.ts:211`/`235` 的初始写,非延长)。watchdog(`spawn.ts:105` `makeDeadlineWatcher`)只提议 T8 `nodeTimeoutEscalated`(`spawn.ts:181`)与 T4 `nodeFailed`(`spawn.ts:160`),**从不**调用 `nodeExtendTimeout`;wake 投递路径(`loop.ts` 8 处 `tryDeliverWake`)从不写 deadline。**re-time 是单路径,单门控点即全覆盖。** - -### 为什么 Round 1 表述在公共路径无效(cons-F1) - -Round 1 把送达门控写成 re-time **放行条件的一个析取项**("已升级且已送达即放行"),结果是它被公共路径的 `deadlineElapsed` 析取项淹没。真实的公共 case 是 `[escalationPending=true ∧ wakeReported=false ∧ deadline≤now]`——升级发生在死线过期之后(`NodeTimeoutEscalated` 投影 `projector.ts:382-405` 不动 `deadline_ms`,死线仍在过去)。在此 case 上 Round 1 公式不改变行为(被 `deadlineElapsed` 析取项覆盖,依旧放行 re-time,悄悄清旗消费未送达 wake——D2 病灶);Round 1 只在罕见的 `[escalationPending=true ∧ deadline>now]` 上改变行为,而该 case A1 门控本就 skip(健康未来死线)。故 Round 1 在公共路径上无效。 - -### 精确编辑规格(loop.ts:800) - -现状(A1 cap gate,只认 `escalationPending`,不认送达): -```ts -if (!node.escalationPending && node.deadlineMs != null && node.deadlineMs > now) continue -``` - -修改后(A1 + 送达门控 G1,**新增 skip 合取项**): -```ts -if ( - (!node.escalationPending && node.deadlineMs != null && node.deadlineMs > now) // A1: 死线健康且无待裁决 → 跳过(防循环改值绕 cap) - || (node.escalationPending && !node.wakeReported) // Q2: 已升级但 wake 未送达 → 跳过(裁决必发生在送达之后) -) continue -``` - -`node.wakeReported` 已在 `NodeRow` 上(`store.ts:111`),replan handler 迭代的 `nodes` 即 `NodeRow[]`,无需新增读取。re-time 的放行条件等价于: - -`re-time ⟺ (¬escalationPending ∧ (deadline≤now ∨ deadline=null)) ∨ (escalationPending ∧ wakeReported)` - -两处皆为 **skip 条件的合取项**,不可改回放行析取项(会重蹈 cons-F1 旧病)。 - -## 验证(逐路径覆盖 + 为何解 cons-F1) - -| 节点状态 | Round 1 行为 | 修改后行为 | 结论 | -|---|---|---|---| -| 已升级未送达 `escalationPending=true ∧ wakeReported=false ∧ deadline≤now`(公共路径) | 因 `escalationPending=true` 而 A1 **不** skip → re-time 触发,悄悄清旗消费未送达 wake(D2 病灶) | 新增 `(escalationPending && !wakeReported)` 命中 → **skip**。节点保留过期死线,watchdog(`spawn.ts:111`)自续再升级,wake 照常投递 | **公共路径结构性修复(cons-F1)** | -| 已升级已送达 `escalationPending=true ∧ wakeReported=true` | 放行 → re-time | 放行 → 编排器 replan 裁决落地(T9) | T9 正常 | -| 未升级死线健康 `¬escalationPending ∧ deadline>now` | A1 skip | A1 skip 不变 | 无变化 | -| wake 投递路径 / watchdog 路径 | 不延长 deadline | 不延长 deadline | 结构上无法绕过(从不调 `nodeExtendTimeout`) | - -`store.updateNodeDeadline`(`store.ts:331`)的 `wake_reported:true` 写入:门控生效后只在已送达 case 触发(`escalationPending=true ∧ wakeReported=true`),此时 `wake_reported` 早已 true → no-op,ADR-0002 的「无害退化」成立。 - -## 后果 - -- `loop.ts:800` 一行条件修改(A1 + 新增 Q2 skip 合取项) -- 已知次生语义:投递滞缓时 extend 被推迟至送达后——agent 本就看不见未送达的升级,推迟即正确行为;投递有 bootstrap sweep + idle 边界兜底(G4 交付边界,节点级谓词 `loop.ts:949-953`、工作流级决策 `loop.ts:960-967`) -- 测试:未送达升级的 re-time 被拒(deadline 冻结 + watchdog 再升级);送达后同 replan 放行 -- 后 ADR-0003 落地建议:把送达门控的权威副本放进 `nodeExtendTimeout` 命令本身(命令持锁读行 `dag.ts:894` withWorkflowLock、skip 即不发事件),`loop.ts:800` 只留 A1 效率预过滤——两处同一谓词,无新机制(符合公理 ③) - -## 修订记录 - -- **Round 2(本修订,2026-08-07)**:机制重写。Round 1 把送达门控写成 re-time 放行条件的析取项,被 cons-F1 证实对公共路径 `[escalationPending=true ∧ wakeReported=false ∧ deadline≤now]` 无效(被 `deadlineElapsed` 析取项淹没)。本修订改为 `loop.ts:800` 的 **skip 合取项** `(escalationPending && !wakeReported)`,直接作用于公共路径。**语义保留**(裁决必发生在送达之后);状态保持 Accepted。伴随同步:`node-lifecycle-transitions.md` G1 重写、`CONTEXT.md` 决策树 Q2 行重写。 diff --git a/.opencode/grill-batch-a/adr/ADR-0003-node-deadline-extended-event.md b/.opencode/grill-batch-a/adr/ADR-0003-node-deadline-extended-event.md deleted file mode 100644 index 23008b25f9..0000000000 --- a/.opencode/grill-batch-a/adr/ADR-0003-node-deadline-extended-event.md +++ /dev/null @@ -1,107 +0,0 @@ -# ADR-0003: NodeDeadlineExtended 入事件日志(Q3,D3 的修复) - -- 状态:已接受(批次 A grilling,2026-08-07,决策 (a);Round 2 机制修正,语义保留) -- 上游公理:设计公理 ④(单一写权威) - -## 背景 - -`nodeExtendTimeout`(`dag.ts:869-871`)是 node 命令族唯一绕过事件日志的持久写(直写 `deadline_ms`,经 `store.updateNodeDeadline` `store.ts:321-343`)。后果: -1. 投影重建(replay)恢复延长前的旧死线——死线 = `now + timeout` 在裁决时刻计算,`now` 不在任何既有事件载荷中,分歧是结构性的 -2. 无 durable 审计(谁/何时/第几次延长) - -## 决策(Q3,机制修正后) - -新增 durable 事件 **`NodeDeadlineExtended`**;`nodeExtendTimeout` 改为标准「命令 → 事件 → 投影」形态,直写废除。 - -- **载荷**:nodeID + 新死线(绝对 ms)+ 裁决时的 extension 计数(审计) -- **guard 前移到命令层**:`nodeExtendTimeout` 持 workflow 锁(`dag.ts:894` withWorkflowLock)、在 `events.publish` **之前**同步读行判 guard——guard 结果是命令的**普通 Effect 同步返回值**(`0` = 拒绝 / `1` = 成功),直接给调用方 `loop.ts:818`,**不经 publish 链** -- **projector 幂等投影**(纯折叠,event id 去重,replay-safe):set `deadline_ms`、清 `escalation_pending`(ADR-0001 裁决清旗) - -## 机制(Round 2 修正:guard 在命令层,非 projector 返回值) - -### 为什么 Round 1 表述不可行(imp-F1) - -Round 1 设想「running-guard 移入 projector 前置校验,projector 返回 0 行 = guard 拒绝,经 publish 暴露给调用方」。**不可行**:durable publish 在 `event.ts:320-326` 是 `Effect.uninterruptible` + `db.transaction(...)`,其内部 `commitDurableEventInner` 对每个 projector 执行 `for (const projector of list) { yield* projector(committed) }`(`event.ts:256-258`)——**projector 的返回值被丢弃**。projector 是折叠纯函数(公理 ①/G3),在 durable publish 事务内、不可发新事件。故 projector 层的 0 行结果**无法**经 publish 回到调用方。imp-F1 成立。 - -但 durable publish 的**同步/事务**特性同时给出解法:命令在 `events.publish` **之前**、持锁做 guard,guard 结果是命令的**普通 Effect 返回值**(不经 publish 链)。`notify` 的 fire-and-forget(listener 扇出)只针对 listener,不影响 projector 事务与命令返回。 - -### 落地规格 - -**(i) schema — 新增 durable 事件 `NodeDeadlineExtended`**(`packages/schema/src/dag-event.ts`,模板 `NodeTimeoutEscalated` `:293-303`): -```ts -export const NodeDeadlineExtended = Event.define({ - type: "dag.node.deadline_extended", - ...options, - schema: { ...Base, nodeID: NodeID, deadlineMs: Schema.Number, timeoutExtensions: Schema.Number }, -}) -``` -注册进 `DurableDefinitions`(`dag-event.ts:309-329`,紧跟 `NodeTimeoutEscalated`)。 - -> **SDK 再生**:manifest 动 → 按 AGENTS.md 不变量跑 `./packages/sdk/js/script/build.ts`(durable 事件进 event union;无 HTTP 路由变化)。 - -**(ii) dag.ts — `nodeExtendTimeout` 改为标准命令(废除直写,`dag.ts:869-871`)**:guard 前移到命令层,发事件前同步判: -```ts -const nodeExtendTimeout = Effect.fn("Dag.nodeExtendTimeout")(function* (lock, dagID, nodeID, newDeadlineMs) { - const node = yield* store.getNode(dagID, nodeID).pipe(Effect.orDie) - if (!node || node.status !== "running") return 0 // running-guard:节点已终态(race-free:持 workflow 锁) - if (node.escalationPending && !node.wakeReported) return 0 // Q2 送达门控(ADR-0002):未送达不可 re-time - yield* events.publish(DagEvent.NodeDeadlineExtended, { - dagID, nodeID, deadlineMs: newDeadlineMs, timeoutExtensions: node.timeoutExtensions, - timestamp: yield* DateTime.now, - }) - return 1 // 成功 -}) -``` -- 返回的 `0/1` 是命令的同步 Effect 返回,直接给调用方 `loop.ts:818`,**不经 publish**——「guard 拒绝可观测」由命令层满足(非 publish 链) -- `store.updateNodeDeadline`(`store.ts:321-343`)**废除**(或降级为仅供 projector 内部复用) - -**(iii) projector — 新增 T9 投影(纯折叠,幂等,紧随 `NodeTimeoutEscalated` handler `projector.ts:382-405`)**: -```ts -yield* events.project(DagEvent.NodeDeadlineExtended, (event) => - db.update(WorkflowNodeTable) - .set({ - deadline_ms: event.data.deadlineMs, - escalation_pending: false, // ADR-0001:裁决清旗 - wake_reported: true, // 门控生效后为无害 no-op(送达早已 true) - seq: event.durable!.seq, time_updated: toMillis(event.data.timestamp), - }) - .where(and( - eq(WorkflowNodeTable.workflow_id, event.data.dagID), - eq(WorkflowNodeTable.id, event.data.nodeID), - inArray(WorkflowNodeTable.status, ["running"]), // replay-safe 幂等:终态行 0 行(benign) - )) - .run().pipe(Effect.orDie)) -``` -projector **不判 guard、不发事件、不返回行数**——符合公理 ①/G3。条件 `status='running'` 仅作 replay 幂等防护(崩溃重放时节点可能已终态,0 行 benign skip)。 - -**(iv) loop.ts:818 调用点 — 返回值语义不变**:`loop.ts:827-838` 现有 `written<0 / written===0 / written>0` 三分支逻辑无需改动;`written===0` 仍表示 guard 拒绝(命令同步返回,非 publish),handler 跳过新 watcher 安装。 - -### 拒绝如何对编排器可观察(公理 ②「错误即状态」) - -编排器**不**经 row-count 观察(那是 runtime 内部信号,供 `loop.ts` handler 决定 watcher 安装);编排器经**状态** + wake 观察: - -| 拒绝原因 | 命令返回 | 状态落点 | 编排器观察通道 | -|---|---|---|---| -| 节点已终态(running-guard 拒) | `0` | 节点 `completed/failed/...` | wake 经 T3/T4/T5 交付终态结果(`[DAG Node Result]`) | -| Q2 未送达(delivery-gate 拒) | `0` | `escalation_pending=true` 持续、`wake_reported=false` | watchdog 自续再升级 / wake 交付该次升级裁决请求(`[DAG Node Timeout]`) | - -## 一致性论证 - -- **公理 ①**(状态流转优先):projector 纯折叠,命令唯一写权威。 -- **公理 ②**(错误即状态):拒绝编码为终态 / 持续 `escalation_pending`,wake 承载。 -- **公理 ③**(奥卡姆):零新错误类、零 per-caller 分支;复用 wake + 终态交付。 -- **公理 ④ / G3**(单一写权威):`nodeExtendTimeout` 直写废除,改 `命令 → NodeDeadlineExtended → projector`;现存唯一破窗关闭。 -- **G5 锁域**:命令持锁期间 publish + projector 同步完成(`event.ts:320-326` 事务),guard race-free;命令本身的工作(publish+projector)非临界区内被禁的长时间 async 等待,外层一行 `Effect.timeout("30 seconds")`(ADR-0004)覆盖。 - -## 后果 - -- schema:`packages/schema` dag-event 定义 + `DurableDefinitions` 收录(durable) -- projector:新增 T9 handler;SDK event union 再生(AGENTS.md 不变量:manifest 动 = SDK 再生) -- dag.ts 重写 `nodeExtendTimeout`(guard 前移到命令层);`store.updateNodeDeadline` 直写废除;`loop.ts:818` 调用点返回值三分支不变 -- **回放一致性**:事件日志含 T9 → 投影重建恢复**裁决后**的死线与计数(T9 携带绝对死线载荷);直写时代的分歧(重建恢复旧死线)随 `updateNodeDeadline` 废除而消失 -- 测试:延长事件投影幂等(重放不双写)、re-time 门控(ADR-0002)联动、replay 一致性、guard 拒绝时命令返回 `0` -- 附带:watcher 的 re-time 感知未来可从轮询演进为订阅——门已打开,本 ADR 不要求 - -## 修订记录 - -- **Round 2(本修订,2026-08-07)**:机制重写。Round 1 设想「guard 移入 projector 前置校验,0 行 = guard 拒绝经 publish 暴露」,被 imp-F1 证伪——projector 返回值在 `event.ts:256-258` 被丢弃,无法经 publish 回到调用方。本修订把 guard 前移到**命令层**(`nodeExtendTimeout` 持锁、`events.publish` 之前同步判),`0/1` 是命令同步 Effect 返回(不经 publish 链);projector 退化为纯幂等折叠(`status='running'` 仅 replay 防护,行数有意忽略)。**保留不变**:durable 事件 `NodeDeadlineExtended`、schema 定义、`DurableDefinitions` 收录、SDK 再生、回放一致性约束。编排器经状态(终态 / 持续 `escalation_pending`)+ wake 观察拒绝(公理 ②)。状态保持 Accepted。伴随同步:`node-lifecycle-transitions.md` T9 投影效果与 replay 节同步、CONTEXT.md 决策树 Q3 行同步。 diff --git a/.opencode/grill-batch-a/adr/ADR-0004-lock-timeout-occams.md b/.opencode/grill-batch-a/adr/ADR-0004-lock-timeout-occams.md deleted file mode 100644 index 796e369ca6..0000000000 --- a/.opencode/grill-batch-a/adr/ADR-0004-lock-timeout-occams.md +++ /dev/null @@ -1,35 +0,0 @@ -# ADR-0004: workflow lock 超时——奥卡姆版一行 timeout(Q4/Q5 被 Q6 收编) - -- 状态:已接受(批次 A grilling,2026-08-07,Q6 决策 (A)) -- 上游公理:设计公理 ③(奥卡姆剃刀) -- 取代:Q4 原案(WorkflowLockTimeoutError 类型化错误类 + per-caller 语义,估算 50-100 行)——被否,违反公理 ③ - -## 背景 - -`withWorkflowLock` = `workflowLocks.withLock(dagID)(body)`(dag.ts:311-312),KeyedMutex 单许可、不可重入、无超时。已知危害:锁不释放 = 该 workflow 全部命令无限期静默排队。 - -关键事实(奥卡姆判决依据): -1. 编译期 witness 已防住已知死锁类(重入)——WorkflowLock 类型只有 withWorkflowLock 能铸造 -2. DB 为 effect-drizzle-sqlite 同步驱动,临界区是同步 DB 写——健康时亚秒级,不可能无限挂起;静默冻结只能由**未来回归**(临界区内引入异步等待)引入 -3. S5 是防御性/假想发现(五轮 review 无实际死锁观测),不是已观测 bug - -## 决策 - -**一行有界超时**,无其他机制: - -```ts -workflowLocks.withLock(dagID)(Effect.suspend(() => body(lockWitness))).pipe(Effect.timeout("30 seconds")) -``` - -- 复用 Effect 内建 `TimeoutException`——零新错误类 -- 超时覆盖「等锁 + 持锁全程」——临界区同步 DB 写下,30s 仍在临界区 = 已有大病,打断并大声报错即正确行为 -- 零 per-caller 改动:运行时 handler 的 guarded catchCause 自动接住记 warning;用户命令经既有错误通道冒泡(可重试);watchdog 自续间隔(escalateIntervalMs ≥1s)天然重试 -- watchdog **零特殊化**:extension 计数只在 escalate 成功时 +1,失败尝试不消耗 cap 预算——监督语义零损耗 -- 常量 30s 单一全局值(DEFAULT_WORKFLOW_CONFIG 或 dag.ts 具名常量),不分档——分档为未来预付复杂度 - -## 后果 - -- dag.ts 一行 + 一常量 -- 测试:spec 阶段定(导出常量跑 ~30s it.live 争用测试,或信任 Effect.timeout 仅测无争用路径不回归) -- 剩余风险(接受):错误类型是通用 TimeoutException 而非专属——日志与错误文本可辨识,无消费者需要程序化区分 -- dag.ts 注释强化:临界区内禁止异步等待(公理 ① 的锁域表述) diff --git a/.opencode/grill-batch-a/node-lifecycle-transitions.md b/.opencode/grill-batch-a/node-lifecycle-transitions.md deleted file mode 100644 index 9e142df277..0000000000 --- a/.opencode/grill-batch-a/node-lifecycle-transitions.md +++ /dev/null @@ -1,60 +0,0 @@ -# DAG 节点生命周期转移表 v2(权威审查基准) - -> **v2(2026-08-07,Round 2 修正)**:G1 re-time 门控按 ADR-0002 修正机制重写(`loop.ts:800` skip 合取项,非放行析取项);T9 投影效果与 replay 节按 ADR-0003 修正机制同步(guard 前移到命令层,projector 纯幂等折叠);新增说明「guard 拒绝非转移」。v1 的逻辑不变式(两旗正交、裁决必发生在送达之后)语义保留,仅机制表述与锚点修正。 - -来源:批次 A grilling Q6=(A) 交付物(`.opencode/grill-batch-a/CONTEXT.md`)+ repair-design 机制修正(cons-F1/imp-F1)。 -用途:此后 DAG 引擎任何改版,**先对照本表审**——改的是哪条转移、提议者是谁、投影效果与 agent 可见信号是否保持。 -标注:`[现状]` = 当前代码事实;`[目标]` = 批次 A 决议引入的变更(ADR-0001~0004)。 - -## 状态空间 - -**主状态**(workflow_node.status):`pending` / `queued` / `running` / 终态 `completed` / `failed` / `skipped` - -> **节点级无独立 `cancelled` 终态**(method-A 对齐实现):`NodeCancelled` 事件投影为 `status=failed` + `error_reason='cancelled via replan'`,取消语义经 error_reason 承载,行永不持有 `status='cancelled'`(`NodeStatus` 枚举无 CANCELLED,`getValidNextNodeStatuses` 对任何 from 均不返回 cancelled)。工作流级 `cancelled`(`WorkflowStatusProjection.cancelled`)是合法独立终态,与节点级无关。见 T5。 - -**running 扩展维度**(子状态): -| 维度 | 语义 | 契约来源 | -|---|---|---| -| `deadline_ms` | 绝对死线(admission 或裁决时刻计算) | [现状] | -| `timeout_extensions` | 本 attempt 升级计数(预算) | [现状] | -| `escalation_pending` | **裁决状态旗**:节点正在等待主 agent 裁决;由裁决写动作(extend/restart/cancel)或终态清除 | [目标] ADR-0001 | -| `wake_reported` | **投递状态**:升级 wake 是否已送达主 agent | [现状],职责与上旗正交 | - -## 转移表 - -| # | 从 | 事件(命令 → durable event) | 提议者 | 到 | 投影效果 | agent 可见信号 | 状态 | -|---|----|----|----|----|----|----|----| -| T1 | pending | nodeQueued | runtime spawnReady | queued | 置 admission 死线 | — | [现状] | -| T2 | queued | nodeStarted | runtime spawn | running | **清 escalation_pending + 重置 timeout_extensions=0**(新 attempt) | 子会话启动 | [现状] | -| T3 | running | nodeCompleted | 子会话结果 | completed | **清 escalation_pending**(终态无裁决对象) | 结果交付(终态交付臂) | [目标] ADR-0001 | -| T4 | running/queued | nodeFailed(reason + trigger) | 子会话失败 / watchdog cap / recovery | failed | **清 escalation_pending**;trigger 入 error 语义 | `[DAG Node Result]`/wake 承载 reason+trigger(错误即状态→处置依据) | [目标] ADR-0001 | -| T5 | pending/queued/running | nodeCancelled | replan cancel / workflow cancel | failed(cancelled) | **status=failed + error_reason='cancelled via replan' + 清 escalation_pending**(cancel 即裁决;节点级无独立 cancelled 终态,取消语义经 error_reason 承载) | 取消交付 | [目标] ADR-0001 | -| T6 | pending/queued | nodeSkipped | 依赖失败级联 | skipped | — | 跳过级联 | [现状] | -| T7 | failed | nodeRestarted | replan restart | running | 清旗 + 重置计数(新 attempt) | 重试 | [现状] | -| T8 | running | nodeTimeoutEscalated | **watchdog(提议者)** | running | timeout_extensions+1、escalation_pending=true、wake re-arm(wake_reported=false) | `[DAG Node Timeout]` wake(extend 或 cancel 的裁决请求) | [现状] | -| T9 | running | **NodeDeadlineExtended**(nodeID+新死线+裁决时计数) | 主 agent replan 带新 timeout → nodeExtendTimeout(持锁命令) | running | 移 deadline_ms、清 escalation_pending(裁决完成)、wake_reported=true(门控生效后为无害 no-op);幂等(`status='running'` replay 防护,event id 去重) | 无新信号(裁决本身是对 T8 wake 的应答) | [目标] ADR-0003 | - -> **guard 拒绝非转移**:T9 命令(`nodeExtendTimeout`,`dag.ts:894` 持锁)在 `events.publish` 之前同步判 guard——节点已终态(running-guard)或 Q2 未送达(delivery-gate,ADR-0002)时命令返回 `0`、不发事件。这不是一条转移行(无新事件、无状态翻转),拒绝编码为**状态**(终态 / 持续 `escalation_pending`),编排器经 wake + 终态交付观察(公理 ②)。故本表无单独的「deadline extension rejected」转移行。 - -## 门控与不变式 - -- **G1 re-time 门控(A1 cap gate + 送达门控,loop.ts:800)**:re-time 放行 ⟺ `deadline 已过期 ∨ deadline=null ∨ (escalationPending ∧ wakeReported)`。实现为**两个 skip 合取项**(ADR-0002 Round 2 修正):A1 跳过 `¬escalationPending ∧ deadline>now`,新增 Q2 跳过 `escalationPending ∧ ¬wakeReported`。两者均为 `continue`(skip)条件的合取项,**不可改回放行析取项**——放行析取项会被 `deadlineElapsed` 析取吞没,对公共路径 `[escalationPending ∧ ¬wakeReported ∧ deadline≤now]` 失效(cons-F1 旧病)。语义:未送达的升级不可被 re-time(裁决必发生在送达之后);被跳过的节点保留过期死线,watchdog(`spawn.ts:111` 自续间隔 `Math.max(1_000, timeoutMs)`)再升级,wake 照常投递 -- **G2 cap 上限**:extensions ≥ max_timeout_extensions → watchdog 提议 T4(trigger=timeout,reason 含计数);计数只在 T8 成功时 +1,失败尝试不耗预算 -- **G3 单一写权威**:一切节点状态变更走「dag 命令 → durable 事件 → projector」;行直写 = 破窗(`store.updateNodeDeadline` 直写由 T9 事件化废除,ADR-0003;guard 在命令层判,projector 纯折叠不返回行数) -- **G4 交付边界**:wake 投递条件 = `escalationPending ∨ (timeoutExtensions>0 ∧ terminal)`——两臂与旗子语义正交后自然正确。节点级谓词锚点 `loop.ts:949-953`,工作流级决策 `loop.ts:960-967`,summary 谓词 `store.ts:245-260`(`escalatedRows`:`escalation_pending=true ∧ status='running'`) -- **G5 锁域(ADR-0004)**:全部命令经 per-dagID KeyedMutex 串行 + 一行 `Effect.timeout("30 seconds")`(超时 = TimeoutException,guarded 记 warning 跳过,watchdog 自续重试);**临界区内禁止异步等待**。命令持锁期间的 publish+projector 同步事务(`event.ts:320-326`)属命令自身工作,非被禁的长时间 async 等待,guard race-free - -## watchdog 职责边界(转移提议者,非监督权威) - -- 只做两件事:过期且预算未尽 → 提议 T8(`spawn.ts:181`);预算耗尽 → 提议 T4(`spawn.ts:160`,+ 取消子会话) -- 不写节点行、不改旗子、不裁决、**从不调用 `nodeExtendTimeout`**(全仓 re-time 唯一路径是主 agent replan 经 `loop.ts:818`) -- 自续:每次提议后 sleep `escalateIntervalMs`(`= max(1s, nodeTimeout)`,`spawn.ts:111`)再读行——升级被裁决(T9/T7)则读到新死线安睡;未裁决则再升级,计数爬向 G2 -- **已知瑕疵(入表待修,不另开工单)**:cap 路径先 `promptSvc.cancel` 子会话、后发 nodeFailed——副作用先于转移事件;应改为事件后处置 - -## 错误即状态(trigger 分类 → agent 处置依据) - -nodeFailed.trigger ∈ { `timeout`(watchdog cap / 死线类), `exec_failed`(执行层), `verdict_fail`(契约层), … }——wake 文案按 trigger 承载处置建议(extend/restart/cancel/接受),agent 依据状态而非原始堆栈做判断。 - -## replay 一致性(ADR-0003 后果) - -事件日志含 T1-T9 全部转移 → 投影重建恢复**裁决后**的死线与计数(T9 携带绝对死线载荷);直写时代的分歧(重建恢复旧死线)随 `updateNodeDeadline` 废除而消失。T9 projector 幂等:`status='running'` 条件确保崩溃重放时终态行 0 行 benign skip,不双写。 diff --git a/.opencode/handoff-batch-b.md b/.opencode/handoff-batch-b.md deleted file mode 100644 index 09e58a754b..0000000000 --- a/.opencode/handoff-batch-b.md +++ /dev/null @@ -1,49 +0,0 @@ -# 批次 B 交接文档(新会话入口) - -> 本会话(批 A 全链路)已极长,批次 B 在新对话执行。引用本文件 + 下述证据路径即可开工。 - -## 起点状态(交接时) -- 基线:**dev**(批 B 规划基线 `3e8368f37`;每票开工前重新同步最新 `dev`) -- 批次 A 全闭环:Q1-Q6 引擎语义 + 接受期绑定校验 + flaky 根治(豁免清单已清零)+ 技术债清零(PR #185-#189 全合入) -- lint 棘轮:4852(CI 口径;本地 = CI − 10 生成物差,本地基线 ≤4842)——**只紧不松** -- 台账:.scratch/batch-a/issues/01-11(01-09 完成,10/11 closed) - -## 批次 B 四组票(审计后路由,证据已在案) - -### 组 1:U-1 / U-2 + Transport mid-stream-stall(一个 spec 三张票) -三个 abort-path 集成测试,同源同批。规格已收敛到 `.scratch/batch-b/abort-path-contracts.md`,按 01→02→03 逐票 /implement。 - -### 组 2:F3 / F4 测试卫生债 -小票直接做(无需 grill),每票新上下文 /implement。 - -### 组 3:O1 remote config last-known-good 缓存 -批 A 已完成“网络/响应体失败时 warn + skip”;剩余增量只有持久化 last-known-good。先完成 `.scratch/batch-b/issues/06-o1-lkg-spec.md` 的小规格,再按 07 实现;不得重新实现离线降级。 - -### 组 4:S7 recovery INVENTED 推断 ⚠️ -当前已有 ownership-lost 后暂停工作流的缓解,尚无用户态缺陷实证。**必须走 /diagnosing-bugs**:先建立一条确定性、快速、可红灯的复现命令;不能建立反馈回路则记录尝试并停止,不得先改生产代码。若红灯成立,另开修复票与新上下文。 - -## 证据路径(不用重新调查,票据直接引用) -- `.scratch/batch-b/evidence.md`:已追踪的稳定证据快照,含当前代码路径纠偏与验收边界;新 worktree 只依赖此文件 -- `.opencode/promotion-review-round1/*.md`、`.opencode/.dag-specs/evidence/*.md`:原始本地评审产物,当前未追踪,仅用于复核来源,不作为跨 worktree 前置 -- `.scratch/batch-b/abort-path-contracts.md`:U-1/U-2/mid-stream-stall 的已追踪规格;本地 OpenSpec 原件受 `.gitignore` 约束,不作为跨 worktree 前置 - -## 工程纪律(仓库铁律 + 本项目惯例) -- 分支:从最新 **dev** 切票据指定分支;生产 feature 用 `feat/**`,测试/规格债用 `test/**` 或 `docs/**`,均符合 branch-naming ruleset -- PR → dev:Typecheck 门禁;push dev 自动触发全量测试(Typecheck + Unit + E2E×2) -- 测试从包目录跑(packages/opencode 等),禁根目录;typecheck 用 `bun typecheck` 不用裸 tsc -- 每票一个新上下文会话执行(/implement 内含 /tdd),票间清上下文 -- HTTP API 路由若被触及:再生 SDK(./packages/sdk/js/script/build.ts)+ 更新 httpapi-exercise 场景 - -## 收束清单(批 B + 批 C 全部完成后) -1. dev 全量 CI 绿 → 一次性 dev→main 晋级 PR(四项门禁)→ 手动 release-fork -2. 分支一并清理(用户确认后手跑,dcg 拦 agent 删除): - ```bash - git worktree prune # 先清 opencode/* 残留 worktree(git worktree list 查路径) - git branch -d feat/dag-timeout-escalation feat/event-batch-publish feat/goal-pause-resume \ - feat/llm-request-timeout feat/session-runner-hotpath fix/config-offline-degrade \ - fix/deep-review-fixes review/dev-promotion <批B/C分支> - ``` -3. 台账惯例:新票记 .scratch/batch-b/issues/,完成翻 closed 附 PR/commit 实证 - -## 批 C(观测后再动,勿提前) -- P8 spawnReady O(ready×nodes):当前规模无实感,挂 /improve-codebase-architecture 巡检候选,疼了再做 diff --git a/.opencode/opencode.jsonc b/.opencode/opencode.jsonc index 60c261eb0e..b0f7d59447 100644 --- a/.opencode/opencode.jsonc +++ b/.opencode/opencode.jsonc @@ -2,7 +2,7 @@ "$schema": "https://opencode.ai/config.json", "provider": {}, "permission": {}, - "reference": { + "references": { "effect": { "repository": "github.com/Effect-TS/effect-smol", "description": "Use for Effect v4 and effect-smol implementation details", diff --git a/.opencode/workflows/GRAPH-ENGINEERING.md b/.opencode/workflows/GRAPH-ENGINEERING.md deleted file mode 100644 index 8acf46a8f7..0000000000 --- a/.opencode/workflows/GRAPH-ENGINEERING.md +++ /dev/null @@ -1,41 +0,0 @@ -# Graph Engineering workflow catalog - -GraphAgent treats these workflows as durable, executable reference topologies. -The parent agent selects the closest shape, injects the current task, and derives -the actual DAG while preserving its protected fail-closed gates. - -## Reference graphs - -| Workflow | Protected spine | Use it for | -|---|---|---| -| `design-decision-loop` | internal grill → reasoner → fresh audit → PASS-only finalization | Deep development-document work and design-level debugging before implementation | -| `parallel-development-loop` | frozen contract → parallel modules → local audit → wiring → reasoner + verification → parallel review → arbiter | Medium/high-scale project implementation with bounded local correction waves | -| `deep-review-dag-module` | parallel exploration → parallel review → claim verification → arbiter → PASS report or targeted LOOP | Deep review of an already-built subsystem; retarget its lanes to the current project area | -| `change-review` | survey → parallel review/verification → arbiter | A compact fixed review when the medium/high-scale graph would be wasteful | - -The project-scoped `reasoner` used by the first two graphs lives at -`.opencode/agent/reasoner.md`. `/dag-flow` selects the closest reference from the -request. Start a saved graph by name only when its embedded target and inputs already -fit. Generic design/development requests must be derived into a one-off DAG with the -actual task injected into the root node; deep review requests retarget the hard-coded -DAG-module lanes unless that module is the real target. - -## Agent adaptation contract - -1. **Derive, do not blindly replay.** Preserve the reference graph's phase order and real artifact edges, then choose the actual module count, reviewer lanes, and local scope for the task. -2. **Protected nodes cannot be pruned.** Fresh-context review gates, deterministic verification, the single arbiter, and PASS-only finalization always remain. A parent may replace them only with equivalent fresh nodes carrying the same contract. -3. **Every prune is evidence-bearing.** Record `{node, prune_reason, replacement_coverage}`. Missing either field is fail-closed and the next gate must return `BLOCKED`, not silently accept the smaller graph. -4. **Every loop is local and acyclic.** A gate returns `PASS | LOOP | BLOCKED`. `LOOP` identifies the smallest preceding slice to revisit; the parent pauses, replans new correction/review nodes, and resumes. Completed nodes are never restarted in place. -5. **Expansion stays bounded.** New fan-out must have disjoint work or independent context, real downstream consumers, one merge owner, and enough remaining concurrency/node/replan budget. - -## Gate disposal rules - -| Verdict | Required parent action | Required evidence | -|---|---|---| -| `PASS` | Continue to the next protected phase or finalize | Coverage of every material criterion; no unresolved material finding | -| `LOOP` | Pause, add a fresh local correction/review wave with new node IDs, resume | Reason, minimal `loop_scope`, acceptance condition, `stop_reason` | -| `BLOCKED` | Stop and report; do not reinterpret as advisory success | Missing evidence/decision, unresolved contradiction, no progress, or a reached cap | - -These graphs are project-authored execution contracts, not framework-specific -examples. Their value lies in enforceable edges, evidence gates, bounded local -loops, and explicit parent-agent disposal rules. diff --git a/.opencode/workflows/algo-complexity-review.yaml b/.opencode/workflows/algo-complexity-review.yaml deleted file mode 100644 index 56c8696d52..0000000000 --- a/.opencode/workflows/algo-complexity-review.yaml +++ /dev/null @@ -1,246 +0,0 @@ -title: Algorithm Complexity Review (opencode-dag) -mode: standard -config: - name: algo-complexity-review - max_concurrency: 6 - max_node_replan_attempts: 2 - worker_config: - timeout_ms: 900000 - nodes: - - # ============================================================ - # Wave 1: 6 parallel algorithm reviewers (one per process cluster) - # ============================================================ - - - id: review-llm-pipeline - name: Review LLM Request Pipeline - worker_type: general - depends_on: [] - prompt_template: - inline: | - 你是算法复杂度审查专家。审查 opencode-dag 的 LLM 请求构建管道,验证知识图谱给出的静态复杂度指标是否与真实代码吻合。 - - ## 背景(已知热点指标,待你用真实代码验证或推翻) - - `convertToOpenAIResponsesInput` @ packages/core/src/github-copilot/responses/convert-to-openai-responses-input.ts:21 — cx=47, cog=261, alloc_in_loop=19, lines≈308。每次 LLM provider turn 必经。 - - `normalizeMessages` @ packages/opencode/src/provider/transform.ts — cx=47, ail=2。调用 sanitizeSurrogates + sanitizeToolResultOutput + scrub。 - - `prepareResponsesTools` @ packages/core/src/github-copilot/responses/openai-responses-prepare-tools.ts — ail=8。 - - `variants` @ packages/opencode/src/provider/transform.ts — cx=57, cog=135。 - - `convertToOpenAICompatibleChatMessages` @ packages/core/src/github-copilot/chat/convert-to-openai-compatible-chat-messages.ts — ail=6。 - - ## 审查重点(按优先级) - 1. O(n²) 或更差模式:消息数 n 的循环内,每条消息是否再对消息列表/工具列表做线性扫描?嵌套 switch 是否随 n 放大? - 2. 循环内分配(alloc_in_loop):每个对象字面量/数组 push/JSON.stringify 的实际位置(file:line)及其是否可提到循环外。 - 3. 缺失 memoization:同一计算(如工具列表序列化、message key)是否在一次 turn 内重复执行。 - 4. 数据结构选择:是否有 Array.indexOf/includes/find 可改 Map/Set。 - 5. JSON.stringify 在热路径中的开销。 - - ## 工具使用 - - 用 Read 工具读取上述文件确认实现细节(精确 file:line)。 - - 可用 codebase-memory-mcp 的 get_code_snippet / trace_path(project="opencode-dag")查调用关系与邻居。 - - 调用链:packages/opencode/src/session/llm.ts → transformParams → normalizeMessages → 3 个 sanitizer;openai-responses-language-model.ts → getArgs/doGenerate/doStream → convertToOpenAIResponsesInput + prepareResponsesTools。 - - ## 输出要求(Markdown,提交为最终文本) - 对每个目标输出: - ### 目标名 (file:line) - - **已验证问题**:逐条列出,每条含 [severity] 描述 + `file:line` 证据 + 实测复杂度(含 n 代表什么)+ 修复建议。 - - **被推翻/夸大的声明**:指标与实际不符之处(如 alloc_in_loop 计数偏高/偏低,注明原因)。 - - **unverified_claims**:你无法从静态代码确认、需运行时 profiling 的点。 - 末尾给该 cluster 一个 **总体严重度评级**(CRITICAL/HIGH/MEDIUM/LOW)与一句话结论。区分"每次 LLM 请求"是已验证(代码路径必经)还是推测。 - - - id: review-dag-replan - name: Review DAG Replan Engine - worker_type: general - depends_on: [] - prompt_template: - inline: | - 你是算法复杂度审查专家。审查 opencode-dag 的 DAG replan 引擎,验证环检测与图重建的算法复杂度。 - - ## 背景(待验证指标) - - `planReplan` @ packages/core/src/dag/core/replan.ts:79 — cx=47, cog=87, alloc_in_loop=15, loop_count=14, tld=2, lines≈168。14 个循环遍历节点集合,15 处循环内分配。 - - 调用 `DependencyGraph.addNode/addEdge`、`DependencyGraph.hasCycle/findCycles`(声称 O(V+E) 环检测)。 - - 调用方:packages/opencode/src/dag/dag.ts。每次 DAG replan 操作触发。 - - ## 审查重点 - 1. **planReplan 的 O(n²) 风险**:survivingIds 集合构建是否真的需要 3 次遍历?14 个循环是否可合并?循环内是否对节点集合做 Array.includes/线性查找(应用 Set.has)。 - 2. **环检测开销**:定位 DependencyGraph 实现(find packages/core/src/dag 下的 dependency-graph)。hasCycle + findCycles 是否重复遍历?是 DFS 还是 Tarjan?replan 中是否每次都全图重算,而非增量。 - 3. **数据结构**:邻接表 vs 邻接矩阵;Map/Set vs Array。 - 4. **alloc_in_loop=15** 的真实位置(file:line)。 - 5. 大工作流(100+ 节点)时的最坏复杂度推导。 - - ## 工具 - - Read 上述文件;用 codebase-memory-mcp search_graph / get_code_snippet 找 DependencyGraph 实现(project="opencode-dag")。 - - trace_path(trace function_name="planReplan") 看调用方频率。 - - ## 输出(同上格式) - 重点给出:planReplan 的实测时间复杂度(用 n=节点数、e=边数表达),环检测是否增量,以及 100 节点时的推算开销。区分"O(n²)"是已验证(代码确有嵌套线性查找)还是推测。 - - - id: review-event-persist - name: Review Event Persistence & Reduction - worker_type: general - depends_on: [] - prompt_template: - inline: | - 你是算法复杂度审查专家。审查 opencode-dag 的持久化事件提交管道与会话数据归约。 - - ## 背景(待验证指标) - - `commitDurableEvent` @ packages/core/src/event.ts:126 — recursive=true, cx=13, lines≈163。每个持久化事件必经。含 DB 事务(immediate)、多次 SELECT/INSERT、isDeepStrictEqual 深比较。 - - `publish` @ packages/core/src/event.ts:340(通过 publishEvent 间接递归)。 - - `reduceSessionData` @ packages/opencode/src/cli/cmd/run/session-data.ts — cx=63, cog=139。fan-in 高(被 createLayer/applyChildEvent/bootstrapChildEvent/session-replay.apply 等 5+ 调用方使用)。 - - ## 审查重点 - 1. **递归安全性**:commitDurableEvent 的递归终止条件(守卫)是什么?最坏递归深度?是否尾递归/有深度上限?会否因恶意/深层 payload 栈溢出? - 2. **isDeepStrictEqual 开销**:定位其调用点 file:line,确认比较对象大小(payload?整条事件?),是否 O(n) 且在每个事件上执行。是否可用哈希替代。 - 3. **DB 往返**:确认每个事件的确切 SELECT/INSERT 次数(声称 2 SELECT + 2 INSERT)。是否可批量化/合并。immediate 事务模式的锁开销。 - 4. **去重检查**:SELECT EventTable 去重是否在主键/唯一索引上? - 5. **reduceSessionData**:cog=139 的高认知复杂度来源,是否有重复归约/未 memoize 的派生计算;事件序列化是否每次重建。 - - ## 工具 - - Read packages/core/src/event.ts 全文;Read session-data.ts。 - - codebase-memory-mcp trace_path(function_name="commitDurableEvent") 与 trace_path("reduceSessionData") 看真实 fan-in(project="opencode-dag")。 - - ## 输出(同上格式) - 重点:递归深度上界(已验证 vs 推测)、每事件 DB 往返实测数、isDeepStrictEqual 比较粒度。区分"全局热路径"是已验证(所有 durable event 必经)还是推测。 - - - id: review-frontend-render - name: Review Frontend Render Hot Paths - worker_type: general - depends_on: [] - prompt_template: - inline: | - 你是算法复杂度审查专家。审查 opencode-dag 前端(React/SolidJS app 包)渲染热路径的算法与重渲染开销。 - - ## 背景(待验证指标) - - `LegacyLayout` @ packages/app/src/pages/layout.tsx — cx=222, cog=249, tld=5, lsil=1, ail=4。全项目最高圈复杂度,每次渲染。 - - `Page` (session) @ packages/app/src/pages/session.tsx — cx=194, tld=6,含 revealMessage(unguarded_recursion=true)。 - - `PromptInput` @ packages/app/src/components/prompt-input.tsx — cx=148, cog=208,handleKeyDown(cx=33) 每次按键。 - - `MessageTimeline` @ packages/app/src/pages/session/timeline/message-timeline.tsx — cx=119, tld=6,长会话渲染开销。 - - ## 审查重点 - 1. **循环内线性扫描 (lsil)**:LegacyLayout 的 lsil=1 实际是哪段代码(file:line)?是否在 render 中对数组做 indexOf/find。MessageTimeline 是否对消息列表做 O(n²)。 - 2. **缺失 memoization**:useMemo/useCallback 缺失导致每次渲染重算;派生数据是否在 render 内现算而非 selector。 - 3. **重渲染触发面**:LegacyLayout 作为主布局,哪些 state 变化触发它重渲染;是否有 context 过度订阅。 - 4. **revealMessage 无守卫递归**:定位实现,确认终止条件与最坏深度,DOM 滚动场景会否栈溢出。 - 5. **PromptInput.handleKeyDown**:cx=33 的按键处理是否有重复计算/未 debounce。 - - ## 工具 - - Read 上述 .tsx 文件确认实现。注意区分 React(useMemo/useCallback/useEffect) vs SolidJS(createMemo/Signal) —— 先看包用的是哪个框架。 - - codebase-memory-mcp search_graph 可辅助(project="opencode-dag")。 - - ## 输出(同上格式) - 重点:每项的实测最坏复杂度(用消息数 m / 渲染次数 r 表达),lsil 的确切位置,memoization 缺口清单。区分"每次渲染/每次按键"是已验证(确认在 render/事件处理体内)还是推测。 - - - id: review-cli-tui - name: Review CLI Transport & TUI Hot Paths - worker_type: general - depends_on: [] - prompt_template: - inline: | - 你是算法复杂度审查专家。审查 opencode-dag 的 CLI 会话传输层与 TUI 输入热路径。 - - ## 背景(待验证指标) - - `createLayer` (stream.transport) @ packages/opencode/src/cli/cmd/run/stream.transport.ts — cx=79, cog=104, tld=6, ail=4。CLI 每个事件经过此路径。 - - `Autocomplete` (TUI) @ packages/tui/src/component/prompt/autocomplete.tsx — cx=45, tld=3, ail=2,每次按键。 - - `Session` (TUI) @ packages/tui/src/routes/session/index.tsx — cx=81, cog=100, tld=6。 - - 关联:reduceSessionData @ session-data.ts(另一 reviewer 负责其本体,你关注 createLayer 如何调用它)。 - - ## 审查重点 - 1. **createLayer tld=6**:6 层传递循环深度的实际代码结构(file:line)。是嵌套 for/while 还是递归?每个会话事件是否触发全量归约而非增量。 - 2. **ail=4** 的循环内分配位置,是否可提循环外。 - 3. **事件传输开销**:每个事件是否复制/序列化整个 session state;有无增量更新机制。 - 4. **Autocomplete 每次按键**:候选集构建是否在按键处理内重算;是否线性扫描候选;有无缓存。 - 5. **Session(TUI)** tld=6 的来源,渲染开销。 - - ## 工具 - - Read 上述文件;codebase-memory-mcp trace_path(function_name="createLayer", project="opencode-dag")。 - - ## 输出(同上格式) - 重点:createLayer 的传递循环结构(嵌套 vs 递归)、每事件开销是否随 session 大小放大。区分 tld=6 是真实嵌套循环还是误统计(如 Effect.gen 链)。 - - - id: review-recursion-risks - name: Review Recursion & Cross-Cut Event Sync - worker_type: general - depends_on: [] - prompt_template: - inline: | - 你是算法复杂度审查专家。审查 opencode-dag 的递归函数栈安全性与前端事件同步链。 - - ## 背景(待验证指标) - - `revealMessage` @ packages/app/src/pages/session.tsx — **unguarded_recursion=true**(高风险)。 - - `toModelOutput` @ packages/core/src/tool/edit.ts — **unguarded_recursion=true**(高风险)。 - - `flatten` @ packages/core/src/observability/logging.ts — unguarded_recursion,日志展平。 - - `settle`/`run` @ packages/core/src/session/run-coordinator.ts — 有守卫递归。 - - `applyDirectoryEvent` @ packages/app/src/context/global-sync/event-reducer.ts — cx=56, cog=153, tld=4,每个目录事件。 - - `createLLMEventPublisher` @ packages/core/src/session/runner/publish-llm-event.ts — cx=46, cog=96,每个 LLM 流事件。 - - ## 审查重点 - 1. **revealMessage / toModelOutput 无守卫递归**:读取实现,确认终止条件是否存在、最坏递归深度、触发场景,栈溢出可能性(给出具体 file:line)。这是本审查最高优先级。 - 2. **flatten** 递归:展平对象深度是否可控,恶意深层结构会否栈溢出。 - 3. **applyDirectoryEvent** cog=153:153 认知复杂度的具体来源;每个事件是否做全量重算;有无增量 reducer 模式缺失;是否有 O(n²) 事件应用。 - 4. **createLLMEventPublisher**:每个 LLM 流事件的开销;是否对每个 token 做重计算;事件去重/合并是否缺失。 - - ## 工具 - - Read 上述文件确认实现(unguarded 递归务必读到函数体与调用点)。 - - codebase-memory-mcp trace_path(project="opencode-dag")。 - - ## 输出(同上格式) - 重点:每个 unguarded 递归给出「终止条件是否存在 + 最坏深度 + 栈溢出风险评级」三要素,附 file:line。区分 unguarded_recursion 标记是否属实(有时是 mutual/indirect recursion 被误标,需你核实)。 - - # ============================================================ - # Wave 2: Arbiter — consolidate, dedupe, rank, emit verdict - # ============================================================ - - - id: arbitrate - name: Arbitrate & Consolidate Findings - worker_type: general - depends_on: - - review-llm-pipeline - - review-dag-replan - - review-event-persist - - review-frontend-render - - review-cli-tui - - review-recursion-risks - required: true - report_to_parent: true - output_schema: - type: object - required: [verdict, severity_ranked_findings, cross_cutting_patterns, unverified_claims, top_fixes, next_action] - properties: - verdict: - type: string - enum: [ACCEPT, REVISE, REJECT, BLOCKED] - description: "ACCEPT=审查完成结论可信;BLOCKED=证据不足" - severity_ranked_findings: - type: array - description: "按 CRITICAL>HIGH>MEDIUM>LOW 排序的最终发现,每条含 target, file:line, severity, 实测复杂度, 修复建议" - cross_cutting_patterns: - type: array - description: "跨多个 cluster 的共性算法反模式(如多处缺 memoization、多处 Array 代替 Set)" - unverified_claims: - type: array - description: "需运行时 profiling 才能确认的点" - top_fixes: - type: array - description: "投入产出比最高的 3-5 个修复,按 ROI 排序" - next_action: - type: object - required: [operation, targets] - properties: - operation: - type: string - enum: [continue, extend, replan, complete, stop] - targets: - type: array - prompt_template: - inline: | - 你是仲裁专家。6 位算法审查员已分别审查了 opencode-dag 的 6 个 cluster(LLM 管道 / DAG replan / 事件持久化 / 前端渲染 / CLI&TUI / 递归风险)。他们的发现作为你的结构化输入已附在上方。 - - ## 你的任务 - 1. **去重与合并**:同一问题被多个 reviewer 提及则合并,保留最强证据。 - 2. **核实冲突**:若 reviewer 间结论冲突,以 file:line 代码证据为准,必要时自行 Read 关键文件复核。 - 3. **按严重度排序**:CRITICAL > HIGH > MEDIUM > LOW。判定依据:实测最坏复杂度 × 触发频率 × 影响面(是否热路径)。每条给出「实测复杂度(含 n 的含义)」。 - 4. **提取横切反模式**:跨 cluster 的共性(如 alloc_in_loop 普遍、Set/Map 用 Array 代替普遍、缺 memoization 普遍)。 - 5. **厘清 unverified_claims**:把所有"需运行时验证"的点单列,不混入已验证发现。 - 6. **输出 top_fixes**:投入产出比最高的 3-5 个修复,按 ROI 排序(预期收益 ÷ 改动风险)。 - 7. **verdict**:若证据充分结论可信 → ACCEPT;若关键目标证据不足 → BLOCKED 并说明缺什么。 - - 严格区分 ✅已验证(代码路径确认必经、复杂度推导自源码)vs ⚠️未验证(频率/运行时开销推测)。调用 submit_result 提交符合 output_schema 的结构化结果。 diff --git a/.opencode/workflows/dag-module-review-v2.yaml b/.opencode/workflows/dag-module-review-v2.yaml deleted file mode 100644 index da2a7f223f..0000000000 --- a/.opencode/workflows/dag-module-review-v2.yaml +++ /dev/null @@ -1,255 +0,0 @@ -title: DAG Module Deep Review -config: - name: dag-module-review - max_concurrency: 5 - max_node_replan_attempts: 3 - max_total_nodes: 20 - node_defaults: - required: false - report_to_parent: false - worker_config: - timeout_ms: 600000 - nodes: - - id: explore-dag - name: explore-dag - worker_type: explore - depends_on: [] - required: true - prompt_template: - inline: | - Map the DAG module at packages/opencode/src/dag/ in this repository. - - The module has 13 source files across these clusters: - - Core workflow control: dag.ts (withWorkflowLock, pause, resume, step, cancel, complete, parseWorkflowConfig, normalizeModel, computeMergedConfig) - - Configuration: config.ts (load, tierModel, globalConfigDir) - - Model resolution: model.ts (resolve) - - Admission QA: admission.ts (transitionAdmission, fingerprintBrief, createAdmissionRecord, validateAdmission, evaluateQa, projectBriefForNode) - - Review lifecycle: review-lifecycle.ts (validateReviewLifecycle, validateReviewExecutionInput, reviewImplementationFingerprint, isReviewWorker) - - Templates: templates/resolve.ts, templates/sanitize.ts (renderTemplate, sanitizeInput, preserveEvidence) - - Runtime: runtime/spawn.ts (spawnNode), runtime/loop.ts, runtime/recovery.ts (settle), runtime/capture.ts (registerCaptureSlot), runtime/eval.ts (evaluateCondition, resolveInputMapping), runtime/summary-publisher.ts (schedulePublish) - - Tests are in packages/opencode/test/dag/ (25 test files). - - Your task: - 1. Read each source file and produce a responsibility map: file → public API → internal dependencies → external dependencies (imports from outside src/dag/). - 2. Identify the dependency direction between clusters. Flag any circular or upward dependencies. - 3. List all cross-module consumers: who outside src/dag/ imports from this module? - 4. Note any files that appear under-tested or over-tested relative to their complexity. - - Output a structured inventory with file paths and line numbers for key exports. - - - id: review-arch - name: review-arch - worker_type: general - depends_on: [explore-dag] - prompt_template: - inline: | - You are the PROSECUTOR in an adversarial architecture review of the DAG module at packages/opencode/src/dag/. - - Your mandate: argue the structure is WRONG. Focus on: - - Coupling violations: clusters that should be independent but share state or call each other - - Hidden invariants: assumptions that are enforced by convention rather than types or runtime checks - - Failure modes: what happens when withWorkflowLock contention occurs, when spawn fails mid-graph, when recovery races with a live scheduler - - Layer violations: runtime/ depending on dag.ts control flow, or templates/ reaching into runtime state - - Missing abstractions: god-objects, files doing too much, logic that should be extracted - - Use the exploration inventory as your starting point: {{explore-dag}} - - RULES: - - Every finding MUST cite file:line evidence from the actual source code - - Read the source files directly to verify your claims - - List any claim you could NOT verify as unverified_claims with the reason - - Severity: CRITICAL (data loss/corruption), HIGH (incorrect behavior), MEDIUM (maintainability), LOW (style) - - Output format: - ## Findings - [numbered list with severity, file:line, description, evidence] - - ## unverified_claims - [claims you made but could not confirm from code, with reason] - - - id: review-logic - name: review-logic - worker_type: general - depends_on: [explore-dag] - prompt_template: - inline: | - You are a CORRECTNESS reviewer for the DAG module at packages/opencode/src/dag/. - - Your mandate: find logic bugs, race conditions, and edge-case failures. Focus on: - - State machine correctness: admission transitions (admission.ts), workflow status transitions in dag.ts - - Concurrency: withWorkflowLock implementation, can two operations interleave incorrectly? Does recovery.ts race with loop.ts? - - Condition evaluation: eval.ts edge cases (undefined references, circular conditions, type coercion) - - Loop termination: can the execution loop in runtime/loop.ts get stuck? Are guard counters correct? - - Schema validation: can invalid payloads pass validateAgainstSchema? Are error paths handled? - - Recovery semantics: does settle() in recovery.ts correctly handle all crash states? Can it lose node results? - - Use the exploration inventory as your starting point: {{explore-dag}} - - RULES: - - Every finding MUST cite file:line evidence from the actual source code - - Read the source files directly, trace execution paths - - List any claim you could NOT verify as unverified_claims with the reason - - Severity: CRITICAL (data loss/corruption), HIGH (incorrect behavior), MEDIUM (edge case), LOW (theoretical) - - Output format: - ## Findings - [numbered list with severity, file:line, description, evidence] - - ## unverified_claims - [claims you made but could not confirm from code, with reason] - - - id: review-style - name: review-style - worker_type: general - depends_on: [explore-dag] - prompt_template: - inline: | - You are a CONVENTIONS reviewer for the DAG module at packages/opencode/src/dag/. - - Your mandate: check adherence to the project's documented coding standards from AGENTS.md. Focus on: - - Import conventions: no aliases, no star imports, prefer dynamic imports for heavy modules - - Variable style: const over let, ternaries over reassignment, no unnecessary destructuring - - Control flow: early returns over else, no try/catch where avoidable - - Effect patterns: services bound to named variables before method calls, no nested service yields - - Type safety: no `any`, rely on type inference where possible - - Comments: should only exist for non-obvious constraints (flag unnecessary comments) - - Schema/Drizzle: snake_case field names (if applicable) - - Helper extraction: single-use helpers should be inlined; helpers should not return Effect unless effectful - - Use the exploration inventory as your starting point: {{explore-dag}} - - RULES: - - Every finding MUST cite file:line evidence from the actual source code - - Read the source files directly - - List any claim you could NOT verify as unverified_claims with the reason - - Severity: HIGH (violates explicit AGENTS.md rule), MEDIUM (violates spirit), LOW (inconsistency) - - Output format: - ## Findings - [numbered list with severity, file:line, description, evidence] - - ## unverified_claims - [claims you made but could not confirm from code, with reason] - - - id: verify-claims - name: verify-claims - worker_type: explore - depends_on: [review-arch, review-logic, review-style] - prompt_template: - inline: | - You are the CLAIM VERIFIER. Three reviewers produced findings about the DAG module at packages/opencode/src/dag/. - - Architecture prosecutor findings: - {{review-arch}} - - Correctness reviewer findings: - {{review-logic}} - - Conventions reviewer findings: - {{review-style}} - - Your task: - 1. Collect ALL items from every reviewer's unverified_claims section - 2. Collect all CRITICAL and HIGH severity findings - 3. For each item, read the actual source code at the cited file:line and determine: CONFIRMED (evidence supports the claim), REFUTED (evidence contradicts it), or PARTIAL (partially true, explain) - 4. For disputed claims between reviewers (one says X, another implies not-X), resolve the conflict with evidence - - Output format: - ## Verification Results - [for each claim: reviewer source, claim summary, file:line checked, verdict (CONFIRMED/REFUTED/PARTIAL), evidence] - - ## Conflicts Resolved - [where reviewers disagreed, state who was right and why] - - ## Remaining Uncertain - [claims that remain genuinely uncertain after your check, with reason] - - - id: arbitrate - name: arbitrate - worker_type: general - depends_on: [verify-claims] - required: true - report_to_parent: true - output_schema: - type: object - required: [verdict, summary, findings, required_actions, next_action] - properties: - verdict: - type: string - enum: [ACCEPT, REVISE, REJECT, BLOCKED] - summary: - type: string - findings: - type: array - items: - type: object - properties: - severity: - type: string - file: - type: string - description: - type: string - verified: - type: boolean - required_actions: - type: array - items: - type: string - next_action: - type: object - required: [operation, targets] - properties: - operation: - type: string - enum: [continue, extend, replan, complete, stop] - targets: - type: array - items: - type: string - prompt_template: - inline: | - You are the ARBITER for the DAG module review. Rule on VERIFIED evidence only. - - The claim verifier's results: - {{verify-claims}} - - Your task: - 1. Discard any finding that was REFUTED by the verifier - 2. For CONFIRMED and PARTIAL findings, rule finding-by-finding: is this a real issue requiring action? - 3. Deduplicate findings that describe the same root cause - 4. Rank by severity and impact - 5. Emit a structured verdict: - - ACCEPT: no CRITICAL/HIGH issues remain after verification - - REVISE: CRITICAL/HIGH issues confirmed but bounded and actionable - - REJECT: systemic issues requiring redesign - - BLOCKED: cannot determine without additional information - 6. For each confirmed finding in the output, set verified=true; for any you include from PARTIAL, set verified=false - 7. required_actions: concrete, file-specific fixes - 8. next_action: if ACCEPT → operation=complete; if REVISE → operation=extend with targets being the files to fix; if REJECT → operation=replan - - Call submit_result with your structured verdict. - - - 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 the DAG module review. Its verdict and findings: - {{arbitrate}} - - Your task: - 1. For each required_action, read the actual code at the cited location - 2. Produce a concrete, implementable fix proposal for each action: - - Exact file and line range to modify - - What the code currently does (quote it) - - What it should do instead - - Why the fix is safe (what invariants it preserves) - 3. If any finding is actually a false positive on closer inspection, say so and explain - 4. Rank the fixes by priority (CRITICAL first) - - Output a prioritized fix plan that a developer can execute directly. diff --git a/.opencode/workflows/dag-module-review.yaml b/.opencode/workflows/dag-module-review.yaml deleted file mode 100644 index 636ae194e2..0000000000 --- a/.opencode/workflows/dag-module-review.yaml +++ /dev/null @@ -1,235 +0,0 @@ -title: DAG Module Deep Review -config: - name: dag-module-review - max_concurrency: 5 - max_node_replan_attempts: 3 - node_defaults: - report_to_parent: false - worker_config: - timeout_ms: 600000 - nodes: - - id: explore-dag - name: explore-dag - worker_type: explore - depends_on: [] - required: true - prompt_template: - inline: | - Explore the DAG workflow orchestration module at packages/opencode/src/dag/ in this repository. - - Use the codebase-memory-mcp tools (project name: "opencode-dag") for structural analysis: - - search_graph, trace_path, get_code_snippet, query_graph - - Map the complete module structure: - 1. List every file with its responsibility (admission.ts, config.ts, dag.ts, model.ts, review-lifecycle.ts, runtime/*.ts, templates/*.ts) - 2. Identify the core data flow: workflow creation → node scheduling → execution → completion - 3. Map cross-file dependencies and coupling points - 4. Identify the public API surface (exported functions/classes) - 5. Note integration points with the rest of the system (Session, EventV2, Provider, etc.) - 6. Flag any files over 300 lines or functions with high cyclomatic complexity - - Output a structured inventory: file paths, responsibilities, key exports, dependency edges, and integration boundaries. - - - id: review-arch - name: review-arch - worker_type: general - depends_on: [explore-dag] - prompt_template: - inline: | - You are an architecture reviewer. Review the DAG module at packages/opencode/src/dag/ for architectural soundness. - - Context from exploration: - {{explore-dag}} - - Use codebase-memory-mcp (project: "opencode-dag") and file reads to examine the actual code. - - Review dimensions: - - Module boundaries: are responsibilities cleanly separated? (admission vs config vs runtime vs templates) - - Coupling: does dag.ts (core) leak runtime concerns? Does runtime/ properly abstract execution? - - Layer violations: does the module respect the LayerNode/defaultLayer self-containment invariant from AGENTS.md? - - State management: is workflow state mutation properly guarded (withWorkflowLock)? - - Extension points: are templates, model resolution, and config properly pluggable? - - HARD REQUIREMENTS: - - Every finding MUST cite file:line evidence - - List any claims you could NOT verify as `unverified_claims` - - Do NOT modify any files - - Rate each finding: CRITICAL / HIGH / MEDIUM / LOW - - - id: review-logic - name: review-logic - worker_type: general - depends_on: [explore-dag] - prompt_template: - inline: | - You are a correctness reviewer. Review the DAG module at packages/opencode/src/dag/ for logic correctness and edge-case safety. - - Context from exploration: - {{explore-dag}} - - Use codebase-memory-mcp (project: "opencode-dag") and file reads to examine the actual code. - - Review dimensions: - - State machine correctness: workflow lifecycle transitions (pending → running → completed/failed/cancelled/paused) - - Concurrency safety: lock usage in withWorkflowLock, race conditions in node scheduling - - Error handling: are failures properly propagated? Can a node failure leave the workflow in an inconsistent state? - - Recovery logic (runtime/recovery.ts): does crash recovery correctly handle mid-flight nodes? - - Condition evaluation (runtime/eval.ts): can conditions reference undefined outputs? - - Admission state machine (admission.ts): are transitions exhaustive and mutually exclusive? - - HARD REQUIREMENTS: - - Every finding MUST cite file:line evidence - - List any claims you could NOT verify as `unverified_claims` - - Do NOT modify any files - - Rate each finding: CRITICAL / HIGH / MEDIUM / LOW - - - id: review-style - name: review-style - worker_type: general - depends_on: [explore-dag] - prompt_template: - inline: | - You are a code style and conventions reviewer. Review the DAG module at packages/opencode/src/dag/ against the project's documented style guide in AGENTS.md. - - Context from exploration: - {{explore-dag}} - - Use codebase-memory-mcp (project: "opencode-dag") and file reads to examine the actual code. - - Review dimensions: - - Effect usage: are services bound to named variables before method calls? No nested service yields? - - Import style: no aliases, no star imports, dynamic imports for heavy modules? - - Variable style: const over let, ternaries over reassignment, no unnecessary destructuring? - - Control flow: early returns over else, no try/catch where avoidable? - - Schema/Drizzle conventions: snake_case field names? - - Type safety: no `any`, proper use of Schema helpers for JSON parsing? - - Comment discipline: comments only for non-obvious constraints? - - HARD REQUIREMENTS: - - Every finding MUST cite file:line evidence - - List any claims you could NOT verify as `unverified_claims` - - Do NOT modify any files - - Rate each finding: CRITICAL / HIGH / MEDIUM / LOW - - - id: verify-claims - name: verify-claims - worker_type: general - depends_on: [review-arch, review-logic, review-style] - required: true - prompt_template: - inline: | - You are a claim verification agent. Three reviewers produced findings about the DAG module at packages/opencode/src/dag/. - - Architecture review: - {{review-arch}} - - Logic/correctness review: - {{review-logic}} - - Style review: - {{review-style}} - - Your job: - 1. Collect ALL items listed as `unverified_claims` from all three reviews - 2. Collect all CRITICAL and HIGH findings - 3. For EACH item, verify it against the actual source code using file reads and codebase-memory-mcp (project: "opencode-dag") - 4. Mark each as: CONFIRMED (evidence supports it), REFUTED (evidence contradicts it), or PARTIAL (partially true) - 5. For CONFIRMED findings, note the exact file:line evidence - 6. For REFUTED findings, explain why the reviewer was wrong - - Output a verification report with: - - verified_findings: list of CONFIRMED findings with evidence - - refuted_findings: list of REFUTED findings with counter-evidence - - partial_findings: list of PARTIAL findings with nuance - - unresolved: items you could not determine - - Do NOT modify any files. - - - id: arbitrate - name: arbitrate - worker_type: general - depends_on: [verify-claims] - required: true - report_to_parent: true - output_schema: - type: object - required: [verdict, summary, findings, required_actions, next_action] - properties: - verdict: - type: string - enum: [ACCEPT, REVISE, REJECT, BLOCKED] - summary: - type: string - findings: - type: array - items: - type: object - properties: - id: { type: string } - severity: { type: string } - category: { type: string } - description: { type: string } - evidence: { type: string } - verified: { type: boolean } - required_actions: - type: array - items: { type: string } - next_action: - type: object - required: [operation, targets] - properties: - operation: - type: string - enum: [continue, extend, replan, complete, stop] - targets: - type: array - items: { type: string } - prompt_template: - inline: | - You are the final arbiter for a code review of the DAG module at packages/opencode/src/dag/. - - Verification report: - {{verify-claims}} - - Your job: - 1. Rule finding-by-finding on the VERIFIED evidence only (CONFIRMED and PARTIAL items) - 2. Discard REFUTED findings entirely - 3. Deduplicate overlapping findings across reviewers - 4. Resolve conflicts between reviewers using the verification evidence - 5. Produce a final ranked list of findings by severity - 6. Decide verdict: - - ACCEPT: no CRITICAL or HIGH verified findings remain - - REVISE: HIGH findings exist but are actionable and bounded - - REJECT: CRITICAL findings exist or the module has fundamental structural problems - - BLOCKED: cannot determine due to unresolved verification gaps - 7. Specify required_actions for each confirmed finding - 8. Set next_action: if ACCEPT → complete; if REVISE/REJECT → extend with targets being the problem areas - - Call submit_result with your structured verdict. - - - 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 the DAG module review. Verify each required action against the actual code and produce a corrected, evidence-backed action plan. - - Arbiter verdict: - {{arbitrate}} - - For each required_action: - 1. Read the relevant source files in packages/opencode/src/dag/ - 2. Confirm the problem exists with exact file:line evidence - 3. Propose a concrete fix (code-level, not abstract) - 4. Assess fix risk and blast radius - - Output a prioritized remediation plan with: - - Each action confirmed or dismissed - - Concrete code suggestions for confirmed actions - - Dependency order for fixes - - Risk assessment per fix - - Do NOT modify any files. diff --git a/.opencode/workflows/dag-review.yaml b/.opencode/workflows/dag-review.yaml deleted file mode 100644 index f1bac27152..0000000000 --- a/.opencode/workflows/dag-review.yaml +++ /dev/null @@ -1,166 +0,0 @@ -title: "DAG Module Deep Review" -config: - name: dag-module-review - max_concurrency: 5 - max_node_replan_attempts: 3 - max_total_nodes: 20 - node_defaults: - required: false - report_to_parent: false - worker_config: - timeout_ms: 600000 - nodes: - - id: explore-core - name: explore-core - worker_type: explore - depends_on: [] - prompt_template: - id: code-explore - input: - target: "packages/opencode/src/dag core lifecycle files: dag.ts, config.ts, model.ts, admission.ts, review-lifecycle.ts. Map workflow state machine transitions, locking strategy (withWorkflowLock), node lifecycle (spawn/complete/fail/cancel/pause/resume/step), config normalization, model resolution, and admission QA protocol. Identify state ownership, concurrency guards, and cross-module contracts." - - - id: explore-runtime - name: explore-runtime - worker_type: explore - depends_on: [] - prompt_template: - id: code-explore - input: - target: "packages/opencode/src/dag/runtime execution engine: loop.ts (scheduling loop, layer computation, concurrency control), spawn.ts (child session creation), recovery.ts (crash recovery, reconciliation), eval.ts (condition evaluation, input mapping), capture.ts (output schema validation, submit_result), summary-publisher.ts (event emission). Map the scheduling algorithm, session lifecycle, error propagation, and recovery invariants." - - - id: explore-templates - name: explore-templates - worker_type: explore - depends_on: [] - prompt_template: - id: code-explore - input: - target: "packages/opencode/src/dag/templates template system: resolve.ts (template resolution, rendering, interpolation) and sanitize.ts (input sanitization, injection prevention). Map template loading (by ID from .opencode/dag-prompts, inline), variable interpolation mechanics, and the sanitization boundary. Identify trust assumptions and injection vectors." - - - id: review-arch - name: review-arch - worker_type: review - depends_on: [explore-core, explore-runtime, explore-templates] - prompt_template: - id: review-arch - - - id: review-logic - name: review-logic - worker_type: review - depends_on: [explore-core, explore-runtime, explore-templates] - prompt_template: - id: review-logic - - - id: review-style - name: review-style - worker_type: review - depends_on: [explore-core, explore-runtime, explore-templates] - prompt_template: - id: review-style - - - id: verify-claims - name: verify-claims - worker_type: general - depends_on: [review-arch, review-logic, review-style] - required: true - prompt_template: - inline: | - You are a claim verifier. Three reviewers produced findings and unverified_claims about the DAG module (packages/opencode/src/dag/). - - Your job: take EVERY item listed under `unverified_claims` from all three reviews and check it against the actual source code. For each claim: - 1. Open the cited file(s) and read the relevant code. - 2. Determine: CONFIRMED (the claim is true, cite evidence), REFUTED (the claim is false, cite counter-evidence), or INCONCLUSIVE (cannot determine from code alone, state why). - 3. Also spot-check any finding marked CRITICAL/P0 that lacks a clear file:line citation. - - Output a structured verdict per claim. Never modify any file. - - ## Reviewer outputs to verify: - - ### Architecture Review - {{review-arch}} - - ### Logic Review - {{review-logic}} - - ### Style Review - {{review-style}} - - - id: arbitrate - name: arbitrate - worker_type: review - depends_on: [verify-claims] - required: true - report_to_parent: true - output_schema: - type: object - required: [verdict, summary, findings, required_actions, next_action] - properties: - verdict: - type: string - enum: [ACCEPT, REVISE, REJECT, BLOCKED] - summary: - type: string - findings: - type: array - items: - type: object - properties: - severity: { type: string } - title: { type: string } - evidence: { type: string } - status: { type: string, enum: [confirmed, refuted, inconclusive] } - required_actions: - type: array - items: { type: string } - next_action: - type: object - required: [operation, targets] - properties: - operation: - type: string - enum: [continue, extend, replan, complete, stop] - targets: - type: array - items: { type: string } - prompt_template: - inline: | - You are the arbiter for a deep review of the DAG workflow engine (packages/opencode/src/dag/). - - Three reviewers (architecture, logic, style) produced findings. A verification node then checked all unverified_claims against the actual code. - - Your task: - 1. Rule finding-by-finding: accept only findings with CONFIRMED evidence. Discard REFUTED claims. Flag INCONCLUSIVE items as residual risk. - 2. Deduplicate overlapping findings across reviewers. - 3. Rank confirmed findings by severity and blast radius. - 4. Emit a structured verdict: - - ACCEPT: no CRITICAL/P0 confirmed findings, module is sound. - - REVISE: confirmed findings exist but are addressable without redesign. - - REJECT: confirmed CRITICAL/P0 findings require structural rework. - - BLOCKED: verification was insufficient to rule. - 5. Provide required_actions (concrete, file-scoped) and next_action for the orchestrator. - - Never modify any file. Base your ruling ONLY on verified evidence from the verification node. - - ## Verification Results - {{verify-claims}} - - - 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 the DAG module review. Its findings and required actions are below. - - For each required_action: - 1. Open the cited file(s) and verify the problem still exists at the stated location. - 2. Produce a corrected, evidence-backed remediation plan: exact file, function, what to change, and why. - 3. Identify any dependencies between actions (ordering constraints). - 4. Flag any action that is infeasible or would cause a regression. - - Output a prioritized remediation plan. Never modify any file. - - ## Arbiter Verdict - {{arbitrate}} diff --git a/.opencode/workflows/deep-perf-review.yaml b/.opencode/workflows/deep-perf-review.yaml deleted file mode 100644 index c81e88d9c6..0000000000 --- a/.opencode/workflows/deep-perf-review.yaml +++ /dev/null @@ -1,346 +0,0 @@ -title: "Deep Performance Review - opencode-dag" -mode: deep -admission: - brief_revision: 1 - qa_mode: LIGHT - verdict: READY - brief: - goal: "对 opencode-dag 仓库进行深度性能审查,识别热路径、启动瓶颈、I/O 低效、并发问题和内存压力" - scope: - in: - - "packages/opencode (core runtime, session, DAG engine, event system, storage)" - - "packages/tui (SolidJS rendering, reactivity)" - - "packages/plugin (plugin lifecycle)" - - "packages/schema (serialization)" - - "packages/sdk/js (generated client)" - - "packages/client (HTTP client)" - out: - - "功能正确性(非性能维度)" - - "代码风格/命名" - - "测试覆盖率(除非性能测试缺失)" - constraints: - - "只读审查,不修改任何文件" - - "所有发现必须附带 file:line 证据" - - "无法验证的声明必须标记为 unverified_claims" - assumptions: - - "当前工作树即审查目标" - - "Bun 为运行时" - - "Effect-TS 为核心框架" - acceptance_criteria: - - "每个发现都有 file:line 代码引用" - - "发现按严重程度排序(CRITICAL/HIGH/MEDIUM/LOW)" - - "包含具体优化建议" - - "unverified_claims 经过验证波次确认" - evidence_required: - - "代码引用(file:line)" - - "算法复杂度分析" - - "调用链/数据流路径" - risks: - - "跨模块性能回归被遗漏" - - "Effect-TS 特有的性能陷阱(Layer 构建、Fiber 泄漏)" - - "DAG 引擎在大规模工作流下的扩展性" - review_plan: - - "Wave 1: 探索性能关键表面" - - "Wave 2: 5 个并行维度审查(启动/热路径/IO/并发/TUI)" - - "Wave 3: 验证波次检查 unverified_claims" - - "Wave 4: 仲裁节点裁决并排序" - open_questions: [] - blocking_questions: [] -config: - name: deep-perf-review - max_concurrency: 6 - max_node_replan_attempts: 3 - max_total_nodes: 20 - node_defaults: - required: false - report_to_parent: false - worker_config: - timeout_ms: 900000 - nodes: - - id: explore-perf - name: "Performance Surface Mapping" - worker_type: explore - depends_on: [] - required: true - prompt_template: - inline: | - 你是性能探索专家。映射 opencode-dag 仓库中所有性能关键的代码表面。 - - 重点探索: - 1. 启动路径:入口点 → 服务初始化 → Layer 构建链(packages/opencode/src 的 main/index/entry) - 2. 热循环:DAG 调度循环、事件处理循环、Session drain 循环 - 3. I/O 边界:SQLite/Drizzle 查询、文件读写、HTTP 请求、MCP 通信 - 4. 内存敏感区:事件存储、会话历史、知识图谱缓存 - 5. TUI 渲染:SolidJS 响应式更新、列表渲染、事件流消费 - - 输出格式: - - 每个表面:文件路径、关键函数、调用深度、潜在瓶颈假设 - - 标记哪些区域需要深入审查 - - 列出 packages/opencode/src 下的核心模块及其职责 - - 使用 codebase-memory-mcp 工具(search_graph, trace_path, get_architecture)优先,grep/glob 作为补充。 - - - id: review-startup - name: "Startup & Lazy Loading Review" - worker_type: review - depends_on: [explore-perf] - review: - phase: design - prompt_template: - inline: | - 你是启动性能审查专家。审查 opencode-dag 的启动性能和模块加载策略。 - - 审查维度: - 1. 入口点到首个可交互状态的完整路径(packages/opencode/src/index.ts 或 main) - 2. Effect Layer 构建链:哪些 Layer 是 eager 的?哪些可以 lazy? - 3. 动态 import 使用情况:是否有不必要的静态 import 拖慢启动? - 4. 服务初始化顺序:是否有可以并行化的串行初始化? - 5. 配置加载、插件发现、MCP 服务器启动的开销 - - 规则: - - 每个发现必须引用 file:line - - 无法确认的声明放入 unverified_claims 部分 - - 按 CRITICAL/HIGH/MEDIUM/LOW 分级 - - 给出具体的优化建议 - - 输出结构: - ## findings - ## unverified_claims - ## recommendations - - - id: review-hotpath - name: "Hot Path & Algorithmic Complexity Review" - worker_type: review - depends_on: [explore-perf] - review: - phase: design - prompt_template: - inline: | - 你是算法性能审查专家。审查 opencode-dag 中的热路径和算法复杂度问题。 - - 审查维度: - 1. DAG 调度器:节点就绪检测、拓扑排序、并发调度的复杂度 - 2. 事件系统:事件分发、订阅匹配、历史回放的效率 - 3. Session 管理:消息序列化/反序列化、上下文窗口管理 - 4. 搜索/索引:codebase-memory-mcp 集成、代码搜索路径 - 5. 循环内的线性扫描(find/includes/indexOf in loop) - 6. 不必要的重复计算或缺失的缓存 - - 使用 codebase-memory-mcp 的 query_graph 查询: - - transitive_loop_depth >= 3 的函数 - - linear_scan_in_loop >= 1 的函数 - - alloc_in_loop 的函数 - - 规则: - - 每个发现必须引用 file:line - - 标注大 O 复杂度 - - 无法确认的声明放入 unverified_claims - - 按 CRITICAL/HIGH/MEDIUM/LOW 分级 - - 输出结构: - ## findings - ## unverified_claims - ## recommendations - - - id: review-io - name: "I/O & Database Efficiency Review" - worker_type: review - depends_on: [explore-perf] - review: - phase: design - prompt_template: - inline: | - 你是 I/O 性能审查专家。审查 opencode-dag 的数据库访问、文件 I/O 和网络通信效率。 - - 审查维度: - 1. SQLite/Drizzle 查询:N+1 查询、缺失索引、全表扫描、事务粒度 - 2. 文件 I/O:是否有可以批处理的小文件读写?Bun.file() 使用是否最优? - 3. HTTP 通信:SDK client 调用是否有不必要的串行请求?连接复用? - 4. MCP 通信:stdio/SSE 传输的效率、消息序列化开销 - 5. 事件持久化:写入频率、批量策略、WAL 模式使用 - 6. 日志系统:是否有过度日志影响性能? - - 规则: - - 每个发现必须引用 file:line - - 标注 I/O 模式(同步/异步/批量) - - 无法确认的声明放入 unverified_claims - - 按 CRITICAL/HIGH/MEDIUM/LOW 分级 - - 输出结构: - ## findings - ## unverified_claims - ## recommendations - - - id: review-concurrency - name: "Concurrency & Memory Review" - worker_type: review - depends_on: [explore-perf] - review: - phase: design - prompt_template: - inline: | - 你是并发和内存性能审查专家。审查 opencode-dag 的异步模式、内存使用和 Effect-TS 特有性能问题。 - - 审查维度: - 1. Fiber 管理:是否有泄漏的 Fiber?未取消的后台任务? - 2. Layer 构建:重复构建昂贵 Layer?Layer 缓存策略? - 3. 背压:事件流消费是否有背压机制?DAG 节点产出是否有限流? - 4. 内存:大对象生命周期、缓存淘汰策略、会话历史增长 - 5. 锁/信号量:是否有不必要的串行化?死锁风险? - 6. GC 压力:频繁分配/释放、大数组拷贝、字符串拼接 - - 规则: - - 每个发现必须引用 file:line - - 标注并发模式(fork/join/race/stream) - - 无法确认的声明放入 unverified_claims - - 按 CRITICAL/HIGH/MEDIUM/LOW 分级 - - 输出结构: - ## findings - ## unverified_claims - ## recommendations - - - id: review-tui - name: "TUI Rendering & Reactivity Review" - worker_type: review - depends_on: [explore-perf] - review: - phase: design - prompt_template: - inline: | - 你是前端/TUI 渲染性能审查专家。审查 packages/tui 的 SolidJS/opentui 渲染性能。 - - 审查维度: - 1. 响应式更新:是否有过度细粒度的 signal 导致频繁重渲染? - 2. 列表渲染:大列表(会话列表、DAG 节点列表)是否虚拟化? - 3. 事件流消费:SSE 事件处理是否高效?是否有节流/防抖? - 4. 组件结构:是否有不必要的组件重建?memo 使用是否合理? - 5. 状态管理:store 更新是否触发不必要的依赖计算? - 6. 键盘输入处理:是否有输入延迟? - - 规则: - - 每个发现必须引用 file:line(packages/tui/src/ 下) - - 无法确认的声明放入 unverified_claims - - 按 CRITICAL/HIGH/MEDIUM/LOW 分级 - - 输出结构: - ## findings - ## unverified_claims - ## recommendations - - - id: verify-claims - name: "Claim Verification" - worker_type: verify - depends_on: [review-startup, review-hotpath, review-io, review-concurrency, review-tui] - required: true - prompt_template: - inline: | - 你是性能声明验证专家。你的任务是验证上游 5 个审查节点产出的 unverified_claims。 - - 对每个 unverified_claim: - 1. 定位到实际代码(file:line) - 2. 确认或否定该声明 - 3. 如果确认,补充具体证据(代码片段、调用链、复杂度分析) - 4. 如果否定,说明为什么该声明不成立 - - 同时交叉检查: - - 不同审查者是否对同一代码段有矛盾结论? - - 是否有跨维度的复合性能问题(如 I/O + 并发)? - - 输出: - ## verified_claims(确认的声明 + 证据) - ## rejected_claims(否定的声明 + 原因) - ## cross_cutting_issues(跨维度问题) - ## conflicts(审查者间的矛盾) - - - id: arbitrate - name: "Performance Arbitration" - worker_type: general - depends_on: [verify-claims] - required: true - report_to_parent: true - output_schema: - type: object - required: [verdict, summary, findings, required_actions, next_action] - properties: - verdict: - type: string - enum: [ACCEPT, REVISE, REJECT, BLOCKED] - summary: - type: string - findings: - type: array - items: - type: object - properties: - severity: - type: string - enum: [CRITICAL, HIGH, MEDIUM, LOW] - category: - type: string - location: - type: string - description: - type: string - recommendation: - type: string - required_actions: - type: array - items: - type: string - next_action: - type: object - required: [operation, targets] - properties: - operation: - type: string - enum: [continue, extend, replan, complete, stop] - targets: - type: array - items: - type: string - prompt_template: - inline: | - 你是性能审查仲裁者。基于验证波次的结果,对 opencode-dag 的性能状况做出最终裁决。 - - 你的输入: - - 5 个维度审查者的发现(startup, hotpath, io, concurrency, tui) - - 验证节点的确认/否定结果 - - 裁决规则: - 1. 只基于已验证的证据做判断,忽略被否定的声明 - 2. 去重:多个审查者报告同一问题时合并 - 3. 排序:按实际性能影响排序(CRITICAL > HIGH > MEDIUM > LOW) - 4. 每个 finding 必须包含:severity, category, location(file:line), description, recommendation - 5. 解决审查者间的矛盾,给出理由 - - verdict 含义: - - ACCEPT: 无 CRITICAL/HIGH 问题,性能状况可接受 - - REVISE: 有 HIGH 问题需要关注但不阻塞 - - REJECT: 有 CRITICAL 问题需要立即修复 - - BLOCKED: 无法做出判断(证据不足) - - 调用 submit_result 提交结构化裁决。 - - - id: deep-dive - name: "Performance Deep Dive" - worker_type: general - depends_on: [arbitrate] - condition: 'arbitrate.output.verdict != "ACCEPT"' - report_to_parent: true - prompt_template: - inline: | - 仲裁者发现了需要关注的性能问题。对每个 required_action 进行深入分析: - - 1. 定位具体代码,确认问题存在 - 2. 分析根因(为什么会出现这个问题?) - 3. 评估影响范围(哪些用户场景受影响?影响程度?) - 4. 给出具体的修复方案(代码级别的建议) - 5. 评估修复成本和风险 - - 输出:按优先级排序的修复计划,每项包含: - - 问题描述 + file:line - - 根因分析 - - 影响评估 - - 修复方案(具体代码变更建议) - - 成本/风险评估 diff --git a/.opencode/workflows/full-codebase-critical-review.yaml b/.opencode/workflows/full-codebase-critical-review.yaml deleted file mode 100644 index 6363aecc5b..0000000000 --- a/.opencode/workflows/full-codebase-critical-review.yaml +++ /dev/null @@ -1,251 +0,0 @@ -title: "OpenCode Full Codebase Review - Critical Bugs & Performance" -config: - name: full-codebase-critical-review - max_concurrency: 4 - max_node_replan_attempts: 3 - node_defaults: - worker_config: - timeout_ms: 600000 - nodes: - - id: review-concurrency - name: "Concurrency & Resource Leaks" - worker_type: general - depends_on: [] - report_to_parent: false - prompt_template: - inline: | - 你是并发与资源泄漏专家。对 opencode 代码库进行只读审查,只关注严重 bug 和性能问题。 - - ## 审查维度 - - 竞态条件:并发访问共享状态无保护、TOCTOU - - 资源泄漏:未关闭的进程/文件句柄/定时器/EventSource/AbortController - - 死锁/活锁:Effect 层中的循环依赖、Fiber 泄漏 - - 内存泄漏:无限增长的 Map/Array、未清理的订阅 - - ## 重点文件(来自知识图谱热点) - - packages/opencode/src/cli/cmd/run/stream.transport.ts (complexity 79, 43 callees) - - packages/opencode/src/cli/cmd/run/runtime.ts (complexity 46, 16 callees) - - packages/opencode/src/lsp/server.ts (unguarded_recursion in spawn) - - packages/opencode/src/session/ (session lifecycle) - - packages/core/src/process.ts (unguarded_recursion in describeCommand) - - packages/core/src/session/runner/ (LLM event publishing) - - packages/app/src/context/terminal.tsx (clearWorkspaceTerminals) - - packages/opencode/src/dag/ (DAG runtime, workflow execution) - - ## 工具使用 - 优先使用 codebase-memory-mcp 工具(project: "opencode-dag"): - - search_graph 查找函数定义 - - get_code_snippet 读取源码 - - trace_path 追踪调用链 - - query_graph 查询复杂模式 - - ## 输出要求 - 对每个发现输出: - - 严重性:CRITICAL / HIGH - - 文件:行号 - - 问题描述(一句话) - - 证据(代码片段或调用链) - - 潜在影响 - - 只报告 CRITICAL 和 HIGH 级别。不要报告风格问题、命名问题或低影响问题。 - 最多报告 10 个最严重的问题。 - - - id: review-performance - name: "Performance Hotspots" - worker_type: general - depends_on: [] - report_to_parent: false - prompt_template: - inline: | - 你是性能优化专家。对 opencode 代码库进行只读审查,只关注严重性能问题。 - - ## 审查维度 - - O(n²) 或更差的算法复杂度(循环内线性扫描) - - 热路径上的不必要分配(alloc_in_loop) - - 大数据集的同步阻塞操作 - - 不必要的重复计算/渲染 - - 内存膨胀(大对象未释放、缓存无上限) - - ## 重点文件(来自知识图谱热点) - - packages/app/src/pages/session.tsx (Page complexity 194!, transitive_loop_depth 6) - - packages/app/src/pages/session/timeline/message-timeline.tsx (complexity 119, 7 loops) - - packages/core/src/github-copilot/responses/convert-to-openai-responses-input.ts (complexity 47, 19 alloc_in_loop!) - - packages/opencode/src/cli/cmd/run/session-data.ts (reduceSessionData complexity 63) - - packages/session-ui/src/components/session-turn.tsx (complexity 44, transitive_loop_depth 7) - - packages/app/src/context/global-sync/event-reducer.ts (complexity 56) - - packages/tui/src/component/prompt/autocomplete.tsx (complexity 45, 2 alloc_in_loop) - - packages/console/app/src/routes/zen/util/handler.ts (complexity 113, cognitive 214) - - ## 工具使用 - 优先使用 codebase-memory-mcp 工具(project: "opencode-dag"): - - query_graph 查询性能模式 - - get_code_snippet 读取源码 - - trace_path 追踪热路径 - - ## 输出要求 - 对每个发现输出: - - 严重性:CRITICAL / HIGH - - 文件:行号 - - 问题描述(一句话) - - 证据(代码片段 + 复杂度数据) - - 性能影响估算 - - 只报告 CRITICAL 和 HIGH 级别。不要报告微小的优化机会。 - 最多报告 10 个最严重的问题。 - - - id: review-error-handling - name: "Error Handling & Data Corruption" - worker_type: general - depends_on: [] - report_to_parent: false - prompt_template: - inline: | - 你是错误处理与数据完整性专家。对 opencode 代码库进行只读审查,只关注可能导致数据损坏或系统崩溃的严重问题。 - - ## 审查维度 - - 静默吞掉错误导致状态不一致 - - 未处理的 Promise rejection / Effect 失败 - - 数据库/文件写入的原子性缺失 - - 状态机非法转换 - - 类型断言绕过安全检查(as any, as unknown) - - ## 重点文件 - - packages/opencode/src/dag/ (DAG 状态机、workflow 持久化) - - packages/opencode/src/session/ (session 生命周期、消息持久化) - - packages/opencode/src/server/ (HTTP API 错误处理) - - packages/core/src/session/runner/ (LLM 流式处理错误) - - packages/llm/src/route/executor.ts (retryStatusFailures - unguarded_recursion) - - packages/llm/src/protocols/shared.ts (removeNullSchemas - unguarded_recursion) - - packages/opencode/src/plugin/ (插件系统错误隔离) - - ## 工具使用 - 优先使用 codebase-memory-mcp 工具(project: "opencode-dag"): - - search_code 搜索错误处理模式 - - get_code_snippet 读取源码 - - trace_path 追踪错误传播路径 - - ## 输出要求 - 对每个发现输出: - - 严重性:CRITICAL / HIGH - - 文件:行号 - - 问题描述(一句话) - - 证据(代码片段) - - 数据损坏/崩溃场景 - - 只报告 CRITICAL 和 HIGH 级别。不要报告日志缺失或错误消息不清晰。 - 最多报告 10 个最严重的问题。 - - - id: review-critical-logic - name: "Critical Path Logic Bugs" - worker_type: general - depends_on: [] - report_to_parent: false - prompt_template: - inline: | - 你是逻辑正确性专家。对 opencode 代码库的核心路径进行只读审查,只关注可能导致功能完全失效的严重逻辑 bug。 - - ## 审查维度 - - 边界条件错误(off-by-one、空集合、null/undefined) - - 异步时序错误(await 缺失、竞态窗口) - - 状态不一致(读写不同源、缓存失效) - - 协议违规(HTTP/SSE/WebSocket 协议假设错误) - - 递归终止条件缺失 - - ## 重点文件 - - packages/opencode/src/dag/runtime/ (DAG 调度、节点生命周期) - - packages/opencode/src/session/v2/ (V2 session 核心) - - packages/core/src/session/runner/ (模型调用循环) - - packages/llm/src/route/ (LLM 路由、流式解析) - - packages/opencode/src/server/routes/ (API 路由) - - packages/cli/src/commands/handlers/serve.ts (unguarded_recursion in next) - - packages/opencode/src/acp/service.ts (16 callees, complexity 16) - - ## 工具使用 - 优先使用 codebase-memory-mcp 工具(project: "opencode-dag"): - - get_code_snippet 读取核心函数源码 - - trace_path 追踪关键调用链 - - query_graph 查询递归/循环模式 - - ## 输出要求 - 对每个发现输出: - - 严重性:CRITICAL / HIGH - - 文件:行号 - - 问题描述(一句话) - - 证据(代码片段 + 触发条件) - - 失效场景 - - 只报告 CRITICAL 和 HIGH 级别。不要报告代码可读性或设计偏好。 - 最多报告 10 个最严重的问题。 - - - id: arbitrate - name: "Arbiter - Synthesize & Rank" - worker_type: general - depends_on: [review-concurrency, review-performance, review-error-handling, review-critical-logic] - required: true - report_to_parent: true - output_schema: - type: object - required: [verdict, summary, findings, required_actions, next_action] - properties: - verdict: - type: string - enum: [ACCEPT, REVISE, REJECT, BLOCKED] - summary: - type: string - findings: - type: array - items: - type: object - required: [severity, file, description, evidence] - properties: - severity: - type: string - enum: [CRITICAL, HIGH] - file: - type: string - line: - type: string - description: - type: string - evidence: - type: string - impact: - type: string - category: - type: string - required_actions: - type: array - items: - type: string - next_action: - type: object - required: [operation, targets] - properties: - operation: - type: string - enum: [continue, extend, replan, complete, stop] - targets: - type: array - prompt_template: - inline: | - 你是高级仲裁者。四个独立审查者分别从并发/资源泄漏、性能热点、错误处理/数据损坏、关键路径逻辑 bug 四个维度审查了 opencode 代码库。 - - ## 你的任务 - 1. 去重:合并指向同一根因的发现 - 2. 验证:对每个发现评估证据是否充分,标记证据不足的为"待验证" - 3. 排序:按影响范围和触发概率排序 - 4. 裁决: - - ACCEPT:所有发现都有充分证据,可以输出最终报告 - - REVISE:部分发现需要更深入的代码验证 - - REJECT:大部分发现证据不足 - - BLOCKED:无法完成审查 - - ## 输出 - 调用 submit_result 提交结构化结果: - - verdict: 裁决 - - summary: 一句话总结代码库健康度 - - findings: 去重排序后的发现列表(最多 15 个最严重的) - - required_actions: 建议的修复优先级 - - next_action: 工作流下一步操作 - - 严格只保留 CRITICAL 和 HIGH 级别的发现。每个 finding 必须有具体的文件路径和代码证据。 diff --git a/.opencode/workflows/perf-deep-review.yaml b/.opencode/workflows/perf-deep-review.yaml deleted file mode 100644 index 76c13c42a4..0000000000 --- a/.opencode/workflows/perf-deep-review.yaml +++ /dev/null @@ -1,376 +0,0 @@ -title: "Deep Performance Review - opencode-dag" -mode: deep -admission: - brief_revision: 1 - qa_mode: LIGHT - verdict: READY - brief: - goal: "深度性能审查 opencode-dag 项目,识别算法瓶颈、资源泄漏、I/O 阻塞、启动延迟、数据层低效" - scope: - in: - - "packages/core" - - "packages/opencode" - - "packages/tui" - - "packages/app" - - "packages/server" - - "packages/llm" - - "packages/sdk" - - "packages/client" - - "packages/plugin" - - "packages/schema" - out: - - "第三方依赖内部实现" - - "CI/CD 配置" - - "storybook mocks" - constraints: - - "只读审查,不修改任何文件" - - "所有发现必须引用 file:line 证据" - - "使用 codebase-memory-mcp 工具进行结构化分析" - assumptions: - - "工作树即审查目标" - - "main 分支当前 HEAD 为审查版本" - acceptance_criteria: - - "每个性能发现都有 file:line 证据支撑" - - "发现按严重程度排序(CRITICAL/HIGH/MEDIUM/LOW)" - - "提供可操作的修复建议" - - "区分已验证事实和未验证推测" - evidence_required: - - "代码引用(file:line)" - - "复杂度指标(loop_depth, linear_scan_in_loop 等)" - - "调用链分析" - risks: - - "遗漏跨包性能回归" - - "误报:静态分析无法确认运行时热点" - review_plan: - - "Wave 1: 探索性能关键表面(hotspots + architecture)" - - "Wave 2: 5 维度并行审查(算法/内存/IO/启动/数据层)" - - "Wave 3: 证据验证(检查 unverified_claims)" - - "Wave 4: 仲裁(去重、排序、结构化裁决)" - - "Wave 5: 深挖(verdict 驱动的条件节点)" - open_questions: [] - blocking_questions: [] -config: - name: perf-deep-review - max_concurrency: 8 - max_node_replan_attempts: 3 - max_total_nodes: 30 - node_defaults: - required: false - report_to_parent: false - worker_config: - timeout_ms: 900000 - nodes: - - id: explore-hotspots - name: "Explore Performance Hotspots" - worker_type: explore - depends_on: [] - required: true - prompt_template: - inline: | - 你是性能探索专家。使用 codebase-memory-mcp 工具分析 opencode-dag 项目的性能热点。 - - 执行以下分析: - 1. 使用 query_graph 查找高复杂度函数: - - transitive_loop_depth >= 3 的函数 - - linear_scan_in_loop >= 1 的函数 - - alloc_in_loop >= 1 的函数 - - recursive 标记为 true 的函数 - 2. 使用 trace_path 追踪 fan_in 最高的 top 20 函数的调用链 - 3. 识别 packages/core, packages/opencode, packages/tui, packages/server 中的热路径 - - 输出格式: - - 按严重程度排序的热点列表 - - 每个热点包含:qualified_name, file:line, 复杂度指标, 调用频率估计 - - 标注跨包调用链中的性能瓶颈 - - 项目名:opencode-dag - - - id: explore-arch - name: "Explore Performance Architecture" - worker_type: explore - depends_on: [] - required: true - prompt_template: - inline: | - 你是架构性能分析专家。探索 opencode-dag 项目的性能相关架构设计。 - - 分析以下方面: - 1. 缓存策略:搜索 cache, memo, lru 相关实现,评估是否有无界缓存 - 2. 懒加载设计:搜索 dynamic import, lazy 模式,评估启动路径是否过重 - 3. 事件系统:搜索 EventBus, subscribe, emit 模式,评估是否有监听器泄漏风险 - 4. 连接管理:搜索 HttpClient, WebSocket, connection pool 模式 - 5. 序列化开销:搜索 JSON.parse, JSON.stringify, Schema.decode 在热路径中的使用 - - 使用 codebase-memory-mcp 的 search_code 和 search_graph 工具。 - 项目名:opencode-dag - - 输出:每个方面的架构现状 + 潜在性能风险 + file:line 证据 - - - id: review-algorithmic - name: "Review: Algorithmic Complexity" - worker_type: general - depends_on: [explore-hotspots] - prompt_template: - inline: | - 你是算法复杂度审查专家。基于以下热点探索结果,深入审查算法性能问题。 - - 探索结果: - {{explore-hotspots}} - - 审查重点: - 1. O(n²) 或更差的算法模式(嵌套循环 + 线性搜索) - 2. 不必要的重复计算(缺少 memoization) - 3. 递归深度风险(无尾递归优化、无深度限制) - 4. 数据结构选择不当(应该用 Map/Set 却用 Array 查找) - 5. 排序/过滤在热路径中的重复执行 - - 规则: - - 每个发现必须引用 file:line - - 使用 codebase-memory-mcp 的 get_code_snippet 验证具体实现 - - 无法验证的声明标记为 unverified_claims - - 按 CRITICAL/HIGH/MEDIUM/LOW 分级 - - 项目名:opencode-dag - - - id: review-memory - name: "Review: Memory & Resource Management" - worker_type: general - depends_on: [explore-hotspots, explore-arch] - prompt_template: - inline: | - 你是内存与资源管理审查专家。审查 opencode-dag 的内存泄漏和资源管理问题。 - - 架构探索结果: - {{explore-arch}} - - 审查重点: - 1. 事件监听器未清理(subscribe 无对应 unsubscribe) - 2. 无界缓存/集合持续增长(Map, Set, Array 无淘汰策略) - 3. 闭包持有大对象引用(阻止 GC) - 4. Effect 纤维泄漏(fork 后未 join/interrupt) - 5. 文件句柄/连接未关闭 - 6. 定时器(setInterval/setTimeout)未清理 - - 规则: - - 每个发现必须引用 file:line - - 使用 search_code 搜索 subscribe/addEventListener/setInterval 等模式 - - 无法验证的声明标记为 unverified_claims - - 按 CRITICAL/HIGH/MEDIUM/LOW 分级 - - 项目名:opencode-dag - - - id: review-io - name: "Review: I/O & Concurrency" - worker_type: general - depends_on: [explore-arch] - prompt_template: - inline: | - 你是 I/O 与并发审查专家。审查 opencode-dag 的异步性能和阻塞问题。 - - 架构探索结果: - {{explore-arch}} - - 审查重点: - 1. 同步阻塞操作在异步上下文中(readFileSync, execSync) - 2. 串行化的并行机会(await 循环 vs Promise.all/Effect.all) - 3. 缺少背压控制的流处理 - 4. HTTP 请求无超时/重试/连接复用 - 5. 大文件一次性读入内存(应流式处理) - 6. 锁竞争或过度序列化 - - 规则: - - 每个发现必须引用 file:line - - 使用 search_code 搜索 readFileSync/execSync/await.*for 等模式 - - 无法验证的声明标记为 unverified_claims - - 按 CRITICAL/HIGH/MEDIUM/LOW 分级 - - 项目名:opencode-dag - - - id: review-startup - name: "Review: Startup & Bundle Performance" - worker_type: general - depends_on: [explore-arch] - prompt_template: - inline: | - 你是启动与构建性能审查专家。审查 opencode-dag 的启动时间和 bundle 效率。 - - 架构探索结果: - {{explore-arch}} - - 审查重点: - 1. 入口点(packages/opencode/src/index.ts, packages/cli)的 import 深度 - 2. 应该动态导入但静态导入的重模块 - 3. 模块初始化副作用(顶层 await、立即执行的数据库连接) - 4. Layer 构建顺序中的不必要串行化 - 5. TUI 渲染首屏的关键路径长度 - 6. 重复初始化(同一服务被多个路径触发) - - 规则: - - 每个发现必须引用 file:line - - 使用 trace_path 追踪入口点的 outbound 调用深度 - - 无法验证的声明标记为 unverified_claims - - 按 CRITICAL/HIGH/MEDIUM/LOW 分级 - - 项目名:opencode-dag - - - id: review-data - name: "Review: Data Layer Performance" - worker_type: general - depends_on: [explore-hotspots, explore-arch] - prompt_template: - inline: | - 你是数据层性能审查专家。审查 opencode-dag 的数据库和序列化性能。 - - 热点探索结果: - {{explore-hotspots}} - - 架构探索结果: - {{explore-arch}} - - 审查重点: - 1. SQLite 查询缺少索引(WHERE/JOIN 字段) - 2. N+1 查询模式(循环中逐条查询) - 3. 大结果集未分页(SELECT * 无 LIMIT) - 4. 频繁序列化/反序列化(JSON.parse/stringify 在热路径) - 5. Drizzle ORM 使用不当(缺少事务批量操作) - 6. 事件存储的读写放大 - - 规则: - - 每个发现必须引用 file:line - - 使用 search_code 搜索 db.select/db.insert/JSON.parse 等模式 - - 检查 packages/core/src 和 packages/opencode/src 中的数据访问层 - - 无法验证的声明标记为 unverified_claims - - 按 CRITICAL/HIGH/MEDIUM/LOW 分级 - - 项目名:opencode-dag - - - id: verify-claims - name: "Verify Performance Claims" - worker_type: verify - depends_on: [review-algorithmic, review-memory, review-io, review-startup, review-data] - required: true - prompt_template: - inline: | - 你是性能声明验证专家。验证以下 5 个审查报告中所有标记为 unverified_claims 的声明,以及所有 CRITICAL/HIGH 级别发现。 - - 审查报告: - 算法复杂度:{{review-algorithmic}} - 内存资源:{{review-memory}} - I/O并发:{{review-io}} - 启动性能:{{review-startup}} - 数据层:{{review-data}} - - 验证方法: - 1. 对每个 unverified_claim,使用 codebase-memory-mcp 的 get_code_snippet 读取实际代码 - 2. 对每个 CRITICAL/HIGH 发现,确认 file:line 引用准确且问题真实存在 - 3. 检查是否有误报(代码已修复、有缓解措施、或分析错误) - 4. 使用 query_graph 验证复杂度指标 - - 输出: - - VERIFIED: 确认为真的发现列表 - - REFUTED: 被证伪的声明列表(附原因) - - UNCERTAIN: 无法静态确认的声明(需要运行时 profiling) - - 项目名:opencode-dag - - - id: arbitrate - name: "Performance Arbitration" - worker_type: general - depends_on: [verify-claims] - required: true - report_to_parent: true - output_schema: - type: object - required: [verdict, summary, findings, required_actions, next_action] - properties: - verdict: - type: string - enum: [ACCEPT, REVISE, REJECT, BLOCKED] - summary: - type: string - findings: - type: array - items: - type: object - required: [severity, category, description, evidence, recommendation] - properties: - severity: - type: string - enum: [CRITICAL, HIGH, MEDIUM, LOW] - category: - type: string - description: - type: string - evidence: - type: string - recommendation: - type: string - required_actions: - type: array - items: - type: string - next_action: - type: object - required: [operation, targets] - properties: - operation: - type: string - enum: [continue, extend, replan, complete, stop] - targets: - type: array - items: - type: string - prompt_template: - inline: | - 你是性能审查仲裁专家。基于已验证的证据,做出最终裁决。 - - 验证结果: - {{verify-claims}} - - 仲裁规则: - 1. 只采纳 VERIFIED 的发现作为正式 findings - 2. REFUTED 的声明不得出现在 findings 中 - 3. UNCERTAIN 的发现降级为 MEDIUM 并标注"需要运行时验证" - 4. 去重:相同根因的多个表现合并为一条 - 5. 按严重程度排序:CRITICAL > HIGH > MEDIUM > LOW - 6. 每条 finding 必须有可操作的修复建议 - - Verdict 判定: - - ACCEPT: 无 CRITICAL/HIGH 发现,项目性能状况良好 - - REVISE: 存在 HIGH 发现但无 CRITICAL,需要改进 - - REJECT: 存在 CRITICAL 发现,需要立即修复 - - BLOCKED: 无法完成审查(工具不可用等) - - next_action: - - ACCEPT → operation: complete - - REVISE/REJECT → operation: extend, targets: 需要深挖的具体模块/文件 - - 使用 submit_result 提交结构化裁决。 - 项目名:opencode-dag - - - id: deep-dive - name: "Performance Deep Dive" - worker_type: general - depends_on: [arbitrate] - condition: 'arbitrate.output.verdict != "ACCEPT"' - report_to_parent: true - prompt_template: - inline: | - 你是性能深挖专家。仲裁未通过,需要对确认的问题区域进行深入分析。 - - 仲裁裁决: - {{arbitrate}} - - 任务: - 1. 对每个 CRITICAL/HIGH finding,追踪完整的调用链(使用 trace_path) - 2. 量化影响:估算受影响的用户操作路径 - 3. 提供具体的修复方案(代码级别,不是泛泛建议) - 4. 识别修复的依赖顺序(哪些必须先修) - 5. 评估修复风险(是否可能引入回归) - - 输出: - - 每个 CRITICAL/HIGH 问题的完整修复计划 - - 修复优先级排序 - - 风险评估 - - 项目名:opencode-dag diff --git a/.opencode/workflows/review-dag-subsystem.yaml b/.opencode/workflows/review-dag-subsystem.yaml deleted file mode 100644 index 3c5cd7245d..0000000000 --- a/.opencode/workflows/review-dag-subsystem.yaml +++ /dev/null @@ -1,206 +0,0 @@ -title: "DAG Subsystem Review" -config: - name: dag-subsystem-review - max_concurrency: 5 - max_node_replan_attempts: 3 - node_defaults: - required: false - report_to_parent: false - worker_config: - timeout_ms: 600000 - nodes: - - id: explore-structure - name: explore-structure - worker_type: explore - depends_on: [] - required: true - prompt_template: - inline: | - Explore the DAG orchestration subsystem at packages/opencode/src/dag/. - Map: - 1. All files and their responsibilities (one line each) - 2. Internal module boundaries and dependency direction between files - 3. Public API surface (exports consumed by other packages/modules) - 4. External dependencies (what this subsystem imports from outside) - Output a structured inventory with file paths. - - - id: explore-runtime - name: explore-runtime - worker_type: explore - depends_on: [] - required: true - prompt_template: - inline: | - Explore the runtime behavior of the DAG subsystem at packages/opencode/src/dag/. - Map: - 1. Entry points: how workflows are created, started, and scheduled - 2. State machine: workflow and node lifecycle states, transitions - 3. Concurrency model: how parallel nodes execute, locking, coordination - 4. Error handling and recovery: crash recovery, replan, pause/resume - 5. Integration points: how the DAG connects to sessions, tools, events - Output structured findings with file:line references. - - - id: review-arch - name: review-arch - worker_type: review - depends_on: [explore-structure, explore-runtime] - prompt_template: - inline: | - Architecture review of the DAG subsystem at packages/opencode/src/dag/. - Focus: - - Layering violations and dependency direction - - Coupling between modules (circular deps, god objects) - - Separation of concerns (state vs execution vs persistence) - - Extension points and their adequacy - - Whether the module boundaries match the conceptual model - - RULES: - - Cite file:line for every finding - - List claims you could not verify as unverified_claims - - Do NOT modify any file - Output: findings list with severity (HIGH/MEDIUM/LOW), evidence, and unverified_claims section. - - - id: review-logic - name: review-logic - worker_type: review - depends_on: [explore-structure, explore-runtime] - prompt_template: - inline: | - Logic correctness review of the DAG subsystem at packages/opencode/src/dag/. - Focus: - - State machine completeness: unreachable states, missing transitions - - Race conditions in concurrent node scheduling - - Edge cases: empty graphs, single node, max concurrency boundaries - - Error propagation: does a node failure correctly affect dependents? - - Replan/recovery correctness: can state become inconsistent? - - Condition evaluation and skip semantics - - RULES: - - Cite file:line for every finding - - List claims you could not verify as unverified_claims - - Do NOT modify any file - Output: findings list with severity (HIGH/MEDIUM/LOW), evidence, and unverified_claims section. - - - id: review-conventions - name: review-conventions - worker_type: review - depends_on: [explore-structure, explore-runtime] - prompt_template: - inline: | - Code style and conventions review of the DAG subsystem at packages/opencode/src/dag/. - Check against the project AGENTS.md style guide: - - No unnecessary destructuring (prefer dot notation) - - No import aliases or star imports - - const over let, ternaries over reassignment - - No else statements (prefer early returns) - - Effect conventions: named service bindings, no nested yields - - Drizzle schema: snake_case fields - - Single-use helpers should be inlined - - Prefer Bun APIs - - No comments unless non-obvious constraints - - RULES: - - Cite file:line for every finding - - List claims you could not verify as unverified_claims - - Do NOT modify any file - Output: findings list with severity (HIGH/MEDIUM/LOW), evidence, and unverified_claims section. - - - id: verify-claims - name: verify-claims - worker_type: general - depends_on: [review-arch, review-logic, review-conventions] - required: true - prompt_template: - inline: | - You are the claim verification step. Three reviewers produced findings about - packages/opencode/src/dag/. Your job: - - 1. Collect all unverified_claims from all three reviews - 2. Collect all HIGH severity findings - 3. For each, open the actual source code and verify whether the claim is TRUE, FALSE, or PARTIALLY TRUE - 4. For disputed findings (where reviewers disagree), check the code and state which is correct - - RULES: - - Read the actual files. Do not speculate. - - For each claim, output: claim text, source file:line, verdict (CONFIRMED/REFUTED/PARTIAL), evidence - - Do NOT modify any file - - Upstream reviews: - {{review-arch}} - {{review-logic}} - {{review-conventions}} - - - id: arbitrate - name: arbitrate - worker_type: general - depends_on: [verify-claims] - required: true - report_to_parent: true - output_schema: - type: object - required: [verdict, summary, findings, required_actions, next_action] - properties: - verdict: - type: string - enum: [ACCEPT, REVISE, REJECT, BLOCKED] - summary: - type: string - findings: - type: array - items: - type: object - properties: - severity: { type: string } - description: { type: string } - evidence: { type: string } - verified: { type: boolean } - required_actions: - type: array - items: { type: string } - next_action: - type: object - required: [operation, targets] - properties: - operation: - type: string - enum: [continue, extend, replan, complete, stop] - targets: - type: array - items: { type: string } - prompt_template: - inline: | - You are the final arbiter for the DAG subsystem review. - - Based on the verified claims, rule finding-by-finding: - 1. Only include findings that were CONFIRMED or PARTIAL in the verification step - 2. Deduplicate overlapping findings across reviewers - 3. Rank by severity and impact - 4. Emit a structured verdict: - - ACCEPT: no HIGH findings survived verification - - REVISE: HIGH findings exist but are fixable with targeted changes - - REJECT: fundamental design issues require rethinking - - BLOCKED: cannot determine without additional information - 5. Provide required_actions (concrete fix steps) and next_action - - Verified evidence: - {{verify-claims}} - - - 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 the DAG subsystem review. - For each required_action in the arbiter's findings: - 1. Open the relevant source files - 2. Verify the problem exists as described - 3. Propose a concrete, minimal fix with code-level detail - 4. Identify any secondary effects of the proposed fix - - Do NOT modify any file. Output an evidence-backed action plan. - - Arbiter findings: - {{arbitrate}} diff --git a/.scratch/batch-a/issues/01-q1-escalation-pending-lifecycle.md b/.scratch/batch-a/issues/01-q1-escalation-pending-lifecycle.md deleted file mode 100644 index 62c43fde33..0000000000 --- a/.scratch/batch-a/issues/01-q1-escalation-pending-lifecycle.md +++ /dev/null @@ -1,17 +0,0 @@ -# 01 — Q1:escalation_pending 裁决旗生命周期闭环 - -**What to build:** 节点到终态(completed/failed/aborted)或被取消时,裁决状态旗 escalation_pending 必清零——裁决不可能悬挂超过它所属的裁决周期。wake_reported 投递旗不受影响(两旗正交)。终态清旗发生在事件折叠侧,与既有的 NodeStarted/NodeRestarted 清旗点共同构成完整生命周期。 - -规格依据:ADR-0001-escalation-pending-semantics + 节点生命周期转移表 v2(Q1 行)。 - -**Blocked by:** None — can start immediately - -**Status:** closed(PR #186,merge commit `4ddeaf2fc`) - -**Completion evidence:** 批次 A 实现与测试随 PR #186 合入 `dev`,并随 PR #188 通过 main 全量门禁。 - -- [x] 节点终态转移(completed/failed/aborted)与取消路径清 escalation_pending -- [x] wake_reported 在清旗路径上不被触碰(两旗正交测试) -- [x] 已有 NodeStarted/NodeRestarted 清旗点保持不回退 -- [x] replay/恢复场景下清旗经事件折叠重放一致 -- [x] dag 测试套件 + typecheck 绿 diff --git a/.scratch/batch-a/issues/02-q2-delivery-gated-retime.md b/.scratch/batch-a/issues/02-q2-delivery-gated-retime.md deleted file mode 100644 index 955ae776e6..0000000000 --- a/.scratch/batch-a/issues/02-q2-delivery-gated-retime.md +++ /dev/null @@ -1,17 +0,0 @@ -# 02 — Q2:送达门控 re-time(watchdog 提案者门控) - -**What to build:** wake 已送达未裁决期间,watchdog 不得抢占式 re-time。按 v2 机制实现:在 re-time 唯一写路径的发起点加 skip 合取项(escalationPending 且裁决未完成 ⇒ 跳过),而非放行析取项——deadline 驱动的初始升级路径不受影响,watchdog 保持纯提议者(提案不改状态)。 - -规格依据:ADR-0002-delivery-gated-retime(Round 2 修订版)+ 转移表 v2 G1 门控不变式。旧机制(放行析取臂)已证伪,禁止复用其表述。 - -**Blocked by:** None — can start immediately - -**Status:** closed(PR #186,merge commit `4ddeaf2fc`) - -**Completion evidence:** 批次 A 实现与测试随 PR #186 合入 `dev`,并随 PR #188 通过 main 全量门禁。 - -- [x] skip 合取项落在 re-time 唯一发起点,全 re-time 触发路径逐条覆盖(测试枚举,不只抄规格) -- [x] 初始升级(deadline ⟹ 首次 wake)不受门控影响 -- [x] 裁决写入后 re-time 能力恢复的测试 -- [x] watchdog 无状态写(仅提案)的断言保持 -- [x] dag 测试套件 + typecheck 绿 diff --git a/.scratch/batch-a/issues/03-q3-node-deadline-extended-event.md b/.scratch/batch-a/issues/03-q3-node-deadline-extended-event.md deleted file mode 100644 index f09f418f09..0000000000 --- a/.scratch/batch-a/issues/03-q3-node-deadline-extended-event.md +++ /dev/null @@ -1,18 +0,0 @@ -# 03 — Q3:NodeDeadlineExtended durable 事件 + guard 前移命令层 - -**What to build:** deadline 延长成为 durable 事件:NodeDeadlineExtended(nodeID + 新 deadline)入事件日志,废除直写 deadline 的旧路径。guard(延长是否被允许)前移到命令层执行——0 行 = 命令失败,编排器即时可观察(错误即状态);事件只记录成功,projector 保持纯幂等折叠(确定性重放)。guard 拒绝不是转移,不进事件日志。 - -规格依据:ADR-0003-node-deadline-extended-event(Round 2 修订版)+ 转移表 v2 T9/T11。旧机制(publish 返回行数契约)已证伪——发布链丢弃返回值,禁止复用。 - -**Blocked by:** 01 — Q1:escalation_pending 裁决旗生命周期闭环(projector 折叠侧写集串行) - -**Status:** closed(PR #186,merge commit `4ddeaf2fc`) - -**Completion evidence:** 批次 A 实现与测试随 PR #186 合入 `dev`,并随 PR #188 通过 main 全量门禁。 - -- [x] Schema 定义 NodeDeadlineExtended + 入 EventManifest.Definitions -- [x] 命令层执行 guard:拒绝时命令失败并携带 typed 错误,编排器可区分拒绝与成功 -- [x] 直写 deadline 旧路径废除(无遗留调用方) -- [x] projector 纯折叠:无事件发布、无返回值契约依赖 -- [x] 恢复/replay 一致性测试(事件日志重放 ⟺ 活跃态) -- [x] dag 测试套件 + typecheck 绿 diff --git a/.scratch/batch-a/issues/04-q3-sdk-regen-consumers.md b/.scratch/batch-a/issues/04-q3-sdk-regen-consumers.md deleted file mode 100644 index b099b39682..0000000000 --- a/.scratch/batch-a/issues/04-q3-sdk-regen-consumers.md +++ /dev/null @@ -1,15 +0,0 @@ -# 04 — Q3 派生:SDK 再生 + 消费者对齐 - -**What to build:** 03 落地后再生 JS SDK,使 NodeDeadlineExtended 进入生成的事件联合类型;对齐一切消费事件流/类型联合的消费者(TUI sync、httpapi-exercise 场景若涉及),保证 CI 生成物新鲜度门禁与 HttpAPI 契约门禁通过。 - -**Blocked by:** 03 — Q3:NodeDeadlineExtended durable 事件 + guard 前移命令层 - -**Status:** closed(PR #186,merge commit `4ddeaf2fc`) - -**Completion evidence:** 批次 A 生成物与消费者更新随 PR #186 合入 `dev`,并随 PR #188 通过 main 全量门禁。 - -- [x] SDK 再生脚本执行,生成物提交 -- [x] 事件联合类型包含 NodeDeadlineExtended,消费方编译绿 -- [x] `check:generated`(SDK + client)零 diff -- [x] 涉及响应/事件形状的 httpapi-exercise 场景已更新(如有) -- [x] 全量单元测试(含 httpapi 契约)绿 diff --git a/.scratch/batch-a/issues/05-s5-workflow-lock-timeout.md b/.scratch/batch-a/issues/05-s5-workflow-lock-timeout.md deleted file mode 100644 index d568bd49c1..0000000000 --- a/.scratch/batch-a/issues/05-s5-workflow-lock-timeout.md +++ /dev/null @@ -1,17 +0,0 @@ -# 05 — S5:withWorkflowLock 一行超时(奥卡姆版) - -**What to build:** 工作流锁获取加 30 秒上限——withWorkflowLock 外层一行 Effect.timeout,复用 TimeoutException:零新错误类、零 per-caller 改动、watchdog 零特殊化(自续间隔秒级重试天然继续,延长计数只在成功延长时 +1)。禁止引入新错误类型或按调用方分支。 - -规格依据:ADR-0004-lock-timeout-occams + CONTEXT.md 决策树 Q6。 - -**Blocked by:** None — can start immediately - -**Status:** closed(PR #186,merge commit `4ddeaf2fc`) - -**Completion evidence:** 批次 A 实现与测试随 PR #186 合入 `dev`,并随 PR #188 通过 main 全量门禁。 - -- [x] 唯一改动点在 withWorkflowLock 包装层(一行 + 常量) -- [x] 30s 超限产生 TimeoutException,编排器按既有 error_class 分诊规则处置 -- [x] 无新错误类、无 per-caller 分支的断言 -- [x] watchdog 自续行为在锁超时后仍正确的测试 -- [x] dag 测试套件 + typecheck 绿 diff --git a/.scratch/batch-a/issues/06-flaky-stdout-pollution.md b/.scratch/batch-a/issues/06-flaky-stdout-pollution.md deleted file mode 100644 index 57abd202ff..0000000000 --- a/.scratch/batch-a/issues/06-flaky-stdout-pollution.md +++ /dev/null @@ -1,16 +0,0 @@ -# 06 — Flaky:stdout 污染族根治(run-process ×9 + ShareNext 污染分量) - -**What to build:** 非交互子进程 stdout 断言失败的根因修复:测试 LLM/fixture 输出污染了被测进程的 stdout,导致 `expect(stdout).toBe("...")` 精确匹配族在慢主机/并发下失败。按豁免清单已定位的根因修复(污染源隔离或断言确定性化),不削弱断言语义。 - -规格依据:.opencode/promotion-review-round1/exemption-manifest.md(13 项中 run-process ×9 + ShareNext 污染分量)+ Round 1/2 深审根因记录。 - -**Blocked by:** None — can start immediately - -**Status:** closed(PR #186,merge commit `4ddeaf2fc`) - -**Completion evidence:** flaky 根因修复与验证随 PR #186 合入 `dev`,并随 PR #188 通过 main 全量门禁。 - -- [x] 污染源定位经可复现测试验证(修复前红、修复后绿) -- [x] run-process 9 项断言不削弱、不删除,本地重复跑(≥5 次)稳定绿 -- [x] ShareNext 的 stdout 污染分量同步修复(计时问题归 07 票) -- [x] opencode 包测试套全绿(除豁免清单剩余项) diff --git a/.scratch/batch-a/issues/07-flaky-sharenext-timing.md b/.scratch/batch-a/issues/07-flaky-sharenext-timing.md deleted file mode 100644 index ed5980f57e..0000000000 --- a/.scratch/batch-a/issues/07-flaky-sharenext-timing.md +++ /dev/null @@ -1,15 +0,0 @@ -# 07 — Flaky:ShareNext 计时预算稳定化 - -**What to build:** ShareNext 合并测试(15s 超时)在慢 CI 主机上压线失败。以发布就绪信号等待替代墙体时间等待(测试 AGENTS.md 的 pollWithTimeout 惯用法——等信号不等 sleep),或给出经证据支撑的预算调整;禁止单纯放大超时掩盖真实竞态。 - -规格依据:exemption-manifest.md(ShareNext 项)+ 测试 AGENTS.md「Synchronizing With Concurrent Work」节。 - -**Blocked by:** 06 — Flaky:stdout 污染族根治(同一测试文件,写集串行) - -**Status:** closed(PR #186,merge commit `4ddeaf2fc`) - -**Completion evidence:** flaky 稳定化与验证随 PR #186 合入 `dev`,并随 PR #188 通过 main 全量门禁。 - -- [x] 修复走信号等待惯用法;若改预算须附 CI 计时证据 -- [x] 本地重复跑(≥5 次)+ 模拟负载下稳定绿 -- [x] 无新增 Effect.sleep 等待 forked fiber 的反模式 diff --git a/.scratch/batch-a/issues/08-flaky-workspace-timing.md b/.scratch/batch-a/issues/08-flaky-workspace-timing.md deleted file mode 100644 index 0402fbbc16..0000000000 --- a/.scratch/batch-a/issues/08-flaky-workspace-timing.md +++ /dev/null @@ -1,15 +0,0 @@ -# 08 — Flaky:workspace sync 计时预算稳定化 - -**What to build:** workspace sync 历史回放测试(20s 超时)在慢 CI 主机上压线失败。处置同 07 票:信号等待替代墙体时间,或证据支撑的预算调整。若根因并非计时(先诊断后修——诊断优先于理论, tight feedback loop 先行),按实际根因修复并记录。 - -规格依据:exemption-manifest.md(workspace sync 项)。 - -**Blocked by:** None — can start immediately - -**Status:** closed(PR #186,merge commit `4ddeaf2fc`) - -**Completion evidence:** flaky 稳定化与验证随 PR #186 合入 `dev`,并随 PR #188 通过 main 全量门禁。 - -- [x] 先复现并确认根因(计时 vs 其他),根因记录入票 -- [x] 修复后本地重复跑(≥5 次)+ 模拟负载下稳定绿 -- [x] 无新增固定 sleep 反模式 diff --git a/.scratch/batch-a/issues/09-promote-dev-to-main.md b/.scratch/batch-a/issues/09-promote-dev-to-main.md deleted file mode 100644 index bca0d7dada..0000000000 --- a/.scratch/batch-a/issues/09-promote-dev-to-main.md +++ /dev/null @@ -1,14 +0,0 @@ -# 09 — 收束:dev → main 晋级 PR - -**What to build:** 全部批次 A 与 flaky 票合入 dev 且 dev CI 真绿后,开 dev→main 晋级 PR:全量门禁(Typecheck + Unit Tests + E2E linux + E2E windows)通过即合并,使 main 恢复可 release-fork 状态。 - -**Blocked by:** 01、02、03、04、05、06、07、08 全部合入 dev - -**Status:** closed(PR #188,merge commit `e837dcbfa`) - -**Completion evidence:** dev→main 晋级 PR #188 的 Typecheck、Unit Tests (linux)、E2E Tests (linux/windows) 全部通过并合入。 - -- [x] dev 最新 push 的 CI 四项检查全绿(Typecheck、Unit、E2E linux、E2E windows) -- [x] 豁免清单清零或逐项重新裁决留档 -- [x] PR 描述附批次 A 交付清单(Q1/Q2/Q3/S5 + flaky 根因修复)与两轮深审 PASS 证据链接 -- [x] 合并后 main 可手动 release-fork diff --git a/.scratch/batch-a/issues/10-backlog-phantom-cancelled-state.md b/.scratch/batch-a/issues/10-backlog-phantom-cancelled-state.md deleted file mode 100644 index 7f54f181c3..0000000000 --- a/.scratch/batch-a/issues/10-backlog-phantom-cancelled-state.md +++ /dev/null @@ -1,28 +0,0 @@ -# 10 — Backlog:phantom cancelled 节点态(N1-T5 规格-实现漂移) - -**What to build:** 消除节点级状态空间中 phantom `cancelled` 态的规格-实现漂移,二选一收敛: -- 方案 A(对齐实现):状态空间与转移表 T5 取消节点级 `cancelled` 目标态——NodeCancelled 事件维持现投影(status=failed + error_reason 承载取消语义),T5 改写为 to=failed(cancelled);同步转移表 v2、CONTEXT.md 状态机词汇。 -- 方案 B(对齐规格):projector 产出真正的节点级 `cancelled` 终态,审计全部读节点状态的消费方(调度资格、wake 汇总、TUI 展示、恢复路径)对新终态的处置,测试覆盖。 -先做设计裁决(影响面 A≪B:B 触及终态判定函数 isNodeTerminalStatus 与全部消费方),再按裁决实施。 - -**来源证据(批次 A 续作图终审 DEDUP-N1-T5,severity=low,四方接受):** -- projector.ts:35,348:NodeCancelled → status=failed;无任何投影产出节点级 cancelled -- 转移表 v2 T5 声明 to=cancelled——规格侧存在、实现侧不可达 -- 纠错记录:reasoner 曾以 store.ts:452/462 为证,被 review-logic 纠正——那两处查的是 WorkflowTable,工作流级 cancelled 是合法状态,与节点级无关 -- 运行时影响:零(取消语义经 error_reason 保留);批次 A 仅向 cancelled 投影补 EP:false,漂移系既有 -- accepted_by:reasoning N1(附错误证据)/ review-logic(PARTIAL,证据已纠正)/ review-architecture I1 / review-tests GAP-T5 - -**Blocked by:** None(独立设计决策) - -**Status:** closed(方案 A 已实施 — PR #189,commit 67d1ca2b1) - -## 裁决与实施记录(batch-a-residuals DAG,终审 PASS) -- 裁决:方案 A(对齐实现)——消费方核验确认仅 TUI 存在 phantom dead branch 读节点级 cancelled,投影写 status=failed 故永不触发,无真实依赖 -- T5 改写 to=failed(cancelled);状态空间删除节点级 cancelled 目标态(保留工作流级);CONTEXT.md 同步;projector 投影注释固化契约 -- 新增 core 测试断言 NodeCancelled 重放 → status=failed + error_reason 承载取消语义 -- 对抗审查:检察官/辩护人/证据矩阵三路 + 第四方 claim 核验,终审 PASS - -- [x] 设计裁决 A/B(含消费方影响面清单) -- [x] 按裁决实施 + 测试 -- [x] 转移表 v2 与 CONTEXT.md 状态机词汇同步 -- [x] typecheck + dag 套件绿 diff --git a/.scratch/batch-a/issues/11-backlog-spurious-t8-budget.md b/.scratch/batch-a/issues/11-backlog-spurious-t8-budget.md deleted file mode 100644 index 8b01cd76c3..0000000000 --- a/.scratch/batch-a/issues/11-backlog-spurious-t8-budget.md +++ /dev/null @@ -1,27 +0,0 @@ -# 11 — Backlog:F8 spurious T8——watchdog 陈旧读每周期耗一个 cap 预算单位 - -**What to build:** 消除(或显式预算化)F8 spurious T8:watchdog 以陈旧快照读判定超时 → 发延长 → 重放 T8,每发生一次消耗一个 max_timeout_extensions 预算单位。代码自我记录为 cosmetic(loop.ts:860-868 注释:"cap accounting still holds because the count did climb"),但语义上节点并未真正获得有效延长窗口却消耗了预算——极端场景下提前耗尽延长预算。 -修复方向(裁决后实施): -- 方案 A:延长判定前在 workflow lock 内重读节点状态(deadline/状态新鲜读),陈旧读不发延长——根治,注意不引入锁争用回归 -- 方案 B:把"陈旧读引发的延长"与真实延长分开计数(预算只认真实延长)——改计数语义,影响面含恢复/审计 -- 方案 C(维持现状 + 显式化):把预算消耗语义写入 ADR 与转移表,加监控/测试断言行为,不改机制 - -**来源证据(批次 A 续作图终审 DEDUP-N3,severity=low,双方接受):** -- loop.ts:860-868(机制与自我记录注释)、spawn.ts:192-198(watchdog 读路径) -- Q2 送达门控落地后仍存在(门控管的是 re-time 发起,不管陈旧读判定) -- 既有问题,非批次 A 回归 -- accepted_by:reasoning N3(Notable)/ review-logic(CONFIRM pre-existing cosmetic) - -**Blocked by:** None(独立设计决策) - -**Status:** closed(方案 A 已实施 — PR #189,commit b35630486) - -## 裁决与实施记录(batch-a-residuals DAG,终审 PASS) -- 裁决:方案 A(锁内新鲜读)——watchdog 以 staleDeadlineMs 守卫判定:陈旧读触发的延长被新鲜读否决时不发布 NodeDeadlineExtended、不递增 timeoutExtensions -- 真红→绿:test/dag/dag-retime-stale-read.test.ts,case1(陈旧读抑制)vs case2(真实超时延长)对照 -- 不变式保持:-2/0/1 三值契约、N1 监督不变式(running 节点总有 watcher) -- 对抗审查两项开放担忧裁决:U1(Effect.timeout 败者中断产生孤立节点)经 Effect v4 源码分析 REFUTED(TimeoutError=Cause.Fail,raceAllFirst 败者中断不经 hasInterrupts 匹配);N1(抑制守卫 >staleDeadlineMs 缺 >now)经三方一致论证为有界自愈(下一 tick 必发布,延迟 ≤1 escalateIntervalMs,预算不丢) - -- [x] 裁决修复方向(A/B/C,含锁交互与预算语义影响面) -- [x] 按裁决实施 + 测试(含陈旧读复现场景) -- [x] typecheck + dag 套件绿 diff --git a/.scratch/batch-b/README.md b/.scratch/batch-b/README.md deleted file mode 100644 index 7a94dbb54e..0000000000 --- a/.scratch/batch-b/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# 批次 B 执行台账 - -**规划基线:** `dev@3e8368f37`(PR #190) - -**当前状态:** 规格与票据已就绪;实现代码尚未开始。 - -## 审计结论 - -- 批次 A 已经 PR #188 晋级 `main`;残余修复 PR #189 与交接 PR #190 已进入 `dev`。 -- U-1/U-2/mid-stream-stall 的 OpenSpec 已完成并通过校验;仓库规定 `/openspec/` local-only,跨 worktree 使用已追踪镜像 `.scratch/batch-b/abort-path-contracts.md`。 -- 原始 `.opencode/promotion-review-round1/` 与 `.opencode/.dag-specs/evidence/` 是未追踪本地文件;跨 worktree 统一引用 `.scratch/batch-b/evidence.md`。 -- O1 不是直接实现票:离线降级已完成,剩余 LKG 的持久化、键、失效和安全边界需先写小规格。 -- S7 只有静态 bug 气味;先诊断,不能复现就无代码收口。P8 维持批 C 的观测候选,不阻塞批 B。 - -## 串行顺序 - -1. 01 U-1 → 02 U-2 → 03 mid-stream-stall;02/03 写同一测试文件,禁止并行。 -2. 04 F3 → 05 F4;两票只清测试债,不顺带改运行时。 -3. 06 O1 规格 → 07 O1 实现。 -4. 08 S7 诊断;只有红灯成立才新建独立修复票。 -5. 批 B 全部处置 + 批 C P8 观测记录关闭后,执行 09 的 dev→main 晋级与 release-fork。 - -每票从最新 `dev` 创建符合仓库规则的分支,单独 PR → `dev`,单独新任务执行。票据完成时将状态改为 `closed`,附 PR、merge commit 与验证命令。 diff --git a/.scratch/batch-b/abort-path-contracts.md b/.scratch/batch-b/abort-path-contracts.md deleted file mode 100644 index eaaa7dde92..0000000000 --- a/.scratch/batch-b/abort-path-contracts.md +++ /dev/null @@ -1,53 +0,0 @@ -# 批次 B:abort-path integrity 规格 - -**Status:** accepted -**Applies to:** 01 U-1、02 U-2、03 Transport mid-stream-stall -**Local OpenSpec source:** `openspec/changes/batch-b-abort-path-contracts/`(仓库规定 local-only;`openspec validate --changes` 已通过) - -## 目标 - -三个边界目前只有实现/注释声明,没有真实失败路径证据:Session fork 嵌套发布失败时的批次回滚、HTTP timeout 对真实连接的取消传播、合法首帧后的逐帧间隔 timeout。本规格只补集成测试;红灯暴露违约时,才允许做对应契约所需的最小生产修复。 - -## Requirement 1:fork 复制批次原子回滚 - -系统 SHALL 在 `Session.fork` 的消息/part 复制批次中保持原子性:任一嵌套 durable event 发布失败时,同一外层事务内此前写入的复制事件及其投影全部回滚。 - -### Scenario:部分复制后嵌套发布失败 - -- WHEN fork 已创建目标 session,至少一个 message/part 发布完成,随后嵌套发布失败 -- THEN fork 调用失败,目标 session 不存在本批复制出的 message/part projections 与 durable copy events -- THEN 源 session 的 message/part 保持不变 -- THEN 复制事务外已经提交的目标 Session Created 记录可以保留 - -**设计裁决:** 必须扩展 `packages/opencode/test/session/fork-batch.test.ts` 的真实 SQLite fixture;adapter-only savepoint 测试不足以证明 EventV2/projector 共用外层连接。 - -## Requirement 2:provider timeout 取消真实 transport - -系统 MUST 在 provider HTTP timeout 到期时,以现有 Transport/Timeout 结束 LLM stream,并取消仍在进行的真实 HTTP response stream,使 provider 端观察到连接或响应体取消。 - -### Scenario:真实 provider response 超时后仍保持打开 - -- WHEN loopback provider 已接收请求并返回超过 timeout 仍保持打开的 response stream -- THEN 客户端在有界时间内以现有 `LLMError` Transport/Timeout 失败 -- THEN provider 在有界时间内观察到 response cancellation 或等价 request abort - -**设计裁决:** 使用真实 `Bun.serve` loopback 与生产 fetch-backed client。以 response cancellation 为确定性主信号,request abort 可作补充;内存 HttpClient 和显式 Fiber interrupt 都不能替代本场景。 - -## Requirement 3:timeout 约束每个帧间隔 - -系统 SHALL 将 stream timeout 作为相邻数据帧之间的最大间隔,而不是只覆盖 response headers 或首帧等待。 - -### Scenario:合法首帧后永久停顿 - -- WHEN provider 在 timeout 内发出至少一个合法 SSE frame,随后不关闭且不再发送数据 -- THEN timeout 前到达的 frame 已交付消费者 -- THEN 停顿超过 timeout 后,stream 以现有 Transport/Timeout 失败 - -**设计裁决:** 使用 fence 证明首帧已交付,再用 TestClock 越过下一帧间隔;本场景验证 stream timing,不重复真实 socket 取消测试。 - -## 非目标与顺序 - -- 不要求把 fork session creation 与复制批次合并为同一事务。 -- 不改变 timeout 默认值、错误词汇、retry policy 或 provider protocol。 -- 01 → 02 → 03 串行落地;02/03 都修改 `packages/llm/test/transport-timeout.test.ts`。 -- 若 Bun 在目标 CI 平台无法提供可重复的服务端取消信号,02 停在诊断结论,不能退化为重复断言 Timeout 错误。 diff --git a/.scratch/batch-b/config-lkg-spec.md b/.scratch/batch-b/config-lkg-spec.md deleted file mode 100644 index 3b6e329459..0000000000 --- a/.scratch/batch-b/config-lkg-spec.md +++ /dev/null @@ -1,269 +0,0 @@ -# remote-config-lkg — OpenSpec apply-ready 镜像 - -- **基线:** `dev@4675435d94d462d2f9317d6688ddab2f0105c746` -- **change:** `remote-config-lkg` -- **local-only 原件:** `openspec/changes/remote-config-lkg/` -- **状态:** `4/4 artifacts complete`,apply-ready -- **校验:** `openspec validate --changes` → `1 passed, 0 failed` -- **严格校验:** `openspec validate remote-config-lkg --type change --strict --no-interactive` → `valid` -- **实施入口:** 按末尾 `tasks.md` 由票 07 以 TDD 执行;本镜像不包含生产代码。 - -以下四段在提交前按字节与 local-only 原件逐段核对。 - - -## Why - -remote config 在 transport 或 body 读取失败时会告警并跳过来源;长期离线时,这会让最近一次已验证的在线配置不可用。需要一个持久化 last-known-good(LKG),同时严格避免缓存或日志泄露认证材料,并且不掩盖认证与 schema 错误。 - -## What Changes - -- 为每个规范化 remote URL 保存最近一次完整通过解析与 schema 验证的远端响应;缓存保留 Environment 替换前的响应内容,不保存请求 header、token 或替换后的秘密。 -- 仅在既有可降级错误类别上读取 LKG;401/403、HTML 登录/认证响应和 schema decode 错误继续硬失败。 -- 使用同目录临时文件、原子 rename 和最终 `0600` 文件模式更新 LKG;写入失败不影响在线读取,也不破坏旧 LKG。 -- 损坏或空 LKG 告警后按既有 warn + skip 行为继续;日志不包含缓存正文或凭据。 -- LKG 不因年龄自动过期;记录 `writtenAt`,年龄只用于安全诊断,下一次合法在线成功原子覆盖旧值。 -- 用目标测试锁定在线写入后离线回退、失败写入保留旧值、硬失败边界、损坏缓存、安全键与日志以及原子文件语义。 - -## Capabilities - -### New Capabilities - -- `remote-config-lkg`: 定义 remote config 已验证响应的持久化、回退边界、隐私约束、耐久写入和诊断行为。 - -### Modified Capabilities - -无。 - -## Impact - -- 主要影响 `packages/opencode/src/config/config.ts`、一个位于 `packages/opencode/src/config/` 的 LKG 持久化模块,以及 `packages/opencode/test/config/wellknown-offline.test.ts` 的集成场景。 -- 在 OpenCode 的 XDG cache 根下新增用户私有的 remote-config LKG 文件;不改变 HTTP API、SDK、配置 schema 或依赖。 -- 没有 breaking change;没有可配置 TTL,也不重写当前无可用来源时的 warn + skip 降级。 - - - -## Context - -`Config.layer` 目前按以下顺序加载一个 well-known 认证来源:请求 `/.well-known/opencode`,解析并 decode `ConfigV1.WellKnown`,对 `remote_config.url` 与请求 headers 做 Environment 替换,可选请求第二跳 JSON,把内嵌与第二跳配置合并,最后由 `loadConfig` 执行 Environment 替换、JSONC 解析和 `ConfigV1.Info` schema 验证。transport、非认证 HTTP 不可用和 body 读取失败当前会 warn + skip;HTML 登录响应与 decode 失败会中止该配置加载。 - -LKG 必须接在这条流程上,而不能缓存最终的 `Info`。最终 `Info` 已经包含 Environment 替换结果,可能固化 token 或文件引用中的秘密。缓存还必须区分“在线 body 不是 JSON”与“JSON 可解析但不符合 schema”:前者属于允许降级的 body 失败,后者属于必须暴露的配置错误。 - -## Goals / Non-Goals - -**Goals:** - -- 在长期离线或远端暂时不可用时,复用同一 remote URL 最近一次完整验证成功的原始响应 body。 -- 保持认证、HTML 登录和 schema 错误为硬失败,保证 LKG 不掩盖需要用户处理的问题。 -- 让缓存更新具备用户私有权限和单文件原子替换语义;任何写入失败都不改变在线成功结果或旧 LKG。 -- 保持没有可用 LKG 时现有 warn + skip 的结果与合并边界。 - -**Non-Goals:** - -- 不增加 TTL 配置、后台刷新、跨设备同步、缓存清理命令或多版本迁移框架。 -- 不缓存请求 header、认证 token、Environment 替换后的正文、最终合并后的 `Info` 或 HTTP API 数据。 -- 不改变 remote config 的优先级、插件解析、普通本地配置加载、HTTP API 或 SDK。 -- 不借本变更重写现有 warn + skip 流程;只在允许降级的失败点插入 LKG 读取。 - -## Decisions - -### 1. 每个 HTTP remote URL 保存一个原始响应 LKG - -well-known 第一跳与可选 remote-config 第二跳各自以其请求 URL 标识缓存记录。记录格式固定为版本化 JSON envelope: - -```json -{ - "version": 1, - "writtenAt": "2026-08-09T00:00:00.000Z", - "body": "{...the exact response text...}" -} -``` - -`body` 是 Environment 替换前的响应文本。envelope 不保存响应 headers/status,也不保存请求 URL、请求 headers、认证 token、Environment map 或 decode 后对象。读取 LKG 后,body 必须重新经过与在线 body 相同的 JSON 解析、请求级 schema decode、remote-config 对象检查、Environment 替换和最终 `ConfigV1.Info` 验证。 - -在线 body 先进入暂存结果,只有该 well-known 来源的完整下游流程通过解析与 schema 验证后才允许写入本次在线取得的记录。任一在线 auth、HTML、JSON shape、对象检查、Environment 替换或最终 `ConfigV1.Info` 失败时,不写本次来源暂存的任何 LKG。选择延迟提交而不是在 `fetchRemoteJson` decode 后立即写,是为了避免把“JSON 合法但最终配置无效”的响应提升为 LKG。 - -未选择缓存最终 `Info`,因为它已经过 Environment 替换;也未选择缓存 request/response headers,因为它们不是离线重放配置所需的数据,并可能携带凭据。 - -### 2. cache key 只有规范化 URL 的稳定摘要 - -URL 用 WHATWG `URL` 规范化:清除不会随 HTTP 请求发送的 fragment,依赖 URL 实现统一 scheme/host 大小写、默认端口与转义形式,并保留会改变资源身份的 path 与 query。缓存文件名是规范化 URL UTF-8 字节的 SHA-256 小写十六进制摘要加 `.json`;目录固定为 `Global.Path.cache/remote-config-lkg/`。 - -文件名和 envelope 都不拼接原始 URL、认证 header、token、Environment 变量值或配置正文。摘要是 key 中唯一由 URL 派生的值;headers、token、Environment map 与 body 不参与额外的 key 组成。remote-config 日志使用稳定摘要和 `well-known`/`remote-config` 角色标识,不记录请求 headers、token、Environment 值、缓存 body 或响应 body;错误原因先归类,不直接序列化可能回显请求的底层错误对象。 - -未选择可读 URL 文件名,因为 query/userinfo 可能携带凭据;未选择 header/token 分区,因为它会把认证材料引入持久身份并违反本票边界。 - -### 3. 明确在线失败分类,再决定是否读取 LKG - -`fetchRemoteJson` 将在线结果表达为成功、允许降级失败或硬失败,而不是用一个捕获所有错误的分支: - -- 允许降级并尝试 LKG:DNS/连接/timeout 等 transport 错误;除 401/403 外的现有不可用 HTTP 状态;body stream 读取失败;内容不是 HTML 登录页但 JSON 语法不可解析。 -- 直接硬失败且不得读取 LKG:HTTP 401/403;content-type 或 body 特征识别出的 HTML/login/auth 响应;JSON 可解析但请求级 schema decode 失败;第二跳结果不是对象;Environment 替换、JSONC 解析或最终 `ConfigV1.Info` schema decode 失败。 - -在线 JSON 语法解析与 schema decode 必须分成两个可观察步骤,才能保持上述边界。401/403 在进入通用非 2xx 降级分支前转换为现有 `RemoteAuthError` 语义。已有 LKG 也不能改变硬失败结果,且硬失败不能覆盖旧 LKG。 - -第一跳允许降级失败且没有可用 LKG时,继续跳过整个 well-known 来源。第二跳同类失败且没有可用 LKG时,继续返回空的 fetched config,使 well-known 内嵌 config 按现状合并。这个分支只复用原行为,不重新定义 warn + skip。 - -### 4. 损坏、空或缺失缓存不成为新的硬错误 - -仅在在线失败属于允许降级类别时读取 LKG。文件缺失表示没有 LKG,保留原在线失败告警并执行原 skip;空文件、JSON envelope 损坏、版本不支持、`writtenAt` 无效、空 body 或缓存 body 无法重新解析/decode 则额外记录不含正文与凭据的 warning,并把缓存视为不可用,随后执行同一 skip 分支。缓存自身的 schema 错误属于缓存损坏,不提升为在线 schema 硬失败。 - -未选择让损坏缓存中止启动,因为 LKG 是可丢弃的恢复材料,不能比当前无缓存流程更脆弱。 - -### 5. 同目录临时文件保证旧值不被失败更新破坏 - -实现放在新的 `packages/opencode/src/config/remote-lkg.ts` 自包含模块,公开一个窄的读取/写入接口给 `config.ts`。写入顺序为:确保专用目录存在;在目标同目录创建唯一临时文件并以 `0600` 写完整 envelope;关闭文件;原子 rename 到摘要目标;确认最终目标模式为 `0600`。成功 rename 前的任意失败只做安全 warning 和临时文件 best-effort 清理,旧目标保持不变。因为临时文件从创建起就是 `0600`,rename 后不会出现更宽权限窗口。 - -缓存写入是在线成功路径的 best-effort side effect。任一记录写失败只保留对应 URL 的旧文件并继续返回已验证在线配置;多条暂存记录独立提交,失败不会回滚配置加载。并发更新采用“最后一个完整 rename 获胜”,每个可见文件始终是完整 envelope,不增加锁服务。 - -未选择直接 truncate 目标文件,因为进程崩溃或磁盘错误会破坏旧 LKG;未选择跨目录临时文件,因为 rename 可能失去原子性。 - -### 6. LKG 永不过期,年龄只用于诊断 - -读取逻辑不以 `writtenAt` 或文件 mtime 拒绝 LKG。回退成功时可以记录 `writtenAt` 或计算后的非敏感年龄诊断,但年龄不改变控制流。下一次合法在线成功按上述原子写流程覆盖同 URL 的记录。 - -未选择固定或可配置 TTL:LKG 的目标是支持长期离线,自动过期会在最需要它时恢复到 warn + skip,并引入本票不需要的策略面。 - -### 7. TDD 边界 - -07 先扩展 `packages/opencode/test/config/wellknown-offline.test.ts`,用真实 `Config.layer` 锁定两跳在线写入后离线回退、预置 LKG 下的 auth/decode 硬失败、损坏/空缓存和日志无凭据。持久化细节放在 `packages/opencode/test/config/remote-lkg.test.ts`,用隔离 cache 根验证稳定摘要文件名、Environment 替换前 body、同目录 rename、写失败保留旧值和 POSIX `0600`。测试可为 rename 失败提供最窄的文件系统故障注入点,其余路径使用真实文件系统。 - -生产改动限定为 `packages/opencode/src/config/config.ts` 与新的 `packages/opencode/src/config/remote-lkg.ts`;不改 `packages/core`、schema、路由或生成物。 - -## Risks / Trade-offs - -- [永不过期的 LKG 可能很旧] → 每次回退记录安全的 `writtenAt`/年龄诊断,并由下一次合法在线成功覆盖;不静默声称缓存新鲜。 -- [只按 URL 分区会让同一用户下不同认证上下文共享该 URL 的 LKG] → 文件保持用户私有 `0600`,不把凭据加入 key;这是安全 key 约束与认证维度隔离之间的明确取舍。 -- [磁盘写入、rename 或 chmod 失败] → 在线配置仍成功,旧 LKG 在 rename 前保持完整,日志只包含摘要与分类。 -- [Windows 不提供等价的 POSIX mode 语义] → 创建与替换仍请求 `0600`;POSIX 测试断言精确 mode,Windows 保留原子替换与不扩宽应用请求的行为。 -- [缓存 body 本身包含用户配置] → 只写入专用 cache 目录的 `0600` 文件,绝不写入 key 或日志,也不保存经过 Environment 替换的版本。 - -## Migration Plan - -无需迁移:首次合法在线成功按需创建 version 1 文件;没有文件时行为与当前版本相同。回滚代码后这些 cache 文件无人读取,可安全保留;未来不兼容版本按损坏/不支持缓存的 warn + skip 语义处理。 - -## Open Questions - -无。本票明确采用永不过期策略,不增加可配置 TTL 或后续扩展点。 - - - -## ADDED Requirements - -### Requirement: 只持久化完整验证成功的原始远端响应 -系统 MUST 以版本、`writtenAt` 和原始响应 body 组成 LKG;MUST 在对应 well-known 来源的请求级解析、schema decode、第二跳对象检查、Environment 替换和最终 `ConfigV1.Info` schema 验证全部成功后,才持久化本次在线取得的响应。系统 MUST 保存 Environment 替换前的 body,且 MUST NOT 把请求 headers、认证 token、Environment map、替换后的正文或最终合并 `Info` 作为缓存字段。 - -#### Scenario: 在线成功后写入并可离线复用 -- **WHEN** well-known 与第二跳 remote-config 在线响应均成功,完整配置通过最终 schema 验证,随后同一 URL 的 transport 请求失败 -- **THEN** 系统写入各 URL 的原始响应 LKG,并在后续离线加载中重新验证和应用 LKG,得到与上一次合法在线读取相同的配置语义 - -#### Scenario: Environment 秘密不被固化 -- **WHEN** 原始远端 body 包含 Environment 占位符,在线加载用当前 Environment 值完成替换并通过验证 -- **THEN** LKG body 保留占位符形式,缓存 envelope 不包含替换后的 Environment 值、请求 header 或 token,回退时使用当次 Environment 重新执行替换 - -#### Scenario: 下游验证失败不产生新 LKG -- **WHEN** 在线 body 可解析,但第二跳不是对象、Environment 替换失败或最终 `ConfigV1.Info` schema decode 失败 -- **THEN** 系统硬失败,MUST NOT 写入本次暂存响应,也 MUST NOT 覆盖已有 LKG - -### Requirement: 缓存身份与诊断不得泄露凭据 -系统 MUST 用规范化 remote URL 的 SHA-256 小写十六进制摘要作为唯一文件标识。规范化 MUST 清除 fragment,并统一 WHATWG URL 定义的 scheme/host 大小写、默认端口与转义形式,同时保留改变资源身份的 path 与 query。key、文件名和日志 MUST NOT 拼接原始 URL、认证 headers、token、Environment 变量值、缓存正文或配置正文;remote-config 诊断 MUST 只使用摘要、端点角色、失败分类和非敏感年龄信息。 - -#### Scenario: 等价 URL 使用同一稳定文件名 -- **WHEN** 两个 remote URL 仅在 host 大小写、默认 HTTPS 端口或 fragment 上不同 -- **THEN** 系统规范化后生成相同的 64 位十六进制摘要文件名 - -#### Scenario: 凭据与正文不出现在 key 或日志 -- **WHEN** remote config 请求包含认证 header、token、Environment 替换值和可识别的配置正文标记,并发生在线成功、缓存写入、离线回退及缓存错误诊断 -- **THEN** 缓存文件名、key 和捕获到的日志均不包含这些 header、token、Environment 值或正文标记,缓存 envelope 也不包含请求认证元数据 - -### Requirement: LKG 更新必须原子且用户私有 -系统 MUST 在目标文件同目录创建唯一临时文件,以 `0600` 写入完整 envelope,关闭后通过原子 rename 替换目标,并保证最终文件模式为 `0600`。缓存写入 MUST 是在线成功路径的 best-effort side effect;写入失败 MUST NOT 使已验证在线配置失败,也 MUST NOT 修改或删除旧 LKG。 - -#### Scenario: 同目录原子替换并设置文件模式 -- **WHEN** 系统首次写入或覆盖一个 LKG -- **THEN** 完整内容先写入目标同目录的临时文件,再由 rename 发布,最终目标是完整 envelope 且权限模式为 `0600` - -#### Scenario: rename 前写入失败保留旧 LKG -- **WHEN** 已存在可用旧 LKG,而新在线响应验证成功但临时写入、关闭或 rename 失败 -- **THEN** 系统记录不含正文和凭据的 warning,仍返回新在线配置,并保留旧 LKG 原封不动供后续允许降级的失败使用 - -#### Scenario: 并发写入不暴露部分文件 -- **WHEN** 同一 URL 的两个合法在线加载并发更新 LKG -- **THEN** 最终读者只能观察到某一个完整 envelope,不能观察到截断或混合内容 - -### Requirement: 只有允许降级的在线失败可以回退 -系统 MUST 先按现有 remote-config 错误语义分类在线失败。transport 错误、除 401/403 外的非认证不可用 HTTP 状态、body stream 读取失败以及非 HTML 的 JSON 语法不可解析 body MUST 尝试读取 LKG。HTTP 401/403、HTML/login/auth 响应、JSON 可解析后的请求级 schema decode 错误、第二跳非对象、Environment 替换错误和最终配置 schema decode 错误 MUST 硬失败,且 MUST NOT 读取 LKG 掩盖错误。 - -#### Scenario: transport 或非认证不可用状态回退 -- **WHEN** 已有可用 LKG,在线请求发生 DNS、连接、timeout 或除 401/403 外的既有不可用 HTTP 状态 -- **THEN** 系统告警该在线失败,重新验证 LKG,并用其继续 remote config 加载 - -#### Scenario: body 读取或 JSON 语法失败回退 -- **WHEN** 已有可用 LKG,在线 response body stream 读取失败,或 body 不是 HTML/login 响应但不是可解析 JSON -- **THEN** 系统把失败归入允许降级 body 类别并使用 LKG - -#### Scenario: 401 或 403 不回退 -- **WHEN** 已有可用 LKG,但在线 remote endpoint 返回 401 或 403 -- **THEN** 系统保持认证硬失败语义,不读取 LKG,也不覆盖旧 LKG - -#### Scenario: HTML 登录页不回退 -- **WHEN** 已有可用 LKG,但在线响应由 content-type 或 body 特征识别为 HTML/login/auth 页面 -- **THEN** 系统产生现有 `RemoteAuthError` 语义,不读取 LKG,也不覆盖旧 LKG - -#### Scenario: schema decode 错误不回退 -- **WHEN** 已有可用 LKG,但在线 body 是合法 JSON,随后在 well-known schema、第二跳对象检查或最终 `ConfigV1.Info` schema decode 中失败 -- **THEN** 系统暴露硬失败,不读取 LKG,也不覆盖旧 LKG - -### Requirement: 不可用缓存保留原 warn + skip 语义 -系统 MUST 把缺失 LKG 视为没有恢复材料;MUST 把空文件、损坏 envelope、不支持版本、无效 `writtenAt`、空 body 或无法重新解析/decode 的缓存视为不可用。损坏或空缓存 MUST 产生不含正文与凭据的 warning,随后 MUST 执行原有允许降级分支,而不是崩溃或改变在线错误类别。 - -#### Scenario: 第一跳损坏或空缓存告警后跳过来源 -- **WHEN** well-known 在线失败允许降级,但对应 LKG 为空或损坏 -- **THEN** 系统告警缓存不可用并跳过整个 well-known 来源,本地配置继续按现有行为加载 - -#### Scenario: 第二跳损坏或空缓存保留内嵌配置 -- **WHEN** 第二跳 remote-config 在线失败允许降级,但对应 LKG 为空或损坏 -- **THEN** 系统告警缓存不可用并按现有空 fetched-config 分支继续,well-known 内嵌配置仍可合并 - -#### Scenario: 缓存告警不回显缓存内容 -- **WHEN** 损坏缓存包含可识别的凭据或配置正文标记 -- **THEN** warning 只包含安全摘要、端点角色和损坏分类,不包含文件正文、底层解析输入或凭据标记 - -### Requirement: LKG 不因年龄自动过期 -系统 MUST 保存合法 RFC 3339 `writtenAt`,但 MUST NOT 以 `writtenAt`、文件 mtime 或任何固定/可配置 TTL 拒绝 LKG。年龄 MUST 只用于安全诊断;下一次同 URL 的合法在线成功 MUST 通过原子更新覆盖旧记录。系统 MUST NOT 为本能力增加可配置 TTL 扩展点。 - -#### Scenario: 很旧的 LKG 仍支持长期离线 -- **WHEN** 在线失败允许降级且可用 LKG 的 `writtenAt` 已经过任意长时间 -- **THEN** 系统仍重新验证并使用该 LKG,同时可记录不含凭据的年龄诊断,不因年龄执行 warn + skip - -#### Scenario: 合法在线成功覆盖旧记录 -- **WHEN** 使用旧 LKG 后,同一规范化 URL 再次获得并完整验证合法在线响应 -- **THEN** 系统原子覆盖旧 LKG,更新 `writtenAt`,且不创建或读取 TTL 配置 - - - -## 1. 红灯:锁定外部行为 - -- [x] 1.1 在 `packages/opencode/test/config/wellknown-offline.test.ts` 增加两跳在线成功写入、换实例后第一跳/第二跳 transport 与非认证 body 失败使用 LKG 的场景,并先运行目标文件确认新断言因 LKG 尚未实现而失败;对应[在线成功后写入并可离线复用](specs/remote-config-lkg/spec.md#scenario-在线成功后写入并可离线复用)与[允许降级回退](specs/remote-config-lkg/spec.md#scenario-transport-或非认证不可用状态回退)。 -- [x] 1.2 在同一集成测试预置可用 LKG,再覆盖 401、403、HTML login、合法 JSON 的 well-known schema 错误、第二跳非对象和最终 `ConfigV1.Info` decode 错误;逐项断言硬失败、未使用/未覆盖 LKG,对应[401/403](specs/remote-config-lkg/spec.md#scenario-401-或-403-不回退)、[HTML](specs/remote-config-lkg/spec.md#scenario-html-登录页不回退)与[schema decode](specs/remote-config-lkg/spec.md#scenario-schema-decode-错误不回退)。 -- [x] 1.3 在同一集成测试加入缺失、空、损坏和超旧缓存;断言第一跳保持 warn + skip、本地配置可用,第二跳保持内嵌 config 合并,超旧记录仍使用且只给安全年龄诊断;不得改写现有降级分支的结果。 -- [x] 1.4 新建 `packages/opencode/test/config/remote-lkg.test.ts`,用隔离 cache 根和真实文件系统锁定 URL 规范化摘要、原始 Environment 占位符、同目录 rename、完整 envelope、POSIX `0600`、并发完整性,并用最窄 rename 故障注入锁定失败更新后旧 LKG 仍可读。 -- [x] 1.5 在两份目标测试放置独特的 header/token/Environment/正文标记,断言文件名、key 和所有 remote-config/cache 日志不含标记,envelope 不含请求认证元数据;确认新增安全断言先红。 - -## 2. 绿灯:实现私有原子 LKG 模块 - -- [x] 2.1 新建 `packages/opencode/src/config/remote-lkg.ts` 并按 `src/config` 自导出规范提供窄接口:WHATWG URL 去 fragment、SHA-256 文件名、version 1 envelope decode,以及从 `Global.Path.cache/remote-config-lkg/` 读取原始 body;损坏、空和不支持版本返回可分类的不可用结果,不抛出正文。 -- [x] 2.2 在该模块实现 best-effort 写入:目标同目录唯一临时文件以 `0600` 写完整 envelope,关闭后原子 rename,最终模式 `0600`;失败时安全告警、best-effort 清理临时文件且不触碰旧目标。 -- [x] 2.3 保持 `writtenAt` 为合法 RFC 3339 诊断字段;读取不检查 TTL/mtime,不增加配置项、清理器、后台刷新或 TTL 扩展接口。 - -## 3. 绿灯:接入当前 remote config 流程 - -- [x] 3.1 仅在 `packages/opencode/src/config/config.ts` 调整 `fetchRemoteJson` 附近:把 JSON 语法解析与 schema decode 分开,并将结果分类为在线成功、允许降级失败和硬失败;401/403 与 HTML/login/auth 继续使用硬认证错误,合法 JSON 的 schema/object/final-config 错误继续硬失败。 -- [x] 3.2 well-known 与第二跳在线响应只暂存 Environment 替换前 body;完整来源通过 `loadConfig` 最终 schema 验证后才调用 LKG 写入。仅允许降级失败读取并重验 LKG;无可用 LKG 时分别复用现有“第一跳 skip 来源”和“第二跳空 fetched config”分支。 -- [x] 3.3 把触及的 remote-config/cache 诊断限定为摘要、端点角色、失败分类和非敏感年龄;不得序列化原始 URL、底层可能回显请求的错误对象、headers、token、Environment 值或 body。 - -## 4. 验收与范围门禁 - -- [x] 4.1 从 `packages/opencode` 运行 `bun test test/config/remote-lkg.test.ts test/config/wellknown-offline.test.ts`,确认在线→离线、旧 LKG、auth/decode、损坏/空缓存、永不过期、key/log 安全及原子/权限场景全绿。 -- [x] 4.2 从 `packages/opencode` 运行 `bun typecheck`;不得用 `bun run build` 代替类型门禁。 -- [x] 4.3 检查实现 diff 只涉及 `packages/opencode/src/config/config.ts`、`packages/opencode/src/config/remote-lkg.ts` 和上述两份 config 测试;如确需测试 fixture 的最小改动须在提交说明中列出,`packages/core`、HTTP routes、SDK 生成物、依赖与既有 warn + skip 语义保持零改动。 - diff --git a/.scratch/batch-b/evidence.md b/.scratch/batch-b/evidence.md deleted file mode 100644 index b2cfb8887e..0000000000 --- a/.scratch/batch-b/evidence.md +++ /dev/null @@ -1,61 +0,0 @@ -# 批次 B 证据快照 - -**代码基线:** `dev@3e8368f37` -**用途:** 给每票的新任务/worktree 提供稳定证据;无需重新调查原始评审。 -**原始来源:** 本地未追踪的 `.opencode/promotion-review-round1/*.md` 与 `.opencode/.dag-specs/evidence/*.md`。 - -## 组 1:abort-path 契约 - -### U-1 — Session fork 嵌套事务回滚 - -- 原始 finding 引用的 `packages/core/src/session.ts` 已过期;当前实现位于 `packages/opencode/src/session/session.ts` 的 `Session.fork`。 -- fork 的消息/part 复制由一个外层 `db.transaction` 包裹,嵌套 `events.publish` 会进入 Effect-Drizzle SQLite savepoint。 -- `packages/effect-drizzle-sqlite/src/effect-sqlite/session.ts` 已实现嵌套事务的 savepoint/rollback。 -- `packages/opencode/test/session/fork-batch.test.ts` 已验证成功路径与事务/savepoint 数量,缺口仅是“部分发布成功后失败”的整体回滚。 -- 目标契约、测试边界与非目标见 `.scratch/batch-b/abort-path-contracts.md`。 - -### U-2 — timeout 传播到底层 HTTP 取消 - -- `packages/llm/src/route/transport/http.ts` 对请求执行使用 `Effect.timeout`,对响应 stream 使用 `Stream.timeoutOrElse`。 -- `packages/llm/test/transport-timeout.test.ts` 已覆盖 headers 挂起、body 从不发帧、正常完成、默认 timeout 与选项合并,但使用内存 HTTP client,不能证明真实 socket/response body 被取消。 -- `packages/opencode/test/session/llm.test.ts` 已覆盖显式 Fiber interrupt 导致 provider response body 取消;本票必须验证“timeout 驱动”的取消,不能复制该场景。 -- 验收必须使用真实 loopback `Bun.serve` + 生产 fetch-backed client,并以服务端可观察的 response cancellation 为主信号。 - -### Transport mid-stream-stall - -- 当前 timeout suite 没有“合法首帧已交付,随后永久停顿”的场景。 -- 本票验证逐帧间隔 timeout;可使用 TestClock,不承担真实 socket 取消证明。 -- U-2 与本票都修改 `packages/llm/test/transport-timeout.test.ts`,必须先 02 后 03。 - -## 组 2:测试卫生债 - -### F3 — 固定订阅 settle sleep - -- `packages/opencode/test/goal/e2e-loop.test.ts` 定义 `SUBSCRIPTION_SETTLE_MS = 200`,共有 8 个固定 `Effect.sleep` 等待点。 -- `GoalLoop` 初始化主要读取实例状态并 fork 事件订阅;票据应以可观察 readiness/fence 或最小调度让步替代墙钟等待。 -- 验收要求旧的固定 settle sleep 全部消失,并重复运行目标测试;不把生产行为修改当作默认方案。 - -### F4 — DagStore 双重断言 - -- `packages/opencode/test/dag/dag-timeout-escalation-fixes.test.ts` 有两处 `as unknown as DagStore.Interface`,原始证据只记录了第一处。 -- 两处都需改为类型安全的 `Layer.mock`/fixture factory;不得只清一处。 - -## 组 3/4 与批 C - -### O1 — remote config last-known-good - -- PR #182/#189 前的现状已变化:`packages/opencode/src/config/config.ts` 在 remote transport/body 失败时会 warn + skip;HTML 登录页/auth 与 schema decode 仍硬失败。 -- 剩余需求仅是持久化 LKG。实施前需裁决:缓存内容、稳定键、原子写与权限、何种失败允许回退、损坏缓存行为、TTL。 -- 安全下限:缓存键不得含 header/token;LKG 不得掩盖 auth/decode 错误;损坏缓存只能 warn + skip。 - -### S7 — recovery INVENTED 推断 - -- `packages/opencode/src/dag/runtime/recovery.ts` 的 session checker 从最后一条 assistant finish 推断 active/terminal;tool-calls、unknown 或无 finish 会落入 active/unknown,并可能在 reconcile 中写入 `exec_failed`。 -- `packages/opencode/src/dag/runtime/loop.ts` 在 `ownershipLost` 后会暂停 workflow,现有测试已覆盖该缓解;目前没有已复现的用户态缺陷。 -- 只能按 `/diagnosing-bugs` 先建红灯反馈回路。若“durable transcript 已语义完成却被判 active 并写失败”无法稳定复现,结论应是无修复,不得凭静态推断改代码。 - -### P8 — spawnReady 复杂度 - -- `packages/opencode/src/dag/runtime/loop.ts` 的 `spawnReady` 对 ready 节点反复在全节点数组中 `.find`,静态复杂度为 `O(ready × nodes)`。 -- 原始性能评审明确标记“>50 节点的实际调度开销未实测”;当前无用户痛点或 benchmark。 -- 批 C 只记录观测结论;没有 trace/benchmark 证明影响时,以 no-code 关闭,不阻塞最终晋级。 diff --git a/.scratch/batch-b/issues/01-u1-fork-rollback.md b/.scratch/batch-b/issues/01-u1-fork-rollback.md deleted file mode 100644 index 6c5c8af49d..0000000000 --- a/.scratch/batch-b/issues/01-u1-fork-rollback.md +++ /dev/null @@ -1,23 +0,0 @@ -# 01 — U-1:Session fork 中途失败整体回滚 - -**What to build:** 在真实 SQLite 的 `Session.fork` 集成测试中注入确定性中途失败,证明一个嵌套 durable publication 失败会回滚同一复制批次中已写入的消息/part 事件及投影。 - -**Spec:** `.scratch/batch-b/abort-path-contracts.md` 的 Requirement 1 -**Evidence:** `.scratch/batch-b/evidence.md#u-1--session-fork-嵌套事务回滚` -**Branch:** `test/fork-rollback` -**Blocked by:** None -**Status:** done - -- [x] 复用 `packages/opencode/test/session/fork-batch.test.ts` 的真实 SQLite fixture;不写 adapter-only 替代测试 -- [x] 至少一个 message/part 发布完成后再确定性失败,旧实现若违约时测试能红 -- [x] 目标 session 无复制出的 durable events 与 projections,源 session 不变;Session Created 可保留 -- [x] 若红灯暴露生产缺陷,只做本契约所需的最小修复 -- [x] 在 `packages/opencode` 运行目标测试与 `bun typecheck`,结果附入票据 - -## 验证证据 - -- 基线:`dev@8f8465753b6517b3deeb6ad37002263d3da287fe`;分支:`test/fork-rollback`。 -- 失败注入:真实 SQLite trigger 在第二条复制 part 的 durable event insert 上执行 `RAISE(ABORT)`;此前 3 个嵌套 publication 已释放 savepoint。 -- mutation 红灯:临时移除外层复制事务后,目标 projection 残留 2 条 message(第一条含已复制 part),新增用例 0 pass / 1 fail;mutation 未保留。 -- `cd packages/opencode && bun test test/session/fork-batch.test.ts`:3 pass,0 fail,52 expect。 -- `cd packages/opencode && bun typecheck`:`tsgo --noEmit`,exit 0;现有生产实现满足契约,无生产代码修改。 diff --git a/.scratch/batch-b/issues/02-u2-transport-timeout-abort.md b/.scratch/batch-b/issues/02-u2-transport-timeout-abort.md deleted file mode 100644 index c140dc049e..0000000000 --- a/.scratch/batch-b/issues/02-u2-transport-timeout-abort.md +++ /dev/null @@ -1,24 +0,0 @@ -# 02 — U-2:HTTP timeout 传播到真实 response 取消 - -**What to build:** 使用 loopback `Bun.serve` 与生产 fetch-backed HTTP client,证明 provider timeout 不只返回 Transport/Timeout,还会取消仍打开的真实响应流。 - -**Spec:** `.scratch/batch-b/abort-path-contracts.md` 的 Requirement 2 -**Evidence:** `.scratch/batch-b/evidence.md#u-2--timeout-传播到底层-http-取消` -**Branch:** `test/transport-abort` -**Blocked by:** 01(同一 OpenSpec 串行落地) -**Status:** closed - -- [x] fixture 提供“请求已接收”与“response 已取消”的有界 fence,禁止用固定 sleep 猜时序 -- [x] timeout 后断言现有 `LLMError` Transport/Timeout 形状 -- [x] 服务端确定性观察到 response stream cancellation;request abort 只作补充信号 -- [x] 不用内存 HttpClient 或显式 Fiber interrupt 重复现有覆盖 -- [x] 在 `packages/llm` 连续运行目标测试至少 3 次并运行 `bun typecheck` - -## 验证证据 - -- 基线:`dev@55dd345491de4542dbf6fa7a4ba126a2c23104c4`;分支:`test/transport-abort`。 -- 实现提交:`8ec1ef192`;PR:[LeXwDeX/OpenCode-GraphAgent#193](https://github.com/LeXwDeX/OpenCode-GraphAgent/pull/193) → `dev`。 -- 真实 transport:公开 `LLMClient.stream(...)` 经 `FetchHttpClient.layer` 请求 loopback `Bun.serve`;2 秒有界 fence 分别证明请求已接收与服务端 response `cancel()` 已触发。 -- mutation 红灯:临时移除 response stream 的 `Stream.timeoutOrElse` 后,新增场景在 1 秒测试边界超时,0 pass / 1 fail;mutation 已恢复,生产文件无 diff。 -- `cd packages/llm && bun test test/transport-timeout.test.ts --timeout 30000`:连续 3 次均为 7 pass、0 fail、14 expect;`bun typecheck`:`tsgo --noEmit`,exit 0。 -- 现有生产实现满足 OpenSpec Requirement 2,无生产代码修改;Requirement 3 留给 03 票。 diff --git a/.scratch/batch-b/issues/03-transport-midstream-stall.md b/.scratch/batch-b/issues/03-transport-midstream-stall.md deleted file mode 100644 index 0e57d6a6c7..0000000000 --- a/.scratch/batch-b/issues/03-transport-midstream-stall.md +++ /dev/null @@ -1,24 +0,0 @@ -# 03 — Transport:合法首帧后的 stall 触发逐帧超时 - -**What to build:** 增加“合法 SSE 首帧已交付,连接随后永久停顿”的测试,固定 `Stream.timeoutOrElse` 是相邻帧间隔上界的契约。 - -**Spec:** `.scratch/batch-b/abort-path-contracts.md` 的 Requirement 3 -**Evidence:** `.scratch/batch-b/evidence.md#transport-mid-stream-stall` -**Branch:** `test/midstream-timeout` -**Blocked by:** None(02 已完成) -**Status:** closed - -- [x] 用 fence 证明 timeout 前合法首帧已经交付给消费者 -- [x] 用 TestClock 越过下一帧间隔,随后得到现有 Transport/Timeout -- [x] 不引入真实墙钟 sleep,不改变 timeout 默认值与错误词汇 -- [x] 完整 `transport-timeout.test.ts` 覆盖保持绿色 -- [x] 在 `packages/llm` 运行目标测试与 `bun typecheck` - -## 验证证据 - -- 基线:`dev@1a8635400f3f4b7c4985b06f0776e13d6f2e5e05`;分支:`test/midstream-timeout`。 -- 实现提交:`1d5d08f76`;PR:[LeXwDeX/OpenCode-GraphAgent#194](https://github.com/LeXwDeX/OpenCode-GraphAgent/pull/194) → `dev`。 -- 公开 seam:`LLMClient.stream(...)` 消费合法 SSE text delta;`Deferred` fence 只在 `text-delta` 的 `text === "Hello"` 已交付时解除,随后 `TestClock` 将 1000ms 帧间 timeout 推进到 2000ms。 -- mutation 红灯:临时绕过 response stream 的 `Stream.timeoutOrElse` 后,首帧 fence 仍解除,但新增场景因 stream 未结束而 0 pass / 1 fail;mutation 已恢复,生产文件无 diff。 -- `cd packages/llm && bun test test/transport-timeout.test.ts`:连续 3 次均为 8 pass、0 fail、16 expect;`bun typecheck`:`tsgo --noEmit`,exit 0。 -- 现有生产实现满足 OpenSpec Requirement 3;无生产代码、timeout 默认值、错误词汇或 provider protocol 修改。 diff --git a/.scratch/batch-b/issues/04-f3-subscription-readiness.md b/.scratch/batch-b/issues/04-f3-subscription-readiness.md deleted file mode 100644 index 6233c2602d..0000000000 --- a/.scratch/batch-b/issues/04-f3-subscription-readiness.md +++ /dev/null @@ -1,22 +0,0 @@ -# 04 — F3:用确定性 readiness 替代订阅 settle sleep - -**What to build:** 清除 `packages/opencode/test/goal/e2e-loop.test.ts` 的 `SUBSCRIPTION_SETTLE_MS = 200` 与 8 个固定 settle sleeps,用可观察 readiness/fence 或最小调度让步同步 GoalLoop 订阅就绪。 - -**Evidence:** `.scratch/batch-b/evidence.md#f3--固定订阅-settle-sleep` -**Branch:** `test/goal-readiness` -**Blocked by:** None(03 已完成;代码写集独立) -**Status:** closed - -- [x] 先证明每个 sleep 等待的具体事件/状态,不用另一个超时数值替换 200ms -- [x] 8 个固定 settle sleeps 全部删除或由同一确定性同步机制取代 -- [x] 默认不改生产行为;确需生产 readiness 信号时先在票内写明边界 -- [x] 目标测试连续运行至少 5 次稳定绿色 -- [x] 在 `packages/opencode` 运行目标测试与 `bun typecheck` - -## 完成证据 - -- Commit: `f77106bc03b5c4ec6816dd82d9bba8485974f790`(`test(goal): replace subscription settle sleeps`) -- PR: [#195](https://github.com/LeXwDeX/OpenCode-GraphAgent/pull/195) → `dev` -- TDD: 首场景保留 200ms sleep 时基线绿色;删除同步点后首次 idle 被漏掉,`judge call 1` 未触发;加入一次 `Effect.yieldNow` 后恢复绿色,再机械推广到其余 7 处。 -- 验证:目标文件连续 5 次全绿(每次 8/8);`packages/opencode` 的 `bun typecheck` 通过;提交钩子 lint 0 error、全仓 typecheck 29/29。 -- 范围:旧常量与 8 个固定 sleep 全部消失;生产代码零 diff。 diff --git a/.scratch/batch-b/issues/05-f4-type-safe-dag-store-fixtures.md b/.scratch/batch-b/issues/05-f4-type-safe-dag-store-fixtures.md deleted file mode 100644 index 5a196308ce..0000000000 --- a/.scratch/batch-b/issues/05-f4-type-safe-dag-store-fixtures.md +++ /dev/null @@ -1,21 +0,0 @@ -# 05 — F4:移除 DagStore fixture 的双重类型断言 - -**What to build:** 清除 `packages/opencode/test/dag/dag-timeout-escalation-fixes.test.ts` 中两处 `as unknown as DagStore.Interface`,改用类型安全的 `Layer.mock` 或测试 fixture factory。 - -**Evidence:** `.scratch/batch-b/evidence.md#f4--dagstore-双重断言` -**Branch:** `test/dag-store-fixtures` -**Blocked by:** None(04 已完成;代码写集独立) -**Status:** closed - -- [x] 两处双重断言都消失,不能只修原评审记录的第一处 -- [x] fixture 缺少/签名漂移的方法能在 typecheck 时暴露 -- [x] 不复制 DagStore 生产逻辑到测试 -- [x] timeout escalation 目标测试行为与断言不削弱 -- [x] 在 `packages/opencode` 运行目标测试与 `bun typecheck` - -## 红绿验证(基线 `403461e831aa8cda65d449f4a873db1d1686b44f`) - -- **红灯:** 直接删除两处 `as unknown as DagStore.Interface` 后,在 `packages/opencode` 运行 `bun typecheck`,退出码 2。`TS2740` 分别出现在原第 322、370 行:仅含 `getNode` 的对象缺少 `getWorkflow`、`listWorkflows`、`listBySession`、`listByProject` 与另外 13 个 `DagStore.Interface` 成员。 -- **绿灯:** 改为 `Layer.mock(DagStore.Service)`,通过 `Layer.unwrap` 将类型安全的 store fixture 注入 `Layer.mock(Dag.Service)`;`bun typecheck` 退出码 0。 -- **重复验证:** `bun test test/dag/dag-timeout-escalation-fixes.test.ts` 连续运行 3 次,每次均为 12 pass、0 fail、38 次断言。 -- **范围验证:** 相对上述基线,生产文件零 diff;只修改目标测试与批次 B 的票 05/06。 diff --git a/.scratch/batch-b/issues/06-o1-lkg-spec.md b/.scratch/batch-b/issues/06-o1-lkg-spec.md deleted file mode 100644 index 862d90e665..0000000000 --- a/.scratch/batch-b/issues/06-o1-lkg-spec.md +++ /dev/null @@ -1,22 +0,0 @@ -# 06 — O1:remote config LKG 小规格 - -**What to build:** 只为 remote config 的持久化 last-known-good 增量产出一份小规格;不修改生产代码。OpenSpec 原件在 local-only `/openspec/` 生成并校验,同时把可执行镜像写入 `.scratch/batch-b/config-lkg-spec.md`,再更新 07 的具体文件、验收与分支边界。 - -**Evidence:** `.scratch/batch-b/evidence.md#o1--remote-config-last-known-good` -**Branch:** `docs/config-lkg-spec` -**Blocked by:** 05(批次串行) -**Status:** closed - -- [x] 定义缓存内容与写入时机:只缓存完整下游验证成功的原始响应 body,持久化发生在 Environment 替换前,envelope 不保存请求 header/token -- [x] 定义稳定 cache key、原子写、文件权限:规范化 URL 的 SHA-256 文件名、同目录临时文件 + rename、最终 `0600`,key/日志不含凭据或正文 -- [x] 定义回退矩阵:transport、非 401/403 的不可用状态、body read 与非 HTML JSON 语法失败可回退;401/403、HTML/login/auth、schema/object/final decode 硬失败 -- [x] 定义损坏/空缓存 warn + skip 与 LKG 永不过期策略;`writtenAt`/年龄只做安全诊断,下一次合法在线成功原子覆盖 -- [x] OpenSpec 4/4 apply-ready,原件与镜像逐 artifact 字节一致,07 已获得 TDD 文件边界与可执行验收 - -## 交付记录 - -- **Change 原件(local-only):** `openspec/changes/remote-config-lkg/`(`proposal.md`、`design.md`、`specs/remote-config-lkg/spec.md`、`tasks.md`) -- **Tracked 镜像:** `.scratch/batch-b/config-lkg-spec.md` -- **校验 1:** `openspec validate --changes` → `✓ change/remote-config-lkg`,`1 passed, 0 failed` -- **校验 2:** `openspec validate remote-config-lkg --type change --strict --no-interactive` → `Change 'remote-config-lkg' is valid` -- **基线/范围:** `dev@4675435d94d462d2f9317d6688ddab2f0105c746`;本票没有修改 `packages/**` 或生产代码 diff --git a/.scratch/batch-b/issues/07-o1-lkg-implement.md b/.scratch/batch-b/issues/07-o1-lkg-implement.md deleted file mode 100644 index d0c367c4b1..0000000000 --- a/.scratch/batch-b/issues/07-o1-lkg-implement.md +++ /dev/null @@ -1,45 +0,0 @@ -# 07 — O1:实现 remote config last-known-good 缓存 - -**What to build:** 按 06 产出的已校验 OpenSpec 实现 LKG,并扩展现有 `packages/opencode/test/config/wellknown-offline.test.ts`;不得重新实现已存在的 warn + skip 离线降级。 - -**Evidence:** `.scratch/batch-b/evidence.md#o1--remote-config-last-known-good` -**Branch:** `feat/config-lkg` -**Blocked by:** 无(06 已关闭;OpenSpec `remote-config-lkg` 为 apply-ready) -**Status:** closed - -- [x] 06 的 OpenSpec requirements/scenarios 已写入下方“规格入口”;实施以 tracked 镜像为稳定入口,以 local-only change 为 OpenSpec 原件 -- [x] 在线成功后产生可复用 LKG,随后 transport/body 失败按规格回退 -- [x] auth/HTML login/decode 失败仍保持硬失败 -- [x] 损坏缓存不崩溃、不覆盖错误类别,且日志不含凭据 -- [x] 在 `packages/opencode` 运行目标测试与 `bun typecheck` - -## 实施证据 - -- **基线/分支:** `af0be65b0831c352cad28e6a32ac1c3c883fc5d8` → `feat/config-lkg` -- **公开 seam:** `Config.Service.get()` 的 well-known/remote-config 加载结果;持久化细节通过 `RemoteLkg.digest/read/write` 窄接口验证 -- **RED:** 在线→transport 新实例期望 `lkg/transport-model`、实际 `undefined`;持久化测试因 `@/config/remote-lkg` 不存在失败;401/403 预实现错误地返回成功 -- **GREEN:** `bun test test/config/remote-lkg.test.ts test/config/wellknown-offline.test.ts` 连续 3 次 `24 pass / 0 fail`;`bun typecheck` 绿色 -- **OpenSpec:** `remote-config-lkg` 为 `14/14`、`all_done`;`openspec validate --changes` 为 `1 passed, 0 failed`,strict change validate 为 valid;tracked 镜像 4/4 artifacts 与当前 local-only 原件逐字节一致 -- **范围:** 4 个指定代码/测试文件;无 fixture、依赖、lockfile、core、HTTP/SDK 改动 - -## 规格入口 - -- [只持久化完整验证成功的原始远端响应](../config-lkg-spec.md#requirement-只持久化完整验证成功的原始远端响应):[在线写入→离线复用](../config-lkg-spec.md#scenario-在线成功后写入并可离线复用)、[Environment 秘密不固化](../config-lkg-spec.md#scenario-environment-秘密不被固化)、[验证失败不写入](../config-lkg-spec.md#scenario-下游验证失败不产生新-lkg) -- [缓存身份与诊断不得泄露凭据](../config-lkg-spec.md#requirement-缓存身份与诊断不得泄露凭据):[规范化 URL 稳定摘要](../config-lkg-spec.md#scenario-等价-url-使用同一稳定文件名)、[key/log 无凭据正文](../config-lkg-spec.md#scenario-凭据与正文不出现在-key-或日志) -- [LKG 更新必须原子且用户私有](../config-lkg-spec.md#requirement-lkg-更新必须原子且用户私有):[同目录 rename + `0600`](../config-lkg-spec.md#scenario-同目录原子替换并设置文件模式)、[失败更新保留旧 LKG](../config-lkg-spec.md#scenario-rename-前写入失败保留旧-lkg) -- [只有允许降级的在线失败可以回退](../config-lkg-spec.md#requirement-只有允许降级的在线失败可以回退):[transport/body 回退](../config-lkg-spec.md#scenario-transport-或非认证不可用状态回退)、[401/403 不回退](../config-lkg-spec.md#scenario-401-或-403-不回退)、[HTML 不回退](../config-lkg-spec.md#scenario-html-登录页不回退)、[decode 不回退](../config-lkg-spec.md#scenario-schema-decode-错误不回退) -- [不可用缓存保留原 warn + skip](../config-lkg-spec.md#requirement-不可用缓存保留原-warn--skip-语义)与[LKG 不因年龄自动过期](../config-lkg-spec.md#requirement-lkg-不因年龄自动过期):[第一/第二跳损坏缓存边界](../config-lkg-spec.md#scenario-第一跳损坏或空缓存告警后跳过来源)、[长期离线继续使用](../config-lkg-spec.md#scenario-很旧的-lkg-仍支持长期离线) - -## 实现边界 - -- **生产文件:** 仅修改 `packages/opencode/src/config/config.ts`,新增 `packages/opencode/src/config/remote-lkg.ts` -- **测试文件:** 扩展 `packages/opencode/test/config/wellknown-offline.test.ts`,新增 `packages/opencode/test/config/remote-lkg.test.ts` -- **禁止扩张:** 不修改 `packages/core`、HTTP routes、SDK/生成物、依赖或配置 schema;不重写当前第一跳 skip / 第二跳空 fetched-config 的 warn + skip 结果 -- **方法:** 严格按镜像末尾 `tasks.md` 红-绿顺序实施;rename 失败只允许最窄文件系统故障注入,其余路径使用真实实现 - -## 可执行验收 - -1. `cd packages/opencode && bun test test/config/remote-lkg.test.ts test/config/wellknown-offline.test.ts` -2. `cd packages/opencode && bun typecheck` -3. 核对目标测试覆盖在线→离线、失败写入后旧 LKG、401/403/HTML/decode 硬失败、损坏/空缓存、key/log 无凭据、永久 LKG、`0600` 与同目录原子 rename -4. 核对实现 diff 只包含上述四个主要文件;任何最小 fixture 例外必须在提交说明中单列 diff --git a/.scratch/batch-b/issues/08-s7-recovery-invented-diagnosis.md b/.scratch/batch-b/issues/08-s7-recovery-invented-diagnosis.md deleted file mode 100644 index fcc13269c1..0000000000 --- a/.scratch/batch-b/issues/08-s7-recovery-invented-diagnosis.md +++ /dev/null @@ -1,23 +0,0 @@ -# 08 — S7:诊断 recovery INVENTED 推断 - -**What to build:** 仅诊断“已语义完成的 durable transcript 被 recovery 判为 active/ownershipLost 并写入 `exec_failed`”是否可复现。当前票不预设存在缺陷,也不授权先改生产代码。 - -**Method:** `/diagnosing-bugs` -**Evidence:** `.scratch/batch-b/evidence.md#s7--recovery-invented-推断` -**Branch:** `test/recovery-diagnosis` -**Blocked by:** 无(07 已关闭) -**Status:** closed-no-fix - -- [x] 第一项产出是一条确定性、快速、可由 agent 重复运行且能红灯的命令;在此之前不写理论/修复 -- [x] 症状必须包含“durable transcript 语义完成”与“reconcile 实际写 failed”,不能只单测 helper 返回 active -- [x] 红灯成立后才列 3–5 个可证伪假设、最小化复现并另开独立修复票(未出现红灯,因此未进入该阶段) -- [x] 无法建立反馈回路时记录尝试和阻塞原因,以 no-fix 关闭(已建立反馈回路且症状未复现,按 no-fix 关闭) -- [x] 不把既有 ownershipLost → workflow pause 缓解误报为未覆盖 - -## 关闭证据 - -- 命令:`cd packages/opencode && bun test test/dag/dag-recovery-transcript-diagnosis.test.ts` -- 三次结果:均为 `2 pass / 0 fail`,约 `1.13s`。 -- 完成态:真实 durable transcript `finish: "stop"` 经 `DagLoop.init → reconcileWorkflow` 后持久化为 `completed`,未写 `exec_failed`。 -- 对照态:真实 durable transcript `finish: "tool-calls"` 经同一路径持久化为 `failed/exec_failed`,workflow 随后被既有 recovery-pause 置为 `paused`。 -- 完整报告:`.scratch/batch-b/s7-diagnosis.md`;未创建票 09。 diff --git a/.scratch/batch-b/issues/09-promote-dev-main-release.md b/.scratch/batch-b/issues/09-promote-dev-main-release.md deleted file mode 100644 index a7bce5d9fa..0000000000 --- a/.scratch/batch-b/issues/09-promote-dev-main-release.md +++ /dev/null @@ -1,12 +0,0 @@ -# 09 — 收束:批 B+C 后 dev → main → release-fork - -**What to build:** 批 B 所有票关闭、批 C P8 完成观测处置后,确认最新 `dev` 全量 CI 绿色,发 dev→main 晋级 PR;四项门禁全绿后合并并手动运行 release-fork。 - -**Blocked by:** 01–08 全部关闭;`.scratch/batch-c/issues/01-p8-spawn-ready-observation.md` 已处置 -**Status:** blocked - -- [ ] 最新 dev push 的 Typecheck、Unit Tests (linux)、E2E Tests (linux/windows) 全绿 -- [ ] 每票 PR/merge commit/验证命令已回填,S7 若确认缺陷则其独立修复票也关闭 -- [ ] dev→main PR 描述列出批 B 交付与批 C no-code/benchmark 裁决 -- [ ] 四项 main PR 门禁全绿后合并 -- [ ] release-fork 从 main 成功产出正式版,再执行用户确认过的分支/worktree 清理 diff --git a/.scratch/batch-b/s7-diagnosis.md b/.scratch/batch-b/s7-diagnosis.md deleted file mode 100644 index aae0f0126c..0000000000 --- a/.scratch/batch-b/s7-diagnosis.md +++ /dev/null @@ -1,53 +0,0 @@ -# S7 — recovery INVENTED 推断诊断 - -- **Status:** closed-no-fix -- **基线:** `dev@18273554f4f2c18cab1370922eb1ec004ba5bad9` -- **分支:** `test/recovery-diagnosis` -- **日期:** 2026-08-09 - -## 反馈回路 - -从 `packages/opencode` 运行: - -```bash -bun test test/dag/dag-recovery-transcript-diagnosis.test.ts -``` - -诊断期间使用的 throwaway 测试已删除。它走过以下真实持久化链路: - -1. `Session.layer` 通过 `Session.updateMessage` 发布 durable transcript 事件,`SessionProjector` 写入数据库;测试再用 `Session.messages` 读回并断言完成边界。 -2. `DagLoop.init` 扫描 durable running workflow,进入 `recoverWorkflow`。 -3. `makeSessionStatusChecker` 通过真实 `Session.get/messages` 读取 child transcript,`reconcileWorkflow` 作出 settlement。 -4. `Dag.nodeCompleted/nodeFailed` 发布事件,`DagProjector` 投影到真实 `DagStore`;测试直接读取 node/workflow 持久化结果。 - -观测断言: - -| 输入 | transcript 证据 | 实际持久化结果 | workflow 结果 | -|---|---|---|---| -| 语义完成 | 最后一条 assistant 为 `finish: "stop"`,并带 `time.completed` | node `completed`,`errorClass: null` | `completed` | -| red-capable 对照 | 最后一条 assistant 为 `finish: "tool-calls"` | node `failed`,`errorClass: "exec_failed"` | `paused` | - -## 运行结果 - -最终 `DagLoop.init` seam 连续运行三次,均为 `2 pass / 0 fail`: - -| 次数 | 结果 | 耗时 | -|---|---|---| -| 1 | `2 pass / 0 fail` | `1.129s` | -| 2 | `2 pass / 0 fail` | `1.131s` | -| 3 | `2 pass / 0 fail` | `1.133s` | - -首次运行因工作区尚未安装 `@opentui/solid/preload`,在加载测试前退出;执行 `bun install --no-save` 后依赖就绪,未修改 lockfile。随后先在真实 `Session → reconcileWorkflow → DagStore` seam 连续跑绿三次,再收紧到上述 `DagLoop.init` seam 并连续跑绿三次。 - -## 语义边界核对 - -- `packages/opencode/src/session/prompt.ts` 的真实 loop 只有在最后 assistant 已有 finish、finish 不是 `tool-calls`、没有待处理 tool call 且 assistant 位于最后 user 之后时才走完成退出。 -- 同一 loop 将 `tool-calls` 与 `unknown` 明确视为需要 continuation;因此这两类 transcript 不能作为“系统语义已经完成”的证据。 -- 测试没有使用自定义完成布尔值。完成态由真实持久化 transcript 中的 `finish: "stop"` 与 `time.completed` 证明,并经生产 `Session.messages` 读回。 -- `tool-calls` 对照确实经过 recovery 写入 `exec_failed`,随后触发现有 `ownershipLost → workflow pause` 缓解;该行为用于证明反馈回路可红,不作为新缺陷上报。 - -## 结论 - -精确症状“已语义完成的 durable transcript 被 recovery 判为 active/ownershipLost,并实际持久化 `exec_failed`”未复现。完成态 transcript 在真实 recovery/loop 调用链中稳定投影为 node/workflow `completed`;会写 `exec_failed` 的对照 transcript 按现有 Session loop 语义仍需 continuation。 - -本票不修改生产代码,不保留诊断测试,不创建猜测性修复票,也不创建票 09。 diff --git a/.scratch/batch-c/issues/01-p8-spawn-ready-observation.md b/.scratch/batch-c/issues/01-p8-spawn-ready-observation.md deleted file mode 100644 index d4be883719..0000000000 --- a/.scratch/batch-c/issues/01-p8-spawn-ready-observation.md +++ /dev/null @@ -1,30 +0,0 @@ -# 01 — P8:spawnReady 复杂度观测处置 - -**What to build:** 记录 `spawnReady O(ready × nodes)` 是否有实际性能证据。没有 trace/benchmark/用户痛点时,以 no-code 关闭;不得仅凭静态复杂度实施缓存或索引改造。 - -**Evidence:** `.scratch/batch-b/evidence.md#p8--spawnready-复杂度` -**Branch:** `docs/p8-observation` -**Blocked by:** None -**Status:** closed-no-code - -- [x] 搜集已有生产 trace、benchmark 或明确用户场景,不为本票新造大规模优化工程 -- [x] 无量化证据:记录“当前不做”与重开阈值,状态改 closed-no-code -- [x] 有量化证据:另开 `/improve-codebase-architecture` 设计票,写明基线与目标(本次无量化证据,因此未开票) -- [x] 本观测票本身不改 `spawnReady` - -## 关闭结论 - -- 已复核 promotion evidence 与 DAG deep-review 报告;只有静态 `O(ready × nodes)` 推断。 -- 未发现生产 trace、可重复 benchmark 或明确用户场景能把可感知延迟归因到 `spawnReady`。 -- 当前不增加索引或缓存;在没有收益基线时,这类状态会扩大一致性与失效维护面。 -- 本票仅记录裁决,生产代码零改动。 - -## 重开阈值 - -满足任一条件时重开独立性能设计票: - -1. 用户态或生产 profile 将调度延迟明确归因到 `spawnReady`。 -2. 可重复 benchmark 显示 `spawnReady` 占一次 wake 调度耗时的 10% 以上。 -3. 实际工作流规模长期超过当前评审采用的 50 节点观察区间。 - -重开后必须先记录基线、目标与代表性图规模,再选择索引或缓存方案。 diff --git a/AGENTS.md b/AGENTS.md index b6a7f32ee1..6a95435adf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -215,6 +215,12 @@ Invariants for extending the SolidJS/opentui TUI. The DAG inspector (`src/featur - Keep EventV2 replay owner claims separate from clustered Session execution ownership. - Keep the System Context algebra, registry, and built-ins in `src/system-context`; keep Context Source producers with their observed domains, and keep Session History selection plus Context Epoch persistence Session-owned. +## DAG Configuration Repository + +The authoritative repository for curated DAG workflow YAML and configuration-owned block or prompt assets is [`LeXwDeX/opencode-dag-config`](https://github.com/LeXwDeX/opencode-dag-config). Inspect and update that repository when a task changes reference workflows, composable block configurations, or their embedded worker prompts; configuration-only changes do not belong in this runtime repository. + +This repository owns the DAG schema, compiler, validator, runtime, and release integration. Changes that cross the boundary land runtime support first, then update the config repository's `runtime-compat.json` to the merged full runtime commit SHA and pass its template-validation CI. + ## Agent skills ### Issue tracker diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..f3b1950516 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,178 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this is + +**OpenCode-GraphAgent** (product name "GraphAgent"): a fork of the MIT-licensed +[opencode](https://github.com/anomalyco/opencode) terminal AI agent that adds a +**DAG workflow engine** for multi-agent orchestration. A task is decomposed into a +dependency graph of child-agent sessions, driven to completion with durable, +crash-recoverable, inspectable state. Upstream opencode capabilities (multi-provider +LLM, built-in LSP, TUI/desktop/web clients, client/server architecture) are preserved. + +**`AGENTS.md` is the canonical contributor guide.** It holds the full style guide, +git workflow (铁律), and二次开发 (extending) invariants. Read it for *how* to write +code here; this file covers the *what* and the big-picture architecture, and does not +repeat AGENTS.md. Default branch is `main`. + +## Commands + +Requirements: **Bun 1.3+** (`packageManager: bun@1.3.14`). All commands run from repo +root unless noted. + +```bash +bun install # install (postinstall fixes node-pty) + +# Run the app (bun dev == local equivalent of the built `opencode` CLI) +bun dev # TUI, in packages/opencode by default +bun dev # TUI against another dir (`bun dev .` for repo root) +bun dev serve # headless HTTP API server (default port 4096) +bun dev serve --port 8080 # custom port +bun dev web # server + web UI +bun run --cwd packages/app dev # web app dev server (needs `bun dev serve` running) +bun run --cwd packages/desktop dev # Electron desktop app + +# Quality gates +bun typecheck # turbo typecheck across all packages (the commit gate) +bun typecheck # also runnable from a package dir, e.g. packages/opencode +bun lint # oxlint, ratcheted: --max-warnings=4852 (see below) + +# Tests — NEVER run from repo root (guard: do-not-run-tests-from-root; bunfig enforces it) +cd packages/opencode && bun test # full suite (only-failures shown) +cd packages/opencode && bun test path/to/file.test.ts # one file +cd packages/opencode && bun test --test-name-pattern "pattern" # filtered tests +cd packages/opencode && bun run test:dag-core # DAG scheduling/state-machine coverage gate +cd packages/opencode && bun run test:httpapi # HTTP API contract exerciser (3 modes) + +# Build & codegen +./packages/opencode/script/build.ts --single # standalone binary → packages/opencode/dist//bin/opencode +./packages/sdk/js/script/build.ts # regenerate the JS SDK from the OpenAPI spec (after HTTP route changes) +bun run generate # root: regen SDK + openapi.json + format (wrapper of the above) +``` + +**`bun typecheck` (`tsgo --noEmit`) is the real gate.** `bun run build` uses esbuild +and transpiles only — a green build can still ship a missing import or non-existent API. +Never invoke `tsc` directly. + +**Lint ratchet:** `bun lint` runs `oxlint --max-warnings=4852`. The threshold only ever +tightens — new warnings fail CI and the pre-commit hook. When you fix existing warnings, +lower the number in the root `package.json` `lint` script to match (rationale recorded in +the `_lint_ratchet_note` field there). `oxlint` is `typeAware: true`. + +Pre-commit (husky) runs `lint` + `typecheck`. `post-checkout`/`pre-push` hooks also exist. + +## Architecture + +### Monorepo layout (Bun workspaces + Turborepo) + +`packages/core` (`@opencode-ai/core`) is the framework layer: pure domain logic, the +plugin/SDK, schema, storage, event system, and the **pure half of the DAG engine**. +`packages/opencode` (`opencode`) is the application: the CLI/server entrypoint, session +runtime, HTTP server, and the **execution half of the DAG engine**. `core` has no +dependency on `opencode`; the arrow points the other way. + +Key packages: + +| Package | Role | +|---|---| +| `packages/core` | Domain primitives, storage, schema, events, **pure DAG state machine/projector/store** | +| `packages/opencode` | CLI + headless server, session runtime, **DAG execution/loop/spawn/admission/recovery** | +| `packages/tui` | Terminal UI (SolidJS + opentui), incl. the DAG inspector (`src/feature-plugins/system/dag-inspector.tsx`) | +| `packages/app` · `packages/web` · `packages/desktop` | Web components / web app / Electron wrapper | +| `packages/sdk/js` | `@opencode-ai/sdk` — **generated** from the server's OpenAPI spec (`src/v2/gen`) | +| `packages/plugin` · `packages/schema` · `packages/protocol` · `packages/client` | Plugin SDK, event/schema definitions, wire protocol, server client | + +### The DAG engine is split across two packages (the non-obvious part) + +The workflow engine is the fork's reason for existing. It is deliberately divided: + +- **`packages/core/src/dag`** — *pure, side-effect-free*: declared state-machine transition + tables (`core/transitions.ts`), dependency graph + cycle/dangling validation + (`core/graph.ts`), wave-based scheduler (`core/scheduling.ts`), replan fragment merge + (`core/replan.ts`), and the **event projector** (`projector.ts`) that writes the SQLite + read model *inside* the event-publish transaction. History is event replay, not a log + table. `store.ts` / `sql.ts` are the persistence boundary. +- **`packages/opencode/src/dag`** — *effectful execution*: the workflow service (`dag.ts`, + `workflows.ts`), the execution loop (`runtime/loop.ts`), spawning real child sessions per + node (`runtime/spawn.ts`, same path as the `task` tool), deep-mode admission Q&A + (`admission.ts`), the `design`/`diff` review lifecycle with implementation-fingerprint + contracts (`review-lifecycle.ts`), lazy evidence-based crash recovery + (`runtime/recovery.ts`), and prompt-template resolution (`templates/`). + +A node never names its own model — the graph declares which nodes are *critical* and +`.opencode/dag.jsonc` decides what model runs each tier (`advanced` / `standard`). +Agents drive workflows through a single `workflow` tool; humans observe/control via the +TUI DAG inspector or the `GET/POST /dag*` HTTP routes. + +### Effect-TS is the composition backbone + +The codebase is built on `effect` 4.0.0-beta. Services are `Context.Tag`s wired through +`Layer`s. Two parallel composition systems coexist and **do not share wiring**: + +1. `X.defaultLayer` / `AppLayer` — the primary Effect layer graph. +2. `LayerNode` (`.node` exports, `LayerNode.buildLayer`) — a separate node-based system. + +Both demand **self-contained layers**: a `defaultLayer` must `Layer.provide` every +dependency its body `yield*`s. `Layer.provideMerge(self, layer)` builds `layer` in +isolation, and `Layer.mergeAll` does not cross-provide siblings — so a layer that quietly +assumes an ambient service will compile clean and crash at runtime in a different entry +point. Optional/heavyweight cross-deps (Provider, MCP, HttpClient) are resolved lazily via +`Effect.serviceOption(Tag)` at the call site. See AGENTS.md "Extending the Codebase" for +the full invariant list — the build will not catch violations of these. + +### Configuration & data files (all under `.opencode/`) + +| Path | Purpose | +|---|---| +| `.opencode/dag.jsonc` | Model tiers + `thinking_depth` for DAG child sessions (global counterpart in opencode config dir) | +| `.opencode/workflows/*.yaml` | Project-local saved workflow specs; curated workflows live in the config repository | +| `.opencode/dag-prompts/*.md` | Project-local node prompt templates referenced by `prompt_template.id` | +| `.opencode/command/*.md` | Custom slash commands (`commit`, `issues`, `changelog`, `translate`, `learn`, …) | +| `.opencode/opencode.jsonc` | Main app config | + +Curated *global* workflows live in a separate repo, [`LeXwDeX/opencode-dag-config`](https://github.com/LeXwDeX/opencode-dag-config); config-only changes belong there, not here. `dag.jsonc` and the workflow library are read lazily — edits apply to the next workflow start without a restart. + +### Spec-driven & domain docs + +- **`openspec/`** — spec-driven change proposals. `openspec/changes//` holds + `proposal.md` / `design.md` / `tasks.md` / `specs/`; `openspec/specs/` holds the + established capability specs. Active proposals (e.g. `harden-goal-state-machine`, + `internalize-dag-block-capabilities`) define in-flight work. +- **`CONTEXT-MAP.md` → `CONTEXT.md`** — multi-context domain docs. `CONTEXT-MAP.md` is the + index; read the linked `CONTEXT.md`(s) relevant to the area before working in it. A DAG + `CONTEXT.md` does not yet exist. +- **`docs/agents/`** — issue-tracker workflow, triage labels, domain-doc conventions. + +## Critical, non-obvious rules + +These compile clean but bite at runtime or in CI — the build will not catch them: + +- **Regenerate the SDK after touching any HTTP API route.** `packages/sdk/js` is generated + from the server's OpenAPI spec; a stale SDK breaks the TUI at runtime (calling a client + method that doesn't exist) in a way typecheck can't catch. After route changes run + `./packages/sdk/js/script/build.ts`. CI's `Check generated SDK` step + (`bun run check:generated` = regen + `git diff --exit-code -- src/v2/gen`) enforces this. +- **Changing an HTTP route's request/response shape** requires updating its scenario in + `test/server/httpapi-exercise/index.ts`; `bun run test:httpapi --fail-on-missing` fails otherwise. +- **Don't hand-duplicate SDK types in TUI/plugin code** — re-export the generated type so a + server schema change surfaces as a typecheck error instead of silent drift. +- **Every event type the TUI consumes** must be `define()`d in `packages/schema` and listed + in `Event manifest.Definitions`, or the generated event union won't contain it. Ephemeral + push events (e.g. `dag.workflow.summary.updated`) stay OUT of the durable manifest — emit + via `GlobalBus`, never persist, design consumers to tolerate missed events (re-fetch on bootstrap). +- **Adding a service other services see:** find every consumer's `.node` list (not just its + `defaultLayer`) and add the new service's node there. A missing wire compiles clean and + fails silently (feature no-ops) rather than erroring. +- **Mixed license:** upstream code is MIT; the DAG engine + (`packages/core/src/dag/**`, `packages/opencode/src/dag/**`) is AGPL-3.0-or-later. Exact + boundaries are in `NOTICE`. Don't move AGPL code into MIT-licensed paths or vice versa. + +## Git workflow (summary — full rules in AGENTS.md) + +`feat/fix` branches → PR (Typecheck gate) → `dev` (fast integration, push runs full tests) → +PR (full gate: Typecheck + Unit + E2E on linux+windows) → `main` → manual release. Direct +pushes to `main`/`dev` are blocked by GitHub Rulesets. Branch names: `{type}/{short-name}` +(`feat`, `fix`, `chore`, `docs`, `refactor`, `test`, `release`, `hotfix`), enforced by +Ruleset. Commits/PR titles: conventional `type(scope): summary`. All PRs must reference an +existing issue (`Fixes #N`). Curated DAG configs are owned by the `opencode-dag-config` repo. From 0a1f1d31aee93373d326209d95e10abc2c5715bd Mon Sep 17 00:00:00 2001 From: Lex Date: Thu, 13 Aug 2026 20:18:48 +0800 Subject: [PATCH 2/3] chore(repo): remove legacy issue forms --- .github/ISSUE_TEMPLATE/bug-report.yml | 66 ---------------------- .github/ISSUE_TEMPLATE/config.yml | 5 -- .github/ISSUE_TEMPLATE/feature-request.yml | 19 ------- 3 files changed, 90 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug-report.yml delete mode 100644 .github/ISSUE_TEMPLATE/config.yml delete mode 100644 .github/ISSUE_TEMPLATE/feature-request.yml diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml deleted file mode 100644 index da82bde2e1..0000000000 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ /dev/null @@ -1,66 +0,0 @@ -name: Bug report -description: Report an issue that should be fixed (avoid pasting giant AI generated summaries or your issue may be closed/ignored) -body: - - type: textarea - id: description - attributes: - label: Description - description: Describe the bug you encountered - placeholder: What happened? - validations: - required: true - - - type: input - id: plugins - attributes: - label: Plugins - description: What plugins are you using? - validations: - required: false - - - type: input - id: opencode-version - attributes: - label: OpenCode version - description: What version of OpenCode are you using? - validations: - required: false - - - type: textarea - id: reproduce - attributes: - label: Steps to reproduce - description: How can we reproduce this issue? - placeholder: | - 1. - 2. - 3. - validations: - required: false - - - type: textarea - id: screenshot-or-link - attributes: - label: Screenshot and/or share link - description: Run `/share` to get a share link, or attach a screenshot - placeholder: Paste link or drag and drop screenshot here - validations: - required: false - - - type: input - id: os - attributes: - label: Operating System - description: what OS are you using? - placeholder: e.g., macOS 26.0.1, Ubuntu 22.04, Windows 11 - validations: - required: false - - - type: input - id: terminal - attributes: - label: Terminal - description: what terminal are you using? - placeholder: e.g., iTerm2, Ghostty, Alacritty, Windows Terminal - validations: - required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 9501a1be65..0000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,5 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: 💬 Discord Community - url: https://discord.gg/opencode - about: For support, troubleshooting, how-to questions, and real-time discussion. diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml deleted file mode 100644 index 42f1d3c51a..0000000000 --- a/.github/ISSUE_TEMPLATE/feature-request.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: 🚀 Feature Request -description: Suggest an idea, feature, or enhancement -title: "[FEATURE]:" - -body: - - type: checkboxes - id: verified - attributes: - label: Feature hasn't been suggested before. - options: - - label: I have verified this feature I'm about to request hasn't been suggested before. - required: true - - - type: textarea - attributes: - label: Describe the enhancement you want to request - description: What do you want to change or add? What are the benefits of implementing this? Try to be detailed so we can understand your request better :) - validations: - required: true From a406dcc54859e00982b6320881969df501adf8df Mon Sep 17 00:00:00 2001 From: Lex Date: Thu, 13 Aug 2026 20:26:05 +0800 Subject: [PATCH 3/3] docs: align claude guide with domain map --- CLAUDE.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index f3b1950516..4b6352ab73 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -140,8 +140,7 @@ Curated *global* workflows live in a separate repo, [`LeXwDeX/opencode-dag-confi established capability specs. Active proposals (e.g. `harden-goal-state-machine`, `internalize-dag-block-capabilities`) define in-flight work. - **`CONTEXT-MAP.md` → `CONTEXT.md`** — multi-context domain docs. `CONTEXT-MAP.md` is the - index; read the linked `CONTEXT.md`(s) relevant to the area before working in it. A DAG - `CONTEXT.md` does not yet exist. + index; read the linked `CONTEXT.md`(s) relevant to the area before working in it. - **`docs/agents/`** — issue-tracker workflow, triage labels, domain-doc conventions. ## Critical, non-obvious rules