@tea-agent/loop-agent 0.16.19 → 0.16.21
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 +28 -5
- package/README.md +8 -0
- package/dist/cli/command-definitions.js +4 -2
- package/dist/cli/program.js +2 -1
- package/dist/cli/update/init-surface-notifier.js +167 -0
- package/dist/cli/update/policy.js +36 -1
- package/dist/cli/update/runtime-activity.js +29 -0
- package/dist/cli.js +14 -2
- package/dist/commands/init.js +85 -2
- package/dist/executors/shell-executor.js +92 -66
- package/dist/executors/shell-write-guard.js +5 -0
- package/dist/shared/runtime-activity.js +6 -0
- package/dist/worker/observability/read-model.js +10 -7
- package/dist/worker/observe/static/views/session-timeline.js +1 -1
- package/dist/workflows/dag/backend-test-case-manifest.js +13 -3
- package/dist/workflows/dag/backend-test-classification-contract.js +38 -0
- package/dist/workflows/dag/backend-test-contract-envelope.js +167 -0
- package/dist/workflows/dag/backend-test-semantic-review-contract.js +2 -2
- package/dist/workflows/dag/init-hybrid.js +64 -409
- package/dist/workflows/dag/node-execution.js +4 -3
- package/dist/workflows/dag/types.js +1 -5
- package/docs/README.md +1 -0
- package/docs/templates/agent-dag.schema.json +1 -1
- package/docs/templates/backend-test-case-manifest.schema.json +35 -2
- package/docs/templates/backend-test-dag.json +39 -340
- package/docs/templates/backend-test-dag.review-cases.prompt.md +4 -4
- package/package.json +1 -1
- package/skills/loop-agent/references/command-reference.md +4 -0
- package/skills/loop-agent/references/hybrid-dag.md +2 -2
- package/dist/workflows/dag/backend-test-repair-contract.js +0 -94
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Purpose
|
|
4
4
|
|
|
5
|
-
Use this prompt for
|
|
5
|
+
Use this prompt for the single read-only **backend test case review** node: `executor: "pi"`, `role: "reviewer"`, `writePolicy: "read-only"`. The reviewer audits generated backend functional test cases for completeness, format compliance, and traceability to source requirements. `VERDICT: request-revision` causes the deterministic `review-backend-cases-gate-shell` to fail; it does not authorize another writer in the same run.
|
|
6
6
|
|
|
7
7
|
Do **not** create `executor: reviewer`. Reviewer is a **role** on `executor: pi`.
|
|
8
8
|
|
|
@@ -82,8 +82,8 @@ Do NOT re-read source documents. Use the validated analysis artifact, case manif
|
|
|
82
82
|
1. **Coverage Assessment** — table mapping each AC to covering **full** test case IDs (or "uncovered" / gap).
|
|
83
83
|
2. **Findings** — bullet list tagged `Critical`, `Important`, or `Informational`.
|
|
84
84
|
3. **Statistics** — total case count, positive/negative/boundary breakdown, module distribution.
|
|
85
|
-
4. **Required
|
|
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
86
|
|
|
87
|
-
###
|
|
87
|
+
### Fail-fast note
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
There is no final review or in-run revision writer. Any `request-revision` verdict ends the current backend-test run at the deterministic case gate.
|
package/package.json
CHANGED
|
@@ -108,6 +108,7 @@ SDK 回归或 SDK 可选依赖不可用时用 `cli-only` 诊断。CLI fallback
|
|
|
108
108
|
| loop-agent 初始化 / loop agent 初始化 / loop agent初始化 / 初始化 loop-agent | 先运行 `loop-agent init instructions --repo-root .`,再运行 `loop-agent init --repo-root . --profile full --merge`,随后补全 README/验证矩阵并复查 doctor/inspect/docs audit |
|
|
109
109
|
| 初始化更新校验 / loop agent初始化更新校验 / 检查初始化更新 | `loop-agent init check-update --repo-root . --markdown`(只读,不得隐含 `apply-safe`) |
|
|
110
110
|
| 初始化安全更新 / loop agent初始化安全更新 / 应用初始化更新 | 先 `loop-agent init check-update --repo-root . --markdown`,再 `loop-agent init update --repo-root . --apply-safe`(surface 缺失时先 `--bootstrap-surface`;human decisions 存在时停下等用户) |
|
|
111
|
+
| 初始化对齐 / 升级后对齐 / init reconcile / 控制器升级后对齐目标项目 | `loop-agent init reconcile --repo-root .`(统一入口:surface 缺失返回 `needs-baseline` 零写入;human decisions 返回 `needs-human-decision` 零写入;活跃 DAG/Worker 或 Worker 状态无法确认时返回 `blocked-active-runtime` 零写入;其余执行 safe actions 后复查) |
|
|
111
112
|
|
|
112
113
|
```bash
|
|
113
114
|
loop-agent init instructions --repo-root <target-repo>
|
|
@@ -117,12 +118,15 @@ loop-agent init check-update --repo-root <target-repo> --json
|
|
|
117
118
|
loop-agent init check-update --repo-root <target-repo> --markdown
|
|
118
119
|
loop-agent init update --repo-root <target-repo> --bootstrap-surface
|
|
119
120
|
loop-agent init update --repo-root <target-repo> --apply-safe
|
|
121
|
+
loop-agent init reconcile --repo-root <target-repo> [--json|--markdown]
|
|
120
122
|
```
|
|
121
123
|
|
|
122
124
|
`init check-update` 是只读升级报告,用于发现目标项目是否落后于当前包内初始化 surface。输出会区分 deterministic actions、model merge tasks、human decisions 和 recommended next。`--markdown` 会渲染可直接交给模型执行的合并指引,包含 `allowedPaths`、`forbiddenPaths`、`mergeRules` 和 `verification`。
|
|
123
125
|
|
|
124
126
|
`init update --bootstrap-surface` 为旧项目写入 `.harness/init-surface.json` 的 `inferred-baseline`,不伪装成历史 recorded baseline。`init update --apply-safe` 只执行确定性安全动作:补缺失文件、创建目录、刷新 managed block;已有但无法确认与当前包一致的文件会进入 model merge tasks,不会被覆盖。
|
|
125
127
|
|
|
128
|
+
`init reconcile` 是升级后对齐入口:surface 缺失返回 `needs-baseline` 且零写入,存在 human decisions 返回 `needs-human-decision` 且零写入,活跃 DAG/Worker 或 Worker 状态无法确认时返回 `blocked-active-runtime` 且零写入;其余情况复用 `--apply-safe` 的安全动作并复查返回 `clean`/`needs-model-merge`/`needs-safe-update`。控制器升级成功后,普通安全仓库命令结束时会自动检测目标项目是否需要 reconcile;非 TTY 只向 stderr 输出 `loop-agent init reconcile --repo-root ...` 提示且不写入目标,TTY 且无 human decisions、无活跃运行时可经明确 `y/yes` 同意后应用 deterministic safe actions。
|
|
129
|
+
|
|
126
130
|
### 查看或复制内置示例
|
|
127
131
|
```bash
|
|
128
132
|
loop-agent examples list
|
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
前端专用链在 `frontend-scout-pi` 后加入只读 `frontend-mock-assess-pi` 和确定性 contract gate,根据接口契约、后端就绪度、生成期能力 seed 与固化验证入口选择 `native|browser-intercept|request-adapter|not-needed|blocked`。策略直接传给 plan、两阶段 design review、唯一 writer、实现 review 和 closeout;真实请求保持默认,缺少契约、生产隔离、注释真实请求或显式 required 合同不完整时 gate 阻塞。可选 `frontendMock` 配置声明 `auto|required|disabled`、既有服务目录与专项命令;默认 `auto` 下没有已确认 Mock 能力时跳过 Mock 继续实现并保留真实联调缺口,不安全或不完整的显式 required 合同只生成无 writer 的评估链,有可信命令时才增加 `frontend-mock-verify-shell`。Mock-backed 证据只证明前端状态;跳过 Mock 且未调用后端时 closeout 报告 `Frontend status: locally-validated` 与 `Real integration: pending`。
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
> Backend-test vNext:Analysis v2 明确 responseBody shape、ordering、field comparison/precision 与 sourceRefs,并兼容归一化 v1;pytest 生成后 traceability
|
|
22
|
+
> Backend-test vNext:Analysis v2 明确 responseBody shape、ordering、field comparison/precision 与 sourceRefs,并兼容归一化 v1;pytest 生成后 traceability 按真实 symbol 更新 generated Manifest 和 coverage,随后执行唯一一次结构化语义 review。任何 `request-revision` 由确定性 gate 终止当前 run。pytest 只执行一次,Observe 对新 run 投影 initial/canonical、classification 与 coverage;历史 repair/final evidence 仍兼容读取。
|
|
23
23
|
|
|
24
|
-
显式专用 `taskKind` 保持兼容并优先于任务源分类。`backend-test` 选择固定 **
|
|
24
|
+
显式专用 `taskKind` 保持兼容并优先于任务源分类。`backend-test` 选择固定 **15 个真实顶层节点**的后端测试 DAG:intake 双合同、cases+manifest、用例 pass-only gate、pytest 生成与 semantic review、语义 pass-only gate、单次 execute+parse、classification、canonical Result、retrospective 和 outcome gate。新 run 没有 case revision、semantic revision、repair eligibility、TestBug repair 或 final rerun;失败分类继续支持独立 follow-up。`knowledge-sync` 与 `knowledge-graph-bootstrap` 继续通过各自显式 taskKind 选择知识回写/图谱开荒 DAG。治理等级仍由 `minimal|standard|reviewed|supervised` 推断。
|
|
25
25
|
|
|
26
26
|
### DAG workflow 层级
|
|
27
27
|
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { z } from "zod";
|
|
5
|
-
import { writeDagRunJsonArtifact } from "../../infrastructure/harness/artifact-store.js";
|
|
6
|
-
import { BACKEND_TEST_CLASSIFICATION_CATEGORIES, backendTestResultContractSchema, } from "./backend-test-result-contract.js";
|
|
7
|
-
export const BACKEND_TEST_CLASSIFICATION_SCHEMA_ID = "backend-test-classification-v1";
|
|
8
|
-
export const backendTestClassificationContractSchema = z
|
|
9
|
-
.object({
|
|
10
|
-
category: z.enum(BACKEND_TEST_CLASSIFICATION_CATEGORIES),
|
|
11
|
-
evidence: z.array(z.string().min(1)).min(1),
|
|
12
|
-
confidence: z.number().min(0).max(1),
|
|
13
|
-
notes: z.string().min(1),
|
|
14
|
-
})
|
|
15
|
-
.strict();
|
|
16
|
-
function extractJsonObject(text) {
|
|
17
|
-
const trimmed = text.trim();
|
|
18
|
-
const fenced = trimmed.match(/^```json\s*([\s\S]*?)\s*```$/i);
|
|
19
|
-
return JSON.parse(fenced ? fenced[1] : trimmed);
|
|
20
|
-
}
|
|
21
|
-
export async function materializeBackendTestClassification(input) {
|
|
22
|
-
const wrapperPath = path.join(input.runDir, `${input.fromNodeId}.json`);
|
|
23
|
-
const wrapper = JSON.parse(await readFile(wrapperPath, "utf8"));
|
|
24
|
-
const raw = wrapper.assistantText?.trim() || wrapper.stdout?.trim() || "";
|
|
25
|
-
if (!raw)
|
|
26
|
-
throw new Error("backend-test classification output is empty");
|
|
27
|
-
const classification = backendTestClassificationContractSchema.parse(extractJsonObject(raw));
|
|
28
|
-
const relativePath = path.posix.join(input.outputDir, input.artifactName);
|
|
29
|
-
const artifactPath = await writeDagRunJsonArtifact(input.runDir, relativePath, classification);
|
|
30
|
-
const serialized = `${JSON.stringify(classification, null, 2)}\n`;
|
|
31
|
-
return {
|
|
32
|
-
path: artifactPath,
|
|
33
|
-
sha256: createHash("sha256").update(serialized).digest("hex"),
|
|
34
|
-
schemaId: BACKEND_TEST_CLASSIFICATION_SCHEMA_ID,
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
export function assessBackendTestRepairEligibility(input) {
|
|
38
|
-
const result = backendTestResultContractSchema.parse(input.result);
|
|
39
|
-
const classification = backendTestClassificationContractSchema.parse(input.classification);
|
|
40
|
-
const attempt = input.revisionAttempt ?? 0;
|
|
41
|
-
let reason = "eligible TestBug assertion failure";
|
|
42
|
-
let eligible = true;
|
|
43
|
-
if (attempt !== 0) {
|
|
44
|
-
eligible = false;
|
|
45
|
-
reason = "repair attempt already consumed";
|
|
46
|
-
}
|
|
47
|
-
else if (classification.category !== "TestBug") {
|
|
48
|
-
eligible = false;
|
|
49
|
-
reason = `classification ${classification.category} is not TestBug`;
|
|
50
|
-
}
|
|
51
|
-
else if (classification.confidence < 0.7) {
|
|
52
|
-
eligible = false;
|
|
53
|
-
reason = "TestBug confidence is below 0.7";
|
|
54
|
-
}
|
|
55
|
-
else if (result.executionStatus !== "completed" ||
|
|
56
|
-
result.outcome !== "completed-with-failures") {
|
|
57
|
-
eligible = false;
|
|
58
|
-
reason = "initial pytest did not complete with assertion failures";
|
|
59
|
-
}
|
|
60
|
-
else if (result.failed <= 0 || result.error !== 0) {
|
|
61
|
-
eligible = false;
|
|
62
|
-
reason = "repair requires failed>0 and error=0";
|
|
63
|
-
}
|
|
64
|
-
else if (result.failures.length === 0) {
|
|
65
|
-
eligible = false;
|
|
66
|
-
reason = "repair requires concrete failure summaries";
|
|
67
|
-
}
|
|
68
|
-
return {
|
|
69
|
-
schemaVersion: 1,
|
|
70
|
-
eligible,
|
|
71
|
-
reason,
|
|
72
|
-
revisionAttempt: attempt === 0 ? 0 : 1,
|
|
73
|
-
category: classification.category,
|
|
74
|
-
confidence: classification.confidence,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
export function buildBackendTestRepairEligibilityShellSnippet() {
|
|
78
|
-
return [
|
|
79
|
-
'test -n "${HARNESS_DAG_RUN_DIR:-}" || { echo "missing HARNESS_DAG_RUN_DIR for backend-test repair eligibility" >&2; exit 2; }',
|
|
80
|
-
`node -e 'const fs=require("fs"),path=require("path");const root=process.env.HARNESS_DAG_RUN_DIR;const r=JSON.parse(fs.readFileSync(path.join(root,"contracts","backend-test-result-initial.json"),"utf8"));const c=JSON.parse(fs.readFileSync(path.join(root,"contracts","backend-test-classification.json"),"utf8"));let eligible=true,reason="eligible TestBug assertion failure";if(c.category!=="TestBug"){eligible=false;reason="classification "+c.category+" is not TestBug";}else if(Number(c.confidence)<0.7){eligible=false;reason="TestBug confidence is below 0.7";}else if(r.executionStatus!=="completed"||r.outcome!=="completed-with-failures"){eligible=false;reason="initial pytest did not complete with assertion failures";}else if(Number(r.failed)<=0||Number(r.error)!==0){eligible=false;reason="repair requires failed>0 and error=0";}else if(!Array.isArray(r.failures)||r.failures.length===0){eligible=false;reason="repair requires concrete failure summaries";}const out={schemaVersion:1,eligible,reason,revisionAttempt:0,category:c.category,confidence:c.confidence};fs.writeFileSync(path.join(root,"contracts","backend-test-repair-eligibility.json"),JSON.stringify(out,null,2)+"\\n");process.stdout.write(JSON.stringify(out));'`,
|
|
81
|
-
].join("; ");
|
|
82
|
-
}
|
|
83
|
-
export function buildBackendTestEffectiveResultSelectorShellSnippet() {
|
|
84
|
-
return [
|
|
85
|
-
'test -n "${HARNESS_DAG_RUN_DIR:-}" || { echo "missing HARNESS_DAG_RUN_DIR for backend-test effective result" >&2; exit 2; }',
|
|
86
|
-
`node -e 'const fs=require("fs"),path=require("path"),crypto=require("crypto");const root=process.env.HARNESS_DAG_RUN_DIR;const contracts=path.join(root,"contracts");const finalPath=path.join(contracts,"backend-test-result-final.json");const initialPath=path.join(contracts,"backend-test-result-initial.json");const source=fs.existsSync(finalPath)?finalPath:initialPath;if(!fs.existsSync(source))throw new Error("missing initial/final backend-test result");const result=JSON.parse(fs.readFileSync(source,"utf8"));const target=path.join(contracts,"backend-test-result.json");const body=JSON.stringify(result,null,2)+"\\n";fs.writeFileSync(target,body);process.stdout.write(JSON.stringify({effectiveResult:path.basename(source),revisionAttempt:fs.existsSync(finalPath)?1:0,outcome:result.outcome,sha256:crypto.createHash("sha256").update(body).digest("hex")}));'`,
|
|
87
|
-
].join("; ");
|
|
88
|
-
}
|
|
89
|
-
export function buildBackendTestRepairSafetyShellSnippet() {
|
|
90
|
-
return [
|
|
91
|
-
'test -d testcase || { echo "missing testcase directory after TestBug repair" >&2; exit 1; }',
|
|
92
|
-
`node -e 'const fs=require("fs"),path=require("path");const root="testcase";const bad=[];function walk(p){for(const e of fs.readdirSync(p,{withFileTypes:true})){const f=path.join(p,e.name);if(e.isDirectory())walk(f);else if(/\\.py$/.test(e.name)){const s=fs.readFileSync(f,"utf8");if(/@pytest\\.mark\\.(?:skip|skipif|xfail)\\b|pytest\\.(?:skip|xfail)\\s*\\(/.test(s))bad.push(f+": skip/xfail is forbidden in automatic repair");if(/except\\s+(?:AssertionError|Exception|BaseException)\\s*:\\s*(?:pass|return)\\b/.test(s))bad.push(f+": broad failure swallowing is forbidden");}}}walk(root);if(bad.length){console.error(bad.join("\\n"));process.exit(1);}console.log("backend-test repair safety gate: pass");'`,
|
|
93
|
-
].join("; ");
|
|
94
|
-
}
|