@tea-agent/loop-agent 0.19.0 → 0.20.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.
- package/CHANGELOG.md +12 -0
- package/dist/application/dag/generate-task-dag.js +12 -1
- package/dist/executors/shell-executor.js +188 -2
- package/dist/workflows/dag/backend-test-markdown-workflow.js +163 -41
- package/dist/workflows/dag/backend-test-result-contract.js +30 -7
- package/dist/workflows/dag/frontend-prewrite-gate.js +77 -0
- package/dist/workflows/dag/frontend-repair.js +7 -1
- package/dist/workflows/dag/frontend-review-context.js +43 -0
- package/dist/workflows/dag/frontend-verification-trace.js +34 -15
- package/dist/workflows/dag/governance-profile.js +14 -6
- package/dist/workflows/dag/init-hybrid.js +143 -399
- package/dist/workflows/dag/types.js +30 -0
- package/dist/workflows/dag/validate.js +22 -1
- package/docs/README.md +1 -0
- package/docs/templates/agent-dag.schema.json +40 -0
- package/docs/templates/backend-test-dag.generate-pytest.prompt.md +23 -192
- package/docs/templates/backend-test-dag.json +8 -8
- package/docs/templates/backend-test-dag.review-cases.prompt.md +22 -75
- package/package.json +1 -1
- package/skills/frontend-implementation/references/node-contracts.md +6 -8
- package/skills/frontend-review/SKILL.md +5 -8
- package/skills/loop-agent/references/hybrid-dag.md +2 -2
|
@@ -113,6 +113,33 @@ export const dagJsonArtifactGateSchema = z.object({
|
|
|
113
113
|
outputDir: z.string().regex(/^[a-z0-9][a-z0-9._-]*$/),
|
|
114
114
|
junitRelativePath: z.string().min(1).optional(),
|
|
115
115
|
});
|
|
116
|
+
const dagFrontendNodeIdSchema = z
|
|
117
|
+
.string()
|
|
118
|
+
.regex(/^[a-z][a-z0-9-]*$/, "frontend node id must be kebab-case");
|
|
119
|
+
export const dagFrontendPrewriteGateSchema = z.object({
|
|
120
|
+
schemaVersion: z.literal(1),
|
|
121
|
+
planFromNodeId: dagFrontendNodeIdSchema,
|
|
122
|
+
planFallbackFromNodeIds: z.array(dagFrontendNodeIdSchema).default([]),
|
|
123
|
+
reviewFromNodeId: dagFrontendNodeIdSchema,
|
|
124
|
+
reviewFallbackFromNodeIds: z.array(dagFrontendNodeIdSchema).default([]),
|
|
125
|
+
requiredRequirementIds: z.array(z.string().regex(/^(?:REQ|BR|AC)-[A-Z0-9]+(?:-[A-Z0-9]+)*$/)).default([]),
|
|
126
|
+
allowedMockStrategies: z.array(z.enum(["native", "browser-intercept", "request-adapter", "not-needed"])).min(1),
|
|
127
|
+
artifactName: z.string().regex(/^[a-z0-9][a-z0-9._-]*\.json$/),
|
|
128
|
+
outputDir: z.string().regex(/^[a-z0-9][a-z0-9._-]*$/),
|
|
129
|
+
});
|
|
130
|
+
export const dagFrontendVerificationBundleSchema = z.object({
|
|
131
|
+
schemaVersion: z.literal(1),
|
|
132
|
+
mockCommands: z.array(z.string()).default([]),
|
|
133
|
+
staticCommands: z.array(z.string()).min(1),
|
|
134
|
+
behaviorCommands: z.array(z.string()).min(1),
|
|
135
|
+
mockEvidence: dagShellVerifyEvidenceSchema.optional(),
|
|
136
|
+
staticEvidence: dagShellVerifyEvidenceSchema,
|
|
137
|
+
behaviorEvidence: dagShellVerifyEvidenceSchema,
|
|
138
|
+
mode: z.enum(["initial", "repair"]),
|
|
139
|
+
});
|
|
140
|
+
export const dagFrontendReviewContextSchema = z.object({
|
|
141
|
+
schemaVersion: z.literal(1),
|
|
142
|
+
});
|
|
116
143
|
export const ENV_VAR_NAME_PATTERN = /^[A-Z_][A-Z0-9_]*$/;
|
|
117
144
|
export const dagVersionSchema = z
|
|
118
145
|
.union([z.literal(1), z.literal(2), z.literal(3), z.literal(4)])
|
|
@@ -191,6 +218,9 @@ export const dagShellConfigSchema = z.object({
|
|
|
191
218
|
}).strict().optional(),
|
|
192
219
|
requirementCoverageGate: dagRequirementCoverageGateSchema.optional(),
|
|
193
220
|
jsonArtifactGate: dagJsonArtifactGateSchema.optional(),
|
|
221
|
+
frontendPrewriteGate: dagFrontendPrewriteGateSchema.optional(),
|
|
222
|
+
frontendVerificationBundle: dagFrontendVerificationBundleSchema.optional(),
|
|
223
|
+
frontendReviewContext: dagFrontendReviewContextSchema.optional(),
|
|
194
224
|
backendTestPipeline: dagBackendTestPipelineSchema.optional(),
|
|
195
225
|
verifyEvidence: dagShellVerifyEvidenceSchema.optional(),
|
|
196
226
|
repairArtifactGate: dagRepairArtifactGateSchema.optional(),
|
|
@@ -456,7 +456,12 @@ function validateShellTaskConfig(task, spec, issues) {
|
|
|
456
456
|
return;
|
|
457
457
|
}
|
|
458
458
|
const commands = resolveShellCommands(shell);
|
|
459
|
-
if (commands.length === 0 &&
|
|
459
|
+
if (commands.length === 0 &&
|
|
460
|
+
!shell.jsonArtifactGate &&
|
|
461
|
+
!shell.backendTestPipeline &&
|
|
462
|
+
!shell.frontendPrewriteGate &&
|
|
463
|
+
!shell.frontendVerificationBundle &&
|
|
464
|
+
!shell.frontendReviewContext) {
|
|
460
465
|
issues.push({
|
|
461
466
|
type: "missing-shell-commands",
|
|
462
467
|
message: `shell task ${task.id} requires shell.preset, shell.verdictGate, shell.jsonArtifactGate, shell.backendTestPipeline, and/or non-empty shell.commands`,
|
|
@@ -517,6 +522,22 @@ function validateShellTaskConfig(task, spec, issues) {
|
|
|
517
522
|
}
|
|
518
523
|
}
|
|
519
524
|
}
|
|
525
|
+
if (shell.frontendPrewriteGate) {
|
|
526
|
+
const sourceNodeIds = [
|
|
527
|
+
shell.frontendPrewriteGate.planFromNodeId,
|
|
528
|
+
...shell.frontendPrewriteGate.planFallbackFromNodeIds,
|
|
529
|
+
shell.frontendPrewriteGate.reviewFromNodeId,
|
|
530
|
+
...shell.frontendPrewriteGate.reviewFallbackFromNodeIds,
|
|
531
|
+
];
|
|
532
|
+
for (const sourceNodeId of new Set(sourceNodeIds)) {
|
|
533
|
+
if (!task.depends_on.includes(sourceNodeId)) {
|
|
534
|
+
issues.push({
|
|
535
|
+
type: "missing-dependency",
|
|
536
|
+
message: `shell task ${task.id} frontendPrewriteGate source ${sourceNodeId} must be a direct dependency`,
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
520
541
|
validateRepairArtifactGateConfig(task, spec, issues);
|
|
521
542
|
validateShellVerdictGateGovernance(task, commands, issues);
|
|
522
543
|
}
|
package/docs/README.md
CHANGED
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
|
|
76
76
|
完整 completed 列表与主题速览见 `exec-plans/completed/README.md`。近期高频归档:
|
|
77
77
|
|
|
78
|
+
- `exec-plans/completed/2026-07-23-backend-test-human-readable-artifacts.md` — 中文 README/用例卡片、class-based pytest traceability 与逐条 self-contained HTML 报告;真实 `my-webapp` Campaign Round 04 8/8、16 passed
|
|
78
79
|
- `exec-plans/completed/2026-07-22-backend-markdown-gate-fix.md` — backend-test Markdown gate、traceability、中文生成与真实 Campaign 收口
|
|
79
80
|
- `exec-plans/completed/2026-07-22-backend-test-markdown-first-8-node.md` — backend-test Markdown-first 8 节点
|
|
80
81
|
- `exec-plans/completed/2026-07-22-backend-test-report-first-flow.md` — backend-test 报告优先 12 节点
|
|
@@ -303,6 +303,43 @@
|
|
|
303
303
|
"junitRelativePath": { "type": "string", "minLength": 1 }
|
|
304
304
|
}
|
|
305
305
|
},
|
|
306
|
+
"frontendPrewriteGate": {
|
|
307
|
+
"type": "object",
|
|
308
|
+
"additionalProperties": false,
|
|
309
|
+
"required": ["schemaVersion", "planFromNodeId", "reviewFromNodeId", "allowedMockStrategies", "artifactName", "outputDir"],
|
|
310
|
+
"properties": {
|
|
311
|
+
"schemaVersion": { "const": 1 },
|
|
312
|
+
"planFromNodeId": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$" },
|
|
313
|
+
"planFallbackFromNodeIds": { "type": "array", "items": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$" } },
|
|
314
|
+
"reviewFromNodeId": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$" },
|
|
315
|
+
"reviewFallbackFromNodeIds": { "type": "array", "items": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$" } },
|
|
316
|
+
"requiredRequirementIds": { "type": "array", "items": { "type": "string", "pattern": "^(?:REQ|BR|AC)-[A-Z0-9]+(?:-[A-Z0-9]+)*$" } },
|
|
317
|
+
"allowedMockStrategies": { "type": "array", "minItems": 1, "items": { "enum": ["native", "browser-intercept", "request-adapter", "not-needed"] } },
|
|
318
|
+
"artifactName": { "type": "string", "pattern": "^[a-z0-9][a-z0-9._-]*\\.json$" },
|
|
319
|
+
"outputDir": { "type": "string", "pattern": "^[a-z0-9][a-z0-9._-]*$" }
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
"frontendVerificationBundle": {
|
|
323
|
+
"type": "object",
|
|
324
|
+
"additionalProperties": false,
|
|
325
|
+
"required": ["schemaVersion", "staticCommands", "behaviorCommands", "staticEvidence", "behaviorEvidence", "mode"],
|
|
326
|
+
"properties": {
|
|
327
|
+
"schemaVersion": { "const": 1 },
|
|
328
|
+
"mockCommands": { "type": "array", "items": { "type": "string" } },
|
|
329
|
+
"staticCommands": { "type": "array", "minItems": 1, "items": { "type": "string" } },
|
|
330
|
+
"behaviorCommands": { "type": "array", "minItems": 1, "items": { "type": "string" } },
|
|
331
|
+
"mockEvidence": { "$ref": "#/$defs/shellVerifyEvidence" },
|
|
332
|
+
"staticEvidence": { "$ref": "#/$defs/shellVerifyEvidence" },
|
|
333
|
+
"behaviorEvidence": { "$ref": "#/$defs/shellVerifyEvidence" },
|
|
334
|
+
"mode": { "enum": ["initial", "repair"] }
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
"frontendReviewContext": {
|
|
338
|
+
"type": "object",
|
|
339
|
+
"additionalProperties": false,
|
|
340
|
+
"required": ["schemaVersion"],
|
|
341
|
+
"properties": { "schemaVersion": { "const": 1 } }
|
|
342
|
+
},
|
|
306
343
|
"backendTestPipeline": {
|
|
307
344
|
"enum": ["contracts", "semantic-initial", "execute-parse-initial", "classification-result-context"]
|
|
308
345
|
},
|
|
@@ -324,6 +361,9 @@
|
|
|
324
361
|
{ "required": ["verdictGate"] },
|
|
325
362
|
{ "required": ["requirementCoverageGate"] },
|
|
326
363
|
{ "required": ["jsonArtifactGate"] },
|
|
364
|
+
{ "required": ["frontendPrewriteGate"] },
|
|
365
|
+
{ "required": ["frontendVerificationBundle"] },
|
|
366
|
+
{ "required": ["frontendReviewContext"] },
|
|
327
367
|
{ "required": ["backendTestPipeline"] }
|
|
328
368
|
]
|
|
329
369
|
},
|
|
@@ -2,205 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
## Purpose
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
供 `generate-backend-pytest-pi` 使用。该节点是 `executor: "pi"`、`role: "implementer"`、`toolProfile: "write"` 的受限 writer,只把已经通过 Markdown gate 的最终用例转换为 pytest 资产。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## 当前合同
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"depends_on": ["backend-test-case-manifest-shell", "validate-backend-test-contracts-shell"],
|
|
15
|
-
"complexity": "HIGH",
|
|
16
|
-
"executor": "pi",
|
|
17
|
-
"role": "implementer",
|
|
18
|
-
"toolProfile": "write",
|
|
19
|
-
"writePolicy": "exclusive",
|
|
20
|
-
"writeSet": ["testcase/**/test_*.py", "testcase/**/helpers/**", "testcase/**/factories/**"],
|
|
21
|
-
"allowedPaths": ["**"],
|
|
22
|
-
"forbiddenPaths": [".harness/**", "artifacts/**"],
|
|
23
|
-
"outputContract": "Pytest test files under testcase/ with 1:1 mapping to functional test case IDs; optional helpers/factories. Summary lists generated files, test function count, and any skipped cases with reasons.",
|
|
24
|
-
"subtask_prompt_markdown": "./backend-test-dag.generate-pytest.prompt.md"
|
|
25
|
-
}
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Prompt Body
|
|
29
|
-
|
|
30
|
-
You are the Backend Test DAG **pytest code generator**.
|
|
31
|
-
|
|
32
|
-
Your job is to convert reviewed test cases under `testcase/md/` into pytest automation code. Write test files under `testcase/` only. Stay within `writeSet`. Do not write root `artifacts/**`.
|
|
33
|
-
|
|
34
|
-
### Output Steps (do in order)
|
|
35
|
-
|
|
36
|
-
1. First, output a brief summary: how many files, how many test functions planned
|
|
37
|
-
2. Then write each test file under `testcase/`
|
|
38
|
-
|
|
39
|
-
### Inputs
|
|
40
|
-
|
|
41
|
-
1. **Validated test cases and manifest** — files under `testcase/md/` plus run-owned `contracts/backend-test-case-manifest.json`. The case review runs independently as advisory evidence.
|
|
42
|
-
2. **Validated Backend Test Analysis v2** — run-owned `contracts/backend-test-analysis.json`.
|
|
43
|
-
3. **Validated Backend Test Execution Contract v1** — run-owned `contracts/backend-test-execution.json` from `backend-test-execution-contract-shell` (fixtures, env *names*, `testRoot`, `targetMode`, authenticationMode).
|
|
44
|
-
4. **Target project conventions** — read `conftest.py`, `pytest.ini` / `pyproject.toml` to understand conventions, but do NOT modify them.
|
|
45
|
-
|
|
46
|
-
Do NOT re-read source documents for free-form analysis. Use only validated cases, manifest, and contracts. Use only fixture/env/testRoot facts already present in the execution contract; never invent production credentials or secret values.
|
|
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
|
-
|
|
50
|
-
### Conversion Rules
|
|
51
|
-
|
|
52
|
-
#### File Naming
|
|
53
|
-
|
|
54
|
-
- Every test file must start with `test_` prefix (e.g. `test_order.py`, `test_user_api.py`)
|
|
55
|
-
- pytest collects tests from files matching `test_*.py` or `*_test.py` — use `test_` prefix exclusively
|
|
56
|
-
- Never create test files without the `test_` prefix
|
|
57
|
-
|
|
58
|
-
#### Write Boundary
|
|
59
|
-
|
|
60
|
-
- Only **create new** files under writeSet:
|
|
61
|
-
- `testcase/**/test_*.py`
|
|
62
|
-
- `testcase/**/helpers/**` (optional pure helpers)
|
|
63
|
-
- `testcase/**/factories/**` (optional test data factories)
|
|
64
|
-
- Do NOT modify existing files: `conftest.py`, `pytest.ini`, `pyproject.toml`, `setup.cfg`, `__init__.py`, or any other framework/config file
|
|
65
|
-
- Reuse existing fixtures; if required helpers are missing, create NEW helper/factory modules under the writeSet paths above — never edit root conftest
|
|
66
|
-
- Read existing framework files to understand conventions, but treat them as immutable
|
|
67
|
-
- Do NOT write production code, `.env`, secrets, or credential files
|
|
68
|
-
|
|
69
|
-
#### Naming Conflict Resolution
|
|
70
|
-
|
|
71
|
-
- If a file with the target name already exists under `testcase/`, add a numeric suffix: `test_order.py` → `test_order_01.py` → `test_order_02.py`
|
|
72
|
-
- Never overwrite or append to existing files — each test script must be a standalone file
|
|
73
|
-
- Check for existing files before writing; if `test_<module>.py` exists, use `test_<module>_01.py`
|
|
74
|
-
|
|
75
|
-
#### 1:1 Traceability
|
|
76
|
-
|
|
77
|
-
Every functional test case ID (`BE-<MODULE>-<NNN>`) must map to exactly one pytest function:
|
|
9
|
+
- 输入:最终 `testcase/md/**`、run-owned 环境报告与 Markdown 校验报告,以及有界的 pytest config / `conftest.py`。
|
|
10
|
+
- 禁止重新读取 `source/**`、新增测试场景、重新分配 AC、执行 pytest、修改生产代码/配置或输出业务 JSON。
|
|
11
|
+
- 写入范围:`testcase/**/test_*.py`、`testcase/**/helpers/**`、`testcase/**/factories/**`。
|
|
12
|
+
- pytest 可以使用模块级 `test_*` 函数,也可以使用 pytest 测试类中的 `test_*` 方法;不得为了追溯强迫目标项目放弃现有 class-based 风格。
|
|
13
|
+
- 每个最终 Markdown Case ID 必须出现在至少一个真实测试函数/方法区域中,优先同时出现在函数/方法名和 docstring:
|
|
78
14
|
|
|
79
15
|
```python
|
|
80
|
-
|
|
81
|
-
def
|
|
82
|
-
|
|
83
|
-
|
|
16
|
+
class TestOrderApi:
|
|
17
|
+
def test_BE_ORDER_001_create_order(self) -> None:
|
|
18
|
+
"""BE-ORDER-001 创建合法订单。"""
|
|
19
|
+
...
|
|
84
20
|
```
|
|
85
21
|
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
- All test files go under `testcase/` directory in the host project root
|
|
92
|
-
- Group test files by MODULE segment: `BE-ORDER-*` → `testcase/test_order.py`, `BE-USER-*` → `testcase/test_user.py`
|
|
93
|
-
- Follow existing project conventions for import style, fixture scope
|
|
94
|
-
|
|
95
|
-
#### Fixture Strategy
|
|
96
|
-
|
|
97
|
-
- Reuse existing project fixtures from `conftest.py` when available (read-only)
|
|
98
|
-
- Do not create or modify root fixture/configuration files (`conftest.py`, pytest.ini, …)
|
|
99
|
-
- Optional NEW helpers/factories may live under `testcase/**/helpers/**` or `testcase/**/factories/**` only
|
|
100
|
-
- Prefer `@pytest.fixture(scope="function")` for test isolation
|
|
101
|
-
- Use `@pytest.mark.parametrize` for boundary condition cases with multiple inputs
|
|
102
|
-
|
|
103
|
-
#### Test Integrity
|
|
104
|
-
|
|
105
|
-
- Tests verify implementation correctness — if a test fails, the implementation likely has a bug, not the test
|
|
106
|
-
- Do NOT weaken assertions, remove test cases, or modify test logic to make tests pass
|
|
107
|
-
- Do NOT add workarounds, skips, or try/except blocks to hide failures without explicit justification
|
|
108
|
-
- Report all failures honestly in the output; the downstream `execute-backend-pytest-shell` node captures exit codes and stdout/stderr as-is
|
|
109
|
-
|
|
110
|
-
#### Test Data Preparation Rules (MUST follow)
|
|
111
|
-
|
|
112
|
-
**When Setup is Needed**
|
|
113
|
-
|
|
114
|
-
Setup phase is REQUIRED only when test cases need pre-existing data:
|
|
115
|
-
- Query/Read APIs: need data to exist before querying
|
|
116
|
-
- Update/Delete APIs: need data to exist before modifying
|
|
117
|
-
- State transition tests: need data in specific state
|
|
118
|
-
|
|
119
|
-
Setup phase is NOT needed for:
|
|
120
|
-
- Create APIs: testing the creation itself
|
|
121
|
-
- Validation tests: testing input validation with invalid data
|
|
122
|
-
|
|
123
|
-
**Data Setup Strategy**
|
|
124
|
-
|
|
125
|
-
When setup is needed:
|
|
126
|
-
1. Use `@pytest.fixture(scope='module')` or `@pytest.fixture(scope='session')` to prepare shared test data
|
|
127
|
-
2. All test cases in the file share the same pre-constructed data
|
|
128
|
-
|
|
129
|
-
**Data Construction Priority**
|
|
130
|
-
|
|
131
|
-
1. **API-first**: Use documented APIs from `analyze-inputs-pi` / reviewed cases
|
|
132
|
-
2. **Reuse existing conftest fixtures** (read-only)
|
|
133
|
-
3. **Direct DB writes are last resort** and only via safe test-DB fixtures with rollback/isolation
|
|
134
|
-
4. If neither API nor safe DB fixture exists, **skip with an explicit gap note** — do not invent credentials or touch live data
|
|
135
|
-
|
|
136
|
-
**API Data Construction**
|
|
137
|
-
|
|
138
|
-
- Prefer the `analyze-inputs-pi` API Endpoints section and reviewed cases for method/path/fields
|
|
139
|
-
- Chain API calls only when cases document multi-step preconditions
|
|
140
|
-
- Store created resource IDs in fixtures for reuse
|
|
141
|
-
- Do **not** broadly search host route/controller trees for secrets, `.env`, private keys, or production configs
|
|
142
|
-
- Read host API definitions only when needed to resolve a field name already referenced by reviewed cases
|
|
143
|
-
|
|
144
|
-
**Database Data Construction (restricted)**
|
|
145
|
-
|
|
146
|
-
- Allowed only via existing `conftest.py` test-DB fixtures with transaction rollback or equivalent isolation
|
|
147
|
-
- Never hardcode connection strings, passwords, tokens, or cloud credentials
|
|
148
|
-
- Never target production/shared non-test databases
|
|
149
|
-
- If isolation is unclear, report the gap instead of writing DB rows
|
|
150
|
-
|
|
151
|
-
#### Assertion Rules (MUST follow)
|
|
152
|
-
|
|
153
|
-
**Positive Path (成功场景)**
|
|
154
|
-
|
|
155
|
-
MUST assert ALL of the following:
|
|
156
|
-
1. HTTP status code: as defined in API spec (e.g. 200, 201)
|
|
157
|
-
2. Response structure: key fields exist in response body
|
|
158
|
-
3. Specific values: each field equals expected value from test case
|
|
159
|
-
4. Data type: each field is correct type
|
|
160
|
-
|
|
161
|
-
**Negative Path (异常场景)**
|
|
162
|
-
|
|
163
|
-
MUST assert ALL of the following:
|
|
164
|
-
1. HTTP status code: as defined in API spec (e.g. 400, 404, 500)
|
|
165
|
-
2. Error code field: field name from API spec (e.g. code, error_code, errcode, ret)
|
|
166
|
-
3. Error message field: field name from API spec (e.g. message, msg, errmsg, error)
|
|
167
|
-
|
|
168
|
-
**Field Name Resolution**
|
|
169
|
-
|
|
170
|
-
Field names MUST come from the upstream `analyze-inputs-pi` output (API Endpoints section), NOT hardcoded. For example:
|
|
171
|
-
- If API spec defines `{"ret": 0, "msg": "success"}`, assert `response.json()["ret"]` and `response.json()["msg"]`
|
|
172
|
-
- If API spec defines `{"code": 4001, "message": "error"}`, assert `response.json()["code"]` and `response.json()["message"]`
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
#### Conditional Test Implementation (include ONLY if test cases exist)
|
|
176
|
-
|
|
177
|
-
- **Authentication tests**: implement ONLY if `testcase/md/` contains auth-related cases
|
|
178
|
-
- Use `@pytest.mark.auth` marker
|
|
179
|
-
- Test no token, expired token, invalid token, insufficient permissions, cross-user access
|
|
180
|
-
- **Timeout tests**: implement ONLY if `testcase/md/` contains timeout-related cases
|
|
181
|
-
- Use `@pytest.mark.timeout` marker
|
|
182
|
-
- Use `unittest.mock.patch` or `pytest-mock` to simulate slow responses
|
|
183
|
-
- If no such cases exist in the reviewed test cases, do NOT add these tests
|
|
184
|
-
|
|
185
|
-
#### Markers
|
|
186
|
-
|
|
187
|
-
- `@pytest.mark.positive` — happy path cases
|
|
188
|
-
- `@pytest.mark.negative` — error/exception cases
|
|
189
|
-
- `@pytest.mark.boundary` — edge cases
|
|
190
|
-
- `@pytest.mark.<MODULE>` — module-specific marker (e.g. `@pytest.mark.order`)
|
|
191
|
-
|
|
192
|
-
#### Skip Policy
|
|
193
|
-
|
|
194
|
-
If a test case cannot be automated (requires external service not mockable, requires manual verification), add it with `@pytest.mark.skip(reason="...")` and document the reason. Do not omit the function — traceability requires it exists.
|
|
22
|
+
- 断言只来自 `### 预期结果` / `### Expected Results`;setup 只来自 `### 前置条件`、`### 测试数据`、`### 自动化映射` 或对应历史英文分节。
|
|
23
|
+
- 禁止 `skip` / `xfail`、吞断言、宽异常静默通过、mock 替代真实目标、删除用例或弱化断言。
|
|
24
|
+
- best-effort 清理只能捕获所选 HTTP client 实际抛出的窄 transport exception,例如 `requests.RequestException` 或 `urllib.error.URLError`;禁止 `except:`、`except Exception`、`except BaseException` 后 `pass`。
|
|
25
|
+
- 同一 Case ID 可以由多个 pytest 函数覆盖;额外映射会进入 traceability 报告,但不能伪造未在 Markdown 中定义的业务场景。
|
|
26
|
+
- 测试失败必须诚实保留,后续节点只执行一次 pytest,并从同一 JUnit 生成 HTML/facts。
|
|
195
27
|
|
|
196
|
-
|
|
28
|
+
## 推荐输出
|
|
197
29
|
|
|
198
|
-
|
|
30
|
+
写入文件后,用简短中文总结:
|
|
199
31
|
|
|
200
|
-
1.
|
|
201
|
-
2.
|
|
202
|
-
3.
|
|
203
|
-
4.
|
|
204
|
-
5. **Residual Risks** — cases that may need manual verification or environment setup.
|
|
32
|
+
1. 生成或更新了哪些测试文件;
|
|
33
|
+
2. Case ID → 脚本 → pytest 函数/方法映射;
|
|
34
|
+
3. 复用的 fixture/config;
|
|
35
|
+
4. 仍存在的环境或自动化缺口。
|
|
205
36
|
|
|
206
|
-
|
|
37
|
+
不得输出 chain-of-thought,不得执行 pytest。
|
|
@@ -68,8 +68,8 @@
|
|
|
68
68
|
"executorModels": {
|
|
69
69
|
"pi": {
|
|
70
70
|
"LOW": "gpt-5.3-codex-spark",
|
|
71
|
-
"MED": "
|
|
72
|
-
"HIGH": "gpt-5.
|
|
71
|
+
"MED": "grok-4.5",
|
|
72
|
+
"HIGH": "gpt-5.6-sol"
|
|
73
73
|
}
|
|
74
74
|
},
|
|
75
75
|
"tasks": [
|
|
@@ -124,8 +124,8 @@
|
|
|
124
124
|
".harness/dag-runs/**",
|
|
125
125
|
"artifacts/**"
|
|
126
126
|
],
|
|
127
|
-
"outputContract": "Write testcase/md/README.md plus module Markdown
|
|
128
|
-
"subtask_prompt": "Read the upstream environment report. Generate a Markdown-first backend test strategy and cases under testcase/md/**.\n\nWrite human-readable content in Simplified Chinese by default
|
|
127
|
+
"outputContract": "Write a Chinese, human-readable testcase/md/README.md plus module Markdown case cards using BE-<MODULE>-<NNN>; keep machine IDs/literals exact and do not execute pytest or modify production code/config.",
|
|
128
|
+
"subtask_prompt": "Read the upstream environment report. Generate a Markdown-first backend test strategy and cases under testcase/md/**.\n\nWrite human-readable content in Simplified Chinese by default. Keep English only for machine-readable IDs and technical literals such as Case/AC/REQ/BR IDs, HTTP methods, paths, field names, enum values, commands, filenames, code symbols and exact source citations.\n\nCreate testcase/md/README.md as the concise entry page: test objective, target/environment, isolation/cleanup, module summary and a linked case index table with Case ID, Chinese case name, scenario type, endpoint and expected status/result. Avoid repeating every case body in README.\n\nWrite each module as readable case cards. Every case starts with `## BE-<MODULE>-<NNN>|<中文用例名称>` and uses these Chinese headings: `### 测试目的`, `### 验收标准`, `### 需求依据`, `### 前置条件`, optional `### 测试数据`, `### 操作步骤`, `### 预期结果`, and `### 自动化映射`. API metadata may use a compact table under the case heading. The deterministic validator also accepts legacy English headings, but new output should use this Chinese presentation.\n\nPlace steps and their expected results in a compact readable table when that improves clarity; otherwise keep numbered executable steps and numbered/bulleted independently assertable results. Every result must name the observable HTTP status, response field/value, state transition or membership condition, never vague wording such as ‘符合预期’.\n\nIn `自动化映射`, record the planned script path and pytest function name when known. Put implementation-only restrictions in a concise `<details>` block rather than dominating the main case flow. Use only environment-supported fixtures/targets/isolation, record evidence gaps in Chinese, and do not emit JSON, pytest, or execute commands.\n\n## Derived task contract: 需求.md\n\n# Backend test\n- AC-001 proof\n\n## Authoritative reference index\n\n[]\n\nFor each index entry, use `readPath` for Pi read-tool calls and copy `path` exactly into Markdown Source References. Bound files under .harness/tasks/<taskId>/source/** are read-only inputs: reading them is allowed even though writing .harness/** is forbidden. Never resolve `path` relative to the repository root, search for substitutes, or fall back to docs/** when a bound read fails.\n\nRead only precise indexed references needed for AC/API/field/rule evidence; references remain authoritative over derived text."
|
|
129
129
|
},
|
|
130
130
|
{
|
|
131
131
|
"id": "review-and-revise-backend-md-cases-pi",
|
|
@@ -149,7 +149,7 @@
|
|
|
149
149
|
"artifacts/**"
|
|
150
150
|
],
|
|
151
151
|
"outputContract": "Review source fidelity and directly revise only testcase/md/**; return concise Markdown, never JSON.",
|
|
152
|
-
"subtask_prompt": "Independently review generated Markdown cases against each case
|
|
152
|
+
"subtask_prompt": "Independently review generated Markdown cases against each case 需求依据 and environment evidence. Treat the files as human-facing test documentation: require a clear Chinese name and scenario/purpose, compact metadata, readable steps/results, and a concise automation mapping while preserving exact machine IDs and technical literals.\n\nCheck AC completeness/meaning, endpoint, fields/shape, status/error codes, rules, states, documented boundaries/auth, positive/negative coverage, executable steps and assertable results. Reject avoidable English prose, duplicated bilingual wording, repeated boilerplate, oversized unstructured sections, vague results such as ‘符合预期’, and missing script/function mapping where it can be derived.\n\nCorrect testcase/md/** directly: add documented omissions, remove unsupported cases, fix mappings/expectations, merge duplicates, improve navigation/tables/Chinese wording, or record gaps in Chinese. Keep Case IDs, AC/REQ/BR IDs, HTTP methods, paths, fields, enum values, filenames, code symbols and source citations exact. The validator accepts Chinese and legacy English section aliases; retain or converge to the Chinese human-readable headings without losing structure.\n\nRead only precise referenced source paths plus requirement sections needed for uncovered ACs. Do not scan the repository, modify source/**, generate pytest, execute tests, or emit JSON.\n\n## Derived task contract: 需求.md\n\n# Backend test\n- AC-001 proof\n\n## Authoritative reference index\n\n[]\n\nFor each index entry, use `readPath` for Pi read-tool calls and keep `path` as the exact Markdown Source References citation. Bound files under .harness/tasks/<taskId>/source/** are read-only inputs: reading them is allowed even though writing .harness/** is forbidden. Never resolve `path` relative to the repository root, search for substitutes, or fall back to docs/** when a bound read fails."
|
|
153
153
|
},
|
|
154
154
|
{
|
|
155
155
|
"id": "validate-backend-md-cases-shell",
|
|
@@ -203,7 +203,7 @@
|
|
|
203
203
|
"artifacts/**"
|
|
204
204
|
],
|
|
205
205
|
"outputContract": "Convert every final automatable Markdown case into pytest assets whose actual test function region contains the exact Case ID, preferably in the function name or docstring; no JSON and no pytest execution.",
|
|
206
|
-
"subtask_prompt": "Convert validated testcase/md/** to pytest using upstream environment and validation evidence plus only bounded pytest config/conftest.\n\nEnsure every final Markdown Case ID appears in at least one real
|
|
206
|
+
"subtask_prompt": "Convert validated testcase/md/** to pytest using upstream environment and validation evidence plus only bounded pytest config/conftest.\n\nEnsure every final Markdown Case ID appears in at least one real pytest test function or pytest test class method region, preferably as `test_BE_<MODULE>_<NNN>_<description>` and in that function/method docstring. Module-level functions and class-based pytest methods are both supported. Multiple test functions may cover one Case ID; assertions come only from 预期结果/Expected Results and setup comes only from 前置条件/测试数据/自动化映射 or their legacy English aliases.\n\nDo not read source/**, add cases, reassign ACs, modify conftest/config/production code, use skip/xfail, swallow assertions, execute pytest, or emit JSON. For best-effort cleanup, catch only the narrow transport exception actually raised by the selected HTTP client (for example `requests.RequestException` or `urllib.error.URLError`); never use bare `except`, `Exception`, or `BaseException` with `pass`."
|
|
207
207
|
},
|
|
208
208
|
{
|
|
209
209
|
"id": "backend-test-traceability-gate-shell",
|
|
@@ -224,7 +224,7 @@
|
|
|
224
224
|
"artifacts/**"
|
|
225
225
|
],
|
|
226
226
|
"outputContract": "Run-owned reports/backend-test-traceability.md proving every real Markdown Case ID is covered by at least one pytest test function.",
|
|
227
|
-
"subtask_prompt": "Fail closed only when a real Markdown case heading has no associated
|
|
227
|
+
"subtask_prompt": "Fail closed only when a real Markdown case heading has no associated pytest test function or class method. Accept exact Case IDs in the function/method name or its decorator/body/docstring region; report multiple mappings and extra automation Case IDs without blocking. Continue to reject skip/xfail or swallowed exceptions.",
|
|
228
228
|
"shell": {
|
|
229
229
|
"commands": [],
|
|
230
230
|
"backendTestPipeline": "markdown-traceability",
|
|
@@ -251,7 +251,7 @@
|
|
|
251
251
|
"artifacts/**"
|
|
252
252
|
],
|
|
253
253
|
"outputContract": "One pytest execution producing valid JUnit, self-contained HTML and reports/backend-test-facts.md; exit 0/1 with valid evidence continues.",
|
|
254
|
-
"subtask_prompt": "Execute pytest exactly once. Validate JUnit, render self-contained HTML from
|
|
254
|
+
"subtask_prompt": "Execute pytest exactly once. Validate JUnit, render the primary self-contained Chinese HTML report from the same JUnit plus final Markdown case metadata without rerun, list every case with name/scenario/script/function/result/duration, and preserve failure summaries plus expandable technical details as facts.",
|
|
255
255
|
"shell": {
|
|
256
256
|
"commands": [
|
|
257
257
|
"mkdir -p \"${HARNESS_DAG_RUN_DIR}/reports\"; PYTHONUTF8=1 PYTHONIOENCODING=utf-8 PYTHONDONTWRITEBYTECODE=1 python -m pytest testcase/ -v -p no:cacheprovider --junitxml=\"${HARNESS_DAG_RUN_DIR}/reports/backend-test.junit.xml\"; STATUS=$?; printf \"%s\" \"${STATUS}\" > \"${HARNESS_DAG_RUN_DIR}/reports/backend-test-pytest-exit.txt\"; if { [ \"${STATUS}\" -eq 0 ] || [ \"${STATUS}\" -eq 1 ]; } && [ -s \"${HARNESS_DAG_RUN_DIR}/reports/backend-test.junit.xml\" ]; then exit 0; fi; exit \"${STATUS}\""
|
|
@@ -2,88 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
## Purpose
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
供 `review-and-revise-backend-md-cases-pi` 使用。该节点是 `executor: "pi"`、`role: "reviewer"`、`toolProfile: "write"` 的受限 reviewer,只能直接修订 `testcase/md/**`,不得生成 pytest 或修改任务源、生产代码和配置。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## 当前合同
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
把 Markdown 用例视为面向测试、研发、产品与评审人员的正式文档,而不是模型中间产物。
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
{
|
|
13
|
-
"id": "review-backend-cases-pi",
|
|
14
|
-
"depends_on": ["backend-test-case-manifest-shell", "backend-test-analysis-contract-shell"],
|
|
15
|
-
"complexity": "HIGH",
|
|
16
|
-
"executor": "pi",
|
|
17
|
-
"role": "reviewer",
|
|
18
|
-
"writePolicy": "read-only",
|
|
19
|
-
"allowedPaths": ["**"],
|
|
20
|
-
"forbiddenPaths": [".harness/**", "artifacts/**"],
|
|
21
|
-
"outputContract": "Plain Markdown whose first non-empty line is VERDICT: pass or VERDICT: request-revision; followed by Findings and Coverage Assessment. No file writes.",
|
|
22
|
-
"subtask_prompt_markdown": "./backend-test-dag.review-cases.prompt.md"
|
|
23
|
-
}
|
|
24
|
-
```
|
|
11
|
+
### 阅读体验
|
|
25
12
|
|
|
26
|
-
|
|
13
|
+
- `testcase/md/README.md` 是简洁入口,包含测试目标、环境、隔离/清理策略、模块汇总和可跳转的用例索引。
|
|
14
|
+
- 模块文件采用中文用例卡片;每条以 `## BE-<MODULE>-<NNN>|<中文用例名称>` 开始。
|
|
15
|
+
- 新文档优先使用:`测试目的`、`验收标准`、`需求依据`、`前置条件`、`测试数据`、`操作步骤`、`预期结果`、`自动化映射`。
|
|
16
|
+
- validator 同时接受上述中文分节和历史英文分节;机器 ID、HTTP 方法、路径、字段、枚举、文件名、函数名与 source citation 必须保持精确。
|
|
17
|
+
- 步骤和预期可以用紧凑表格,也可以分别使用编号/项目列表;必须可执行、可独立断言。
|
|
18
|
+
- 自动化内部限制应简短或放进 `<details>`,不能淹没人类主要阅读路径。
|
|
27
19
|
|
|
28
|
-
|
|
20
|
+
### 正确性
|
|
29
21
|
|
|
30
|
-
|
|
22
|
+
- 对照每条 `需求依据` 和环境报告检查 AC、接口、字段/响应形状、状态码、错误语义、状态转换、正向/异常/边界场景。
|
|
23
|
+
- 删除无依据场景、合并重复用例、补齐有依据的遗漏;无法确认的内容写入中文证据缺口,不猜测行为或凭据。
|
|
24
|
+
- 拒绝“符合预期”“正常工作”等模糊结果,以及无意义的中英双写和大段重复 boilerplate。
|
|
25
|
+
- 若能确定脚本与函数命名,在 `自动化映射` 中写明计划脚本路径与 pytest 函数/方法名。
|
|
31
26
|
|
|
32
|
-
|
|
27
|
+
## 推荐输出
|
|
33
28
|
|
|
34
|
-
|
|
29
|
+
完成文件修订后,仅用简短中文说明:
|
|
35
30
|
|
|
36
|
-
-
|
|
37
|
-
-
|
|
31
|
+
- 修订了哪些文档;
|
|
32
|
+
- 用例数与主要模块;
|
|
33
|
+
- 修复了哪些需求一致性或阅读问题;
|
|
34
|
+
- 仍有哪些证据缺口。
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
### Inputs to Review
|
|
42
|
-
|
|
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. **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/`.
|
|
46
|
-
|
|
47
|
-
Do NOT re-read source documents. Use the validated analysis artifact, case manifest, and generated cases only.
|
|
48
|
-
|
|
49
|
-
### Review Checklist
|
|
50
|
-
|
|
51
|
-
| Area | Check | Severity if Missing |
|
|
52
|
-
|------|-------|---------------------|
|
|
53
|
-
| **ID format** | Every test case ID matches `BE-<MODULE>-<NNN>` (e.g. `BE-ORDER-001`) | Critical |
|
|
54
|
-
| **Positive path coverage** | Happy-path scenarios for each acceptance criterion | Critical |
|
|
55
|
-
| **Negative path coverage** | Error/exception scenarios (invalid input, not found, state violations) | Important |
|
|
56
|
-
| **Boundary conditions** | Edge cases (empty input, max length, edge values) | Important |
|
|
57
|
-
| **State transitions** | Illegal state changes covered | Important |
|
|
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 case bodies **and** any AC matrix list the **same full** `BE-*` ids as Case Manifest v1 `caseId`→`acIds`. Never claim "all cases" / "全部用例" cover an AC unless every case maps that AC. | Critical |
|
|
60
|
-
| **Planned automation** | Missing `test_*.py` before generate-pytest is **Informational only**, not Critical | Informational |
|
|
61
|
-
| **Out-of-scope ACs** | Flyway / frontend e2e / `mvn test` etc. already in manifest `evidenceGaps` must not be treated as uncovered Critical | Informational |
|
|
62
|
-
| **Case structure** | Each case has: ID, Title, Precondition, Steps, Expected Result | Important |
|
|
63
|
-
| **No duplicate IDs** | All test case IDs are unique across files | Critical |
|
|
64
|
-
|
|
65
|
-
### Conditional Coverage (check ONLY if mentioned in upstream analysis)
|
|
66
|
-
|
|
67
|
-
- **Authentication coverage**: check ONLY if the validated analysis artifact mentions auth mechanism (JWT, OAuth2, API Key, etc.)
|
|
68
|
-
- **Timeout coverage**: check ONLY if the validated analysis artifact mentions timeout handling or degradation strategy
|
|
69
|
-
- If not mentioned in the validated analysis artifact, do NOT flag as missing
|
|
70
|
-
|
|
71
|
-
### Verdict Rules
|
|
72
|
-
|
|
73
|
-
| Condition | Verdict |
|
|
74
|
-
|-----------|---------|
|
|
75
|
-
| All Critical checks pass, Important checks have no more than 2 findings | `VERDICT: pass` |
|
|
76
|
-
| Any Critical check fails | `VERDICT: request-revision` |
|
|
77
|
-
| More than 2 Important findings | `VERDICT: request-revision` |
|
|
78
|
-
| Only Informational findings | `VERDICT: pass` (with findings listed) |
|
|
79
|
-
|
|
80
|
-
### Output Shape (after verdict line)
|
|
81
|
-
|
|
82
|
-
1. **Coverage Assessment** — table mapping each AC to covering **full** test case IDs (or "uncovered" / gap).
|
|
83
|
-
2. **Findings** — bullet list tagged `Critical`, `Important`, or `Informational`.
|
|
84
|
-
3. **Statistics** — total case count, positive/negative/boundary breakdown, module distribution.
|
|
85
|
-
4. **Required follow-up** (only when `request-revision`) — numbered, concrete MD corrections for a separate follow-up task. These findings do not authorize edits in the current run.
|
|
86
|
-
|
|
87
|
-
### Fail-fast note
|
|
88
|
-
|
|
89
|
-
There is no final review or in-run revision writer. Any `request-revision` verdict remains auditable advisory evidence and must be reported downstream; it does not stop pytest generation.
|
|
36
|
+
不得输出 JSON,不得执行测试。
|