@tea-agent/loop-agent 0.16.23 → 0.16.25
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.
- package/CHANGELOG.md +33 -13
- package/dist/cli/command-definitions.js +13 -0
- package/dist/cli/program.js +4 -0
- package/dist/commands/coverage-report.js +50 -0
- package/dist/executors/shell-presets.js +27 -26
- package/dist/executors/shell-verification.js +2 -0
- package/dist/task/config-types.js +15 -3
- package/dist/workflows/dag/backend-test-analysis-contract.js +21 -8
- package/dist/workflows/dag/backend-test-coverage-contract.js +202 -0
- package/dist/workflows/dag/backend-test-execution-contract.js +84 -18
- package/dist/workflows/dag/backend-test-stability-contract.js +57 -0
- package/dist/workflows/dag/dynamic-runtime/map.js +129 -22
- package/dist/workflows/dag/init-hybrid.js +374 -272
- package/dist/workflows/dag/l5-report-metrics.js +36 -0
- package/dist/workflows/dag/types.js +7 -0
- package/docs/architecture/evolution.md +3 -1
- package/docs/local-development-environment.md +4 -0
- package/docs/templates/backend-test-dag.generate-pytest.prompt.md +2 -0
- package/docs/templates/backend-test-dag.json +40 -7
- package/docs/templates/backend-test-dag.retrospect.prompt.md +36 -7
- package/docs/templates/frontend-test-case-checklist.md +25 -0
- package/docs/templates/frontend-test-dag.json +4 -3
- package/package.json +1 -1
- package/skills/loop-agent/references/command-reference.md +1 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
function unavailable(reason, numerator = null, denominator = null, threshold = null) {
|
|
2
|
+
return { numerator, denominator, ratio: null, threshold, status: "unavailable", reason };
|
|
3
|
+
}
|
|
4
|
+
function ratioMetric(numerator, denominator, threshold, label) {
|
|
5
|
+
if (denominator === 0)
|
|
6
|
+
return unavailable(`${label}-denominator-is-zero`, numerator, denominator, threshold);
|
|
7
|
+
const ratio = numerator / denominator;
|
|
8
|
+
return { numerator, denominator, ratio, threshold, status: ratio >= threshold ? "pass" : "fail", reason: ratio >= threshold ? null : `${label}-below-threshold` };
|
|
9
|
+
}
|
|
10
|
+
export function computeL5ReportMetrics(input) {
|
|
11
|
+
const executed = input.result.passed + input.result.failed + input.result.error;
|
|
12
|
+
const metrics = {
|
|
13
|
+
passRate: ratioMetric(input.result.passed, executed, 1, "pass-rate"),
|
|
14
|
+
acCoverage: input.manifest.coverageSummary
|
|
15
|
+
? input.manifest.coverageSummary.explicitAcCount === 0
|
|
16
|
+
? { numerator: 0, denominator: 0, ratio: 1, threshold: 1, status: "pass", reason: null }
|
|
17
|
+
: ratioMetric(input.manifest.coverageSummary.coveredAcCount, input.manifest.coverageSummary.explicitAcCount, 1, "ac-coverage")
|
|
18
|
+
: unavailable("case-manifest-coverage-missing", null, null, 1),
|
|
19
|
+
automationCoverage: input.manifest.coverageSummary
|
|
20
|
+
? ratioMetric(input.manifest.coverageSummary.generatedCount, input.manifest.coverageSummary.caseCount, 0.9, "automation-coverage")
|
|
21
|
+
: unavailable("case-manifest-coverage-missing", null, null, 0.9),
|
|
22
|
+
stability: input.stability?.status === "available" && input.stability.ratio !== null
|
|
23
|
+
? { numerator: input.stability.successfulRuns, denominator: input.stability.recordedRuns, ratio: input.stability.ratio, threshold: 0.95, status: input.stability.ratio >= 0.95 ? "pass" : "fail", reason: input.stability.ratio >= 0.95 ? null : "stability-below-threshold" }
|
|
24
|
+
: unavailable(input.stability?.reason ?? "stability-evidence-missing", input.stability?.successfulRuns ?? null, input.stability?.recordedRuns ?? null, 0.95),
|
|
25
|
+
lineCoverage: input.coverage?.metrics.line.status === "available" && input.coverage.metrics.line.ratio !== null
|
|
26
|
+
? { numerator: input.coverage.metrics.line.covered, denominator: input.coverage.metrics.line.total, ratio: input.coverage.metrics.line.ratio, threshold: 0.8, status: input.coverage.metrics.line.ratio >= 0.8 ? "pass" : "fail", reason: input.coverage.metrics.line.ratio >= 0.8 ? null : "line-coverage-below-threshold" }
|
|
27
|
+
: unavailable(input.coverage?.metrics.line.reason ?? "line-coverage-missing", input.coverage?.metrics.line.covered ?? null, input.coverage?.metrics.line.total ?? null, 0.8),
|
|
28
|
+
branchCoverage: input.coverage?.metrics.branch.status === "available" && input.coverage.metrics.branch.ratio !== null
|
|
29
|
+
? { numerator: input.coverage.metrics.branch.covered, denominator: input.coverage.metrics.branch.total, ratio: input.coverage.metrics.branch.ratio, threshold: 0.7, status: input.coverage.metrics.branch.ratio >= 0.7 ? "pass" : "fail", reason: input.coverage.metrics.branch.ratio >= 0.7 ? null : "branch-coverage-below-threshold" }
|
|
30
|
+
: unavailable(input.coverage?.metrics.branch.reason ?? "branch-coverage-missing", input.coverage?.metrics.branch.covered ?? null, input.coverage?.metrics.branch.total ?? null, 0.7),
|
|
31
|
+
skipped: { numerator: input.result.skipped, denominator: input.result.skipped, ratio: input.result.skipped === 0 ? 1 : 0, threshold: 1, status: input.result.skipped === 0 ? "pass" : "fail", reason: input.result.skipped === 0 ? null : "skipped-tests-present" },
|
|
32
|
+
criticalRisks: { numerator: input.criticalRiskCount, denominator: input.criticalRiskCount, ratio: input.criticalRiskCount === 0 ? 1 : 0, threshold: 1, status: input.criticalRiskCount === 0 ? "pass" : "fail", reason: input.criticalRiskCount === 0 ? null : "critical-risk-present" },
|
|
33
|
+
};
|
|
34
|
+
const blockingItems = Object.entries(metrics).filter(([, value]) => value.status !== "pass").map(([name, value]) => `${name}:${value.reason ?? value.status}`);
|
|
35
|
+
return { status: blockingItems.length === 0 ? "ready" : "not-ready", metrics, blockingItems };
|
|
36
|
+
}
|
|
@@ -201,6 +201,13 @@ export const dagDynamicExpansionSchema = z.object({
|
|
|
201
201
|
maxTotalTokens: z.number().int().positive().optional(),
|
|
202
202
|
})
|
|
203
203
|
.optional(),
|
|
204
|
+
/**
|
|
205
|
+
* When true, map child ERROR/auth/timeout is recorded as case-level
|
|
206
|
+
* failed/blocked evidence and the map barrier still succeeds (frontend-test).
|
|
207
|
+
* Default false: any non-budget child failure fails the map aggregate
|
|
208
|
+
* (sharded migration and other map_agent workflows).
|
|
209
|
+
*/
|
|
210
|
+
tolerateChildFailures: z.boolean().optional(),
|
|
204
211
|
});
|
|
205
212
|
export const dagDynamicReductionSchema = z.object({
|
|
206
213
|
type: z.literal("verified_findings_report"),
|
|
@@ -35,10 +35,12 @@
|
|
|
35
35
|
| 云 Task Pool / SQL / Orchestrator | 规划 / 未实现 | 同上(第 2 月原始设计已调整为本地 Feature 闭环) |
|
|
36
36
|
| 多仓库平台 | 规划 / 未实现 | 同上 |
|
|
37
37
|
| 组织级服务 | 规划 / 未实现 | 同上 |
|
|
38
|
-
| Web Console(远端) | 规划 /
|
|
38
|
+
| Web Console(远端) | 规划 / 未实现;指多用户/远程编排平台,不等同于 `docs/design/local-operator-console-from-pi-web.md` 的单机 loopback Operator Console | 同上 |
|
|
39
39
|
| Dynamic Workflow runtime limits 强执法、更广 profile | 设计输入 | `ai_workspace/loop-agent/design/dynamic-workflow-dag-engine-roadmap.md`(未勾选 phase) |
|
|
40
40
|
| Loop 与 Dynamic Workflow 更深的双向集成、稳定化与自动恢复 | 设计输入 | 同上;当前已有基础 `workflow` action,不应误写为完全缺失 |
|
|
41
41
|
|
|
42
|
+
> 本地 Loop Operator Console 已在 `docs/design/local-operator-console-from-pi-web.md` 作为独立设计输入:单仓库、loopback、随 `@tea-agent/loop-agent` 同包发布、canonical mutation 只经 sibling CLI;它不是本表中的远端 Web Console,也不能把远端多租户/云编排需求偷渡进本地 MVP。
|
|
43
|
+
|
|
42
44
|
> 注意:`ai_workspace/loop-agent/design/dynamic-workflow-dag-engine-roadmap.md` 是 2026-07-04 历史叙述;文中凡把 Cursor 写成受治理 executor 或 `loop` 的 `cursor-fix` 动作,均为**历史叙述**,现状以 Pi-only + 显式 `cursor-prompt` sidecar 为准。
|
|
43
45
|
|
|
44
46
|
## 已收敛为 archive / 历史基线(非未来)
|
|
@@ -45,6 +45,10 @@ git config commit.gpgsign false
|
|
|
45
45
|
|
|
46
46
|
该设置只用于本地开发和测试稳定性,不应写入仓库代码、模板或发布包配置。
|
|
47
47
|
|
|
48
|
+
## Windows clean Shell 与用户级 Python
|
|
49
|
+
|
|
50
|
+
DAG Shell/verification 默认使用安全的 clean environment,不继承完整宿主环境。在 Windows 上,clean baseline 会保留 `APPDATA`,使 Python/pip 能按标准规则定位 `%APPDATA%/Python/PythonXY/site-packages` 中的用户级安装,同时 token、credential 与其他未授权变量仍被过滤。不要在仓库中硬编码用户目录、盘符、Python 版本或 `site-packages` 路径;业务环境变量仍应通过任务/DAG 的显式 `envAllowlist` 授权。
|
|
51
|
+
|
|
48
52
|
## 维护规则
|
|
49
53
|
|
|
50
54
|
- 仅把可复现、会反复影响开发或验证的环境问题写入本文。
|
|
@@ -45,6 +45,8 @@ Your job is to convert reviewed test cases under `testcase/md/` into pytest auto
|
|
|
45
45
|
|
|
46
46
|
Do NOT re-read source documents for free-form analysis. Use only reviewed cases and the validated contracts. Use only fixture/env/testRoot facts already present in the execution contract; never invent production credentials or secret values.
|
|
47
47
|
|
|
48
|
+
When `targetMode` is `in-process` (including demoted local npm/node managed servers): bootstrap the service inside function-scoped pytest fixtures under `testcase/**` (for example subprocess `node server.js` / `startWelcomeServer` with `PORT=0`). Never require host-injected base URL env vars such as `WELCOME_BASE_URL` / `API_BASE_URL` — the clean-env pytest shell will not provide them.
|
|
49
|
+
|
|
48
50
|
### Conversion Rules
|
|
49
51
|
|
|
50
52
|
#### File Naming
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"Root artifacts/ is reserved for explicit exclusive write nodes, not read-only scout/reviewer output",
|
|
29
29
|
"exclusive implementer nodes must use narrow, concrete writeSet paths; never keep ** or repo root",
|
|
30
30
|
"Replace REPLACE/WITH/NARROW/IMPLEMENT/PATHS/** with concrete paths before executing the implementation writer",
|
|
31
|
-
"backend-test-dag uses exactly
|
|
31
|
+
"backend-test-dag uses exactly 16 real top-level tasks and executes pytest exactly once.",
|
|
32
32
|
"Case and semantic request-revision verdicts fail at deterministic gates; no in-run revision or repair writer is authorized.",
|
|
33
33
|
"Analysis, execution, manifest, semantic review, single-run result, classification, canonical result, retrospective and outcome evidence remain run-owned and fail-closed.",
|
|
34
34
|
"Functional test case IDs must use BE-<MODULE>-<NNN> format.",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"artifacts/**"
|
|
91
91
|
],
|
|
92
92
|
"outputContract": "Pure JSON envelope {analysis: Backend Test Analysis v2, execution: Backend Test Execution Contract v1}; no prose or writes.",
|
|
93
|
-
"subtask_prompt": "Read the task source materials and return exactly one JSON object matching Backend Test Analysis v2.\n\nDo not wrap it in explanatory prose. A single fenced json block is tolerated, but pure JSON is preferred.\n\nCopy the sourceBinding object exactly from the JSON block below; do not infer, add, remove, or reclassify source paths.\n\nOnly kind=reference sources belong in referencePaths; kind=constraint sources MUST NOT be included in referencePaths.\n\n## Exact Backend Test Analysis sourceBinding JSON\n\n{\n \"taskId\": \"backend-test-template\",\n \"requirementPath\": \"source/需求.md\",\n \"requirementSha256\": \"e33ab9d1d3d6a785b8f429d8581d015a299adc093b0c9a7c0f9057f968a71837\",\n \"referencePaths\": [],\n \"requirementIds\": [\n \"AC-001\"\n ]\n}\n\nFor every endpoint, explicitly set responseBody.kind=array|object|scalar|empty|unknown and ordering=specified|unspecified|not-applicable. Add itemSchemaRef for arrays when documented.\n\nFor response fields, use comparison=exact|parseable-only|semantic when the source defines assertion semantics; date-time fields whose precision is unspecified should use parseable-only, not string equality.\n\nEndpoint sourceRefs and field sourceRefs must cite only requirement/reference evidence actually read. Empty sourceRefs are allowed only when normalizing legacy v1 input; newly generated v2 should cite evidence.\n\nUse empty arrays for categories not documented. Never include credentials, tokens, private keys, or secret values.\n\nRequired top-level keys: schemaVersion=2, sourceBinding, acceptanceCriteria, endpoints, dataModels, businessRules, stateTransitions, boundaryConstraints, externalDependencies, risks, evidenceGaps.\n\nRead-only: do not modify code, docs, artifacts, or repository files.\n\n## Task source: 需求.md\n\n# Backend test\n- AC-001 proof\n\n## Task config summary\n\n- taskId: backend-test-template\n\n- flow: auto\n\n- complexity: medium\n\n- contextProfile: full\n\n- allowedPaths: testcase/**, docs/test-reports/**\n\n- forbiddenPaths: (none)\n\n- Pi DAG nodes are read-only unless toolProfile=\"write\" is explicitly selected for a bounded writer node.\n\n- Agent DAG read-only nodes must not write root artifacts/**; root artifacts/ is not a per-node scratchpad.\n\n- source/references/* are immutable user/source facts; source/需求.md is the derived execution contract.\n\nAlso perform the read-only environment discovery described by Backend Test Execution Contract v1. Return exactly one JSON envelope with top-level keys analysis and execution; analysis must satisfy v2 and execution must satisfy v1.",
|
|
93
|
+
"subtask_prompt": "Read the task source materials and return exactly one JSON object matching Backend Test Analysis v2.\n\nDo not wrap it in explanatory prose. A single fenced json block is tolerated, but pure JSON is preferred.\n\nCopy the sourceBinding object exactly from the JSON block below; do not infer, add, remove, or reclassify source paths.\n\nOnly kind=reference sources belong in referencePaths; kind=constraint sources MUST NOT be included in referencePaths.\n\n## Exact Backend Test Analysis sourceBinding JSON\n\n{\n \"taskId\": \"backend-test-template\",\n \"requirementPath\": \"source/需求.md\",\n \"requirementSha256\": \"e33ab9d1d3d6a785b8f429d8581d015a299adc093b0c9a7c0f9057f968a71837\",\n \"referencePaths\": [],\n \"requirementIds\": [\n \"AC-001\"\n ]\n}\n\nFor every endpoint, explicitly set responseBody.kind=array|object|scalar|empty|unknown and ordering=specified|unspecified|not-applicable. Add itemSchemaRef for arrays when documented.\n\nFor response fields, use comparison=exact|parseable-only|semantic when the source defines assertion semantics; date-time fields whose precision is unspecified should use parseable-only, not string equality.\n\nEndpoint sourceRefs and field sourceRefs must cite only requirement/reference evidence actually read. Empty sourceRefs are allowed only when normalizing legacy v1 input; newly generated v2 should cite evidence.\n\nFor externalDependencies and risks, emit canonical items with exactly description plus optional name and sourceRef. For a dependency target, put the target value in name. Do not emit type, target, kind, required, severity, mitigation, level, impact, sourceRefs, or custom keys in newly generated v2 output.\n\nUse empty arrays for categories not documented. Never include credentials, tokens, private keys, or secret values.\n\nRequired top-level keys: schemaVersion=2, sourceBinding, acceptanceCriteria, endpoints, dataModels, businessRules, stateTransitions, boundaryConstraints, externalDependencies, risks, evidenceGaps.\n\nRead-only: do not modify code, docs, artifacts, or repository files.\n\n## Task source: 需求.md\n\n# Backend test\n- AC-001 proof\n\n## Task config summary\n\n- taskId: backend-test-template\n\n- flow: auto\n\n- complexity: medium\n\n- contextProfile: full\n\n- allowedPaths: testcase/**, docs/test-reports/**\n\n- forbiddenPaths: (none)\n\n- Pi DAG nodes are read-only unless toolProfile=\"write\" is explicitly selected for a bounded writer node.\n\n- Agent DAG read-only nodes must not write root artifacts/**; root artifacts/ is not a per-node scratchpad.\n\n- source/references/* are immutable user/source facts; source/需求.md is the derived execution contract.\n\nAlso perform the read-only environment discovery described by Backend Test Execution Contract v1. Return exactly one JSON envelope with top-level keys analysis and execution; analysis must satisfy v2 and execution must satisfy v1.",
|
|
94
94
|
"retryPolicy": {
|
|
95
95
|
"maxAttempts": 3,
|
|
96
96
|
"backoff": "exponential",
|
|
@@ -280,7 +280,7 @@
|
|
|
280
280
|
".harness/dag-runs/**",
|
|
281
281
|
"artifacts/**"
|
|
282
282
|
],
|
|
283
|
-
"subtask_prompt": "Convert the reviewed test cases under testcase/md/ into pytest automation code.\n\n\n\n## Inputs (MUST use validated contracts):\n\n- Reviewed cases under testcase/md/ (after review-backend-cases-gate-shell).\n\n- Validated Backend Test Analysis v1 under the current run contracts/ (analysis gate).\n\n- Validated Backend Test Execution Contract v1 under contracts/backend-test-execution.json (execution gate).\n\nUse only fixture names, env NAMES, testRoot, targetMode, and field/API facts already present in those contracts or reviewed cases. Do not invent production credentials or secret values.\n\n\n\n## Output Steps (do in order):\n\n1. First, output a brief summary: how many files, how many test functions planned\n\n2. Then write each test file under testcase/\n\n\n\n## Format Rules:\n\n- File prefix: test_<module>.py\n\n- Function name: test_BE_<MODULE>_<NNN>_<description>\n\n- Docstring first line: BE-<MODULE>-<NNN>: <Case Title>\n\n- 1:1 mapping: each functional case → one pytest function\n\n\n\n## Implementation Rules:\n\n- Use assert statements, not unittest assertions\n\n- Use @pytest.mark.parametrize for boundary cases when the case defines edge values\n\n- Use markers: @pytest.mark.positive, @pytest.mark.negative, @pytest.mark.boundary\n\n\n\n## Test Data Preparation Rules (MUST follow):\n\n\n\n### When Setup is Needed\n\nSetup phase is REQUIRED only when test cases need pre-existing data:\n\n- Query/Read APIs: need data to exist before querying\n\n- Update/Delete APIs: need data to exist before modifying\n\n- State transition tests: need data in specific state\n\n\n\nSetup phase is NOT needed for:\n\n- Create APIs: testing the creation itself\n\n- Validation tests: testing input validation with invalid data\n\n\n\n### Data Setup Strategy\n\nWhen setup is needed:\n\n1. Prefer function-scoped fixtures for isolation; use module/session scope only when cases explicitly share immutable fixtures\n\n2. Prefer API-based setup from the upstream analyze-inputs-pi API list and reviewed cases\n\n3. If a required helper/factory is missing, create NEW files only under testcase/**/helpers/** or testcase/**/factories/**\n\n\n\n### Data Construction Priority\n\n1. API-first: construct data via documented APIs from analyze-inputs-pi / reviewed cases\n\n2. Reuse existing conftest fixtures when present (read-only)\n\n3. Direct DB writes are LAST RESORT and only if conftest already exposes a safe test DB fixture with rollback/isolation\n\n4. If neither API nor safe DB fixture exists, skip the case with an explicit gap note — do NOT invent production DB credentials or write live data\n\n\n\n### API Data Construction\n\n- Prefer the analyze-inputs-pi API Endpoints section and reviewed cases for method/path/fields\n\n- Chain API calls only when cases document multi-step preconditions\n\n- Store created resource IDs in fixtures for reuse\n\n- Do NOT broadly search host route/controller trees for secrets, .env, private keys, or production configs\n\n- Read host API definitions only when needed to resolve a field name already referenced by reviewed cases; stay out of credential/config paths\n\n\n\n### Database Data Construction (restricted)\n\n- Allowed only via existing conftest test-DB fixtures with transaction rollback or equivalent isolation\n\n- Never hardcode connection strings, passwords, tokens, or cloud credentials\n\n- Never target production/shared non-test databases\n\n- If isolation is unclear, report the gap instead of writing DB rows\n\n\n\n## Assertion Rules (MUST follow):\n\n\n\n### Positive Path\n\nMUST assert ALL of the following:\n\n1. HTTP status code: as defined in API spec (e.g. 200, 201)\n\n2. Response structure: key fields exist in response body\n\n3. Specific values: each field equals expected value from test case\n\n4. Data type: each field is correct type\n\n\n\n### Negative Path\n\nMUST assert ALL of the following:\n\n1. HTTP status code: as defined in API spec (e.g. 400, 404, 500)\n\n2. Error code field: field name from API spec (e.g. code, error_code, errcode, ret)\n\n3. Error message field: field name from API spec (e.g. message, msg, errmsg, error)\n\n\n\n### Field Name Resolution\n\nField names MUST come from the upstream analyze-inputs-pi output (API Endpoints section) or reviewed cases, NOT guessed. For example:\n\n- If API spec defines {\"ret\": 0, \"msg\": \"success\"}, assert response.json()['ret'] and response.json()['msg']\n\n- If API spec defines {\"code\": 4001, \"message\": \"error\"}, assert response.json()['code'] and response.json()['message']\n\n\n\n## Conditional Implementation (include ONLY if test cases exist):\n\n- Authentication tests: implement ONLY if testcase/md/ contains auth-related cases\n\n- Timeout tests: implement ONLY if testcase/md/ contains timeout-related cases\n\n- Boundary tests: implement ONLY when cases define value ranges, length limits, or format constraints\n\n- Use @pytest.mark.auth for auth tests, @pytest.mark.timeout for timeout tests\n\n- If no such cases exist, do NOT add these tests\n\n\n\n## Constraints:\n\n- Only create NEW files under writeSet: testcase/**/test_*.py, testcase/**/helpers/**, testcase/**/factories/**\n\n- Do NOT modify existing framework files (conftest.py, pytest.ini, pyproject.toml, setup.cfg, __init__.py)\n\n- If a test filename exists, add suffix: test_order.py → test_order_01.py\n\n- Do NOT re-read source documents — use reviewed cases under testcase/md/ and upstream analyze-inputs-pi output only\n\n- Read existing conftest.py/pytest.ini to understand conventions, but do NOT modify them\n\n- Do NOT execute pytest/python -m pytest or npm test in this node; the single execution is owned by the dedicated shell node. Local smoke runs create __pycache__/.pytest_cache and are unnecessary here."
|
|
283
|
+
"subtask_prompt": "Convert the reviewed test cases under testcase/md/ into pytest automation code.\n\n\n\n## Inputs (MUST use validated contracts):\n\n- Reviewed cases under testcase/md/ (after review-backend-cases-gate-shell).\n\n- Validated Backend Test Analysis v1 under the current run contracts/ (analysis gate).\n\n- Validated Backend Test Execution Contract v1 under contracts/backend-test-execution.json (execution gate).\n\nUse only fixture names, env NAMES, testRoot, targetMode, and field/API facts already present in those contracts or reviewed cases. Do not invent production credentials or secret values.\n\nWhen targetMode is in-process (including demoted local npm/node managed servers): bootstrap the service inside function-scoped pytest fixtures under testcase/** — e.g. subprocess node server.js / startWelcomeServer with PORT=0 — and never require host-injected base URL env vars (clean-env shell will not provide WELCOME_BASE_URL / API_BASE_URL).\n\nDo not depend on requiredEnvNames being present at process start for in-process mode; if the contract still lists an env name, the fixture must set it or start the server without that env.\n\n\n\n## Output Steps (do in order):\n\n1. First, output a brief summary: how many files, how many test functions planned\n\n2. Then write each test file under testcase/\n\n\n\n## Format Rules:\n\n- File prefix: test_<module>.py\n\n- Function name: test_BE_<MODULE>_<NNN>_<description>\n\n- Docstring first line: BE-<MODULE>-<NNN>: <Case Title>\n\n- 1:1 mapping: each functional case → one pytest function\n\n\n\n## Implementation Rules:\n\n- Use assert statements, not unittest assertions\n\n- Use @pytest.mark.parametrize for boundary cases when the case defines edge values\n\n- Use markers: @pytest.mark.positive, @pytest.mark.negative, @pytest.mark.boundary\n\n\n\n## Test Data Preparation Rules (MUST follow):\n\n\n\n### When Setup is Needed\n\nSetup phase is REQUIRED only when test cases need pre-existing data:\n\n- Query/Read APIs: need data to exist before querying\n\n- Update/Delete APIs: need data to exist before modifying\n\n- State transition tests: need data in specific state\n\n\n\nSetup phase is NOT needed for:\n\n- Create APIs: testing the creation itself\n\n- Validation tests: testing input validation with invalid data\n\n\n\n### Data Setup Strategy\n\nWhen setup is needed:\n\n1. Prefer function-scoped fixtures for isolation; use module/session scope only when cases explicitly share immutable fixtures\n\n2. Prefer API-based setup from the upstream analyze-inputs-pi API list and reviewed cases\n\n3. If a required helper/factory is missing, create NEW files only under testcase/**/helpers/** or testcase/**/factories/**\n\n\n\n### Data Construction Priority\n\n1. API-first: construct data via documented APIs from analyze-inputs-pi / reviewed cases\n\n2. Reuse existing conftest fixtures when present (read-only)\n\n3. Direct DB writes are LAST RESORT and only if conftest already exposes a safe test DB fixture with rollback/isolation\n\n4. If neither API nor safe DB fixture exists, skip the case with an explicit gap note — do NOT invent production DB credentials or write live data\n\n\n\n### API Data Construction\n\n- Prefer the analyze-inputs-pi API Endpoints section and reviewed cases for method/path/fields\n\n- Chain API calls only when cases document multi-step preconditions\n\n- Store created resource IDs in fixtures for reuse\n\n- Do NOT broadly search host route/controller trees for secrets, .env, private keys, or production configs\n\n- Read host API definitions only when needed to resolve a field name already referenced by reviewed cases; stay out of credential/config paths\n\n\n\n### Database Data Construction (restricted)\n\n- Allowed only via existing conftest test-DB fixtures with transaction rollback or equivalent isolation\n\n- Never hardcode connection strings, passwords, tokens, or cloud credentials\n\n- Never target production/shared non-test databases\n\n- If isolation is unclear, report the gap instead of writing DB rows\n\n\n\n## Assertion Rules (MUST follow):\n\n\n\n### Positive Path\n\nMUST assert ALL of the following:\n\n1. HTTP status code: as defined in API spec (e.g. 200, 201)\n\n2. Response structure: key fields exist in response body\n\n3. Specific values: each field equals expected value from test case\n\n4. Data type: each field is correct type\n\n\n\n### Negative Path\n\nMUST assert ALL of the following:\n\n1. HTTP status code: as defined in API spec (e.g. 400, 404, 500)\n\n2. Error code field: field name from API spec (e.g. code, error_code, errcode, ret)\n\n3. Error message field: field name from API spec (e.g. message, msg, errmsg, error)\n\n\n\n### Field Name Resolution\n\nField names MUST come from the upstream analyze-inputs-pi output (API Endpoints section) or reviewed cases, NOT guessed. For example:\n\n- If API spec defines {\"ret\": 0, \"msg\": \"success\"}, assert response.json()['ret'] and response.json()['msg']\n\n- If API spec defines {\"code\": 4001, \"message\": \"error\"}, assert response.json()['code'] and response.json()['message']\n\n\n\n## Conditional Implementation (include ONLY if test cases exist):\n\n- Authentication tests: implement ONLY if testcase/md/ contains auth-related cases\n\n- Timeout tests: implement ONLY if testcase/md/ contains timeout-related cases\n\n- Boundary tests: implement ONLY when cases define value ranges, length limits, or format constraints\n\n- Use @pytest.mark.auth for auth tests, @pytest.mark.timeout for timeout tests\n\n- If no such cases exist, do NOT add these tests\n\n\n\n## Constraints:\n\n- Only create NEW files under writeSet: testcase/**/test_*.py, testcase/**/helpers/**, testcase/**/factories/**\n\n- Do NOT modify existing framework files (conftest.py, pytest.ini, pyproject.toml, setup.cfg, __init__.py)\n\n- If a test filename exists, add suffix: test_order.py → test_order_01.py\n\n- Do NOT re-read source documents — use reviewed cases under testcase/md/ and upstream analyze-inputs-pi output only\n\n- Read existing conftest.py/pytest.ini to understand conventions, but do NOT modify them\n\n- Do NOT execute pytest/python -m pytest or npm test in this node; the single execution is owned by the dedicated shell node. Local smoke runs create __pycache__/.pytest_cache and are unnecessary here."
|
|
284
284
|
},
|
|
285
285
|
{
|
|
286
286
|
"id": "review-generated-backend-pytest-pi",
|
|
@@ -395,7 +395,7 @@
|
|
|
395
395
|
"subtask_prompt": "Run pytest for the backend test suite; write JUnit + pytestExitCode evidence only under the current HARNESS_DAG_RUN_DIR/reports/.",
|
|
396
396
|
"shell": {
|
|
397
397
|
"commands": [
|
|
398
|
-
"test -n \"${HARNESS_DAG_RUN_DIR:-}\" || { echo \"missing HARNESS_DAG_RUN_DIR for backend pytest preflight\" >&2; exit 2; }
|
|
398
|
+
"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(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-initial-junit.xml\"; EXIT_FILE=\"${HARNESS_DAG_RUN_DIR}/reports/backend-test-initial-pytest-exit.txt\"; mkdir -p \"$(dirname \"${REPORT}\")\"; PYTHONUTF8=1 PYTHONIOENCODING=utf-8 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}\"; }"
|
|
399
399
|
],
|
|
400
400
|
"envAllowlist": [],
|
|
401
401
|
"verifyEvidence": {
|
|
@@ -404,7 +404,7 @@
|
|
|
404
404
|
"commandSource": "inline",
|
|
405
405
|
"commandCount": 1,
|
|
406
406
|
"commandLabels": [
|
|
407
|
-
"test -n \"${HARNESS_DAG_RUN_DIR:-}\" || { echo \"missing HARNESS_DAG_RUN_DIR for backend pytest preflight\" >&2; exit 2; }
|
|
407
|
+
"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(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-initial-junit.xml\"; EXIT_FILE=\"${HARNESS_DAG_RUN_DIR}/reports/backend-test-initial-pytest-exit.txt\"; mkdir -p \"$(dirname \"${REPORT}\")\"; PYTHONUTF8=1 PYTHONIOENCODING=utf-8 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}\"; }"
|
|
408
408
|
],
|
|
409
409
|
"finalFullRequired": true
|
|
410
410
|
},
|
|
@@ -496,13 +496,46 @@
|
|
|
496
496
|
"artifacts/**"
|
|
497
497
|
],
|
|
498
498
|
"outputContract": "Maturity rating in assistant output plus a report written under docs/test-reports/**.",
|
|
499
|
-
"subtask_prompt": "Read the complete JSON from direct upstream materialize-classification-and-result-context-shell and generate a test retrospective report.\n\nThat JSON contains result, manifest (including coverageSummary), and classification. Treat those fields as authoritative; do not rely on pointer/hash summaries.\n\n\n\n## Output Steps (do in order):\n\n1. First, output the maturity rating on the first line: Rating: A/B/C/D\n\n2. Then write the full report under docs/test-reports/\n\n\n\n## Stats authority (deterministic only):\n\n- Pass rate, failed/error/skipped counts, and failure list MUST come from contracts/backend-test-result.json only.\n\n- AC coverage ratio / case counts MUST come from contracts/backend-test-case-manifest.json coverageSummary (or gate-derived fields). Do NOT invent coverage %.\n\n- Use classify-backend-test-result-pi JSON as interpretive evidence only.\n\n- NEVER rewrite a failed result as passed. Outcome gate (not this report) is authoritative for task success.\n\n\n\n## Report Structure:\n\n1. Maturity Rating with rationale\n\n2. Test Coverage Summary (
|
|
499
|
+
"subtask_prompt": "Read the complete JSON from direct upstream materialize-classification-and-result-context-shell and generate a test retrospective report.\n\nThat JSON contains result, manifest (including coverageSummary), and classification. Treat those fields as authoritative; do not rely on pointer/hash summaries.\n\n\n\n## Output Steps (do in order):\n\n1. First, output the maturity rating on the first line: Rating: A/B/C/D\n\n2. Then write the full report under docs/test-reports/\n\n\n\n## Stats authority (deterministic only):\n\n- Pass rate, failed/error/skipped counts, and failure list MUST come from contracts/backend-test-result.json only.\n\n- AC coverage ratio / case counts MUST come from contracts/backend-test-case-manifest.json coverageSummary (or gate-derived fields). Do NOT invent coverage %.\n\n- Automation coverage MUST use coverageSummary.generatedCount / coverageSummary.caseCount. If either field is missing, write unavailable; do not estimate.\n\n- Code coverage MUST come only from the validated contracts/code-coverage-v1.json artifact generated by coverage.py/pytest-cov or JaCoCo. Show line, branch, function/method, covered, total, ratio, threshold, status, source scope, requirement IDs, tool, commit, and artifact hash.\n\n- Stability MUST come from independent Stability Evidence: use successfulRuns / recordedRuns, same suite/version, and require n≥5; a single run is unavailable.\n\n- Use classify-backend-test-result-pi JSON as interpretive evidence only.\n\n- NEVER rewrite a failed result as passed. Outcome gate (not this report) is authoritative for task success.\n\n\n\n## Report Structure:\n\n1. Maturity Rating with rationale\n\n2. Test Coverage Summary (Result v1 pass rate, AC coverage, automation coverage, code coverage, and stability evidence)\n\n3. Failed Test Analysis (failure/error details, category, confidence, evidence, and owner direction)\n\n4. Defects (local Bug ledger in the same report directory; unavailable when absent)\n\n5. Risks (Critical/High/Medium/Low, impact, controls, residual risk, treatment; Critical risks block L-5, High risks do not automatically block)\n\n6. Regression Recommendations (immediate, related, periodic, deferred; every item links to failure/risk/AC/case IDs)\n\n7. L-5 conclusion with blocking items\n\n\n\n## Rating Criteria:\n\n- L-5 ready requires pass rate=100%, AC coverage=100%, automation coverage≥90%, stability≥95% with n≥5, line coverage≥80%, branch coverage≥70%, skipped=0, and no blocking Critical risk.\n\n- Any required metric fail or unavailable means L-5 not-ready. Function/method coverage is displayed but not a gate. Preserve the existing A/B/C/D single-run rating separately.\n\n\n\n## Constraints:\n\n- Stay within writeSet: docs/test-reports/**\n\n- Do NOT re-read source documents — use upstream outputs only\n\n- Do not write root artifacts/**"
|
|
500
500
|
},
|
|
501
501
|
{
|
|
502
|
-
"id": "
|
|
502
|
+
"id": "l5-metrics-pi",
|
|
503
503
|
"depends_on": [
|
|
504
504
|
"test-retrospect-pi"
|
|
505
505
|
],
|
|
506
|
+
"role": "reviewer",
|
|
507
|
+
"executor": "pi",
|
|
508
|
+
"complexity": "MED",
|
|
509
|
+
"writePolicy": "read-only",
|
|
510
|
+
"allowedPaths": [
|
|
511
|
+
"testcase/**",
|
|
512
|
+
"docs/test-reports/**"
|
|
513
|
+
],
|
|
514
|
+
"forbiddenPaths": [
|
|
515
|
+
".harness/**",
|
|
516
|
+
".harness/dag-runs/**",
|
|
517
|
+
"artifacts/**"
|
|
518
|
+
],
|
|
519
|
+
"outputContract": "Exactly one JSON object with status=ready|not-ready, metrics, and blockingItems; no file writes.",
|
|
520
|
+
"subtask_prompt": "You are the independent L-5 metrics node at the end of the existing backend-test DAG.\n\nThe direct upstream test-retrospect-pi output is the primary report to assess. Read it together with the run-owned Result v1, Case Manifest v1, Code Coverage v1, and Stability Evidence artifacts when present.\n\nDo not create a new DAG, rewrite the retrospective report, change test outcome, or modify any repository file.\n\nReturn exactly one JSON object and no surrounding prose.\n\nRequired shape: {\"status\":\"ready\"|\"not-ready\",\"metrics\":{\"passRate\":metric,\"acCoverage\":metric,\"automationCoverage\":metric,\"stability\":metric,\"lineCoverage\":metric,\"branchCoverage\":metric,\"skipped\":metric,\"criticalRisks\":metric},\"blockingItems\":[string]}.\n\nEach metric must contain numerator, denominator, ratio, threshold, status=pass|fail|unavailable, and reason (null only when passed).\n\nUse only explicit evidence. Missing or invalid required evidence is unavailable, never zero or an estimate.\n\nL-5 ready requires pass rate=100%, AC coverage=100%, automation coverage>=90%, stability>=95% with n>=5, line coverage>=80%, branch coverage>=70%, skipped=0, and zero blocking Critical risks.\n\nFunction/method coverage is display-only and does not gate L-5. Preserve the distinction between L-5 maturity and the Result v1 outcome gate.",
|
|
521
|
+
"retryPolicy": {
|
|
522
|
+
"maxAttempts": 3,
|
|
523
|
+
"backoff": "exponential",
|
|
524
|
+
"initialDelayMs": 2000,
|
|
525
|
+
"maxDelayMs": 30000,
|
|
526
|
+
"retryCategories": [
|
|
527
|
+
"timeout",
|
|
528
|
+
"network",
|
|
529
|
+
"rate-limit",
|
|
530
|
+
"unavailable"
|
|
531
|
+
]
|
|
532
|
+
}
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
"id": "backend-test-outcome-gate-shell",
|
|
536
|
+
"depends_on": [
|
|
537
|
+
"l5-metrics-pi"
|
|
538
|
+
],
|
|
506
539
|
"role": "verifier",
|
|
507
540
|
"executor": "shell",
|
|
508
541
|
"complexity": "LOW",
|
|
@@ -46,7 +46,9 @@ This node runs on **both pass and assertion-fail** paths (after parse + classify
|
|
|
46
46
|
Use `coverageSummary.acCoverageRatio`, `coveredAcCount`, `explicitAcCount`, case counts only from this artifact.
|
|
47
47
|
3. **Classification** — `classify-backend-test-result-pi` JSON (`category`, `confidence`, `evidence`). Interpretive only; does not override outcome.
|
|
48
48
|
4. **Review report** — `review-backend-cases-pi` output (VERDICT, findings, coverage assessment).
|
|
49
|
-
5.
|
|
49
|
+
5. **Code Coverage v1** — optional validated `contracts/code-coverage-v1.json`, generated by coverage.py/pytest-cov or JaCoCo. Never infer it from Result v1.
|
|
50
|
+
6. **Stability Evidence** — optional independent contract for repeated runs of the same suite and version.
|
|
51
|
+
7. Optional secondary: execute stdout markers / JUnit path (do not re-parse logs for counts when Result v1 exists).
|
|
50
52
|
|
|
51
53
|
Do NOT re-read source documents. Use upstream outputs only.
|
|
52
54
|
|
|
@@ -54,6 +56,9 @@ Do NOT re-read source documents. Use upstream outputs only.
|
|
|
54
56
|
|
|
55
57
|
- Pass rate = `passed / (passed + failed + error)` when denominator > 0 (skipped excluded from denominator unless Result documents otherwise) — **Result v1 only**.
|
|
56
58
|
- AC coverage = `coverageSummary.acCoverageRatio` from Case Manifest v1 only (do **not** recompute or invent percentages).
|
|
59
|
+
- Automation coverage = `coverageSummary.generatedCount / coverageSummary.caseCount`; if either field is missing, output `unavailable`.
|
|
60
|
+
- Code coverage = Code Coverage v1 only. Display line (gate ≥80%), branch (gate ≥70%), and function/method (display only), each with covered, total, ratio, status, reason, source scope, requirement IDs, tool/version, commit and artifact SHA-256. Missing artifacts or line/branch metrics are `unavailable` and block L-5.
|
|
61
|
+
- Stability = Stability Evidence `successfulRuns / recordedRuns`, same suite and version, minimum `n≥5`; one run is `unavailable` and cannot prove `FlakyTest`.
|
|
57
62
|
- Failed case table rows must match `failures[]` from Result v1.
|
|
58
63
|
- If Result v1 `outcome` is not `passed`, the retrospective **must not** claim overall success.
|
|
59
64
|
|
|
@@ -74,6 +79,7 @@ Do NOT re-read source documents. Use upstream outputs only.
|
|
|
74
79
|
- If Result v1 shows >30% failed+error among executed tests, cap at **D** regardless of coverage.
|
|
75
80
|
- Skipped tests count as "not covered" for pass rate but not as failures.
|
|
76
81
|
- Collection/command/report errors → cap at **D** and record classification (not ProductBug by default).
|
|
82
|
+
- The downstream `l5-metrics-pi` node owns the separate L-5 conclusion. This retrospective must provide the authoritative evidence and risks, but must not replace the downstream L-5 JSON conclusion.
|
|
77
83
|
|
|
78
84
|
### Report Structure
|
|
79
85
|
|
|
@@ -95,6 +101,15 @@ Write the report as a Markdown file named `backend-test-retrospect-<date>.md` un
|
|
|
95
101
|
| Total acceptance criteria | N |
|
|
96
102
|
| Covered by test cases | N (X%) |
|
|
97
103
|
| Total functional test cases | N |
|
|
104
|
+
| Automated cases / total cases | N / N (X%) or unavailable |
|
|
105
|
+
| Code line coverage | N / N (X%), threshold ≥80%, status |
|
|
106
|
+
| Code branch coverage | N / N (X%), threshold ≥70%, status |
|
|
107
|
+
| Code function/method coverage | N / N (X%) or unavailable |
|
|
108
|
+
| Stability | successfulRuns / recordedRuns (X%), n, or unavailable |
|
|
109
|
+
|
|
110
|
+
Code coverage must include language, tool/version, requirement IDs, source scope, commit and artifact SHA-256.
|
|
111
|
+
|
|
112
|
+
The downstream `l5-metrics-pi` node consumes this report and the run-owned contracts to calculate the independent L-5 conclusion.
|
|
98
113
|
|
|
99
114
|
## 2. Automation Results (from Result v1)
|
|
100
115
|
|
|
@@ -112,21 +127,35 @@ Write the report as a Markdown file named `backend-test-retrospect-<date>.md` un
|
|
|
112
127
|
|
|
113
128
|
### Failed Test Analysis
|
|
114
129
|
|
|
115
|
-
| Test Case / Function |
|
|
116
|
-
|
|
117
|
-
| ... | ... | ... |
|
|
130
|
+
| Test Case / Function | Kind | Classification | Confidence | Evidence | Owner direction |
|
|
131
|
+
|----------------------|------|----------------|------------|----------|----------------|
|
|
132
|
+
| ... | ... | ... | ... | ... | ... |
|
|
133
|
+
|
|
134
|
+
## 3. Defects
|
|
135
|
+
|
|
136
|
+
Use the local Bug ledger in the same directory as the report. Include defect ID, title, AC/case, severity, priority, status, reproduction, evidence, category, owner, fix version and regression status. If absent, write `缺陷登记:unavailable`.
|
|
137
|
+
|
|
138
|
+
## 4. Risks
|
|
139
|
+
|
|
140
|
+
| Risk ID | Level | Impact | Likelihood | Current control | Residual risk | Treatment |
|
|
141
|
+
|---------|-------|--------|------------|-----------------|---------------|-----------|
|
|
142
|
+
| ... | Critical/High/Medium/Low | ... | ... | ... | ... | ... |
|
|
143
|
+
|
|
144
|
+
## 5. Regression Recommendations
|
|
145
|
+
|
|
146
|
+
Every recommendation must reference a failure/risk/AC/case ID and include target suite, priority and verification command.
|
|
118
147
|
|
|
119
|
-
##
|
|
148
|
+
## 6. Review Findings
|
|
120
149
|
|
|
121
150
|
| Severity | Finding | Status |
|
|
122
151
|
|----------|---------|--------|
|
|
123
152
|
| … | … | … |
|
|
124
153
|
|
|
125
|
-
##
|
|
154
|
+
## 7. Maturity Rating Rationale
|
|
126
155
|
|
|
127
156
|
Explain which threshold was met or missed.
|
|
128
157
|
|
|
129
|
-
##
|
|
158
|
+
## 8. Recommendations
|
|
130
159
|
|
|
131
160
|
- Actionable items for the next iteration.
|
|
132
161
|
- Do not propose changing production code solely to greenwash tests.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Frontend-test case blocking checklist
|
|
2
|
+
|
|
3
|
+
Shared mechanical rules for `frontend-case-checklist-shell` and generate prompts.
|
|
4
|
+
LLM review (when `frontendTest.reviewMode=blocking`) must not invent blocking rules outside this list.
|
|
5
|
+
|
|
6
|
+
## Blocking (fail closed)
|
|
7
|
+
|
|
8
|
+
| ruleId | Rule |
|
|
9
|
+
|---|---|
|
|
10
|
+
| `open-prefix` | Each case body includes `playwright-cli open --browser=chrome --headed <absolute-http(s)-url>` |
|
|
11
|
+
| `production-url` | Open URL must not look like a production host |
|
|
12
|
+
| `ac-mapping` | Manifest entry has non-empty `acIds` |
|
|
13
|
+
| `case-file-missing` | `casePath` exists |
|
|
14
|
+
| `no-test-source` | Case text must not introduce pytest / Playwright test source (`pytest`, `playwright.test`, `@playwright/test`) |
|
|
15
|
+
|
|
16
|
+
## Non-blocking (notes only)
|
|
17
|
+
|
|
18
|
+
- Preferred extra evidence filenames not required by generate
|
|
19
|
+
- Style / wording preferences
|
|
20
|
+
- Additional network envelope proofs beyond capability matrix
|
|
21
|
+
|
|
22
|
+
## Pipeline vs quality
|
|
23
|
+
|
|
24
|
+
- **Pipeline acceptance**: final `testcase/frontend/reports/frontend-test-retrospect-*.md` exists after result materialize
|
|
25
|
+
- **Quality**: `frontend-test-result-v1.outcome=passed` with 0 blocked/failed (opt-in via `frontendTest.strictOutcomeGate`)
|
|
@@ -7,14 +7,15 @@
|
|
|
7
7
|
"agentRuntime": "pi-only",
|
|
8
8
|
"repairWriterProtocol": "explicit-node-v1"
|
|
9
9
|
},
|
|
10
|
-
"objective": "Build a frontend test RAG package, generate Markdown cases, execute each case serially through playwright-cli, and
|
|
10
|
+
"objective": "Build a frontend test RAG package, generate Markdown cases, mechanically checklist them (LLM review optional), execute each case serially through playwright-cli, materialize frontend-test-result-v1, and write retrospect report (pipeline acceptance).",
|
|
11
11
|
"globalConstraints": [
|
|
12
12
|
"Do not generate pytest or Playwright source code.",
|
|
13
13
|
"Only use declared isolated test environments; production URLs and real credentials are blocked.",
|
|
14
14
|
"Every generated browser start command is playwright-cli open --browser=chrome --headed <resolved-base-url> (from task source config.md when present, else http://localhost:5173); subsequent commands stay in that default session and must not use unverified named-session flags.",
|
|
15
|
-
"A frontend case review must emit VERDICT: pass before manifest materialization; request-revision blocks browser execution.",
|
|
16
15
|
"Case children execute serially. Persist each case result, logs and browser evidence before the next child starts.",
|
|
17
|
-
"A token threshold is a post-case stop check, not a model hard token cap; unstarted cases must be recorded as blocked: token-budget-exhausted."
|
|
16
|
+
"A token threshold is a post-case stop check, not a model hard token cap; unstarted cases must be recorded as blocked: token-budget-exhausted.",
|
|
17
|
+
"Default pipeline acceptance is the final retrospect report under testcase/frontend/reports/; case full green is optional quality (frontendTest.strictOutcomeGate).",
|
|
18
|
+
"Default frontendTest.reviewMode=off uses mechanical checklist-shell before materialize; set reviewMode=blocking for legacy dual LLM review gate."
|
|
18
19
|
],
|
|
19
20
|
"tasks": [
|
|
20
21
|
{
|
package/package.json
CHANGED
|
@@ -374,6 +374,7 @@ loop-agent plan complete <plan-id> --summary "<summary>"
|
|
|
374
374
|
loop-agent plan check
|
|
375
375
|
loop-agent handoff check [task-id]
|
|
376
376
|
loop-agent handoff coverage <task-id> [--json|--markdown]
|
|
377
|
+
loop-agent coverage report --language python|java --input <coverage.json|jacoco.xml> --requirement-id <AC-id> --source-scope <path[,path]> [--output <path>] [--json|--markdown]
|
|
377
378
|
```
|
|
378
379
|
|
|
379
380
|
- `docs audit`:扫描文档腐化风险,如 active/completed 漂移、失效链接、host-gap closeout
|