@tea-agent/loop-agent 0.26.5-beta.2 → 0.27.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 (71) hide show
  1. package/AGENTS.md +2 -2
  2. package/CHANGELOG.md +29 -0
  3. package/README.md +13 -23
  4. package/dist/application/dag/args.js +7 -7
  5. package/dist/application/dag/generate-task-dag.js +2 -2
  6. package/dist/application/dag/run-dag.js +3 -4
  7. package/dist/application/dag/validate-dag.js +1 -1
  8. package/dist/application/task-lifecycle/advance.js +845 -0
  9. package/dist/application/task-lifecycle/gates.js +80 -0
  10. package/dist/application/task-lifecycle/index.js +7 -0
  11. package/dist/application/task-lifecycle/observe.js +395 -0
  12. package/dist/application/task-lifecycle/plan-transitions.js +224 -0
  13. package/dist/application/task-lifecycle/recommendations.js +180 -0
  14. package/dist/application/task-lifecycle/record.js +73 -0
  15. package/dist/application/task-lifecycle/types.js +1 -0
  16. package/dist/cli/command-definitions.js +14 -111
  17. package/dist/cli/help.js +6 -7
  18. package/dist/cli/program.js +26 -90
  19. package/dist/cli/update/policy.js +0 -1
  20. package/dist/commands/dag-final-verification.js +1 -1
  21. package/dist/commands/dag-init-hybrid.js +1 -1
  22. package/dist/commands/delegate.js +2 -2
  23. package/dist/commands/init.js +21 -19
  24. package/dist/commands/run-dag-progress.js +1 -1
  25. package/dist/commands/status.js +18 -17
  26. package/dist/commands/study-init.js +2 -2
  27. package/dist/commands/task-advance.js +335 -0
  28. package/dist/commands/task-contract.js +3 -5
  29. package/dist/commands/task-source-prepare.js +9 -4
  30. package/dist/commands/task-status.js +133 -0
  31. package/dist/governance/manifest-types.js +2 -2
  32. package/dist/shared/operator/capabilities.js +1358 -243
  33. package/dist/task/contract/adopt.js +1 -1
  34. package/dist/task/contract/apply.js +1 -1
  35. package/dist/task/contract/import-revision.js +1 -1
  36. package/dist/task/contract/recover.js +2 -2
  37. package/dist/task/read-model.js +18 -23
  38. package/dist/task/runtime.js +1 -1
  39. package/dist/task/source-prepare/completeness.js +1 -1
  40. package/dist/task/source-prepare/parse-intent.js +6 -1
  41. package/dist/task/source-prepare/prepare.js +23 -20
  42. package/dist/worker/console/operator-actions.js +288 -95
  43. package/dist/worker/console/recovery-cta.js +4 -4
  44. package/dist/worker/console/static/assets/{index-CSRIhuzh.js → index-CNO7n6qB.js} +1 -1
  45. package/dist/worker/console/static/index.html +1 -1
  46. package/dist/worker/materialize/harness-task-materializer.js +6 -5
  47. package/dist/worker/run-task/run-task.js +204 -112
  48. package/dist/worker/runner/run-ready.js +1 -1
  49. package/dist/workflows/dag/frontend-implementation-contract.js +6 -72
  50. package/dist/workflows/dag/frontend-prewrite-gate.js +4 -17
  51. package/dist/workflows/dag/init-hybrid.js +11 -66
  52. package/docs/templates/evaluation/agents-map-slim-v1.md +1 -1
  53. package/docs/templates/evaluation/agents-map-verbose-v0.md +3 -3
  54. package/docs/templates/harness.schema.json +2 -2
  55. package/docs/templates/init-managed-agents.md +13 -16
  56. package/docs/templates/production-readiness-checklist.md +3 -3
  57. package/harness.json +4 -4
  58. package/package.json +1 -1
  59. package/scripts/kb-bootstrap-init-skeleton.sh +2 -1
  60. package/scripts/kb-graph-incremental-prepare.mjs +2 -2
  61. package/skills/loop-agent/SKILL.md +18 -13
  62. package/skills/loop-agent/references/README.md +1 -1
  63. package/skills/loop-agent/references/command-reference.md +55 -84
  64. package/skills/loop-agent/references/harness-policy.md +19 -23
  65. package/skills/loop-agent/references/hybrid-dag.md +31 -33
  66. package/skills/loop-agent/references/long-running-loop.md +2 -2
  67. package/skills/loop-agent/references/one-shot-runs.md +4 -5
  68. package/skills/loop-agent/references/orchestrator-and-interventions.md +3 -3
  69. package/skills/loop-agent/references/post-implementation-and-patterns.md +4 -4
  70. package/skills/loop-agent/references/source-and-plan-practice.md +45 -51
  71. package/skills/loop-agent/references/task-workflow.md +14 -15
package/dist/cli/help.js CHANGED
@@ -22,13 +22,12 @@ export function formatCommandTierSummary(manifest = CODE_AGENT_COMMAND_MANIFEST)
22
22
  .map((entry) => entry.name)
23
23
  .join(", ");
24
24
  return [
25
- "Recommended DAG path:",
26
- " 1. new-task <task-id> [title]",
27
- " 2. import-prd <task-id> --file <detailed-prd> # PRD-first recommended",
28
- " 3. task source prepare <task-id> --use-imported-prd --allowed-path <glob> --apply",
29
- " 4. dag run-task <task-id> --profile auto --strict-models",
30
- " 5. run-dag --dag <path> --cwd <repo-root>",
31
- "Operator recovery/closeout: dag status/report/doctor/closeout-draft",
25
+ "Recommended task lifecycle (single mutation + single read):",
26
+ " 1. task advance <task-id> [title] --prd <prd.md> --allowed-path <glob> --verify <label:command> --json",
27
+ " 2. review writeSet gate digest from JSON",
28
+ " 3. task advance <task-id> --approve-gate write-set-review:<digest> --json",
29
+ " 4. task status <task-id> --json # optional read-only",
30
+ "Advanced DAG authoring/forensics: dag validate|execute|report|workflow-* (not the standard happy path)",
32
31
  `Primary commands: ${primary}`,
33
32
  `Operator commands: ${operator}`,
34
33
  compatibility ? `Legacy compatibility commands: ${compatibility}` : "",
@@ -3,7 +3,6 @@ import { fileURLToPath } from "node:url";
3
3
  import { Command } from "commander";
4
4
  import { readPackageVersion } from "../shared/package-metadata.js";
5
5
  import { resolveAdapter } from "../adapters/index.js";
6
- import { runCloseout } from "../commands/closeout.js";
7
6
  import { runCoverageAudit } from "../commands/coverage-audit.js";
8
7
  import { runCoverageReport } from "../commands/coverage-report.js";
9
8
  import { parseCursorPromptArgs, printCursorPromptUsage, runCursorPrompt, } from "../commands/cursor-prompt.js";
@@ -17,7 +16,6 @@ import { runDagRerun } from "../commands/dag-rerun.js";
17
16
  import { runDagReject } from "../commands/dag-reject.js";
18
17
  import { runDagCloseoutDraft, runDagReport } from "../commands/dag-report.js";
19
18
  import { runDagResume } from "../commands/dag-resume.js";
20
- import { runDagRunTask } from "../commands/dag-run-task.js";
21
19
  import { runDagValidate } from "../commands/dag-validate.js";
22
20
  import { runDagWorkflowCompile } from "../commands/dag-workflow-compile.js";
23
21
  import { runDagWorkflowPlan } from "../commands/dag-workflow-plan.js";
@@ -38,21 +36,17 @@ import { runInstructions } from "../commands/instructions.js";
38
36
  import { runKnowledge } from "../commands/knowledge.js";
39
37
  import { runLoop } from "../commands/loop.js";
40
38
  import { runLoopBenchmark } from "../commands/loop-benchmark.js";
41
- import { runNewTask } from "../commands/new-task.js";
42
- import { runImportPrd } from "../commands/import-prd.js";
43
- import { runTaskContract } from "../commands/task-contract.js";
44
- import { runTaskSourcePrepare } from "../commands/task-source-prepare.js";
39
+ import { runTaskAdvance } from "../commands/task-advance.js";
40
+ import { runTaskStatus } from "../commands/task-status.js";
45
41
  import { runOperator } from "../commands/operator.js";
46
42
  import { runPiReuseBenchmark } from "../commands/pi-reuse-benchmark.js";
47
43
  import { parsePiPromptArgs, printPiPromptUsage, runPiPrompt, } from "../commands/pi-prompt.js";
48
44
  import { runPlanList } from "../commands/plan-list.js";
49
45
  import { runPlanCheck, runPlanComplete, runPlanCreate, } from "../commands/plan.js";
50
- import { runPromoteRun } from "../commands/promote-run.js";
51
46
  import { runReferenceIndex } from "../commands/reference-index.js";
52
47
  import { runRunDag } from "../commands/run-dag.js";
53
48
  import { runSpine } from "../commands/spine.js";
54
49
  import { runStats } from "../commands/stats.js";
55
- import { runStatus } from "../commands/status.js";
56
50
  import { runStudyInit } from "../commands/study-init.js";
57
51
  import { runWorkflowCommand } from "../commands/workflow.js";
58
52
  import { runWorktreeCreate } from "../commands/worktree-create.js";
@@ -116,6 +110,17 @@ async function dispatchRegisteredCommand(commandObject, defaultRepoRoot, rawArgs
116
110
  };
117
111
  await runCommanderAction(ctx, command, subcommand, rest);
118
112
  }
113
+ async function runTaskCommand(repoRoot, subcommand, rest) {
114
+ if (subcommand === "advance") {
115
+ await runTaskAdvance(repoRoot, rest);
116
+ return;
117
+ }
118
+ if (subcommand === "status") {
119
+ await runTaskStatus(repoRoot, rest);
120
+ return;
121
+ }
122
+ throw new Error("usage: task <advance|status> ...");
123
+ }
119
124
  async function runCommanderAction(ctx, command, subcommand, rest) {
120
125
  switch (command) {
121
126
  case "inspect":
@@ -127,63 +132,16 @@ async function runCommanderAction(ctx, command, subcommand, rest) {
127
132
  case "docs":
128
133
  await runDocsCommand(ctx.repoRoot, subcommand, rest);
129
134
  return;
130
- case "new-task": {
131
- const [taskId, ...titleParts] = [subcommand, ...rest];
132
- if (!taskId)
133
- throw new Error("taskId required");
134
- await runNewTask(ctx.repoRoot, taskId, titleParts.join(" ") || undefined);
135
- return;
136
- }
137
- case "import-prd":
138
- await runImportPrd(ctx.repoRoot, compactArgs([subcommand, ...rest]));
139
- return;
140
135
  case "task": {
141
- if (subcommand === "contract") {
142
- const [contractSub, ...contractRest] = rest;
143
- const allowed = [
144
- "show",
145
- "validate",
146
- "diff",
147
- "apply",
148
- "adopt",
149
- "doctor",
150
- "recover",
151
- ];
152
- if (!contractSub ||
153
- !allowed.includes(contractSub)) {
154
- throw new Error(`usage: task contract <${allowed.join("|")}> ...`);
155
- }
156
- await runTaskContract(ctx.repoRoot, [contractSub, ...contractRest]);
157
- return;
158
- }
159
- if (subcommand === "source") {
160
- const [sourceSub, ...sourceRest] = rest;
161
- if (sourceSub !== "prepare") {
162
- throw new Error("usage: task source <prepare> ...");
163
- }
164
- await runTaskSourcePrepare(ctx.repoRoot, sourceRest);
165
- return;
166
- }
167
- throw new Error("usage: task <contract|source> ...");
136
+ await runTaskCommand(ctx.repoRoot, subcommand, rest);
137
+ return;
168
138
  }
169
139
  case "operator":
170
140
  await runOperator(ctx.repoRoot, compactArgs([subcommand, ...rest]));
171
141
  return;
172
- case "status": {
173
- if (!subcommand)
174
- throw new Error("taskId required");
175
- await runStatus(ctx.repoRoot, subcommand);
176
- return;
177
- }
178
142
  case "instructions":
179
143
  await runInstructions(ctx.repoRoot, compactArgs([subcommand, ...rest]));
180
144
  return;
181
- case "promote-run":
182
- await runPromoteRun(ctx.repoRoot, compactArgs([subcommand, ...rest]));
183
- return;
184
- case "closeout":
185
- await runCloseout(ctx.repoRoot, compactArgs([subcommand, ...rest]));
186
- return;
187
145
  case "stats":
188
146
  await runStats(ctx.repoRoot, compactArgs([subcommand, ...rest]));
189
147
  return;
@@ -240,9 +198,6 @@ async function runCommanderAction(ctx, command, subcommand, rest) {
240
198
  case "dag":
241
199
  await runDagAction(ctx.repoRoot, subcommand, rest);
242
200
  return;
243
- case "run-dag":
244
- await runRunDag(ctx.repoRoot, compactArgs([subcommand, ...rest]));
245
- return;
246
201
  case "workflow":
247
202
  await runWorkflowCommand(ctx.repoRoot, compactArgs([subcommand, ...rest]));
248
203
  return;
@@ -364,8 +319,8 @@ async function runDagAction(repoRoot, subcommand, rest) {
364
319
  case "doctor":
365
320
  await runDagDoctor(repoRoot, args);
366
321
  return;
367
- case "run-task":
368
- await runDagRunTask(repoRoot, args);
322
+ case "execute":
323
+ await runRunDag(repoRoot, args);
369
324
  return;
370
325
  case "report":
371
326
  await runDagReport(repoRoot, args);
@@ -401,7 +356,7 @@ async function runDagAction(repoRoot, subcommand, rest) {
401
356
  throw new Error("usage: dag decision <inspect|validate> --run-id <id> [--node-id <node-id>]");
402
357
  }
403
358
  default:
404
- throw new Error("usage: dag <init-hybrid|run-task|validate|workflow-plan|workflow-validate|workflow-compile|approve|reject|resume|status|doctor|report|closeout-draft|reconcile-run|rerun|rerun-task|reconcile-tasks|final-verification|decision> ...");
359
+ throw new Error("usage: dag <init-hybrid|execute|validate|workflow-plan|workflow-validate|workflow-compile|approve|reject|resume|status|doctor|report|closeout-draft|reconcile-run|rerun|rerun-task|reconcile-tasks|final-verification|decision> ...");
405
360
  }
406
361
  }
407
362
  function compactArgs(args) {
@@ -431,7 +386,8 @@ function createRawArgsCommand(name, description, defaultRepoRoot) {
431
386
  }
432
387
  function addRawSubcommands(parent, subcommands, defaultRepoRoot) {
433
388
  for (const subcommand of subcommands) {
434
- parent.addCommand(createRawArgsCommand(subcommand, `${parent.name()} ${subcommand}`, defaultRepoRoot));
389
+ const command = createRawArgsCommand(subcommand, `${parent.name()} ${subcommand}`, defaultRepoRoot);
390
+ parent.addCommand(command);
435
391
  }
436
392
  }
437
393
  function addStandaloneSubcommands(parent, subcommands, handler) {
@@ -464,9 +420,9 @@ function configureDagValidate(command) {
464
420
  .option("--forbid-executor <executor>", "forbid executor", (value, previous = []) => [...previous, value])
465
421
  .option("--spine-task <task-id>", "run spine audit for task id");
466
422
  }
467
- function configureRunDag(command) {
423
+ function configureDagExecute(command) {
468
424
  command
469
- .description("Execute a reviewed Agent DAG spec")
425
+ .description("Advanced: execute an arbitrary DagSpec JSON (standard task path uses task advance)")
470
426
  .option("--dag <path>", "DAG spec path")
471
427
  .option("-C, --cwd <dir>", "working directory")
472
428
  .option("--init-only", "initialize run artifacts without executing")
@@ -480,22 +436,6 @@ function configureRunDag(command) {
480
436
  .option("--canvases-dir <dir>", "canvas output directory")
481
437
  .option("--events-jsonl <path>", "append DAG node events to a JSONL file");
482
438
  }
483
- function configureDagRunTask(command) {
484
- command
485
- .description("Create and optionally execute a DAG for a task")
486
- .option("-o, --output <path>", "output DAG path")
487
- .option("--profile <profile>", "auto|minimal|standard|reviewed|supervised")
488
- .option("--strict-models", "fail on model routing warnings")
489
- .option("--execute", "execute after creating the DAG")
490
- .option("--init-only", "initialize run artifacts without executing")
491
- .option("--dry-run", "validate and plan without executing")
492
- .option("--cwd <dir>", "working directory")
493
- .option("--max-concurrent <n>", "maximum concurrent nodes")
494
- .option("--run-id <id>", "run id")
495
- .option("--canvas-path <path>", "absolute canvas output path")
496
- .option("--canvas <name>", "canvas name")
497
- .option("--canvases-dir <dir>", "canvas output directory");
498
- }
499
439
  function optionTokens(command) {
500
440
  const tokens = [];
501
441
  const options = command.opts();
@@ -562,10 +502,10 @@ function addDagCommand(program, defaultRepoRoot) {
562
502
  configureDagValidate(validate);
563
503
  replaceActionWithOptionAwareDispatch(validate, defaultRepoRoot);
564
504
  dag.addCommand(validate);
565
- const runTask = createRawArgsCommand("run-task", "Create and optionally execute a DAG for a task", defaultRepoRoot);
566
- configureDagRunTask(runTask);
567
- replaceActionWithOptionAwareDispatch(runTask, defaultRepoRoot);
568
- dag.addCommand(runTask);
505
+ const execute = createRawArgsCommand("execute", "Advanced: execute an arbitrary DagSpec JSON", defaultRepoRoot);
506
+ configureDagExecute(execute);
507
+ replaceActionWithOptionAwareDispatch(execute, defaultRepoRoot);
508
+ dag.addCommand(execute);
569
509
  const decision = new Command("decision").description("DAG decision envelope commands");
570
510
  addRawSubcommands(decision, ["inspect", "validate"], defaultRepoRoot);
571
511
  dag.addCommand(decision);
@@ -673,10 +613,6 @@ export function buildLoopAgentProgram(options) {
673
613
  continue;
674
614
  }
675
615
  const command = createRawArgsCommand(entry.name, entry.intent, options.defaultRepoRoot);
676
- if (entry.name === "run-dag") {
677
- configureRunDag(command);
678
- replaceActionWithOptionAwareDispatch(command, options.defaultRepoRoot);
679
- }
680
616
  if (entry.subcommands)
681
617
  addRawSubcommands(command, entry.subcommands, options.defaultRepoRoot);
682
618
  if (entry.name === "task") {
@@ -1,6 +1,5 @@
1
1
  import semver from "semver";
2
2
  const CONTROLLER_SENSITIVE_COMMANDS = new Set([
3
- "run-dag",
4
3
  "dag",
5
4
  "loop",
6
5
  "delegate",
@@ -65,7 +65,7 @@ export async function runDagFinalVerification(repoRoot, rawArgs) {
65
65
  next: [
66
66
  `Review ${result.outputPath}`,
67
67
  `npm run dev -- dag validate --dag ${result.outputPath} --strict-models --strict-governance`,
68
- `npm run dev -- run-dag --dag ${result.outputPath} --cwd ${repoRoot}`,
68
+ `npm run dev -- dag execute --dag ${result.outputPath} --cwd ${repoRoot}`,
69
69
  ],
70
70
  }, null, 2));
71
71
  }
@@ -51,7 +51,7 @@ export async function runDagInitHybrid(repoRoot, rawArgs) {
51
51
  next: [
52
52
  `Review ${result.outputPath}`,
53
53
  `npm run dev -- dag validate --dag ${result.outputPath}`,
54
- "Use run-dag --dry-run only when you intentionally need an active run snapshot.",
54
+ "Use dag execute --dry-run only when you intentionally need an active run snapshot.",
55
55
  ],
56
56
  }, null, 2));
57
57
  }
@@ -64,9 +64,9 @@ export function printDelegateNextSteps(taskId, result) {
64
64
  ` worktree: ${result.worktreePath}`,
65
65
  ` Next — generate, validate, and run DAG in the worktree:`,
66
66
  ` cd ${JSON.stringify(result.worktreePath)}`,
67
- ` loop-agent --repo-root ${JSON.stringify(result.worktreePath)} dag run-task ${taskId} --profile auto --strict-models`,
67
+ ` loop-agent --repo-root ${JSON.stringify(result.worktreePath)} task advance ${taskId} --profile auto --json`,
68
68
  ` loop-agent dag validate --dag ${JSON.stringify(dagPath)} --strict-models --strict-governance`,
69
- ` loop-agent run-dag --dag ${JSON.stringify(dagPath)} --cwd ${JSON.stringify(result.worktreePath)}`,
69
+ ` loop-agent dag execute --dag ${JSON.stringify(dagPath)} --cwd ${JSON.stringify(result.worktreePath)}`,
70
70
  ` Or: loop-agent delegate ${taskId} --auto-run`,
71
71
  ` After work completes:`,
72
72
  ` loop-agent harvest ${taskId}`,
@@ -446,7 +446,7 @@ check_skill "agent-worker" "references/agent-worker-operator.md"
446
446
  # against the whole skill file so host auto-discovery phrasing cannot drift.
447
447
  loop_agent_skill=".agents/skills/loop-agent/SKILL.md"
448
448
  loop_agent_text="$(tr '[:upper:]' '[:lower:]' < "\${loop_agent_skill}")"
449
- for term in "loop-agent 帮我完成" "帮我实现" "帮我修复" "帮我开发" "agent dag" "new-task" "dag run-task" "dag validate" "run-dag"; do
449
+ for term in "loop-agent 帮我完成" "帮我实现" "帮我修复" "帮我开发" "agent dag" "task advance" "task status" "dag validate" "dag execute"; do
450
450
  if [[ "\${loop_agent_text}" != *"\${term}"* ]]; then
451
451
  echo "skill entry 检查失败(loop-agent):description 缺少触发/路由词 \${term}" >&2
452
452
  exit 1
@@ -1810,16 +1810,17 @@ function buildManagedReadmeBlock(input) {
1810
1810
  "默认使用 Agent DAG 作为实现工作流:",
1811
1811
  "",
1812
1812
  "```bash",
1813
- 'loop-agent new-task <task-id> "任务标题"',
1814
- "# 推荐:详细 PRD → import-prd → task source prepare --apply(无需手写两 source)",
1815
- "# loop-agent import-prd <task-id> --file <prd.md>",
1816
- 'loop-agent task source prepare <task-id> --use-imported-prd --allowed-path "<glob>" --apply --json',
1817
- "loop-agent dag run-task <task-id> --profile auto --strict-models",
1818
- "loop-agent dag validate --dag .harness/tasks/<task-id>/dag.json --strict-models --strict-governance",
1819
- "loop-agent run-dag --dag .harness/tasks/<task-id>/dag.json --cwd .",
1813
+ 'loop-agent task advance <task-id> "任务标题" \\',
1814
+ " --prd <prd.md> \\",
1815
+ ' --allowed-path "<glob>" \\',
1816
+ ' --verify "typecheck:npm run typecheck" \\',
1817
+ " --json",
1818
+ "# 审查 writeSet gate digest 后:",
1819
+ 'loop-agent task advance <task-id> --approve-gate "write-set-review:<digest>" --json',
1820
+ "loop-agent task status <task-id> --json",
1820
1821
  "```",
1821
1822
  "",
1822
- `详细工作流见 \`${input.governanceRoot}/feature-workflow.md\`;验证矩阵见 \`${input.governanceRoot}/verification-matrix.md\`。任务 source 仍必需(\`source/需求.md\` / \`source/执行约束.md\`),默认由 \`task source prepare --apply\` 从详细 PRD 确定性派生,而不是主会话手写或 LLM 写 md。`,
1823
+ `详细工作流见 \`${input.governanceRoot}/feature-workflow.md\`;验证矩阵见 \`${input.governanceRoot}/verification-matrix.md\`。任务 source 仍必需(\`source/需求.md\` / \`source/执行约束.md\`),默认由 \`task advance --prd\` 从详细 PRD 确定性派生,而不是主会话手写或 LLM 写 md。高级任意 DagSpec 才用 \`dag validate|execute|report\`,不进入标准 happy path。`,
1823
1824
  "",
1824
1825
  "执行后使用 `loop-agent dag report --run-id <run-id> --markdown` 和 `loop-agent dag doctor --run-id <run-id> --markdown` 读取事实与诊断失败。失败 run 不做成功式 closeout;使用 `loop-agent dag closeout-draft --run-id <run-id>` 生成 failure handoff。",
1825
1826
  "",
@@ -2043,13 +2044,14 @@ function buildTargetFeatureWorkflow(input) {
2043
2044
  "## Agent DAG Path",
2044
2045
  "",
2045
2046
  "```bash",
2046
- 'loop-agent new-task <task-id> "Task title"',
2047
- "# Preferred: detailed PRD → import-prd → task source prepare --apply",
2048
- "# loop-agent import-prd <task-id> --file <prd.md>",
2049
- 'loop-agent task source prepare <task-id> --use-imported-prd --allowed-path "<glob>" --apply --json',
2050
- "loop-agent dag run-task <task-id> --profile auto --strict-models",
2051
- "loop-agent dag validate --dag .harness/tasks/<task-id>/dag.json --strict-models --strict-governance",
2052
- "loop-agent run-dag --dag .harness/tasks/<task-id>/dag.json --cwd .",
2047
+ 'loop-agent task advance <task-id> "Task title" \\',
2048
+ " --prd <prd.md> \\",
2049
+ ' --allowed-path "<glob>" \\',
2050
+ ' --verify "typecheck:npm run typecheck" \\',
2051
+ " --json",
2052
+ "# Review writeSet gate digest, then:",
2053
+ 'loop-agent task advance <task-id> --approve-gate "write-set-review:<digest>" --json',
2054
+ "loop-agent task status <task-id> --json",
2053
2055
  "```",
2054
2056
  "",
2055
2057
  DAG_HARD_GATE_TRIGGER,
@@ -2082,7 +2084,7 @@ function buildTargetFeatureWorkflow(input) {
2082
2084
  "```bash",
2083
2085
  "loop-agent knowledge graph-init --product-name <name>",
2084
2086
  "# edit knowledge/bootstrap/scope.yaml, then:",
2085
- "loop-agent dag run-task <task-id> # taskKind: knowledge-graph-bootstrap",
2087
+ "loop-agent task advance <task-id> --task-kind knowledge-graph-bootstrap --json",
2086
2088
  "loop-agent knowledge query --mode by_feature --feature F-2026-004 --json",
2087
2089
  "loop-agent knowledge query --mode by_id --id SVC-order --json",
2088
2090
  'loop-agent knowledge query --mode search --text "keyword" --json',
@@ -2156,14 +2158,14 @@ function buildTargetLoopAgentHarness(input) {
2156
2158
  "",
2157
2159
  "## Default Workflow",
2158
2160
  "",
2159
- "Use `loop-agent new-task`, `loop-agent dag run-task`, `loop-agent dag validate`, and `loop-agent run-dag` for non-trivial implementation work.",
2161
+ "Use `loop-agent task advance` / `loop-agent task status` for non-trivial implementation work; advanced arbitrary DagSpec uses `loop-agent dag execute`.",
2160
2162
  "",
2161
2163
  "## Adaptive Liveness",
2162
2164
  "",
2163
2165
  "- Healthy Pi nodes are no longer stopped by a fixed 30-minute deadline. The default 4h absolute max is a final safety bound and cannot be extended by synthetic heartbeats.",
2164
2166
  "- Runner heartbeat proves only the runner lease. Provider, tool, or output activity records meaningful progress; prolonged inactivity can surface as `quiet`, `suspected-stall`, `probing`, or `needs-attention` in `dag status`, `dag doctor`, and Observe.",
2165
2167
  "- A silent transport is aborted in a controlled way. `termination-unconfirmed` means the old attempt may still exist, so loop-agent fails closed and does not start an automatic retry; inspect the run before any operator recovery.",
2166
- "- `agent-worker` does not set an outer `run-dag` wall-clock by default. An explicit `worker.timeout_ms` remains a hard timeout.",
2168
+ "- `agent-worker` does not set an outer `task advance` / `dag execute` wall-clock by default. An explicit `worker.timeout_ms` remains a hard timeout.",
2167
2169
  "",
2168
2170
  "## Operator Recovery",
2169
2171
  "",
@@ -59,7 +59,7 @@ export function createRunDagProgressObserver(options = {}) {
59
59
  let timer;
60
60
  const emit = (message) => {
61
61
  try {
62
- write(`[run-dag] ${message}\n`);
62
+ write(`[dag execute] ${message}\n`);
63
63
  }
64
64
  catch {
65
65
  // Progress is a derived CLI view and must never decide DAG execution.
@@ -1,8 +1,8 @@
1
- import { getTaskDir, getTaskPaths, getTaskStatus, loadTaskConfig, refreshSourceState, } from '../task/runtime.js';
2
- import { getArtifactPath } from '../shared/artifacts-core.js';
3
- import { buildTaskReadModel } from '../task/read-model.js';
1
+ import { getTaskDir, getTaskPaths, getTaskStatus, loadTaskConfig, } from "../task/runtime.js";
2
+ import { getArtifactPath } from "../shared/artifacts-core.js";
3
+ import { buildTaskReadModel } from "../task/read-model.js";
4
4
  export async function runStatus(repoRoot, taskId) {
5
- await refreshSourceState(repoRoot, taskId);
5
+ // Read-only: do not refresh/repair/migrate. Mutations belong to task advance.
6
6
  const state = await getTaskStatus(repoRoot, taskId);
7
7
  const taskConfig = await loadTaskConfig(repoRoot, taskId);
8
8
  const taskDir = getTaskDir(repoRoot, taskId);
@@ -12,17 +12,17 @@ export async function runStatus(repoRoot, taskId) {
12
12
  taskId,
13
13
  taskDir,
14
14
  taskRoot: taskDir,
15
- sourceOfTruth: 'task',
15
+ sourceOfTruth: "task",
16
16
  artifactPaths: {
17
17
  taskConfig: taskPaths.taskConfigPath,
18
18
  workflowState: taskPaths.statePath,
19
19
  sourceDir: taskPaths.sourceDir,
20
20
  artifactsDir: `${taskDir}/artifacts`,
21
- analysis: getArtifactPath(taskDir, 'analyze'),
22
- plan: getArtifactPath(taskDir, 'plan'),
23
- modifyLog: getArtifactPath(taskDir, 'implement'),
24
- verifyResult: getArtifactPath(taskDir, 'verify'),
25
- retrospective: getArtifactPath(taskDir, 'retrospective'),
21
+ analysis: getArtifactPath(taskDir, "analyze"),
22
+ plan: getArtifactPath(taskDir, "plan"),
23
+ modifyLog: getArtifactPath(taskDir, "implement"),
24
+ verifyResult: getArtifactPath(taskDir, "verify"),
25
+ retrospective: getArtifactPath(taskDir, "retrospective"),
26
26
  },
27
27
  runRefs: {
28
28
  oneShotRuns: readModel.runRefs.oneShotRuns,
@@ -30,7 +30,7 @@ export async function runStatus(repoRoot, taskId) {
30
30
  },
31
31
  readModel,
32
32
  actionContext: {
33
- mode: 'dag',
33
+ mode: "dag",
34
34
  allowedEditRoots: taskConfig.allowedPaths,
35
35
  forbiddenPaths: taskConfig.forbiddenPaths,
36
36
  constraints: taskConfig.hardConstraints,
@@ -40,17 +40,18 @@ export async function runStatus(repoRoot, taskId) {
40
40
  status: state.status,
41
41
  complexity: taskConfig.complexity,
42
42
  maxFixLoops: taskConfig.maxFixLoops,
43
- verifyEnv: taskConfig.verifyEnv ?? 'clean',
43
+ verifyEnv: taskConfig.verifyEnv ?? "clean",
44
44
  maxGoalContinuationsPerRun: taskConfig.maxGoalContinuationsPerRun ?? 5,
45
45
  goal: state.goal,
46
46
  sourceState: state.sourceState,
47
47
  completedSteps: state.completedSteps,
48
48
  decisionLogSize: state.decisionLog.length,
49
49
  artifacts: state.artifacts,
50
- nextActions: [
51
- readModel.nextAction.command,
52
- `npm run dev -- instructions dag-draft --task ${taskId} --json`,
53
- ],
54
- nextSuggestedCommand: readModel.nextAction.command,
50
+ // Prefer task advance/status; keep single next action (no parallel command arrays).
51
+ next: {
52
+ kind: "advance-or-status",
53
+ command: readModel.nextAction.command,
54
+ reason: readModel.nextAction.reason,
55
+ },
55
56
  }, null, 2));
56
57
  }
@@ -120,8 +120,8 @@ export async function runStudyInit(repoRoot, args) {
120
120
  next: [
121
121
  'edit source/需求.md and source/执行约束.md',
122
122
  indexed
123
- ? `npm run dev -- dag run-task ${options.taskId} --profile auto --strict-models`
124
- : `npm run dev -- reference index ${options.taskId}; npm run dev -- dag run-task ${options.taskId} --profile auto --strict-models`,
123
+ ? `npm run dev -- task advance ${options.taskId} --profile auto --json`
124
+ : `npm run dev -- reference index ${options.taskId}; npm run dev -- task advance ${options.taskId} --profile auto --json`,
125
125
  ],
126
126
  }, null, 2));
127
127
  }