@tea-agent/loop-agent 0.16.24 → 0.16.26
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 +20 -0
- 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/dag-pi-executor.js +63 -9
- package/dist/executors/shell-executor.js +30 -0
- package/dist/executors/shell-write-guard.js +64 -2
- package/dist/worker/delivery/git-transaction.js +43 -8
- package/dist/worker/observe/paths.js +81 -0
- package/dist/worker/observe/routes.js +127 -19
- package/dist/worker/observe/spec-evidence.js +84 -0
- package/dist/worker/observe/static/api.js +23 -0
- package/dist/worker/observe/static/state.js +26 -0
- package/dist/worker/observe/static/styles.css +10 -0
- package/dist/worker/observe/static/views/dag-inspector.js +173 -6
- 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/init-hybrid.js +150 -13
- package/dist/workflows/dag/l5-report-metrics.js +36 -0
- package/dist/workflows/dag/node-execution.js +32 -5
- package/dist/workflows/dag/project-governance-context.js +508 -0
- package/dist/workflows/dag/prompt.js +46 -1
- package/dist/workflows/dag/skill-snapshot.js +1 -0
- package/dist/workflows/dag/types.js +10 -0
- package/dist/workflows/dag/validate.js +28 -0
- package/docs/architecture/evolution.md +3 -1
- package/docs/templates/agent-dag.schema.json +15 -0
- package/docs/templates/agent-dag.supervised-implementation.json +1 -0
- package/docs/templates/backend-test-dag.generate-pytest.prompt.md +2 -0
- package/docs/templates/backend-test-dag.json +39 -6
- package/docs/templates/backend-test-dag.retrospect.prompt.md +36 -7
- package/package.json +1 -1
- package/skills/loop-agent/references/command-reference.md +1 -0
|
@@ -98,8 +98,49 @@ function buildReadOnlyBoundary(writePolicy) {
|
|
|
98
98
|
"Return findings in the assistant response/stdout only; the DAG runner persists node artifacts under .harness/dag-runs/<state>/<run-id>/<node-id>/.",
|
|
99
99
|
];
|
|
100
100
|
}
|
|
101
|
+
function formatProjectGovernanceContext(ctx) {
|
|
102
|
+
if (!ctx || !ctx.applicable)
|
|
103
|
+
return undefined;
|
|
104
|
+
const lines = [];
|
|
105
|
+
lines.push("Deterministic project governance manifest for the actual writer changeset of this run. Interpret it for compliance review; do not invent rules beyond the files and hashes listed here.");
|
|
106
|
+
lines.push("Before issuing a verdict, use read-only tools to read every applicable AGENTS.md and every mandatory referenced standard listed below from the repository; hashes bind the exact inputs for audit.");
|
|
107
|
+
lines.push("Your first non-empty output line must be exactly VERDICT: pass or VERDICT: request-revision. Request revision while any applicable mandatory instruction is violated.");
|
|
108
|
+
lines.push("");
|
|
109
|
+
lines.push("Change manifest (writer nodes and their changed files):");
|
|
110
|
+
for (const entry of ctx.changeManifest) {
|
|
111
|
+
if (entry.changedFiles.length === 0)
|
|
112
|
+
continue;
|
|
113
|
+
lines.push(`- ${entry.writerNodeId}: ${entry.changedFiles.join(", ")}`);
|
|
114
|
+
}
|
|
115
|
+
lines.push("");
|
|
116
|
+
lines.push("Applicable AGENTS.md chain (root -> nearest):");
|
|
117
|
+
for (const entry of ctx.agentsMdChain) {
|
|
118
|
+
lines.push(`- ${entry.path} (enforcement=mandatory, directory=${entry.directory || "/"}, sha256=${entry.sha256}, bytes=${entry.bytes}) applies to: ${entry.appliesTo.join(", ") || "(none)"}`);
|
|
119
|
+
}
|
|
120
|
+
if (ctx.referencedStandards.length > 0) {
|
|
121
|
+
lines.push("");
|
|
122
|
+
lines.push("Referenced repository-local code standards:");
|
|
123
|
+
for (const std of ctx.referencedStandards) {
|
|
124
|
+
lines.push(`- ${std.path} (enforcement=${std.enforcement}, from ${ctx.agentsMdChain[std.fromAgentsMd]?.path ?? "AGENTS.md"}, sha256=${std.sha256}, bytes=${std.bytes}, scope=${std.scope})`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (ctx.unresolvedReferences.length > 0) {
|
|
128
|
+
lines.push("");
|
|
129
|
+
lines.push("Unresolved reference diagnostics (structured, not read): for awareness only.");
|
|
130
|
+
for (const ref of ctx.unresolvedReferences) {
|
|
131
|
+
lines.push(`- ${ref.declaredPath}: ${ref.reason} (from ${ctx.agentsMdChain[ref.fromAgentsMd]?.path ?? "AGENTS.md"})`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (ctx.diagnostics.length > 0) {
|
|
135
|
+
lines.push("");
|
|
136
|
+
lines.push("Resolver diagnostics:");
|
|
137
|
+
for (const diag of ctx.diagnostics)
|
|
138
|
+
lines.push(`- ${diag}`);
|
|
139
|
+
}
|
|
140
|
+
return lines.join("\n");
|
|
141
|
+
}
|
|
101
142
|
export function buildDagNodePromptEnvelope(input) {
|
|
102
|
-
const { spec, task, upstream, resolvedSkills = [], resolvedSkillInstructions = [], maxUpstreamChars = MAX_UPSTREAM_CHARS, } = input;
|
|
143
|
+
const { spec, task, upstream, resolvedSkills = [], resolvedSkillInstructions = [], maxUpstreamChars = MAX_UPSTREAM_CHARS, projectGovernanceContext, } = input;
|
|
103
144
|
const objective = spec.objective ?? spec.title;
|
|
104
145
|
const successCriteria = formatBulletList(spec.successCriteria, "(none specified)");
|
|
105
146
|
const globalConstraints = formatBulletList(spec.globalConstraints, "(none specified)");
|
|
@@ -150,6 +191,10 @@ export function buildDagNodePromptEnvelope(input) {
|
|
|
150
191
|
else {
|
|
151
192
|
sections.push("<upstream_context>\n(none)\n</upstream_context>");
|
|
152
193
|
}
|
|
194
|
+
const governanceSection = formatProjectGovernanceContext(projectGovernanceContext);
|
|
195
|
+
if (governanceSection) {
|
|
196
|
+
sections.push(`<project_governance_context>\n${governanceSection}\n</project_governance_context>`);
|
|
197
|
+
}
|
|
153
198
|
sections.push(`<task>\n${task.subtask_prompt}\n</task>`);
|
|
154
199
|
return sections.join("\n\n");
|
|
155
200
|
}
|
|
@@ -524,6 +524,7 @@ export function buildNodePromptFromSnapshot(input) {
|
|
|
524
524
|
resolvedSkills: skillNames,
|
|
525
525
|
resolvedSkillInstructions,
|
|
526
526
|
maxUpstreamChars: policy.resolveMaxUpstreamChars(input.task),
|
|
527
|
+
projectGovernanceContext: input.projectGovernanceContext,
|
|
527
528
|
}),
|
|
528
529
|
resolvedSkills: resolvedSkillInstructions.map(stripPromptText),
|
|
529
530
|
};
|
|
@@ -152,6 +152,9 @@ export const dagShellConfigSchema = z.object({
|
|
|
152
152
|
commands: z.array(z.string()).default([]),
|
|
153
153
|
preset: dagShellPresetSchema.optional(),
|
|
154
154
|
verdictGate: dagVerdictGateSchema.optional(),
|
|
155
|
+
projectGovernanceGate: z.object({
|
|
156
|
+
contextPath: z.literal(".runtime/project-governance-context.json"),
|
|
157
|
+
}).strict().optional(),
|
|
155
158
|
requirementCoverageGate: dagRequirementCoverageGateSchema.optional(),
|
|
156
159
|
jsonArtifactGate: dagJsonArtifactGateSchema.optional(),
|
|
157
160
|
backendTestPipeline: dagBackendTestPipelineSchema.optional(),
|
|
@@ -311,6 +314,13 @@ export const dagTaskSchema = z.object({
|
|
|
311
314
|
static: dagStaticConfigSchema.optional(),
|
|
312
315
|
outputContract: z.string().optional(),
|
|
313
316
|
firstProtocolLine: z.string().min(1).optional(),
|
|
317
|
+
/**
|
|
318
|
+
* Explicit opt-in for the deterministic project governance context resolver
|
|
319
|
+
* (AGENTS.md chain + referenced code standards). Only tasks that set this
|
|
320
|
+
* to `true` receive a `<project_governance_context>` prompt section and
|
|
321
|
+
* closeout-blocking gate behavior. Never inferred from role/name.
|
|
322
|
+
*/
|
|
323
|
+
governanceStandardReview: z.boolean().optional(),
|
|
314
324
|
allowedPaths: z.array(z.string()).optional().default([]),
|
|
315
325
|
forbiddenPaths: z.array(z.string()).optional().default([]),
|
|
316
326
|
decisionGate: dagDecisionGateSchema.optional(),
|
|
@@ -653,12 +653,40 @@ export function validateDagSpec(spec) {
|
|
|
653
653
|
validateStaticTaskConfig(task, issues);
|
|
654
654
|
validateDecisionGateTaskConfig(task, issues);
|
|
655
655
|
validateRetryPolicyTaskConfig(task, issues);
|
|
656
|
+
validateProjectGovernanceTaskConfig(task, spec, issues);
|
|
656
657
|
}
|
|
657
658
|
validateSameRankWriteSetConflicts(spec, ranks, issues);
|
|
658
659
|
validateSameRankAgentAttributionRisks(spec, ranks, issues);
|
|
659
660
|
validateWriterSourceBinding(spec, issues);
|
|
660
661
|
return issues;
|
|
661
662
|
}
|
|
663
|
+
function validateProjectGovernanceTaskConfig(task, spec, issues) {
|
|
664
|
+
if (task.governanceStandardReview) {
|
|
665
|
+
if (task.executor !== "pi" ||
|
|
666
|
+
task.toolProfile === "write" ||
|
|
667
|
+
!["read-only", "none"].includes((task.writePolicy ?? "read-only"))) {
|
|
668
|
+
issues.push({
|
|
669
|
+
type: "invalid-project-governance-config",
|
|
670
|
+
message: `task ${task.id} governanceStandardReview requires a read-only Pi node`,
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
if (!task.shell?.projectGovernanceGate)
|
|
675
|
+
return;
|
|
676
|
+
const verdictGate = task.shell.verdictGate;
|
|
677
|
+
const source = verdictGate
|
|
678
|
+
? spec.tasks.find((candidate) => candidate.id === verdictGate.fromNodeId)
|
|
679
|
+
: undefined;
|
|
680
|
+
if (task.executor !== "shell" ||
|
|
681
|
+
!verdictGate ||
|
|
682
|
+
!task.depends_on.includes(verdictGate.fromNodeId) ||
|
|
683
|
+
!source?.governanceStandardReview) {
|
|
684
|
+
issues.push({
|
|
685
|
+
type: "invalid-project-governance-config",
|
|
686
|
+
message: `task ${task.id} projectGovernanceGate requires verdictGate over a directly-dependent governanceStandardReview node`,
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
}
|
|
662
690
|
export function assertValidDagSpec(spec, options = {}) {
|
|
663
691
|
const issues = collectBlockingIssues(options.issues ?? validateDagSpec(spec), options);
|
|
664
692
|
if (issues.length > 0) {
|
|
@@ -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 / 历史基线(非未来)
|
|
@@ -241,6 +241,17 @@
|
|
|
241
241
|
}
|
|
242
242
|
}
|
|
243
243
|
},
|
|
244
|
+
"projectGovernanceGate": {
|
|
245
|
+
"type": "object",
|
|
246
|
+
"additionalProperties": false,
|
|
247
|
+
"required": ["contextPath"],
|
|
248
|
+
"properties": {
|
|
249
|
+
"contextPath": {
|
|
250
|
+
"const": ".runtime/project-governance-context.json",
|
|
251
|
+
"description": "Run-owned project governance context. When applicable=false the gate is a deterministic no-op; otherwise verdictGate remains authoritative."
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
},
|
|
244
255
|
"requirementCoverageGate": {
|
|
245
256
|
"type": "object", "additionalProperties": false,
|
|
246
257
|
"required": ["fromNodeIds", "requiredIds"],
|
|
@@ -370,6 +381,10 @@
|
|
|
370
381
|
"items": { "type": "string", "minLength": 1 }
|
|
371
382
|
},
|
|
372
383
|
"toolProfile": { "$ref": "#/$defs/toolProfile" },
|
|
384
|
+
"governanceStandardReview": {
|
|
385
|
+
"type": "boolean",
|
|
386
|
+
"description": "Explicitly opts this node into writer-change-scoped AGENTS.md and repository-local code-standard review. Never inferred from node id or role."
|
|
387
|
+
},
|
|
373
388
|
"writePolicy": { "$ref": "#/$defs/writePolicy" },
|
|
374
389
|
"writeSet": {
|
|
375
390
|
"type": "array",
|
|
@@ -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.",
|
|
@@ -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.
|
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
|