@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.
Files changed (30) hide show
  1. package/CHANGELOG.md +28 -5
  2. package/README.md +8 -0
  3. package/dist/cli/command-definitions.js +4 -2
  4. package/dist/cli/program.js +2 -1
  5. package/dist/cli/update/init-surface-notifier.js +167 -0
  6. package/dist/cli/update/policy.js +36 -1
  7. package/dist/cli/update/runtime-activity.js +29 -0
  8. package/dist/cli.js +14 -2
  9. package/dist/commands/init.js +85 -2
  10. package/dist/executors/shell-executor.js +92 -66
  11. package/dist/executors/shell-write-guard.js +5 -0
  12. package/dist/shared/runtime-activity.js +6 -0
  13. package/dist/worker/observability/read-model.js +10 -7
  14. package/dist/worker/observe/static/views/session-timeline.js +1 -1
  15. package/dist/workflows/dag/backend-test-case-manifest.js +13 -3
  16. package/dist/workflows/dag/backend-test-classification-contract.js +38 -0
  17. package/dist/workflows/dag/backend-test-contract-envelope.js +167 -0
  18. package/dist/workflows/dag/backend-test-semantic-review-contract.js +2 -2
  19. package/dist/workflows/dag/init-hybrid.js +64 -409
  20. package/dist/workflows/dag/node-execution.js +4 -3
  21. package/dist/workflows/dag/types.js +1 -5
  22. package/docs/README.md +1 -0
  23. package/docs/templates/agent-dag.schema.json +1 -1
  24. package/docs/templates/backend-test-case-manifest.schema.json +35 -2
  25. package/docs/templates/backend-test-dag.json +39 -340
  26. package/docs/templates/backend-test-dag.review-cases.prompt.md +4 -4
  27. package/package.json +1 -1
  28. package/skills/loop-agent/references/command-reference.md +4 -0
  29. package/skills/loop-agent/references/hybrid-dag.md +2 -2
  30. 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 a 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. A first review may return `request-revision` to drive a single `revise-backend-cases-pi` pass; **final** review (`review-backend-cases-final-pi`) must emit `VERDICT: pass` before `generate-backend-pytest-pi`.
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 revisions** (only when `request-revision` on the **first** review) — numbered, concrete MD edits for `revise-backend-cases-pi` (fix matrices, acIds, full BE-* ids).
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
- ### Final review note
87
+ ### Fail-fast note
88
88
 
89
- When this prompt is used for `review-backend-cases-final-pi` after revision, still use the same checklist. Remaining Critical issues must yield `request-revision` (final gate will stop the DAG). Do not pass with unresolved MD↔manifest AC mismatches.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tea-agent/loop-agent",
3
- "version": "0.16.19",
3
+ "version": "0.16.21",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "loop-agent": "bin/loop-agent.js",
@@ -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 会按真实 symbol 更新 generated Manifest 和 global/in-scope/cross-domain coverage,随后执行结构化语义 review,最多一次 testcase-only revision,final pass 后才运行 initial pytestObserve snapshot 投影 initial/classification/repair/final/effective facts。
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 个真实顶层节点**的后端测试 DAG:intake 双合同、cases+manifest、单次 case revision、pytest 生成与 semantic review、单次 semantic revision、initial execute+parse、classification+eligibility、单次 TestBug repairfinal/effective Result、retrospective 和 outcome gate。三条可选分支由 fail-closed `runIf` 控制,不通过动态子节点隐藏数量;所有原有 contracts/JUnit/Result/traceability/repair safety artifacts 继续保留。`knowledge-sync` 与 `knowledge-graph-bootstrap` 继续通过各自显式 taskKind 选择知识回写/图谱开荒 DAG。治理等级仍由 `minimal|standard|reviewed|supervised` 推断。
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
- }