@tea-agent/loop-agent 0.1.0 → 0.2.1

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 (143) hide show
  1. package/AGENTS.md +62 -45
  2. package/CHANGELOG.md +60 -28
  3. package/README.md +160 -124
  4. package/bin/loop-agent.js +21 -21
  5. package/dist/adapters/index.js +3 -2
  6. package/dist/adapters/loop-agent.js +44 -2
  7. package/dist/application/dag/args.js +420 -0
  8. package/dist/application/dag/generate-task-dag.js +280 -0
  9. package/dist/application/dag/report-dag.js +14 -0
  10. package/dist/application/dag/run-dag.js +106 -0
  11. package/dist/application/dag/validate-dag.js +102 -0
  12. package/dist/application/loop/run-action.js +23 -0
  13. package/dist/cli/catalog.js +2 -237
  14. package/dist/cli/command-definitions.js +571 -0
  15. package/dist/cli/index.js +2 -0
  16. package/dist/cli/program.js +65 -1
  17. package/dist/cli/router.js +13 -0
  18. package/dist/cli-governance/active-residue-check.js +38 -0
  19. package/dist/commands/dag-report.js +6 -107
  20. package/dist/commands/dag-run-task.js +8 -466
  21. package/dist/commands/dag-validate.js +7 -179
  22. package/dist/commands/examples.js +90 -0
  23. package/dist/commands/init.js +1518 -0
  24. package/dist/commands/loop.js +57 -31
  25. package/dist/commands/pi-prompt.js +2 -9
  26. package/dist/commands/run-dag.js +7 -180
  27. package/dist/executors/cursor-executor-artifacts.js +3 -4
  28. package/dist/executors/cursor-worker-client.js +13 -3
  29. package/dist/executors/dag-cursor-executor.js +2 -3
  30. package/dist/executors/dag-pi-executor.js +3 -4
  31. package/dist/executors/dag-static-executor.js +2 -5
  32. package/dist/executors/pi-defaults.js +9 -0
  33. package/dist/executors/shell-executor.js +12 -20
  34. package/dist/governance/manifest-types.js +1 -0
  35. package/dist/infrastructure/harness/active-residue-policy.js +73 -0
  36. package/dist/infrastructure/harness/artifact-store.js +72 -0
  37. package/dist/infrastructure/harness/atomic-write.js +49 -0
  38. package/dist/infrastructure/harness/completed-facts-guard.js +40 -0
  39. package/dist/infrastructure/harness/loop-action-store.js +23 -0
  40. package/dist/infrastructure/harness/loop-store.js +41 -0
  41. package/dist/infrastructure/harness/one-shot-run-store.js +94 -0
  42. package/dist/infrastructure/harness/task-store.js +77 -0
  43. package/dist/records/one-shot-runs.js +26 -61
  44. package/dist/records/promotion.js +3 -4
  45. package/dist/shared/artifacts-core.js +5 -5
  46. package/dist/shared/logger.js +9 -15
  47. package/dist/task/delegate.js +4 -4
  48. package/dist/task/runtime.js +5 -7
  49. package/dist/task/state.js +6 -20
  50. package/dist/workflows/dag/convergence/controller.js +277 -0
  51. package/dist/workflows/dag/dynamic-runtime/condition.js +48 -0
  52. package/dist/workflows/dag/dynamic-runtime/loop-until.js +156 -0
  53. package/dist/workflows/dag/dynamic-runtime/map.js +185 -0
  54. package/dist/workflows/dag/dynamic-runtime/reduction.js +72 -0
  55. package/dist/workflows/dag/dynamic-runtime/shared.js +133 -0
  56. package/dist/workflows/dag/failure-routing.js +82 -0
  57. package/dist/workflows/dag/lifecycle.js +101 -8
  58. package/dist/workflows/dag/node-execution.js +262 -0
  59. package/dist/workflows/dag/report.js +73 -1
  60. package/dist/workflows/dag/run-store.js +36 -0
  61. package/dist/workflows/dag/runner.js +82 -1341
  62. package/dist/workflows/dag/scheduler.js +84 -0
  63. package/dist/workflows/dag/upstream-artifacts.js +20 -18
  64. package/dist/workflows/loop/actions/cursor-fix.js +191 -0
  65. package/dist/workflows/loop/actions/dag-action.js +130 -0
  66. package/dist/workflows/loop/actions/pi-review.js +267 -0
  67. package/dist/workflows/loop/actions/shared.js +157 -0
  68. package/dist/workflows/loop/actions/shell-verify.js +82 -0
  69. package/dist/workflows/loop/actions/types.js +1 -0
  70. package/dist/workflows/loop/actions/workflow-action.js +255 -0
  71. package/dist/workflows/loop/actions.js +55 -1212
  72. package/dist/workflows/loop/closeout.js +5 -4
  73. package/dist/workflows/loop/context.js +2 -3
  74. package/dist/workflows/loop/events.js +3 -2
  75. package/dist/workflows/loop/policy/auto-policy.js +104 -0
  76. package/dist/workflows/loop/policy/cursor-fix-policy.js +31 -0
  77. package/dist/workflows/loop/rounds.js +3 -3
  78. package/dist/workflows/loop/signals.js +4 -7
  79. package/dist/workflows/loop/state.js +11 -11
  80. package/docs/README.md +47 -44
  81. package/docs/agent-dag-recovery-playbook.md +32 -6
  82. package/docs/agent-dag-runner.md +17 -17
  83. package/docs/architecture/runtime-boundaries.md +147 -0
  84. package/docs/cursor-executor-usage.md +5 -5
  85. package/docs/decisions/README.md +2 -2
  86. package/docs/design/README.md +24 -24
  87. package/docs/development-principles.md +50 -50
  88. package/docs/dynamic-workflow-dag-engine-roadmap.md +6 -6
  89. package/docs/exec-plans/README.md +4 -4
  90. package/docs/exec-plans/active/README.md +10 -5
  91. package/docs/exec-plans/completed/README.md +9 -5
  92. package/docs/feature-workflow.md +111 -109
  93. package/docs/harness-methodology-verification.md +18 -18
  94. package/docs/loop-agent-harness.md +36 -36
  95. package/docs/production-readiness.md +96 -0
  96. package/docs/progress/README.md +2 -2
  97. package/docs/reports/README.md +4 -2
  98. package/docs/templates/agent-dag-decision-gate-dogfood-report.md +1 -1
  99. package/docs/templates/agent-dag-process-supervisor.prompt.md +2 -2
  100. package/docs/templates/agent-dag-report.schema.json +33 -2
  101. package/docs/templates/agent-dag-review-verdict.prompt.md +1 -1
  102. package/docs/templates/agent-dag.base.json +195 -195
  103. package/docs/templates/agent-dag.final-verification.json +190 -190
  104. package/docs/templates/agent-dag.schema.json +17 -17
  105. package/docs/templates/agent-dag.supervised-implementation.json +500 -500
  106. package/docs/templates/hybrid-dag.json +193 -193
  107. package/docs/templates/production-readiness-checklist.md +57 -0
  108. package/docs/templates/progress-log.md +7 -7
  109. package/docs/templates/project-start-checklist.md +8 -8
  110. package/docs/templates/qa-report.md +17 -11
  111. package/docs/templates/sprint-contract.md +19 -19
  112. package/docs/verification-matrix.md +37 -26
  113. package/examples/example-dag.json +51 -51
  114. package/examples/hybrid-loop-agent-dag.json +194 -194
  115. package/harness.json +5 -5
  116. package/package.json +62 -61
  117. package/skills/ai-engineering-context/SKILL.md +21 -21
  118. package/skills/loop-agent/SKILL.md +56 -171
  119. package/skills/loop-agent/references/README.md +6 -2
  120. package/skills/loop-agent/references/command-reference.md +107 -65
  121. package/skills/loop-agent/references/harness-policy.md +115 -115
  122. package/skills/loop-agent/references/hybrid-dag.md +30 -30
  123. package/skills/loop-agent/references/learned/README.md +13 -13
  124. package/skills/loop-agent/references/long-running-loop.md +59 -0
  125. package/skills/loop-agent/references/model-routing.md +1 -1
  126. package/skills/loop-agent/references/orchestrator-and-interventions.md +1 -1
  127. package/skills/loop-agent/references/pi-prompt.md +9 -9
  128. package/skills/loop-agent/references/pi-subagent-assisted-mode.md +0 -2
  129. package/skills/loop-agent/references/post-implementation-and-patterns.md +7 -7
  130. package/skills/loop-agent/references/task-workflow.md +19 -19
  131. package/skills/loop-agent/references/verification-and-failure-handling.md +54 -0
  132. package/skills/requesting-code-review/SKILL.md +40 -40
  133. package/skills/requesting-code-review/code-reviewer.md +4 -4
  134. package/skills/systematic-debugging/CREATION-LOG.md +43 -43
  135. package/skills/systematic-debugging/SKILL.md +113 -113
  136. package/skills/systematic-debugging/condition-based-waiting.md +20 -20
  137. package/skills/systematic-debugging/defense-in-depth.md +27 -27
  138. package/skills/systematic-debugging/root-cause-tracing.md +38 -38
  139. package/skills/systematic-debugging/test-academic.md +6 -6
  140. package/skills/systematic-debugging/test-pressure-1.md +6 -6
  141. package/skills/systematic-debugging/test-pressure-2.md +2 -2
  142. package/skills/systematic-debugging/test-pressure-3.md +6 -6
  143. package/skills/verification-before-completion/SKILL.md +37 -37
@@ -1,10 +1,11 @@
1
- import { readFile, writeFile } from "node:fs/promises";
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { writeLoopCloseout } from "../../infrastructure/harness/loop-store.js";
2
4
  import { getLoopPaths } from "./paths.js";
3
5
  import { readLoopRounds } from "./rounds.js";
4
6
  import { readLoopSignals, pendingLoopSignals } from "./signals.js";
5
7
  import { loadLoopState } from "./state.js";
6
8
  import { loadTaskConfig } from "../../task/runtime.js";
7
- import { repoRelativePath } from "../../shared/path-refs.js";
8
9
  function unique(values) {
9
10
  return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
10
11
  }
@@ -68,7 +69,7 @@ export async function generateLoopCloseout(repoRoot, taskId) {
68
69
  : [`Loop status is ${state.status}; treat this closeout as partial.`]),
69
70
  ]);
70
71
  const recordedAt = new Date().toISOString();
71
- const relativeCloseoutPath = repoRelativePath(repoRoot, paths.closeoutPath);
72
+ const relativeCloseoutPath = path.relative(repoRoot, paths.closeoutPath);
72
73
  const record = {
73
74
  schemaVersion: 1,
74
75
  taskId,
@@ -129,6 +130,6 @@ export async function generateLoopCloseout(repoRoot, taskId) {
129
130
  "- It does not modify completed DAG or one-shot run facts.",
130
131
  "",
131
132
  ].join("\n");
132
- await writeFile(paths.closeoutPath, markdown, "utf-8");
133
+ await writeLoopCloseout(repoRoot, taskId, markdown);
133
134
  return record;
134
135
  }
@@ -1,5 +1,4 @@
1
- import { writeFile } from "node:fs/promises";
2
- import { getLoopPaths } from "./paths.js";
1
+ import { writeLoopContext } from "../../infrastructure/harness/loop-store.js";
3
2
  import { readLoopRounds } from "./rounds.js";
4
3
  import { refreshLoopState } from "./state.js";
5
4
  import { appendLoopEvent } from "./events.js";
@@ -36,7 +35,7 @@ export async function rewriteLoopContext(repoRoot, taskId) {
36
35
  `- Last decision: ${latest.decision}.`,
37
36
  "",
38
37
  ].join("\n");
39
- await writeFile(getLoopPaths(repoRoot, taskId).contextPath, content, "utf-8");
38
+ await writeLoopContext(repoRoot, taskId, content);
40
39
  await refreshLoopState(repoRoot, taskId);
41
40
  await appendLoopEvent(repoRoot, taskId, {
42
41
  type: "context_rewrite",
@@ -1,4 +1,5 @@
1
- import { appendFile, readFile } from "node:fs/promises";
1
+ import { readFile } from "node:fs/promises";
2
+ import { appendLoopEventLine } from "../../infrastructure/harness/loop-store.js";
2
3
  import { getLoopPaths } from "./paths.js";
3
4
  export async function appendLoopEvent(repoRoot, taskId, input) {
4
5
  const now = new Date().toISOString();
@@ -12,7 +13,7 @@ export async function appendLoopEvent(repoRoot, taskId, input) {
12
13
  refs: input.refs ?? [],
13
14
  recordedAt: now,
14
15
  };
15
- await appendFile(getLoopPaths(repoRoot, taskId).eventsPath, `${JSON.stringify(entry)}\n`, "utf-8");
16
+ await appendLoopEventLine(repoRoot, taskId, entry);
16
17
  return entry;
17
18
  }
18
19
  export async function readLoopEvents(repoRoot, taskId) {
@@ -0,0 +1,104 @@
1
+ import { pendingLoopSignals } from "../signals.js";
2
+ export function decideNextLoopAutoAction(state, signals = [], options = {}) {
3
+ const pendingSignals = pendingLoopSignals(signals);
4
+ const urgent = pendingSignals.find((signal) => signal.urgent);
5
+ if (urgent) {
6
+ return {
7
+ action: "pause",
8
+ reason: `urgent ${urgent.type} signal: ${urgent.message}`,
9
+ decision: "pause",
10
+ };
11
+ }
12
+ const scopeChanged = pendingSignals.find((signal) => signal.type === "scope_changed");
13
+ if (scopeChanged) {
14
+ return {
15
+ action: "pause",
16
+ reason: `scope changed signal requires objective/context review: ${scopeChanged.message}`,
17
+ decision: "pause",
18
+ };
19
+ }
20
+ const reviewSignal = pendingSignals.find((signal) => signal.type === "review_feedback" || signal.type === "human_followup");
21
+ if (reviewSignal) {
22
+ return {
23
+ action: "pi-review",
24
+ reason: `${reviewSignal.type} signal requires read-only review: ${reviewSignal.message}`,
25
+ decision: "continue",
26
+ };
27
+ }
28
+ if (state.round >= state.maxRounds) {
29
+ return {
30
+ action: "blocked",
31
+ reason: `loop maxRounds reached (${state.round}/${state.maxRounds})`,
32
+ decision: "blocked",
33
+ };
34
+ }
35
+ if (state.failureStreak.category && state.failureStreak.count >= 2) {
36
+ return {
37
+ action: "blocked",
38
+ reason: `repeated failure category ${state.failureStreak.category} x${state.failureStreak.count}`,
39
+ decision: "blocked",
40
+ };
41
+ }
42
+ if (state.freshness.sourceStale) {
43
+ return {
44
+ action: "pause",
45
+ reason: "task source changed; rebuild or review action prompt before continuing",
46
+ decision: "pause",
47
+ };
48
+ }
49
+ if (state.lastAction === "shell-verify") {
50
+ return {
51
+ action: "pi-review",
52
+ reason: "shell verification already ran; request read-only review before next implementation decision",
53
+ decision: "continue",
54
+ };
55
+ }
56
+ if (state.lastAction === "pi-review") {
57
+ if (options.latestPiReview?.recommendedAction === "implement_fix") {
58
+ const policy = options.loopAutoWritePolicy ?? "off";
59
+ const approvalSignal = pendingSignals.find((signal) => signal.type === "approval");
60
+ const allowedByPolicy = policy === "enabled" ||
61
+ (policy === "approval-required" &&
62
+ (Boolean(options.allowCursorFix) || Boolean(approvalSignal)));
63
+ if (options.cursorFixPolicyError) {
64
+ return {
65
+ action: "pause",
66
+ reason: `cursor-fix policy guard failed: ${options.cursorFixPolicyError}`,
67
+ decision: "pause",
68
+ };
69
+ }
70
+ if (allowedByPolicy) {
71
+ return {
72
+ action: "cursor-fix",
73
+ reason: `pi-review recommended implement_fix; loopAutoWritePolicy=${policy}`,
74
+ decision: "continue",
75
+ };
76
+ }
77
+ }
78
+ return {
79
+ action: "dag",
80
+ reason: "read-only review produced next action; generate a governed DAG review packet",
81
+ decision: "continue",
82
+ };
83
+ }
84
+ const approvalSignal = pendingSignals.find((signal) => signal.type === "approval");
85
+ if (approvalSignal) {
86
+ return {
87
+ action: "dag",
88
+ reason: `approval signal received; generate next governed DAG packet: ${approvalSignal.message}`,
89
+ decision: "continue",
90
+ };
91
+ }
92
+ if (state.lastAction === "cursor-fix" || state.lastAction === "dag") {
93
+ return {
94
+ action: "shell-verify",
95
+ reason: `${state.lastAction} round must be followed by deterministic shell verification`,
96
+ decision: "continue",
97
+ };
98
+ }
99
+ return {
100
+ action: "dag",
101
+ reason: "default to DAG governed review packet for the next bounded round",
102
+ decision: "continue",
103
+ };
104
+ }
@@ -0,0 +1,31 @@
1
+ import { pathMatchesPattern } from "../../../shared/git-progress.js";
2
+ export function normalizePattern(pattern) {
3
+ return pattern.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/\*\*$/, "");
4
+ }
5
+ export function patternsOverlap(allowed, forbidden) {
6
+ const a = normalizePattern(allowed);
7
+ const f = normalizePattern(forbidden);
8
+ return (a === f ||
9
+ a.startsWith(`${f}/`) ||
10
+ f.startsWith(`${a}/`) ||
11
+ pathMatchesPattern(a, forbidden) ||
12
+ pathMatchesPattern(f, allowed));
13
+ }
14
+ export function validateLoopCursorFixPolicy(input) {
15
+ if (input.allowedPaths.length === 0) {
16
+ throw new Error("loop cursor-fix requires non-empty task allowedPaths");
17
+ }
18
+ for (const allowed of input.allowedPaths) {
19
+ for (const forbidden of input.forbiddenPaths) {
20
+ if (patternsOverlap(allowed, forbidden)) {
21
+ throw new Error(`loop cursor-fix allowed/forbidden paths overlap: ${allowed} vs ${forbidden}`);
22
+ }
23
+ }
24
+ }
25
+ if (input.complexity !== undefined &&
26
+ input.complexity !== "small" &&
27
+ !input.hasDagRound &&
28
+ !input.dagFallbackReason?.trim()) {
29
+ throw new Error("loop cursor-fix for medium/large tasks requires a prior loop dag round or task dagFallbackReason");
30
+ }
31
+ }
@@ -1,4 +1,5 @@
1
- import { readFile, writeFile } from "node:fs/promises";
1
+ import { readFile } from "node:fs/promises";
2
+ import { writeLoopRounds } from "../../infrastructure/harness/loop-store.js";
2
3
  import { getLoopPaths } from "./paths.js";
3
4
  import { refreshLoopState } from "./state.js";
4
5
  import { appendLoopEvent } from "./events.js";
@@ -33,14 +34,13 @@ export async function appendLoopRound(repoRoot, taskId, input) {
33
34
  failureCategory: input.failureCategory ?? "",
34
35
  completedCriteria: input.completedCriteria ?? [],
35
36
  };
36
- const paths = getLoopPaths(repoRoot, taskId);
37
37
  await appendLoopEvent(repoRoot, taskId, {
38
38
  type: "round_start",
39
39
  round: entry.round,
40
40
  message: `recording round ${entry.round} action ${entry.action}`,
41
41
  refs: entry.refs,
42
42
  });
43
- await writeFile(paths.roundsPath, `${rounds.map((round) => JSON.stringify(round)).join("\n")}${rounds.length > 0 ? "\n" : ""}${JSON.stringify(entry)}\n`, "utf-8");
43
+ await writeLoopRounds(repoRoot, taskId, [...rounds, entry]);
44
44
  const previousState = await refreshLoopState(repoRoot, taskId, {
45
45
  status: statusForDecision(entry.decision),
46
46
  round: entry.round,
@@ -1,4 +1,5 @@
1
- import { readFile, writeFile } from "node:fs/promises";
1
+ import { readFile } from "node:fs/promises";
2
+ import { writeLoopSignals as persistLoopSignals } from "../../infrastructure/harness/loop-store.js";
2
3
  import { getLoopPaths } from "./paths.js";
3
4
  import { LOOP_SIGNAL_TYPES, } from "./types.js";
4
5
  export function parseLoopSignalType(value) {
@@ -31,7 +32,7 @@ export async function appendLoopSignal(repoRoot, taskId, input) {
31
32
  if (!entry.message) {
32
33
  throw new Error("loop signal message cannot be empty");
33
34
  }
34
- await writeLoopSignals(repoRoot, taskId, [...signals, entry]);
35
+ await persistLoopSignals(repoRoot, taskId, [...signals, entry]);
35
36
  return entry;
36
37
  }
37
38
  export function pendingLoopSignals(signals) {
@@ -46,10 +47,6 @@ export async function drainLoopSignals(repoRoot, taskId, ids) {
46
47
  const drained = signals.map((signal) => idSet.has(signal.id) && !signal.drainedAt
47
48
  ? { ...signal, drainedAt: now }
48
49
  : signal);
49
- await writeLoopSignals(repoRoot, taskId, drained);
50
+ await persistLoopSignals(repoRoot, taskId, drained);
50
51
  return drained;
51
52
  }
52
- async function writeLoopSignals(repoRoot, taskId, signals) {
53
- await writeFile(getLoopPaths(repoRoot, taskId).signalsPath, signals.map((signal) => JSON.stringify(signal)).join("\n") +
54
- (signals.length > 0 ? "\n" : ""), "utf-8");
55
- }
@@ -1,6 +1,7 @@
1
- import { access, mkdir, readFile, writeFile } from "node:fs/promises";
1
+ import { access, readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { ensureLoopDir, writeLoopContext, writeLoopObjective, writeLoopRounds, writeLoopSignals, writeLoopEvents, writeLoopState, } from "../../infrastructure/harness/loop-store.js";
2
4
  import { getTaskDir } from "../../task/runtime.js";
3
- import { repoRelativePath } from "../../shared/path-refs.js";
4
5
  import { hashFileIfPresent, hashTaskSource } from "./hash.js";
5
6
  import { getLoopPaths } from "./paths.js";
6
7
  import { defaultContext, defaultObjective } from "./templates.js";
@@ -21,7 +22,7 @@ async function exists(filePath) {
21
22
  }
22
23
  }
23
24
  function rel(repoRoot, filePath) {
24
- return repoRelativePath(repoRoot, filePath);
25
+ return path.relative(repoRoot, filePath);
25
26
  }
26
27
  export async function buildLoopState(repoRoot, taskId, paths = getLoopPaths(repoRoot, taskId), previous) {
27
28
  const now = new Date().toISOString();
@@ -59,8 +60,7 @@ export async function buildLoopState(repoRoot, taskId, paths = getLoopPaths(repo
59
60
  };
60
61
  }
61
62
  export async function saveLoopState(repoRoot, taskId, state) {
62
- const paths = getLoopPaths(repoRoot, taskId);
63
- await writeFile(paths.statePath, `${JSON.stringify(state, null, 2)}\n`, "utf-8");
63
+ await writeLoopState(repoRoot, taskId, state);
64
64
  }
65
65
  export async function loadLoopState(repoRoot, taskId) {
66
66
  const paths = getLoopPaths(repoRoot, taskId);
@@ -86,21 +86,21 @@ export async function initializeLoop(repoRoot, taskId, options = {}) {
86
86
  if (stateExists && !options.recover) {
87
87
  throw new Error(`loop already initialized for task: ${taskId}`);
88
88
  }
89
- await mkdir(paths.loopDir, { recursive: true });
89
+ await ensureLoopDir(repoRoot, taskId);
90
90
  if (!(await exists(paths.objectivePath))) {
91
- await writeFile(paths.objectivePath, defaultObjective(taskId), "utf-8");
91
+ await writeLoopObjective(repoRoot, taskId, defaultObjective(taskId));
92
92
  }
93
93
  if (!(await exists(paths.contextPath))) {
94
- await writeFile(paths.contextPath, defaultContext(), "utf-8");
94
+ await writeLoopContext(repoRoot, taskId, defaultContext());
95
95
  }
96
96
  if (!(await exists(paths.roundsPath))) {
97
- await writeFile(paths.roundsPath, "", "utf-8");
97
+ await writeLoopRounds(repoRoot, taskId, []);
98
98
  }
99
99
  if (!(await exists(paths.signalsPath))) {
100
- await writeFile(paths.signalsPath, "", "utf-8");
100
+ await writeLoopSignals(repoRoot, taskId, []);
101
101
  }
102
102
  if (!(await exists(paths.eventsPath))) {
103
- await writeFile(paths.eventsPath, "", "utf-8");
103
+ await writeLoopEvents(repoRoot, taskId, []);
104
104
  }
105
105
  const previous = stateExists ? await loadLoopState(repoRoot, taskId) : undefined;
106
106
  const state = await buildLoopState(repoRoot, taskId, paths, {
package/docs/README.md CHANGED
@@ -1,61 +1,64 @@
1
- # Documentation Index
1
+ # 文档索引
2
2
 
3
- `docs/` is the governance root for loop-agent. It contains workflow rules, methodology, verification rules, execution plans, reports, progress logs, decisions, and reusable templates.
3
+ `docs/` loop-agent 的治理根目录,包含工作流规则、方法论、验证规则、执行计划、报告、进度日志、决策记录和可复用模板。
4
4
 
5
- Top-level `AGENTS.md` is the operating map. Durable knowledge belongs here: decisions, contracts, plans, verification evidence, debugging notes, and reusable process rules should be recorded under `docs/` instead of staying only in chat.
5
+ 顶层 `AGENTS.md` 是操作地图。长期知识应落在此处:决策、契约、计划、验证证据、调试笔记和可复用流程规则应记录在 `docs/` 下,而不是只留在聊天里。
6
6
 
7
- ## Core Documents
7
+ ## 核心文档
8
8
 
9
- - `development-principles.md` — repository development principles
10
- - `feature-workflow.md` — bounded feature workflow
11
- - `verification-matrix.md` — verification command selection
12
- - `loop-agent-harness.md` — runtime and command surface overview
13
- - `agent-dag-runner.md` — Agent DAG runner guide
14
- - `cursor-executor-usage.md` — Cursor executor usage
15
- - `dynamic-workflow-dag-engine-roadmap.md` — Dynamic Workflow DAG Engine roadmap and fit analysis
9
+ - `development-principles.md` — 仓库开发原则
10
+ - `architecture/runtime-boundaries.md` — runtime 层边界与依赖方向
11
+ - `feature-workflow.md` — 有边界的功能工作流
12
+ - `verification-matrix.md` — 验证命令选择
13
+ - `production-readiness.md` — Production Readiness v0.1 范围、证据与 DAG hardening 标准
14
+ - `loop-agent-harness.md` — runtime command surface 概览
15
+ - `agent-dag-runner.md` — Agent DAG runner 指南
16
+ - `cursor-executor-usage.md` — Cursor executor 用法
17
+ - `dynamic-workflow-dag-engine-roadmap.md` — Dynamic Workflow DAG Engine 路线图与适配分析
16
18
 
17
- ## Methodology
19
+ ## 方法论
18
20
 
19
- - `harness-methodology-tdd.md` — TDD discipline for behavior changes and bug fixes
20
- - `harness-methodology-verification.md` — verification discipline before completion claims
21
- - `harness-methodology-debugging.md` — systematic debugging workflow before fixes
21
+ - `harness-methodology-tdd.md` — 行为变更与 bug 修复的 TDD 纪律
22
+ - `harness-methodology-verification.md` — 完成声明前的验证纪律
23
+ - `harness-methodology-debugging.md` — 修复前的系统化调试工作流
22
24
 
23
- ## Artifacts
25
+ ## 产物目录
24
26
 
25
- - `design/README.md` — draft design notes and implementation contracts
26
- - `exec-plans/active/README.md` — active execution plans
27
- - `exec-plans/completed/README.md` — completed execution plans
28
- - `progress/README.md` — progress handoff logs
29
- - `reports/README.md` — verification and audit reports
30
- - `decisions/README.md` — architecture decisions
31
- - `templates/` — reusable planning, reporting, and DAG templates
27
+ - `design/README.md` — 设计草稿与实现契约
28
+ - `exec-plans/active/README.md` — 进行中的执行计划
29
+ - `exec-plans/completed/README.md` — 已完成的执行计划
30
+ - `progress/README.md` — 进度交接日志
31
+ - `reports/README.md` — 验证与审计报告
32
+ - `decisions/README.md` — 架构决策
33
+ - `templates/` — 可复用的规划、报告与 DAG 模板
32
34
 
33
- ## Repository Skills
35
+ ## 仓库 Skills
34
36
 
35
- - `../skills/loop-agent/` — loop-agent's own skill instructions and references.
36
- - Each additional skill uses its own subdirectory under repository-root `../skills/`; these local copies are referenced by DAG templates so maintenance does not depend on external agent skill directories.
37
+ - `../skills/loop-agent/` — loop-agent 自身的 skill 指令与参考资料
38
+ - 每个额外 skill 在仓库根 `../skills/` 下使用独立子目录;这些本地副本由 DAG 模板引用,维护不依赖外部 agent skill 目录
37
39
 
38
- ## Templates
40
+ ## 模板
39
41
 
40
- - `templates/project-start-checklist.md` — pre-work checklist
41
- - `templates/feature-spec.md` — bounded feature specification
42
- - `templates/sprint-contract.md` — implementation contract and acceptance criteria
43
- - `templates/exec-plan.md` — execution plan for non-trivial work
44
- - `templates/progress-log.md` — progress and handoff log
45
- - `templates/qa-report.md` — verification and QA evidence
46
- - `templates/adr.md` — architecture decision record
42
+ - `templates/project-start-checklist.md` — 开工前检查清单
43
+ - `templates/feature-spec.md` — 有边界的功能规格
44
+ - `templates/sprint-contract.md` — 实现契约与验收标准
45
+ - `templates/exec-plan.md` — 非平凡工作的执行计划
46
+ - `templates/progress-log.md` — 进度与交接日志
47
+ - `templates/qa-report.md` — 验证与 QA 证据
48
+ - `templates/production-readiness-checklist.md` — 低/中风险单仓库 DAG readiness 检查清单
49
+ - `templates/adr.md` — 架构决策记录(ADR)
47
50
 
48
- ## Maintenance
51
+ ## 维护
49
52
 
50
- After docs changes, run:
51
-
52
- ```bash
53
- bash scripts/check-repo.sh
54
- ```
55
-
56
- On Windows, run Bash scripts through Git Bash or a configured compatible Bash. Use platform-native paths for actual file operations; reserve `/` for repo refs, JSON/Markdown evidence refs, and glob conventions.
57
-
58
- For a full local gate, run:
53
+ 文档变更后运行:
54
+
55
+ ```bash
56
+ bash scripts/check-repo.sh
57
+ ```
58
+
59
+ Windows 上通过 Git Bash 或已配置的兼容 Bash 运行脚本。实际文件操作使用平台原生路径;`/` 仅用于 repo 引用、JSON/Markdown 证据引用和 glob 约定。
60
+
61
+ 完整本地门禁:
59
62
 
60
63
  ```bash
61
64
  bash scripts/ci.sh
@@ -1,4 +1,4 @@
1
- # Agent DAG Recovery Playbook
1
+ # Agent DAG Recovery Playbook(恢复手册)
2
2
 
3
3
  > **关联**:[`agent-dag-runner.md`](agent-dag-runner.md)(CLI 与 run 语义)· [`templates/agent-dag-decision-gate.prompt.md`](templates/agent-dag-decision-gate.prompt.md)(Decision Gate 消费 recovery 证据)
4
4
 
@@ -6,6 +6,17 @@
6
6
 
7
7
  Agent DAG **recovery planning 是只读、派生、advisory** 的。`dag report` 与 `buildDagDecisionGateEvidence()` 从 `.harness/dag-runs/` 的 canonical facts 聚合 `normalizedFailureCategory` → `recoveryRecommendation`,供人工或 Decision Gate prompt 消费。
8
8
 
9
+ Production Readiness v0.1 在 normalized DAG category 之上增加 product-line routing。Report 与 doctor 输出应保留 raw DAG fact 并派生,不重写已完成 facts:
10
+
11
+ ```text
12
+ raw_failure_category
13
+ dag_normalized_failure_category
14
+ product_line_failure_category
15
+ recommended_follow_up
16
+ ```
17
+
18
+ Product-line taxonomy 定义见 `design/state-and-failure-taxonomy.md`。
19
+
9
20
  **非目标(本 playbook 不覆盖、runner 不实现):**
10
21
 
11
22
  - 自动 retry / resume 节点执行
@@ -47,7 +58,7 @@ npm run dev -- dag decision inspect --run-id <run-id> [--node-id <node-id>]
47
58
  npm run dev -- dag decision validate --run-id <run-id> [--node-id <node-id>]
48
59
  ```
49
60
 
50
- ### Paused run 操作员路径
61
+ ### Paused run operator 路径
51
62
 
52
63
  1. `dag report --paused-latest --json` 或 `dag doctor` — 定位最新 paused run 与 `primaryRecovery`
53
64
  2. `dag status --run-id <id>` — 读 `approvalFlow`、`escalationArtifactPath`、`pendingNodes`
@@ -79,7 +90,22 @@ Decision Gate prompt 侧:`buildDagDecisionGateEvidence()`(`./src/core/dag-de
79
90
  | `inspect-upstream` | 先查上游失败 | SKIPPED 下游节点 |
80
91
  | `unknown` | 未映射类别(不应出现在正常派生路径) | 内部兜底 |
81
92
 
82
- ## 类别 动作 → 操作员指引
93
+ ## Product-Line Routing v0.1
94
+
95
+ | Product-line category | Default follow-up |
96
+ |---|---|
97
+ | `SpecUnclear` | `spec-clarification` |
98
+ | `ContractMismatch` | `architecture-contract-fix` |
99
+ | `ProductBug` | `dev-fix` |
100
+ | `TestBug` | `qa-fix-test` |
101
+ | `EnvFailure` | `env-fix` 或 retry verify |
102
+ | `FlakyTest` | `flaky-test-analysis` |
103
+ | `RiskyChange` | `human-review` / `architecture-review` |
104
+ | `DependencyFailure` | unblock dependency |
105
+ | `NeedsHuman` | `human-review` |
106
+ | `Unknown` | human triage |
107
+
108
+ ## 类别 → 动作 → operator 指引
83
109
 
84
110
  | Normalized category | Recovery action | Operator guidance | Anti-patterns |
85
111
  |---------------------|-----------------|-------------------|---------------|
@@ -107,9 +133,9 @@ Decision Gate prompt 侧:`buildDagDecisionGateEvidence()`(`./src/core/dag-de
107
133
  1. **Primary Failure** — `primaryFailure`(node 或 run scope)
108
134
  2. **Recovery Action** — `primaryRecovery`(action、summary、reason、flags、commandHint)
109
135
  3. **Blocked Downstream / Skipped Nodes** — `downstreamSkippedNodes`
110
- 4. **Recommended Operator Action** — 面向操作员的步骤摘要
136
+ 4. **Recommended Operator Action** — 面向 operator 的步骤摘要
111
137
 
112
- 保存 handoff 时重定向到平台临时目录或 `docs/reports/`,不要写入 `.harness/dag-runs/`。
138
+ 保存 handoff 时重定向到平台临时目录或 `docs/reports/`,不要写入 `.harness/dag-runs/`。
113
139
 
114
140
  ## Decision Gate 消费约定
115
141
 
@@ -123,7 +149,7 @@ Decision Gate prompt 侧:`buildDagDecisionGateEvidence()`(`./src/core/dag-de
123
149
 
124
150
  `dag doctor` 与 `dag status` 通过 `detectDagRunHealthIssues()` 检测 lifecycle 不一致,**不** mutate run facts。
125
151
 
126
- | Code | 典型场景 | 操作员指引 |
152
+ | Code | 典型场景 | operator 指引 |
127
153
  |------|----------|------------|
128
154
  | `terminal-in-active` | run 已完成但 `active/<run-id>/` 残留 | 对照 `completed/` canonical facts;手动 archive 或删除 stale 目录 |
129
155
  | `paused-in-active` | pause 后目录未迁至 `paused/` | `dag doctor` 诊断;修复 facts 后再 approve/resume |
@@ -1,40 +1,40 @@
1
1
  # Agent DAG Runner
2
2
 
3
- Agent DAG is loop-agent's declarative orchestration runtime. A DAG decomposes work into nodes, runs eligible ranks in order, records artifacts, and uses gates for review and verification.
3
+ Agent DAG loop-agent 的声明式编排 runtimeDAG 将工作拆为节点、按序执行 eligible ranks、记录 artifacts,并用 gate review 与验证。
4
4
 
5
- ## Basic Use
5
+ ## 基本用法
6
6
 
7
7
  ```bash
8
- loop-agent dag run-task <task-id> --profile auto --strict-models --output <temp-dir>/<task-id>-dag.json
9
- loop-agent dag validate --dag <temp-dir>/<task-id>-dag.json --strict-models --strict-governance
10
- loop-agent run-dag --dag <temp-dir>/<task-id>-dag.json --cwd .
11
- ```
12
-
13
- `<temp-dir>` is the platform-native temp directory. On Windows, pass native paths for actual `--output`, `--dag`, and `--cwd` values.
8
+ loop-agent dag run-task <task-id> --profile auto --strict-models --output <temp-dir>/<task-id>-dag.json
9
+ loop-agent dag validate --dag <temp-dir>/<task-id>-dag.json --strict-models --strict-governance
10
+ loop-agent run-dag --dag <temp-dir>/<task-id>-dag.json --cwd .
11
+ ```
12
+
13
+ `<temp-dir>` 为平台原生临时目录。Windows `--output`、`--dag`、`--cwd` 的实际值用原生路径。
14
14
 
15
15
  ## Executors
16
16
 
17
- - `static`: deterministic generated artifacts or notes
18
- - `shell`: verification and file-system checks
19
- - `pi`: planning, review, diagnosis, and bounded writing when a node sets `toolProfile: "write"`
20
- - `cursor`: optional bounded write backend when explicitly enabled
17
+ - `static`:确定性生成的 artifacts notes
18
+ - `shell`:验证与文件系统检查
19
+ - `pi`:规划、review、诊断;节点设 `toolProfile: "write"` 时有界写入
20
+ - `cursor`:显式启用时的可选有界写后端
21
21
 
22
22
  ## Skills
23
23
 
24
- DAG specs may declare `defaults.skills`, `skillsByRole`, and node-level `skills`. The runner resolves local instructions from `skills/<skill-name>/SKILL.md` and records resolution metadata in each node's `skills.json` artifact.
24
+ DAG spec 可声明 `defaults.skills`、`skillsByRole` 与节点级 `skills`。Runner `skills/<skill-name>/SKILL.md` 解析本地指令,并在各节点 `skills.json` artifact 中记录解析元数据。
25
25
 
26
- The `loop-agent` skill lives at `skills/loop-agent/SKILL.md`. The legacy root `skill/SKILL.md` path is retained only as a compatibility fallback for older worktrees.
26
+ `loop-agent` skill 位于 `skills/loop-agent/SKILL.md`。遗留根路径 `skill/SKILL.md` 仅为旧 worktree 保留兼容 fallback
27
27
 
28
28
  ## Artifacts
29
29
 
30
- DAG artifacts belong under:
30
+ DAG artifacts 位于:
31
31
 
32
32
  ```text
33
33
  .harness/dag-runs/<state>/<run-id>/artifacts/<node-id>/
34
34
  ```
35
35
 
36
- Root `artifacts/` is not a valid default DAG artifact location.
36
+ 根目录 `artifacts/` 不是有效的默认 DAG artifact 位置。
37
37
 
38
38
  ## Shell Gates
39
39
 
40
- - `shell.verdictGate` reads `$HARNESS_DAG_RUN_DIR/<fromNodeId>.json` from the injected current run directory; 不应自行发现 active run paths.
40
+ - `shell.verdictGate` 从注入的当前 run 目录读取 `$HARNESS_DAG_RUN_DIR/<fromNodeId>.json`;不应自行发现 active run paths