@tea-agent/loop-agent 0.11.0 → 0.13.0-alpha.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.
Files changed (123) hide show
  1. package/CHANGELOG.md +74 -1
  2. package/README.md +33 -4
  3. package/dist/application/dag/generate-task-dag.js +45 -0
  4. package/dist/application/dag/run-dag.js +10 -0
  5. package/dist/application/dag/validate-dag.js +11 -0
  6. package/dist/cli/command-definitions.js +10 -3
  7. package/dist/commands/init.js +74 -7
  8. package/dist/commands/knowledge.js +129 -31
  9. package/dist/governance/manifest-types.js +3 -0
  10. package/dist/shared/package-metadata.js +135 -0
  11. package/dist/task/config-types.js +6 -1
  12. package/dist/worker/cli.js +99 -2
  13. package/dist/worker/delivery/package.js +3 -3
  14. package/dist/worker/feature/decision-loader.js +37 -6
  15. package/dist/worker/feature/next-action.js +10 -2
  16. package/dist/worker/feature/ready-plan-projection.js +81 -0
  17. package/dist/worker/feature/reducer.js +2 -1
  18. package/dist/worker/feature/review.js +19 -2
  19. package/dist/worker/feature/run.js +27 -2
  20. package/dist/worker/follow-up/approve.js +5 -2
  21. package/dist/worker/follow-up/factory.js +1 -1
  22. package/dist/worker/observability/event-history.js +216 -0
  23. package/dist/worker/observability/read-model.js +552 -118
  24. package/dist/worker/observe/paths.js +17 -0
  25. package/dist/worker/observe/routes.js +310 -23
  26. package/dist/worker/observe/server.js +59 -1
  27. package/dist/worker/observe/spec-evidence.js +281 -0
  28. package/dist/worker/observe/static/api.js +46 -0
  29. package/dist/worker/observe/static/app.js +120 -2598
  30. package/dist/worker/observe/static/constants.js +148 -0
  31. package/dist/worker/observe/static/copy.js +67 -0
  32. package/dist/worker/observe/static/dag-helpers.js +172 -0
  33. package/dist/worker/observe/static/dag-model.js +72 -0
  34. package/dist/worker/observe/static/dom.js +61 -0
  35. package/dist/worker/observe/static/format-pool.js +67 -0
  36. package/dist/worker/observe/static/format.js +292 -0
  37. package/dist/worker/observe/static/index.html +300 -82
  38. package/dist/worker/observe/static/kpi.js +94 -0
  39. package/dist/worker/observe/static/relations.js +133 -0
  40. package/dist/worker/observe/static/router.js +93 -0
  41. package/dist/worker/observe/static/run-processing.js +148 -0
  42. package/dist/worker/observe/static/shell-chrome.js +68 -0
  43. package/dist/worker/observe/static/state.js +253 -0
  44. package/dist/worker/observe/static/styles.css +1731 -495
  45. package/dist/worker/observe/static/views/batch.js +227 -0
  46. package/dist/worker/observe/static/views/dag-graph.js +172 -0
  47. package/dist/worker/observe/static/views/dag-inspector.js +596 -0
  48. package/dist/worker/observe/static/views/dag.js +362 -0
  49. package/dist/worker/observe/static/views/dashboard.js +445 -0
  50. package/dist/worker/observe/static/views/failures.js +143 -0
  51. package/dist/worker/observe/static/views/feature.js +492 -0
  52. package/dist/worker/observe/static/views/pool.js +350 -0
  53. package/dist/worker/observe/static/views/run.js +453 -0
  54. package/dist/worker/observe/static/views/session-timeline.js +205 -0
  55. package/dist/worker/observe/static/views/shell.js +7 -0
  56. package/dist/worker/observe/static/views/task.js +314 -0
  57. package/dist/worker/observe/static/views/timeline.js +163 -0
  58. package/dist/worker/pool/doctor.js +165 -0
  59. package/dist/worker/pool/migrate-state.js +303 -0
  60. package/dist/worker/pool/run-store.js +205 -17
  61. package/dist/worker/pool/types.js +17 -1
  62. package/dist/worker/pool/validation.js +100 -15
  63. package/dist/worker/report/morning-report.js +12 -2
  64. package/dist/worker/runner/run-ready.js +41 -26
  65. package/dist/worker/task-graph/ready-planner.js +136 -0
  66. package/dist/workflows/dag/controller-identity.js +104 -0
  67. package/dist/workflows/dag/convergence/controller.js +16 -8
  68. package/dist/workflows/dag/failure-routing.js +12 -1
  69. package/dist/workflows/dag/init-hybrid.js +1233 -11
  70. package/dist/workflows/dag/node-execution.js +123 -29
  71. package/dist/workflows/dag/repair-artifact.js +91 -0
  72. package/dist/workflows/dag/report.js +50 -0
  73. package/dist/workflows/dag/retry-policy.js +138 -0
  74. package/dist/workflows/dag/runner.js +32 -0
  75. package/dist/workflows/dag/runtime-contract.js +87 -0
  76. package/dist/workflows/dag/skill-snapshot.js +2 -0
  77. package/dist/workflows/dag/types.js +45 -1
  78. package/dist/workflows/dag/validate.js +68 -4
  79. package/docs/README.md +1 -1
  80. package/docs/agent-dag-recovery-playbook.md +9 -0
  81. package/docs/agent-dag-runner.md +26 -1
  82. package/docs/architecture/dag-execution.md +6 -0
  83. package/docs/architecture/evolution.md +7 -5
  84. package/docs/architecture/facts-and-state.md +15 -2
  85. package/docs/architecture/worker-and-feature.md +6 -2
  86. package/docs/decisions/README.md +3 -0
  87. package/docs/design/README.md +12 -3
  88. package/docs/exec-plans/active/README.md +2 -2
  89. package/docs/exec-plans/completed/README.md +12 -0
  90. package/docs/feature-workflow.md +108 -2
  91. package/docs/loop-agent-harness.md +15 -4
  92. package/docs/progress/README.md +22 -0
  93. package/docs/reports/README.md +14 -2
  94. package/docs/templates/agent-dag-report.schema.json +17 -0
  95. package/docs/templates/agent-dag.schema.json +69 -1
  96. package/docs/templates/agent-dag.supervised-implementation.json +8 -2
  97. package/docs/templates/backend-test-dag.generate-pytest.prompt.md +139 -0
  98. package/docs/templates/backend-test-dag.json +288 -0
  99. package/docs/templates/backend-test-dag.retrospect.prompt.md +125 -0
  100. package/docs/templates/backend-test-dag.review-cases.prompt.md +81 -0
  101. package/docs/templates/knowledge-graph-bootstrap-dag.json +118 -0
  102. package/docs/templates/knowledge-sync-dag.json +177 -0
  103. package/docs/templates/knowledge-sync-draft.schema.json +71 -0
  104. package/docs/verification-matrix.md +2 -1
  105. package/package.json +8 -2
  106. package/scripts/kb-bootstrap-init-skeleton.sh +239 -0
  107. package/scripts/kb-graph-incremental-prepare.mjs +372 -0
  108. package/scripts/kb-graph-incremental-prepare.sh +5 -0
  109. package/scripts/kb-graph-materialize.mjs +105 -0
  110. package/scripts/kb-graph-materialize.sh +4 -0
  111. package/scripts/kb-graph-promote.mjs +153 -0
  112. package/scripts/kb-graph-promote.sh +4 -0
  113. package/scripts/kb-query.mjs +554 -0
  114. package/scripts/kb-query.sh +5 -0
  115. package/skills/agent-worker/SKILL.md +3 -1
  116. package/skills/agent-worker/references/agent-worker-operator.md +18 -1
  117. package/skills/frontend-design-review/SKILL.md +26 -24
  118. package/skills/frontend-implementation/SKILL.md +29 -26
  119. package/skills/frontend-implementation/references/node-contracts.md +50 -19
  120. package/skills/frontend-review/SKILL.md +1 -1
  121. package/skills/loop-agent/references/command-reference.md +2 -0
  122. package/skills/loop-agent/references/hybrid-dag.md +22 -3
  123. package/skills/loop-agent/references/verification-and-failure-handling.md +6 -0
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { assertDagPromptSourceRule } from "./prompt-source.js";
3
+ import { dagRetryPolicySchema } from "./retry-policy.js";
3
4
  export const dagComplexitySchema = z.enum(["HIGH", "MED", "LOW"]);
4
5
  export const dagNodeExecutorSchema = z.enum([
5
6
  "pi",
@@ -33,6 +34,32 @@ export const dagRepairArtifactGateSchema = z.object({
33
34
  fromNodeId: z
34
35
  .string()
35
36
  .regex(/^[a-z][a-z0-9-]*$/, "fromNodeId must be kebab-case"),
37
+ /**
38
+ * Explicit reference to the governed Pi repair writer this gate feeds.
39
+ * New generated supervised DAGs must set this. Legacy DAGs without it are
40
+ * only accepted when a unique safe downstream Pi writer can be derived.
41
+ */
42
+ repairNodeId: z
43
+ .string()
44
+ .regex(/^[a-z][a-z0-9-]*$/, "repairNodeId must be kebab-case")
45
+ .optional(),
46
+ });
47
+ export const DAG_RUNTIME_CONTRACT_SCHEMA_VERSION = 1;
48
+ export const DAG_AGENT_RUNTIME_PI_ONLY = "pi-only";
49
+ export const DAG_REPAIR_WRITER_PROTOCOL_EXPLICIT_NODE_V1 = "explicit-node-v1";
50
+ export const dagRuntimeContractSchema = z.object({
51
+ schemaVersion: z.literal(DAG_RUNTIME_CONTRACT_SCHEMA_VERSION),
52
+ agentRuntime: z.literal(DAG_AGENT_RUNTIME_PI_ONLY),
53
+ repairWriterProtocol: z.literal(DAG_REPAIR_WRITER_PROTOCOL_EXPLICIT_NODE_V1),
54
+ /**
55
+ * Optional minimum controller version for diagnosis / fail-below. Capability
56
+ * fields above are primary; semver is only used for guidance when the
57
+ * controller is otherwise compatible.
58
+ */
59
+ minimumControllerVersion: z
60
+ .string()
61
+ .regex(/^\d+\.\d+\.\d+([-+].+)?$/, "minimumControllerVersion must be semver")
62
+ .optional(),
36
63
  });
37
64
  export const dagVerdictGateSchema = z.object({
38
65
  fromNodeId: z
@@ -44,7 +71,7 @@ export const dagVerdictGateSchema = z.object({
44
71
  });
45
72
  export const ENV_VAR_NAME_PATTERN = /^[A-Z_][A-Z0-9_]*$/;
46
73
  export const dagVersionSchema = z
47
- .union([z.literal(1), z.literal(2)])
74
+ .union([z.literal(1), z.literal(2), z.literal(3)])
48
75
  .default(1);
49
76
  export const dagRoleSchema = z.enum([
50
77
  "planner",
@@ -190,6 +217,7 @@ export const dagConvergenceSpecSchema = z
190
217
  stopOnVerdictPass: z.boolean().optional().default(true),
191
218
  stopOnHardVerifyPass: z.boolean().optional().default(true),
192
219
  pauseOnRegression: z.boolean().optional().default(true),
220
+ chainNodeIds: z.array(z.string()).optional(),
193
221
  })
194
222
  .optional();
195
223
  export const dagTaskSchema = z.object({
@@ -217,6 +245,7 @@ export const dagTaskSchema = z.object({
217
245
  allowedPaths: z.array(z.string()).optional().default([]),
218
246
  forbiddenPaths: z.array(z.string()).optional().default([]),
219
247
  decisionGate: dagDecisionGateSchema.optional(),
248
+ retryPolicy: dagRetryPolicySchema.optional(),
220
249
  dynamicExpansion: dagDynamicExpansionSchema.optional(),
221
250
  dynamicReduction: dagDynamicReductionSchema.optional(),
222
251
  dynamicCondition: dagDynamicConditionSchema.optional(),
@@ -240,6 +269,7 @@ export const dagSpecSchema = z
240
269
  .object({
241
270
  version: dagVersionSchema,
242
271
  title: z.string().min(1),
272
+ runtimeContract: dagRuntimeContractSchema.optional(),
243
273
  outputLanguage: dagOutputLanguageSchema.optional(),
244
274
  objective: z.string().optional(),
245
275
  successCriteria: z.array(z.string()).optional(),
@@ -252,6 +282,20 @@ export const dagSpecSchema = z
252
282
  tasks: z.array(dagTaskSchema).min(1),
253
283
  })
254
284
  .superRefine((spec, ctx) => {
285
+ if (spec.runtimeContract && spec.version !== 3) {
286
+ ctx.addIssue({
287
+ code: z.ZodIssueCode.custom,
288
+ message: "runtimeContract requires DagSpec version 3",
289
+ path: ["version"],
290
+ });
291
+ }
292
+ if (spec.version === 3 && !spec.runtimeContract) {
293
+ ctx.addIssue({
294
+ code: z.ZodIssueCode.custom,
295
+ message: "DagSpec version 3 requires runtimeContract",
296
+ path: ["runtimeContract"],
297
+ });
298
+ }
255
299
  // Catch raw cursor keys that Zod .strict() on nested objects already rejects when parsed
256
300
  // via parseDagSpec; this refine covers typed object construction paths.
257
301
  const models = spec.executorModels;
@@ -1,7 +1,9 @@
1
1
  import { DEFAULT_DAG_EXECUTOR_MODELS, ENV_VAR_NAME_PATTERN, } from "./types.js";
2
2
  import { resolveShellCommands } from "../../executors/shell-executor.js";
3
3
  import { pathMatchesPattern } from "../../shared/git-progress.js";
4
+ import { resolveRepairTaskForGate } from "./repair-artifact.js";
4
5
  import { topoSortToRanks } from "./topo.js";
6
+ import { isSafeReadOnlyPiRetryCandidate } from "./retry-policy.js";
5
7
  const GOVERNANCE_WARNING_TYPES = new Set([
6
8
  "read-only-missing-artifacts-forbidden",
7
9
  "read-only-prompt-mentions-artifact-writes",
@@ -176,14 +178,65 @@ function validateRepairArtifactGateConfig(task, spec, issues) {
176
178
  });
177
179
  }
178
180
  const upstream = spec.tasks.find((candidate) => candidate.id === repairArtifactGate.fromNodeId);
179
- if (!upstream)
180
- return;
181
- if (upstream.executor === "shell") {
181
+ if (upstream && upstream.executor === "shell") {
182
182
  issues.push({
183
183
  type: "invalid-repair-artifact-gate-config",
184
184
  message: `task ${task.id} shell.repairArtifactGate.fromNodeId "${repairArtifactGate.fromNodeId}" must reference a non-shell upstream artifact node`,
185
185
  });
186
186
  }
187
+ const resolution = resolveRepairTaskForGate({
188
+ tasks: spec.tasks,
189
+ gateTask: task,
190
+ });
191
+ if (!resolution.ok) {
192
+ issues.push({
193
+ type: "invalid-repair-artifact-gate-config",
194
+ message: `task ${task.id} ${resolution.reason}`,
195
+ });
196
+ return;
197
+ }
198
+ const hardVerifyCandidates = spec.tasks.filter((candidate) => candidate.depends_on.includes(resolution.repairTask.id) &&
199
+ candidate.executor === "shell" &&
200
+ candidate.role === "verifier" &&
201
+ candidate.shell?.verifyEvidence?.phase === "final" &&
202
+ candidate.shell.verifyEvidence.quota === "full" &&
203
+ candidate.shell.verifyEvidence.finalFullRequired === true);
204
+ if (hardVerifyCandidates.length !== 1) {
205
+ issues.push({
206
+ type: "invalid-repair-artifact-gate-config",
207
+ message: hardVerifyCandidates.length === 0
208
+ ? `task ${task.id} repair node "${resolution.repairTask.id}" must have one direct downstream final/full hard verification shell node with finalFullRequired=true`
209
+ : `task ${task.id} repair node "${resolution.repairTask.id}" has multiple hard verification shell nodes (${hardVerifyCandidates.map((candidate) => candidate.id).join(", ")})`,
210
+ });
211
+ return;
212
+ }
213
+ const hardVerify = hardVerifyCandidates[0];
214
+ const descendantIds = new Set();
215
+ const queue = [hardVerify.id];
216
+ while (queue.length > 0) {
217
+ const parentId = queue.shift();
218
+ for (const candidate of spec.tasks) {
219
+ if (candidate.depends_on.includes(parentId) &&
220
+ !descendantIds.has(candidate.id)) {
221
+ descendantIds.add(candidate.id);
222
+ queue.push(candidate.id);
223
+ }
224
+ }
225
+ }
226
+ const reviewCandidates = spec.tasks.filter((candidate) => descendantIds.has(candidate.id) &&
227
+ candidate.executor === "pi" &&
228
+ candidate.role === "reviewer" &&
229
+ !candidate.decisionGate?.enabled &&
230
+ candidate.writePolicy === "read-only" &&
231
+ (candidate.outputContract?.includes("VERDICT:") ?? false));
232
+ if (reviewCandidates.length !== 1) {
233
+ issues.push({
234
+ type: "invalid-repair-artifact-gate-config",
235
+ message: reviewCandidates.length === 0
236
+ ? `task ${task.id} hard verification node "${hardVerify.id}" must reach one read-only Pi reviewer with a VERDICT output contract`
237
+ : `task ${task.id} hard verification node "${hardVerify.id}" has multiple Pi reviewer nodes (${reviewCandidates.map((candidate) => candidate.id).join(", ")})`,
238
+ });
239
+ }
187
240
  }
188
241
  function validateShellVerdictGateGovernance(task, commands, issues) {
189
242
  if (task.executor !== "shell") {
@@ -229,7 +282,7 @@ function writeSetEntriesOverlap(a, b) {
229
282
  pathMatchesPattern(probeB, a)));
230
283
  }
231
284
  function shouldValidateWritePolicy(spec, task) {
232
- return (spec.version === 2 ||
285
+ return (spec.version >= 2 ||
233
286
  task.writePolicy !== undefined ||
234
287
  task.writeSet !== undefined);
235
288
  }
@@ -414,6 +467,16 @@ function validateStaticTaskConfig(task, issues) {
414
467
  });
415
468
  }
416
469
  }
470
+ function validateRetryPolicyTaskConfig(task, issues) {
471
+ if (task.retryPolicy === undefined)
472
+ return;
473
+ if (!isSafeReadOnlyPiRetryCandidate(task)) {
474
+ issues.push({
475
+ type: "invalid-retry-policy",
476
+ message: `task ${task.id} declares retryPolicy but is not a safe read-only non-dynamic Pi node; retry is only allowed for read-only/none Pi planner/scout/reviewer/verifier/closeout nodes without write, supervisor, or dynamic capabilities`,
477
+ });
478
+ }
479
+ }
417
480
  function validateDecisionGateTaskConfig(task, issues) {
418
481
  if (!task.decisionGate?.enabled) {
419
482
  return;
@@ -512,6 +575,7 @@ export function validateDagSpec(spec) {
512
575
  validateShellTaskConfig(task, spec, issues);
513
576
  validateStaticTaskConfig(task, issues);
514
577
  validateDecisionGateTaskConfig(task, issues);
578
+ validateRetryPolicyTaskConfig(task, issues);
515
579
  }
516
580
  validateSameRankWriteSetConflicts(spec, ranks, issues);
517
581
  validateSameRankAgentAttributionRisks(spec, ranks, issues);
package/docs/README.md CHANGED
@@ -55,7 +55,7 @@
55
55
  - `exec-plans/completed/README.md` — 已完成的执行计划(全量)
56
56
  - `progress/README.md` — 进度交接日志(全量)
57
57
  - `reports/README.md` — 验证与审计报告(全量);活能力摘要见 `reports/current-capability-summary.md`
58
- - `decisions/README.md` — 架构决策(ADR 0001–0003
58
+ - `decisions/README.md` — 架构决策(ADR 0001–0004;0004 = Task Pool feature-scoped identity
59
59
  - `skills/README.md` — repo-local skill registry and vetting notes
60
60
  - `templates/` — 可复用的规划、报告与 DAG 模板
61
61
 
@@ -17,6 +17,15 @@ recommended_follow_up
17
17
 
18
18
  Product-line taxonomy 定义见 `docs/design/state-and-failure-taxonomy.md`。
19
19
 
20
+ ### 前端设计门禁专用恢复路径
21
+
22
+ 前端 DAG 的 design gate shell 失败(`frontend-first-design-gate-shell`、`frontend-final-design-gate-shell`、`frontend-design-gate-shell`)**不路由为 `ProductBug` / `dev-fix`**。此类失败固定路由为:
23
+
24
+ - `productLineFailureCategory`: `ContractMismatch`
25
+ - `recommendedFollowUp`: `frontend-plan-revision-and-rerun`
26
+
27
+ 恢复动作由 `planDagRecovery` 根据实际的 `normalizedFailureCategory` 和 run status 决定(通常为 `rerun-after-fix` 或 `manual-review`),但 product-line 维度的分类确保 Task Pool 和 morning report 不会将其混入普通 bug backlog。
28
+
20
29
  **非目标(本 playbook 不覆盖、runner 不实现):**
21
30
 
22
31
  - 自动 retry / resume 节点执行
@@ -17,7 +17,32 @@ loop-agent run-dag --dag <temp-dir>/<task-id>-dag.json --cwd .
17
17
  - `static`:确定性生成的 artifacts 或 notes
18
18
  - `shell`:验证与文件系统检查
19
19
  - `pi`:规划、review、诊断;节点设 `toolProfile: "write"` 时有界写入
20
- - `cursor`:显式启用时的可选有界写后端
20
+
21
+ ## Retry (read-only Pi nodes)
22
+
23
+ planner/scout/reviewer/verifier/closeout 角色的只读 Pi 节点可声明 opt-in `retryPolicy`,用于在同一 run 内有界重试模型连接中断、provider 限流、临时不可用或请求 timeout。生成器会为这些安全节点自动声明默认策略:总尝试次数 3(手工配置上限 5),指数退避,单次等待上限 30s。
24
+
25
+ - 仅以下原始失败分类默认可重试:`timeout`、`network`、`rate-limit`、`unavailable`。
26
+ - `quota`、`auth`、`invalid-output`、`write-guard`、`decision-envelope` 与未知失败不重试。`quota` 不是 rate limit,不会被自动重试。
27
+ - 资格由确定性 helper 判断:仅 `writePolicy=read-only|none`(或 Pi 默认只读)的 planner/scout/reviewer/verifier/closeout 可用。supervisor、implementer、writer(`toolProfile=write` 或 `writePolicy=exclusive`)、docs-only、dynamic、shell、static 与 decision-gate 节点一律不重试,DAG validation 会拒绝其策略。
28
+ - 每次 attempt 写入独立不可变证据(`<node-id>/attempt-<n>.json`,run-relative path),最终 node record 的 `attempts` 字段引用完整 attempt 历史;后一次成功不会覆盖前一次失败证据。
29
+ - 重试期间复用同一 run、controller identity、skill snapshot、prompt、model 与上游输入。节点终态的 `durationMs`、`tokensUsed`、`parsedEvents` 聚合全部 attempts;退避等待会刷新 `lastActivityAt`,避免被误判为 node-quiet。当前退避会占用该节点所在的并发槽。
30
+
31
+ 示例:
32
+
33
+ ```json
34
+ {
35
+ "retryPolicy": {
36
+ "maxAttempts": 3,
37
+ "backoff": "exponential",
38
+ "initialDelayMs": 2000,
39
+ "maxDelayMs": 30000,
40
+ "retryCategories": ["timeout", "network", "rate-limit", "unavailable"]
41
+ }
42
+ }
43
+ ```
44
+
45
+ 未声明 `retryPolicy` 的历史 DAG 行为不变(单次执行、无 `attempts` 字段,也不新增 attempt artifact)。
21
46
 
22
47
  ## Skills
23
48
 
@@ -34,6 +34,12 @@ src/commands/dag-validate.ts runDagValidate
34
34
  → src/application/dag/validate-dag.ts validateDagUseCase
35
35
  ```
36
36
 
37
+ ### runtime contract preflight 与 repair writer 解析
38
+
39
+ - `src/workflows/dag/runtime-contract.ts` `assertRuntimeContractCompatible` 依据 controller capabilities(`DAG_CONTROLLER_CAPABILITIES`:`agentRuntime="pi-only"`、`repairWriterProtocol="explicit-node-v1"`)校验 DagSpec v3 必需的 `runtimeContract`。v3 让旧 controller 在解析阶段拒绝;新 controller 的 `validateDagUseCase`、`runDagUseCase`、`runDag` 与 resume 还会校验 capability 和可选最低版本,不兼容在任何节点执行前 fail-fast。legacy v1/v2 DagSpec 可读但没有 v3 握手。
40
+ - `src/workflows/dag/repair-artifact.ts` `resolveRepairTaskForGate` 解析 `shell.repairArtifactGate`:优先显式 `repairNodeId`,否则推导唯一的下游受治理 Pi writer(`repairWriterContractIssues` 校验 executor/toolProfile/writePolicy/path 契约)。`validate.ts` 与 `node-execution.ts` 复用同一 resolver,runtime 不再按节点名硬编码。
41
+ - `src/workflows/dag/controller-identity.ts` 在 run 创建前要求 controller identity 可解析,再由 `captureControllerIdentity` 冻结到 `<runDir>/controller-identity.json`;`verifyControllerIdentityForResume` 在 resume 前重新校验并对漂移、篡改或 legacy-unpinned run fail closed。
42
+
37
43
  ## rank 调度
38
44
 
39
45
  拓扑排序与按 rank 执行的符号归属(校准版,勿笼统归到 `runner.ts`):
@@ -1,8 +1,8 @@
1
1
  # 架构演进:当前 vs 未来
2
2
 
3
- 本页区分 loop-agent **当前已实现**的架构能力与**未来规划**。当前事实以代码、发布 CLI、已完成计划为准;未来能力一律标「规划 / 未实现 / 前瞻」。权威源:`CHANGELOG.md`、`docs/reports/current-capability-summary.md`、ADR 0001–0003、`docs/exec-plans/completed/`。
3
+ 本页区分 loop-agent **当前已实现**的架构能力与**未来规划**。当前事实以代码、发布 CLI、已完成计划为准;未来能力一律标「规划 / 未实现 / 前瞻」。权威源:`CHANGELOG.md`、`docs/reports/current-capability-summary.md`、ADR 0001–0004、`docs/exec-plans/completed/` 与 active identity plan 的 progress。
4
4
 
5
- ## 当前已实现(0.10.0 + 主干 Unreleased
5
+ ## 当前已实现(0.11.0)
6
6
 
7
7
  | 域 | 现状 | 权威入口 |
8
8
  | --- | --- | --- |
@@ -11,8 +11,10 @@
11
11
  | Dynamic Workflow | `WorkflowSpec` → validate → compile → 同一 `run-dag`;agent-like 节点仅 `pi\|static` | `src/workflows/dynamic/{spec,validate,compile}.ts`、`website/docs/guides/dynamic-workflow.md` |
12
12
  | 版本化自举 | controller identity + run-owned skill snapshot + deterministic canary | `docs/reports/2026-07-13-versioned-self-hosting-bootstrap.md` |
13
13
  | Feature 交付(M2) | review/run/approve-followup/delivery/closeout/verify-final | `docs/reports/2026-07-12-m2-completion-audit.md` |
14
- | Task Pool | 唯一根 `.harness/task-pool/` | ADR 0002 |
15
- | Observe | 本地只读暖白控制台(derived) | `website/docs/guides/observe-ui.md` |
14
+ | Task Pool | 唯一根 `.harness/task-pool/`;**feature-scoped** state v2(`TaskPoolTaskRef`) | ADR 0002、ADR 0004 |
15
+ | 本地多 Feature 运营 | 同仓库多 Feature 可共用 Task ID;Ready / retry / Delivery / Observe 按 Feature 隔离 | ADR 0004、`docs/progress/2026-07-15-task-pool-v2-feature-scoped-task-identity.md` |
16
+ | Observe | 本地只读暖白运营控制台 R1–R5(derived);canonical Task route 为 feature-scoped | `website/docs/guides/observe-ui.md`、`CHANGELOG.md [0.11.0]`、ADR 0004 |
17
+ | DagSpec / repair | v3 + `runtimeContract`;显式 `repairNodeId` | `CHANGELOG.md [0.11.0]`、`dag-execution.md` |
16
18
  | 文档双树 | `website/docs/` 用法 vs `docs/` 治理;docs-converge | ADR 0003 |
17
19
  | 文档治理 | `docs/architecture/` 主题文档(本目录)+ package 可达 | 本目录 README |
18
20
 
@@ -24,7 +26,7 @@
24
26
 
25
27
  ## 未来规划(第 3–6 月,**未实现**)
26
28
 
27
- 以下能力来自 `docs/design/六个月规划.md` 与 `docs/design/dynamic-workflow-dag-engine-roadmap.md`(两文件均带 2026-07-14 校准条,未交付 phase 为**设计输入**,不是已实现证明)。它们**当前不存在于代码或 CLI**:
29
+ 以下能力来自 `docs/design/六个月规划.md` 与 `docs/design/dynamic-workflow-dag-engine-roadmap.md`(六个月规划页首 2026-07-15 / 0.11.0 校准;未交付 phase 为**设计输入**,不是已实现证明)。它们**当前不存在于代码或 CLI**:
28
30
 
29
31
  | 未来方向 | 状态 | 规划来源 |
30
32
  | --- | --- | --- |
@@ -10,17 +10,30 @@
10
10
  | DAG run | `.harness/dag-runs/{active,paused,completed}/<runId>/` | `src/workflows/dag/lifecycle.ts` `DAG_RUNS_DIR` | canonical,active/paused 可写;completed 受 guard |
11
11
  | one-shot run | `.harness/runs/{active,completed,failed}/<slug>/` | `src/infrastructure/harness/one-shot-run-store.ts`(逻辑封装见 `src/records/one-shot-runs.ts`) | canonical;active 可写,completed/failed 为终态事实 |
12
12
  | Loop | `.harness/tasks/<taskId>/loop/` | `src/workflows/loop/**` | canonical,**不是独立根**,是 task 之上的多轮状态机 |
13
- | Task Pool | `.harness/task-pool/`(唯一根) | `src/worker/pool/run-store.ts` `TASK_POOL_RELATIVE_ROOT`(ADR 0002) | canonical,可写(Worker 专用,可选) |
13
+ | Task Pool | `.harness/task-pool/`(唯一根;state v2 见下) | `src/worker/pool/run-store.ts` `TASK_POOL_RELATIVE_ROOT`(ADR 0002 / 0004) | canonical,可写(Worker 专用,可选) |
14
14
  | Observe snapshot | Worker 内存/HTTP 派生视图 | `src/worker/observability/read-model.ts` `buildGlobalSnapshot` | **derived**,advisory |
15
15
 
16
16
  ### 区分要点
17
17
 
18
- - **Task vs DAG run**:Task 是用户意图的源(`source/`、需求、执行约束、artifacts);DAG run 是一次执行实例,`<runId>/` 下落 spec、state、节点 artifacts、skill snapshot、decision envelope、convergence。
18
+ - **Task vs DAG run**:Task 是用户意图的源(`source/`、需求、执行约束、artifacts);DAG run 是一次执行实例,`<runId>/` 下落 spec、state、节点 artifacts、skill snapshot、controller identity(`controller-identity.json`,冻结执行 controller 的 package version/binary 哈希/portable fingerprint,state 记录相对 ref 与内容哈希,resume 时重新校验、漂移 fail closed)、decision envelope、convergence。
19
19
  - **DAG run vs one-shot run**:DAG run 在 `.harness/dag-runs/`,有三态 lifecycle;one-shot run(当前主要由 `cursor-prompt` 及显式 one-shot evidence 路径产生)在 `.harness/runs/`,三态为 `active|completed|failed`。`pi-prompt` 当前不创建该目录下的 run evidence。两者是不同根、不同 schema。
20
20
  - **Loop 不是顶层根**:Loop 状态在 `.harness/tasks/<taskId>/loop/`,是 task 之上的多轮状态机(round、signal、context、failureStreak、closeout)。
21
21
  - **Task Pool 是 Worker 专用可选根**:只有使用 `agent-worker` 产品线时才存在;唯一根 `.harness/task-pool/`。
22
+ - **Task Pool state identity(ADR 0004)**:canonical 键为 `{ featureId, taskId }`(`TaskPoolTaskRef`),不是裸 `taskId`。
22
23
  - **Observe snapshot 不是事实源**:`buildGlobalSnapshot` 投影失败返回安全错误摘要而非全零健康,不改变执行成败。
23
24
 
25
+ ### Task Pool state 布局与迁移
26
+
27
+ | 路径 / 对象 | 性质 | 说明 |
28
+ | --- | --- | --- |
29
+ | `.harness/task-pool/states/<featureId>/<taskId>.json` | canonical 可写 | schema v2;路径解码 identity 必须与文件内容一致 |
30
+ | `.harness/task-pool/states/<taskId>.json`(扁平 v1) | legacy | 不得静默猜 Feature;存在时 v2 写入 fail-closed |
31
+ | `.harness/task-pool/runs.jsonl` / `events.jsonl` | canonical append-only | migration 不改写 JSONL;`workerRunId` 仍是执行历史键 |
32
+ | `agent-worker pool doctor` | 只读诊断 | inventory + mapping 证据;exit 0 可有 findings |
33
+ | `agent-worker pool migrate-state` | 显式迁移 | 默认 dry-run;`--apply --owner --reason` 才写入;失败全回滚 |
34
+
35
+ `readFeatureTaskPoolStates` / `listTaskPoolStates` 是 v2 列表入口;deprecated `readAllTaskPoolStates` 仅扫描 legacy 扁平 state,不合并 v2。
36
+
24
37
  ## canonical(可写)
25
38
 
26
39
  - `.harness/tasks/<taskId>/` — `getTaskDir`。
@@ -54,8 +54,12 @@ controller identity 与 DAG skill snapshot 是两个不同冻结层(前者跨
54
54
 
55
55
  - 唯一 runtime root:`src/worker/pool/run-store.ts`
56
56
  `TASK_POOL_RELATIVE_ROOT = ".harness/task-pool"`(ADR 0002)。
57
- - 旧顶层路径不读取、不迁移、不合并、不重映射。
58
- - batch/retry/morning report 等都基于此根。
57
+ - 此前的顶层 Task Pool 位置不读取、不迁移、不合并、不重映射。
58
+ - **Feature-scoped identity(ADR 0004)**:canonical Task 身份为复合键 `TaskPoolTaskRef = { featureId, taskId }`。`taskId` 仅 Feature 内唯一;同仓库多 Feature 可安全共用同名 Task ID。
59
+ - **State schema v2**:新 state 必须 `schemaVersion: 2` 且显式携带 `featureId` / `taskId`;canonical 路径为 `.harness/task-pool/states/<featureId>/<taskId>.json`。
60
+ - **Consumers**:runner Ready Queue、retry、Follow-up、Feature review、Delivery / Closeout、morning report / metrics 均按 Feature 作用域读写,不得把裸 `taskId` 当作仓库全局唯一键。
61
+ - **Operator**:`pool doctor` 只读 inventory;`pool migrate-state` 默认 dry-run,apply 需 `--owner` + `--reason`;legacy v1 写入路径 fail-closed。
62
+ - batch / retry / morning report 等都基于此根。
59
63
 
60
64
  ### Feature(M2 交付闭环)
61
65
 
@@ -9,6 +9,9 @@
9
9
  | [`0001-pi-only-agent-runtime.md`](0001-pi-only-agent-runtime.md) | accepted | 受治理 Agent 仅 Pi;`cursor-prompt` 为显式 sidecar |
10
10
  | [`0002-task-pool-runtime-root.md`](0002-task-pool-runtime-root.md) | accepted | Task Pool 唯一根 `.harness/task-pool/`,旧 `.task-pool/` 不兼容 |
11
11
  | [`0003-docs-dual-tree-converge.md`](0003-docs-dual-tree-converge.md) | accepted | `website/docs/` 用法 vs `docs/` 治理;docs-converge 检查表 |
12
+ | [`0004-task-pool-feature-scoped-task-identity.md`](0004-task-pool-feature-scoped-task-identity.md) | accepted | Task Pool 复合身份 `{featureId,taskId}`、state v2、legacy fail-closed、Observe composite route |
13
+
14
+ **读者注意(M4)**:ADR 0004 **决策边界**仍有效;正文中「M2+ 未改 consumers/Observe」段落是 M0/M1 当时的切片叙述,现已过时。实现进度以 `docs/progress/2026-07-15-task-pool-v2-feature-scoped-task-identity.md`、最终报告 `docs/reports/2026-07-15-task-pool-v2-feature-scoped-task-identity.md` 与 `CHANGELOG.md [Unreleased]` 为准。本任务 **不修改** ADR 0004 正文(不在 closeout allowedPaths)。
12
15
 
13
16
  新增跨版本架构取舍时:用模板新增 `NNNN-title.md`,并更新本表。不要把 ADR 正文复制进 `website/docs/`。
14
17
 
@@ -10,16 +10,24 @@
10
10
  | `taskspec-to-loop-agent-mapping.yaml` | 同上契约的结构化对照表 |
11
11
  | `state-and-failure-taxonomy.md` | 文档、Task Pool、DAG、Loop 共用的 canonical status 与 failure taxonomy |
12
12
  | `frontend-implementation-workflow.md` | 前端 DAG 实现 / 评审 / 验证工作流与证据约定 |
13
+ | `full-chain-test-knowledge-base.md` | 全流程测试知识库分层、目录、对象模型、与 backend-test-dag / Feature QA 的读写映射 |
14
+ | `full-chain-testing-system-redesign.md` | **综合重设计**:知识库权威层 + I/T/V/K 四类 DAG + knowledge-sync v2 硬化与迁移 |
15
+ | `testing-knowledge-base-impl-spec.md` | **知识库实现细节 v1(先冻结)**:目录、schema、ID、读写矩阵、校验规则、MVP 清单 |
16
+ | `knowledge-graph-and-query-spec.md` | **业务知识图谱 + 查询协议 v1**:实体/边、knowledge-links、kb-query 分层、索引 materialize |
17
+ | `knowledge-graph-ai-bootstrap.md` | **AI 构建图谱 + 初始化 B0–B6**:staging 提案、人审晋升、bootstrap DAG、增量更新 |
18
+ | `verification-subdag-plugin.md` | 独立测试全流程 Verification DAG 与三种实现 DAG 的 compose 设计(若分支存在) |
19
+
20
+ **执行 Contract(进行中):** `docs/exec-plans/active/2026-07-14-knowledge-sync-and-graph.md` · PRD/issues:`.scratch/knowledge-sync-and-graph/` |
13
21
 
14
22
  ## 路线与产品线笔记(非实现证明)
15
23
 
16
24
  | 文档 | 用途 |
17
25
  |---|---|
18
26
  | `dynamic-workflow-dag-engine-roadmap.md` | Dynamic Workflow 适配分析与阶段规划;**页首有实现状态 / Pi-only 校准条**(已落地 vs 设计输入);正文 Cursor 叙述视为历史 |
19
- | `六个月规划.md` | 长期路线;**第 1–2 月已收敛为 archive/reports 指针**,当前有效前瞻从第 3 月起 |
20
- | `产品线共享知识库.md` | 产品线文档仓库作为上游事实源 |
27
+ | `六个月规划.md` | 长期路线;**页首 2026-07-15 / 0.11.0 校准**;第 1–2 月为 archive/reports 指针,当前有效前瞻从第 3 月起 |
28
+ | `产品线共享知识库.md` | 产品线文档仓库作为上游事实源;**页首 2026-07-15 / 0.11.0 校准**(模板+Feature dogfood 已落地 vs docs-sync 未来) |
21
29
  | `研发模式.md` | 10 个工作日 Feature 团队工作流 |
22
- | `腾讯实践对当前项目的指引.md` | 腾讯 Harness Engineering 实践对本仓库的映射笔记 |
30
+ | `腾讯实践对当前项目的指引.md` | 腾讯 Harness Engineering 实践对本仓库的映射笔记;源文见 `website/docs/practices/tencent-harness-engineering/index.md` |
23
31
 
24
32
  ## 视觉参考
25
33
 
@@ -39,6 +47,7 @@
39
47
  | `archive/2026-07-10-observe-ui.md` | 已归档:Observe UI v0 设计(OBS-001~010;0.9.0 暖白重设计在其上演进) |
40
48
  | `archive/2026-07-10-observe-ui-optimization.md` | 已归档:Observe UI 中文化与过程时间线(UI-1~UI-9 已实现) |
41
49
  | `archive/2026-07-10-observe-ui-goal.md` | 已归档:OBS-001~010 逐任务进度看板(全部 done) |
50
+ | `archive/2026-07-14-observe-ui-roadmap.md` | 已归档:Observe R1–R5 运营控制台路线图(资源池/Batch-Run/Feature 关联/事件时间线/前端模块化;证据见 completed plans + reports) |
42
51
  | `archive/2026-07-11-第一月规划.md` | 已归档:首月落地计划(0.8.0 + Round 1/2/3 闭环) |
43
52
  | `archive/2026-07-11-第一月wbs.md` | 已归档:首月 WBS 与分工(历史记录) |
44
53
  | `archive/2026-07-12-第二月规划.md` | 已归档:第二月本地 Feature 交付闭环(M2-01~08;见 `docs/reports/2026-07-12-m2-completion-audit.md`) |
@@ -6,6 +6,6 @@
6
6
 
7
7
  当前 active execution plan:
8
8
 
9
- - 暂无。
9
+ - [`2026-07-14-knowledge-sync-and-graph.md`](2026-07-14-knowledge-sync-and-graph.md) — 知识库同步 DAG(测试结论/用例/缺陷写回)+ 业务知识图谱开荒与持续更新;Contract:`.scratch/knowledge-sync-and-graph/PRD.md`
10
10
 
11
- - 已归档:`../completed/2026-07-14-init-canonical-layout.md`、`../completed/2026-07-12-observe-warm-console-redesign.md`、`../completed/2026-07-14-website-docs-ia-and-converge.md`、`../completed/2026-07-13-versioned-self-hosting-bootstrap.md`、`../completed/2026-07-12-pi-only-agent-runtime.md`、第二月 M2-01~M2-08、`../completed/2026-07-11-observe-dashboard-page-system.md`、`../completed/2026-07-11-observe-dashboard-detail-refinement.md`、`../completed/2026-07-11-observe-terminal-dag-kpi.md`、`../completed/2026-07-11-observe-polling-efficiency.md`、`../completed/2026-07-11-command-performance-guardrails.md` 及更早计划。
11
+ - 已归档:`../completed/2026-07-15-repair-artifact-gate-runtime-contract.md`、`../completed/2026-07-14-observe-ui-r5.md`、`../completed/2026-07-14-observe-ui-r4.md`、`../completed/2026-07-14-observe-ui-r3.md`、`../completed/2026-07-14-observe-ui-r2.md`、`../completed/2026-07-14-observe-ui-r1.md`、`../completed/2026-07-14-init-canonical-layout.md`、`../completed/2026-07-12-observe-warm-console-redesign.md`、`../completed/2026-07-14-website-docs-ia-and-converge.md`、`../completed/2026-07-13-versioned-self-hosting-bootstrap.md`、`../completed/2026-07-12-pi-only-agent-runtime.md`、第二月 M2-01~M2-08、`../completed/2026-07-11-observe-dashboard-page-system.md`、`../completed/2026-07-11-observe-dashboard-detail-refinement.md`、`../completed/2026-07-11-observe-terminal-dag-kpi.md`、`../completed/2026-07-11-observe-polling-efficiency.md`、`../completed/2026-07-11-command-performance-guardrails.md` 及更早计划。
@@ -4,6 +4,13 @@
4
4
 
5
5
  npm 包携带本 README 作为目录契约。具体 completed plan 属于目标仓库历史,不从 loop-agent 源码历史复制。
6
6
 
7
+ - [`2026-07-15-repair-artifact-gate-runtime-contract.md`](2026-07-15-repair-artifact-gate-runtime-contract.md) — 显式 `repairNodeId` + 严格 repair writer 契约、DagSpec `runtimeContract` capability preflight,以及 run-owned controller identity(resume 漂移 fail-closed)
8
+ - [`2026-07-14-backend-test-dag-template.md`](2026-07-14-backend-test-dag-template.md) — 通过 `taskKind: "backend-test"` 生成需求分析、功能用例、pytest 自动化、执行与复盘的 Pi-only 专用 DAG
9
+ - [`2026-07-14-observe-ui-r5.md`](2026-07-14-observe-ui-r5.md) — Observe 原生 ES module 拆分、四态 helper、Pool hash 筛选与入口 re-export
10
+ - [`2026-07-14-observe-ui-r4.md`](2026-07-14-observe-ui-r4.md) — Observe Batch/Pool 有界事件时间线、cursor/limit、JSONL 容量与投影 fault-first
11
+ - [`2026-07-14-observe-ui-r2.md`](2026-07-14-observe-ui-r2.md) — Observe Batch / Worker Run 证据化详情、三层只读结构与安全 artifact preview
12
+ - [`2026-07-14-observe-ui-r3.md`](2026-07-14-observe-ui-r3.md) — Observe Feature 决策详情、copy-only 建议命令与 canonical 对象关系导航
13
+ - [`2026-07-14-observe-ui-r1.md`](2026-07-14-observe-ui-r1.md) — Observe 资源池总览、Task 下钻、精确对象关联与有界 run history
7
14
  - [`2026-07-14-init-canonical-layout.md`](2026-07-14-init-canonical-layout.md) — 目标项目治理资料统一到 `ai_workspace/loop-agent/` 与 `.agents/skills/`,并为旧布局提供保守安全迁移
8
15
  - [`2026-07-14-self-update-notifier-implementation.md`](2026-07-14-self-update-notifier-implementation.md) — 实现 loop-agent CLI 自更新提醒,覆盖拒绝版本、精确安装、npm global 来源证明和验证收口
9
16
  - [`2026-07-12-observe-warm-console-redesign.md`](2026-07-12-observe-warm-console-redesign.md) — 以暖白、细边界和高密度信息架构重构 Observe Dashboard;多轮细节调整后按用户确认收口归档。
@@ -60,3 +67,8 @@ npm 包携带本 README 作为目录契约。具体 completed plan 属于目标
60
67
  - [`2026-07-10-observe-ui-review-remediation.md`](2026-07-10-observe-ui-review-remediation.md) — 修复 Observe UI 事件链路、历史 run 投影、artifact 安全边界与失败状态展示
61
68
 
62
69
  - [`2026-07-13-exec-plan-lifecycle.md`](2026-07-13-exec-plan-lifecycle.md)
70
+
71
+ - [`2026-07-15-task-pool-priority-aware-ready-planner.md`](2026-07-15-task-pool-priority-aware-ready-planner.md)
72
+ - [`2026-07-15-task-pool-v2-feature-scoped-task-identity.md`](2026-07-15-task-pool-v2-feature-scoped-task-identity.md)
73
+ - [`2026-07-15-init-update-surface-coverage.md`](2026-07-15-init-update-surface-coverage.md)
74
+ - [`2026-07-15-readonly-node-retry.md`](2026-07-15-readonly-node-retry.md)
@@ -111,7 +111,10 @@ frontend-contract-pi
111
111
  -> frontend-scout-pi
112
112
  -> frontend-plan-pi
113
113
  -> frontend-design-gate-pi
114
- -> frontend-design-gate-shell
114
+ -> frontend-first-design-gate-shell
115
+ -> frontend-plan-revision-pi
116
+ -> frontend-final-design-review-pi
117
+ -> frontend-final-design-gate-shell
115
118
  -> frontend-implement-pi
116
119
  -> frontend-static-verify-shell
117
120
  -> frontend-behavior-verify-shell
@@ -120,7 +123,106 @@ frontend-contract-pi
120
123
  -> frontend-closeout-pi
121
124
  ```
122
125
 
123
- 这条链在实现前加入 design gate,并将前端静态验证与行为验证分开建模;当前 MVP 不包含独立 a11y、视觉回归或浏览器自动化 executor。
126
+ 这条链在实现前加入两阶段 design gate:首轮 design review 同时接受 `VERDICT: pass` 和 `VERDICT: request-revision`,request-revision 时由只读 `frontend-plan-revision-pi` 消费原计划与 design findings 完成修订,再经 `frontend-final-design-review-pi` 和 `frontend-final-design-gate-shell` 最终门禁;只有最终 `VERDICT: pass` 才授权写入。design gate 失败路由为 `ContractMismatch` / `frontend-plan-revision-and-rerun`,不路由为 `ProductBug` / `dev-fix`。
127
+
128
+ 这条链还将前端静态验证与行为验证分开建模;当前 MVP 不包含独立 a11y、视觉回归或浏览器自动化 executor。
129
+
130
+ 后端测试任务可通过 `task.json.taskKind = "backend-test"` 选择专用模板;它不新增 governance profile:
131
+
132
+ ```text
133
+ analyze-inputs-pi
134
+ -> generate-backend-functional-cases-pi
135
+ -> review-backend-cases-pi
136
+ -> review-backend-cases-gate-shell
137
+ -> generate-backend-pytest-pi
138
+ -> execute-backend-pytest-shell
139
+ -> test-retrospect-pi
140
+ ```
141
+
142
+ 这条链覆盖后端功能测试从需求分析到复盘评级的全链路流程:
143
+
144
+ 1. **analyze-inputs-pi**:读取需求.md 和开发详设等参考文档,产出端到端测试分析契约(范围、风险、策略要点)
145
+ 2. **generate-backend-functional-cases-pi**:根据契约生成结构化后端功能测试用例(Markdown),用例 ID 带 `BE-` 前缀(如 `BE-ORDER-001`),写入 `testcase/md/`
146
+ 3. **review-backend-cases-pi**:评审后端功能测试用例,输出审查报告 + `VERDICT: pass` / `VERDICT: request-revision`
147
+ 4. **review-backend-cases-gate-shell**:只有评审首条 verdict 为 `VERDICT: pass` 时才允许继续生成 pytest
148
+ 5. **generate-backend-pytest-pi**:将后端功能用例转化为 pytest 自动化代码,仅写入 `testcase/**/test_*.py`
149
+ 6. **execute-backend-pytest-shell**:执行 `pytest testcase/` 并生成 HTML 报告
150
+ 7. **test-retrospect-pi**:读取上游审查报告和测试报告,生成复盘报告 + 成熟度评级(A/B/C/D)
151
+
152
+ 最终验证后的知识库回写可通过 `task.json.taskKind = "knowledge-sync"` 选择专用模板(与 `backend-test` 一样走 taskKind 路由,不占用 governance `--profile`)。
153
+
154
+ **必须绑定 `featureId`**(fail-closed)。解析顺序:`task.json.featureId` → `hardConstraints` 中 `featureId=F-…` → 需求正文中的 `F-YYYY-NNN` → 若 `taskId` 本身是 `F-*`。生成器会把 writeSet 收窄到 `features/<featureId>/…`,validate 只检查该 Feature 下的 draft。
155
+
156
+ ```json
157
+ {
158
+ "taskKind": "knowledge-sync",
159
+ "featureId": "F-2026-004"
160
+ }
161
+ ```
162
+
163
+ ```text
164
+ knowledge-sync-collect-pi
165
+ -> knowledge-sync-draft-pi
166
+ -> knowledge-sync-validate-shell
167
+ -> knowledge-sync-apply-pi
168
+ -> knowledge-sync-pointer-pi
169
+ ```
170
+
171
+ 这条链在 shell 最终验证证据之后,把稳定事实写入 Feature 测试知识库(L1),而不是把 `.harness` 大日志搬进 docs:
172
+
173
+ 1. **knowledge-sync-collect-pi**:只读汇总 final verification / AC / 用例 / 缺陷 / 需求 delta 候选
174
+ 2. **knowledge-sync-draft-pi**:写入 `features/<featureId>/testing/sync/pending/knowledge-sync-draft.json`
175
+ 3. **knowledge-sync-validate-shell**:校验该路径 draft 的 schema、`featureId` 一致、operations 目标路径、finalVerification 门禁
176
+ 4. **knowledge-sync-apply-pi**:仅在 `features/<featureId>/testing/**`(及该 Feature 的 `requirement-delta.md`、`docs/test-reports/**`)受控回写
177
+ 5. **knowledge-sync-pointer-pi**:写 `features/<featureId>/testing/runs/latest.md` 与 `sync/applied/KS-*.json`
178
+
179
+ 设计说明见 `docs/design/full-chain-test-knowledge-base.md` §11;JSON 示例见 `docs/templates/knowledge-sync-dag.json`。
180
+
181
+ 业务知识图谱**初始化**可通过 `task.json.taskKind = "knowledge-graph-bootstrap"` 选择专用模板。跑前先落 B1 骨架:
182
+
183
+ ```bash
184
+ bash scripts/kb-bootstrap-init-skeleton.sh --root .
185
+ # 编辑 knowledge/bootstrap/scope.yaml 后再 run-task
186
+ ```
187
+
188
+ (需有 `knowledge/bootstrap/scope.yaml` 与 `status.yaml`;脚本幂等,默认不覆盖已有 scope/status,可用 `--force`。)
189
+
190
+ 图谱索引与 Phase A 查询(脚本,非 RAG):
191
+
192
+ ```bash
193
+ bash scripts/kb-graph-materialize.sh --root .
194
+ bash scripts/kb-query.sh --mode by_feature --feature F-2026-004 --json
195
+ bash scripts/kb-query.sh --mode by_id --id SVC-order --json
196
+ bash scripts/kb-query.sh --mode search --text "预占" --json
197
+ ```
198
+
199
+ **增量更新(非全量开荒)**:缩小 scope 后复用同一 `knowledge-graph-bootstrap` DAG(propose 仅针对 seeds/includes;promote 仍默认不覆盖已有正式文件):
200
+
201
+ ```bash
202
+ bash scripts/kb-graph-incremental-prepare.sh --root . \
203
+ --feature F-2026-004 --service order [--reset-staging]
204
+ # 审阅 knowledge/bootstrap/scope.yaml(update_mode: incremental)
205
+ # task.json.taskKind = "knowledge-graph-bootstrap"
206
+ loop-agent dag run-task <id>
207
+ bash scripts/kb-graph-materialize.sh --root .
208
+ ```
209
+
210
+ 测试知识日常写回仍用 `knowledge-sync`(`featureId` 必填),与图谱增量入口分离。
211
+
212
+ ```text
213
+ kg-bootstrap-preflight-shell
214
+ -> kg-bootstrap-inventory-shell
215
+ -> kg-bootstrap-propose-pi
216
+ -> kg-bootstrap-validate-shell
217
+ -> kg-bootstrap-review-pi
218
+ -> kg-bootstrap-review-gate-shell
219
+ -> kg-bootstrap-promote-shell
220
+ -> kg-bootstrap-materialize-shell
221
+ ```
222
+
223
+ AI 只写 `knowledge/bootstrap/staging/**`;禁止 self-`asserted`;review-gate 通过后 promote 仅合并新文件;materialize 写 `knowledge/graph/` 索引。设计见 `docs/design/knowledge-graph-ai-bootstrap.md`,示意 JSON 见 `docs/templates/knowledge-graph-bootstrap-dag.json`。
224
+
225
+ 前端测试模板(`frontend-test-dag`)后续沿用对称命名即可接入。
124
226
  前端 shell 验证优先使用任务源 `需求.md` / `执行约束.md` 中声明的前端验证命令,例如 `npm run typecheck`、`npm run build`、`npm test`;解析不到时再使用 adapter 验证命令和模板 fallback。
125
227
 
126
228
  `verify-shell` 使用 adapter 根据 task verify preset/quota 解析出的最终验证命令,并把新鲜 exit code/stdout/stderr 交给后续只读 verifier。review-gated 模板继续插入:
@@ -131,6 +233,10 @@ verify-shell -> verify-pi -> review-pi -> review-gate-shell -> closeout-pi
131
233
 
132
234
  supervised 模板在实现路径上增加 write-set audit、soft/hard shell 验证、process supervision、有界 repair、decision gates 与可选 convergence retry。
133
235
 
236
+ ### 只读 Pi 节点安全重试
237
+
238
+ 所有生成模板都会为安全的只读 Pi 节点(planner/scout/reviewer/verifier/closeout,且 `writePolicy=read-only|none`、非 writer、非 dynamic、非 decision-gate)自动声明默认 `retryPolicy`(总尝试 3 次,手工配置最多 5 次,指数退避,单次等待上限 30s)。supervisor 与 implementer 明确不在资格范围。仅重试 `timeout`、`network`、`rate-limit`、`unavailable`;`quota`、`auth`、`invalid-output`、`write-guard` 与未知失败不重试。每次 attempt 保留独立证据,详见 [docs/agent-dag-runner.md](./agent-dag-runner.md#retry-read-only-pi-nodes)。
239
+
134
240
  ### 可选 repo-local SDD skill 增强
135
241
 
136
242
  `dag run-task` 会在目标项目的 `.agents/skills/` 中探测三个可选 skill:
@@ -10,6 +10,7 @@ loop-agent 提供结构化 agent 工作的本地 harness。
10
10
  - `.harness/cache/` — 本地 runtime 缓存
11
11
  - `.harness/live/` — 瞬态 live-session 文件
12
12
  - `.harness/task-pool/` — `agent-worker` 的 Task Pool state、batch artifacts、failure handoffs 与 Observe events;它是 `.harness/` 内的独立 Worker runtime root,默认不提交
13
+ - `.harness/task-pool/states/<featureId>/<taskId>.json` — Task Pool schema v2 的 canonical state(ADR 0004);复合身份 `TaskPoolTaskRef = { featureId, taskId }`。扁平 `states/<taskId>.json` 为 legacy,不得静默解释
13
14
  - `.harness/dag-runs/<lifecycle>/<run-id>/.runtime/skill-snapshot.json` — 该 run 实际使用的 resolved skill profile snapshot;由 state 中相对 ref 与原始 bytes SHA-256 锚定,随 lifecycle 目录整体迁移
14
15
 
15
16
  ## Skill 指令
@@ -71,7 +72,7 @@ DAG agent 节点的输出语言由 `harness.json.workflowPolicy.dag.outputLangua
71
72
  - `loop init`、`loop run`、`loop status`、`loop closeout`
72
73
  - `pi-prompt`、`cursor-prompt`
73
74
  - `handoff check`、`spine audit`、`knowledge curate`
74
- - `agent-worker feature review|run|verify-final|delivery|closeout|approve-followup`、`agent-worker task validate|explain-profile|retry|draft-followup`、`agent-worker batch run-ready`、`agent-worker report morning|metrics`、`agent-worker observe serve|snapshot`
75
+ - `agent-worker feature review|run|verify-final|delivery|closeout|approve-followup`、`agent-worker task validate|explain-profile|retry|draft-followup`、`agent-worker batch run-ready`、`agent-worker pool doctor|migrate-state`、`agent-worker report morning|metrics`、`agent-worker observe serve|snapshot`
75
76
 
76
77
  `dag run-task` 在生成 DAG 草稿前会运行与 `plan check` 同源的 exec-plan 索引校验;active plan 与 `docs/exec-plans/active/README.md` 索引不一致时立即失败,避免运行昂贵节点后才在末端 shell verify 发现。`plan create` 优先复用目标项目模板并回退到发布包内置 `docs/templates/exec-plan.md`,`plan create`/`plan complete` 的多文件写入均有回滚保护;`new-task` 不自动绑定 exec-plan。
77
78
 
@@ -112,14 +113,24 @@ canary 会记录 tarball SHA-256、slot containment、两个 bin identity、由
112
113
 
113
114
  ## Worker Retry
114
115
 
115
- 失败 Task Pool task 必须显式 retry,不能删除 `.harness/task-pool/runs.jsonl` 或复用失败的 `workerRunId`:
116
+ 失败 Task Pool task 必须显式 retry,不能删除 `.harness/task-pool/runs.jsonl` 或复用失败的 `workerRunId`。canonical 命令必须携带 Feature:
116
117
 
117
118
  ```bash
118
- agent-worker task retry <task-id> --repo <target-repo> --reason "provider configuration corrected"
119
+ agent-worker task retry <task-id> --feature-id <feature-id> --repo <target-repo> --reason "provider configuration corrected"
119
120
  agent-worker batch run-ready --feature-dir <feature-dir> --repo <target-repo>
120
121
  ```
121
122
 
122
- `task retry` 只接受 `Failed` state,保留旧 run 和 failure handoff,并写入 `retryOfWorkerRunId`。下一次 `run-ready` 生成新的 `workerRunId`;`Blocked`、`Done` 或无 state 的 task 必须先由 operator 处理根因,不能盲目重试。
123
+ `task retry` 只接受指定 Feature 下的 `Failed` state,保留旧 run 和 failure handoff,并写入 `retryOfWorkerRunId`。跨 Feature 同名 Task 时省略 `--feature-id` 必须 fail-closed。下一次 `run-ready` 生成新的 `workerRunId`;`Blocked`、`Done` 或无 state 的 task 必须先由 operator 处理根因,不能盲目重试。
124
+
125
+ ## Task Pool Doctor 与 State 迁移
126
+
127
+ ```bash
128
+ agent-worker pool doctor --repo <target-repo> --json
129
+ agent-worker pool migrate-state --repo <target-repo> # dry-run 默认
130
+ agent-worker pool migrate-state --repo <target-repo> --apply --owner <owner> --reason <reason>
131
+ ```
132
+
133
+ `pool doctor` 只读扫描 v2 / legacy inventory 与映射证据,exit 0 也可带 findings。`pool migrate-state` 默认零写入;apply 需要 `--owner` 与 `--reason`,保留 bytes/hash 审计 artifact,失败全回滚,且不改写 `runs.jsonl` / `events.jsonl`。
123
134
 
124
135
  ## 验证 Preset
125
136