@tea-agent/loop-agent 0.13.0-alpha.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/AGENTS.md +4 -0
  2. package/CHANGELOG.md +57 -53
  3. package/README.md +12 -3
  4. package/dist/application/dag/generate-task-dag.js +28 -58
  5. package/dist/application/evaluation/candidate-hash.js +75 -0
  6. package/dist/application/evaluation/candidate.js +52 -0
  7. package/dist/application/evaluation/replay.js +289 -0
  8. package/dist/application/evaluation/types.js +130 -0
  9. package/dist/cli/command-definitions.js +17 -4
  10. package/dist/cli/program.js +8 -4
  11. package/dist/commands/eval.js +235 -0
  12. package/dist/commands/init.js +131 -24
  13. package/dist/executors/pi-sdk-executor.js +38 -24
  14. package/dist/executors/shell-executor.js +226 -15
  15. package/dist/executors/shell-presets.js +20 -0
  16. package/dist/executors/shell-verification.js +7 -0
  17. package/dist/governance/manifest-types.js +1 -0
  18. package/dist/infrastructure/evaluation/candidate-store.js +439 -0
  19. package/dist/infrastructure/evaluation/store.js +40 -0
  20. package/dist/task/config-types.js +23 -0
  21. package/dist/worker/observe/routes.js +18 -3
  22. package/dist/worker/observe/spec-evidence.js +1 -1
  23. package/dist/worker/observe/static/dom.js +160 -1
  24. package/dist/worker/observe/static/state.js +14 -0
  25. package/dist/worker/observe/static/views/dag-inspector.js +35 -4
  26. package/dist/worker/observe/static/views/dag.js +9 -0
  27. package/dist/worker/observe/static/views/dashboard.js +702 -445
  28. package/dist/worker/observe/static/views/session-timeline.js +15 -1
  29. package/dist/workflows/dag/backend-test-analysis-contract.js +120 -0
  30. package/dist/workflows/dag/backend-test-case-manifest.js +503 -0
  31. package/dist/workflows/dag/backend-test-execution-contract.js +353 -0
  32. package/dist/workflows/dag/backend-test-result-contract.js +568 -0
  33. package/dist/workflows/dag/decision-envelope.js +57 -2
  34. package/dist/workflows/dag/dynamic-runtime/map.js +90 -2
  35. package/dist/workflows/dag/frontend-implementation-contract.js +240 -0
  36. package/dist/workflows/dag/frontend-project-capability.js +309 -0
  37. package/dist/workflows/dag/frontend-repair.js +341 -0
  38. package/dist/workflows/dag/frontend-risk.js +161 -0
  39. package/dist/workflows/dag/frontend-verification-trace.js +190 -0
  40. package/dist/workflows/dag/init-hybrid.js +2407 -297
  41. package/dist/workflows/dag/node-execution.js +9 -0
  42. package/dist/workflows/dag/prompt.js +9 -0
  43. package/dist/workflows/dag/repair-artifact.js +43 -3
  44. package/dist/workflows/dag/report.js +35 -1
  45. package/dist/workflows/dag/runner.js +28 -2
  46. package/dist/workflows/dag/skill-instructions.js +4 -2
  47. package/dist/workflows/dag/task-demand-routing.js +383 -0
  48. package/dist/workflows/dag/types.js +71 -13
  49. package/dist/workflows/dag/upstream-artifacts.js +1 -0
  50. package/dist/workflows/dag/validate.js +59 -1
  51. package/docs/README.md +6 -3
  52. package/docs/agent-dag-recovery-playbook.md +5 -3
  53. package/docs/agent-dag-runner.md +3 -3
  54. package/docs/architecture/README.md +3 -3
  55. package/docs/architecture/dag-execution.md +1 -1
  56. package/docs/architecture/evolution.md +13 -13
  57. package/docs/architecture/facts-and-state.md +1 -1
  58. package/docs/architecture/runtime-boundaries.md +7 -7
  59. package/docs/architecture/system-overview.md +3 -3
  60. package/docs/architecture/worker-and-feature.md +3 -3
  61. package/docs/design/README.md +124 -42
  62. package/docs/development-principles.md +4 -4
  63. package/docs/exec-plans/active/README.md +12 -11
  64. package/docs/exec-plans/completed/README.md +33 -0
  65. package/docs/feature-workflow.md +114 -39
  66. package/docs/init-surface.manifest.json +30 -3
  67. package/docs/loop-agent-harness.md +9 -8
  68. package/docs/production-readiness.md +1 -1
  69. package/docs/progress/README.md +23 -1
  70. package/docs/reports/README.md +65 -6
  71. package/docs/skills/vetted-skill-registry.md +2 -0
  72. package/docs/templates/agent-dag.schema.json +29 -1
  73. package/docs/templates/agent-dag.supervised-implementation.json +127 -8
  74. package/docs/templates/backend-test-analysis.schema.json +44 -0
  75. package/docs/templates/backend-test-case-manifest.schema.json +190 -0
  76. package/docs/templates/backend-test-dag.classify.prompt.md +75 -0
  77. package/docs/templates/backend-test-dag.generate-pytest.prompt.md +81 -16
  78. package/docs/templates/backend-test-dag.json +311 -40
  79. package/docs/templates/backend-test-dag.retrospect.prompt.md +44 -30
  80. package/docs/templates/backend-test-dag.review-cases.prompt.md +10 -8
  81. package/docs/templates/backend-test-execution.schema.json +133 -0
  82. package/docs/templates/backend-test-result.schema.json +99 -0
  83. package/docs/templates/branch-merge-report.md +93 -0
  84. package/docs/templates/frontend-design-contract.md +9 -0
  85. package/docs/templates/frontend-eval/fixtures/failures/01-type-build-error.md +17 -0
  86. package/docs/templates/frontend-eval/fixtures/failures/02-unit-component-test-fail.md +16 -0
  87. package/docs/templates/frontend-eval/fixtures/failures/03-fixture-schema-drift.md +16 -0
  88. package/docs/templates/frontend-eval/fixtures/failures/04-missing-loading-empty-error-state.md +16 -0
  89. package/docs/templates/frontend-eval/fixtures/failures/05-forbidden-write-writeset-expansion.md +16 -0
  90. package/docs/templates/frontend-eval/fixtures/failures/06-unapproved-dependency-add.md +16 -0
  91. package/docs/templates/frontend-eval/fixtures/failures/07-mock-production-on.md +21 -0
  92. package/docs/templates/frontend-eval/fixtures/functional/01-simple-component-style.md +29 -0
  93. package/docs/templates/frontend-eval/fixtures/functional/02-form-validation.md +28 -0
  94. package/docs/templates/frontend-eval/fixtures/functional/03-list-detail-page.md +28 -0
  95. package/docs/templates/frontend-eval/fixtures/functional/04-api-mock.md +29 -0
  96. package/docs/templates/frontend-eval/fixtures/functional/05-permission-auth-gated-ui.md +27 -0
  97. package/docs/templates/frontend-eval/fixtures/functional/06-ssr-server-client-boundary.md +28 -0
  98. package/docs/templates/frontend-eval/fixtures/functional/07-shared-public-component-api.md +28 -0
  99. package/docs/templates/frontend-eval/fixtures/functional/08-pure-local-no-remote.md +27 -0
  100. package/docs/templates/frontend-eval/metrics.md +138 -0
  101. package/docs/templates/frontend-eval/smoke-targets.md +53 -0
  102. package/docs/templates/frontend-implementation-contract.schema.json +27 -0
  103. package/docs/templates/frontend-task-constraints.md +10 -0
  104. package/docs/templates/frontend-task-requirement.md +9 -0
  105. package/docs/templates/frontend-test-dag.generate-cases.prompt.md +5 -0
  106. package/docs/templates/frontend-test-dag.json +23 -0
  107. package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +3 -0
  108. package/docs/templates/frontend-test-dag.retrospect.prompt.md +3 -0
  109. package/docs/templates/frontend-test-dag.review-cases.prompt.md +3 -0
  110. package/docs/templates/frontend-test-dag.review-execution.prompt.md +3 -0
  111. package/docs/templates/knowledge-graph-bootstrap-dag.json +1 -1
  112. package/docs/templates/knowledge-sync-dag.json +1 -0
  113. package/docs/verification-matrix.md +4 -1
  114. package/examples/decision-gate-agent-dag.json +4 -4
  115. package/examples/hybrid-loop-agent-dag.json +1 -1
  116. package/package.json +2 -2
  117. package/scripts/kb-bootstrap-init-skeleton.sh +2 -1
  118. package/scripts/kb-graph-incremental-prepare.mjs +19 -5
  119. package/scripts/kb-graph-promote.mjs +12 -1
  120. package/skills/ai-engineering-context/SKILL.md +2 -2
  121. package/skills/analyze-product-dependencies/SKILL.md +67 -0
  122. package/skills/analyze-product-dependencies/agents/openai.yaml +4 -0
  123. package/skills/analyze-product-dependencies/references/api-documentation-schema.md +30 -0
  124. package/skills/analyze-product-dependencies/references/dependency-analysis-schema.md +28 -0
  125. package/skills/analyze-product-dependencies/references/example.md +76 -0
  126. package/skills/analyze-product-dependencies/references/forward-test-cases.md +35 -0
  127. package/skills/analyze-product-dependencies/references/input-contract.md +11 -0
  128. package/skills/analyze-product-dependencies/references/scouting-rules.md +61 -0
  129. package/skills/analyze-product-dependencies/scripts/test-validators.mjs +267 -0
  130. package/skills/analyze-product-dependencies/scripts/validate-api-documentation.mjs +101 -0
  131. package/skills/analyze-product-dependencies/scripts/validate-dependency-analysis.mjs +142 -0
  132. package/skills/analyze-product-dependencies/scripts/validate-product-requirement-input.mjs +76 -0
  133. package/skills/analyze-product-dependencies/scripts/validation-helpers.mjs +146 -0
  134. package/skills/analyze-product-requirements/SKILL.md +90 -0
  135. package/skills/analyze-product-requirements/agents/openai.yaml +4 -0
  136. package/skills/analyze-product-requirements/references/acceptance-criteria.md +91 -0
  137. package/skills/analyze-product-requirements/references/clarification-and-knowledge.md +56 -0
  138. package/skills/analyze-product-requirements/references/example.md +86 -0
  139. package/skills/analyze-product-requirements/references/forward-test-cases.md +66 -0
  140. package/skills/analyze-product-requirements/references/product-analysis-schema.md +32 -0
  141. package/skills/analyze-product-requirements/references/product-requirement-schema.md +33 -0
  142. package/skills/analyze-product-requirements/references/requirement-clarification-schema.md +35 -0
  143. package/skills/analyze-product-requirements/scripts/test-validators.mjs +193 -0
  144. package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +69 -0
  145. package/skills/analyze-product-requirements/scripts/validate-product-requirement.mjs +97 -0
  146. package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +98 -0
  147. package/skills/analyze-product-requirements/scripts/validation-helpers.mjs +156 -0
  148. package/skills/browser-tools/SKILL.md +196 -0
  149. package/skills/browser-tools/browser-content.js +103 -0
  150. package/skills/browser-tools/browser-cookies.js +35 -0
  151. package/skills/browser-tools/browser-eval.js +53 -0
  152. package/skills/browser-tools/browser-hn-scraper.js +108 -0
  153. package/skills/browser-tools/browser-nav.js +44 -0
  154. package/skills/browser-tools/browser-pick.js +162 -0
  155. package/skills/browser-tools/browser-screenshot.js +34 -0
  156. package/skills/browser-tools/browser-start.js +86 -0
  157. package/skills/browser-tools/package-lock.json +2556 -0
  158. package/skills/browser-tools/package.json +19 -0
  159. package/skills/frontend-design-review/SKILL.md +6 -1
  160. package/skills/frontend-design-review/references/review-checklist.md +25 -4
  161. package/skills/frontend-implementation/SKILL.md +25 -30
  162. package/skills/frontend-implementation/references/code-standards.md +20 -22
  163. package/skills/frontend-implementation/references/node-contracts.md +17 -53
  164. package/skills/frontend-review/SKILL.md +10 -4
  165. package/skills/frontend-review/references/review-findings.md +8 -3
  166. package/skills/frontend-verification/SKILL.md +22 -9
  167. package/skills/frontend-verification/references/verification-checklist.md +17 -5
  168. package/skills/grill-with-docs/SKILL.md +5 -5
  169. package/skills/grill-with-docs/adr-format.md +3 -3
  170. package/skills/init-capability-evolution/SKILL.md +5 -5
  171. package/skills/loop-agent/SKILL.md +5 -5
  172. package/skills/loop-agent/references/README.md +3 -3
  173. package/skills/loop-agent/references/command-reference.md +98 -24
  174. package/skills/loop-agent/references/docs-converge.md +15 -15
  175. package/skills/loop-agent/references/harness-policy.md +2 -2
  176. package/skills/loop-agent/references/hybrid-dag.md +32 -22
  177. package/skills/loop-agent/references/multi-worktree.md +1 -1
  178. package/skills/loop-agent/references/orchestrator-and-interventions.md +8 -8
  179. package/skills/loop-agent/references/task-workflow.md +1 -1
  180. package/skills/loop-agent/references/verification-and-failure-handling.md +6 -4
  181. package/skills/playwright-cli/SKILL.md +420 -0
  182. package/skills/playwright-cli/references/element-attributes.md +23 -0
  183. package/skills/playwright-cli/references/playwright-tests.md +39 -0
  184. package/skills/playwright-cli/references/request-mocking.md +87 -0
  185. package/skills/playwright-cli/references/running-code.md +241 -0
  186. package/skills/playwright-cli/references/session-management.md +225 -0
  187. package/skills/playwright-cli/references/storage-state.md +275 -0
  188. package/skills/playwright-cli/references/test-generation.md +433 -0
  189. package/skills/playwright-cli/references/tracing.md +139 -0
  190. package/skills/playwright-cli/references/video-recording.md +143 -0
  191. package/skills/playwright-cli-case-generator/SKILL.md +74 -0
  192. package/skills/requesting-code-review/SKILL.md +1 -1
  193. package/skills/systematic-debugging/CREATION-LOG.md +3 -3
  194. package/skills/systematic-debugging/SKILL.md +1 -1
  195. package/skills/systematic-debugging/test-academic.md +1 -1
  196. package/skills/systematic-debugging/test-pressure-1.md +1 -1
  197. package/skills/systematic-debugging/test-pressure-2.md +1 -1
  198. package/skills/systematic-debugging/test-pressure-3.md +1 -1
  199. package/skills/verification-before-completion/SKILL.md +1 -1
@@ -7,14 +7,26 @@
7
7
  "agentRuntime": "pi-only",
8
8
  "repairWriterProtocol": "explicit-node-v1"
9
9
  },
10
- "objective": "End-to-end backend functional testing pipeline: analyze requirements → generate functional test cases → review cases → generate pytest automation → execute pytest → retrospective with maturity rating. Covers the full chain from requirement analysis to test maturity assessment.",
10
+ "objective": "End-to-end backend functional testing pipeline: analyze requirements as Backend Test Analysis v1 JSON validate analysis contract → environment scout + execution contract → generate functional test cases → emit+validate Case Manifest v1 → review cases → generate pytest automation → traceability gate → preflight + execute pytest → parse Result v1 → classify failures → retrospective outcome gate. Covers the full chain from requirement analysis to test maturity assessment with always-run retrospect on assertion failures and deterministic AC→case→symbol traceability.",
11
11
  "successCriteria": [
12
- "analyze-inputs-pi returns a read-only test analysis contract covering scope, risks, and strategy",
13
- "generate-backend-functional-cases-pi produces structured test cases with BE-<MODULE>-<NNN> IDs under testcase/md/",
12
+ "analyze-inputs-pi returns pure Backend Test Analysis v1 JSON (schema docs/templates/backend-test-analysis.schema.json) with no Markdown prose",
13
+ "backend-test-analysis-contract-shell validates schemaId backend-test-analysis-v1 and materializes run-owned contracts/backend-test-analysis.json",
14
+ "backend-test-environment-scout-pi returns pure Backend Test Execution Contract v1 JSON (schema docs/templates/backend-test-execution.schema.json)",
15
+ "backend-test-execution-contract-shell validates schemaId backend-test-execution-v1 and materializes run-owned contracts/backend-test-execution.json",
16
+ "generate-backend-functional-cases-pi consumes validated contracts and produces structured test cases with BE-<MODULE>-<NNN> IDs under testcase/md/",
17
+ "emit-backend-case-manifest-pi returns pure Backend Test Case Manifest v1 JSON (schema docs/templates/backend-test-case-manifest.schema.json) with no Markdown prose",
18
+ "backend-test-case-manifest-shell validates schemaId backend-test-case-manifest-v1 and materializes run-owned contracts/backend-test-case-manifest.json with deterministic AC coverage",
19
+ "backend-test-traceability-gate-shell verifies generated file/symbol existence after pytest generation and before execute",
20
+ "test-retrospect-pi uses Case Manifest coverageSummary + Result v1 only for coverage/pass rate inputs (model must not invent percentages)",
14
21
  "review-backend-cases-pi returns VERDICT: pass or request-revision with coverage assessment",
15
22
  "review-backend-cases-gate-shell blocks pytest generation unless the review verdict is VERDICT: pass",
16
- "generate-backend-pytest-pi converts reviewed cases into pytest code with 1:1 traceability under testcase/",
17
- "execute-backend-pytest-shell runs pytest and produces HTML report under reports/",
23
+ "generate-backend-pytest-pi converts reviewed cases into pytest code using verified analysis + execution contracts under testcase/",
24
+ "execute-backend-pytest-shell runs deterministic preflight against the execution contract, then pytest with frozen Adapter testRoot (default testcase/), writing JUnit XML under the current $HARNESS_DAG_RUN_DIR/reports/** only",
25
+ "execute-backend-pytest-shell maps pytest exit 0/1 to node success only when non-empty JUnit exists; raw pytestExitCode is side-channeled under reports/",
26
+ "parse-backend-test-result-shell materializes run-owned contracts/backend-test-result.json (Backend Test Result v1) from JUnit + pytestExitCode",
27
+ "classify-backend-test-result-pi returns structured failure classification JSON (ProductBug|TestBug|EnvFailure|ContractMismatch|FlakyTest|Unknown) without writing files",
28
+ "test-retrospect-pi runs after parse+classify on both pass and assertion-fail paths; stats come from Result v1 only",
29
+ "backend-test-outcome-gate-shell exits non-zero unless result.outcome=passed (shell facts only; retrospective cannot override)",
18
30
  "test-retrospect-pi generates retrospective report with A/B/C/D maturity rating under docs/test-reports/",
19
31
  "Full traceability from acceptance criteria → functional test case ID → pytest function name"
20
32
  ],
@@ -26,29 +38,20 @@
26
38
  "Exclusive writer nodes must stay within declared writeSet.",
27
39
  "Every task must explicitly declare executor; defaults.executor is schema-only and not a runtime fallback.",
28
40
  "Functional test case IDs must use BE-<MODULE>-<NNN> format.",
29
- "pytest execution must produce HTML reports under reports/.",
41
+ "pytest execution must keep the target worktree read-only and write machine-readable results only under the current HARNESS_DAG_RUN_DIR/reports/** (e.g. JUnit XML).",
30
42
  "Backend-test-dag nodes must maintain traceability from requirements to functional cases to pytest automation.",
31
43
  "pytest automation scripts must use test_ filename prefix for pytest discovery.",
32
- "generate-backend-pytest-pi must only create new test files under testcase/; modifying existing framework files (conftest.py, pytest.ini, pyproject.toml) is forbidden.",
44
+ "generate-backend-pytest-pi may create only new files under testcase/**/test_*.py, testcase/**/helpers/**, and testcase/**/factories/**; modifying conftest.py, pytest.ini, pyproject.toml, or production code is forbidden.",
33
45
  "review-backend-cases-gate-shell must block pytest generation unless the review verdict is exactly VERDICT: pass.",
34
46
  "If a target test filename already exists under testcase/, add a numeric suffix (_01, _02, ...); never overwrite or append to existing files.",
35
47
  "execute-backend-pytest-shell must not modify test assertions or production code to make tests pass; test failures indicate potential implementation issues and must be reported honestly.",
36
48
  "Prompt templates must not ask main session to write artifacts; node output is the artifact and runner archives it under .harness/dag-runs/.",
37
49
  "Actual file operation paths must be macOS/Windows compatible; use / only for stable repo refs, JSON/Markdown evidence refs, and glob conventions.",
38
- "Same-rank exclusive writeSet entries must be disjoint."
50
+ "Same-rank exclusive writeSet entries must be disjoint.",
51
+ "parse-backend-test-result-shell materializes Backend Test Result v1 from JUnit + pytestExitCode; classify/retrospect run on pass and assertion-fail; backend-test-outcome-gate-shell uses result.outcome only.",
52
+ "backend-test-case-manifest-shell validates schemaId backend-test-case-manifest-v1 and materializes contracts/backend-test-case-manifest.json; AC coverage is fail-closed and deterministic.",
53
+ "backend-test-traceability-gate-shell verifies generated file/symbol existence after pytest generation and before execute; models must not invent coverage percentages."
39
54
  ],
40
- "convergence": {
41
- "enabled": true,
42
- "maxPasses": 3,
43
- "stopOnVerdictPass": true,
44
- "stopOnHardVerifyPass": true,
45
- "pauseOnRegression": true,
46
- "chainNodeIds": [
47
- "generate-backend-functional-cases-pi",
48
- "review-backend-cases-pi",
49
- "review-backend-cases-gate-shell"
50
- ]
51
- },
52
55
  "defaults": {
53
56
  "executor": "pi",
54
57
  "contextProfile": "slim",
@@ -101,7 +104,7 @@
101
104
  ".harness/**",
102
105
  "artifacts/**"
103
106
  ],
104
- "outputContract": "Structured Markdown extracting core content from source documents. No file writes.",
107
+ "outputContract": "Pure Backend Test Analysis v1 JSON object matching docs/templates/backend-test-analysis.schema.json. No Markdown prose and no file writes.",
105
108
  "retryPolicy": {
106
109
  "maxAttempts": 3,
107
110
  "backoff": "exponential",
@@ -114,18 +117,109 @@
114
117
  "unavailable"
115
118
  ]
116
119
  },
117
- "subtask_prompt": "Read the task source materials and extract the following structured content for downstream test generation.\n\n## Required Output Sections:\n\n### 1. API Endpoints\nList all API endpoints: Method, Path, Description, Request params, Response format.\n\n### 2. Data Model\nFor each table/collection: fields, types, constraints, descriptions.\n\n### 3. Business Logic\nCore business rules, validation rules, calculation formulas.\n\n### 4. State Transitions\nState machines (e.g. order status: pending paid → shipped → completed).\n\n### 5. Error Scenarios & Error Codes\nAll error codes, error messages, and when they occur.\n\n### 6. External Dependencies\nThird-party services, databases, message queues. Include timeout settings if documented.\n\n### 7. Acceptance Criteria\nExtract ALL acceptance criteria from 需求.md. Number them AC-001, AC-002, etc. If not explicitly listed, derive from functional requirements.\n\n### 8. Risk Areas\nHigh-risk areas requiring extra test coverage.\n\n## Conditional Sections (include ONLY if mentioned in requirements):\n- Authentication & Authorization: include ONLY if requirements mention auth mechanism (JWT, OAuth2, API Key, etc.)\n- Timeout Handling: include ONLY if requirements mention timeout configuration or degradation strategy\n- Concurrency & Idempotency: include ONLY if requirements mention concurrency, idempotency rules, or locking mechanisms\n- State Transitions: include ONLY if requirements mention business state machines\n- If not mentioned in requirements, do NOT include these sections\n\nThis output will be used directly by downstream nodes. Be thorough and structured.\nRead-only: do not modify code, docs, artifacts, or repository files."
120
+ "subtask_prompt": "Read the task source materials and return exactly one JSON object matching Backend Test Analysis v1.\n\nDo not wrap it in explanatory prose. A single fenced json block is tolerated, but pure JSON is preferred.\n\nCopy taskId, requirementPath, requirementSha256, referencePaths, and requirementIds exactly from the DAG source binding shown below.\n\nPreserve existing AC IDs. Do not invent endpoint methods, paths, fields, errors, boundaries, or business rules; record unknowns in evidenceGaps.\n\nUse empty arrays for categories not documented. Never include credentials, tokens, private keys, or secret values.\n\nRequired top-level keys: schemaVersion, sourceBinding, acceptanceCriteria, endpoints, dataModels, businessRules, stateTransitions, boundaryConstraints, externalDependencies, risks, evidenceGaps.\n\nRead-only: do not modify code, docs, artifacts, or repository files."
118
121
  },
119
122
  {
120
- "id": "generate-backend-functional-cases-pi",
123
+ "id": "backend-test-analysis-contract-shell",
121
124
  "depends_on": [
122
125
  "analyze-inputs-pi"
123
126
  ],
127
+ "complexity": "LOW",
128
+ "executor": "shell",
129
+ "role": "verifier",
130
+ "writePolicy": "read-only",
131
+ "allowedPaths": [
132
+ "REPLACE/WITH/SOURCE/PATH/**"
133
+ ],
134
+ "forbiddenPaths": [
135
+ ".harness/**",
136
+ "artifacts/**"
137
+ ],
138
+ "outputContract": "Validated run-owned Backend Test Analysis v1 artifact pointer, schema ID, and SHA-256.",
139
+ "subtask_prompt": "Materialize and validate the backend-test analysis contract under the current DAG run.",
140
+ "shell": {
141
+ "commands": [],
142
+ "jsonArtifactGate": {
143
+ "fromNodeId": "analyze-inputs-pi",
144
+ "schemaId": "backend-test-analysis-v1",
145
+ "artifactName": "backend-test-analysis.json",
146
+ "outputDir": "contracts"
147
+ },
148
+ "cwd": ".",
149
+ "timeoutMs": 60000
150
+ }
151
+ },
152
+ {
153
+ "id": "backend-test-environment-scout-pi",
154
+ "depends_on": [
155
+ "backend-test-analysis-contract-shell"
156
+ ],
157
+ "complexity": "MED",
158
+ "executor": "pi",
159
+ "role": "scout",
160
+ "writePolicy": "read-only",
161
+ "allowedPaths": [
162
+ "REPLACE/WITH/SOURCE/PATH/**"
163
+ ],
164
+ "forbiddenPaths": [
165
+ ".harness/**",
166
+ "artifacts/**"
167
+ ],
168
+ "outputContract": "Pure Backend Test Execution Contract v1 JSON object matching docs/templates/backend-test-execution.schema.json. No Markdown prose and no file writes.",
169
+ "retryPolicy": {
170
+ "maxAttempts": 3,
171
+ "backoff": "exponential",
172
+ "initialDelayMs": 2000,
173
+ "maxDelayMs": 30000,
174
+ "retryCategories": [
175
+ "timeout",
176
+ "network",
177
+ "rate-limit",
178
+ "unavailable"
179
+ ]
180
+ },
181
+ "subtask_prompt": "Read-only environment scout for backend-test pytest MVP.\n\nReturn exactly one JSON object matching Backend Test Execution Contract v1 (schema docs/templates/backend-test-execution.schema.json).\n\nPrefer pure JSON; a single fenced json block is tolerated; no trailing prose.\n\nDiscover only non-secret evidence: pytest config files (pytest.ini / pyproject.toml / setup.cfg test paths), candidate test roots, existing fixtures/clients, documented run commands, and env *names* (not values).\n\nDo NOT search the whole repo for secrets, .env values, tokens, private keys, or production credentials.\n\nframework must be \"pytest\". Default targetMode to \"in-process\" unless evidence clearly shows an external service base URL env name or documented managed start/stop with sourceRef.\n\nDo NOT select targetMode \"managed-command\" unless task source documents a safe start/stop command with an explicit sourceRef; otherwise leave managedCommand absent and record the gap in evidenceGaps.\n\ntestRoot and workingDirectory must be repo-relative posix paths without .. or absolute form. Adapter default testRoot is testcase when evidence is incomplete.\n\nrunner must not include secret values. report.format must be junit with a relativeHint under the run (e.g. reports/backend-test-junit.xml).\n\nrequiredEnvNames lists env NAMES only. baseUrlEnvName is required only for external-running-service and must match ^[A-Z_][A-Z0-9_]*$.\n\nRecord incomplete discovery in evidenceGaps. Populate evidenceRefs with repo-relative paths actually read.\n\nRequired top-level keys: schemaVersion, framework, runner, testRoot, workingDirectory, report, targetMode, existingFixtures, authenticationMode, requiredEnvNames, dataIsolation, evidenceGaps, evidenceRefs.\n\nRead-only: do not modify code, docs, artifacts, or repository files."
182
+ },
183
+ {
184
+ "id": "backend-test-execution-contract-shell",
185
+ "depends_on": [
186
+ "backend-test-environment-scout-pi"
187
+ ],
188
+ "complexity": "LOW",
189
+ "executor": "shell",
190
+ "role": "verifier",
191
+ "writePolicy": "read-only",
192
+ "allowedPaths": [
193
+ "REPLACE/WITH/SOURCE/PATH/**"
194
+ ],
195
+ "forbiddenPaths": [
196
+ ".harness/**",
197
+ "artifacts/**"
198
+ ],
199
+ "outputContract": "Validated run-owned Backend Test Execution Contract v1 artifact pointer, schema ID, and SHA-256.",
200
+ "subtask_prompt": "Materialize and validate the backend-test execution contract under the current DAG run.",
201
+ "shell": {
202
+ "commands": [],
203
+ "jsonArtifactGate": {
204
+ "fromNodeId": "backend-test-environment-scout-pi",
205
+ "schemaId": "backend-test-execution-v1",
206
+ "artifactName": "backend-test-execution.json",
207
+ "outputDir": "contracts"
208
+ },
209
+ "cwd": ".",
210
+ "timeoutMs": 60000
211
+ }
212
+ },
213
+ {
214
+ "id": "generate-backend-functional-cases-pi",
215
+ "depends_on": [
216
+ "backend-test-execution-contract-shell"
217
+ ],
124
218
  "complexity": "MED",
125
219
  "executor": "pi",
126
220
  "role": "implementer",
127
- "toolProfile": "write",
128
221
  "writePolicy": "exclusive",
222
+ "toolProfile": "write",
129
223
  "writeSet": [
130
224
  "testcase/md/**"
131
225
  ],
@@ -136,13 +230,75 @@
136
230
  ".harness/**",
137
231
  "artifacts/**"
138
232
  ],
139
- "outputContract": "Structured backend functional test cases in Markdown under testcase/md/. Each case uses BE-<MODULE>-<NNN> ID format.",
140
- "subtask_prompt": "Based on the upstream analyze-inputs-pi output, generate structured backend functional test cases.\n\n## Output Steps (do in order):\n1. First, output a brief summary: how many modules, how many cases planned per module\n2. Then write each test case file under testcase/md/\n\n## Format Rules:\n- Each test case ID: BE-<MODULE>-<NNN> (e.g. BE-ORDER-001)\n- Each file covers one module\n- Case structure: ID, Title, Precondition, Steps, Expected Result\n- Map each case to acceptance criteria (AC-xxx)\n\n## Coverage Requirements:\n- Positive paths: happy path for each acceptance criterion\n- Negative paths: error scenarios (invalid input, not found, state violations)\n- Boundary conditions: empty input, max length, edge values\n\n## Conditional Coverage (include ONLY if mentioned in upstream analysis):\n- State transitions: include ONLY if upstream analyze-inputs-pi mentions state machine\n- Authentication scenarios: include ONLY if upstream analyze-inputs-pi mentions auth mechanism\n- Timeout scenarios: include ONLY if upstream analyze-inputs-pi mentions timeout handling\n- Concurrency scenarios: include ONLY if upstream analyze-inputs-pi mentions concurrency/idempotency rules\n- If not mentioned, do NOT generate these test cases\n\n## Constraints:\n- Stay within writeSet: testcase/md/**\n- Do NOT re-read source documents — use the upstream analyze-inputs-pi output only\n- Do not write root artifacts/**"
233
+ "subtask_prompt": "Read the validated structured artifact pointer from backend-test-analysis-contract-shell and generate cases only from that JSON contract.\n\n\n\n\n\n## Output Steps (do in order):\n\n1. First, output a brief summary: how many modules, how many cases planned per module\n\n2. Then write each test case file under testcase/md/\n\n\n\n## Format Rules:\n\n- Each test case ID: BE-<MODULE>-<NNN> (e.g. BE-ORDER-001)\n\n- Each file covers one module\n\n- Case structure: ID, Title, Precondition, Steps, Expected Result\n\n- Map each case to acceptance criteria (AC-xxx)\n\n\n\n## Coverage Requirements:\n\n- Positive paths: happy path for each acceptance criterion\n\n- Negative paths: error scenarios (invalid input, not found, state violations)\n\n\n\n## Conditional Coverage (include ONLY if mentioned in upstream analysis):\n\n- Boundary conditions: include ONLY if upstream analyze-inputs-pi mentions value ranges, length limits, numeric bounds, or format constraints\n\n- State transitions: include ONLY if upstream analyze-inputs-pi mentions state machine\n\n- Authentication scenarios: include ONLY if upstream analyze-inputs-pi mentions auth mechanism\n\n- Timeout scenarios: include ONLY if upstream analyze-inputs-pi mentions timeout handling\n\n- Concurrency scenarios: include ONLY if upstream analyze-inputs-pi mentions concurrency/idempotency rules\n\n- If not mentioned, do NOT generate these test cases\n\n\n\n## Constraints:\n\n- Stay within writeSet: testcase/md/**\n\n- Do NOT re-read source documents or fall back to free-form analysis — use the validated structured artifact only\n\n\n\n- Do not write root artifacts/**"
234
+ },
235
+ {
236
+ "id": "emit-backend-case-manifest-pi",
237
+ "depends_on": [
238
+ "generate-backend-functional-cases-pi",
239
+ "backend-test-analysis-contract-shell"
240
+ ],
241
+ "complexity": "MED",
242
+ "executor": "pi",
243
+ "role": "scout",
244
+ "writePolicy": "read-only",
245
+ "allowedPaths": [
246
+ "**"
247
+ ],
248
+ "forbiddenPaths": [
249
+ ".harness/**",
250
+ "artifacts/**"
251
+ ],
252
+ "outputContract": "Pure Backend Test Case Manifest v1 JSON (schema docs/templates/backend-test-case-manifest.schema.json). No file writes; model must not write .harness/**.",
253
+ "retryPolicy": {
254
+ "maxAttempts": 3,
255
+ "backoff": "exponential",
256
+ "initialDelayMs": 2000,
257
+ "maxDelayMs": 30000,
258
+ "retryCategories": [
259
+ "timeout",
260
+ "network",
261
+ "rate-limit",
262
+ "unavailable"
263
+ ]
264
+ },
265
+ "subtask_prompt": "Emit Backend Test Case Manifest v1 as pure JSON (or one fenced json block with no trailing text).\n\nRead-only: use validated contracts/backend-test-analysis.json pointer + testcase/md/** only. Do not write repository files or .harness/**.\n\nsourceBinding must match the analysis contract / DAG source binding exactly (taskId, requirementPath, requirementSha256, referencePaths, requirementIds).\n\nFor each functional case under testcase/md/: caseId BE-<MODULE>-<NNN>, acIds[], title, category, automationStatus.\n\nAfter case generation (pre-pytest), default automationStatus=planned. Use skipped/unsupported only with gapReason. Use generated only when file+symbol already exist.\n\nevidenceGaps: structured gaps for explicit AC-* that cannot be mapped to a case.\n\nDo NOT invent coverage percentages. Optional coverageSummary must match deterministic counts (gate recomputes/validates).\n\nNo secrets or credential-shaped fields."
266
+ },
267
+ {
268
+ "id": "backend-test-case-manifest-shell",
269
+ "depends_on": [
270
+ "emit-backend-case-manifest-pi"
271
+ ],
272
+ "complexity": "LOW",
273
+ "executor": "shell",
274
+ "role": "verifier",
275
+ "writePolicy": "read-only",
276
+ "allowedPaths": [
277
+ "**"
278
+ ],
279
+ "forbiddenPaths": [
280
+ ".harness/**",
281
+ "artifacts/**"
282
+ ],
283
+ "outputContract": "Validated run-owned Backend Test Case Manifest v1 at contracts/backend-test-case-manifest.json (schemaId backend-test-case-manifest-v1) with deterministic AC coverage.",
284
+ "subtask_prompt": "Materialize and validate Backend Test Case Manifest v1; fail closed on duplicate IDs, unknown AC, missing AC coverage without gap, or skipped without gapReason.",
285
+ "shell": {
286
+ "commands": [],
287
+ "jsonArtifactGate": {
288
+ "fromNodeId": "emit-backend-case-manifest-pi",
289
+ "schemaId": "backend-test-case-manifest-v1",
290
+ "artifactName": "backend-test-case-manifest.json",
291
+ "outputDir": "contracts"
292
+ },
293
+ "cwd": ".",
294
+ "timeoutMs": 60000
295
+ }
141
296
  },
142
297
  {
143
298
  "id": "review-backend-cases-pi",
144
299
  "depends_on": [
145
- "generate-backend-functional-cases-pi"
300
+ "backend-test-case-manifest-shell",
301
+ "backend-test-analysis-contract-shell"
146
302
  ],
147
303
  "complexity": "HIGH",
148
304
  "executor": "pi",
@@ -205,30 +361,61 @@
205
361
  {
206
362
  "id": "generate-backend-pytest-pi",
207
363
  "depends_on": [
208
- "review-backend-cases-gate-shell"
364
+ "review-backend-cases-gate-shell",
365
+ "backend-test-execution-contract-shell"
209
366
  ],
210
367
  "complexity": "HIGH",
211
368
  "executor": "pi",
212
369
  "role": "implementer",
213
- "toolProfile": "write",
214
370
  "writePolicy": "exclusive",
371
+ "toolProfile": "write",
215
372
  "writeSet": [
216
- "testcase/**/test_*.py"
373
+ "testcase/**/test_*.py",
374
+ "testcase/**/helpers/**",
375
+ "testcase/**/factories/**"
217
376
  ],
218
377
  "allowedPaths": [
219
- "testcase/**/test_*.py"
378
+ "testcase/**",
379
+ "**"
220
380
  ],
221
381
  "forbiddenPaths": [
222
382
  ".harness/**",
223
383
  "artifacts/**"
224
384
  ],
225
- "outputContract": "Pytest test files under testcase/ with 1:1 mapping to functional test case IDs. Summary lists generated files, test function count, and any skipped cases with reasons.",
226
385
  "subtask_prompt_markdown": "./backend-test-dag.generate-pytest.prompt.md"
227
386
  },
387
+ {
388
+ "id": "backend-test-traceability-gate-shell",
389
+ "depends_on": [
390
+ "generate-backend-pytest-pi",
391
+ "backend-test-case-manifest-shell"
392
+ ],
393
+ "complexity": "LOW",
394
+ "executor": "shell",
395
+ "role": "verifier",
396
+ "writePolicy": "read-only",
397
+ "allowedPaths": [
398
+ "**"
399
+ ],
400
+ "forbiddenPaths": [
401
+ ".harness/**",
402
+ "artifacts/**"
403
+ ],
404
+ "outputContract": "Deterministic traceability: generated cases have real file/symbol; skipped/unsupported have gapReason; convention symbols scanned under testcase/**/test_*.py.",
405
+ "subtask_prompt": "Fail closed when generated automation claims do not resolve to workspace pytest symbols, or skip/unsupported lacks gapReason.",
406
+ "shell": {
407
+ "commands": [
408
+ "backend-test-traceability-gate"
409
+ ],
410
+ "cwd": ".",
411
+ "timeoutMs": 60000
412
+ }
413
+ },
228
414
  {
229
415
  "id": "execute-backend-pytest-shell",
230
416
  "depends_on": [
231
- "generate-backend-pytest-pi"
417
+ "backend-test-traceability-gate-shell",
418
+ "backend-test-execution-contract-shell"
232
419
  ],
233
420
  "complexity": "LOW",
234
421
  "executor": "shell",
@@ -241,11 +428,11 @@
241
428
  ".harness/**",
242
429
  "artifacts/**"
243
430
  ],
244
- "outputContract": "Archived pytest stdout/stderr with exit codes and HTML report path; no source or test file modifications.",
245
- "subtask_prompt": "Run pytest for the backend test suite and capture results.",
431
+ "outputContract": "Archived pytest stdout/stderr; raw pytestExitCode side-channel + JUnit at $HARNESS_DAG_RUN_DIR/reports/**. Exit 0/1 with non-empty JUnit finishes the node so parse/classify/retrospect can run; assertion failures remain recorded in exit file.",
432
+ "subtask_prompt": "Run pytest for the backend test suite; write JUnit + pytestExitCode evidence only under the current HARNESS_DAG_RUN_DIR/reports/.",
246
433
  "shell": {
247
434
  "commands": [
248
- "python -m pytest testcase/ --html=reports/backend-test-report.html -v"
435
+ "test -n \"${HARNESS_DAG_RUN_DIR:-}\" || { echo \"missing HARNESS_DAG_RUN_DIR for backend pytest preflight\" >&2; exit 2; }; CONTRACT=\"${HARNESS_DAG_RUN_DIR}/contracts/backend-test-execution.json\"; test -f \"${CONTRACT}\" || { echo \"missing backend-test execution contract: ${CONTRACT}\" >&2; exit 2; }; node -e 'const fs=require(\"fs\");const path=require(\"path\");const contractPath=process.argv[1];const contract=JSON.parse(fs.readFileSync(contractPath,\"utf8\"));const expected=\"testcase\";const errors=[];if(contract.framework!==\"pytest\") errors.push(\"framework must be pytest\");const testRoot=String(contract.testRoot||\"\");if(!testRoot||testRoot.includes(\"..\")||path.isAbsolute(testRoot)) errors.push(\"unsafe testRoot\");if(testRoot.replace(/\\/+$/,\"\")!==expected.replace(/\\/+$/,\"\")) errors.push(\"testRoot mismatch vs frozen command: \"+testRoot+\" !== \"+expected);const rootAbs=path.resolve(process.cwd(),testRoot);if(!fs.existsSync(rootAbs)) errors.push(\"testRoot does not exist: \"+testRoot);if(Array.isArray(contract.evidenceGaps)&&contract.evidenceGaps.length) errors.push(\"evidenceGaps present: \"+contract.evidenceGaps.length);if(contract.targetMode===\"in-process\"&&!(Array.isArray(contract.existingFixtures)&&contract.existingFixtures.length)) errors.push(\"in-process requires existingFixtures\");for (const name of (contract.requiredEnvNames||[])) { if(!process.env[name]) errors.push(\"required env missing: \"+name); }if(contract.targetMode===\"external-running-service\"){ const n=contract.baseUrlEnvName; if(!n||!process.env[n]) errors.push(\"external base URL env missing: \"+String(n||\"<empty>\")); }if(contract.targetMode===\"managed-command\" && !(contract.managedCommand&&contract.managedCommand.sourceRef)) errors.push(\"managed-command requires sourceRef evidence\");if(errors.length){ console.error(errors.join(\"; \")); process.exit(2);} console.log(\"backend-test preflight ok: framework=pytest testRoot=\"+testRoot+\" targetMode=\"+contract.targetMode);' \"${CONTRACT}\"; REPORT=\"${HARNESS_DAG_RUN_DIR}/reports/backend-test-junit.xml\"; EXIT_FILE=\"${HARNESS_DAG_RUN_DIR}/reports/backend-test-pytest-exit.txt\"; mkdir -p \"$(dirname \"${REPORT}\")\"; PYTHONDONTWRITEBYTECODE=1 python -m pytest testcase/ -v -p no:cacheprovider --junitxml=\"${REPORT}\"; STATUS=$?; printf \"%s\" \"${STATUS}\" > \"${EXIT_FILE}\"; printf \"JUnit report: %s\\n\" \"${REPORT}\"; printf \"pytestExitCode=%s\\n\" \"${STATUS}\"; if { [ \"${STATUS}\" -eq 0 ] || [ \"${STATUS}\" -eq 1 ]; } && [ -s \"${REPORT}\" ]; then exit 0; fi; exit \"${STATUS}\""
249
436
  ],
250
437
  "verifyEvidence": {
251
438
  "phase": "final",
@@ -253,7 +440,7 @@
253
440
  "commandSource": "inline",
254
441
  "commandCount": 1,
255
442
  "commandLabels": [
256
- "backend pytest execution"
443
+ "test -n \"${HARNESS_DAG_RUN_DIR:-}\" || { echo \"missing HARNESS_DAG_RUN_DIR for backend pytest preflight\" >&2; exit 2; }; CONTRACT=\"${HARNESS_DAG_RUN_DIR}/contracts/backend-test-execution.json\"; test -f \"${CONTRACT}\" || { echo \"missing backend-test execution contract: ${CONTRACT}\" >&2; exit 2; }; node -e 'const fs=require(\"fs\");const path=require(\"path\");const contractPath=process.argv[1];const contract=JSON.parse(fs.readFileSync(contractPath,\"utf8\"));const expected=\"testcase\";const errors=[];if(contract.framework!==\"pytest\") errors.push(\"framework must be pytest\");const testRoot=String(contract.testRoot||\"\");if(!testRoot||testRoot.includes(\"..\")||path.isAbsolute(testRoot)) errors.push(\"unsafe testRoot\");if(testRoot.replace(/\\/+$/,\"\")!==expected.replace(/\\/+$/,\"\")) errors.push(\"testRoot mismatch vs frozen command: \"+testRoot+\" !== \"+expected);const rootAbs=path.resolve(process.cwd(),testRoot);if(!fs.existsSync(rootAbs)) errors.push(\"testRoot does not exist: \"+testRoot);if(Array.isArray(contract.evidenceGaps)&&contract.evidenceGaps.length) errors.push(\"evidenceGaps present: \"+contract.evidenceGaps.length);if(contract.targetMode===\"in-process\"&&!(Array.isArray(contract.existingFixtures)&&contract.existingFixtures.length)) errors.push(\"in-process requires existingFixtures\");for (const name of (contract.requiredEnvNames||[])) { if(!process.env[name]) errors.push(\"required env missing: \"+name); }if(contract.targetMode===\"external-running-service\"){ const n=contract.baseUrlEnvName; if(!n||!process.env[n]) errors.push(\"external base URL env missing: \"+String(n||\"<empty>\")); }if(contract.targetMode===\"managed-command\" && !(contract.managedCommand&&contract.managedCommand.sourceRef)) errors.push(\"managed-command requires sourceRef evidence\");if(errors.length){ console.error(errors.join(\"; \")); process.exit(2);} console.log(\"backend-test preflight ok: framework=pytest testRoot=\"+testRoot+\" targetMode=\"+contract.targetMode);' \"${CONTRACT}\"; REPORT=\"${HARNESS_DAG_RUN_DIR}/reports/backend-test-junit.xml\"; EXIT_FILE=\"${HARNESS_DAG_RUN_DIR}/reports/backend-test-pytest-exit.txt\"; mkdir -p \"$(dirname \"${REPORT}\")\"; PYTHONDONTWRITEBYTECODE=1 python -m pytest testcase/ -v -p no:cacheprovider --junitxml=\"${REPORT}\"; STATUS=$?; printf \"%s\" \"${STATUS}\" > \"${EXIT_FILE}\"; printf \"JUnit report: %s\\n\" \"${REPORT}\"; printf \"pytestExitCode=%s\\n\" \"${STATUS}\"; if { [ \"${STATUS}\" -eq 0 ] || [ \"${STATUS}\" -eq 1 ]; } && [ -s \"${REPORT}\" ]; then exit 0; fi; exit \"${STATUS}\""
257
444
  ],
258
445
  "finalFullRequired": true
259
446
  },
@@ -262,15 +449,64 @@
262
449
  }
263
450
  },
264
451
  {
265
- "id": "test-retrospect-pi",
452
+ "id": "parse-backend-test-result-shell",
266
453
  "depends_on": [
267
454
  "execute-backend-pytest-shell"
268
455
  ],
456
+ "complexity": "LOW",
457
+ "executor": "shell",
458
+ "role": "verifier",
459
+ "writePolicy": "read-only",
460
+ "allowedPaths": [
461
+ "**"
462
+ ],
463
+ "forbiddenPaths": [
464
+ ".harness/**",
465
+ "artifacts/**"
466
+ ],
467
+ "outputContract": "Validated run-owned Backend Test Result v1 at contracts/backend-test-result.json (schemaId backend-test-result-v1) with outcome/counts/failures from deterministic JUnit parse.",
468
+ "subtask_prompt": "Materialize Backend Test Result v1 from JUnit + pytestExitCode under the current DAG run (fail-closed on missing/corrupt report).",
469
+ "shell": {
470
+ "commands": [],
471
+ "jsonArtifactGate": {
472
+ "fromNodeId": "execute-backend-pytest-shell",
473
+ "schemaId": "backend-test-result-v1",
474
+ "artifactName": "backend-test-result.json",
475
+ "outputDir": "contracts"
476
+ },
477
+ "cwd": ".",
478
+ "timeoutMs": 60000
479
+ }
480
+ },
481
+ {
482
+ "id": "classify-backend-test-result-pi",
483
+ "depends_on": [
484
+ "parse-backend-test-result-shell"
485
+ ],
486
+ "complexity": "MED",
487
+ "executor": "pi",
488
+ "role": "reviewer",
489
+ "writePolicy": "read-only",
490
+ "allowedPaths": [
491
+ "**"
492
+ ],
493
+ "forbiddenPaths": [
494
+ ".harness/**",
495
+ "artifacts/**"
496
+ ],
497
+ "outputContract": "Pure JSON classification: category in {ProductBug,TestBug,EnvFailure,ContractMismatch,FlakyTest,Unknown}, evidence[], confidence (capped), notes. No file writes.",
498
+ "subtask_prompt_markdown": "./backend-test-dag.classify.prompt.md"
499
+ },
500
+ {
501
+ "id": "test-retrospect-pi",
502
+ "depends_on": [
503
+ "classify-backend-test-result-pi"
504
+ ],
269
505
  "complexity": "MED",
270
506
  "executor": "pi",
271
507
  "role": "closeout",
272
- "toolProfile": "write",
273
508
  "writePolicy": "exclusive",
509
+ "toolProfile": "write",
274
510
  "writeSet": [
275
511
  "docs/test-reports/**"
276
512
  ],
@@ -281,8 +517,43 @@
281
517
  ".harness/**",
282
518
  "artifacts/**"
283
519
  ],
284
- "outputContract": "Markdown retrospective report under docs/test-reports/ with coverage summary, review findings, pytest results, and maturity rating (A/B/C/D).",
285
520
  "subtask_prompt_markdown": "./backend-test-dag.retrospect.prompt.md"
521
+ },
522
+ {
523
+ "id": "backend-test-outcome-gate-shell",
524
+ "depends_on": [
525
+ "test-retrospect-pi"
526
+ ],
527
+ "complexity": "LOW",
528
+ "executor": "shell",
529
+ "role": "verifier",
530
+ "writePolicy": "read-only",
531
+ "allowedPaths": [
532
+ "**"
533
+ ],
534
+ "forbiddenPaths": [
535
+ ".harness/**",
536
+ "artifacts/**"
537
+ ],
538
+ "outputContract": "Shell exit 0 only when Result v1 outcome=passed with failed=0 and error=0; non-zero otherwise. Ignores retrospective Markdown.",
539
+ "subtask_prompt": "Gate the backend-test DAG on run-owned Result v1 shell facts only (not retrospective prose).",
540
+ "shell": {
541
+ "commands": [
542
+ "test -n \"${HARNESS_DAG_RUN_DIR:-}\" || { echo \"missing HARNESS_DAG_RUN_DIR for backend-test outcome gate\" >&2; exit 2; }; RESULT=\"${HARNESS_DAG_RUN_DIR}/contracts/backend-test-result.json\"; test -f \"${RESULT}\" || { echo \"missing backend-test result: ${RESULT}\" >&2; exit 2; }; node -e 'const fs=require(\"fs\");const r=JSON.parse(fs.readFileSync(process.argv[1],\"utf8\"));const outcome=String(r.outcome||\"\");const ok=outcome===\"passed\"&&Number(r.failed||0)===0&&Number(r.error||0)===0;console.log(\"backend-test outcome=\"+outcome+\" passed=\"+r.passed+\" failed=\"+r.failed+\" error=\"+r.error+\" executionStatus=\"+r.executionStatus);if(!ok){process.exit(1);}' \"${RESULT}\""
543
+ ],
544
+ "verifyEvidence": {
545
+ "phase": "final",
546
+ "quota": "full",
547
+ "commandSource": "inline",
548
+ "commandCount": 1,
549
+ "commandLabels": [
550
+ "test -n \"${HARNESS_DAG_RUN_DIR:-}\" || { echo \"missing HARNESS_DAG_RUN_DIR for backend-test outcome gate\" >&2; exit 2; }; RESULT=\"${HARNESS_DAG_RUN_DIR}/contracts/backend-test-result.json\"; test -f \"${RESULT}\" || { echo \"missing backend-test result: ${RESULT}\" >&2; exit 2; }; node -e 'const fs=require(\"fs\");const r=JSON.parse(fs.readFileSync(process.argv[1],\"utf8\"));const outcome=String(r.outcome||\"\");const ok=outcome===\"passed\"&&Number(r.failed||0)===0&&Number(r.error||0)===0;console.log(\"backend-test outcome=\"+outcome+\" passed=\"+r.passed+\" failed=\"+r.failed+\" error=\"+r.error+\" executionStatus=\"+r.executionStatus);if(!ok){process.exit(1);}' \"${RESULT}\""
551
+ ],
552
+ "finalFullRequired": true
553
+ },
554
+ "cwd": ".",
555
+ "timeoutMs": 60000
556
+ }
286
557
  }
287
558
  ]
288
559
  }