@tea-agent/loop-agent 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +10 -2
- package/CHANGELOG.md +91 -24
- package/README.md +84 -12
- package/dist/application/dag/args.js +1 -12
- package/dist/application/dag/generate-task-dag.js +38 -2
- package/dist/application/dag/run-dag.js +11 -27
- package/dist/application/dag/validate-dag.js +13 -2
- package/dist/application/loop/run-action.js +0 -4
- package/dist/cli/command-definitions.js +44 -16
- package/dist/cli/program.js +40 -23
- package/dist/cli/update/notifier.js +117 -0
- package/dist/cli/update/npm-client.js +151 -0
- package/dist/cli/update/policy.js +58 -0
- package/dist/cli/update/state.js +68 -0
- package/dist/cli.js +33 -0
- package/dist/commands/cursor-prompt.js +42 -82
- package/dist/commands/dag-approve.js +36 -0
- package/dist/commands/delegate.js +75 -77
- package/dist/commands/doctor.js +0 -18
- package/dist/commands/init.js +547 -95
- package/dist/commands/instructions.js +7 -10
- package/dist/commands/loop.js +4 -20
- package/dist/commands/plan.js +50 -0
- package/dist/executors/config-core.js +0 -51
- package/dist/executors/dag-pi-executor.js +1 -1
- package/dist/executors/dag.js +0 -1
- package/dist/executors/index.js +0 -2
- package/dist/executors/model-routing.js +9 -9
- package/dist/executors/shell-executor.js +1 -1
- package/dist/governance/checks.js +6 -3
- package/dist/governance/exec-plans.js +545 -0
- package/dist/governance/manifest-types.js +24 -2
- package/dist/infrastructure/harness/loop-action-store.js +0 -3
- package/dist/records/harvest.js +2 -23
- package/dist/records/one-shot-runs.js +1 -1
- package/dist/shared/artifacts-core.js +24 -5
- package/dist/shared/output-truncation.js +37 -0
- package/dist/shared/package-metadata.js +488 -0
- package/dist/{executors/cursor-executor.js → sidecars/cursor-prompt/executor.js} +2 -42
- package/dist/sidecars/cursor-prompt/index.js +3 -0
- package/dist/sidecars/cursor-prompt/stream.js +121 -0
- package/dist/task/config-types.js +29 -12
- package/dist/task/delegate.js +9 -21
- package/dist/task/runtime.js +1 -2
- package/dist/worker/cli.js +32 -3
- package/dist/worker/delivery/final-verification.js +47 -11
- package/dist/worker/delivery/package.js +63 -10
- package/dist/worker/feature/run.js +60 -8
- package/dist/worker/loop-agent/loop-agent-client.js +329 -126
- package/dist/worker/observability/event-history.js +216 -0
- package/dist/worker/observability/read-model.js +338 -83
- package/dist/worker/observe/paths.js +17 -0
- package/dist/worker/observe/routes.js +165 -21
- package/dist/worker/observe/server.js +59 -1
- package/dist/worker/observe/static/api.js +27 -0
- package/dist/worker/observe/static/app.js +120 -2317
- package/dist/worker/observe/static/constants.js +148 -0
- package/dist/worker/observe/static/copy.js +67 -0
- package/dist/worker/observe/static/dag-helpers.js +172 -0
- package/dist/worker/observe/static/dag-model.js +72 -0
- package/dist/worker/observe/static/dom.js +61 -0
- package/dist/worker/observe/static/format-pool.js +67 -0
- package/dist/worker/observe/static/format.js +292 -0
- package/dist/worker/observe/static/index.html +300 -82
- package/dist/worker/observe/static/kpi.js +94 -0
- package/dist/worker/observe/static/relations.js +128 -0
- package/dist/worker/observe/static/router.js +85 -0
- package/dist/worker/observe/static/run-processing.js +148 -0
- package/dist/worker/observe/static/shell-chrome.js +68 -0
- package/dist/worker/observe/static/state.js +253 -0
- package/dist/worker/observe/static/styles.css +1720 -495
- package/dist/worker/observe/static/views/batch.js +226 -0
- package/dist/worker/observe/static/views/dag-graph.js +172 -0
- package/dist/worker/observe/static/views/dag-inspector.js +477 -0
- package/dist/worker/observe/static/views/dag.js +362 -0
- package/dist/worker/observe/static/views/dashboard.js +442 -0
- package/dist/worker/observe/static/views/failures.js +143 -0
- package/dist/worker/observe/static/views/feature.js +453 -0
- package/dist/worker/observe/static/views/pool.js +347 -0
- package/dist/worker/observe/static/views/run.js +453 -0
- package/dist/worker/observe/static/views/session-timeline.js +205 -0
- package/dist/worker/observe/static/views/shell.js +7 -0
- package/dist/worker/observe/static/views/task.js +260 -0
- package/dist/worker/observe/static/views/timeline.js +163 -0
- package/dist/worker/preflight.js +49 -1
- package/dist/worker/run-task/run-task.js +22 -12
- package/dist/worker/runner/run-ready.js +76 -12
- package/dist/worker/task-spec/schema.js +0 -1
- package/dist/workflows/dag/controller-identity.js +104 -0
- package/dist/workflows/dag/convergence/controller.js +1 -1
- package/dist/workflows/dag/executor-registry.js +0 -2
- package/dist/workflows/dag/init-hybrid.js +797 -27
- package/dist/workflows/dag/node-execution.js +183 -35
- package/dist/workflows/dag/repair-artifact.js +91 -0
- package/dist/workflows/dag/report.js +50 -0
- package/dist/workflows/dag/retry-policy.js +138 -0
- package/dist/workflows/dag/runner.js +77 -17
- package/dist/workflows/dag/runtime-contract.js +87 -0
- package/dist/workflows/dag/scheduler.js +7 -2
- package/dist/workflows/dag/sdd-embedded.js +128 -0
- package/dist/workflows/dag/skill-instructions.js +5 -4
- package/dist/workflows/dag/skill-snapshot.js +529 -0
- package/dist/workflows/dag/types.js +86 -10
- package/dist/workflows/dag/validate.js +73 -12
- package/dist/workflows/loop/actions/dag-action.js +0 -2
- package/dist/workflows/loop/actions/shared.js +1 -1
- package/dist/workflows/loop/actions.js +14 -31
- package/dist/workflows/loop/benchmark.js +1 -1
- package/dist/workflows/loop/index.js +1 -1
- package/dist/workflows/loop/policy/auto-policy.js +22 -14
- package/dist/workflows/loop/policy/path-patterns.js +13 -0
- package/docs/README.md +36 -33
- package/docs/agent-dag-recovery-playbook.md +1 -1
- package/docs/agent-dag-runner.md +28 -3
- package/docs/architecture/README.md +26 -0
- package/docs/architecture/dag-execution.md +140 -0
- package/docs/architecture/evolution.md +53 -0
- package/docs/architecture/facts-and-state.md +58 -0
- package/docs/architecture/runtime-boundaries.md +45 -17
- package/docs/architecture/system-overview.md +93 -0
- package/docs/architecture/worker-and-feature.md +81 -0
- package/docs/cursor-prompt-sidecar.md +36 -0
- package/docs/decisions/README.md +13 -1
- package/docs/design/README.md +43 -21
- package/docs/development-principles.md +2 -2
- package/docs/exec-plans/active/README.md +1 -3
- package/docs/exec-plans/completed/README.md +23 -0
- package/docs/feature-workflow.md +78 -4
- package/docs/harness-methodology-debugging.md +1 -1
- package/docs/harness-methodology-tdd.md +3 -3
- package/docs/init-surface.manifest.json +60 -25
- package/docs/loop-agent-harness.md +28 -4
- package/docs/progress/README.md +50 -1
- package/docs/reports/README.md +90 -18
- package/docs/skills/README.md +2 -1
- package/docs/skills/vetted-skill-registry.md +2 -1
- package/docs/templates/agent-dag-report.schema.json +23 -6
- package/docs/templates/agent-dag.base.json +0 -5
- package/docs/templates/agent-dag.final-verification.json +0 -5
- package/docs/templates/agent-dag.schema.json +70 -3
- package/docs/templates/agent-dag.supervised-implementation.json +9 -8
- package/docs/templates/backend-test-dag.generate-pytest.prompt.md +139 -0
- package/docs/templates/backend-test-dag.json +276 -0
- package/docs/templates/backend-test-dag.retrospect.prompt.md +125 -0
- package/docs/templates/backend-test-dag.review-cases.prompt.md +81 -0
- package/docs/templates/frontend-design-contract.md +33 -0
- package/docs/templates/frontend-task-constraints.md +25 -0
- package/docs/templates/frontend-task-requirement.md +61 -0
- package/docs/templates/harness.schema.json +10 -12
- package/docs/templates/hybrid-dag.json +1 -6
- package/docs/templates/interactive-ui-round2-experiment.md +1 -1
- package/docs/templates/product-line/task.yaml +0 -1
- package/docs/templates/project-start-checklist.md +2 -2
- package/docs/templates/worker-dogfood-evidence.md +28 -0
- package/docs/templates/worker-dogfood-setup.md +20 -0
- package/docs/verification-matrix.md +10 -0
- package/examples/decision-gate-agent-dag.json +87 -33
- package/examples/example-dag.json +0 -5
- package/examples/hybrid-loop-agent-dag.json +0 -5
- package/harness.json +7 -15
- package/package.json +22 -46
- package/scripts/check-product-line-docs.sh +10 -7
- package/skills/agent-worker/SKILL.md +37 -0
- package/skills/agent-worker/references/agent-worker-operator.md +43 -0
- package/skills/frontend-design-review/SKILL.md +59 -0
- package/skills/frontend-design-review/references/review-checklist.md +37 -0
- package/skills/frontend-implementation/SKILL.md +51 -0
- package/skills/frontend-implementation/references/code-standards.md +34 -0
- package/skills/frontend-implementation/references/design-spec.md +46 -0
- package/skills/frontend-implementation/references/node-contracts.md +32 -0
- package/skills/frontend-review/SKILL.md +53 -0
- package/skills/frontend-review/references/review-findings.md +42 -0
- package/skills/frontend-verification/SKILL.md +40 -0
- package/skills/frontend-verification/references/verification-checklist.md +56 -0
- package/skills/grill-me/SKILL.md +10 -0
- package/skills/grill-with-docs/SKILL.md +88 -0
- package/skills/grill-with-docs/adr-format.md +47 -0
- package/skills/grill-with-docs/context-format.md +60 -0
- package/skills/loop-agent/SKILL.md +11 -9
- package/skills/loop-agent/references/command-reference.md +14 -15
- package/skills/loop-agent/references/docs-converge.md +126 -0
- package/skills/loop-agent/references/harness-policy.md +7 -7
- package/skills/loop-agent/references/hybrid-dag.md +36 -20
- package/skills/loop-agent/references/long-running-loop.md +4 -6
- package/skills/loop-agent/references/multi-worktree.md +6 -6
- package/skills/loop-agent/references/orchestrator-and-interventions.md +3 -3
- package/skills/loop-agent/references/pi-subagent-assisted-mode.md +14 -11
- package/skills/loop-agent/references/task-workflow.md +1 -1
- package/skills/loop-agent/references/verification-and-failure-handling.md +6 -0
- package/skills/using-git-worktrees/SKILL.md +215 -0
- package/dist/commands/cursor-worker.js +0 -43
- package/dist/cursor-worker-entry.js +0 -8
- package/dist/executors/cursor-artifacts.js +0 -33
- package/dist/executors/cursor-execution-log.js +0 -81
- package/dist/executors/cursor-executor-artifacts.js +0 -134
- package/dist/executors/cursor-run.js +0 -115
- package/dist/executors/cursor-tool.js +0 -94
- package/dist/executors/cursor-worker-client.js +0 -223
- package/dist/executors/cursor-worker-protocol.js +0 -18
- package/dist/executors/cursor-worker-server.js +0 -54
- package/dist/executors/cursor-worker.js +0 -3
- package/dist/executors/cursor.js +0 -6
- package/dist/executors/dag-cursor-executor.js +0 -87
- package/dist/workflows/loop/actions/cursor-fix.js +0 -191
- package/dist/workflows/loop/policy/cursor-fix-policy.js +0 -31
- package/docs/cursor-executor-usage.md +0 -25
- package/docs/dynamic-workflow-dag-engine-roadmap.md +0 -1749
|
@@ -2,12 +2,14 @@ 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_OUTPUT_LANGUAGE, DEFAULT_DAG_EXECUTOR_MODELS, parseDagSpec, } from "./types.js";
|
|
5
|
+
import { DAG_AGENT_RUNTIME_PI_ONLY, DAG_REPAIR_WRITER_PROTOCOL_EXPLICIT_NODE_V1, DAG_RUNTIME_CONTRACT_SCHEMA_VERSION, 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
|
+
import { DEFAULT_READ_ONLY_PI_RETRY_POLICY, isSafeReadOnlyPiRetryCandidate } from "./retry-policy.js";
|
|
8
9
|
import { resolveAdapter } from "../../adapters/index.js";
|
|
9
10
|
import { loadHarnessManifest } from "../../governance/harness.js";
|
|
10
11
|
import { buildAuthoritySurfaceAuditNode, buildAuthoritySurfaceGateNode, resolveAuthoritySurfaceAudit, } from "./authority-surface.js";
|
|
12
|
+
import { applySddEmbeddedEnhancements, probeRepoLocalSddSkills, } from "./sdd-embedded.js";
|
|
11
13
|
import { getTaskPaths, loadTaskConfig } from "../../task/runtime.js";
|
|
12
14
|
import { materializeTaskReferenceDocs } from "../../task/source-references.js";
|
|
13
15
|
import { resolveVerifyPreset } from "../../executors/shell-verification.js";
|
|
@@ -58,27 +60,50 @@ const HYBRID_SKILLS_BY_ROLE = {
|
|
|
58
60
|
verifier: ["verification-before-completion", "systematic-debugging"],
|
|
59
61
|
closeout: ["loop-agent", "verification-before-completion"],
|
|
60
62
|
};
|
|
63
|
+
const FRONTEND_DEFAULTS = {
|
|
64
|
+
...HYBRID_DEFAULTS,
|
|
65
|
+
skills: [],
|
|
66
|
+
};
|
|
67
|
+
const FRONTEND_SKILLS_BY_ROLE = {
|
|
68
|
+
planner: [],
|
|
69
|
+
scout: [],
|
|
70
|
+
implementer: [],
|
|
71
|
+
reviewer: [],
|
|
72
|
+
supervisor: [],
|
|
73
|
+
verifier: [],
|
|
74
|
+
closeout: [],
|
|
75
|
+
};
|
|
76
|
+
const FRONTEND_IMPLEMENTATION_SKILLS = ["frontend-implementation"];
|
|
77
|
+
const FRONTEND_DESIGN_REVIEW_SKILLS = ["frontend-design-review"];
|
|
78
|
+
const FRONTEND_REVIEW_SKILLS = ["frontend-review"];
|
|
79
|
+
const FRONTEND_VERIFICATION_SKILLS = ["frontend-verification"];
|
|
61
80
|
const IMPLEMENT_WRITESET_PLACEHOLDER = "REPLACE/WITH/NARROW/IMPLEMENT/PATHS/**";
|
|
81
|
+
/**
|
|
82
|
+
* Runtime contract stamped on every newly generated DAG. Capability fields are
|
|
83
|
+
* authoritative and are the primary compatibility gate. `minimumControllerVersion`
|
|
84
|
+
* is intentionally omitted here: it is an optional diagnostic hint, and hardcoding
|
|
85
|
+
* a future version would make a controller reject the very DAGs it generates.
|
|
86
|
+
* Operators can still pin a minimum version on hand-authored specs when needed.
|
|
87
|
+
*/
|
|
88
|
+
export const GENERATED_DAG_RUNTIME_CONTRACT = {
|
|
89
|
+
schemaVersion: DAG_RUNTIME_CONTRACT_SCHEMA_VERSION,
|
|
90
|
+
agentRuntime: DAG_AGENT_RUNTIME_PI_ONLY,
|
|
91
|
+
repairWriterProtocol: DAG_REPAIR_WRITER_PROTOCOL_EXPLICIT_NODE_V1,
|
|
92
|
+
};
|
|
62
93
|
function resolveEnabledExecutors(manifestExecutors) {
|
|
63
94
|
return Object.entries(manifestExecutors)
|
|
64
95
|
.filter(([, config]) => config.enabled !== false)
|
|
65
|
-
.map(([name]) => name)
|
|
96
|
+
.map(([name]) => name)
|
|
97
|
+
.filter((name) => name === "pi");
|
|
66
98
|
}
|
|
67
|
-
function
|
|
68
|
-
|
|
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";
|
|
99
|
+
function implementationNodeId() {
|
|
100
|
+
return "implement-pi";
|
|
76
101
|
}
|
|
77
|
-
function
|
|
78
|
-
return
|
|
102
|
+
function repairNodeId() {
|
|
103
|
+
return "repair-pi";
|
|
79
104
|
}
|
|
80
|
-
function
|
|
81
|
-
return
|
|
105
|
+
function frontendImplementationNodeId() {
|
|
106
|
+
return "frontend-implement-pi";
|
|
82
107
|
}
|
|
83
108
|
function probePathsForWriteSetEntry(entry) {
|
|
84
109
|
const normalized = entry.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
@@ -275,6 +300,65 @@ function buildVerifyShellCommands(input) {
|
|
|
275
300
|
const repoRoot = input.repoRoot;
|
|
276
301
|
return input.commands.map((command) => verifyCommandToShell(repoRoot, command));
|
|
277
302
|
}
|
|
303
|
+
function markdownVerifyCommand(repoRoot, command) {
|
|
304
|
+
const args = command.split(/\s+/).filter(Boolean);
|
|
305
|
+
if (args.length === 0 ||
|
|
306
|
+
args.some((arg) => !/^[a-z0-9@._/:=,+-]+$/i.test(arg))) {
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
args,
|
|
311
|
+
cwd: repoRoot,
|
|
312
|
+
label: command,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
function extractFrontendVerifyCommandsFromMarkdown(input) {
|
|
316
|
+
if (!input.repoRoot)
|
|
317
|
+
return { staticCommands: [], behaviorCommands: [] };
|
|
318
|
+
const markdown = [
|
|
319
|
+
input.requirementMarkdown,
|
|
320
|
+
input.constraintMarkdown ?? "",
|
|
321
|
+
].join("\n");
|
|
322
|
+
const commands = new Set();
|
|
323
|
+
for (const line of markdown.split(/\r?\n/)) {
|
|
324
|
+
const bulletless = line
|
|
325
|
+
.trim()
|
|
326
|
+
.replace(/^[-*]\s*(?:\[[ xX]\]\s*)?/, "")
|
|
327
|
+
.trim();
|
|
328
|
+
const codeSpanCommands = Array.from(bulletless.matchAll(/`([^`]+)`/g), (match) => match[1].trim());
|
|
329
|
+
const candidates = codeSpanCommands.length > 0 ? codeSpanCommands : [bulletless];
|
|
330
|
+
for (const candidate of candidates) {
|
|
331
|
+
if (/^(npm|pnpm|yarn|bun)\s+(run\s+)?[a-z0-9:_-]+(?:\s.*)?$/i.test(candidate) ||
|
|
332
|
+
/^(npx|pnpm\s+exec|yarn\s+exec|bunx)\s+(vitest|jest|playwright|cypress|tsc|eslint)(?:\s.*)?$/i.test(candidate)) {
|
|
333
|
+
commands.add(candidate);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const staticCommands = [];
|
|
338
|
+
const behaviorCommands = [];
|
|
339
|
+
for (const command of commands) {
|
|
340
|
+
const verifyCommand = markdownVerifyCommand(input.repoRoot, command);
|
|
341
|
+
if (!verifyCommand)
|
|
342
|
+
continue;
|
|
343
|
+
if (/\b(typecheck|lint|eslint|tsc|build)\b/i.test(command)) {
|
|
344
|
+
staticCommands.push(verifyCommand);
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
if (/\b(test|vitest|jest|playwright|cypress|e2e)\b/i.test(command)) {
|
|
348
|
+
behaviorCommands.push(verifyCommand);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return { staticCommands, behaviorCommands };
|
|
352
|
+
}
|
|
353
|
+
function chooseFrontendVerifyCommands(input) {
|
|
354
|
+
if (input.parsedCommands.length > 0) {
|
|
355
|
+
return { commands: input.parsedCommands, commandSource: "inline" };
|
|
356
|
+
}
|
|
357
|
+
if (input.adapterCommands && input.adapterCommands.length > 0) {
|
|
358
|
+
return { commands: input.adapterCommands, commandSource: "adapter" };
|
|
359
|
+
}
|
|
360
|
+
return { commandSource: "inline" };
|
|
361
|
+
}
|
|
278
362
|
function resolveDagVerifyStrategy(taskConfig) {
|
|
279
363
|
return {
|
|
280
364
|
intermediateQuota: taskConfig.dagVerifyStrategy?.intermediateQuota ?? taskConfig.verifyQuota,
|
|
@@ -311,6 +395,17 @@ function deriveParallelScoutPaths(taskConfig) {
|
|
|
311
395
|
: allowed,
|
|
312
396
|
};
|
|
313
397
|
}
|
|
398
|
+
function deriveFrontendBehaviorPaths(taskConfig) {
|
|
399
|
+
if (taskConfig.allowedPaths.length === 0)
|
|
400
|
+
return ["**"];
|
|
401
|
+
const explicitTestPaths = taskConfig.allowedPaths.filter((entry) => entry.includes("/test/") ||
|
|
402
|
+
entry.includes("/tests/") ||
|
|
403
|
+
entry.includes("/e2e/") ||
|
|
404
|
+
entry.includes("/spec/"));
|
|
405
|
+
if (explicitTestPaths.length > 0)
|
|
406
|
+
return explicitTestPaths;
|
|
407
|
+
return taskConfig.allowedPaths;
|
|
408
|
+
}
|
|
314
409
|
function toTaskRelativeSourcePath(sources, absolutePath) {
|
|
315
410
|
return path
|
|
316
411
|
.relative(sources.taskDir, absolutePath)
|
|
@@ -454,6 +549,7 @@ export async function loadTaskHybridSources(repoRoot, taskId) {
|
|
|
454
549
|
enabledExecutors: resolveEnabledExecutors(manifest.executors),
|
|
455
550
|
executorModelMatrix: resolveExecutorModelMatrices(manifest),
|
|
456
551
|
verifyCommands,
|
|
552
|
+
sddEmbeddedSkills: await probeRepoLocalSddSkills(repoRoot),
|
|
457
553
|
};
|
|
458
554
|
}
|
|
459
555
|
function mergeFinalVerifyCommands(repoRoot, taskConfig, adapterCommands) {
|
|
@@ -479,8 +575,7 @@ export function buildStandardHybridDagFromTask(sources) {
|
|
|
479
575
|
const scoutPaths = deriveParallelScoutPaths(taskConfig);
|
|
480
576
|
const scoutComplexity = mapTaskComplexity(taskConfig.complexity);
|
|
481
577
|
const implementComplexity = resolveWriterComplexity(taskConfig);
|
|
482
|
-
const
|
|
483
|
-
const implementId = implementationNodeId(writerExecutor);
|
|
578
|
+
const implementId = implementationNodeId();
|
|
484
579
|
const finalVerifyCommands = sources.verifyCommands?.final ?? [];
|
|
485
580
|
const verifyShellCommands = buildVerifyShellCommands({
|
|
486
581
|
repoRoot: sources.repoRoot,
|
|
@@ -523,8 +618,9 @@ export function buildStandardHybridDagFromTask(sources) {
|
|
|
523
618
|
...STANDARD_GLOBAL_CONSTRAINTS,
|
|
524
619
|
];
|
|
525
620
|
const spec = {
|
|
526
|
-
version:
|
|
621
|
+
version: 3,
|
|
527
622
|
title: `Hybrid DAG: ${taskConfig.title}`,
|
|
623
|
+
runtimeContract: GENERATED_DAG_RUNTIME_CONTRACT,
|
|
528
624
|
outputLanguage: sources.outputLanguage ?? DEFAULT_DAG_OUTPUT_LANGUAGE,
|
|
529
625
|
objective: extractObjective(sources.requirementMarkdown, taskConfig.title),
|
|
530
626
|
successCriteria: extractSuccessCriteria(sources.requirementMarkdown, sources.taskId),
|
|
@@ -604,7 +700,7 @@ export function buildStandardHybridDagFromTask(sources) {
|
|
|
604
700
|
id: implementId,
|
|
605
701
|
depends_on: ["plan-pi"],
|
|
606
702
|
role: "implementer",
|
|
607
|
-
executor:
|
|
703
|
+
executor: "pi",
|
|
608
704
|
toolProfile: "write",
|
|
609
705
|
complexity: implementComplexity,
|
|
610
706
|
writePolicy: "exclusive",
|
|
@@ -656,11 +752,666 @@ export function buildStandardHybridDagFromTask(sources) {
|
|
|
656
752
|
},
|
|
657
753
|
],
|
|
658
754
|
};
|
|
755
|
+
applySddEmbeddedEnhancements(spec, sources.sddEmbeddedSkills ?? new Set());
|
|
756
|
+
applyDefaultReadOnlyRetryPolicy(spec);
|
|
757
|
+
parseDagSpec(spec);
|
|
758
|
+
assertValidDagSpec(spec);
|
|
759
|
+
return spec;
|
|
760
|
+
}
|
|
761
|
+
function buildFrontendHybridDagFromTask(sources) {
|
|
762
|
+
const { taskConfig } = sources;
|
|
763
|
+
const forbiddenPaths = mergeForbiddenPaths(taskConfig);
|
|
764
|
+
const implementPaths = resolveImplementPaths(taskConfig);
|
|
765
|
+
const implementId = frontendImplementationNodeId();
|
|
766
|
+
const sourceContext = buildSourceContextBlock(sources);
|
|
767
|
+
const strategy = resolveDagVerifyStrategy(taskConfig);
|
|
768
|
+
const readOnlyPaths = taskConfig.allowedPaths.length > 0 ? taskConfig.allowedPaths : ["**"];
|
|
769
|
+
const behaviorPaths = deriveFrontendBehaviorPaths(taskConfig);
|
|
770
|
+
const globalConstraints = [
|
|
771
|
+
...taskConfig.hardConstraints,
|
|
772
|
+
...(sources.constraintMarkdown
|
|
773
|
+
? [`See 执行约束.md in task source (${sources.taskId})`]
|
|
774
|
+
: []),
|
|
775
|
+
...STANDARD_GLOBAL_CONSTRAINTS,
|
|
776
|
+
"frontend-implementation DAGs must pass the design verdict gate before any write node executes.",
|
|
777
|
+
"frontend-implementation DAGs must complete deterministic static verification and behavior verification before final review.",
|
|
778
|
+
"frontend review must block closeout unless review verdict is exactly VERDICT: pass.",
|
|
779
|
+
];
|
|
780
|
+
const staticFallbackCommands = ["npm run typecheck", "npm run build"];
|
|
781
|
+
const behaviorFallbackCommands = ["npm test"];
|
|
782
|
+
const parsedFrontendVerifyCommands = extractFrontendVerifyCommandsFromMarkdown({
|
|
783
|
+
repoRoot: sources.repoRoot,
|
|
784
|
+
requirementMarkdown: sources.requirementMarkdown,
|
|
785
|
+
constraintMarkdown: sources.constraintMarkdown,
|
|
786
|
+
});
|
|
787
|
+
const staticVerifyCommands = chooseFrontendVerifyCommands({
|
|
788
|
+
parsedCommands: parsedFrontendVerifyCommands.staticCommands,
|
|
789
|
+
adapterCommands: sources.verifyCommands?.intermediate,
|
|
790
|
+
});
|
|
791
|
+
const behaviorVerifyCommands = chooseFrontendVerifyCommands({
|
|
792
|
+
parsedCommands: parsedFrontendVerifyCommands.behaviorCommands,
|
|
793
|
+
adapterCommands: sources.verifyCommands?.final,
|
|
794
|
+
});
|
|
795
|
+
const spec = {
|
|
796
|
+
version: 3,
|
|
797
|
+
title: `Frontend implementation DAG: ${taskConfig.title}`,
|
|
798
|
+
runtimeContract: GENERATED_DAG_RUNTIME_CONTRACT,
|
|
799
|
+
objective: extractObjective(sources.requirementMarkdown, taskConfig.title),
|
|
800
|
+
successCriteria: extractSuccessCriteria(sources.requirementMarkdown, sources.taskId),
|
|
801
|
+
globalConstraints,
|
|
802
|
+
defaults: {
|
|
803
|
+
...FRONTEND_DEFAULTS,
|
|
804
|
+
contextProfile: taskConfig.contextProfile,
|
|
805
|
+
},
|
|
806
|
+
skillsByRole: FRONTEND_SKILLS_BY_ROLE,
|
|
807
|
+
executorModels: sources.executorModelMatrix ?? DEFAULT_DAG_EXECUTOR_MODELS,
|
|
808
|
+
verifyStrategy: resolveDagVerifyStrategy(taskConfig),
|
|
809
|
+
tasks: [
|
|
810
|
+
{
|
|
811
|
+
id: "frontend-contract-pi",
|
|
812
|
+
depends_on: [],
|
|
813
|
+
role: "planner",
|
|
814
|
+
executor: "pi",
|
|
815
|
+
complexity: "MED",
|
|
816
|
+
writePolicy: "read-only",
|
|
817
|
+
allowedPaths: readOnlyPaths,
|
|
818
|
+
forbiddenPaths,
|
|
819
|
+
skills: FRONTEND_IMPLEMENTATION_SKILLS,
|
|
820
|
+
outputContract: "Markdown contract with Scope, Non-goals, Acceptance Criteria, UI States, Target Runtime Environment, Risks, and Verification Expectations. No file writes.",
|
|
821
|
+
subtask_prompt: [
|
|
822
|
+
"Read task source and produce a concise frontend implementation contract.",
|
|
823
|
+
"Cover scope, non-goals, acceptance criteria, UI states, target runtime environment, risks, and verification expectations.",
|
|
824
|
+
"Read-only: do not modify code, docs, artifacts, or repository files.",
|
|
825
|
+
sourceContext,
|
|
826
|
+
].join("\n\n"),
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
id: "frontend-scout-pi",
|
|
830
|
+
depends_on: ["frontend-contract-pi"],
|
|
831
|
+
role: "scout",
|
|
832
|
+
executor: "pi",
|
|
833
|
+
complexity: mapTaskComplexity(taskConfig.complexity),
|
|
834
|
+
writePolicy: "read-only",
|
|
835
|
+
allowedPaths: readOnlyPaths,
|
|
836
|
+
forbiddenPaths,
|
|
837
|
+
skills: FRONTEND_IMPLEMENTATION_SKILLS,
|
|
838
|
+
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.",
|
|
839
|
+
subtask_prompt: [
|
|
840
|
+
"Inspect frontend code, routing, components, styles, package scripts, and tests.",
|
|
841
|
+
"Return code and design observations, existing reuse opportunities, and verification entry points.",
|
|
842
|
+
"Read-only: do not modify repository files.",
|
|
843
|
+
sourceContext,
|
|
844
|
+
].join("\n\n"),
|
|
845
|
+
},
|
|
846
|
+
{
|
|
847
|
+
id: "frontend-plan-pi",
|
|
848
|
+
depends_on: ["frontend-scout-pi"],
|
|
849
|
+
role: "planner",
|
|
850
|
+
executor: "pi",
|
|
851
|
+
complexity: "MED",
|
|
852
|
+
writePolicy: "read-only",
|
|
853
|
+
allowedPaths: readOnlyPaths,
|
|
854
|
+
forbiddenPaths,
|
|
855
|
+
skills: FRONTEND_IMPLEMENTATION_SKILLS,
|
|
856
|
+
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.",
|
|
857
|
+
subtask_prompt: [
|
|
858
|
+
"Based on frontend-contract-pi and frontend-scout-pi, return a minimal frontend implementation plan.",
|
|
859
|
+
"Include ordered steps, target files, UI state handling, styling/component strategy, interaction notes, dependency policy, and deterministic verification commands.",
|
|
860
|
+
"Read-only: do not modify code, docs, artifacts, or repository files.",
|
|
861
|
+
sourceContext,
|
|
862
|
+
].join("\n\n"),
|
|
863
|
+
},
|
|
864
|
+
{
|
|
865
|
+
id: "frontend-design-gate-pi",
|
|
866
|
+
depends_on: ["frontend-plan-pi"],
|
|
867
|
+
role: "reviewer",
|
|
868
|
+
executor: "pi",
|
|
869
|
+
complexity: "MED",
|
|
870
|
+
writePolicy: "read-only",
|
|
871
|
+
allowedPaths: readOnlyPaths,
|
|
872
|
+
forbiddenPaths,
|
|
873
|
+
skills: FRONTEND_DESIGN_REVIEW_SKILLS,
|
|
874
|
+
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.",
|
|
875
|
+
subtask_prompt: [
|
|
876
|
+
"Audit the frontend plan before implementation.",
|
|
877
|
+
"First non-empty line must be exactly VERDICT: pass or VERDICT: request-revision.",
|
|
878
|
+
"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.",
|
|
879
|
+
"Read-only: do not modify repository files.",
|
|
880
|
+
sourceContext,
|
|
881
|
+
].join("\n\n"),
|
|
882
|
+
},
|
|
883
|
+
{
|
|
884
|
+
id: "frontend-design-gate-shell",
|
|
885
|
+
depends_on: ["frontend-design-gate-pi"],
|
|
886
|
+
role: "verifier",
|
|
887
|
+
executor: "shell",
|
|
888
|
+
complexity: "LOW",
|
|
889
|
+
writePolicy: "read-only",
|
|
890
|
+
allowedPaths: readOnlyPaths,
|
|
891
|
+
forbiddenPaths,
|
|
892
|
+
outputContract: "Deterministic frontend design verdict gate: exit 0 only when frontend-design-gate-pi emits VERDICT: pass.",
|
|
893
|
+
subtask_prompt: "Deterministic gate: block frontend implementation unless frontend-design-gate-pi emitted VERDICT: pass.",
|
|
894
|
+
shell: {
|
|
895
|
+
commands: [],
|
|
896
|
+
verdictGate: {
|
|
897
|
+
fromNodeId: "frontend-design-gate-pi",
|
|
898
|
+
accept: ["VERDICT: pass"],
|
|
899
|
+
label: "frontend design gate",
|
|
900
|
+
lineMode: "first-verdict-line",
|
|
901
|
+
},
|
|
902
|
+
cwd: ".",
|
|
903
|
+
timeoutMs: 60000,
|
|
904
|
+
},
|
|
905
|
+
},
|
|
906
|
+
{
|
|
907
|
+
id: implementId,
|
|
908
|
+
depends_on: ["frontend-design-gate-shell"],
|
|
909
|
+
role: "implementer",
|
|
910
|
+
executor: "pi",
|
|
911
|
+
toolProfile: "write",
|
|
912
|
+
complexity: taskConfig.complexity === "large"
|
|
913
|
+
? "HIGH"
|
|
914
|
+
: "MED",
|
|
915
|
+
writePolicy: "exclusive",
|
|
916
|
+
writeSet: implementPaths.writeSet,
|
|
917
|
+
allowedPaths: implementPaths.allowedPaths,
|
|
918
|
+
forbiddenPaths,
|
|
919
|
+
skills: FRONTEND_IMPLEMENTATION_SKILLS,
|
|
920
|
+
outputContract: "Markdown summary with Changed Files, Implemented Behavior, UI States Covered, Styling / Component Notes, Verification Attempted, and Residual Risks.",
|
|
921
|
+
subtask_prompt: [
|
|
922
|
+
"Implement the approved frontend plan with minimal focused changes.",
|
|
923
|
+
"Stay within writeSet and preserve unrelated files. Do not write root artifacts/** unless explicitly included in writeSet.",
|
|
924
|
+
sourceContext,
|
|
925
|
+
].join("\n\n"),
|
|
926
|
+
},
|
|
927
|
+
{
|
|
928
|
+
id: "frontend-static-verify-shell",
|
|
929
|
+
depends_on: [implementId],
|
|
930
|
+
role: "verifier",
|
|
931
|
+
executor: "shell",
|
|
932
|
+
complexity: "LOW",
|
|
933
|
+
writePolicy: "read-only",
|
|
934
|
+
allowedPaths: readOnlyPaths,
|
|
935
|
+
forbiddenPaths,
|
|
936
|
+
outputContract: "Archived shell stdout/stderr with exit codes for deterministic static verification; no worktree writes.",
|
|
937
|
+
subtask_prompt: "Run deterministic static verification for the frontend implementation.",
|
|
938
|
+
shell: {
|
|
939
|
+
commands: buildVerifyShellCommands({
|
|
940
|
+
repoRoot: sources.repoRoot,
|
|
941
|
+
commands: staticVerifyCommands.commands,
|
|
942
|
+
fallbackCommands: staticFallbackCommands,
|
|
943
|
+
}),
|
|
944
|
+
verifyEvidence: buildVerifyEvidence({
|
|
945
|
+
phase: "intermediate",
|
|
946
|
+
quota: strategy.intermediateQuota ?? "full",
|
|
947
|
+
commandSource: staticVerifyCommands.commandSource,
|
|
948
|
+
commands: staticVerifyCommands.commands,
|
|
949
|
+
fallbackCommands: staticFallbackCommands,
|
|
950
|
+
}),
|
|
951
|
+
cwd: ".",
|
|
952
|
+
timeoutMs: 300000,
|
|
953
|
+
},
|
|
954
|
+
},
|
|
955
|
+
{
|
|
956
|
+
id: "frontend-behavior-verify-shell",
|
|
957
|
+
depends_on: ["frontend-static-verify-shell"],
|
|
958
|
+
role: "verifier",
|
|
959
|
+
executor: "shell",
|
|
960
|
+
complexity: "LOW",
|
|
961
|
+
writePolicy: "read-only",
|
|
962
|
+
allowedPaths: behaviorPaths,
|
|
963
|
+
forbiddenPaths,
|
|
964
|
+
outputContract: "Archived shell stdout/stderr with exit codes for deterministic behavior verification; no worktree writes.",
|
|
965
|
+
subtask_prompt: "Run deterministic behavior verification for frontend flows, states, and integration points.",
|
|
966
|
+
shell: {
|
|
967
|
+
commands: buildVerifyShellCommands({
|
|
968
|
+
repoRoot: sources.repoRoot,
|
|
969
|
+
commands: behaviorVerifyCommands.commands,
|
|
970
|
+
fallbackCommands: behaviorFallbackCommands,
|
|
971
|
+
}),
|
|
972
|
+
verifyEvidence: buildVerifyEvidence({
|
|
973
|
+
phase: "final",
|
|
974
|
+
quota: "full",
|
|
975
|
+
commandSource: behaviorVerifyCommands.commandSource,
|
|
976
|
+
commands: behaviorVerifyCommands.commands,
|
|
977
|
+
fallbackCommands: behaviorFallbackCommands,
|
|
978
|
+
finalFullRequired: true,
|
|
979
|
+
}),
|
|
980
|
+
cwd: ".",
|
|
981
|
+
timeoutMs: 300000,
|
|
982
|
+
},
|
|
983
|
+
},
|
|
984
|
+
{
|
|
985
|
+
id: "frontend-review-pi",
|
|
986
|
+
depends_on: ["frontend-behavior-verify-shell"],
|
|
987
|
+
role: "reviewer",
|
|
988
|
+
executor: "pi",
|
|
989
|
+
complexity: "HIGH",
|
|
990
|
+
writePolicy: "read-only",
|
|
991
|
+
allowedPaths: readOnlyPaths,
|
|
992
|
+
forbiddenPaths,
|
|
993
|
+
skills: FRONTEND_REVIEW_SKILLS,
|
|
994
|
+
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.",
|
|
995
|
+
subtask_prompt: [
|
|
996
|
+
"Review the frontend implementation and verification evidence.",
|
|
997
|
+
"First non-empty line must be exactly VERDICT: pass or VERDICT: request-revision.",
|
|
998
|
+
"Any Critical or Important finding must force VERDICT: request-revision.",
|
|
999
|
+
"Review implementation quality, behavior/state coverage, verification evidence, and maintainability. Read-only: do not modify files.",
|
|
1000
|
+
sourceContext,
|
|
1001
|
+
].join("\n\n"),
|
|
1002
|
+
},
|
|
1003
|
+
{
|
|
1004
|
+
id: "frontend-review-gate-shell",
|
|
1005
|
+
depends_on: ["frontend-review-pi"],
|
|
1006
|
+
role: "verifier",
|
|
1007
|
+
executor: "shell",
|
|
1008
|
+
complexity: "LOW",
|
|
1009
|
+
writePolicy: "read-only",
|
|
1010
|
+
allowedPaths: readOnlyPaths,
|
|
1011
|
+
forbiddenPaths,
|
|
1012
|
+
outputContract: "Deterministic frontend review verdict gate: exit 0 only when frontend-review-pi emits VERDICT: pass.",
|
|
1013
|
+
subtask_prompt: "Deterministic gate: block downstream closeout unless frontend-review-pi emitted VERDICT: pass.",
|
|
1014
|
+
shell: {
|
|
1015
|
+
commands: [],
|
|
1016
|
+
verdictGate: {
|
|
1017
|
+
fromNodeId: "frontend-review-pi",
|
|
1018
|
+
accept: ["VERDICT: pass"],
|
|
1019
|
+
label: "frontend review",
|
|
1020
|
+
lineMode: "first-verdict-line",
|
|
1021
|
+
},
|
|
1022
|
+
cwd: ".",
|
|
1023
|
+
timeoutMs: 60000,
|
|
1024
|
+
},
|
|
1025
|
+
},
|
|
1026
|
+
{
|
|
1027
|
+
id: "frontend-closeout-pi",
|
|
1028
|
+
depends_on: ["frontend-review-gate-shell"],
|
|
1029
|
+
role: "closeout",
|
|
1030
|
+
executor: "pi",
|
|
1031
|
+
complexity: "MED",
|
|
1032
|
+
writePolicy: "read-only",
|
|
1033
|
+
allowedPaths: taskConfig.allowedPaths.length > 0
|
|
1034
|
+
? [...taskConfig.allowedPaths, "docs/**"]
|
|
1035
|
+
: ["**", "docs/**"],
|
|
1036
|
+
forbiddenPaths,
|
|
1037
|
+
skills: FRONTEND_VERIFICATION_SKILLS,
|
|
1038
|
+
outputContract: "Markdown closeout summary with Changes, Verification Evidence, Review Result, Known Risks, and Follow-up. No file writes.",
|
|
1039
|
+
subtask_prompt: [
|
|
1040
|
+
"Return a frontend closeout summary covering changes, verification evidence, review result, known risks, and follow-up.",
|
|
1041
|
+
"Read-only: do not modify code, docs, artifacts, or .harness/dag-runs/.",
|
|
1042
|
+
sourceContext,
|
|
1043
|
+
].join("\n\n"),
|
|
1044
|
+
},
|
|
1045
|
+
],
|
|
1046
|
+
};
|
|
1047
|
+
applyDefaultReadOnlyRetryPolicy(spec);
|
|
1048
|
+
parseDagSpec(spec);
|
|
1049
|
+
assertValidDagSpec(spec);
|
|
1050
|
+
return spec;
|
|
1051
|
+
}
|
|
1052
|
+
// ---------------------------------------------------------------------------
|
|
1053
|
+
// Backend test DAG template
|
|
1054
|
+
// ---------------------------------------------------------------------------
|
|
1055
|
+
function buildAnalyzeInputsNode(sources) {
|
|
1056
|
+
return {
|
|
1057
|
+
id: "analyze-inputs-pi",
|
|
1058
|
+
depends_on: [],
|
|
1059
|
+
role: "planner",
|
|
1060
|
+
executor: "pi",
|
|
1061
|
+
complexity: "MED",
|
|
1062
|
+
writePolicy: "read-only",
|
|
1063
|
+
allowedPaths: commonReadOnlyPaths(sources),
|
|
1064
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
1065
|
+
outputContract: "Structured Markdown extracting core content from source documents. No file writes.",
|
|
1066
|
+
subtask_prompt: [
|
|
1067
|
+
"Read the task source materials and extract the following structured content for downstream test generation.",
|
|
1068
|
+
"",
|
|
1069
|
+
"## Required Output Sections:",
|
|
1070
|
+
"",
|
|
1071
|
+
"### 1. API Endpoints",
|
|
1072
|
+
"List all API endpoints: Method, Path, Description, Request params, Response format.",
|
|
1073
|
+
"",
|
|
1074
|
+
"### 2. Data Model",
|
|
1075
|
+
"For each table/collection: fields, types, constraints, descriptions.",
|
|
1076
|
+
"",
|
|
1077
|
+
"### 3. Business Logic",
|
|
1078
|
+
"Core business rules, validation rules, calculation formulas.",
|
|
1079
|
+
"",
|
|
1080
|
+
"### 4. State Transitions",
|
|
1081
|
+
"State machines (e.g. order status: pending → paid → shipped → completed).",
|
|
1082
|
+
"",
|
|
1083
|
+
"### 5. Error Scenarios & Error Codes",
|
|
1084
|
+
"All error codes, error messages, and when they occur.",
|
|
1085
|
+
"",
|
|
1086
|
+
"### 6. External Dependencies",
|
|
1087
|
+
"Third-party services, databases, message queues. Include timeout settings if documented.",
|
|
1088
|
+
"",
|
|
1089
|
+
"### 7. Acceptance Criteria",
|
|
1090
|
+
"Extract ALL acceptance criteria from 需求.md. Number them AC-001, AC-002, etc. If not explicitly listed, derive from functional requirements.",
|
|
1091
|
+
"",
|
|
1092
|
+
"### 8. Risk Areas",
|
|
1093
|
+
"High-risk areas requiring extra test coverage.",
|
|
1094
|
+
"",
|
|
1095
|
+
"## Conditional Sections (include ONLY if mentioned in requirements):",
|
|
1096
|
+
"- Authentication & Authorization: include ONLY if requirements mention auth mechanism (JWT, OAuth2, API Key, etc.)",
|
|
1097
|
+
"- Timeout Handling: include ONLY if requirements mention timeout configuration or degradation strategy",
|
|
1098
|
+
"- Concurrency & Idempotency: include ONLY if requirements mention concurrency, idempotency rules, or locking mechanisms",
|
|
1099
|
+
"- State Transitions: include ONLY if requirements mention business state machines",
|
|
1100
|
+
"- If not mentioned in requirements, do NOT include these sections",
|
|
1101
|
+
"",
|
|
1102
|
+
"This output will be used directly by downstream nodes. Be thorough and structured.",
|
|
1103
|
+
"Read-only: do not modify code, docs, artifacts, or repository files.",
|
|
1104
|
+
buildSourceContextBlock(sources),
|
|
1105
|
+
].join("\n\n"),
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
function buildGenerateBackendFunctionalCasesNode(sources) {
|
|
1109
|
+
return {
|
|
1110
|
+
id: "generate-backend-functional-cases-pi",
|
|
1111
|
+
depends_on: ["analyze-inputs-pi"],
|
|
1112
|
+
role: "implementer",
|
|
1113
|
+
executor: "pi",
|
|
1114
|
+
toolProfile: "write",
|
|
1115
|
+
complexity: "MED",
|
|
1116
|
+
writePolicy: "exclusive",
|
|
1117
|
+
writeSet: ["testcase/md/**"],
|
|
1118
|
+
allowedPaths: ["testcase/md/**"],
|
|
1119
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
1120
|
+
// 注意:Pi 节点超时由 executor 层控制(默认 30 分钟)
|
|
1121
|
+
// 如需调整,在 harness.json 的 executors.pi 中配置 modelConfig.timeoutMs
|
|
1122
|
+
subtask_prompt: [
|
|
1123
|
+
"Based on the upstream analyze-inputs-pi output, generate structured backend functional test cases.",
|
|
1124
|
+
"",
|
|
1125
|
+
"## Output Steps (do in order):",
|
|
1126
|
+
"1. First, output a brief summary: how many modules, how many cases planned per module",
|
|
1127
|
+
"2. Then write each test case file under testcase/md/",
|
|
1128
|
+
"",
|
|
1129
|
+
"## Format Rules:",
|
|
1130
|
+
"- Each test case ID: BE-<MODULE>-<NNN> (e.g. BE-ORDER-001)",
|
|
1131
|
+
"- Each file covers one module",
|
|
1132
|
+
"- Case structure: ID, Title, Precondition, Steps, Expected Result",
|
|
1133
|
+
"- Map each case to acceptance criteria (AC-xxx)",
|
|
1134
|
+
"",
|
|
1135
|
+
"## Coverage Requirements:",
|
|
1136
|
+
"- Positive paths: happy path for each acceptance criterion",
|
|
1137
|
+
"- Negative paths: error scenarios (invalid input, not found, state violations)",
|
|
1138
|
+
"- Boundary conditions: empty input, max length, edge values",
|
|
1139
|
+
"",
|
|
1140
|
+
"## Conditional Coverage (include ONLY if mentioned in upstream analysis):",
|
|
1141
|
+
"- State transitions: include ONLY if upstream analyze-inputs-pi mentions state machine",
|
|
1142
|
+
"- Authentication scenarios: include ONLY if upstream analyze-inputs-pi mentions auth mechanism",
|
|
1143
|
+
"- Timeout scenarios: include ONLY if upstream analyze-inputs-pi mentions timeout handling",
|
|
1144
|
+
"- Concurrency scenarios: include ONLY if upstream analyze-inputs-pi mentions concurrency/idempotency rules",
|
|
1145
|
+
"- If not mentioned, do NOT generate these test cases",
|
|
1146
|
+
"",
|
|
1147
|
+
"## Constraints:",
|
|
1148
|
+
"- Stay within writeSet: testcase/md/**",
|
|
1149
|
+
"- Do NOT re-read source documents — use the upstream analyze-inputs-pi output only",
|
|
1150
|
+
"- Do not write root artifacts/**",
|
|
1151
|
+
].join("\n\n"),
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
function buildReviewBackendCasesNode(sources) {
|
|
1155
|
+
return {
|
|
1156
|
+
id: "review-backend-cases-pi",
|
|
1157
|
+
depends_on: ["generate-backend-functional-cases-pi"],
|
|
1158
|
+
role: "reviewer",
|
|
1159
|
+
executor: "pi",
|
|
1160
|
+
complexity: "HIGH",
|
|
1161
|
+
writePolicy: "read-only",
|
|
1162
|
+
allowedPaths: commonReadOnlyPaths(sources),
|
|
1163
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
1164
|
+
outputContract: "Plain Markdown whose first non-empty line is VERDICT: pass or VERDICT: request-revision; followed by Findings and Coverage Assessment. No file writes.",
|
|
1165
|
+
subtask_prompt: [
|
|
1166
|
+
"Review the generated backend functional test cases under testcase/md/.",
|
|
1167
|
+
"",
|
|
1168
|
+
"## Mandatory First Line:",
|
|
1169
|
+
"First non-empty line must be exactly: VERDICT: pass or VERDICT: request-revision",
|
|
1170
|
+
"",
|
|
1171
|
+
"## Review Checklist:",
|
|
1172
|
+
"- ID format: every case uses BE-<MODULE>-<NNN>",
|
|
1173
|
+
"- Positive coverage: each acceptance criterion (AC-xxx) has happy-path case",
|
|
1174
|
+
"- Negative coverage: error scenarios (invalid input, not found, state violations)",
|
|
1175
|
+
"- Boundary coverage: edge cases (empty, max length, edge values)",
|
|
1176
|
+
"- Traceability: each AC maps to at least one case ID",
|
|
1177
|
+
"- Case structure: ID, Title, Precondition, Steps, Expected Result",
|
|
1178
|
+
"- No duplicate IDs across files",
|
|
1179
|
+
"",
|
|
1180
|
+
"## Conditional Coverage (check ONLY if mentioned in upstream analysis):",
|
|
1181
|
+
"- State transition coverage: check ONLY if analyze-inputs-pi mentions state machine",
|
|
1182
|
+
"- Authentication coverage: check ONLY if analyze-inputs-pi mentions auth mechanism",
|
|
1183
|
+
"- Timeout coverage: check ONLY if analyze-inputs-pi mentions timeout handling",
|
|
1184
|
+
"- Concurrency coverage: check ONLY if analyze-inputs-pi mentions concurrency/idempotency rules",
|
|
1185
|
+
"- If not mentioned, do NOT flag as missing",
|
|
1186
|
+
"",
|
|
1187
|
+
"## Verdict Rules:",
|
|
1188
|
+
"- All Critical checks pass + Important findings ≤ 2 → VERDICT: pass",
|
|
1189
|
+
"- Any Critical fails OR Important > 2 → VERDICT: request-revision",
|
|
1190
|
+
"",
|
|
1191
|
+
"## Output After Verdict:",
|
|
1192
|
+
"1. Coverage Assessment table (AC → case IDs)",
|
|
1193
|
+
"2. Findings list (Critical/Important/Informational)",
|
|
1194
|
+
"3. Statistics (total cases, positive/negative/boundary breakdown)",
|
|
1195
|
+
"",
|
|
1196
|
+
"## Constraints:",
|
|
1197
|
+
"- Read-only: do not modify files",
|
|
1198
|
+
"- Do NOT re-read source documents — use upstream analyze-inputs-pi output for acceptance criteria",
|
|
1199
|
+
"- Use testcase/md/ files for case review",
|
|
1200
|
+
].join("\n\n"),
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
function buildReviewBackendCasesGateNode(sources) {
|
|
1204
|
+
return {
|
|
1205
|
+
id: "review-backend-cases-gate-shell",
|
|
1206
|
+
depends_on: ["review-backend-cases-pi"],
|
|
1207
|
+
role: "verifier",
|
|
1208
|
+
executor: "shell",
|
|
1209
|
+
complexity: "LOW",
|
|
1210
|
+
writePolicy: "read-only",
|
|
1211
|
+
allowedPaths: commonReadOnlyPaths(sources),
|
|
1212
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
1213
|
+
outputContract: "Deterministic backend case review gate: exit 0 only when review-backend-cases-pi emits VERDICT: pass.",
|
|
1214
|
+
subtask_prompt: "Deterministic gate: block pytest generation unless backend case review emitted VERDICT: pass.",
|
|
1215
|
+
shell: {
|
|
1216
|
+
commands: [],
|
|
1217
|
+
verdictGate: {
|
|
1218
|
+
fromNodeId: "review-backend-cases-pi",
|
|
1219
|
+
accept: ["VERDICT: pass"],
|
|
1220
|
+
label: "backend case review",
|
|
1221
|
+
lineMode: "first-verdict-line",
|
|
1222
|
+
},
|
|
1223
|
+
cwd: ".",
|
|
1224
|
+
timeoutMs: 60000,
|
|
1225
|
+
},
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
function buildGenerateBackendPytestNode(sources) {
|
|
1229
|
+
return {
|
|
1230
|
+
id: "generate-backend-pytest-pi",
|
|
1231
|
+
depends_on: ["review-backend-cases-gate-shell"],
|
|
1232
|
+
role: "implementer",
|
|
1233
|
+
executor: "pi",
|
|
1234
|
+
toolProfile: "write",
|
|
1235
|
+
complexity: "HIGH",
|
|
1236
|
+
writePolicy: "exclusive",
|
|
1237
|
+
writeSet: ["testcase/**/test_*.py"],
|
|
1238
|
+
allowedPaths: ["testcase/**/test_*.py"],
|
|
1239
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
1240
|
+
// 注意:Pi 节点超时由 executor 层控制(默认 30 分钟)
|
|
1241
|
+
// 如需调整,在 harness.json 的 executors.pi 中配置 modelConfig.timeoutMs
|
|
1242
|
+
subtask_prompt: [
|
|
1243
|
+
"Convert the reviewed test cases under testcase/md/ into pytest automation code.",
|
|
1244
|
+
"",
|
|
1245
|
+
"## Output Steps (do in order):",
|
|
1246
|
+
"1. First, output a brief summary: how many files, how many test functions planned",
|
|
1247
|
+
"2. Then write each test file under testcase/",
|
|
1248
|
+
"",
|
|
1249
|
+
"## Format Rules:",
|
|
1250
|
+
"- File prefix: test_<module>.py",
|
|
1251
|
+
"- Function name: test_BE_<MODULE>_<NNN>_<description>",
|
|
1252
|
+
"- Docstring first line: BE-<MODULE>-<NNN>: <Case Title>",
|
|
1253
|
+
"- 1:1 mapping: each functional case → one pytest function",
|
|
1254
|
+
"",
|
|
1255
|
+
"## Implementation Rules:",
|
|
1256
|
+
"- Use assert statements, not unittest assertions",
|
|
1257
|
+
"- Assert specific values, not just 'no exception'",
|
|
1258
|
+
"- Use @pytest.mark.parametrize for boundary cases",
|
|
1259
|
+
"- Use markers: @pytest.mark.positive, @pytest.mark.negative, @pytest.mark.boundary",
|
|
1260
|
+
"",
|
|
1261
|
+
"## Conditional Implementation (include ONLY if test cases exist):",
|
|
1262
|
+
"- Authentication tests: implement ONLY if testcase/md/ contains auth-related cases",
|
|
1263
|
+
"- Timeout tests: implement ONLY if testcase/md/ contains timeout-related cases",
|
|
1264
|
+
"- Use @pytest.mark.auth for auth tests, @pytest.mark.timeout for timeout tests",
|
|
1265
|
+
"- If no such cases exist, do NOT add these tests",
|
|
1266
|
+
"",
|
|
1267
|
+
"## Constraints:",
|
|
1268
|
+
"- Only create NEW files, do NOT modify existing framework files (conftest.py, pytest.ini, pyproject.toml)",
|
|
1269
|
+
"- If filename exists, add suffix: test_order.py → test_order_01.py",
|
|
1270
|
+
"- Stay within writeSet: testcase/**/test_*.py",
|
|
1271
|
+
"- Do NOT re-read source documents — use the reviewed cases under testcase/md/ only",
|
|
1272
|
+
"- Read existing conftest.py/pytest.ini to understand conventions, but do NOT modify them",
|
|
1273
|
+
].join("\n\n"),
|
|
1274
|
+
};
|
|
1275
|
+
}
|
|
1276
|
+
function buildExecuteBackendPytestNode(sources) {
|
|
1277
|
+
return {
|
|
1278
|
+
id: "execute-backend-pytest-shell",
|
|
1279
|
+
depends_on: ["generate-backend-pytest-pi"],
|
|
1280
|
+
role: "verifier",
|
|
1281
|
+
executor: "shell",
|
|
1282
|
+
complexity: "LOW",
|
|
1283
|
+
writePolicy: "read-only",
|
|
1284
|
+
allowedPaths: commonReadOnlyPaths(sources),
|
|
1285
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
1286
|
+
outputContract: "Archived pytest stdout/stderr with exit codes and HTML report path; no source or test file modifications.",
|
|
1287
|
+
subtask_prompt: "Run pytest for the backend test suite and capture results.",
|
|
1288
|
+
shell: {
|
|
1289
|
+
commands: [
|
|
1290
|
+
"python -m pytest testcase/ --html=reports/backend-test-report.html -v",
|
|
1291
|
+
],
|
|
1292
|
+
verifyEvidence: buildVerifyEvidence({
|
|
1293
|
+
phase: "final",
|
|
1294
|
+
quota: "full",
|
|
1295
|
+
commandSource: "inline",
|
|
1296
|
+
fallbackCommands: [
|
|
1297
|
+
"python -m pytest testcase/ --html=reports/backend-test-report.html -v",
|
|
1298
|
+
],
|
|
1299
|
+
finalFullRequired: true,
|
|
1300
|
+
}),
|
|
1301
|
+
cwd: ".",
|
|
1302
|
+
timeoutMs: 300000,
|
|
1303
|
+
},
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1306
|
+
function buildTestRetrospectNode(sources) {
|
|
1307
|
+
return {
|
|
1308
|
+
id: "test-retrospect-pi",
|
|
1309
|
+
depends_on: ["execute-backend-pytest-shell"],
|
|
1310
|
+
role: "closeout",
|
|
1311
|
+
executor: "pi",
|
|
1312
|
+
toolProfile: "write",
|
|
1313
|
+
complexity: "MED",
|
|
1314
|
+
writePolicy: "exclusive",
|
|
1315
|
+
writeSet: ["docs/test-reports/**"],
|
|
1316
|
+
allowedPaths: ["docs/test-reports/**"],
|
|
1317
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
1318
|
+
subtask_prompt: [
|
|
1319
|
+
"Read upstream outputs (review report + pytest results) and generate a test retrospective report.",
|
|
1320
|
+
"",
|
|
1321
|
+
"## Output Steps (do in order):",
|
|
1322
|
+
"1. First, output the maturity rating on the first line: Rating: A/B/C/D",
|
|
1323
|
+
"2. Then write the full report under docs/test-reports/",
|
|
1324
|
+
"",
|
|
1325
|
+
"## Report Structure:",
|
|
1326
|
+
"1. Maturity Rating with rationale",
|
|
1327
|
+
"2. Test Coverage Summary (total cases, pass rate, failed case analysis)",
|
|
1328
|
+
"3. Review Findings and resolution status",
|
|
1329
|
+
"4. Failed Test Analysis (if any)",
|
|
1330
|
+
"5. Recommendations for improvement",
|
|
1331
|
+
"",
|
|
1332
|
+
"## Rating Criteria:",
|
|
1333
|
+
"- A: 100% acceptance criteria covered + 100% pytest pass + no Critical findings",
|
|
1334
|
+
"- B: ≥80% coverage + ≥90% pass + Low findings only",
|
|
1335
|
+
"- C: ≥60% coverage + ≥70% pass + no Critical findings",
|
|
1336
|
+
"- D: below C thresholds",
|
|
1337
|
+
"",
|
|
1338
|
+
"## Constraints:",
|
|
1339
|
+
"- Stay within writeSet: docs/test-reports/**",
|
|
1340
|
+
"- Do NOT re-read source documents — use upstream outputs only",
|
|
1341
|
+
"- Do not write root artifacts/**",
|
|
1342
|
+
].join("\n\n"),
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
const BACKEND_TEST_DEFAULTS = {
|
|
1346
|
+
...HYBRID_DEFAULTS,
|
|
1347
|
+
writePolicy: "read-only",
|
|
1348
|
+
};
|
|
1349
|
+
const BACKEND_TEST_SKILLS_BY_ROLE = {
|
|
1350
|
+
planner: ["loop-agent"],
|
|
1351
|
+
scout: [],
|
|
1352
|
+
implementer: ["test-driven-development", "verification-before-completion"],
|
|
1353
|
+
reviewer: ["requesting-code-review", "code-review-core"],
|
|
1354
|
+
verifier: ["verification-before-completion", "systematic-debugging"],
|
|
1355
|
+
closeout: ["loop-agent", "verification-before-completion"],
|
|
1356
|
+
};
|
|
1357
|
+
function buildBackendTestHybridDag(sources) {
|
|
1358
|
+
const { taskConfig } = sources;
|
|
1359
|
+
const sourceContext = buildSourceContextBlock(sources);
|
|
1360
|
+
const readOnlyPaths = commonReadOnlyPaths(sources);
|
|
1361
|
+
const forbiddenPaths = commonForbiddenPaths(sources);
|
|
1362
|
+
const globalConstraints = [
|
|
1363
|
+
...taskConfig.hardConstraints,
|
|
1364
|
+
...(sources.constraintMarkdown
|
|
1365
|
+
? [`See 执行约束.md in task source (${sources.taskId})`]
|
|
1366
|
+
: []),
|
|
1367
|
+
...STANDARD_GLOBAL_CONSTRAINTS,
|
|
1368
|
+
"backend-test-dag nodes must maintain traceability from requirements to functional cases to pytest automation.",
|
|
1369
|
+
"Functional test case IDs must use BE-<MODULE>-<NNN> format.",
|
|
1370
|
+
"pytest execution must produce HTML reports under reports/.",
|
|
1371
|
+
"pytest automation scripts must use test_ filename prefix for pytest discovery.",
|
|
1372
|
+
"generate-backend-pytest-pi must only create new test files under testcase/; modifying existing framework files (conftest.py, pytest.ini, pyproject.toml) is forbidden.",
|
|
1373
|
+
"review-backend-cases-gate-shell must block pytest generation unless the review verdict is exactly VERDICT: pass.",
|
|
1374
|
+
"If a target test filename already exists under testcase/, add a numeric suffix (_01, _02, ...); never overwrite or append to existing files.",
|
|
1375
|
+
"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.",
|
|
1376
|
+
];
|
|
1377
|
+
const spec = {
|
|
1378
|
+
version: 3,
|
|
1379
|
+
title: `Backend test DAG: ${taskConfig.title}`,
|
|
1380
|
+
runtimeContract: GENERATED_DAG_RUNTIME_CONTRACT,
|
|
1381
|
+
outputLanguage: sources.outputLanguage ?? DEFAULT_DAG_OUTPUT_LANGUAGE,
|
|
1382
|
+
objective: extractObjective(sources.requirementMarkdown, taskConfig.title),
|
|
1383
|
+
successCriteria: extractSuccessCriteria(sources.requirementMarkdown, sources.taskId),
|
|
1384
|
+
globalConstraints,
|
|
1385
|
+
defaults: {
|
|
1386
|
+
...BACKEND_TEST_DEFAULTS,
|
|
1387
|
+
contextProfile: taskConfig.contextProfile,
|
|
1388
|
+
},
|
|
1389
|
+
skillsByRole: BACKEND_TEST_SKILLS_BY_ROLE,
|
|
1390
|
+
executorModels: sources.executorModelMatrix ?? DEFAULT_DAG_EXECUTOR_MODELS,
|
|
1391
|
+
tasks: [
|
|
1392
|
+
buildAnalyzeInputsNode(sources),
|
|
1393
|
+
buildGenerateBackendFunctionalCasesNode(sources),
|
|
1394
|
+
buildReviewBackendCasesNode(sources),
|
|
1395
|
+
buildReviewBackendCasesGateNode(sources),
|
|
1396
|
+
buildGenerateBackendPytestNode(sources),
|
|
1397
|
+
buildExecuteBackendPytestNode(sources),
|
|
1398
|
+
buildTestRetrospectNode(sources),
|
|
1399
|
+
],
|
|
1400
|
+
};
|
|
1401
|
+
applyDefaultReadOnlyRetryPolicy(spec);
|
|
659
1402
|
parseDagSpec(spec);
|
|
660
1403
|
assertValidDagSpec(spec);
|
|
661
1404
|
return spec;
|
|
662
1405
|
}
|
|
663
1406
|
export function buildHybridDagFromTask(sources, options = {}) {
|
|
1407
|
+
if (sources.taskConfig.taskKind === "frontend-implementation" ||
|
|
1408
|
+
options.template === "frontend-implementation") {
|
|
1409
|
+
return buildFrontendHybridDagFromTask(sources);
|
|
1410
|
+
}
|
|
1411
|
+
if (sources.taskConfig.taskKind === "backend-test" ||
|
|
1412
|
+
options.template === "backend-test-dag") {
|
|
1413
|
+
return buildBackendTestHybridDag(sources);
|
|
1414
|
+
}
|
|
664
1415
|
const standard = buildStandardHybridDagFromTask(sources);
|
|
665
1416
|
const template = options.template ?? "standard-dag";
|
|
666
1417
|
if (template === "standard-dag")
|
|
@@ -672,6 +1423,21 @@ export function buildHybridDagFromTask(sources, options = {}) {
|
|
|
672
1423
|
function cloneTask(task, patch = {}) {
|
|
673
1424
|
return { ...task, ...patch };
|
|
674
1425
|
}
|
|
1426
|
+
/**
|
|
1427
|
+
* Apply the default read-only Pi retry policy to safe planner/scout/reviewer/
|
|
1428
|
+
* verifier/closeout Pi nodes in the generated DAG. Writers, supervisors,
|
|
1429
|
+
* dynamic, shell, static, and decision-gate nodes are skipped. Idempotent:
|
|
1430
|
+
* never overwrites an explicit retryPolicy a task already declares.
|
|
1431
|
+
*/
|
|
1432
|
+
function applyDefaultReadOnlyRetryPolicy(spec) {
|
|
1433
|
+
for (const task of spec.tasks) {
|
|
1434
|
+
if (task.retryPolicy !== undefined)
|
|
1435
|
+
continue;
|
|
1436
|
+
if (isSafeReadOnlyPiRetryCandidate(task)) {
|
|
1437
|
+
task.retryPolicy = DEFAULT_READ_ONLY_PI_RETRY_POLICY;
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
675
1441
|
function getTaskOrThrow(spec, id) {
|
|
676
1442
|
const task = spec.tasks.find((item) => item.id === id);
|
|
677
1443
|
if (!task)
|
|
@@ -758,6 +1524,8 @@ function buildReviewGatedHybridDag(standard, sources) {
|
|
|
758
1524
|
const closeout = getTaskOrThrow(spec, "closeout-pi");
|
|
759
1525
|
replaceTask(spec, cloneTask(closeout, { depends_on: ["review-gate-shell"] }));
|
|
760
1526
|
spec.tasks.splice(spec.tasks.length - 1, 0, buildReviewNode(sources), buildReviewGateNode(sources));
|
|
1527
|
+
applySddEmbeddedEnhancements(spec, sources.sddEmbeddedSkills ?? new Set());
|
|
1528
|
+
applyDefaultReadOnlyRetryPolicy(spec);
|
|
761
1529
|
parseDagSpec(spec);
|
|
762
1530
|
assertValidDagSpec(spec);
|
|
763
1531
|
return spec;
|
|
@@ -807,7 +1575,7 @@ function buildWriteSetGateNode(sources) {
|
|
|
807
1575
|
};
|
|
808
1576
|
}
|
|
809
1577
|
function buildSoftVerifyNode(sources) {
|
|
810
|
-
const implementId = implementationNodeId(
|
|
1578
|
+
const implementId = implementationNodeId();
|
|
811
1579
|
const strategy = resolveDagVerifyStrategy(sources.taskConfig);
|
|
812
1580
|
const fallbackCommands = ["npm run typecheck"];
|
|
813
1581
|
const commands = buildVerifyShellCommands({
|
|
@@ -841,8 +1609,8 @@ function buildSoftVerifyNode(sources) {
|
|
|
841
1609
|
};
|
|
842
1610
|
}
|
|
843
1611
|
function buildProcessSupervisorNode(sources) {
|
|
844
|
-
const implementId = implementationNodeId(
|
|
845
|
-
const repairId = repairNodeId(
|
|
1612
|
+
const implementId = implementationNodeId();
|
|
1613
|
+
const repairId = repairNodeId();
|
|
846
1614
|
return {
|
|
847
1615
|
id: "process-supervisor-pi",
|
|
848
1616
|
depends_on: ["soft-verify-shell", implementId],
|
|
@@ -886,6 +1654,7 @@ function buildProcessGateNode(sources) {
|
|
|
886
1654
|
},
|
|
887
1655
|
repairArtifactGate: {
|
|
888
1656
|
fromNodeId: "process-supervisor-pi",
|
|
1657
|
+
repairNodeId: repairNodeId(),
|
|
889
1658
|
},
|
|
890
1659
|
cwd: ".",
|
|
891
1660
|
timeoutMs: 60000,
|
|
@@ -893,13 +1662,12 @@ function buildProcessGateNode(sources) {
|
|
|
893
1662
|
};
|
|
894
1663
|
}
|
|
895
1664
|
function buildRepairNode(sources) {
|
|
896
|
-
const writerExecutor = resolveImplementationExecutor(sources);
|
|
897
1665
|
const implement = resolveImplementPaths(sources.taskConfig);
|
|
898
1666
|
return {
|
|
899
|
-
id: repairNodeId(
|
|
1667
|
+
id: repairNodeId(),
|
|
900
1668
|
depends_on: ["process-gate-shell", "process-supervisor-pi"],
|
|
901
1669
|
role: "implementer",
|
|
902
|
-
executor:
|
|
1670
|
+
executor: "pi",
|
|
903
1671
|
toolProfile: "write",
|
|
904
1672
|
complexity: isInteractiveUiTask(sources.taskConfig)
|
|
905
1673
|
? "HIGH"
|
|
@@ -921,7 +1689,7 @@ function buildRepairNode(sources) {
|
|
|
921
1689
|
};
|
|
922
1690
|
}
|
|
923
1691
|
function buildHardVerifyNode(sources) {
|
|
924
|
-
const repairId = repairNodeId(
|
|
1692
|
+
const repairId = repairNodeId();
|
|
925
1693
|
const fallbackCommands = [
|
|
926
1694
|
"HARNESS_ALLOW_ACTIVE_DAG_RUNS=1 bash scripts/check-repo.sh",
|
|
927
1695
|
];
|
|
@@ -980,7 +1748,7 @@ function buildSupervisedHybridDag(standard, sources) {
|
|
|
980
1748
|
const scoutSrc = getTaskOrThrow(standard, "scout-src");
|
|
981
1749
|
const scoutTests = getTaskOrThrow(standard, "scout-tests");
|
|
982
1750
|
const plan = getTaskOrThrow(standard, "plan-pi");
|
|
983
|
-
const implement = getTaskOrThrow(standard, implementationNodeId(
|
|
1751
|
+
const implement = getTaskOrThrow(standard, implementationNodeId());
|
|
984
1752
|
const closeout = getTaskOrThrow(standard, "closeout-pi");
|
|
985
1753
|
const authorityDecision = resolveAuthoritySurfaceAudit(sources);
|
|
986
1754
|
const reviewDependsOn = authorityDecision.enabled
|
|
@@ -1028,6 +1796,8 @@ function buildSupervisedHybridDag(standard, sources) {
|
|
|
1028
1796
|
cloneTask(closeout, { depends_on: ["decision-pi"] }),
|
|
1029
1797
|
],
|
|
1030
1798
|
};
|
|
1799
|
+
applySddEmbeddedEnhancements(spec, sources.sddEmbeddedSkills ?? new Set());
|
|
1800
|
+
applyDefaultReadOnlyRetryPolicy(spec);
|
|
1031
1801
|
parseDagSpec(spec);
|
|
1032
1802
|
assertValidDagSpec(spec);
|
|
1033
1803
|
return spec;
|