@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.
- package/AGENTS.md +10 -0
- package/CHANGELOG.md +101 -1
- package/README.md +69 -5
- package/dist/application/dag/args.js +13 -16
- package/dist/application/dag/generate-task-dag.js +32 -2
- package/dist/application/dag/run-dag.js +1 -27
- package/dist/application/dag/validate-dag.js +2 -2
- package/dist/application/loop/run-action.js +0 -4
- package/dist/cli/command-definitions.js +7 -11
- package/dist/cli/program.js +9 -21
- package/dist/commands/cursor-prompt.js +42 -82
- package/dist/commands/dag-approve.js +36 -0
- package/dist/commands/dag-reconcile-run.js +118 -0
- package/dist/commands/delegate.js +75 -77
- package/dist/commands/doctor.js +0 -18
- package/dist/commands/init.js +60 -40
- package/dist/commands/instructions.js +7 -10
- package/dist/commands/loop.js +4 -20
- 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 +75 -9
- package/dist/governance/checks.js +6 -3
- package/dist/governance/manifest-types.js +33 -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 +353 -0
- package/dist/shared/reference-context.js +48 -22
- 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 +30 -13
- package/dist/task/delegate.js +9 -21
- package/dist/task/runtime.js +2 -3
- package/dist/worker/cli.js +243 -0
- package/dist/worker/closeout/apply.js +73 -0
- package/dist/worker/closeout/preview.js +30 -0
- package/dist/worker/delivery/final-verification.js +194 -0
- package/dist/worker/delivery/git-transaction.js +354 -0
- package/dist/worker/delivery/package.js +502 -0
- package/dist/worker/feature/decision-loader.js +68 -0
- package/dist/worker/feature/discover.js +14 -0
- package/dist/worker/feature/next-action.js +74 -0
- package/dist/worker/feature/reducer.js +133 -0
- package/dist/worker/feature/review.js +502 -0
- package/dist/worker/feature/run.js +365 -0
- package/dist/worker/feature/types.js +1 -0
- package/dist/worker/follow-up/approve.js +270 -0
- package/dist/worker/follow-up/factory.js +234 -0
- package/dist/worker/follow-up/paths.js +25 -0
- package/dist/worker/follow-up/policy.js +26 -0
- package/dist/worker/follow-up/schema.js +93 -0
- package/dist/worker/follow-up/store.js +96 -0
- package/dist/worker/loop-agent/loop-agent-client.js +345 -101
- package/dist/worker/metrics/projector.js +139 -0
- package/dist/worker/observability/read-model.js +282 -15
- package/dist/worker/observe/paths.js +17 -5
- package/dist/worker/observe/routes.js +78 -20
- package/dist/worker/observe/server.js +8 -6
- package/dist/worker/observe/static/app.js +1045 -177
- package/dist/worker/observe/static/index.html +70 -43
- package/dist/worker/observe/static/styles.css +553 -610
- package/dist/worker/pool/run-store.js +14 -2
- package/dist/worker/pool/validation.js +59 -0
- package/dist/worker/preflight.js +49 -1
- package/dist/worker/report/morning-report.js +41 -6
- package/dist/worker/run-task/run-task.js +23 -13
- package/dist/worker/runner/run-ready.js +89 -11
- package/dist/worker/task-spec/schema.js +0 -1
- package/dist/workflows/dag/convergence/controller.js +1 -1
- package/dist/workflows/dag/executor-registry.js +0 -2
- package/dist/workflows/dag/governance-profile.js +10 -0
- package/dist/workflows/dag/init-hybrid.js +601 -26
- package/dist/workflows/dag/lifecycle.js +146 -0
- package/dist/workflows/dag/node-execution.js +64 -7
- package/dist/workflows/dag/prompt.js +16 -0
- package/dist/workflows/dag/report.js +2 -0
- package/dist/workflows/dag/runner.js +176 -119
- package/dist/workflows/dag/scheduler.js +7 -2
- package/dist/workflows/dag/skill-snapshot.js +527 -0
- package/dist/workflows/dag/types.js +45 -9
- package/dist/workflows/dag/validate.js +5 -8
- 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 +35 -12
- package/docs/agent-dag-recovery-playbook.md +1 -1
- package/docs/architecture/README.md +26 -0
- package/docs/architecture/dag-execution.md +134 -0
- package/docs/architecture/evolution.md +52 -0
- package/docs/architecture/facts-and-state.md +58 -0
- package/docs/architecture/runtime-boundaries.md +41 -15
- 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 +39 -13
- package/docs/development-principles.md +1 -1
- package/docs/exec-plans/active/README.md +2 -2
- package/docs/exec-plans/completed/README.md +21 -0
- package/docs/feature-workflow.md +44 -4
- package/docs/init-surface.manifest.json +63 -1
- package/docs/loop-agent-harness.md +65 -3
- package/docs/progress/README.md +27 -0
- package/docs/reports/README.md +74 -5
- package/docs/skills/README.md +2 -1
- package/docs/skills/vetted-skill-registry.md +2 -1
- package/docs/templates/agent-dag-report.schema.json +4 -2
- 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 +1 -2
- package/docs/templates/agent-dag.supervised-implementation.json +1 -6
- package/docs/templates/backend-test-dag.generate-pytest.prompt.md +131 -0
- package/docs/templates/backend-test-dag.json +213 -0
- package/docs/templates/backend-test-dag.retrospect.prompt.md +128 -0
- package/docs/templates/backend-test-dag.review-cases.prompt.md +85 -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 +8 -5
- package/docs/templates/hybrid-dag.json +1 -6
- package/docs/templates/init-evolution-review.md +4 -2
- package/docs/templates/interactive-ui-round2-experiment.md +1 -1
- package/docs/templates/product-line/task.yaml +0 -1
- package/docs/templates/worker-dogfood-evidence.md +28 -0
- package/docs/templates/worker-dogfood-setup.md +20 -0
- package/docs/verification-matrix.md +17 -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 +6 -11
- package/package.json +22 -44
- package/scripts/check-product-line-docs.sh +10 -3
- package/scripts/check-task-pool-root.sh +1 -1
- 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 +48 -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/init-capability-evolution/SKILL.md +1 -0
- package/skills/loop-agent/SKILL.md +11 -9
- package/skills/loop-agent/references/command-reference.md +28 -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 +13 -15
- 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/verification-and-failure-handling.md +8 -3
- 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,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
|
|
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";
|
|
85
|
+
function implementationNodeId() {
|
|
86
|
+
return "implement-pi";
|
|
76
87
|
}
|
|
77
|
-
function
|
|
78
|
-
return
|
|
88
|
+
function repairNodeId() {
|
|
89
|
+
return "repair-pi";
|
|
79
90
|
}
|
|
80
|
-
function
|
|
81
|
-
return
|
|
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
|
|
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:
|
|
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(
|
|
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(
|
|
843
|
-
const repairId = repairNodeId(
|
|
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(
|
|
1472
|
+
id: repairNodeId(),
|
|
898
1473
|
depends_on: ["process-gate-shell", "process-supervisor-pi"],
|
|
899
1474
|
role: "implementer",
|
|
900
|
-
executor:
|
|
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(
|
|
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(
|
|
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
|