@tea-agent/loop-agent 0.8.0 → 0.10.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/AGENTS.md +10 -0
  2. package/CHANGELOG.md +101 -1
  3. package/README.md +69 -5
  4. package/dist/application/dag/args.js +13 -16
  5. package/dist/application/dag/generate-task-dag.js +32 -2
  6. package/dist/application/dag/run-dag.js +1 -27
  7. package/dist/application/dag/validate-dag.js +2 -2
  8. package/dist/application/loop/run-action.js +0 -4
  9. package/dist/cli/command-definitions.js +7 -11
  10. package/dist/cli/program.js +9 -21
  11. package/dist/commands/cursor-prompt.js +42 -82
  12. package/dist/commands/dag-approve.js +36 -0
  13. package/dist/commands/dag-reconcile-run.js +118 -0
  14. package/dist/commands/delegate.js +75 -77
  15. package/dist/commands/doctor.js +0 -18
  16. package/dist/commands/init.js +60 -40
  17. package/dist/commands/instructions.js +7 -10
  18. package/dist/commands/loop.js +4 -20
  19. package/dist/executors/config-core.js +0 -51
  20. package/dist/executors/dag-pi-executor.js +1 -1
  21. package/dist/executors/dag.js +0 -1
  22. package/dist/executors/index.js +0 -2
  23. package/dist/executors/model-routing.js +9 -9
  24. package/dist/executors/shell-executor.js +75 -9
  25. package/dist/governance/checks.js +6 -3
  26. package/dist/governance/manifest-types.js +33 -2
  27. package/dist/infrastructure/harness/loop-action-store.js +0 -3
  28. package/dist/records/harvest.js +2 -23
  29. package/dist/records/one-shot-runs.js +1 -1
  30. package/dist/shared/artifacts-core.js +24 -5
  31. package/dist/shared/output-truncation.js +37 -0
  32. package/dist/shared/package-metadata.js +353 -0
  33. package/dist/shared/reference-context.js +48 -22
  34. package/dist/{executors/cursor-executor.js → sidecars/cursor-prompt/executor.js} +2 -42
  35. package/dist/sidecars/cursor-prompt/index.js +3 -0
  36. package/dist/sidecars/cursor-prompt/stream.js +121 -0
  37. package/dist/task/config-types.js +30 -13
  38. package/dist/task/delegate.js +9 -21
  39. package/dist/task/runtime.js +2 -3
  40. package/dist/worker/cli.js +243 -0
  41. package/dist/worker/closeout/apply.js +73 -0
  42. package/dist/worker/closeout/preview.js +30 -0
  43. package/dist/worker/delivery/final-verification.js +194 -0
  44. package/dist/worker/delivery/git-transaction.js +354 -0
  45. package/dist/worker/delivery/package.js +502 -0
  46. package/dist/worker/feature/decision-loader.js +68 -0
  47. package/dist/worker/feature/discover.js +14 -0
  48. package/dist/worker/feature/next-action.js +74 -0
  49. package/dist/worker/feature/reducer.js +133 -0
  50. package/dist/worker/feature/review.js +502 -0
  51. package/dist/worker/feature/run.js +365 -0
  52. package/dist/worker/feature/types.js +1 -0
  53. package/dist/worker/follow-up/approve.js +270 -0
  54. package/dist/worker/follow-up/factory.js +234 -0
  55. package/dist/worker/follow-up/paths.js +25 -0
  56. package/dist/worker/follow-up/policy.js +26 -0
  57. package/dist/worker/follow-up/schema.js +93 -0
  58. package/dist/worker/follow-up/store.js +96 -0
  59. package/dist/worker/loop-agent/loop-agent-client.js +345 -101
  60. package/dist/worker/metrics/projector.js +139 -0
  61. package/dist/worker/observability/read-model.js +282 -15
  62. package/dist/worker/observe/paths.js +17 -5
  63. package/dist/worker/observe/routes.js +78 -20
  64. package/dist/worker/observe/server.js +8 -6
  65. package/dist/worker/observe/static/app.js +1045 -177
  66. package/dist/worker/observe/static/index.html +70 -43
  67. package/dist/worker/observe/static/styles.css +553 -610
  68. package/dist/worker/pool/run-store.js +14 -2
  69. package/dist/worker/pool/validation.js +59 -0
  70. package/dist/worker/preflight.js +49 -1
  71. package/dist/worker/report/morning-report.js +41 -6
  72. package/dist/worker/run-task/run-task.js +23 -13
  73. package/dist/worker/runner/run-ready.js +89 -11
  74. package/dist/worker/task-spec/schema.js +0 -1
  75. package/dist/workflows/dag/convergence/controller.js +1 -1
  76. package/dist/workflows/dag/executor-registry.js +0 -2
  77. package/dist/workflows/dag/governance-profile.js +10 -0
  78. package/dist/workflows/dag/init-hybrid.js +601 -26
  79. package/dist/workflows/dag/lifecycle.js +146 -0
  80. package/dist/workflows/dag/node-execution.js +64 -7
  81. package/dist/workflows/dag/prompt.js +16 -0
  82. package/dist/workflows/dag/report.js +2 -0
  83. package/dist/workflows/dag/runner.js +176 -119
  84. package/dist/workflows/dag/scheduler.js +7 -2
  85. package/dist/workflows/dag/skill-snapshot.js +527 -0
  86. package/dist/workflows/dag/types.js +45 -9
  87. package/dist/workflows/dag/validate.js +5 -8
  88. package/dist/workflows/loop/actions/dag-action.js +0 -2
  89. package/dist/workflows/loop/actions/shared.js +1 -1
  90. package/dist/workflows/loop/actions.js +14 -31
  91. package/dist/workflows/loop/benchmark.js +1 -1
  92. package/dist/workflows/loop/index.js +1 -1
  93. package/dist/workflows/loop/policy/auto-policy.js +22 -14
  94. package/dist/workflows/loop/policy/path-patterns.js +13 -0
  95. package/docs/README.md +35 -12
  96. package/docs/agent-dag-recovery-playbook.md +1 -1
  97. package/docs/architecture/README.md +26 -0
  98. package/docs/architecture/dag-execution.md +134 -0
  99. package/docs/architecture/evolution.md +52 -0
  100. package/docs/architecture/facts-and-state.md +58 -0
  101. package/docs/architecture/runtime-boundaries.md +41 -15
  102. package/docs/architecture/system-overview.md +93 -0
  103. package/docs/architecture/worker-and-feature.md +81 -0
  104. package/docs/cursor-prompt-sidecar.md +36 -0
  105. package/docs/decisions/README.md +13 -1
  106. package/docs/design/README.md +39 -13
  107. package/docs/development-principles.md +1 -1
  108. package/docs/exec-plans/active/README.md +2 -2
  109. package/docs/exec-plans/completed/README.md +21 -0
  110. package/docs/feature-workflow.md +44 -4
  111. package/docs/init-surface.manifest.json +63 -1
  112. package/docs/loop-agent-harness.md +65 -3
  113. package/docs/progress/README.md +27 -0
  114. package/docs/reports/README.md +74 -5
  115. package/docs/skills/README.md +2 -1
  116. package/docs/skills/vetted-skill-registry.md +2 -1
  117. package/docs/templates/agent-dag-report.schema.json +4 -2
  118. package/docs/templates/agent-dag.base.json +0 -5
  119. package/docs/templates/agent-dag.final-verification.json +0 -5
  120. package/docs/templates/agent-dag.schema.json +1 -2
  121. package/docs/templates/agent-dag.supervised-implementation.json +1 -6
  122. package/docs/templates/backend-test-dag.generate-pytest.prompt.md +131 -0
  123. package/docs/templates/backend-test-dag.json +213 -0
  124. package/docs/templates/backend-test-dag.retrospect.prompt.md +128 -0
  125. package/docs/templates/backend-test-dag.review-cases.prompt.md +85 -0
  126. package/docs/templates/frontend-design-contract.md +33 -0
  127. package/docs/templates/frontend-task-constraints.md +25 -0
  128. package/docs/templates/frontend-task-requirement.md +61 -0
  129. package/docs/templates/harness.schema.json +8 -5
  130. package/docs/templates/hybrid-dag.json +1 -6
  131. package/docs/templates/init-evolution-review.md +4 -2
  132. package/docs/templates/interactive-ui-round2-experiment.md +1 -1
  133. package/docs/templates/product-line/task.yaml +0 -1
  134. package/docs/templates/worker-dogfood-evidence.md +28 -0
  135. package/docs/templates/worker-dogfood-setup.md +20 -0
  136. package/docs/verification-matrix.md +17 -0
  137. package/examples/decision-gate-agent-dag.json +87 -33
  138. package/examples/example-dag.json +0 -5
  139. package/examples/hybrid-loop-agent-dag.json +0 -5
  140. package/harness.json +6 -11
  141. package/package.json +22 -44
  142. package/scripts/check-product-line-docs.sh +10 -3
  143. package/scripts/check-task-pool-root.sh +1 -1
  144. package/skills/agent-worker/SKILL.md +37 -0
  145. package/skills/agent-worker/references/agent-worker-operator.md +43 -0
  146. package/skills/frontend-design-review/SKILL.md +59 -0
  147. package/skills/frontend-design-review/references/review-checklist.md +37 -0
  148. package/skills/frontend-implementation/SKILL.md +48 -0
  149. package/skills/frontend-implementation/references/code-standards.md +34 -0
  150. package/skills/frontend-implementation/references/design-spec.md +46 -0
  151. package/skills/frontend-implementation/references/node-contracts.md +32 -0
  152. package/skills/frontend-review/SKILL.md +53 -0
  153. package/skills/frontend-review/references/review-findings.md +42 -0
  154. package/skills/frontend-verification/SKILL.md +40 -0
  155. package/skills/frontend-verification/references/verification-checklist.md +56 -0
  156. package/skills/grill-me/SKILL.md +10 -0
  157. package/skills/grill-with-docs/SKILL.md +88 -0
  158. package/skills/grill-with-docs/adr-format.md +47 -0
  159. package/skills/grill-with-docs/context-format.md +60 -0
  160. package/skills/init-capability-evolution/SKILL.md +1 -0
  161. package/skills/loop-agent/SKILL.md +11 -9
  162. package/skills/loop-agent/references/command-reference.md +28 -15
  163. package/skills/loop-agent/references/docs-converge.md +126 -0
  164. package/skills/loop-agent/references/harness-policy.md +7 -7
  165. package/skills/loop-agent/references/hybrid-dag.md +13 -15
  166. package/skills/loop-agent/references/long-running-loop.md +4 -6
  167. package/skills/loop-agent/references/multi-worktree.md +6 -6
  168. package/skills/loop-agent/references/orchestrator-and-interventions.md +3 -3
  169. package/skills/loop-agent/references/pi-subagent-assisted-mode.md +14 -11
  170. package/skills/loop-agent/references/verification-and-failure-handling.md +8 -3
  171. package/skills/using-git-worktrees/SKILL.md +215 -0
  172. package/dist/commands/cursor-worker.js +0 -43
  173. package/dist/cursor-worker-entry.js +0 -8
  174. package/dist/executors/cursor-artifacts.js +0 -33
  175. package/dist/executors/cursor-execution-log.js +0 -81
  176. package/dist/executors/cursor-executor-artifacts.js +0 -134
  177. package/dist/executors/cursor-run.js +0 -115
  178. package/dist/executors/cursor-tool.js +0 -94
  179. package/dist/executors/cursor-worker-client.js +0 -223
  180. package/dist/executors/cursor-worker-protocol.js +0 -18
  181. package/dist/executors/cursor-worker-server.js +0 -54
  182. package/dist/executors/cursor-worker.js +0 -3
  183. package/dist/executors/cursor.js +0 -6
  184. package/dist/executors/dag-cursor-executor.js +0 -87
  185. package/dist/workflows/loop/actions/cursor-fix.js +0 -191
  186. package/dist/workflows/loop/policy/cursor-fix-policy.js +0 -31
  187. package/docs/cursor-executor-usage.md +0 -25
  188. package/docs/dynamic-workflow-dag-engine-roadmap.md +0 -1749
@@ -2,7 +2,7 @@ import { access, readdir, readFile, writeFile } from "node:fs/promises";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { assertValidDagSpec } from "./validate.js";
5
- import { DEFAULT_DAG_EXECUTOR_MODELS, parseDagSpec, } from "./types.js";
5
+ import { DEFAULT_DAG_OUTPUT_LANGUAGE, DEFAULT_DAG_EXECUTOR_MODELS, parseDagSpec, } from "./types.js";
6
6
  import { pathMatchesPattern } from "../../shared/git-progress.js";
7
7
  import { BASELINE_FORBIDDEN_PATHS } from "./governance-constants.js";
8
8
  import { resolveAdapter } from "../../adapters/index.js";
@@ -58,27 +58,38 @@ const HYBRID_SKILLS_BY_ROLE = {
58
58
  verifier: ["verification-before-completion", "systematic-debugging"],
59
59
  closeout: ["loop-agent", "verification-before-completion"],
60
60
  };
61
+ const FRONTEND_DEFAULTS = {
62
+ ...HYBRID_DEFAULTS,
63
+ skills: [],
64
+ };
65
+ const FRONTEND_SKILLS_BY_ROLE = {
66
+ planner: [],
67
+ scout: [],
68
+ implementer: [],
69
+ reviewer: [],
70
+ supervisor: [],
71
+ verifier: [],
72
+ closeout: [],
73
+ };
74
+ const FRONTEND_IMPLEMENTATION_SKILLS = ["frontend-implementation"];
75
+ const FRONTEND_DESIGN_REVIEW_SKILLS = ["frontend-design-review"];
76
+ const FRONTEND_REVIEW_SKILLS = ["frontend-review"];
77
+ const FRONTEND_VERIFICATION_SKILLS = ["frontend-verification"];
61
78
  const IMPLEMENT_WRITESET_PLACEHOLDER = "REPLACE/WITH/NARROW/IMPLEMENT/PATHS/**";
62
79
  function resolveEnabledExecutors(manifestExecutors) {
63
80
  return Object.entries(manifestExecutors)
64
81
  .filter(([, config]) => config.enabled !== false)
65
- .map(([name]) => name);
82
+ .map(([name]) => name)
83
+ .filter((name) => name === "pi");
66
84
  }
67
- function resolveImplementationExecutor(sources) {
68
- const enabled = sources.enabledExecutors
69
- ? new Set(sources.enabledExecutors)
70
- : undefined;
71
- if (!enabled || enabled.has("pi"))
72
- return "pi";
73
- if (enabled.has("cursor"))
74
- return "cursor";
75
- return "pi";
85
+ function implementationNodeId() {
86
+ return "implement-pi";
76
87
  }
77
- function implementationNodeId(executor) {
78
- return executor === "cursor" ? "implement-cursor" : "implement-pi";
88
+ function repairNodeId() {
89
+ return "repair-pi";
79
90
  }
80
- function repairNodeId(executor) {
81
- return executor === "cursor" ? "repair-cursor" : "repair-pi";
91
+ function frontendImplementationNodeId() {
92
+ return "frontend-implement-pi";
82
93
  }
83
94
  function probePathsForWriteSetEntry(entry) {
84
95
  const normalized = entry.replace(/\\/g, "/").replace(/^\.\//, "");
@@ -275,6 +286,65 @@ function buildVerifyShellCommands(input) {
275
286
  const repoRoot = input.repoRoot;
276
287
  return input.commands.map((command) => verifyCommandToShell(repoRoot, command));
277
288
  }
289
+ function markdownVerifyCommand(repoRoot, command) {
290
+ const args = command.split(/\s+/).filter(Boolean);
291
+ if (args.length === 0 ||
292
+ args.some((arg) => !/^[a-z0-9@._/:=,+-]+$/i.test(arg))) {
293
+ return null;
294
+ }
295
+ return {
296
+ args,
297
+ cwd: repoRoot,
298
+ label: command,
299
+ };
300
+ }
301
+ function extractFrontendVerifyCommandsFromMarkdown(input) {
302
+ if (!input.repoRoot)
303
+ return { staticCommands: [], behaviorCommands: [] };
304
+ const markdown = [
305
+ input.requirementMarkdown,
306
+ input.constraintMarkdown ?? "",
307
+ ].join("\n");
308
+ const commands = new Set();
309
+ for (const line of markdown.split(/\r?\n/)) {
310
+ const bulletless = line
311
+ .trim()
312
+ .replace(/^[-*]\s*(?:\[[ xX]\]\s*)?/, "")
313
+ .trim();
314
+ const codeSpanCommands = Array.from(bulletless.matchAll(/`([^`]+)`/g), (match) => match[1].trim());
315
+ const candidates = codeSpanCommands.length > 0 ? codeSpanCommands : [bulletless];
316
+ for (const candidate of candidates) {
317
+ if (/^(npm|pnpm|yarn|bun)\s+(run\s+)?[a-z0-9:_-]+(?:\s.*)?$/i.test(candidate) ||
318
+ /^(npx|pnpm\s+exec|yarn\s+exec|bunx)\s+(vitest|jest|playwright|cypress|tsc|eslint)(?:\s.*)?$/i.test(candidate)) {
319
+ commands.add(candidate);
320
+ }
321
+ }
322
+ }
323
+ const staticCommands = [];
324
+ const behaviorCommands = [];
325
+ for (const command of commands) {
326
+ const verifyCommand = markdownVerifyCommand(input.repoRoot, command);
327
+ if (!verifyCommand)
328
+ continue;
329
+ if (/\b(typecheck|lint|eslint|tsc|build)\b/i.test(command)) {
330
+ staticCommands.push(verifyCommand);
331
+ continue;
332
+ }
333
+ if (/\b(test|vitest|jest|playwright|cypress|e2e)\b/i.test(command)) {
334
+ behaviorCommands.push(verifyCommand);
335
+ }
336
+ }
337
+ return { staticCommands, behaviorCommands };
338
+ }
339
+ function chooseFrontendVerifyCommands(input) {
340
+ if (input.parsedCommands.length > 0) {
341
+ return { commands: input.parsedCommands, commandSource: "inline" };
342
+ }
343
+ if (input.adapterCommands && input.adapterCommands.length > 0) {
344
+ return { commands: input.adapterCommands, commandSource: "adapter" };
345
+ }
346
+ return { commandSource: "inline" };
347
+ }
278
348
  function resolveDagVerifyStrategy(taskConfig) {
279
349
  return {
280
350
  intermediateQuota: taskConfig.dagVerifyStrategy?.intermediateQuota ?? taskConfig.verifyQuota,
@@ -311,6 +381,17 @@ function deriveParallelScoutPaths(taskConfig) {
311
381
  : allowed,
312
382
  };
313
383
  }
384
+ function deriveFrontendBehaviorPaths(taskConfig) {
385
+ if (taskConfig.allowedPaths.length === 0)
386
+ return ["**"];
387
+ const explicitTestPaths = taskConfig.allowedPaths.filter((entry) => entry.includes("/test/") ||
388
+ entry.includes("/tests/") ||
389
+ entry.includes("/e2e/") ||
390
+ entry.includes("/spec/"));
391
+ if (explicitTestPaths.length > 0)
392
+ return explicitTestPaths;
393
+ return taskConfig.allowedPaths;
394
+ }
314
395
  function toTaskRelativeSourcePath(sources, absolutePath) {
315
396
  return path
316
397
  .relative(sources.taskDir, absolutePath)
@@ -448,6 +529,7 @@ export async function loadTaskHybridSources(repoRoot, taskId) {
448
529
  constraintPath,
449
530
  requirementMarkdown,
450
531
  constraintMarkdown,
532
+ outputLanguage: manifest.workflowPolicy.dag.outputLanguage,
451
533
  referenceDocuments,
452
534
  taskConfig,
453
535
  enabledExecutors: resolveEnabledExecutors(manifest.executors),
@@ -478,8 +560,7 @@ export function buildStandardHybridDagFromTask(sources) {
478
560
  const scoutPaths = deriveParallelScoutPaths(taskConfig);
479
561
  const scoutComplexity = mapTaskComplexity(taskConfig.complexity);
480
562
  const implementComplexity = resolveWriterComplexity(taskConfig);
481
- const writerExecutor = resolveImplementationExecutor(sources);
482
- const implementId = implementationNodeId(writerExecutor);
563
+ const implementId = implementationNodeId();
483
564
  const finalVerifyCommands = sources.verifyCommands?.final ?? [];
484
565
  const verifyShellCommands = buildVerifyShellCommands({
485
566
  repoRoot: sources.repoRoot,
@@ -524,6 +605,7 @@ export function buildStandardHybridDagFromTask(sources) {
524
605
  const spec = {
525
606
  version: 2,
526
607
  title: `Hybrid DAG: ${taskConfig.title}`,
608
+ outputLanguage: sources.outputLanguage ?? DEFAULT_DAG_OUTPUT_LANGUAGE,
527
609
  objective: extractObjective(sources.requirementMarkdown, taskConfig.title),
528
610
  successCriteria: extractSuccessCriteria(sources.requirementMarkdown, sources.taskId),
529
611
  globalConstraints,
@@ -602,7 +684,7 @@ export function buildStandardHybridDagFromTask(sources) {
602
684
  id: implementId,
603
685
  depends_on: ["plan-pi"],
604
686
  role: "implementer",
605
- executor: writerExecutor,
687
+ executor: "pi",
606
688
  toolProfile: "write",
607
689
  complexity: implementComplexity,
608
690
  writePolicy: "exclusive",
@@ -658,7 +740,501 @@ export function buildStandardHybridDagFromTask(sources) {
658
740
  assertValidDagSpec(spec);
659
741
  return spec;
660
742
  }
743
+ function buildFrontendHybridDagFromTask(sources) {
744
+ const { taskConfig } = sources;
745
+ const forbiddenPaths = mergeForbiddenPaths(taskConfig);
746
+ const implementPaths = resolveImplementPaths(taskConfig);
747
+ const implementId = frontendImplementationNodeId();
748
+ const sourceContext = buildSourceContextBlock(sources);
749
+ const strategy = resolveDagVerifyStrategy(taskConfig);
750
+ const readOnlyPaths = taskConfig.allowedPaths.length > 0 ? taskConfig.allowedPaths : ["**"];
751
+ const behaviorPaths = deriveFrontendBehaviorPaths(taskConfig);
752
+ const globalConstraints = [
753
+ ...taskConfig.hardConstraints,
754
+ ...(sources.constraintMarkdown
755
+ ? [`See 执行约束.md in task source (${sources.taskId})`]
756
+ : []),
757
+ ...STANDARD_GLOBAL_CONSTRAINTS,
758
+ "frontend-implementation DAGs must pass the design verdict gate before any write node executes.",
759
+ "frontend-implementation DAGs must complete deterministic static verification and behavior verification before final review.",
760
+ "frontend review must block closeout unless review verdict is exactly VERDICT: pass.",
761
+ ];
762
+ const staticFallbackCommands = ["npm run typecheck", "npm run build"];
763
+ const behaviorFallbackCommands = ["npm test"];
764
+ const parsedFrontendVerifyCommands = extractFrontendVerifyCommandsFromMarkdown({
765
+ repoRoot: sources.repoRoot,
766
+ requirementMarkdown: sources.requirementMarkdown,
767
+ constraintMarkdown: sources.constraintMarkdown,
768
+ });
769
+ const staticVerifyCommands = chooseFrontendVerifyCommands({
770
+ parsedCommands: parsedFrontendVerifyCommands.staticCommands,
771
+ adapterCommands: sources.verifyCommands?.intermediate,
772
+ });
773
+ const behaviorVerifyCommands = chooseFrontendVerifyCommands({
774
+ parsedCommands: parsedFrontendVerifyCommands.behaviorCommands,
775
+ adapterCommands: sources.verifyCommands?.final,
776
+ });
777
+ const spec = {
778
+ version: 2,
779
+ title: `Frontend implementation DAG: ${taskConfig.title}`,
780
+ objective: extractObjective(sources.requirementMarkdown, taskConfig.title),
781
+ successCriteria: extractSuccessCriteria(sources.requirementMarkdown, sources.taskId),
782
+ globalConstraints,
783
+ defaults: {
784
+ ...FRONTEND_DEFAULTS,
785
+ contextProfile: taskConfig.contextProfile,
786
+ },
787
+ skillsByRole: FRONTEND_SKILLS_BY_ROLE,
788
+ executorModels: sources.executorModelMatrix ?? DEFAULT_DAG_EXECUTOR_MODELS,
789
+ verifyStrategy: resolveDagVerifyStrategy(taskConfig),
790
+ tasks: [
791
+ {
792
+ id: "frontend-contract-pi",
793
+ depends_on: [],
794
+ role: "planner",
795
+ executor: "pi",
796
+ complexity: "MED",
797
+ writePolicy: "read-only",
798
+ allowedPaths: readOnlyPaths,
799
+ forbiddenPaths,
800
+ skills: FRONTEND_IMPLEMENTATION_SKILLS,
801
+ outputContract: "Markdown contract with Scope, Non-goals, Acceptance Criteria, UI States, Target Runtime Environment, Risks, and Verification Expectations. No file writes.",
802
+ subtask_prompt: [
803
+ "Read task source and produce a concise frontend implementation contract.",
804
+ "Cover scope, non-goals, acceptance criteria, UI states, target runtime environment, risks, and verification expectations.",
805
+ "Read-only: do not modify code, docs, artifacts, or repository files.",
806
+ sourceContext,
807
+ ].join("\n\n"),
808
+ },
809
+ {
810
+ id: "frontend-scout-pi",
811
+ depends_on: ["frontend-contract-pi"],
812
+ role: "scout",
813
+ executor: "pi",
814
+ complexity: mapTaskComplexity(taskConfig.complexity),
815
+ writePolicy: "read-only",
816
+ allowedPaths: readOnlyPaths,
817
+ forbiddenPaths,
818
+ skills: FRONTEND_IMPLEMENTATION_SKILLS,
819
+ outputContract: "Markdown scout report covering frontend stack, routes, components, styling system, existing design conventions, state/data flow, test entry points, reuse opportunities, and risks. No file writes.",
820
+ subtask_prompt: [
821
+ "Inspect frontend code, routing, components, styles, package scripts, and tests.",
822
+ "Return code and design observations, existing reuse opportunities, and verification entry points.",
823
+ "Read-only: do not modify repository files.",
824
+ sourceContext,
825
+ ].join("\n\n"),
826
+ },
827
+ {
828
+ id: "frontend-plan-pi",
829
+ depends_on: ["frontend-scout-pi"],
830
+ role: "planner",
831
+ executor: "pi",
832
+ complexity: "MED",
833
+ writePolicy: "read-only",
834
+ allowedPaths: readOnlyPaths,
835
+ forbiddenPaths,
836
+ skills: FRONTEND_IMPLEMENTATION_SKILLS,
837
+ outputContract: "Markdown implementation plan with Implementation Steps, Target Files, UI State Handling, Styling / Component Strategy, Interaction Notes, Dependency Policy, Verification Plan, and Residual Risks. No file writes.",
838
+ subtask_prompt: [
839
+ "Based on frontend-contract-pi and frontend-scout-pi, return a minimal frontend implementation plan.",
840
+ "Include ordered steps, target files, UI state handling, styling/component strategy, interaction notes, dependency policy, and deterministic verification commands.",
841
+ "Read-only: do not modify code, docs, artifacts, or repository files.",
842
+ sourceContext,
843
+ ].join("\n\n"),
844
+ },
845
+ {
846
+ id: "frontend-design-gate-pi",
847
+ depends_on: ["frontend-plan-pi"],
848
+ role: "reviewer",
849
+ executor: "pi",
850
+ complexity: "MED",
851
+ writePolicy: "read-only",
852
+ allowedPaths: readOnlyPaths,
853
+ forbiddenPaths,
854
+ skills: FRONTEND_DESIGN_REVIEW_SKILLS,
855
+ outputContract: "Plain Markdown whose first non-empty line is VERDICT: pass or VERDICT: request-revision, followed by Findings, Required Plan Corrections, and Checked Items. No file writes.",
856
+ subtask_prompt: [
857
+ "Audit the frontend plan before implementation.",
858
+ "First non-empty line must be exactly VERDICT: pass or VERDICT: request-revision.",
859
+ "Request revision for missing applicable UI states, unsupported dependency additions, design-system drift without reason, weak interaction coverage, broad scope, or missing deterministic verification commands.",
860
+ "Read-only: do not modify repository files.",
861
+ sourceContext,
862
+ ].join("\n\n"),
863
+ },
864
+ {
865
+ id: "frontend-design-gate-shell",
866
+ depends_on: ["frontend-design-gate-pi"],
867
+ role: "verifier",
868
+ executor: "shell",
869
+ complexity: "LOW",
870
+ writePolicy: "read-only",
871
+ allowedPaths: readOnlyPaths,
872
+ forbiddenPaths,
873
+ outputContract: "Deterministic frontend design verdict gate: exit 0 only when frontend-design-gate-pi emits VERDICT: pass.",
874
+ subtask_prompt: "Deterministic gate: block frontend implementation unless frontend-design-gate-pi emitted VERDICT: pass.",
875
+ shell: {
876
+ commands: [],
877
+ verdictGate: {
878
+ fromNodeId: "frontend-design-gate-pi",
879
+ accept: ["VERDICT: pass"],
880
+ label: "frontend design gate",
881
+ lineMode: "first-verdict-line",
882
+ },
883
+ cwd: ".",
884
+ timeoutMs: 60000,
885
+ },
886
+ },
887
+ {
888
+ id: implementId,
889
+ depends_on: ["frontend-design-gate-shell"],
890
+ role: "implementer",
891
+ executor: "pi",
892
+ toolProfile: "write",
893
+ complexity: taskConfig.complexity === "large"
894
+ ? "HIGH"
895
+ : "MED",
896
+ writePolicy: "exclusive",
897
+ writeSet: implementPaths.writeSet,
898
+ allowedPaths: implementPaths.allowedPaths,
899
+ forbiddenPaths,
900
+ skills: FRONTEND_IMPLEMENTATION_SKILLS,
901
+ outputContract: "Markdown summary with Changed Files, Implemented Behavior, UI States Covered, Styling / Component Notes, Verification Attempted, and Residual Risks.",
902
+ subtask_prompt: [
903
+ "Implement the approved frontend plan with minimal focused changes.",
904
+ "Stay within writeSet and preserve unrelated files. Do not write root artifacts/** unless explicitly included in writeSet.",
905
+ sourceContext,
906
+ ].join("\n\n"),
907
+ },
908
+ {
909
+ id: "frontend-static-verify-shell",
910
+ depends_on: [implementId],
911
+ role: "verifier",
912
+ executor: "shell",
913
+ complexity: "LOW",
914
+ writePolicy: "read-only",
915
+ allowedPaths: readOnlyPaths,
916
+ forbiddenPaths,
917
+ outputContract: "Archived shell stdout/stderr with exit codes for deterministic static verification; no worktree writes.",
918
+ subtask_prompt: "Run deterministic static verification for the frontend implementation.",
919
+ shell: {
920
+ commands: buildVerifyShellCommands({
921
+ repoRoot: sources.repoRoot,
922
+ commands: staticVerifyCommands.commands,
923
+ fallbackCommands: staticFallbackCommands,
924
+ }),
925
+ verifyEvidence: buildVerifyEvidence({
926
+ phase: "intermediate",
927
+ quota: strategy.intermediateQuota ?? "full",
928
+ commandSource: staticVerifyCommands.commandSource,
929
+ commands: staticVerifyCommands.commands,
930
+ fallbackCommands: staticFallbackCommands,
931
+ }),
932
+ cwd: ".",
933
+ timeoutMs: 300000,
934
+ },
935
+ },
936
+ {
937
+ id: "frontend-behavior-verify-shell",
938
+ depends_on: ["frontend-static-verify-shell"],
939
+ role: "verifier",
940
+ executor: "shell",
941
+ complexity: "LOW",
942
+ writePolicy: "read-only",
943
+ allowedPaths: behaviorPaths,
944
+ forbiddenPaths,
945
+ outputContract: "Archived shell stdout/stderr with exit codes for deterministic behavior verification; no worktree writes.",
946
+ subtask_prompt: "Run deterministic behavior verification for frontend flows, states, and integration points.",
947
+ shell: {
948
+ commands: buildVerifyShellCommands({
949
+ repoRoot: sources.repoRoot,
950
+ commands: behaviorVerifyCommands.commands,
951
+ fallbackCommands: behaviorFallbackCommands,
952
+ }),
953
+ verifyEvidence: buildVerifyEvidence({
954
+ phase: "final",
955
+ quota: "full",
956
+ commandSource: behaviorVerifyCommands.commandSource,
957
+ commands: behaviorVerifyCommands.commands,
958
+ fallbackCommands: behaviorFallbackCommands,
959
+ finalFullRequired: true,
960
+ }),
961
+ cwd: ".",
962
+ timeoutMs: 300000,
963
+ },
964
+ },
965
+ {
966
+ id: "frontend-review-pi",
967
+ depends_on: ["frontend-behavior-verify-shell"],
968
+ role: "reviewer",
969
+ executor: "pi",
970
+ complexity: "HIGH",
971
+ writePolicy: "read-only",
972
+ allowedPaths: readOnlyPaths,
973
+ forbiddenPaths,
974
+ skills: FRONTEND_REVIEW_SKILLS,
975
+ outputContract: "Plain Markdown whose first non-empty line is VERDICT: pass or VERDICT: request-revision, followed by Findings, Verification Assessment, UX Assessment, and Residual Risks. No file writes.",
976
+ subtask_prompt: [
977
+ "Review the frontend implementation and verification evidence.",
978
+ "First non-empty line must be exactly VERDICT: pass or VERDICT: request-revision.",
979
+ "Any Critical or Important finding must force VERDICT: request-revision.",
980
+ "Review implementation quality, behavior/state coverage, verification evidence, and maintainability. Read-only: do not modify files.",
981
+ sourceContext,
982
+ ].join("\n\n"),
983
+ },
984
+ {
985
+ id: "frontend-review-gate-shell",
986
+ depends_on: ["frontend-review-pi"],
987
+ role: "verifier",
988
+ executor: "shell",
989
+ complexity: "LOW",
990
+ writePolicy: "read-only",
991
+ allowedPaths: readOnlyPaths,
992
+ forbiddenPaths,
993
+ outputContract: "Deterministic frontend review verdict gate: exit 0 only when frontend-review-pi emits VERDICT: pass.",
994
+ subtask_prompt: "Deterministic gate: block downstream closeout unless frontend-review-pi emitted VERDICT: pass.",
995
+ shell: {
996
+ commands: [],
997
+ verdictGate: {
998
+ fromNodeId: "frontend-review-pi",
999
+ accept: ["VERDICT: pass"],
1000
+ label: "frontend review",
1001
+ lineMode: "first-verdict-line",
1002
+ },
1003
+ cwd: ".",
1004
+ timeoutMs: 60000,
1005
+ },
1006
+ },
1007
+ {
1008
+ id: "frontend-closeout-pi",
1009
+ depends_on: ["frontend-review-gate-shell"],
1010
+ role: "closeout",
1011
+ executor: "pi",
1012
+ complexity: "MED",
1013
+ writePolicy: "read-only",
1014
+ allowedPaths: taskConfig.allowedPaths.length > 0
1015
+ ? [...taskConfig.allowedPaths, "docs/**"]
1016
+ : ["**", "docs/**"],
1017
+ forbiddenPaths,
1018
+ skills: FRONTEND_VERIFICATION_SKILLS,
1019
+ outputContract: "Markdown closeout summary with Changes, Verification Evidence, Review Result, Known Risks, and Follow-up. No file writes.",
1020
+ subtask_prompt: [
1021
+ "Return a frontend closeout summary covering changes, verification evidence, review result, known risks, and follow-up.",
1022
+ "Read-only: do not modify code, docs, artifacts, or .harness/dag-runs/.",
1023
+ sourceContext,
1024
+ ].join("\n\n"),
1025
+ },
1026
+ ],
1027
+ };
1028
+ parseDagSpec(spec);
1029
+ assertValidDagSpec(spec);
1030
+ return spec;
1031
+ }
1032
+ // ---------------------------------------------------------------------------
1033
+ // Backend test DAG template
1034
+ // ---------------------------------------------------------------------------
1035
+ function buildAnalyzeInputsNode(sources) {
1036
+ return {
1037
+ id: "analyze-inputs-pi",
1038
+ depends_on: [],
1039
+ role: "planner",
1040
+ executor: "pi",
1041
+ complexity: "MED",
1042
+ writePolicy: "read-only",
1043
+ allowedPaths: commonReadOnlyPaths(sources),
1044
+ forbiddenPaths: commonForbiddenPaths(sources),
1045
+ outputContract: "Plain Markdown end-to-end test analysis contract (scope, risks, strategy highlights); no file writes.",
1046
+ subtask_prompt: [
1047
+ "Read the task source materials (需求.md, 开发详设.md and other references) and produce a concise end-to-end test analysis contract.",
1048
+ "Cover: backend test scope, risk items, and strategy highlights. Identify key modules, integration points, and boundary conditions.",
1049
+ "Read-only: do not modify code, docs, artifacts, or repository files.",
1050
+ buildSourceContextBlock(sources),
1051
+ ].join("\n\n"),
1052
+ };
1053
+ }
1054
+ function buildGenerateBackendFunctionalCasesNode(sources) {
1055
+ return {
1056
+ id: "generate-backend-functional-cases-pi",
1057
+ depends_on: ["analyze-inputs-pi"],
1058
+ role: "implementer",
1059
+ executor: "pi",
1060
+ toolProfile: "write",
1061
+ complexity: "MED",
1062
+ writePolicy: "exclusive",
1063
+ writeSet: ["testcase/md/**"],
1064
+ allowedPaths: ["testcase/md/**"],
1065
+ forbiddenPaths: commonForbiddenPaths(sources),
1066
+ subtask_prompt: [
1067
+ "Based on the upstream test analysis contract, generate structured backend functional test cases in Markdown.",
1068
+ "Each test case ID must use the BE-<MODULE>-<NNN> format (e.g. BE-ORDER-001).",
1069
+ "Write test case files under testcase/md/. Cover positive paths, negative paths, and boundary conditions.",
1070
+ "Stay within writeSet. Do not write root artifacts/**.",
1071
+ buildSourceContextBlock(sources),
1072
+ ].join("\n\n"),
1073
+ };
1074
+ }
1075
+ function buildReviewBackendCasesNode(sources) {
1076
+ return {
1077
+ id: "review-backend-cases-pi",
1078
+ depends_on: ["generate-backend-functional-cases-pi"],
1079
+ role: "reviewer",
1080
+ executor: "pi",
1081
+ complexity: "HIGH",
1082
+ writePolicy: "read-only",
1083
+ allowedPaths: commonReadOnlyPaths(sources),
1084
+ forbiddenPaths: commonForbiddenPaths(sources),
1085
+ outputContract: "Plain Markdown whose first non-empty line is VERDICT: pass or VERDICT: request-revision; followed by Findings and Coverage Assessment. No file writes.",
1086
+ subtask_prompt: [
1087
+ "Review the generated backend functional test cases for completeness and quality.",
1088
+ "First non-empty line must be exactly VERDICT: pass or VERDICT: request-revision.",
1089
+ "Check: coverage of requirement acceptance criteria, case ID format (BE-<MODULE>-<NNN>), positive/negative/boundary coverage, and traceability to source requirements.",
1090
+ "Any Critical/Important finding must force VERDICT: request-revision. Read-only: do not modify files.",
1091
+ buildSourceContextBlock(sources),
1092
+ ].join("\n\n"),
1093
+ };
1094
+ }
1095
+ function buildGenerateBackendPytestNode(sources) {
1096
+ return {
1097
+ id: "generate-backend-pytest-pi",
1098
+ depends_on: ["review-backend-cases-pi"],
1099
+ role: "implementer",
1100
+ executor: "pi",
1101
+ toolProfile: "write",
1102
+ complexity: "HIGH",
1103
+ writePolicy: "exclusive",
1104
+ writeSet: ["testcase/**"],
1105
+ allowedPaths: ["testcase/**"],
1106
+ forbiddenPaths: commonForbiddenPaths(sources),
1107
+ subtask_prompt: [
1108
+ "Convert the reviewed backend functional test cases into pytest automation code.",
1109
+ "Write test files under testcase/. Each functional test case ID (BE-<MODULE>-<NNN>) must map 1:1 to an automated pytest function for traceability.",
1110
+ "Every generated test file must start with test_ prefix (e.g. test_order.py) to comply with pytest discovery.",
1111
+ "Identify the target project's pytest conventions (conftest.py, fixture patterns, pytest.ini/pyproject.toml config) by reading existing files, but do NOT modify any existing framework files.",
1112
+ "Only create NEW test script files. Do NOT modify conftest.py, pytest.ini, pyproject.toml, setup.cfg, __init__.py, or any other existing file.",
1113
+ "If a file with the target name already exists under testcase/, add a numeric suffix: test_order.py → test_order_01.py → test_order_02.py. Never overwrite or append to existing files. Each test script must be a standalone file.",
1114
+ "Stay within writeSet. Do not write root artifacts/**.",
1115
+ buildSourceContextBlock(sources),
1116
+ ].join("\n\n"),
1117
+ };
1118
+ }
1119
+ function buildExecuteBackendPytestNode(sources) {
1120
+ return {
1121
+ id: "execute-backend-pytest-shell",
1122
+ depends_on: ["generate-backend-pytest-pi"],
1123
+ role: "verifier",
1124
+ executor: "shell",
1125
+ complexity: "LOW",
1126
+ writePolicy: "read-only",
1127
+ allowedPaths: commonReadOnlyPaths(sources),
1128
+ forbiddenPaths: commonForbiddenPaths(sources),
1129
+ outputContract: "Archived pytest stdout/stderr with exit codes and HTML report path; no worktree writes.",
1130
+ subtask_prompt: "Run pytest for the backend test suite and capture results.",
1131
+ shell: {
1132
+ commands: [
1133
+ "python -m pytest testcase/ --html=reports/backend-test-report.html -v",
1134
+ ],
1135
+ verifyEvidence: buildVerifyEvidence({
1136
+ phase: "final",
1137
+ quota: "full",
1138
+ commandSource: "inline",
1139
+ fallbackCommands: [
1140
+ "python -m pytest testcase/ --html=reports/backend-test-report.html -v",
1141
+ ],
1142
+ finalFullRequired: true,
1143
+ }),
1144
+ cwd: ".",
1145
+ timeoutMs: 300000,
1146
+ },
1147
+ };
1148
+ }
1149
+ function buildTestRetrospectNode(sources) {
1150
+ return {
1151
+ id: "test-retrospect-pi",
1152
+ depends_on: ["execute-backend-pytest-shell"],
1153
+ role: "closeout",
1154
+ executor: "pi",
1155
+ toolProfile: "write",
1156
+ complexity: "MED",
1157
+ writePolicy: "exclusive",
1158
+ writeSet: ["docs/test-reports/**"],
1159
+ allowedPaths: ["docs/test-reports/**"],
1160
+ forbiddenPaths: commonForbiddenPaths(sources),
1161
+ subtask_prompt: [
1162
+ "Read upstream review-backend-cases-pi review report and execute-backend-pytest-shell pytest output, then generate a test retrospective report.",
1163
+ "Write the report under docs/test-reports/ in Markdown. The report must include:",
1164
+ "1) Test coverage summary (total cases, pass rate, failed case analysis)",
1165
+ "2) Review findings and their resolution status",
1166
+ "3) Maturity rating: A (100% coverage + 100% pass + no Critical findings), B (≥80% coverage + ≥90% pass + Low findings only), C (≥60% coverage + ≥70% pass), D (below C thresholds)",
1167
+ "Stay within writeSet. Do not write root artifacts/**.",
1168
+ buildSourceContextBlock(sources),
1169
+ ].join("\n\n"),
1170
+ };
1171
+ }
1172
+ const BACKEND_TEST_DEFAULTS = {
1173
+ ...HYBRID_DEFAULTS,
1174
+ writePolicy: "read-only",
1175
+ };
1176
+ const BACKEND_TEST_SKILLS_BY_ROLE = {
1177
+ planner: ["loop-agent"],
1178
+ scout: [],
1179
+ implementer: ["test-driven-development", "verification-before-completion"],
1180
+ reviewer: ["requesting-code-review", "code-review-core"],
1181
+ verifier: ["verification-before-completion", "systematic-debugging"],
1182
+ closeout: ["loop-agent", "verification-before-completion"],
1183
+ };
1184
+ function buildBackendTestHybridDag(sources) {
1185
+ const { taskConfig } = sources;
1186
+ const sourceContext = buildSourceContextBlock(sources);
1187
+ const readOnlyPaths = commonReadOnlyPaths(sources);
1188
+ const forbiddenPaths = commonForbiddenPaths(sources);
1189
+ const globalConstraints = [
1190
+ ...taskConfig.hardConstraints,
1191
+ ...(sources.constraintMarkdown
1192
+ ? [`See 执行约束.md in task source (${sources.taskId})`]
1193
+ : []),
1194
+ ...STANDARD_GLOBAL_CONSTRAINTS,
1195
+ "backend-test-dag nodes must maintain traceability from requirements to functional cases to pytest automation.",
1196
+ "Functional test case IDs must use BE-<MODULE>-<NNN> format.",
1197
+ "pytest execution must produce HTML reports under reports/.",
1198
+ "pytest automation scripts must use test_ filename prefix for pytest discovery.",
1199
+ "generate-backend-pytest-pi must only create new test files under testcase/; modifying existing framework files (conftest.py, pytest.ini, pyproject.toml) is forbidden.",
1200
+ "If a target test filename already exists under testcase/, add a numeric suffix (_01, _02, ...); never overwrite or append to existing files.",
1201
+ "execute-backend-pytest-shell must not modify test assertions or production code to make tests pass; test failures indicate potential implementation issues and must be reported honestly.",
1202
+ ];
1203
+ const spec = {
1204
+ version: 2,
1205
+ title: `Backend test DAG: ${taskConfig.title}`,
1206
+ outputLanguage: sources.outputLanguage ?? DEFAULT_DAG_OUTPUT_LANGUAGE,
1207
+ objective: extractObjective(sources.requirementMarkdown, taskConfig.title),
1208
+ successCriteria: extractSuccessCriteria(sources.requirementMarkdown, sources.taskId),
1209
+ globalConstraints,
1210
+ defaults: {
1211
+ ...BACKEND_TEST_DEFAULTS,
1212
+ contextProfile: taskConfig.contextProfile,
1213
+ },
1214
+ skillsByRole: BACKEND_TEST_SKILLS_BY_ROLE,
1215
+ executorModels: sources.executorModelMatrix ?? DEFAULT_DAG_EXECUTOR_MODELS,
1216
+ tasks: [
1217
+ buildAnalyzeInputsNode(sources),
1218
+ buildGenerateBackendFunctionalCasesNode(sources),
1219
+ buildReviewBackendCasesNode(sources),
1220
+ buildGenerateBackendPytestNode(sources),
1221
+ buildExecuteBackendPytestNode(sources),
1222
+ buildTestRetrospectNode(sources),
1223
+ ],
1224
+ };
1225
+ parseDagSpec(spec);
1226
+ assertValidDagSpec(spec);
1227
+ return spec;
1228
+ }
661
1229
  export function buildHybridDagFromTask(sources, options = {}) {
1230
+ if (sources.taskConfig.taskKind === "frontend-implementation" ||
1231
+ options.template === "frontend-implementation") {
1232
+ return buildFrontendHybridDagFromTask(sources);
1233
+ }
1234
+ if (sources.taskConfig.taskKind === "backend-test" ||
1235
+ options.template === "backend-test-dag") {
1236
+ return buildBackendTestHybridDag(sources);
1237
+ }
662
1238
  const standard = buildStandardHybridDagFromTask(sources);
663
1239
  const template = options.template ?? "standard-dag";
664
1240
  if (template === "standard-dag")
@@ -805,7 +1381,7 @@ function buildWriteSetGateNode(sources) {
805
1381
  };
806
1382
  }
807
1383
  function buildSoftVerifyNode(sources) {
808
- const implementId = implementationNodeId(resolveImplementationExecutor(sources));
1384
+ const implementId = implementationNodeId();
809
1385
  const strategy = resolveDagVerifyStrategy(sources.taskConfig);
810
1386
  const fallbackCommands = ["npm run typecheck"];
811
1387
  const commands = buildVerifyShellCommands({
@@ -839,8 +1415,8 @@ function buildSoftVerifyNode(sources) {
839
1415
  };
840
1416
  }
841
1417
  function buildProcessSupervisorNode(sources) {
842
- const implementId = implementationNodeId(resolveImplementationExecutor(sources));
843
- const repairId = repairNodeId(resolveImplementationExecutor(sources));
1418
+ const implementId = implementationNodeId();
1419
+ const repairId = repairNodeId();
844
1420
  return {
845
1421
  id: "process-supervisor-pi",
846
1422
  depends_on: ["soft-verify-shell", implementId],
@@ -891,13 +1467,12 @@ function buildProcessGateNode(sources) {
891
1467
  };
892
1468
  }
893
1469
  function buildRepairNode(sources) {
894
- const writerExecutor = resolveImplementationExecutor(sources);
895
1470
  const implement = resolveImplementPaths(sources.taskConfig);
896
1471
  return {
897
- id: repairNodeId(writerExecutor),
1472
+ id: repairNodeId(),
898
1473
  depends_on: ["process-gate-shell", "process-supervisor-pi"],
899
1474
  role: "implementer",
900
- executor: writerExecutor,
1475
+ executor: "pi",
901
1476
  toolProfile: "write",
902
1477
  complexity: isInteractiveUiTask(sources.taskConfig)
903
1478
  ? "HIGH"
@@ -919,7 +1494,7 @@ function buildRepairNode(sources) {
919
1494
  };
920
1495
  }
921
1496
  function buildHardVerifyNode(sources) {
922
- const repairId = repairNodeId(resolveImplementationExecutor(sources));
1497
+ const repairId = repairNodeId();
923
1498
  const fallbackCommands = [
924
1499
  "HARNESS_ALLOW_ACTIVE_DAG_RUNS=1 bash scripts/check-repo.sh",
925
1500
  ];
@@ -978,7 +1553,7 @@ function buildSupervisedHybridDag(standard, sources) {
978
1553
  const scoutSrc = getTaskOrThrow(standard, "scout-src");
979
1554
  const scoutTests = getTaskOrThrow(standard, "scout-tests");
980
1555
  const plan = getTaskOrThrow(standard, "plan-pi");
981
- const implement = getTaskOrThrow(standard, implementationNodeId(resolveImplementationExecutor(sources)));
1556
+ const implement = getTaskOrThrow(standard, implementationNodeId());
982
1557
  const closeout = getTaskOrThrow(standard, "closeout-pi");
983
1558
  const authorityDecision = resolveAuthoritySurfaceAudit(sources);
984
1559
  const reviewDependsOn = authorityDecision.enabled