@tea-agent/loop-agent 0.13.0-beta.0 → 0.14.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 (127) hide show
  1. package/AGENTS.md +2 -0
  2. package/CHANGELOG.md +56 -305
  3. package/README.md +13 -19
  4. package/dist/commands/init.js +92 -23
  5. package/dist/executors/pi-event-serializer.js +33 -11
  6. package/dist/executors/shell-executor.js +200 -21
  7. package/dist/infrastructure/evaluation/candidate-store.js +5 -1
  8. package/dist/worker/observe/spec-evidence.js +19 -10
  9. package/dist/worker/observe/static/app.js +4 -3
  10. package/dist/worker/observe/static/constants.js +10 -2
  11. package/dist/worker/observe/static/dag-helpers.js +37 -8
  12. package/dist/worker/observe/static/dom.js +159 -0
  13. package/dist/worker/observe/static/format-pool.d.ts +71 -0
  14. package/dist/worker/observe/static/format-pool.js +67 -0
  15. package/dist/worker/observe/static/format.js +27 -2
  16. package/dist/worker/observe/static/index.html +76 -34
  17. package/dist/worker/observe/static/kpi.js +12 -6
  18. package/dist/worker/observe/static/markdown-render.js +124 -0
  19. package/dist/worker/observe/static/shell-chrome.js +8 -2
  20. package/dist/worker/observe/static/state.js +20 -0
  21. package/dist/worker/observe/static/styles.css +662 -60
  22. package/dist/worker/observe/static/views/dag-inspector.js +65 -142
  23. package/dist/worker/observe/static/views/dag.js +9 -0
  24. package/dist/worker/observe/static/views/dashboard.js +512 -269
  25. package/dist/worker/observe/static/views/pool.js +595 -237
  26. package/dist/worker/observe/static/views/session-timeline.js +577 -11
  27. package/dist/workflows/dag/backend-test-case-manifest.js +503 -0
  28. package/dist/workflows/dag/backend-test-execution-contract.js +353 -0
  29. package/dist/workflows/dag/backend-test-result-contract.js +568 -0
  30. package/dist/workflows/dag/decision-envelope.js +57 -2
  31. package/dist/workflows/dag/frontend-implementation-contract.js +240 -0
  32. package/dist/workflows/dag/frontend-project-capability.js +309 -0
  33. package/dist/workflows/dag/frontend-repair.js +341 -0
  34. package/dist/workflows/dag/frontend-risk.js +161 -0
  35. package/dist/workflows/dag/frontend-verification-trace.js +190 -0
  36. package/dist/workflows/dag/init-hybrid.js +1020 -125
  37. package/dist/workflows/dag/repair-artifact.js +43 -3
  38. package/dist/workflows/dag/skill-instructions.js +4 -2
  39. package/dist/workflows/dag/types.js +29 -8
  40. package/docs/README.md +2 -0
  41. package/docs/agent-dag-recovery-playbook.md +3 -3
  42. package/docs/agent-dag-runner.md +3 -3
  43. package/docs/architecture/README.md +3 -3
  44. package/docs/architecture/dag-execution.md +1 -1
  45. package/docs/architecture/evolution.md +13 -13
  46. package/docs/architecture/facts-and-state.md +1 -1
  47. package/docs/architecture/runtime-boundaries.md +7 -7
  48. package/docs/architecture/system-overview.md +3 -3
  49. package/docs/architecture/worker-and-feature.md +3 -3
  50. package/docs/design/README.md +7 -7
  51. package/docs/development-principles.md +4 -4
  52. package/docs/exec-plans/active/README.md +2 -4
  53. package/docs/exec-plans/completed/README.md +29 -6
  54. package/docs/feature-workflow.md +57 -32
  55. package/docs/init-surface.manifest.json +21 -3
  56. package/docs/loop-agent-harness.md +8 -8
  57. package/docs/production-readiness.md +1 -1
  58. package/docs/progress/README.md +20 -3
  59. package/docs/reports/README.md +53 -7
  60. package/docs/templates/agent-dag.supervised-implementation.json +127 -8
  61. package/docs/templates/backend-test-case-manifest.schema.json +190 -0
  62. package/docs/templates/backend-test-dag.classify.prompt.md +75 -0
  63. package/docs/templates/backend-test-dag.generate-pytest.prompt.md +6 -4
  64. package/docs/templates/backend-test-dag.json +269 -21
  65. package/docs/templates/backend-test-dag.retrospect.prompt.md +44 -30
  66. package/docs/templates/backend-test-dag.review-cases.prompt.md +6 -4
  67. package/docs/templates/backend-test-execution.schema.json +133 -0
  68. package/docs/templates/backend-test-result.schema.json +99 -0
  69. package/docs/templates/branch-merge-report.md +93 -0
  70. package/docs/templates/frontend-eval/fixtures/failures/01-type-build-error.md +17 -0
  71. package/docs/templates/frontend-eval/fixtures/failures/02-unit-component-test-fail.md +16 -0
  72. package/docs/templates/frontend-eval/fixtures/failures/03-fixture-schema-drift.md +16 -0
  73. package/docs/templates/frontend-eval/fixtures/failures/04-missing-loading-empty-error-state.md +16 -0
  74. package/docs/templates/frontend-eval/fixtures/failures/05-forbidden-write-writeset-expansion.md +16 -0
  75. package/docs/templates/frontend-eval/fixtures/failures/06-unapproved-dependency-add.md +16 -0
  76. package/docs/templates/frontend-eval/fixtures/failures/07-mock-production-on.md +21 -0
  77. package/docs/templates/frontend-eval/fixtures/functional/01-simple-component-style.md +29 -0
  78. package/docs/templates/frontend-eval/fixtures/functional/02-form-validation.md +28 -0
  79. package/docs/templates/frontend-eval/fixtures/functional/03-list-detail-page.md +28 -0
  80. package/docs/templates/frontend-eval/fixtures/functional/04-api-mock.md +29 -0
  81. package/docs/templates/frontend-eval/fixtures/functional/05-permission-auth-gated-ui.md +27 -0
  82. package/docs/templates/frontend-eval/fixtures/functional/06-ssr-server-client-boundary.md +28 -0
  83. package/docs/templates/frontend-eval/fixtures/functional/07-shared-public-component-api.md +28 -0
  84. package/docs/templates/frontend-eval/fixtures/functional/08-pure-local-no-remote.md +27 -0
  85. package/docs/templates/frontend-eval/metrics.md +138 -0
  86. package/docs/templates/frontend-eval/smoke-targets.md +53 -0
  87. package/docs/templates/frontend-implementation-contract.schema.json +27 -0
  88. package/docs/verification-matrix.md +1 -1
  89. package/examples/decision-gate-agent-dag.json +4 -4
  90. package/examples/hybrid-loop-agent-dag.json +1 -1
  91. package/package.json +2 -2
  92. package/skills/ai-engineering-context/SKILL.md +2 -2
  93. package/skills/browser-tools/SKILL.md +196 -0
  94. package/skills/browser-tools/browser-content.js +103 -0
  95. package/skills/browser-tools/browser-cookies.js +35 -0
  96. package/skills/browser-tools/browser-eval.js +53 -0
  97. package/skills/browser-tools/browser-hn-scraper.js +108 -0
  98. package/skills/browser-tools/browser-nav.js +44 -0
  99. package/skills/browser-tools/browser-pick.js +162 -0
  100. package/skills/browser-tools/browser-screenshot.js +34 -0
  101. package/skills/browser-tools/browser-start.js +86 -0
  102. package/skills/browser-tools/package-lock.json +2556 -0
  103. package/skills/browser-tools/package.json +19 -0
  104. package/skills/frontend-implementation/SKILL.md +3 -1
  105. package/skills/frontend-implementation/references/node-contracts.md +17 -66
  106. package/skills/frontend-verification/SKILL.md +1 -1
  107. package/skills/grill-with-docs/SKILL.md +5 -5
  108. package/skills/grill-with-docs/adr-format.md +3 -3
  109. package/skills/init-capability-evolution/SKILL.md +5 -5
  110. package/skills/loop-agent/SKILL.md +5 -5
  111. package/skills/loop-agent/references/README.md +3 -3
  112. package/skills/loop-agent/references/command-reference.md +39 -17
  113. package/skills/loop-agent/references/docs-converge.md +15 -15
  114. package/skills/loop-agent/references/harness-policy.md +2 -2
  115. package/skills/loop-agent/references/hybrid-dag.md +20 -15
  116. package/skills/loop-agent/references/multi-worktree.md +1 -1
  117. package/skills/loop-agent/references/orchestrator-and-interventions.md +8 -8
  118. package/skills/loop-agent/references/task-workflow.md +1 -1
  119. package/skills/loop-agent/references/verification-and-failure-handling.md +6 -4
  120. package/skills/requesting-code-review/SKILL.md +1 -1
  121. package/skills/systematic-debugging/CREATION-LOG.md +3 -3
  122. package/skills/systematic-debugging/SKILL.md +1 -1
  123. package/skills/systematic-debugging/test-academic.md +1 -1
  124. package/skills/systematic-debugging/test-pressure-1.md +1 -1
  125. package/skills/systematic-debugging/test-pressure-2.md +1 -1
  126. package/skills/systematic-debugging/test-pressure-3.md +1 -1
  127. package/skills/verification-before-completion/SKILL.md +1 -1
@@ -7,15 +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 as Backend Test Analysis v1 JSON → validate analysis contract → 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
12
  "analyze-inputs-pi returns pure Backend Test Analysis v1 JSON (schema docs/templates/backend-test-analysis.schema.json) with no Markdown prose",
13
13
  "backend-test-analysis-contract-shell validates schemaId backend-test-analysis-v1 and materializes run-owned contracts/backend-test-analysis.json",
14
- "generate-backend-functional-cases-pi consumes the validated analysis artifact and produces structured test cases with BE-<MODULE>-<NNN> IDs under testcase/md/",
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)",
15
21
  "review-backend-cases-pi returns VERDICT: pass or request-revision with coverage assessment",
16
22
  "review-backend-cases-gate-shell blocks pytest generation unless the review verdict is VERDICT: pass",
17
- "generate-backend-pytest-pi converts reviewed cases into pytest code with 1:1 traceability under testcase/",
18
- "execute-backend-pytest-shell runs pytest with a read-only worktree and writes JUnit XML under the current HARNESS_DAG_RUN_DIR/reports/** only",
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)",
19
30
  "test-retrospect-pi generates retrospective report with A/B/C/D maturity rating under docs/test-reports/",
20
31
  "Full traceability from acceptance criteria → functional test case ID → pytest function name"
21
32
  ],
@@ -36,7 +47,10 @@
36
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.",
37
48
  "Prompt templates must not ask main session to write artifacts; node output is the artifact and runner archives it under .harness/dag-runs/.",
38
49
  "Actual file operation paths must be macOS/Windows compatible; use / only for stable repo refs, JSON/Markdown evidence refs, and glob conventions.",
39
- "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."
40
54
  ],
41
55
  "defaults": {
42
56
  "executor": "pi",
@@ -136,15 +150,76 @@
136
150
  }
137
151
  },
138
152
  {
139
- "id": "generate-backend-functional-cases-pi",
153
+ "id": "backend-test-environment-scout-pi",
140
154
  "depends_on": [
141
155
  "backend-test-analysis-contract-shell"
142
156
  ],
143
157
  "complexity": "MED",
144
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
+ ],
218
+ "complexity": "MED",
219
+ "executor": "pi",
145
220
  "role": "implementer",
146
- "toolProfile": "write",
147
221
  "writePolicy": "exclusive",
222
+ "toolProfile": "write",
148
223
  "writeSet": [
149
224
  "testcase/md/**"
150
225
  ],
@@ -155,15 +230,76 @@
155
230
  ".harness/**",
156
231
  "artifacts/**"
157
232
  ],
158
- "outputContract": "Structured backend functional test cases in Markdown under testcase/md/. Each case uses BE-<MODULE>-<NNN> ID format.",
159
- "subtask_prompt": "Read the validated structured artifact pointer from backend-test-analysis-contract-shell and generate cases only from that JSON contract.\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\n## Conditional Coverage (include ONLY if mentioned in upstream analysis):\n- Boundary conditions: include ONLY if upstream analyze-inputs-pi mentions value ranges, length limits, numeric bounds, or format constraints\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 or fall back to free-form analysis — use the validated structured artifact 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/**"
160
234
  },
161
235
  {
162
- "id": "review-backend-cases-pi",
236
+ "id": "emit-backend-case-manifest-pi",
163
237
  "depends_on": [
164
238
  "generate-backend-functional-cases-pi",
165
239
  "backend-test-analysis-contract-shell"
166
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
+ }
296
+ },
297
+ {
298
+ "id": "review-backend-cases-pi",
299
+ "depends_on": [
300
+ "backend-test-case-manifest-shell",
301
+ "backend-test-analysis-contract-shell"
302
+ ],
167
303
  "complexity": "HIGH",
168
304
  "executor": "pi",
169
305
  "role": "reviewer",
@@ -225,13 +361,14 @@
225
361
  {
226
362
  "id": "generate-backend-pytest-pi",
227
363
  "depends_on": [
228
- "review-backend-cases-gate-shell"
364
+ "review-backend-cases-gate-shell",
365
+ "backend-test-execution-contract-shell"
229
366
  ],
230
367
  "complexity": "HIGH",
231
368
  "executor": "pi",
232
369
  "role": "implementer",
233
- "toolProfile": "write",
234
370
  "writePolicy": "exclusive",
371
+ "toolProfile": "write",
235
372
  "writeSet": [
236
373
  "testcase/**/test_*.py",
237
374
  "testcase/**/helpers/**",
@@ -245,13 +382,40 @@
245
382
  ".harness/**",
246
383
  "artifacts/**"
247
384
  ],
248
- "outputContract": "Pytest test files under testcase/ with 1:1 mapping to functional test case IDs; optional helpers/factories under testcase/**/helpers|factories. Summary lists generated files, test function count, and any skipped cases with reasons.",
249
385
  "subtask_prompt_markdown": "./backend-test-dag.generate-pytest.prompt.md"
250
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
+ },
251
414
  {
252
415
  "id": "execute-backend-pytest-shell",
253
416
  "depends_on": [
254
- "generate-backend-pytest-pi"
417
+ "backend-test-traceability-gate-shell",
418
+ "backend-test-execution-contract-shell"
255
419
  ],
256
420
  "complexity": "LOW",
257
421
  "executor": "shell",
@@ -264,11 +428,11 @@
264
428
  ".harness/**",
265
429
  "artifacts/**"
266
430
  ],
267
- "outputContract": "Archived pytest stdout/stderr with exit codes; JUnit XML is runner-owned evidence at $HARNESS_DAG_RUN_DIR/reports/backend-test-junit.xml. Must not modify worktree files, testcase sources, production code, or assertions.",
268
- "subtask_prompt": "Run pytest for the backend test suite; write JUnit evidence only under the current HARNESS_DAG_RUN_DIR/reports/.",
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/.",
269
433
  "shell": {
270
434
  "commands": [
271
- "test -n \"${HARNESS_DAG_RUN_DIR:-}\" || { echo \"missing HARNESS_DAG_RUN_DIR for backend pytest report\" >&2; exit 2; }; REPORT=\"${HARNESS_DAG_RUN_DIR}/reports/backend-test-junit.xml\"; mkdir -p \"$(dirname \"${REPORT}\")\"; PYTHONDONTWRITEBYTECODE=1 python -m pytest testcase/ -v -p no:cacheprovider --junitxml=\"${REPORT}\"; STATUS=$?; printf \"JUnit report: %s\\n\" \"${REPORT}\"; exit \"${STATUS}\""
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}\""
272
436
  ],
273
437
  "verifyEvidence": {
274
438
  "phase": "final",
@@ -276,7 +440,7 @@
276
440
  "commandSource": "inline",
277
441
  "commandCount": 1,
278
442
  "commandLabels": [
279
- "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}\""
280
444
  ],
281
445
  "finalFullRequired": true
282
446
  },
@@ -285,15 +449,64 @@
285
449
  }
286
450
  },
287
451
  {
288
- "id": "test-retrospect-pi",
452
+ "id": "parse-backend-test-result-shell",
289
453
  "depends_on": [
290
454
  "execute-backend-pytest-shell"
291
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
+ ],
292
505
  "complexity": "MED",
293
506
  "executor": "pi",
294
507
  "role": "closeout",
295
- "toolProfile": "write",
296
508
  "writePolicy": "exclusive",
509
+ "toolProfile": "write",
297
510
  "writeSet": [
298
511
  "docs/test-reports/**"
299
512
  ],
@@ -304,8 +517,43 @@
304
517
  ".harness/**",
305
518
  "artifacts/**"
306
519
  ],
307
- "outputContract": "Markdown retrospective report under docs/test-reports/ with coverage summary, review findings, pytest results, and maturity rating (A/B/C/D).",
308
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
+ }
309
557
  }
310
558
  ]
311
559
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Purpose
4
4
 
5
- Use this prompt for a **test retrospective** node: `executor: "pi"`, `role: "closeout"`, `toolProfile: "write"`, `writePolicy: "exclusive"`. The closeout agent reads upstream review reports and pytest execution results, then generates a retrospective report with an objective maturity rating.
5
+ Use this prompt for a **test retrospective** node: `executor: "pi"`, `role: "closeout"`, `toolProfile: "write"`, `writePolicy: "exclusive"`. The closeout agent reads **Backend Test Result v1** (and classification), then generates a retrospective report with an objective maturity rating.
6
6
 
7
7
  Do **not** create a new executor type. This is a standard `executor: pi` writer node.
8
8
 
@@ -11,7 +11,7 @@ Do **not** create a new executor type. This is a standard `executor: pi` writer
11
11
  ```json
12
12
  {
13
13
  "id": "test-retrospect-pi",
14
- "depends_on": ["execute-backend-pytest-shell"],
14
+ "depends_on": ["classify-backend-test-result-pi"],
15
15
  "complexity": "MED",
16
16
  "executor": "pi",
17
17
  "role": "closeout",
@@ -20,7 +20,7 @@ Do **not** create a new executor type. This is a standard `executor: pi` writer
20
20
  "writeSet": ["docs/test-reports/**"],
21
21
  "allowedPaths": ["docs/test-reports/**"],
22
22
  "forbiddenPaths": [".harness/**", "artifacts/**"],
23
- "outputContract": "Markdown retrospective report under docs/test-reports/ with coverage summary, review findings, pytest results, and maturity rating (A/B/C/D).",
23
+ "outputContract": "Markdown retrospective report under docs/test-reports/ with coverage summary, review findings, Result v1 stats, classification, and maturity rating (A/B/C/D).",
24
24
  "subtask_prompt_markdown": "./backend-test-dag.retrospect.prompt.md"
25
25
  }
26
26
  ```
@@ -29,7 +29,9 @@ Do **not** create a new executor type. This is a standard `executor: pi` writer
29
29
 
30
30
  You are the Backend Test DAG **test retrospective** agent.
31
31
 
32
- Your job is to read upstream outputs (review report + pytest results) and generate a retrospective report with a maturity rating. Write the report under `docs/test-reports/` only. Stay within `writeSet`. Do not write root `artifacts/**`.
32
+ Your job is to read upstream Result v1 + classification (+ review report) and generate a retrospective report with a maturity rating. Write the report under `docs/test-reports/` only. Stay within `writeSet`. Do not write root `artifacts/**`.
33
+
34
+ This node runs on **both pass and assertion-fail** paths (after parse + classify). Final task success is decided later by `backend-test-outcome-gate-shell` using Result v1 shell facts only — **never** rewrite a failed result as passed in this report.
33
35
 
34
36
  ### Output Steps (do in order)
35
37
 
@@ -38,28 +40,40 @@ Your job is to read upstream outputs (review report + pytest results) and genera
38
40
 
39
41
  ### Inputs
40
42
 
41
- 1. **Review report** — `review-backend-cases-pi` output (VERDICT, findings, coverage assessment).
42
- 2. **Pytest output** `execute-backend-pytest-shell` stdout/stderr and exit code.
43
- 3. **Machine-readable report** — `$HARNESS_DAG_RUN_DIR/reports/backend-test-junit.xml` (runner-owned evidence; use the path reported by `execute-backend-pytest-shell`).
43
+ 1. **Result v1 (authoritative stats)** — `$HARNESS_DAG_RUN_DIR/contracts/backend-test-result.json`
44
+ Use `passed` / `failed` / `error` / `skipped` / `outcome` / `failures[]` / `pytestExitCode` only from this artifact.
45
+ 2. **Case Manifest v1 (authoritative AC coverage)** — `$HARNESS_DAG_RUN_DIR/contracts/backend-test-case-manifest.json`
46
+ Use `coverageSummary.acCoverageRatio`, `coveredAcCount`, `explicitAcCount`, case counts only from this artifact.
47
+ 3. **Classification** — `classify-backend-test-result-pi` JSON (`category`, `confidence`, `evidence`). Interpretive only; does not override outcome.
48
+ 4. **Review report** — `review-backend-cases-pi` output (VERDICT, findings, coverage assessment).
49
+ 5. Optional secondary: execute stdout markers / JUnit path (do not re-parse logs for counts when Result v1 exists).
44
50
 
45
51
  Do NOT re-read source documents. Use upstream outputs only.
46
52
 
53
+ ### Stats authority
54
+
55
+ - Pass rate = `passed / (passed + failed + error)` when denominator > 0 (skipped excluded from denominator unless Result documents otherwise) — **Result v1 only**.
56
+ - AC coverage = `coverageSummary.acCoverageRatio` from Case Manifest v1 only (do **not** recompute or invent percentages).
57
+ - Failed case table rows must match `failures[]` from Result v1.
58
+ - If Result v1 `outcome` is not `passed`, the retrospective **must not** claim overall success.
59
+
47
60
  ### Maturity Rating Criteria
48
61
 
49
- | Rating | Coverage | Pass Rate | Review Findings |
50
- |--------|----------|-----------|-----------------|
51
- | **A** | 100% acceptance criteria covered | 100% pytest pass | No Critical or Important findings |
52
- | **B** | ≥80% acceptance criteria covered | ≥90% pytest pass | Only Informational findings |
53
- | **C** | ≥60% acceptance criteria covered | ≥70% pytest pass | No Critical findings (Important allowed) |
62
+ | Rating | Coverage (manifest) | Pass Rate (Result v1) | Review Findings |
63
+ |--------|---------------------|------------------------|-----------------|
64
+ | **A** | `acCoverageRatio` = 1 | 100% pytest pass | No Critical or Important findings |
65
+ | **B** | `acCoverageRatio` 0.8 | ≥90% pytest pass | Only Informational findings |
66
+ | **C** | `acCoverageRatio` 0.6 | ≥70% pytest pass | No Critical findings (Important allowed) |
54
67
  | **D** | Below C thresholds | Below C thresholds | Or any Critical finding unresolved |
55
68
 
56
69
  #### Rating Rules
57
70
 
58
- - **Coverage** = (acceptance criteria with ≥1 covering test case) / (total acceptance criteria) × 100%
59
- - **Pass rate** = (passed pytest functions) / (total non-skipped pytest functions) × 100%
71
+ - **Coverage** from Case Manifest `coverageSummary` only (deterministic gate product).
72
+ - **Pass rate** from Result v1 only (not guessed from logs).
60
73
  - If `review-backend-cases-pi` returned `VERDICT: request-revision` and revision was not completed, cap at **D**.
61
- - If pytest exit code is non-zero and >30% tests failed, cap at **D** regardless of coverage.
62
- - Skipped tests (`@pytest.mark.skip`) count as "not covered" for pass rate but not as failures.
74
+ - If Result v1 shows >30% failed+error among executed tests, cap at **D** regardless of coverage.
75
+ - Skipped tests count as "not covered" for pass rate but not as failures.
76
+ - Collection/command/report errors → cap at **D** and record classification (not ProductBug by default).
63
77
 
64
78
  ### Report Structure
65
79
 
@@ -71,6 +85,8 @@ Write the report as a Markdown file named `backend-test-retrospect-<date>.md` un
71
85
  **Date:** <YYYY-MM-DD>
72
86
  **Task:** <task-id>
73
87
  **Maturity Rating:** <A|B|C|D>
88
+ **Result outcome:** <from Result v1>
89
+ **Classification:** <from classify JSON>
74
90
 
75
91
  ## 1. Test Coverage Summary
76
92
 
@@ -79,43 +95,41 @@ Write the report as a Markdown file named `backend-test-retrospect-<date>.md` un
79
95
  | Total acceptance criteria | N |
80
96
  | Covered by test cases | N (X%) |
81
97
  | Total functional test cases | N |
82
- | Positive path cases | N |
83
- | Negative path cases | N |
84
- | Boundary cases | N |
85
98
 
86
- ## 2. Automation Results
99
+ ## 2. Automation Results (from Result v1)
87
100
 
88
101
  | Metric | Value |
89
102
  |--------|-------|
90
- | Total pytest functions | N |
103
+ | Total tests | N |
91
104
  | Passed | N |
92
105
  | Failed | N |
106
+ | Error | N |
93
107
  | Skipped | N |
94
108
  | Pass rate | X% |
95
109
  | Pytest exit code | N |
110
+ | Outcome | … |
111
+ | Execution status | … |
96
112
 
97
113
  ### Failed Test Analysis
98
114
 
99
- | Test Case ID | Function | Failure Reason | Root Cause |
100
- |--------------|----------|----------------|------------|
101
- | ... | ... | ... | ... |
115
+ | Test Case / Function | Message (truncated) | Classification |
116
+ |----------------------|-------------------|----------------|
117
+ | ... | ... | ... |
102
118
 
103
119
  ## 3. Review Findings
104
120
 
105
121
  | Severity | Finding | Status |
106
122
  |----------|---------|--------|
107
- | Critical | ... | Resolved / Unresolved |
108
- | Important | ... | Resolved / Unresolved |
109
- | Informational | ... | Resolved / Unresolved |
123
+ | | | |
110
124
 
111
125
  ## 4. Maturity Rating Rationale
112
126
 
113
- Explain which threshold was met or missed, and why the specific rating was assigned.
127
+ Explain which threshold was met or missed.
114
128
 
115
129
  ## 5. Recommendations
116
130
 
117
- - Actionable items for improving the rating in the next iteration.
118
- - Specific gaps to close (uncovered criteria, flaky tests, missing negative paths).
131
+ - Actionable items for the next iteration.
132
+ - Do not propose changing production code solely to greenwash tests.
119
133
  ```
120
134
 
121
135
  ### Output Shape (after rating line)
@@ -11,7 +11,7 @@ Do **not** create `executor: reviewer`. Reviewer is a **role** on `executor: pi`
11
11
  ```json
12
12
  {
13
13
  "id": "review-backend-cases-pi",
14
- "depends_on": ["generate-backend-functional-cases-pi", "backend-test-analysis-contract-shell"],
14
+ "depends_on": ["backend-test-case-manifest-shell", "backend-test-analysis-contract-shell"],
15
15
  "complexity": "HIGH",
16
16
  "executor": "pi",
17
17
  "role": "reviewer",
@@ -41,9 +41,10 @@ No preamble, heading, or blank lines before the verdict line.
41
41
  ### Inputs to Review
42
42
 
43
43
  1. **Acceptance criteria / analysis** — from the validated Backend Test Analysis v1 artifact materialized by `backend-test-analysis-contract-shell` (`contracts/backend-test-analysis.json` under the current DAG run). Do not treat free-form Markdown from `analyze-inputs-pi` as the contract.
44
- 2. **Generated test cases** — files under `testcase/md/`.
44
+ 2. **Case Manifest v1** — `contracts/backend-test-case-manifest.json` (schemaId `backend-test-case-manifest-v1`). Prefer `coverageSummary` and caseId↔acIds from this artifact; do not invent coverage percentages.
45
+ 3. **Generated test cases** — files under `testcase/md/`.
45
46
 
46
- Do NOT re-read source documents. Use the validated analysis artifact and generated cases only.
47
+ Do NOT re-read source documents. Use the validated analysis artifact, case manifest, and generated cases only.
47
48
 
48
49
  ### Review Checklist
49
50
 
@@ -54,7 +55,8 @@ Do NOT re-read source documents. Use the validated analysis artifact and generat
54
55
  | **Negative path coverage** | Error/exception scenarios (invalid input, not found, state violations) | Important |
55
56
  | **Boundary conditions** | Edge cases (empty input, max length, edge values) | Important |
56
57
  | **State transitions** | Illegal state changes covered | Important |
57
- | **Requirement traceability** | Each acceptance criterion (AC-xxx) maps to at least one test case ID | Critical |
58
+ | **Requirement traceability** | Each acceptance criterion (AC-xxx) maps to at least one test case ID (manifest coverageSummary or evidenceGaps) | Critical |
59
+ | **Manifest consistency** | Markdown cases align with Case Manifest v1 caseId/acIds | Critical |
58
60
  | **Case structure** | Each case has: ID, Title, Precondition, Steps, Expected Result | Important |
59
61
  | **No duplicate IDs** | All test case IDs are unique across files | Critical |
60
62