@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.
- package/CHANGELOG.md +74 -1
- package/README.md +33 -4
- package/dist/application/dag/generate-task-dag.js +45 -0
- package/dist/application/dag/run-dag.js +10 -0
- package/dist/application/dag/validate-dag.js +11 -0
- package/dist/cli/command-definitions.js +10 -3
- package/dist/commands/init.js +74 -7
- package/dist/commands/knowledge.js +129 -31
- package/dist/governance/manifest-types.js +3 -0
- package/dist/shared/package-metadata.js +135 -0
- package/dist/task/config-types.js +6 -1
- package/dist/worker/cli.js +99 -2
- package/dist/worker/delivery/package.js +3 -3
- package/dist/worker/feature/decision-loader.js +37 -6
- package/dist/worker/feature/next-action.js +10 -2
- package/dist/worker/feature/ready-plan-projection.js +81 -0
- package/dist/worker/feature/reducer.js +2 -1
- package/dist/worker/feature/review.js +19 -2
- package/dist/worker/feature/run.js +27 -2
- package/dist/worker/follow-up/approve.js +5 -2
- package/dist/worker/follow-up/factory.js +1 -1
- package/dist/worker/observability/event-history.js +216 -0
- package/dist/worker/observability/read-model.js +552 -118
- package/dist/worker/observe/paths.js +17 -0
- package/dist/worker/observe/routes.js +310 -23
- package/dist/worker/observe/server.js +59 -1
- package/dist/worker/observe/spec-evidence.js +281 -0
- package/dist/worker/observe/static/api.js +46 -0
- package/dist/worker/observe/static/app.js +120 -2598
- package/dist/worker/observe/static/constants.js +148 -0
- package/dist/worker/observe/static/copy.js +67 -0
- package/dist/worker/observe/static/dag-helpers.js +172 -0
- package/dist/worker/observe/static/dag-model.js +72 -0
- package/dist/worker/observe/static/dom.js +61 -0
- package/dist/worker/observe/static/format-pool.js +67 -0
- package/dist/worker/observe/static/format.js +292 -0
- package/dist/worker/observe/static/index.html +300 -82
- package/dist/worker/observe/static/kpi.js +94 -0
- package/dist/worker/observe/static/relations.js +133 -0
- package/dist/worker/observe/static/router.js +93 -0
- package/dist/worker/observe/static/run-processing.js +148 -0
- package/dist/worker/observe/static/shell-chrome.js +68 -0
- package/dist/worker/observe/static/state.js +253 -0
- package/dist/worker/observe/static/styles.css +1731 -495
- package/dist/worker/observe/static/views/batch.js +227 -0
- package/dist/worker/observe/static/views/dag-graph.js +172 -0
- package/dist/worker/observe/static/views/dag-inspector.js +596 -0
- package/dist/worker/observe/static/views/dag.js +362 -0
- package/dist/worker/observe/static/views/dashboard.js +445 -0
- package/dist/worker/observe/static/views/failures.js +143 -0
- package/dist/worker/observe/static/views/feature.js +492 -0
- package/dist/worker/observe/static/views/pool.js +350 -0
- package/dist/worker/observe/static/views/run.js +453 -0
- package/dist/worker/observe/static/views/session-timeline.js +205 -0
- package/dist/worker/observe/static/views/shell.js +7 -0
- package/dist/worker/observe/static/views/task.js +314 -0
- package/dist/worker/observe/static/views/timeline.js +163 -0
- package/dist/worker/pool/doctor.js +165 -0
- package/dist/worker/pool/migrate-state.js +303 -0
- package/dist/worker/pool/run-store.js +205 -17
- package/dist/worker/pool/types.js +17 -1
- package/dist/worker/pool/validation.js +100 -15
- package/dist/worker/report/morning-report.js +12 -2
- package/dist/worker/runner/run-ready.js +41 -26
- package/dist/worker/task-graph/ready-planner.js +136 -0
- package/dist/workflows/dag/controller-identity.js +104 -0
- package/dist/workflows/dag/convergence/controller.js +16 -8
- package/dist/workflows/dag/failure-routing.js +12 -1
- package/dist/workflows/dag/init-hybrid.js +1233 -11
- package/dist/workflows/dag/node-execution.js +123 -29
- package/dist/workflows/dag/repair-artifact.js +91 -0
- package/dist/workflows/dag/report.js +50 -0
- package/dist/workflows/dag/retry-policy.js +138 -0
- package/dist/workflows/dag/runner.js +32 -0
- package/dist/workflows/dag/runtime-contract.js +87 -0
- package/dist/workflows/dag/skill-snapshot.js +2 -0
- package/dist/workflows/dag/types.js +45 -1
- package/dist/workflows/dag/validate.js +68 -4
- package/docs/README.md +1 -1
- package/docs/agent-dag-recovery-playbook.md +9 -0
- package/docs/agent-dag-runner.md +26 -1
- package/docs/architecture/dag-execution.md +6 -0
- package/docs/architecture/evolution.md +7 -5
- package/docs/architecture/facts-and-state.md +15 -2
- package/docs/architecture/worker-and-feature.md +6 -2
- package/docs/decisions/README.md +3 -0
- package/docs/design/README.md +12 -3
- package/docs/exec-plans/active/README.md +2 -2
- package/docs/exec-plans/completed/README.md +12 -0
- package/docs/feature-workflow.md +108 -2
- package/docs/loop-agent-harness.md +15 -4
- package/docs/progress/README.md +22 -0
- package/docs/reports/README.md +14 -2
- package/docs/templates/agent-dag-report.schema.json +17 -0
- package/docs/templates/agent-dag.schema.json +69 -1
- package/docs/templates/agent-dag.supervised-implementation.json +8 -2
- package/docs/templates/backend-test-dag.generate-pytest.prompt.md +139 -0
- package/docs/templates/backend-test-dag.json +288 -0
- package/docs/templates/backend-test-dag.retrospect.prompt.md +125 -0
- package/docs/templates/backend-test-dag.review-cases.prompt.md +81 -0
- package/docs/templates/knowledge-graph-bootstrap-dag.json +118 -0
- package/docs/templates/knowledge-sync-dag.json +177 -0
- package/docs/templates/knowledge-sync-draft.schema.json +71 -0
- package/docs/verification-matrix.md +2 -1
- package/package.json +8 -2
- package/scripts/kb-bootstrap-init-skeleton.sh +239 -0
- package/scripts/kb-graph-incremental-prepare.mjs +372 -0
- package/scripts/kb-graph-incremental-prepare.sh +5 -0
- package/scripts/kb-graph-materialize.mjs +105 -0
- package/scripts/kb-graph-materialize.sh +4 -0
- package/scripts/kb-graph-promote.mjs +153 -0
- package/scripts/kb-graph-promote.sh +4 -0
- package/scripts/kb-query.mjs +554 -0
- package/scripts/kb-query.sh +5 -0
- package/skills/agent-worker/SKILL.md +3 -1
- package/skills/agent-worker/references/agent-worker-operator.md +18 -1
- package/skills/frontend-design-review/SKILL.md +26 -24
- package/skills/frontend-implementation/SKILL.md +29 -26
- package/skills/frontend-implementation/references/node-contracts.md +50 -19
- package/skills/frontend-review/SKILL.md +1 -1
- package/skills/loop-agent/references/command-reference.md +2 -0
- package/skills/loop-agent/references/hybrid-dag.md +22 -3
- package/skills/loop-agent/references/verification-and-failure-handling.md +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,10 +2,79 @@
|
|
|
2
2
|
|
|
3
3
|
这里记录每个版本对使用者有影响的变化。命令名和配置字段保持原样,其余内容尽量用日常语言说明。
|
|
4
4
|
|
|
5
|
-
## [Unreleased]
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [0.13.0] - Unreleased
|
|
8
|
+
|
|
9
|
+
### 新增
|
|
10
|
+
|
|
11
|
+
- 前端 DAG 新增设计修订闭环:首轮 design review 输出 `request-revision` 时通过只读 `frontend-plan-revision-pi` 修订计划后经 `frontend-final-design-review-pi` 和 `frontend-final-design-gate-shell` 最终门禁,不再直接终止。只有最终 `VERDICT: pass` 才授权写入。
|
|
12
|
+
- Observe 节点检查器新增「规范证据」页签,展示 skill 注入状态、已读取规范文件(基于 session event 中成功完成的 read 工具调用)、检索操作(grep/find/ls)以及知识库查询;证据不足时提供明确提示。
|
|
13
|
+
- 规范证据接口强制要求通过 `toolCallId` 配对 `tool_execution_start` 与 `tool_execution_end` 事件;仅有 start 而无匹配成功 end、end 报错(`isError: true`)或缺失 complete 事件的 read 一律不计入已读取。
|
|
14
|
+
|
|
15
|
+
### 变更
|
|
16
|
+
|
|
17
|
+
- 前端 design gate shell 失败路由改为 `ContractMismatch` / `frontend-plan-revision-and-rerun`,不再路由为 `ProductBug` / `dev-fix`。
|
|
18
|
+
- 前端实现、design review 和 review skill 要求显式执行并记录规范检索与读取,以便后续新 run 在 Observe 中生成可观察证据。
|
|
19
|
+
|
|
20
|
+
### 破坏性变更
|
|
21
|
+
|
|
22
|
+
- Task Pool 任务身份升级为 feature-scoped 复合键 `{ featureId, taskId }`(`TaskPoolTaskRef`,ADR 0004)。新 state 使用 `schemaVersion: 2`,canonical 路径为 `.harness/task-pool/states/<featureId>/<taskId>.json`;裸 `taskId` 不再是仓库全局唯一键。
|
|
23
|
+
- `agent-worker task retry` 的 canonical 调用必须携带 `--feature-id`。跨 Feature 同名 Task 时省略 `--feature-id` 会 fail-closed,不会静默猜 Feature。
|
|
24
|
+
- 扁平 legacy `states/<taskId>.json` 在写入 / Ready 路径 fail-closed,不会被静默解释为某个 Feature 的状态。
|
|
25
|
+
|
|
26
|
+
### 迁移提示
|
|
27
|
+
|
|
28
|
+
1. 运行 `agent-worker pool doctor --repo <repo> --json` 盘点 v2 / legacy inventory。
|
|
29
|
+
2. 需要迁移时先 `agent-worker pool migrate-state --repo <repo>`(dry-run),确认映射后再 `--apply --owner <owner> --reason <reason>`。
|
|
30
|
+
3. 更新脚本与文档中的 retry 示例为 `task retry <task-id> --feature-id <feature-id> ...`。
|
|
31
|
+
4. Observe / 书签改用 `#/feature/:featureId/task/:taskId`;legacy `#/task/:taskId` 仅在 taskId 全库唯一时兼容。
|
|
32
|
+
5. `runs.jsonl` / `events.jsonl` 不会被 migrate 重写;历史 `workerRunId` 仍可追溯。
|
|
33
|
+
|
|
34
|
+
### 新增
|
|
35
|
+
|
|
36
|
+
- `agent-worker pool doctor`:只读 Task Pool inventory 与迁移证据(exit 0 也可带 findings)。
|
|
37
|
+
- `agent-worker pool migrate-state`:legacy → v2 显式迁移;默认 dry-run,apply 需 owner/reason,失败全回滚并保留审计 artifact。
|
|
38
|
+
- `agent-worker batch plan-ready`:零写入预览 TaskSpec priority 驱动的 selected、deferred 与 blocked 计划。
|
|
39
|
+
|
|
40
|
+
### 变更
|
|
41
|
+
|
|
42
|
+
- Runner Ready Queue、Feature review/run、Follow-up、Delivery / Closeout、morning report / metrics 与 Observe read-model 均按 Feature 作用域消费 state,同名 Task 跨 Feature 不串线。
|
|
43
|
+
- Observe canonical API:`GET /api/features/:featureId/tasks/:taskId[/runs]`;legacy bare task route 在歧义时返回 409 + `candidates`。
|
|
44
|
+
- Feature dry-run / review / next-action、morning report、GlobalSnapshot、Observe 消费同一 Ready Planner projection;前端与投影层不得独立重选或重排 ready tasks。
|
|
45
|
+
|
|
46
|
+
### 修复
|
|
47
|
+
|
|
48
|
+
- Windows 下发布/测试更稳:路径断言对齐 `realpathSync`(避免短路径与长路径比较失败)、Git Bash 解析与生产 `shell-executor` 一致(支持 `LOOP_AGENT_BASH` / `GIT_BASH` 与多候选路径),并放宽 self-host canary、Ring 1 materialize 与 Git-backed Delivery 集成测试在重负载下的超时上限。
|
|
49
|
+
- 前端实现 skill 会完整注入增长后的必需节点契约,不再因单引用预算仍停留在旧上限而截断设计修订闭环规则。
|
|
50
|
+
|
|
51
|
+
## [0.12.0] - 2026-07-16
|
|
52
|
+
|
|
53
|
+
### 新增
|
|
54
|
+
|
|
55
|
+
- 只读 Pi DAG 节点支持 opt-in 安全重试:仅 planner/scout/reviewer/verifier/closeout 角色且无写入能力的 Pi 节点可声明 `retryPolicy`,在同一 run 内重试模型连接中断(`network`)、provider 限流(`rate-limit`)、临时不可用(`unavailable`)或请求 timeout。生成模板默认总尝试 3 次,手工配置最多 5 次,并使用有上限的指数退避。每次 attempt 保留独立证据,节点耗时、Token 与事件数按尝试聚合;后一次成功不会覆盖前一次失败证据。`quota`、`auth`、`invalid-output`、`write-guard` 与未知失败不重试;supervisor、implementer、writer、dynamic、shell、static、docs-only、decision-gate 节点声明策略会在校验阶段失败。未声明策略的历史 DAG 不新增 attempt artifact,行为保持不变。
|
|
56
|
+
- 新增 `taskKind: "backend-test"` 专用 DAG:从需求分析开始,生成并评审后端功能用例,再生成和执行 pytest 自动化,最后产出测试复盘报告;新 DAG 沿用当前 Pi-only runtime、版本化 runtime contract 与既有执行恢复机制。
|
|
57
|
+
- 测试知识回写专用 DAG:`taskKind: "knowledge-sync"`。生成 collect→draft→validate→apply→pointer;**必须绑定 `featureId`**(`task.json.featureId` 或 hardConstraints / 需求中的 `F-…`),writeSet 收窄到 `features/<featureId>/…`。
|
|
58
|
+
- 业务知识图谱开荒 DAG:`taskKind: "knowledge-graph-bootstrap"`(preflight→inventory→propose→validate→review→gate→promote→materialize);AI 只写 `knowledge/bootstrap/staging/**`。
|
|
59
|
+
- 图谱辅助脚本与 CLI:`loop-agent knowledge query|graph-init|graph-materialize|graph-promote|graph-incremental-prepare`(薄封装 `scripts/kb-*`);`knowledge curate` 仍只负责 repair guidance 提案。
|
|
60
|
+
- B1 骨架、Phase A 查询(by_id / by_feature / search)、merge-new-only 晋升、增量 scope 准备与 materialize 索引;设计文档见 `docs/design/knowledge-*.md` 与 active plan `docs/exec-plans/active/2026-07-14-knowledge-sync-and-graph.md`。
|
|
61
|
+
|
|
62
|
+
### 修复
|
|
63
|
+
|
|
64
|
+
- `loop-agent init check-update` / `init update --apply-safe` 漏检初始化投影文件:fresh `init --profile full` 通过目录复制把整个 `docs/templates/` 与 `skills/`(镜像到 `.agents/skills/`)投影到目标项目,但期望 init surface 只跟踪其中一个子集。旧版本(如 0.11.0)初始化的项目升级 controller 后,缺失的 backend-test 模板、agent-dag schema/prompt、`loop-agent` 必需 references 等文件无法被报告或补齐,`check-update` 会错误返回 `ok: true`。现在期望 surface 会自动发现包内 `docs/templates/` 与 `skills/` 的所有文件并纳入校验;`check-update` 会报告全部缺失/陈旧 surface;`apply-safe` 会确定性补齐缺失文件,对无法证明 ownership 的旧文件仍保持 fail-safe(给出 model merge / human decision,不覆盖用户内容)。`scripts/check-init-surface.sh` 新增 drift 门,阻止“fresh init 会复制、surface 却不跟踪”的漂移。
|
|
65
|
+
|
|
66
|
+
## [0.11.0] - 2026-07-15
|
|
6
67
|
|
|
7
68
|
### 新增
|
|
8
69
|
|
|
70
|
+
- 受治理 DAG 的 repair 环节改为显式声明:`shell.repairArtifactGate` 新增 `repairNodeId`,新生成的 supervised DAG 会直接写明由哪个 Pi 修复节点承接 supervisor 的修订请求;旧 DAG 未声明时,只有在能唯一、安全地推导出下游 Pi 修复节点时才继续兼容,缺失、歧义或修复节点不满足受治理写入契约都会在执行前明确报错。
|
|
71
|
+
- 新生成的 DagSpec 升级为 `version: 3`,并携带必需的 `runtimeContract`(`agentRuntime` / `repairWriterProtocol` / 可选最低 controller 版本)。旧 controller 会因不认识 v3 而在解析阶段拒绝,新 controller 则在 `dag validate`、执行和 resume 前检查 capability 与最低版本,避免版本漂移运行到中途才失败;legacy v1/v2 DAG 继续可读。
|
|
72
|
+
- 每个新的 DAG run 必须把实际执行的 controller identity(package version、binary SHA-256、portable fingerprint)冻结为 run-owned 事实并记录内容哈希;identity 无法解析时不创建 run。`dag report` 会展示 identity 与 runtime-contract compatibility,resume 时重新校验;身份漂移、artifact 篡改或 legacy-unpinned run 都会拒绝继续。
|
|
73
|
+
- Observe R5:控制台前端拆为原生 ES modules(router/api/format/state/views 等),`app.js` 作轻量入口并 re-export 可测 API;统一 loading/empty/degraded/error 状态 helper;资源池支持可分享的 hash 筛选(`status`/`q`);不引入框架或构建器,R1–R4 路由/API/行为保持。
|
|
74
|
+
- Observe R4:Batch/Pool 有界事件历史 `GET /api/batches/:batchRunId/events` 与 `GET /api/events`(默认 limit 50、硬顶 200,opaque `cursor`/`nextCursor`,与 SSE/run 整数 `after` 分离);服务端尾部有界 JSONL 扫描;snapshot 事件归约容量硬顶并暴露 `projectionWarnings`;Pool 投影失败 fault-first,避免健康零值 KPI。
|
|
75
|
+
- Observe R3:`#/feature/:featureId` 决策详情(状态、AC 覆盖、blocking、risk、follow-ups、evidence、projectionWarnings);总览 Feature 可下钻;Feature/Task/Batch/Run/DAG 统一对象关系条,仅 snapshot 可证对象出链;建议命令仅可复制,与已执行事实分区。
|
|
76
|
+
- Observe R2:Batch 详情展示完整 summary(含 additive `recordErrors?`/`runErrors?`)、派生总耗时、Task 诊断列(失败分类、follow-up、harnessTaskId、起止)与页内安全 artifact preview;Worker Run 收敛为「主事实 / 执行过程 / 诊断证据」三层,Task/Batch/DAG 深链仅在 canonical 字段且 snapshot 命中时生成。`TaskSummary.batchRunId?` 对外保留。
|
|
77
|
+
- Observe R1:顶栏「资源池」与 `#/pool` 盘点当前 Worker Task(状态分布、最新 run、更新时间、失败分类);`#/task/:taskId` 下钻当前事实、真实关联 batch/run/DAG、失败证据与有界 run history(`GET /api/tasks/:taskId/runs`,limit/cursor,`retryOfWorkerRunId`)。`TaskSummary` additive `updatedAt?`;snapshot additive `taskPool.present` 区分未使用与空池。
|
|
9
78
|
- 新增版本更新提醒。发现新版本时可以直接确认升级;忽略某个版本后不会重复提醒,后续新版本仍会正常提示。可用 `LOOP_AGENT_DISABLE_UPDATE_CHECK=1` 关闭检查。
|
|
10
79
|
- 新增 `loop-agent plan create`、`plan complete` 和 `plan check`,统一维护执行计划及其索引,并在生成 DAG 前发现遗漏或不一致。
|
|
11
80
|
- 目标项目可以选择复用本地 SDD skills,为需求、设计、实现和评审节点补充方法指引;未安装时保持原有流程。
|
|
@@ -19,6 +88,8 @@
|
|
|
19
88
|
|
|
20
89
|
### 变更
|
|
21
90
|
|
|
91
|
+
- `observe serve` 提高对外访问稳定性:HTTP keep-alive 默认约 65s(避免 Node 5s 默认导致连接池复用失败),SSE 增加 keepalive comment 与 `X-Accel-Buffering: no`;新增 `--debug` 在 stderr 打印请求来源/方法/路径/状态/耗时。
|
|
92
|
+
- `observe serve` 允许显式绑定非 loopback 地址(如 `--host 0.0.0.0`)以便局域网访问;默认仍是 `127.0.0.1`,非本机绑定时会在 stderr 提示无鉴权风险。
|
|
22
93
|
- 根 README 在「核心概念」后增加极简「仓库地图」,方便一眼区分 `src/`、`skills/`、`.harness/`、`docs/` 等职责;细节仍以 `AGENTS.md` 与 `docs/README.md` 为准。
|
|
23
94
|
- 文档治理收敛:站上「当前规划」与 active plan 同步(无进行中计划时明确写出);`docs/README` 只索引核心契约与产物目录入口,progress/report 全量列表下沉到子目录 README;Dynamic Workflow 路线图迁入 `docs/design/`;补齐 design 漏索引与 DAG recovery playbook 入口。
|
|
24
95
|
- 活文档校准:Dynamic Workflow 路线图页首标明已落地 vs 设计输入(Pi-only);六个月规划第 1–2 月改为 archive/reports 指针;`repository-analysis` 冻结为基线快照,新增 `docs/reports/current-capability-summary.md`;落地 ADR 0001–0003(Pi-only、Task Pool 根、双树/docs-converge)。
|
|
@@ -30,6 +101,8 @@
|
|
|
30
101
|
|
|
31
102
|
### 修复
|
|
32
103
|
|
|
104
|
+
- 修复 supervised convergence DAG 在 `process-gate-shell` 报「repair artifact gate cannot find downstream repair task」的问题:修复环节不再按固定节点名(历史上的 `repair-cursor` / `repair-pi`)猜测,而是根据 gate 声明的 `repairNodeId` 或唯一安全的下游 Pi 写入节点解析,合法的 `request-revision` 修订请求能正常走完 repair、hard verify 与 review。
|
|
105
|
+
- Observe UI R5:可变 UI 状态改为 `uiState` plain object,修复 Chrome 中 ES module namespace 赋值导致的路由崩溃;`app.js` 收为约 136 行 bootstrap/route/listeners/re-export,KPI、DAG helpers、run processing 与 shell chrome 迁入职责模块,移除 nullable `registerShell/getShell` locator;Pool grouped filters、四态、DAG inspector ARIA/方向键同步收敛,并通过 focused tests、构建治理与 fresh dist Chrome 验证。
|
|
33
106
|
- 前端 DAG 从 `需求.md` / `执行约束.md` 读取验证命令时不再交给 shell 解释执行;命令会先解析为受限参数列表并拒绝 shell 特殊语法,避免任务文本触发额外命令。
|
|
34
107
|
- 改进 Windows 下 Git Bash、npm 和集成测试的运行方式,减少环境识别错误与资源竞争导致的误失败。
|
|
35
108
|
|
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ loop-agent --help
|
|
|
27
27
|
如果你拒绝版本 A,当前系统用户下不会再提醒 A;之后发布版本 B 时会继续提醒。确认更新时,CLI 会先证明当前安装来自同一 npm global root,然后安装刚确认的精确版本,例如:
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
npm install -g @tea-agent/loop-agent@0.
|
|
30
|
+
npm install -g @tea-agent/loop-agent@0.13.0
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
需要完全关闭自动检查时设置:
|
|
@@ -78,7 +78,7 @@ loop-agent init update --repo-root <target-repo> --bootstrap-surface
|
|
|
78
78
|
loop-agent init update --repo-root <target-repo> --apply-safe
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
`check-update` 只读报告 deterministic actions、model merge tasks、human decisions 和 recommended next
|
|
81
|
+
`check-update` 只读报告 deterministic actions、model merge tasks、human decisions 和 recommended next。期望 init surface 会自动发现包内 `docs/templates/` 与 `skills/` 的所有文件,因此 fresh `init --profile full` 投影到目标项目的每个 template/skill 文件都会被纳入校验。`update --bootstrap-surface` 为旧项目补 inferred baseline;`update --apply-safe` 只补缺失文件、目录和 managed block(包括过期的 `.gitignore` managed block),不覆盖已有但无法确认来源的本地文件;对在旧路径被用户修改过的遗留副本,会在对应标准路径上给出 model merge / human decision,不会静默用包内容覆盖。
|
|
82
82
|
|
|
83
83
|
当初始化由模型/Agent 执行时,它应把初始化当成一个自动化闭环:确认真正不能安全默认的 provider/model、治理根目录或凭据/成本问题后,运行 deterministic init,随后立刻读取目标项目真实文件,补全根 README 的项目概览、技术栈/目录结构、开发与验证命令,并同步适配 `ai_workspace/loop-agent/verification-matrix.md` 和必要的 `scripts/ci-tests.sh`。
|
|
84
84
|
|
|
@@ -95,7 +95,26 @@ loop-agent dag validate --dag <temp-dir>/<task-id>-dag.json --strict-models --st
|
|
|
95
95
|
loop-agent run-dag --dag <temp-dir>/<task-id>-dag.json --cwd .
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
-
当前 `dag run-task` 也支持按 `task.json.taskKind`
|
|
98
|
+
当前 `dag run-task` 也支持按 `task.json.taskKind` 选择专用模板;`frontend-implementation` 会生成前端契约、design gate、静态验证、行为验证和 review gate 节点链,`backend-test` 会生成从需求分析、功能用例、pytest 自动化到测试复盘的专用节点链。
|
|
99
|
+
|
|
100
|
+
测试结论写回与业务知识图谱(结构化 Git,非 RAG):
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
# 最终验证后:同步结论/用例/缺陷到 features/<F>/testing/**(须 featureId)
|
|
104
|
+
# task.json: { "taskKind": "knowledge-sync", "featureId": "F-2026-004" }
|
|
105
|
+
loop-agent dag run-task <task-id> --strict-models
|
|
106
|
+
|
|
107
|
+
# 图谱开荒 / 增量(AI 只写 staging,晋升默认不覆盖已有正式文件)
|
|
108
|
+
# task.json: { "taskKind": "knowledge-graph-bootstrap" }
|
|
109
|
+
loop-agent knowledge graph-init --product-name my-app
|
|
110
|
+
loop-agent knowledge graph-incremental-prepare --feature F-2026-004 --service order
|
|
111
|
+
loop-agent dag run-task <task-id> --strict-models
|
|
112
|
+
loop-agent knowledge graph-promote
|
|
113
|
+
loop-agent knowledge graph-materialize
|
|
114
|
+
loop-agent knowledge query --mode by_feature --feature F-2026-004 --json
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`knowledge curate` 仍只用于 repair/learned guidance 提案,与图谱查询不是同一能力。细节见 `docs/feature-workflow.md` 与 `docs/design/knowledge-graph-ai-bootstrap.md`。
|
|
99
118
|
|
|
100
119
|
`<temp-dir>` 表示平台原生临时目录;也可以省略 `--output`,再使用命令 JSON 输出里的 `outputPath`。
|
|
101
120
|
|
|
@@ -158,7 +177,16 @@ Morning report 和 Observe snapshot/UI 复用同一 Feature reducer,优先展
|
|
|
158
177
|
|
|
159
178
|
nightly wrapper 按 feature 互斥,保留批次/超时退出码,并输出 morning report、Observe snapshot 和 controller 版本 artifacts。
|
|
160
179
|
|
|
161
|
-
自 0.8.0 起,`.harness/task-pool/` 是唯一受支持的 Task Pool runtime root,其中包含根级 `runs.jsonl`、`events.jsonl`、`states/`、`artifacts/`、`failure-handoffs/`、`observability/` 和 `reports/`。这是 hard cutover
|
|
180
|
+
自 0.8.0 起,`.harness/task-pool/` 是唯一受支持的 Task Pool runtime root,其中包含根级 `runs.jsonl`、`events.jsonl`、`states/`、`artifacts/`、`failure-handoffs/`、`observability/` 和 `reports/`。这是 hard cutover:此前的顶层 Task Pool 位置不读取、不迁移、不合并、不重映射。
|
|
181
|
+
|
|
182
|
+
Task Pool **state identity** 进一步固定为 feature-scoped 复合键 `{ featureId, taskId }`(ADR 0004):
|
|
183
|
+
|
|
184
|
+
- canonical state 路径:`.harness/task-pool/states/<featureId>/<taskId>.json`(schema v2,文件内容必须携带 `featureId` / `taskId`)
|
|
185
|
+
- 同仓库多 Feature 可安全使用相同 Task ID(例如 `F-A/QA-EXEC-001` 与 `F-B/QA-EXEC-001` 互不覆盖)
|
|
186
|
+
- retry 必须显式指定 Feature:`agent-worker task retry <task-id> --feature-id <feature-id> --repo <repo> --reason <reason>`
|
|
187
|
+
- 诊断与迁移:`agent-worker pool doctor --repo <repo> --json`(只读);`agent-worker pool migrate-state --repo <repo>` 默认 dry-run,apply 需 `--owner` + `--reason`
|
|
188
|
+
- 扁平 legacy `states/<taskId>.json` 不得静默解释;存在时新写入 fail-closed,必须经 doctor / migrate 处理
|
|
189
|
+
- Observe 只读:canonical `#/feature/:featureId/task/:taskId` 与 `GET /api/features/:featureId/tasks/:taskId`;legacy bare task route 仅在唯一命中时兼容,歧义返回 409 + candidates
|
|
162
190
|
|
|
163
191
|
## 核心概念
|
|
164
192
|
|
|
@@ -169,6 +197,7 @@ nightly wrapper 按 feature 互斥,保留批次/超时退出码,并输出 mo
|
|
|
169
197
|
- **可选 SDD skill 嵌入**:如果目标项目在 `.agents/skills/` 中提供 `SDD-requirement-analysis`、`SDD-design-analysis`、`SDD-implementation-test-review`,`dag run-task` 会把它们作为知识与方法补充追加到对应的 Contract、Plan、Implement/Repair、Verify、Review 节点。loop-agent 仍控制 DAG、状态、写入边界、验证和收口;不会自动运行 SDD 初始化/扫描 skill,也不会推进 `ai_workspace` 状态或归档。没有这些 repo-local skills 时,生成结果保持原有默认流程。
|
|
170
198
|
- **run-owned skill snapshot**:新 DAG run 会在任何节点执行前,把本次实际注入 prompt 的 resolved skill profiles 冻结到 run 自己的 `.runtime/skill-snapshot.json`。后续节点、dynamic child、approve/resume 都使用同一份 hash-anchored snapshot;run 内修改 skill 只会从下一次 run 生效。
|
|
171
199
|
- **controller identity**:`agent-worker` 把一次 Feature/batch 实际使用的发布包、入口、启动参数和 package 内容 fingerprint 固定下来,并把 identity 传播到 Worker、Task Pool、batch/Feature 与最终验证证据。
|
|
200
|
+
- **只读 Pi 节点安全重试**:仅 planner/scout/reviewer/verifier/closeout 这类没有仓库写入能力的 Pi 节点,遇到模型连接中断、provider 限流、临时不可用或 timeout 时,可在同一 run 内有界重试;生成模板会自动声明默认 `retryPolicy`(总尝试 3 次、最多可配置 5 次、指数退避、单次等待上限 30s)。每次 attempt 保留独立证据,耗时与 Token 用量按尝试聚合,后一次成功不覆盖前一次失败证据。`quota`/`auth`/`invalid-output`/`write-guard` 与未知失败不重试;supervisor、implementer、writer、dynamic、shell、static、docs-only、decision-gate 节点不重试。
|
|
172
201
|
- **`agent-worker` operator skill**:`skills/agent-worker/` 只负责 Feature Packet、TaskSpec、Task Pool、自举 release train 和失败恢复的外层路由;单个 DAG 实现、DAG kernel 修复和节点执行仍由 `loop-agent` 负责,该 skill 不进入默认 DAG role skills。
|
|
173
202
|
- **治理文档**:`docs/` 保存原则、工作流、验证矩阵、runtime 边界、计划和报告。
|
|
174
203
|
- **shell verification**:完成声明必须有可复现命令作为依据,而不是只靠聊天结论。
|
|
@@ -204,6 +204,9 @@ export async function generateTaskDagUseCase(input) {
|
|
|
204
204
|
await assertExecPlanIndexConsistent(repoRoot);
|
|
205
205
|
const taskConfig = await loadTaskConfig(repoRoot, parsed.taskId);
|
|
206
206
|
const isFrontendImplementationTask = taskConfig.taskKind === "frontend-implementation";
|
|
207
|
+
const isBackendTestTask = taskConfig.taskKind === "backend-test";
|
|
208
|
+
const isKnowledgeSyncTask = taskConfig.taskKind === "knowledge-sync";
|
|
209
|
+
const isKnowledgeGraphBootstrapTask = taskConfig.taskKind === "knowledge-graph-bootstrap";
|
|
207
210
|
const candidateResult = await initHybridDagFromTask(repoRoot, parsed.taskId, {
|
|
208
211
|
outputPath: parsed.outputPath,
|
|
209
212
|
template: "standard-dag",
|
|
@@ -230,6 +233,48 @@ export async function generateTaskDagUseCase(input) {
|
|
|
230
233
|
profileRouting.selectedByProfile = parsed.profile;
|
|
231
234
|
}
|
|
232
235
|
}
|
|
236
|
+
if (isBackendTestTask) {
|
|
237
|
+
profileRouting.selectedTemplate = "backend-test-dag";
|
|
238
|
+
profileRouting.source = "taskKind";
|
|
239
|
+
profileRouting.routingReasons = [
|
|
240
|
+
'taskKind "backend-test" selects the dedicated backend test DAG template',
|
|
241
|
+
];
|
|
242
|
+
if (parsed.profile === "auto") {
|
|
243
|
+
profileRouting.selectedByProfile =
|
|
244
|
+
resolveAutoRoutingProfile(profileRouting.candidateProfile);
|
|
245
|
+
}
|
|
246
|
+
else if (parsed.profileExplicit) {
|
|
247
|
+
profileRouting.selectedByProfile = parsed.profile;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (isKnowledgeSyncTask) {
|
|
251
|
+
profileRouting.selectedTemplate = "knowledge-sync-dag";
|
|
252
|
+
profileRouting.source = "taskKind";
|
|
253
|
+
profileRouting.routingReasons = [
|
|
254
|
+
'taskKind "knowledge-sync" selects the dedicated knowledge-sync DAG template',
|
|
255
|
+
];
|
|
256
|
+
if (parsed.profile === "auto") {
|
|
257
|
+
profileRouting.selectedByProfile =
|
|
258
|
+
resolveAutoRoutingProfile(profileRouting.candidateProfile);
|
|
259
|
+
}
|
|
260
|
+
else if (parsed.profileExplicit) {
|
|
261
|
+
profileRouting.selectedByProfile = parsed.profile;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (isKnowledgeGraphBootstrapTask) {
|
|
265
|
+
profileRouting.selectedTemplate = "knowledge-graph-bootstrap-dag";
|
|
266
|
+
profileRouting.source = "taskKind";
|
|
267
|
+
profileRouting.routingReasons = [
|
|
268
|
+
'taskKind "knowledge-graph-bootstrap" selects the dedicated knowledge-graph bootstrap DAG template',
|
|
269
|
+
];
|
|
270
|
+
if (parsed.profile === "auto") {
|
|
271
|
+
profileRouting.selectedByProfile =
|
|
272
|
+
resolveAutoRoutingProfile(profileRouting.candidateProfile);
|
|
273
|
+
}
|
|
274
|
+
else if (parsed.profileExplicit) {
|
|
275
|
+
profileRouting.selectedByProfile = parsed.profile;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
233
278
|
const initResult = profileRouting.selectedTemplate === "standard-dag"
|
|
234
279
|
? candidateResult
|
|
235
280
|
: await initHybridDagFromTask(repoRoot, parsed.taskId, {
|
|
@@ -3,6 +3,9 @@ import { composeDagRunObservers } from "../../workflows/dag/observer-compose.js"
|
|
|
3
3
|
import { createDagCanvasObserver, resolveCanvasPath, } from "../../workflows/dag/canvas-observer.js";
|
|
4
4
|
import { loadDagSpecFromFile, runDag } from "../../workflows/dag/runner.js";
|
|
5
5
|
import { assertValidDagSpec } from "../../workflows/dag/validate.js";
|
|
6
|
+
import { assertRuntimeContractCompatible } from "../../workflows/dag/runtime-contract.js";
|
|
7
|
+
import { DAG_CONTROLLER_CAPABILITIES } from "../../workflows/dag/runtime-contract.js";
|
|
8
|
+
import { resolveRunningControllerIdentity } from "../../shared/package-metadata.js";
|
|
6
9
|
function getCanvasFlushErrorMessage(error) {
|
|
7
10
|
return error instanceof Error ? error.message : String(error);
|
|
8
11
|
}
|
|
@@ -29,6 +32,13 @@ function buildRunDagNextSteps(runId) {
|
|
|
29
32
|
export async function runDagUseCase(input) {
|
|
30
33
|
const spec = await loadDagSpecFromFile(input.dagPath);
|
|
31
34
|
assertValidDagSpec(spec);
|
|
35
|
+
const runningIdentity = resolveRunningControllerIdentity();
|
|
36
|
+
assertRuntimeContractCompatible(spec, {
|
|
37
|
+
...DAG_CONTROLLER_CAPABILITIES,
|
|
38
|
+
...(runningIdentity
|
|
39
|
+
? { controllerVersion: runningIdentity.packageVersion }
|
|
40
|
+
: {}),
|
|
41
|
+
});
|
|
32
42
|
const resolvedCanvasPath = resolveCanvasPath({
|
|
33
43
|
canvasPath: input.canvasPath,
|
|
34
44
|
canvasName: input.canvasName,
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { loadDagSpecFromFile } from "../../workflows/dag/runner.js";
|
|
3
3
|
import { assertValidDagSpec, collectExecutorModelWarnings, collectForbiddenExecutorIssues, collectGovernanceWarnings, validateDagSpec, } from "../../workflows/dag/validate.js";
|
|
4
|
+
import { assertRuntimeContractCompatible } from "../../workflows/dag/runtime-contract.js";
|
|
5
|
+
import { DAG_CONTROLLER_CAPABILITIES } from "../../workflows/dag/runtime-contract.js";
|
|
6
|
+
import { resolveRunningControllerIdentity } from "../../shared/package-metadata.js";
|
|
4
7
|
import { inferGovernanceProfile } from "../../workflows/dag/governance-profile.js";
|
|
5
8
|
import { topoSortToRanks } from "../../workflows/dag/topo.js";
|
|
6
9
|
import { runSpineAudit } from "../../governance/spine-audit.js";
|
|
@@ -103,6 +106,14 @@ export async function validateDagUseCase(input) {
|
|
|
103
106
|
const spec = await loadDagSpecFromFile(input.dagPath);
|
|
104
107
|
const rawTaskExecutors = await loadRawTaskExecutorMap(input.dagPath);
|
|
105
108
|
ensureNoForbiddenExecutors(spec, input.forbidExecutors, rawTaskExecutors);
|
|
109
|
+
// Runtime contract drift is always a hard error at validate time.
|
|
110
|
+
const runningIdentity = resolveRunningControllerIdentity();
|
|
111
|
+
assertRuntimeContractCompatible(spec, {
|
|
112
|
+
...DAG_CONTROLLER_CAPABILITIES,
|
|
113
|
+
...(runningIdentity
|
|
114
|
+
? { controllerVersion: runningIdentity.packageVersion }
|
|
115
|
+
: {}),
|
|
116
|
+
});
|
|
106
117
|
const validationIssues = validateDagSpec(spec);
|
|
107
118
|
const expectedMatrices = await loadExpectedExecutorModelMatrices(input.repoRoot);
|
|
108
119
|
const warnings = collectExecutorModelWarnings(spec, expectedMatrices);
|
|
@@ -84,7 +84,14 @@ const HANDOFF_USAGE = "handoff <check|coverage> [taskId]";
|
|
|
84
84
|
const REFERENCE_SUBCOMMANDS = ["index"];
|
|
85
85
|
const STUDY_SUBCOMMANDS = ["init"];
|
|
86
86
|
const WORKTREE_SUBCOMMANDS = ["create", "list", "remove"];
|
|
87
|
-
const KNOWLEDGE_SUBCOMMANDS = [
|
|
87
|
+
const KNOWLEDGE_SUBCOMMANDS = [
|
|
88
|
+
"curate",
|
|
89
|
+
"query",
|
|
90
|
+
"graph-init",
|
|
91
|
+
"graph-materialize",
|
|
92
|
+
"graph-promote",
|
|
93
|
+
"graph-incremental-prepare",
|
|
94
|
+
];
|
|
88
95
|
const DAG_SUBCOMMANDS = [
|
|
89
96
|
"init-hybrid",
|
|
90
97
|
"run-task",
|
|
@@ -435,8 +442,8 @@ export const COMMAND_DEFINITIONS = [
|
|
|
435
442
|
name: "knowledge",
|
|
436
443
|
adapter: "required",
|
|
437
444
|
tier: "operator",
|
|
438
|
-
intent: "Curate
|
|
439
|
-
usage: "knowledge curate
|
|
445
|
+
intent: "Curate repair guidance proposals, query the structured knowledge graph, and run graph bootstrap helpers (init/materialize/promote/incremental).",
|
|
446
|
+
usage: "knowledge <curate|query|graph-init|graph-materialize|graph-promote|graph-incremental-prepare> ...",
|
|
440
447
|
subcommands: [...KNOWLEDGE_SUBCOMMANDS],
|
|
441
448
|
handler: async ({ repoRoot, subcommand, rest }) => {
|
|
442
449
|
await runKnowledge(repoRoot, [subcommand, ...rest].filter((arg) => Boolean(arg)));
|
package/dist/commands/init.js
CHANGED
|
@@ -1036,15 +1036,62 @@ async function readInitSurfaceManifest(assetRoot) {
|
|
|
1036
1036
|
const manifest = JSON.parse(raw);
|
|
1037
1037
|
const initFullRequired = manifest.initFullRequired ?? [];
|
|
1038
1038
|
const initSurface = manifest.initSurface ?? {};
|
|
1039
|
-
const
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1039
|
+
const seenPaths = new Set();
|
|
1040
|
+
const entries = [];
|
|
1041
|
+
for (const relativePath of initFullRequired) {
|
|
1042
|
+
if (seenPaths.has(relativePath))
|
|
1043
|
+
continue;
|
|
1044
|
+
seenPaths.add(relativePath);
|
|
1045
|
+
entries.push({
|
|
1046
|
+
path: relativePath,
|
|
1047
|
+
mode: initSurface[relativePath] ?? inferInitSurfaceMode(relativePath),
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
// Fresh `init --profile full` copies the entire `docs/templates/` and
|
|
1051
|
+
// `skills/` directories via copyDirMerge. Every file actually projected to a
|
|
1052
|
+
// target project must be represented in the desired init surface so that
|
|
1053
|
+
// check-update can detect missing or stale copies during controller upgrades.
|
|
1054
|
+
// The static manifest intentionally only pins explicit modes for the subset
|
|
1055
|
+
// that needs them; the rest are auto-discovered here as "copied" entries.
|
|
1056
|
+
for (const discovered of await discoverCopiedSurfaceFiles(assetRoot)) {
|
|
1057
|
+
if (seenPaths.has(discovered))
|
|
1058
|
+
continue;
|
|
1059
|
+
seenPaths.add(discovered);
|
|
1060
|
+
entries.push({
|
|
1061
|
+
path: discovered,
|
|
1062
|
+
mode: initSurface[discovered] ?? inferInitSurfaceMode(discovered),
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1043
1065
|
if (!entries.some((entry) => entry.path === INIT_SURFACE_STATE_PATH)) {
|
|
1044
1066
|
entries.push({ path: INIT_SURFACE_STATE_PATH, mode: "state" });
|
|
1045
1067
|
}
|
|
1046
1068
|
return { manifest, raw, sha256: sha256Text(raw), entries };
|
|
1047
1069
|
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Returns manifest-path entries for every file that fresh `init --profile full`
|
|
1072
|
+
* copies from the bundled package into a target project: all of `docs/templates/`
|
|
1073
|
+
* and all of `skills/` (the latter is mirrored to `.agents/skills/` at the
|
|
1074
|
+
* target). Paths use the package source convention so manifestPathToTargetPath
|
|
1075
|
+
* can remap them per governance root.
|
|
1076
|
+
*/
|
|
1077
|
+
async function discoverCopiedSurfaceFiles(assetRoot) {
|
|
1078
|
+
const discovered = [];
|
|
1079
|
+
const roots = [
|
|
1080
|
+
{ source: "docs/templates", prefix: "docs/templates/" },
|
|
1081
|
+
{ source: "skills", prefix: "skills/" },
|
|
1082
|
+
];
|
|
1083
|
+
for (const { source, prefix } of roots) {
|
|
1084
|
+
const rootPath = path.join(assetRoot, source);
|
|
1085
|
+
const rootInfo = await stat(rootPath).catch(() => null);
|
|
1086
|
+
if (!rootInfo?.isDirectory())
|
|
1087
|
+
continue;
|
|
1088
|
+
const files = await listRelativeFiles(rootPath);
|
|
1089
|
+
for (const relativePath of files) {
|
|
1090
|
+
discovered.push(`${prefix}${relativePath}`);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
return discovered.sort();
|
|
1094
|
+
}
|
|
1048
1095
|
function inferInitSurfaceMode(relativePath) {
|
|
1049
1096
|
if (relativePath === INIT_SURFACE_STATE_PATH)
|
|
1050
1097
|
return "state";
|
|
@@ -1378,8 +1425,9 @@ function hasLegacyHarnessGovernancePaths(harness) {
|
|
|
1378
1425
|
return false;
|
|
1379
1426
|
}
|
|
1380
1427
|
async function collectRetiredLayoutActions(input) {
|
|
1428
|
+
const blockedTargetPaths = new Set();
|
|
1381
1429
|
if (!input.recordedState)
|
|
1382
|
-
return [];
|
|
1430
|
+
return { actions: [], blockedTargetPaths };
|
|
1383
1431
|
const actions = [];
|
|
1384
1432
|
const retiredOwnedPaths = [];
|
|
1385
1433
|
const currentPaths = new Set(Object.keys(input.currentState.files));
|
|
@@ -1399,6 +1447,9 @@ async function collectRetiredLayoutActions(input) {
|
|
|
1399
1447
|
path: oldPath,
|
|
1400
1448
|
reason: `legacy init file may have local edits; review before migrating to ${targetPath}`,
|
|
1401
1449
|
});
|
|
1450
|
+
// The user owns a modified copy at the legacy path; do not silently
|
|
1451
|
+
// install the package version at the canonical target path.
|
|
1452
|
+
blockedTargetPaths.add(targetPath);
|
|
1402
1453
|
continue;
|
|
1403
1454
|
}
|
|
1404
1455
|
const targetAbsolute = path.join(input.repoRoot, targetPath);
|
|
@@ -1442,6 +1493,7 @@ async function collectRetiredLayoutActions(input) {
|
|
|
1442
1493
|
path: oldPath,
|
|
1443
1494
|
reason: `legacy init file is owned, but target path ${targetPath} already exists with different content`,
|
|
1444
1495
|
});
|
|
1496
|
+
blockedTargetPaths.add(targetPath);
|
|
1445
1497
|
}
|
|
1446
1498
|
for (const legacyRoot of ["docs", "skills"]) {
|
|
1447
1499
|
for (const relativePath of await listRelativeFiles(path.join(input.repoRoot, legacyRoot))) {
|
|
@@ -1476,6 +1528,7 @@ async function collectRetiredLayoutActions(input) {
|
|
|
1476
1528
|
path: oldPath,
|
|
1477
1529
|
reason: `legacy init-shaped file is not recorded and differs from the current bundled/generated asset; review before migrating to ${targetPath}`,
|
|
1478
1530
|
});
|
|
1531
|
+
blockedTargetPaths.add(targetPath);
|
|
1479
1532
|
continue;
|
|
1480
1533
|
}
|
|
1481
1534
|
if (!targetHash) {
|
|
@@ -1492,6 +1545,7 @@ async function collectRetiredLayoutActions(input) {
|
|
|
1492
1545
|
path: oldPath,
|
|
1493
1546
|
reason: `legacy init-shaped file matches the package, but target path ${targetPath} already exists with different content`,
|
|
1494
1547
|
});
|
|
1548
|
+
blockedTargetPaths.add(targetPath);
|
|
1495
1549
|
}
|
|
1496
1550
|
}
|
|
1497
1551
|
for (const dir of collectSafeRetiredDirectories(retiredOwnedPaths)) {
|
|
@@ -1501,7 +1555,7 @@ async function collectRetiredLayoutActions(input) {
|
|
|
1501
1555
|
reason: "remove directory if it is empty after legacy init files are migrated",
|
|
1502
1556
|
});
|
|
1503
1557
|
}
|
|
1504
|
-
return actions;
|
|
1558
|
+
return { actions, blockedTargetPaths };
|
|
1505
1559
|
}
|
|
1506
1560
|
async function resolveInitProjectContext(input) {
|
|
1507
1561
|
const harness = await readJsonIfExists(path.join(input.repoRoot, "harness.json"));
|
|
@@ -1771,6 +1825,13 @@ function buildTargetFeatureWorkflow(input) {
|
|
|
1771
1825
|
"",
|
|
1772
1826
|
"Before executing a DAG, review profile routing, governance profile, writer writeSet, allowed paths, forbidden paths, shell verification, and decision gate mode.",
|
|
1773
1827
|
"",
|
|
1828
|
+
"## Specialized Task Kinds",
|
|
1829
|
+
"",
|
|
1830
|
+
"Set `taskKind` in `.harness/tasks/<task-id>/task.json` to route a task to a dedicated DAG profile instead of the default auto profile:",
|
|
1831
|
+
"",
|
|
1832
|
+
"- `taskKind: \"backend-test\"` selects the dedicated backend test DAG. Its Pi nodes analyze requirements, generate and review backend cases, generate pytest, and retrospect on results; shell gate/execution nodes enforce the review verdict and run the target project's pytest. The backend test templates (`backend-test-dag.json` and the `backend-test-dag.*.prompt.md` files) ship inside the loop-agent package as static references and are projected to target projects under the governance `templates/` directory.",
|
|
1833
|
+
"- Only eligible read-only Pi nodes (planner, scout, reviewer, verifier, closeout with no write-capable tool profile) receive the conservative automatic retry policy. Supervisor, implementer, writer, docs-only, dynamic, shell, static, and decision-gate nodes are not retried automatically. Eligible nodes cannot write repository files; the controller only records immutable attempt evidence under `.harness/dag-runs/<state>/<run-id>/<node-id>/attempt-<n>.json`.",
|
|
1834
|
+
"",
|
|
1774
1835
|
"## Verification",
|
|
1775
1836
|
"",
|
|
1776
1837
|
`Use \`${input.governanceRoot}/verification-matrix.md\` to choose the narrowest command that proves the claim.`,
|
|
@@ -2169,7 +2230,7 @@ export async function checkInitUpdate(input) {
|
|
|
2169
2230
|
const deterministicActions = [];
|
|
2170
2231
|
const modelMergeTasks = [];
|
|
2171
2232
|
const humanDecisions = [];
|
|
2172
|
-
const
|
|
2233
|
+
const retiredLayoutResult = await collectRetiredLayoutActions({
|
|
2173
2234
|
assetRoot,
|
|
2174
2235
|
repoRoot,
|
|
2175
2236
|
projectName,
|
|
@@ -2178,9 +2239,15 @@ export async function checkInitUpdate(input) {
|
|
|
2178
2239
|
currentState,
|
|
2179
2240
|
humanDecisions,
|
|
2180
2241
|
});
|
|
2242
|
+
const retiredLayoutActions = retiredLayoutResult.actions;
|
|
2181
2243
|
const retiredTargetPaths = new Set(retiredLayoutActions
|
|
2182
2244
|
.filter((action) => action.type === "migrate-owned-file" && action.targetPath)
|
|
2183
2245
|
.map((action) => action.targetPath));
|
|
2246
|
+
// Targets occupied by a user-modified legacy copy must not be silently
|
|
2247
|
+
// (re)installed from the package; preserve them for a human/model merge.
|
|
2248
|
+
for (const blockedPath of retiredLayoutResult.blockedTargetPaths) {
|
|
2249
|
+
retiredTargetPaths.add(blockedPath);
|
|
2250
|
+
}
|
|
2184
2251
|
if (!recordedState) {
|
|
2185
2252
|
deterministicActions.push({
|
|
2186
2253
|
type: "bootstrap-surface",
|