@tea-agent/loop-agent 0.13.0-beta.0 → 0.13.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 +157 -155
- package/CHANGELOG.md +301 -322
- package/README.md +335 -345
- package/bin/agent-worker.js +22 -22
- package/bin/loop-agent.js +21 -21
- package/dist/commands/cursor-prompt.js +6 -6
- package/dist/commands/init.js +597 -528
- package/dist/commands/loop-benchmark.js +11 -11
- package/dist/commands/pi-reuse-benchmark.js +16 -16
- package/dist/executors/shell-executor.js +200 -21
- package/dist/infrastructure/evaluation/candidate-store.js +5 -1
- package/dist/sidecars/cursor-prompt/executor.js +1 -1
- package/dist/task/runtime.js +27 -27
- package/dist/worker/observe/static/api.js +46 -46
- package/dist/worker/observe/static/app.js +150 -150
- package/dist/worker/observe/static/constants.js +148 -148
- package/dist/worker/observe/static/copy.js +67 -67
- package/dist/worker/observe/static/dag-helpers.js +172 -172
- package/dist/worker/observe/static/dag-layout.d.ts +31 -31
- package/dist/worker/observe/static/dag-layout.js +83 -83
- package/dist/worker/observe/static/dag-model.js +72 -72
- package/dist/worker/observe/static/dom.js +212 -53
- package/dist/worker/observe/static/format-pool.js +67 -67
- package/dist/worker/observe/static/format.js +292 -292
- package/dist/worker/observe/static/index.html +308 -308
- package/dist/worker/observe/static/kpi.js +94 -94
- package/dist/worker/observe/static/relations.js +133 -133
- package/dist/worker/observe/static/router.js +93 -93
- package/dist/worker/observe/static/run-processing.js +148 -148
- package/dist/worker/observe/static/shell-chrome.js +68 -68
- package/dist/worker/observe/static/state.js +267 -253
- package/dist/worker/observe/static/styles.css +1902 -1902
- package/dist/worker/observe/static/views/batch.js +227 -227
- package/dist/worker/observe/static/views/dag-graph.js +172 -172
- package/dist/worker/observe/static/views/dag-inspector.js +627 -607
- package/dist/worker/observe/static/views/dag.js +371 -362
- package/dist/worker/observe/static/views/dashboard.js +509 -252
- package/dist/worker/observe/static/views/failures.js +143 -143
- package/dist/worker/observe/static/views/feature.js +492 -492
- package/dist/worker/observe/static/views/pool.js +350 -350
- package/dist/worker/observe/static/views/run.js +453 -453
- package/dist/worker/observe/static/views/session-timeline.js +219 -205
- package/dist/worker/observe/static/views/shell.js +7 -7
- package/dist/worker/observe/static/views/task.js +314 -314
- package/dist/worker/observe/static/views/timeline.js +163 -163
- package/dist/workflows/dag/backend-test-case-manifest.js +503 -0
- package/dist/workflows/dag/backend-test-execution-contract.js +353 -0
- package/dist/workflows/dag/backend-test-result-contract.js +568 -0
- package/dist/workflows/dag/canvas-observer.js +275 -275
- package/dist/workflows/dag/decision-envelope.js +57 -2
- package/dist/workflows/dag/frontend-implementation-contract.js +240 -0
- package/dist/workflows/dag/frontend-project-capability.js +309 -0
- package/dist/workflows/dag/frontend-repair.js +341 -0
- package/dist/workflows/dag/frontend-risk.js +161 -0
- package/dist/workflows/dag/frontend-verification-trace.js +190 -0
- package/dist/workflows/dag/init-hybrid.js +1020 -125
- package/dist/workflows/dag/repair-artifact.js +43 -3
- package/dist/workflows/dag/skill-instructions.js +4 -2
- package/dist/workflows/dag/types.js +29 -8
- package/docs/README.md +105 -104
- package/docs/agent-dag-recovery-playbook.md +195 -195
- package/docs/agent-dag-runner.md +67 -67
- package/docs/architecture/README.md +26 -26
- package/docs/architecture/dag-execution.md +140 -140
- package/docs/architecture/evolution.md +54 -54
- package/docs/architecture/facts-and-state.md +71 -71
- package/docs/architecture/runtime-boundaries.md +191 -191
- package/docs/architecture/system-overview.md +93 -93
- package/docs/architecture/worker-and-feature.md +85 -85
- package/docs/cursor-prompt-sidecar.md +36 -36
- package/docs/decisions/README.md +18 -18
- package/docs/design/README.md +167 -167
- package/docs/development-principles.md +73 -73
- package/docs/exec-plans/README.md +6 -6
- package/docs/exec-plans/active/README.md +1 -4
- package/docs/exec-plans/completed/README.md +106 -84
- package/docs/feature-workflow.md +414 -389
- package/docs/harness-methodology-debugging.md +153 -153
- package/docs/harness-methodology-tdd.md +130 -130
- package/docs/harness-methodology-verification.md +27 -27
- package/docs/init-surface.manifest.json +307 -289
- package/docs/loop-agent-harness.md +142 -142
- package/docs/production-readiness.md +96 -96
- package/docs/progress/README.md +76 -60
- package/docs/reports/README.md +150 -108
- package/docs/skills/README.md +7 -7
- package/docs/skills/vetted-skill-registry.md +29 -29
- package/docs/templates/adr.md +60 -60
- package/docs/templates/agent-dag-authority-surface-audit.prompt.md +94 -94
- package/docs/templates/agent-dag-decision-envelope.schema.json +213 -213
- package/docs/templates/agent-dag-decision-gate-dogfood-report.md +117 -117
- package/docs/templates/agent-dag-decision-gate.prompt.md +246 -246
- package/docs/templates/agent-dag-process-supervisor.prompt.md +98 -98
- package/docs/templates/agent-dag-report.schema.json +473 -473
- package/docs/templates/agent-dag-review-verdict.prompt.md +68 -68
- package/docs/templates/agent-dag.base.json +190 -190
- package/docs/templates/agent-dag.final-verification.json +185 -185
- package/docs/templates/agent-dag.schema.json +411 -411
- package/docs/templates/agent-dag.supervised-implementation.json +620 -501
- package/docs/templates/backend-test-analysis.schema.json +44 -44
- package/docs/templates/backend-test-case-manifest.schema.json +190 -0
- package/docs/templates/backend-test-dag.classify.prompt.md +75 -0
- package/docs/templates/backend-test-dag.generate-pytest.prompt.md +204 -202
- package/docs/templates/backend-test-dag.json +559 -311
- package/docs/templates/backend-test-dag.retrospect.prompt.md +139 -125
- package/docs/templates/backend-test-dag.review-cases.prompt.md +83 -81
- package/docs/templates/backend-test-execution.schema.json +133 -0
- package/docs/templates/backend-test-result.schema.json +99 -0
- package/docs/templates/branch-merge-report.md +93 -0
- package/docs/templates/exec-plan.md +64 -64
- package/docs/templates/feature-spec.md +53 -53
- package/docs/templates/frontend-design-contract.md +42 -42
- package/docs/templates/frontend-eval/fixtures/failures/01-type-build-error.md +17 -0
- package/docs/templates/frontend-eval/fixtures/failures/02-unit-component-test-fail.md +16 -0
- package/docs/templates/frontend-eval/fixtures/failures/03-fixture-schema-drift.md +16 -0
- package/docs/templates/frontend-eval/fixtures/failures/04-missing-loading-empty-error-state.md +16 -0
- package/docs/templates/frontend-eval/fixtures/failures/05-forbidden-write-writeset-expansion.md +16 -0
- package/docs/templates/frontend-eval/fixtures/failures/06-unapproved-dependency-add.md +16 -0
- package/docs/templates/frontend-eval/fixtures/failures/07-mock-production-on.md +21 -0
- package/docs/templates/frontend-eval/fixtures/functional/01-simple-component-style.md +29 -0
- package/docs/templates/frontend-eval/fixtures/functional/02-form-validation.md +28 -0
- package/docs/templates/frontend-eval/fixtures/functional/03-list-detail-page.md +28 -0
- package/docs/templates/frontend-eval/fixtures/functional/04-api-mock.md +29 -0
- package/docs/templates/frontend-eval/fixtures/functional/05-permission-auth-gated-ui.md +27 -0
- package/docs/templates/frontend-eval/fixtures/functional/06-ssr-server-client-boundary.md +28 -0
- package/docs/templates/frontend-eval/fixtures/functional/07-shared-public-component-api.md +28 -0
- package/docs/templates/frontend-eval/fixtures/functional/08-pure-local-no-remote.md +27 -0
- package/docs/templates/frontend-eval/metrics.md +138 -0
- package/docs/templates/frontend-eval/smoke-targets.md +53 -0
- package/docs/templates/frontend-implementation-contract.schema.json +27 -0
- package/docs/templates/frontend-task-constraints.md +35 -35
- package/docs/templates/frontend-task-requirement.md +70 -70
- package/docs/templates/frontend-test-dag.generate-cases.prompt.md +5 -5
- package/docs/templates/frontend-test-dag.json +23 -23
- package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +3 -3
- package/docs/templates/frontend-test-dag.retrospect.prompt.md +3 -3
- package/docs/templates/frontend-test-dag.review-cases.prompt.md +3 -3
- package/docs/templates/frontend-test-dag.review-execution.prompt.md +3 -3
- package/docs/templates/harness.schema.json +221 -221
- package/docs/templates/hybrid-dag.json +188 -188
- package/docs/templates/init-evolution-review.md +35 -35
- package/docs/templates/interactive-ui-round2-experiment.md +66 -66
- package/docs/templates/knowledge-graph-bootstrap-dag.json +118 -118
- package/docs/templates/knowledge-sync-dag.json +178 -178
- package/docs/templates/knowledge-sync-draft.schema.json +71 -71
- package/docs/templates/product-line/AGENTS.md +8 -8
- package/docs/templates/product-line/README.md +9 -9
- package/docs/templates/product-line/acceptance.yaml +14 -14
- package/docs/templates/product-line/closeout.yaml +9 -9
- package/docs/templates/product-line/design.md +13 -13
- package/docs/templates/product-line/links.md +10 -10
- package/docs/templates/product-line/requirement.md +17 -17
- package/docs/templates/product-line/task-graph.yaml +15 -15
- package/docs/templates/product-line/task.yaml +64 -64
- package/docs/templates/product-line/test-plan.md +7 -7
- package/docs/templates/production-readiness-checklist.md +57 -57
- package/docs/templates/progress-log.md +17 -17
- package/docs/templates/project-start-checklist.md +9 -9
- package/docs/templates/qa-report.md +48 -48
- package/docs/templates/sprint-contract.md +29 -29
- package/docs/templates/worker-dogfood-evidence.md +80 -80
- package/docs/templates/worker-dogfood-setup.md +68 -68
- package/docs/verification-matrix.md +70 -70
- package/examples/decision-gate-agent-dag.json +177 -177
- package/examples/example-dag.json +46 -46
- package/examples/hybrid-loop-agent-dag.json +189 -189
- package/harness.json +66 -66
- package/package.json +52 -88
- package/scripts/check-product-line-docs.sh +29 -29
- package/scripts/check-task-pool-root.sh +32 -32
- package/scripts/kb-bootstrap-init-skeleton.sh +240 -240
- package/scripts/kb-graph-incremental-prepare.mjs +386 -386
- package/scripts/kb-graph-incremental-prepare.sh +5 -5
- package/scripts/kb-graph-materialize.mjs +105 -105
- package/scripts/kb-graph-materialize.sh +4 -4
- package/scripts/kb-graph-promote.mjs +164 -164
- package/scripts/kb-graph-promote.sh +4 -4
- package/scripts/kb-query.mjs +554 -554
- package/scripts/kb-query.sh +5 -5
- package/skills/agent-worker/SKILL.md +39 -39
- package/skills/agent-worker/references/agent-worker-operator.md +60 -60
- package/skills/ai-engineering-context/SKILL.md +48 -48
- package/skills/analyze-product-dependencies/SKILL.md +67 -67
- package/skills/analyze-product-dependencies/agents/openai.yaml +4 -4
- package/skills/analyze-product-dependencies/references/api-documentation-schema.md +30 -30
- package/skills/analyze-product-dependencies/references/dependency-analysis-schema.md +28 -28
- package/skills/analyze-product-dependencies/references/example.md +76 -76
- package/skills/analyze-product-dependencies/references/forward-test-cases.md +35 -35
- package/skills/analyze-product-dependencies/references/input-contract.md +11 -11
- package/skills/analyze-product-dependencies/references/scouting-rules.md +61 -61
- package/skills/analyze-product-dependencies/scripts/test-validators.mjs +267 -267
- package/skills/analyze-product-dependencies/scripts/validate-api-documentation.mjs +101 -101
- package/skills/analyze-product-dependencies/scripts/validate-dependency-analysis.mjs +142 -142
- package/skills/analyze-product-dependencies/scripts/validate-product-requirement-input.mjs +76 -76
- package/skills/analyze-product-dependencies/scripts/validation-helpers.mjs +146 -146
- package/skills/analyze-product-requirements/SKILL.md +90 -90
- package/skills/analyze-product-requirements/agents/openai.yaml +4 -4
- package/skills/analyze-product-requirements/references/acceptance-criteria.md +91 -91
- package/skills/analyze-product-requirements/references/clarification-and-knowledge.md +56 -56
- package/skills/analyze-product-requirements/references/example.md +86 -86
- package/skills/analyze-product-requirements/references/forward-test-cases.md +66 -66
- package/skills/analyze-product-requirements/references/product-analysis-schema.md +32 -32
- package/skills/analyze-product-requirements/references/product-requirement-schema.md +33 -33
- package/skills/analyze-product-requirements/references/requirement-clarification-schema.md +35 -35
- package/skills/analyze-product-requirements/scripts/test-validators.mjs +193 -193
- package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +69 -69
- package/skills/analyze-product-requirements/scripts/validate-product-requirement.mjs +97 -97
- package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +98 -98
- package/skills/analyze-product-requirements/scripts/validation-helpers.mjs +156 -156
- package/skills/browser-tools/SKILL.md +196 -0
- package/skills/browser-tools/browser-content.js +103 -0
- package/skills/browser-tools/browser-cookies.js +35 -0
- package/skills/browser-tools/browser-eval.js +53 -0
- package/skills/browser-tools/browser-hn-scraper.js +108 -0
- package/skills/browser-tools/browser-nav.js +44 -0
- package/skills/browser-tools/browser-pick.js +162 -0
- package/skills/browser-tools/browser-screenshot.js +34 -0
- package/skills/browser-tools/browser-start.js +86 -0
- package/skills/browser-tools/package-lock.json +2556 -0
- package/skills/browser-tools/package.json +19 -0
- package/skills/code-review-core/SKILL.md +20 -20
- package/skills/codebase-scout/SKILL.md +19 -19
- package/skills/frontend-design-review/SKILL.md +66 -66
- package/skills/frontend-design-review/references/review-checklist.md +58 -58
- package/skills/frontend-implementation/SKILL.md +49 -47
- package/skills/frontend-implementation/references/code-standards.md +32 -32
- package/skills/frontend-implementation/references/design-spec.md +46 -46
- package/skills/frontend-implementation/references/node-contracts.md +27 -76
- package/skills/frontend-review/SKILL.md +59 -59
- package/skills/frontend-review/references/review-findings.md +47 -47
- package/skills/frontend-verification/SKILL.md +53 -53
- package/skills/frontend-verification/references/verification-checklist.md +68 -68
- package/skills/grill-me/SKILL.md +10 -10
- package/skills/grill-with-docs/SKILL.md +88 -88
- package/skills/grill-with-docs/adr-format.md +47 -47
- package/skills/grill-with-docs/context-format.md +60 -60
- package/skills/init-capability-evolution/SKILL.md +70 -70
- package/skills/loop-agent/SKILL.md +151 -151
- package/skills/loop-agent/references/README.md +67 -67
- package/skills/loop-agent/references/command-reference.md +527 -505
- package/skills/loop-agent/references/docs-converge.md +126 -126
- package/skills/loop-agent/references/harness-policy.md +263 -263
- package/skills/loop-agent/references/hybrid-dag.md +243 -238
- package/skills/loop-agent/references/learned/README.md +21 -21
- package/skills/loop-agent/references/long-running-loop.md +57 -57
- package/skills/loop-agent/references/model-routing.md +36 -36
- package/skills/loop-agent/references/multi-worktree.md +54 -54
- package/skills/loop-agent/references/one-shot-runs.md +85 -85
- package/skills/loop-agent/references/orchestrator-and-interventions.md +169 -169
- package/skills/loop-agent/references/pi-prompt.md +23 -23
- package/skills/loop-agent/references/pi-subagent-assisted-mode.md +84 -84
- package/skills/loop-agent/references/post-implementation-and-patterns.md +44 -44
- package/skills/loop-agent/references/task-workflow.md +89 -89
- package/skills/loop-agent/references/verification-and-failure-handling.md +141 -139
- package/skills/playwright-cli/SKILL.md +420 -420
- package/skills/playwright-cli/references/element-attributes.md +23 -23
- package/skills/playwright-cli/references/playwright-tests.md +39 -39
- package/skills/playwright-cli/references/request-mocking.md +87 -87
- package/skills/playwright-cli/references/running-code.md +241 -241
- package/skills/playwright-cli/references/session-management.md +225 -225
- package/skills/playwright-cli/references/storage-state.md +275 -275
- package/skills/playwright-cli/references/test-generation.md +433 -433
- package/skills/playwright-cli/references/tracing.md +139 -139
- package/skills/playwright-cli/references/video-recording.md +143 -143
- package/skills/playwright-cli-case-generator/SKILL.md +74 -74
- package/skills/requesting-code-review/SKILL.md +101 -101
- package/skills/requesting-code-review/code-reviewer.md +168 -168
- package/skills/systematic-debugging/CREATION-LOG.md +119 -119
- package/skills/systematic-debugging/SKILL.md +296 -296
- package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
- package/skills/systematic-debugging/condition-based-waiting.md +115 -115
- package/skills/systematic-debugging/defense-in-depth.md +122 -122
- package/skills/systematic-debugging/find-polluter.sh +63 -63
- package/skills/systematic-debugging/root-cause-tracing.md +169 -169
- package/skills/systematic-debugging/test-academic.md +14 -14
- package/skills/systematic-debugging/test-pressure-1.md +58 -58
- package/skills/systematic-debugging/test-pressure-2.md +68 -68
- package/skills/systematic-debugging/test-pressure-3.md +69 -69
- package/skills/test-driven-development/SKILL.md +20 -20
- package/skills/using-git-worktrees/SKILL.md +215 -215
- package/skills/verification-before-completion/SKILL.md +154 -154
- package/skills/webapp-testing/SKILL.md +19 -19
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { access, readdir, readFile, realpath, writeFile } from "node:fs/promises";
|
|
2
|
+
import { access, readdir, readFile, realpath, writeFile, } from "node:fs/promises";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { assertValidDagSpec } from "./validate.js";
|
|
6
6
|
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";
|
|
7
7
|
import { pathMatchesPattern } from "../../shared/git-progress.js";
|
|
8
8
|
import { BASELINE_FORBIDDEN_PATHS } from "./governance-constants.js";
|
|
9
|
-
import {
|
|
9
|
+
import { buildDecisionEnvelopePromptContract } from "./decision-envelope.js";
|
|
10
|
+
import { DEFAULT_READ_ONLY_PI_RETRY_POLICY, isSafeReadOnlyPiRetryCandidate, } from "./retry-policy.js";
|
|
10
11
|
import { resolveAdapter } from "../../adapters/index.js";
|
|
11
12
|
import { loadHarnessManifest } from "../../governance/harness.js";
|
|
12
13
|
import { buildAuthoritySurfaceAuditNode, buildAuthoritySurfaceGateNode, resolveAuthoritySurfaceAudit, } from "./authority-surface.js";
|
|
@@ -16,6 +17,10 @@ import { materializeTaskReferenceDocs } from "../../task/source-references.js";
|
|
|
16
17
|
import { resolveVerifyPreset } from "../../executors/shell-verification.js";
|
|
17
18
|
import { resolveExecutorModelMatrices } from "../../executors/model-routing.js";
|
|
18
19
|
import { normalizeTaskRequirementText, resolveTaskDagTemplateSelection, } from "./task-demand-routing.js";
|
|
20
|
+
import { BACKEND_TEST_EXECUTION_DEFAULT_TEST_ROOT, buildBackendTestExecutionPreflightShellSnippet, } from "./backend-test-execution-contract.js";
|
|
21
|
+
import { buildBackendTestOutcomeGateShellSnippet } from "./backend-test-result-contract.js";
|
|
22
|
+
import { classifyFrontendRisk, } from "./frontend-risk.js";
|
|
23
|
+
import { discoverFrontendProjectCapability, } from "./frontend-project-capability.js";
|
|
19
24
|
const REQUIREMENT_FILE = "需求.md";
|
|
20
25
|
const CONSTRAINT_FILE = "执行约束.md";
|
|
21
26
|
const REFERENCE_DIRECTORY = "references";
|
|
@@ -185,7 +190,10 @@ async function hasDirectDependency(repoRoot, depName) {
|
|
|
185
190
|
try {
|
|
186
191
|
const raw = await readFile(path.join(repoRoot, "package.json"), "utf-8");
|
|
187
192
|
const pkg = JSON.parse(raw);
|
|
188
|
-
const deps = {
|
|
193
|
+
const deps = {
|
|
194
|
+
...(pkg.dependencies ?? {}),
|
|
195
|
+
...(pkg.devDependencies ?? {}),
|
|
196
|
+
};
|
|
189
197
|
return depName in deps;
|
|
190
198
|
}
|
|
191
199
|
catch {
|
|
@@ -207,7 +215,10 @@ async function discoverMockHandlerFiles(repoRoot, serviceRoot) {
|
|
|
207
215
|
"mocks/browser.ts",
|
|
208
216
|
"mocks/browser.js",
|
|
209
217
|
].map((candidatePath) => ({ framework: "msw", path: candidatePath })),
|
|
210
|
-
...["db.json", "mock/db.json", "src/mock/db.json"].map((candidatePath) => ({
|
|
218
|
+
...["db.json", "mock/db.json", "src/mock/db.json"].map((candidatePath) => ({
|
|
219
|
+
framework: "json-server",
|
|
220
|
+
path: candidatePath,
|
|
221
|
+
})),
|
|
211
222
|
];
|
|
212
223
|
const directoryCandidates = [
|
|
213
224
|
...(serviceRoot ? [{ path: serviceRoot }] : []),
|
|
@@ -331,7 +342,13 @@ export async function discoverFrontendMockCapability(repoRoot, taskConfig) {
|
|
|
331
342
|
// A script alone is not strong evidence unless we also find config/entry
|
|
332
343
|
}
|
|
333
344
|
// 2. Check for direct mock framework dependencies
|
|
334
|
-
const mockDeps = [
|
|
345
|
+
const mockDeps = [
|
|
346
|
+
"msw",
|
|
347
|
+
"mockjs",
|
|
348
|
+
"miragejs",
|
|
349
|
+
"json-server",
|
|
350
|
+
"vite-plugin-mock",
|
|
351
|
+
];
|
|
335
352
|
const foundDeps = [];
|
|
336
353
|
for (const dep of mockDeps) {
|
|
337
354
|
if (await hasDirectDependency(repoRoot, dep)) {
|
|
@@ -385,7 +402,9 @@ export async function discoverFrontendMockCapability(repoRoot, taskConfig) {
|
|
|
385
402
|
if (foundDeps.length > 0 && handlerResult.paths.length === 0) {
|
|
386
403
|
reasons.push(`Mock dependency found (${foundDeps.join(", ")}) but no handler/bootstrap files detected`);
|
|
387
404
|
}
|
|
388
|
-
if (scriptResult.hasScript &&
|
|
405
|
+
if (scriptResult.hasScript &&
|
|
406
|
+
handlerResult.paths.length === 0 &&
|
|
407
|
+
foundDeps.length === 0) {
|
|
389
408
|
reasons.push("Mock scripts exist but no handler files or direct mock dependencies found");
|
|
390
409
|
}
|
|
391
410
|
}
|
|
@@ -396,8 +415,11 @@ export async function discoverFrontendMockCapability(repoRoot, taskConfig) {
|
|
|
396
415
|
cwd: repoRoot,
|
|
397
416
|
timeoutMs: cmd.timeoutMs,
|
|
398
417
|
}));
|
|
399
|
-
const verifyCommands = [
|
|
400
|
-
|
|
418
|
+
const verifyCommands = [
|
|
419
|
+
...configuredVerifyCommands,
|
|
420
|
+
...scriptResult.verifyCommands,
|
|
421
|
+
].filter((command, index, commands) => commands.findIndex((candidate) => candidate.label === command.label) ===
|
|
422
|
+
index);
|
|
401
423
|
if (strongEvidenceCount > 0) {
|
|
402
424
|
return {
|
|
403
425
|
status: "present",
|
|
@@ -475,7 +497,10 @@ async function frontendMockServiceRootSafetyViolation(repoRoot, taskConfig) {
|
|
|
475
497
|
if (!frontendMockServiceRootAllowed({
|
|
476
498
|
...taskConfig,
|
|
477
499
|
frontendMock: {
|
|
478
|
-
...(taskConfig.frontendMock ?? {
|
|
500
|
+
...(taskConfig.frontendMock ?? {
|
|
501
|
+
policy: "auto",
|
|
502
|
+
verifyCommands: [],
|
|
503
|
+
}),
|
|
479
504
|
serviceRoot: normalizedRealPath,
|
|
480
505
|
},
|
|
481
506
|
})) {
|
|
@@ -504,8 +529,7 @@ export function hasApiDependency(sources) {
|
|
|
504
529
|
if (sources.taskConfig.frontendMock?.policy === "required") {
|
|
505
530
|
return true;
|
|
506
531
|
}
|
|
507
|
-
const requirement = normalizeTaskRequirementText(sources.requirementMarkdown)
|
|
508
|
-
.replace(/`[^`\n]*`/g, " ");
|
|
532
|
+
const requirement = normalizeTaskRequirementText(sources.requirementMarkdown).replace(/`[^`\n]*`/g, " ");
|
|
509
533
|
const dependencyPatterns = [
|
|
510
534
|
/(?:接口文档|接口定义|接口协议|后端接口|服务端接口|接口联调|请求|响应|远程数据|异步数据|数据获取|模拟接口|模拟数据)/,
|
|
511
535
|
/(?<![A-Za-z0-9_])API(?![A-Za-z0-9_])/i,
|
|
@@ -539,7 +563,8 @@ export function hasApiDependency(sources) {
|
|
|
539
563
|
export function resolveFrontendMockMode(capability, taskConfig, hasApiDep) {
|
|
540
564
|
const policy = taskConfig.frontendMock?.policy ?? "auto";
|
|
541
565
|
const hasDeterministicMockVerification = capability.verifyCommands.length > 0;
|
|
542
|
-
if (capability.safetyViolation ||
|
|
566
|
+
if (capability.safetyViolation ||
|
|
567
|
+
!frontendMockServiceRootAllowed(taskConfig)) {
|
|
543
568
|
return "blocked";
|
|
544
569
|
}
|
|
545
570
|
// disabled policy: must respect project mock rules (can't override spec)
|
|
@@ -615,7 +640,8 @@ function isMetadataLine(line) {
|
|
|
615
640
|
}
|
|
616
641
|
if (/^(权威来源|SHA-256|冲突时以)/.test(trimmed))
|
|
617
642
|
return true;
|
|
618
|
-
if (/^>/.test(trimmed) &&
|
|
643
|
+
if (/^>/.test(trimmed) &&
|
|
644
|
+
/(权威来源|SHA-256|原始 PRD|reference)/i.test(trimmed)) {
|
|
619
645
|
return true;
|
|
620
646
|
}
|
|
621
647
|
return false;
|
|
@@ -702,7 +728,9 @@ function shellQuote(value) {
|
|
|
702
728
|
}
|
|
703
729
|
function verifyCommandToShell(repoRoot, command) {
|
|
704
730
|
const relativeCwd = path.relative(repoRoot, command.cwd);
|
|
705
|
-
const cwdPrefix = relativeCwd &&
|
|
731
|
+
const cwdPrefix = relativeCwd &&
|
|
732
|
+
!relativeCwd.startsWith("..") &&
|
|
733
|
+
!path.isAbsolute(relativeCwd)
|
|
706
734
|
? `cd ${shellQuote(relativeCwd)} && `
|
|
707
735
|
: command.cwd !== repoRoot
|
|
708
736
|
? `cd ${shellQuote(command.cwd)} && `
|
|
@@ -851,9 +879,7 @@ function deriveFrontendBehaviorPaths(taskConfig) {
|
|
|
851
879
|
return taskConfig.allowedPaths;
|
|
852
880
|
}
|
|
853
881
|
function toTaskRelativeSourcePath(sources, absolutePath) {
|
|
854
|
-
return path
|
|
855
|
-
.relative(sources.taskDir, absolutePath)
|
|
856
|
-
.replaceAll(path.sep, "/");
|
|
882
|
+
return path.relative(sources.taskDir, absolutePath).replaceAll(path.sep, "/");
|
|
857
883
|
}
|
|
858
884
|
function extractExplicitRequirementIds(...markdownInputs) {
|
|
859
885
|
const ids = [];
|
|
@@ -873,9 +899,25 @@ function extractExplicitRequirementIds(...markdownInputs) {
|
|
|
873
899
|
}
|
|
874
900
|
function buildDagSourceBinding(sources) {
|
|
875
901
|
const sourceEntries = [
|
|
876
|
-
{
|
|
877
|
-
|
|
878
|
-
|
|
902
|
+
{
|
|
903
|
+
kind: "requirement",
|
|
904
|
+
path: sources.requirementPath,
|
|
905
|
+
markdown: sources.requirementMarkdown,
|
|
906
|
+
},
|
|
907
|
+
...(sources.constraintMarkdown
|
|
908
|
+
? [
|
|
909
|
+
{
|
|
910
|
+
kind: "constraint",
|
|
911
|
+
path: sources.constraintPath,
|
|
912
|
+
markdown: sources.constraintMarkdown,
|
|
913
|
+
},
|
|
914
|
+
]
|
|
915
|
+
: []),
|
|
916
|
+
...(sources.referenceDocuments ?? []).map((reference) => ({
|
|
917
|
+
kind: "reference",
|
|
918
|
+
path: reference.path,
|
|
919
|
+
markdown: reference.markdown,
|
|
920
|
+
})),
|
|
879
921
|
];
|
|
880
922
|
return {
|
|
881
923
|
schemaVersion: 1,
|
|
@@ -883,7 +925,9 @@ function buildDagSourceBinding(sources) {
|
|
|
883
925
|
sources: sourceEntries.map((source) => ({
|
|
884
926
|
kind: source.kind,
|
|
885
927
|
path: toTaskRelativeSourcePath(sources, source.path),
|
|
886
|
-
sha256: createHash("sha256")
|
|
928
|
+
sha256: createHash("sha256")
|
|
929
|
+
.update(source.markdown, "utf8")
|
|
930
|
+
.digest("hex"),
|
|
887
931
|
})),
|
|
888
932
|
requirementIds: extractExplicitRequirementIds(sources.requirementMarkdown, sources.constraintMarkdown, ...(sources.referenceDocuments ?? []).map((reference) => reference.markdown)),
|
|
889
933
|
};
|
|
@@ -893,10 +937,7 @@ function buildSourceContextBlock(sources) {
|
|
|
893
937
|
const requirementExcerpt = excerptMarkdown(sources.requirementMarkdown, {
|
|
894
938
|
sourceRef: requirementRef,
|
|
895
939
|
});
|
|
896
|
-
const parts = [
|
|
897
|
-
"## Task source: 需求.md",
|
|
898
|
-
requirementExcerpt.text,
|
|
899
|
-
];
|
|
940
|
+
const parts = ["## Task source: 需求.md", requirementExcerpt.text];
|
|
900
941
|
if (sources.constraintMarkdown) {
|
|
901
942
|
const constraintRef = toTaskRelativeSourcePath(sources, sources.constraintPath);
|
|
902
943
|
const constraintExcerpt = excerptMarkdown(sources.constraintMarkdown, {
|
|
@@ -940,7 +981,8 @@ async function loadMaterializedSourceReferences(sourceDir) {
|
|
|
940
981
|
}
|
|
941
982
|
else if (entry.isFile()) {
|
|
942
983
|
// Skip index/manifest sidecars; keep only user/source reference content.
|
|
943
|
-
if (entry.name === "index.json" ||
|
|
984
|
+
if (entry.name === "index.json" ||
|
|
985
|
+
entry.name === "source-manifest.json") {
|
|
944
986
|
continue;
|
|
945
987
|
}
|
|
946
988
|
referencePaths.push(entryPath);
|
|
@@ -949,7 +991,9 @@ async function loadMaterializedSourceReferences(sourceDir) {
|
|
|
949
991
|
}
|
|
950
992
|
await collect(referenceDir);
|
|
951
993
|
referencePaths.sort((left, right) => left.localeCompare(right));
|
|
952
|
-
return Promise.all(referencePaths
|
|
994
|
+
return Promise.all(referencePaths
|
|
995
|
+
.slice(0, MAX_SOURCE_REFERENCE_DOCUMENTS)
|
|
996
|
+
.map(async (filePath) => ({
|
|
953
997
|
path: filePath,
|
|
954
998
|
markdown: await readFile(filePath, "utf-8"),
|
|
955
999
|
})));
|
|
@@ -1043,10 +1087,26 @@ async function prepareFrontendMockSources(sources) {
|
|
|
1043
1087
|
capability.verifyCommands.push(command);
|
|
1044
1088
|
}
|
|
1045
1089
|
}
|
|
1090
|
+
const projectCapability = await discoverFrontendProjectCapability(repoRoot);
|
|
1091
|
+
const frontendRisk = classifyFrontendRisk({
|
|
1092
|
+
title: sources.taskConfig.title,
|
|
1093
|
+
requirementMarkdown: sources.requirementMarkdown,
|
|
1094
|
+
constraintMarkdown: sources.constraintMarkdown ?? undefined,
|
|
1095
|
+
allowedPaths: sources.taskConfig.allowedPaths,
|
|
1096
|
+
forbiddenPaths: sources.taskConfig.forbiddenPaths,
|
|
1097
|
+
complexity: sources.taskConfig.complexity,
|
|
1098
|
+
manifestEvidence: [
|
|
1099
|
+
projectCapability.framework,
|
|
1100
|
+
projectCapability.frameworkVersion ?? "",
|
|
1101
|
+
projectCapability.adapterGuidance,
|
|
1102
|
+
].join("\n"),
|
|
1103
|
+
});
|
|
1046
1104
|
return {
|
|
1047
1105
|
...sources,
|
|
1048
1106
|
frontendMockCapability: capability,
|
|
1049
1107
|
frontendMockMode: resolveFrontendMockMode(capability, sources.taskConfig, hasApiDependency(sources)),
|
|
1108
|
+
frontendProjectCapability: projectCapability,
|
|
1109
|
+
frontendRisk,
|
|
1050
1110
|
};
|
|
1051
1111
|
}
|
|
1052
1112
|
function mergeFinalVerifyCommands(repoRoot, taskConfig, adapterCommands) {
|
|
@@ -1080,14 +1140,17 @@ export function buildStandardHybridDagFromTask(sources) {
|
|
|
1080
1140
|
fallbackCommands: [],
|
|
1081
1141
|
});
|
|
1082
1142
|
const verifyShellTask = verifyShellCommands.length > 0
|
|
1083
|
-
? [
|
|
1143
|
+
? [
|
|
1144
|
+
{
|
|
1084
1145
|
id: "verify-shell",
|
|
1085
1146
|
depends_on: [implementId],
|
|
1086
1147
|
role: "verifier",
|
|
1087
1148
|
executor: "shell",
|
|
1088
1149
|
complexity: "LOW",
|
|
1089
1150
|
writePolicy: "read-only",
|
|
1090
|
-
allowedPaths: taskConfig.allowedPaths.length > 0
|
|
1151
|
+
allowedPaths: taskConfig.allowedPaths.length > 0
|
|
1152
|
+
? taskConfig.allowedPaths
|
|
1153
|
+
: ["**"],
|
|
1091
1154
|
forbiddenPaths,
|
|
1092
1155
|
outputContract: "Archived final shell verification stdout/stderr with exit codes; no worktree writes.",
|
|
1093
1156
|
subtask_prompt: "Run the adapter-resolved final verification commands before read-only verification review.",
|
|
@@ -1104,7 +1167,8 @@ export function buildStandardHybridDagFromTask(sources) {
|
|
|
1104
1167
|
cwd: ".",
|
|
1105
1168
|
timeoutMs: 300000,
|
|
1106
1169
|
},
|
|
1107
|
-
}
|
|
1170
|
+
},
|
|
1171
|
+
]
|
|
1108
1172
|
: [];
|
|
1109
1173
|
const sourceContext = buildSourceContextBlock(sources);
|
|
1110
1174
|
const globalConstraints = [
|
|
@@ -1209,7 +1273,9 @@ export function buildStandardHybridDagFromTask(sources) {
|
|
|
1209
1273
|
"Stay within writeSet. Do not write root artifacts/** unless artifacts paths are explicitly declared in writeSet.",
|
|
1210
1274
|
writerDeliveryContract(taskConfig),
|
|
1211
1275
|
sourceContext,
|
|
1212
|
-
]
|
|
1276
|
+
]
|
|
1277
|
+
.filter((value) => Boolean(value))
|
|
1278
|
+
.join("\n\n"),
|
|
1213
1279
|
},
|
|
1214
1280
|
...verifyShellTask,
|
|
1215
1281
|
{
|
|
@@ -1505,13 +1571,131 @@ function resolveFrontendMockContextBlock(sources) {
|
|
|
1505
1571
|
}
|
|
1506
1572
|
return parts.join("\n");
|
|
1507
1573
|
}
|
|
1574
|
+
function resolveFrontendCapabilityContextBlock(sources) {
|
|
1575
|
+
const risk = sources.frontendRisk;
|
|
1576
|
+
const capability = sources.frontendProjectCapability;
|
|
1577
|
+
const parts = ["## Frontend risk & project capability", ""];
|
|
1578
|
+
if (risk) {
|
|
1579
|
+
parts.push(`Risk level: ${risk.selectedRisk}`, `Risk reason: ${risk.reason}`, `Risk signals: ${risk.signals.join(", ") || "(none)"}`, `Rejected signals: ${risk.rejectedSignals.join(", ") || "(none)"}`, `Force full gates: ${risk.forceFullGates}`);
|
|
1580
|
+
}
|
|
1581
|
+
else {
|
|
1582
|
+
parts.push("Risk level: standard (not precomputed)");
|
|
1583
|
+
}
|
|
1584
|
+
if (capability) {
|
|
1585
|
+
parts.push("", capability.adapterGuidance);
|
|
1586
|
+
if (capability.designEvidence.normativePaths.length > 0) {
|
|
1587
|
+
parts.push(`openSpec normative candidates: ${capability.designEvidence.normativePaths.slice(0, 12).join(", ")}`);
|
|
1588
|
+
}
|
|
1589
|
+
parts.push(`A11y capability: ${capability.a11y.status}` +
|
|
1590
|
+
(capability.a11y.tools.length
|
|
1591
|
+
? ` (${capability.a11y.tools.join(", ")})`
|
|
1592
|
+
: ""));
|
|
1593
|
+
parts.push("Browser accessibility verification: not-run (out of scope for this workflow).");
|
|
1594
|
+
}
|
|
1595
|
+
return parts.join("\n");
|
|
1596
|
+
}
|
|
1597
|
+
function pruneFrontendTasksForRisk(tasks, risk) {
|
|
1598
|
+
if (risk.forceFullGates || risk.selectedRisk !== "small") {
|
|
1599
|
+
return tasks;
|
|
1600
|
+
}
|
|
1601
|
+
// small topology: drop first design gate + plan revision + second design cycle;
|
|
1602
|
+
// keep mock assess/gate, single design review (final), contract, implement, verify, repair chain, review.
|
|
1603
|
+
const drop = new Set([
|
|
1604
|
+
"frontend-design-gate-pi",
|
|
1605
|
+
"frontend-first-design-gate-shell",
|
|
1606
|
+
"frontend-plan-revision-pi",
|
|
1607
|
+
]);
|
|
1608
|
+
// If we drop plan-revision, contract shell must depend on plan-pi instead; final design review depends on plan.
|
|
1609
|
+
const filtered = tasks.filter((task) => !drop.has(task.id));
|
|
1610
|
+
const byId = new Map(filtered.map((task) => [task.id, task]));
|
|
1611
|
+
const remap = (deps) => {
|
|
1612
|
+
if (!deps)
|
|
1613
|
+
return [];
|
|
1614
|
+
const next = [];
|
|
1615
|
+
for (const dep of deps) {
|
|
1616
|
+
if (dep === "frontend-plan-revision-pi") {
|
|
1617
|
+
if (byId.has("frontend-plan-pi"))
|
|
1618
|
+
next.push("frontend-plan-pi");
|
|
1619
|
+
continue;
|
|
1620
|
+
}
|
|
1621
|
+
if (dep === "frontend-first-design-gate-shell" ||
|
|
1622
|
+
dep === "frontend-design-gate-pi") {
|
|
1623
|
+
// skip removed gates
|
|
1624
|
+
continue;
|
|
1625
|
+
}
|
|
1626
|
+
if (byId.has(dep) || dep === "frontend-implement-pi")
|
|
1627
|
+
next.push(dep);
|
|
1628
|
+
}
|
|
1629
|
+
return [...new Set(next)];
|
|
1630
|
+
};
|
|
1631
|
+
return filtered.map((task) => {
|
|
1632
|
+
const depends_on = remap(task.depends_on);
|
|
1633
|
+
// Ensure final design review still has plan + mock + contract path
|
|
1634
|
+
if (task.id === "frontend-final-design-review-pi") {
|
|
1635
|
+
for (const need of [
|
|
1636
|
+
"frontend-plan-pi",
|
|
1637
|
+
"frontend-mock-assess-pi",
|
|
1638
|
+
"frontend-implementation-contract-shell",
|
|
1639
|
+
]) {
|
|
1640
|
+
if (byId.has(need) && !depends_on.includes(need))
|
|
1641
|
+
depends_on.push(need);
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
if (task.id === "frontend-requirement-coverage-shell") {
|
|
1645
|
+
const shell = task.shell?.requirementCoverageGate
|
|
1646
|
+
? {
|
|
1647
|
+
...task.shell,
|
|
1648
|
+
requirementCoverageGate: {
|
|
1649
|
+
...task.shell.requirementCoverageGate,
|
|
1650
|
+
fromNodeIds: task.shell.requirementCoverageGate.fromNodeIds.map((nodeId) => nodeId === "frontend-plan-revision-pi"
|
|
1651
|
+
? "frontend-plan-pi"
|
|
1652
|
+
: nodeId),
|
|
1653
|
+
},
|
|
1654
|
+
}
|
|
1655
|
+
: task.shell;
|
|
1656
|
+
return { ...task, depends_on, shell };
|
|
1657
|
+
}
|
|
1658
|
+
if (task.id === "frontend-implementation-contract-shell") {
|
|
1659
|
+
const nextDeps = depends_on.filter((dep) => dep !== "frontend-plan-revision-pi");
|
|
1660
|
+
if (!nextDeps.includes("frontend-plan-pi") &&
|
|
1661
|
+
byId.has("frontend-plan-pi")) {
|
|
1662
|
+
nextDeps.push("frontend-plan-pi");
|
|
1663
|
+
}
|
|
1664
|
+
const shell = task.shell
|
|
1665
|
+
? {
|
|
1666
|
+
...task.shell,
|
|
1667
|
+
jsonArtifactGate: task.shell.jsonArtifactGate
|
|
1668
|
+
? {
|
|
1669
|
+
...task.shell.jsonArtifactGate,
|
|
1670
|
+
fromNodeId: "frontend-plan-pi",
|
|
1671
|
+
}
|
|
1672
|
+
: task.shell.jsonArtifactGate,
|
|
1673
|
+
}
|
|
1674
|
+
: task.shell;
|
|
1675
|
+
return { ...task, depends_on: nextDeps, shell };
|
|
1676
|
+
}
|
|
1677
|
+
if (task.id === "frontend-implement-pi") {
|
|
1678
|
+
// still requires final design gate
|
|
1679
|
+
for (const need of [
|
|
1680
|
+
"frontend-final-design-gate-shell",
|
|
1681
|
+
"frontend-implementation-contract-shell",
|
|
1682
|
+
]) {
|
|
1683
|
+
if (byId.has(need) && !depends_on.includes(need))
|
|
1684
|
+
depends_on.push(need);
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
return { ...task, depends_on };
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1508
1690
|
function buildFrontendHybridDagFromTask(sources) {
|
|
1509
1691
|
const { taskConfig } = sources;
|
|
1510
1692
|
const mockCapability = sources.frontendMockCapability ?? {
|
|
1511
1693
|
status: "absent",
|
|
1512
1694
|
evidencePaths: [],
|
|
1513
1695
|
verifyCommands: [],
|
|
1514
|
-
reasons: [
|
|
1696
|
+
reasons: [
|
|
1697
|
+
"Frontend Mock capability was not precomputed; assessment must verify repository evidence.",
|
|
1698
|
+
],
|
|
1515
1699
|
};
|
|
1516
1700
|
const mockMode = sources.frontendMockMode ??
|
|
1517
1701
|
resolveFrontendMockMode(mockCapability, taskConfig, hasApiDependency(sources));
|
|
@@ -1523,8 +1707,22 @@ function buildFrontendHybridDagFromTask(sources) {
|
|
|
1523
1707
|
const forbiddenPaths = mergeForbiddenPaths(taskConfig);
|
|
1524
1708
|
const implementPaths = resolveImplementPaths(taskConfig);
|
|
1525
1709
|
const implementId = frontendImplementationNodeId();
|
|
1526
|
-
const sourceContext = buildSourceContextBlock(sources);
|
|
1527
1710
|
const mockContextBlock = resolveFrontendMockContextBlock(frontendSources);
|
|
1711
|
+
const capabilityContextBlock = resolveFrontendCapabilityContextBlock(frontendSources);
|
|
1712
|
+
const frontendRisk = frontendSources.frontendRisk ??
|
|
1713
|
+
classifyFrontendRisk({
|
|
1714
|
+
title: taskConfig.title,
|
|
1715
|
+
requirementMarkdown: sources.requirementMarkdown,
|
|
1716
|
+
constraintMarkdown: sources.constraintMarkdown ?? undefined,
|
|
1717
|
+
allowedPaths: taskConfig.allowedPaths,
|
|
1718
|
+
complexity: taskConfig.complexity,
|
|
1719
|
+
});
|
|
1720
|
+
const sourceContext = [
|
|
1721
|
+
buildSourceContextBlock(sources),
|
|
1722
|
+
capabilityContextBlock,
|
|
1723
|
+
]
|
|
1724
|
+
.filter(Boolean)
|
|
1725
|
+
.join("\n\n");
|
|
1528
1726
|
const hasMockVerifyCommands = (taskConfig.frontendMock?.verifyCommands.length ?? 0) > 0 ||
|
|
1529
1727
|
mockCapability.verifyCommands.length > 0;
|
|
1530
1728
|
const requirementIds = buildDagSourceBinding(sources).requirementIds;
|
|
@@ -1549,6 +1747,10 @@ function buildFrontendHybridDagFromTask(sources) {
|
|
|
1549
1747
|
"Mock-backed behavior evidence proves only the documented frontend contract, never real API integration.",
|
|
1550
1748
|
"frontend-implementation DAGs must complete deterministic static verification and behavior verification before final review.",
|
|
1551
1749
|
"frontend review must block closeout unless review verdict is exactly VERDICT: pass.",
|
|
1750
|
+
`Frontend risk classification: ${frontendRisk.selectedRisk} — ${frontendRisk.reason}`,
|
|
1751
|
+
frontendRisk.forceFullGates
|
|
1752
|
+
? "High-risk or supervised: keep full design gates; do not weaken write boundaries."
|
|
1753
|
+
: "Risk-aware topology may omit redundant design revision nodes for small tasks only.",
|
|
1552
1754
|
];
|
|
1553
1755
|
// Guard: blocked mode — generate assessment-only DAG with no writer reachable
|
|
1554
1756
|
if (mockMode === "blocked") {
|
|
@@ -1671,11 +1873,12 @@ function buildFrontendHybridDagFromTask(sources) {
|
|
|
1671
1873
|
allowedPaths: readOnlyPaths,
|
|
1672
1874
|
forbiddenPaths,
|
|
1673
1875
|
skills: FRONTEND_IMPLEMENTATION_SKILLS,
|
|
1674
|
-
outputContract: "Markdown implementation plan with Requirement Coverage, Implementation Steps, Target Files, UI State Handling, Styling / Component Strategy, Interaction Notes, Mock / API Strategy, Dependency Policy, Verification Plan, Real Integration Gap, and Residual Risks. No file writes.",
|
|
1876
|
+
outputContract: "Markdown implementation plan with Requirement Coverage, Implementation Steps, Target Files, UI State Handling, Styling / Component Strategy, Interaction Notes, Mock / API Strategy, Dependency Policy, Verification Plan, Real Integration Gap, and Residual Risks, followed by exactly one fenced json object conforming to frontend-implementation-contract-v1 when this node is the effective plan source. No file writes.",
|
|
1675
1877
|
subtask_prompt: [
|
|
1676
1878
|
"Based on frontend-contract-pi, frontend-scout-pi, and the gated frontend-mock-assess-pi strategy, return a minimal frontend implementation plan.",
|
|
1677
1879
|
"Carry the selected Mock / API strategy, endpoint/fixture mapping, explicit activation, production-default-off rule, verification commands, and Real Integration Gap into the plan.",
|
|
1678
1880
|
"Include ordered steps, target files, UI state handling, styling/component strategy, interaction notes, Mock/API strategy, dependency policy, deterministic verification entrypoints, and residual risks. Use only the fixed entrypoints below; implementation may add tests behind them but cannot replace them.",
|
|
1881
|
+
"End with exactly one fenced json object conforming to frontend-implementation-contract-v1 so small topology can materialize the contract without plan-revision.",
|
|
1679
1882
|
requirementCoverageInstruction,
|
|
1680
1883
|
"Read-only: do not modify code, docs, artifacts, or repository files.",
|
|
1681
1884
|
fixedVerificationContext,
|
|
@@ -1742,7 +1945,7 @@ function buildFrontendHybridDagFromTask(sources) {
|
|
|
1742
1945
|
allowedPaths: readOnlyPaths,
|
|
1743
1946
|
forbiddenPaths,
|
|
1744
1947
|
skills: FRONTEND_IMPLEMENTATION_SKILLS,
|
|
1745
|
-
outputContract: "Markdown revision plan
|
|
1948
|
+
outputContract: "Markdown revision plan followed by exactly one fenced json object conforming to frontend-implementation-contract-v1. The JSON is the authoritative materialization input. No file writes.",
|
|
1746
1949
|
subtask_prompt: [
|
|
1747
1950
|
"Consume frontend-plan-pi (original plan) and frontend-design-gate-pi (first design review findings).",
|
|
1748
1951
|
"If the first design gate passed (VERDICT: pass from frontend-design-gate-pi), output exactly:",
|
|
@@ -1755,27 +1958,59 @@ function buildFrontendHybridDagFromTask(sources) {
|
|
|
1755
1958
|
requirementCoverageInstruction,
|
|
1756
1959
|
"Do not turn MOCK_STRATEGY: blocked into an implementable strategy without new repository or contract evidence that resolves every blocker.",
|
|
1757
1960
|
"Read-only: do not modify code, docs, artifacts, or repository files. This node revises the plan only.",
|
|
1961
|
+
"End the response with exactly one fenced json object conforming to frontend-implementation-contract-v1. Bind it to the supplied task sources; map every requirement and applicable UI state to concrete implementation and verification targets or an explicit blocking evidence gap. Do not include secrets or unsafe paths.",
|
|
1758
1962
|
sourceContext,
|
|
1759
1963
|
].join("\n\n"),
|
|
1760
1964
|
},
|
|
1761
|
-
...(requirementIds.length > 0
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1965
|
+
...(requirementIds.length > 0
|
|
1966
|
+
? [
|
|
1967
|
+
{
|
|
1968
|
+
id: "frontend-requirement-coverage-shell",
|
|
1969
|
+
depends_on: ["frontend-plan-revision-pi"],
|
|
1970
|
+
role: "verifier",
|
|
1971
|
+
executor: "shell",
|
|
1972
|
+
complexity: "LOW",
|
|
1973
|
+
writePolicy: "read-only",
|
|
1974
|
+
allowedPaths: readOnlyPaths,
|
|
1975
|
+
forbiddenPaths,
|
|
1976
|
+
outputContract: "Deterministic current-run evidence that the original or revised frontend plan retains every explicit REQ-/BR-/AC- identifier from the bound task sources.",
|
|
1977
|
+
subtask_prompt: "Block final design review when the current run's plan facts omit any explicit requirement identifier from the authoritative task sources.",
|
|
1978
|
+
shell: {
|
|
1979
|
+
commands: [],
|
|
1980
|
+
requirementCoverageGate: {
|
|
1981
|
+
fromNodeIds: ["frontend-plan-revision-pi"],
|
|
1982
|
+
requiredIds: requirementIds,
|
|
1983
|
+
label: "frontend requirement coverage",
|
|
1984
|
+
},
|
|
1985
|
+
cwd: ".",
|
|
1986
|
+
timeoutMs: 60000,
|
|
1987
|
+
},
|
|
1777
1988
|
},
|
|
1778
|
-
|
|
1989
|
+
]
|
|
1990
|
+
: []),
|
|
1991
|
+
{
|
|
1992
|
+
id: "frontend-implementation-contract-shell",
|
|
1993
|
+
depends_on: ["frontend-plan-revision-pi"],
|
|
1994
|
+
role: "verifier",
|
|
1995
|
+
executor: "shell",
|
|
1996
|
+
complexity: "LOW",
|
|
1997
|
+
writePolicy: "read-only",
|
|
1998
|
+
allowedPaths: readOnlyPaths,
|
|
1999
|
+
forbiddenPaths,
|
|
2000
|
+
outputContract: "Run-owned validated frontend-implementation-contract-v1 artifact path, schema id, and SHA-256.",
|
|
2001
|
+
subtask_prompt: "Materialize the effective frontend plan as a source-bound structured contract; fail closed on missing or invalid output.",
|
|
2002
|
+
shell: {
|
|
2003
|
+
commands: [],
|
|
2004
|
+
jsonArtifactGate: {
|
|
2005
|
+
fromNodeId: "frontend-plan-revision-pi",
|
|
2006
|
+
schemaId: "frontend-implementation-contract-v1",
|
|
2007
|
+
artifactName: "frontend-implementation-contract.json",
|
|
2008
|
+
outputDir: "contracts",
|
|
2009
|
+
},
|
|
2010
|
+
cwd: ".",
|
|
2011
|
+
timeoutMs: 60000,
|
|
2012
|
+
},
|
|
2013
|
+
},
|
|
1779
2014
|
{
|
|
1780
2015
|
id: "frontend-final-design-review-pi",
|
|
1781
2016
|
depends_on: [
|
|
@@ -1783,7 +2018,10 @@ function buildFrontendHybridDagFromTask(sources) {
|
|
|
1783
2018
|
"frontend-plan-pi",
|
|
1784
2019
|
"frontend-design-gate-pi",
|
|
1785
2020
|
"frontend-mock-assess-pi",
|
|
1786
|
-
|
|
2021
|
+
"frontend-implementation-contract-shell",
|
|
2022
|
+
...(requirementIds.length > 0
|
|
2023
|
+
? ["frontend-requirement-coverage-shell"]
|
|
2024
|
+
: []),
|
|
1787
2025
|
],
|
|
1788
2026
|
role: "reviewer",
|
|
1789
2027
|
executor: "pi",
|
|
@@ -1833,6 +2071,7 @@ function buildFrontendHybridDagFromTask(sources) {
|
|
|
1833
2071
|
id: implementId,
|
|
1834
2072
|
depends_on: [
|
|
1835
2073
|
"frontend-final-design-gate-shell",
|
|
2074
|
+
"frontend-implementation-contract-shell",
|
|
1836
2075
|
"frontend-plan-revision-pi",
|
|
1837
2076
|
"frontend-final-design-review-pi",
|
|
1838
2077
|
"frontend-plan-pi",
|
|
@@ -1847,22 +2086,28 @@ function buildFrontendHybridDagFromTask(sources) {
|
|
|
1847
2086
|
allowedPaths: implementPaths.allowedPaths,
|
|
1848
2087
|
forbiddenPaths,
|
|
1849
2088
|
skills: FRONTEND_IMPLEMENTATION_SKILLS,
|
|
1850
|
-
outputContract: "Markdown summary with Changed Files, Implemented
|
|
2089
|
+
outputContract: "Markdown delivery summary with Contract Ref (path/schema/hash), Changed Files, Requirements Implemented, UI States, Tests Changed, Verification Attempts, Deviations, and Residual Risks. Follow fixed stages: contract confirm → tests → component/state → API/Mock → focused checks → diff cleanup.",
|
|
1851
2090
|
subtask_prompt: [
|
|
1852
|
-
"Implement the
|
|
1853
|
-
"
|
|
2091
|
+
"Implement against the validated run-owned Frontend Implementation Contract from frontend-implementation-contract-shell (path/schema/hash). Do not rebuild the contract from Markdown alone.",
|
|
2092
|
+
"Execute in fixed stages and report each in the delivery summary: (1) Contract confirm, (2) Tests sync, (3) Component/UI state implementation, (4) API/Mock wiring per contract.mockApi, (5) Focused checks behind frozen entrypoints only, (6) Diff cleanup.",
|
|
2093
|
+
"Map every requirement id and applicable UI state from the contract to concrete files. Do not invent shell verification commands; only frozen static/behavior entrypoints will run.",
|
|
2094
|
+
"Implement only the approved Mock strategy from frontend-mock-assess-pi as carried in the contract. Preserve the real request path as the default, require explicit test/dev activation, and never comment out or replace the real request with inline data.",
|
|
1854
2095
|
"The frontend-final-design-review-pi verdict confirmed the plan is ready. Stay within writeSet and preserve unrelated files.",
|
|
1855
2096
|
"For native, browser-intercept, or request-adapter, implement contract-aligned fixtures/states and a dev/test-only activation boundary in this same writer. For not-needed, do not add Mock files or a framework and state the positive reason.",
|
|
1856
|
-
"Do not write root artifacts/** unless explicitly included in writeSet.",
|
|
2097
|
+
"Do not write root artifacts/** unless explicitly included in writeSet. Do not claim Browser/visual verification.",
|
|
1857
2098
|
writerDeliveryContract(taskConfig),
|
|
1858
2099
|
sourceContext,
|
|
1859
2100
|
mockContextBlock,
|
|
1860
|
-
]
|
|
2101
|
+
]
|
|
2102
|
+
.filter((value) => Boolean(value))
|
|
2103
|
+
.join("\n\n"),
|
|
1861
2104
|
},
|
|
1862
2105
|
// Optional dedicated Mock verification exists only when trusted commands
|
|
1863
2106
|
// were frozen at generation time. Behavior verification remains required.
|
|
1864
2107
|
...(mockMode === "required" && hasMockVerifyCommands
|
|
1865
|
-
? [
|
|
2108
|
+
? [
|
|
2109
|
+
buildFrontendMockVerifyNode(frontendSources, implementId, readOnlyPaths, forbiddenPaths),
|
|
2110
|
+
]
|
|
1866
2111
|
: []),
|
|
1867
2112
|
{
|
|
1868
2113
|
id: "frontend-static-verify-shell",
|
|
@@ -1882,6 +2127,7 @@ function buildFrontendHybridDagFromTask(sources) {
|
|
|
1882
2127
|
verifyEvidence: staticVerifyEvidence,
|
|
1883
2128
|
cwd: ".",
|
|
1884
2129
|
timeoutMs: 300000,
|
|
2130
|
+
nonZeroExitPolicy: "record",
|
|
1885
2131
|
},
|
|
1886
2132
|
},
|
|
1887
2133
|
{
|
|
@@ -1900,14 +2146,166 @@ function buildFrontendHybridDagFromTask(sources) {
|
|
|
1900
2146
|
verifyEvidence: behaviorVerifyEvidence,
|
|
1901
2147
|
cwd: ".",
|
|
1902
2148
|
timeoutMs: 300000,
|
|
2149
|
+
nonZeroExitPolicy: "record",
|
|
1903
2150
|
},
|
|
1904
2151
|
},
|
|
1905
2152
|
{
|
|
1906
|
-
id: "frontend-
|
|
2153
|
+
id: "frontend-verification-trace-shell",
|
|
1907
2154
|
depends_on: [
|
|
2155
|
+
"frontend-behavior-verify-shell",
|
|
1908
2156
|
"frontend-static-verify-shell",
|
|
2157
|
+
"frontend-implementation-contract-shell",
|
|
2158
|
+
],
|
|
2159
|
+
role: "verifier",
|
|
2160
|
+
executor: "shell",
|
|
2161
|
+
complexity: "LOW",
|
|
2162
|
+
writePolicy: "read-only",
|
|
2163
|
+
allowedPaths: readOnlyPaths,
|
|
2164
|
+
forbiddenPaths,
|
|
2165
|
+
outputContract: "Deterministic verification trace: contract verificationTargets bound to current-run static/behavior commandLabels; target files/symbols exist; Browser/visual not-run. No worktree writes.",
|
|
2166
|
+
subtask_prompt: "Validate AC/UI/verification targets against frozen shell evidence and workspace files. Do not invent commands. Does not prove semantic test quality.",
|
|
2167
|
+
shell: {
|
|
2168
|
+
commands: ["frontend-verification-trace-gate"],
|
|
2169
|
+
cwd: ".",
|
|
2170
|
+
timeoutMs: 120000,
|
|
2171
|
+
nonZeroExitPolicy: "record",
|
|
2172
|
+
},
|
|
2173
|
+
},
|
|
2174
|
+
{
|
|
2175
|
+
id: "frontend-failure-assess-shell",
|
|
2176
|
+
depends_on: [
|
|
2177
|
+
"frontend-verification-trace-shell",
|
|
1909
2178
|
"frontend-behavior-verify-shell",
|
|
2179
|
+
"frontend-static-verify-shell",
|
|
2180
|
+
"frontend-implementation-contract-shell",
|
|
2181
|
+
],
|
|
2182
|
+
role: "verifier",
|
|
2183
|
+
executor: "shell",
|
|
2184
|
+
complexity: "LOW",
|
|
2185
|
+
writePolicy: "read-only",
|
|
2186
|
+
allowedPaths: readOnlyPaths,
|
|
2187
|
+
forbiddenPaths,
|
|
2188
|
+
outputContract: "Run-owned frontend-repair-assessment-v1 at contracts/frontend-repair-assessment.json classifying verify/trace failures as repairable or not.",
|
|
2189
|
+
subtask_prompt: "Assess current-run static/behavior/trace failure facts against the validated contract. Do not repair code.",
|
|
2190
|
+
shell: {
|
|
2191
|
+
commands: ["frontend-failure-assess-gate"],
|
|
2192
|
+
cwd: ".",
|
|
2193
|
+
timeoutMs: 60000,
|
|
2194
|
+
},
|
|
2195
|
+
},
|
|
2196
|
+
{
|
|
2197
|
+
id: "frontend-repair-contract-shell",
|
|
2198
|
+
depends_on: ["frontend-failure-assess-shell"],
|
|
2199
|
+
role: "verifier",
|
|
2200
|
+
executor: "shell",
|
|
2201
|
+
complexity: "LOW",
|
|
2202
|
+
writePolicy: "read-only",
|
|
2203
|
+
allowedPaths: readOnlyPaths,
|
|
2204
|
+
forbiddenPaths,
|
|
2205
|
+
outputContract: "Deterministic repair eligibility gate: pass when no failure or repairable assessment; fail-closed on non-repairable classes.",
|
|
2206
|
+
subtask_prompt: "Validate frontend-repair-assessment writeSet subset, attempt limit, and eligibility before repair writer.",
|
|
2207
|
+
shell: {
|
|
2208
|
+
commands: ["frontend-repair-contract-gate"],
|
|
2209
|
+
cwd: ".",
|
|
2210
|
+
timeoutMs: 60000,
|
|
2211
|
+
},
|
|
2212
|
+
},
|
|
2213
|
+
{
|
|
2214
|
+
id: "frontend-repair-pi",
|
|
2215
|
+
depends_on: [
|
|
2216
|
+
"frontend-repair-contract-shell",
|
|
2217
|
+
"frontend-failure-assess-shell",
|
|
1910
2218
|
implementId,
|
|
2219
|
+
],
|
|
2220
|
+
role: "implementer",
|
|
2221
|
+
executor: "pi",
|
|
2222
|
+
toolProfile: "write",
|
|
2223
|
+
complexity: resolveWriterComplexity(taskConfig),
|
|
2224
|
+
writePolicy: "exclusive",
|
|
2225
|
+
writeSet: implementPaths.writeSet,
|
|
2226
|
+
allowedPaths: implementPaths.allowedPaths,
|
|
2227
|
+
forbiddenPaths,
|
|
2228
|
+
skills: FRONTEND_IMPLEMENTATION_SKILLS,
|
|
2229
|
+
outputContract: "Repair summary or explicit no-op when assessment has no failure/eligible=false path already gated. Must not expand writeSet, re-interpret requirements, skip tests, or enable Mock by default.",
|
|
2230
|
+
subtask_prompt: [
|
|
2231
|
+
"Read contracts/frontend-repair-assessment.json and the validated frontend implementation contract.",
|
|
2232
|
+
"If failedNodeIds is empty, return no-op with evidence. If eligible repairable failure, apply the smallest fix inside the original implement writeSet only.",
|
|
2233
|
+
"Do not change lint/type/test config, do not add .skip/.only, do not comment out real requests, do not default-enable Mock, do not add dependencies.",
|
|
2234
|
+
"Do not re-plan requirements or expand allowed paths. Browser/visual remain not-run.",
|
|
2235
|
+
writerDeliveryContract(taskConfig),
|
|
2236
|
+
sourceContext,
|
|
2237
|
+
mockContextBlock,
|
|
2238
|
+
]
|
|
2239
|
+
.filter((value) => Boolean(value))
|
|
2240
|
+
.join("\n\n"),
|
|
2241
|
+
},
|
|
2242
|
+
{
|
|
2243
|
+
id: "frontend-static-reverify-shell",
|
|
2244
|
+
depends_on: ["frontend-repair-pi"],
|
|
2245
|
+
role: "verifier",
|
|
2246
|
+
executor: "shell",
|
|
2247
|
+
complexity: "LOW",
|
|
2248
|
+
writePolicy: "read-only",
|
|
2249
|
+
allowedPaths: readOnlyPaths,
|
|
2250
|
+
forbiddenPaths,
|
|
2251
|
+
outputContract: "Archived static re-verification after repair using the same frozen commands; fail on nonzero.",
|
|
2252
|
+
subtask_prompt: "Re-run frozen static entrypoints after repair. Fresh evidence only; do not rewrite prior failure artifacts.",
|
|
2253
|
+
shell: {
|
|
2254
|
+
commands: staticShellCommands,
|
|
2255
|
+
verifyEvidence: staticVerifyEvidence,
|
|
2256
|
+
cwd: ".",
|
|
2257
|
+
timeoutMs: 300000,
|
|
2258
|
+
},
|
|
2259
|
+
},
|
|
2260
|
+
{
|
|
2261
|
+
id: "frontend-behavior-reverify-shell",
|
|
2262
|
+
depends_on: ["frontend-static-reverify-shell"],
|
|
2263
|
+
role: "verifier",
|
|
2264
|
+
executor: "shell",
|
|
2265
|
+
complexity: "LOW",
|
|
2266
|
+
writePolicy: "read-only",
|
|
2267
|
+
allowedPaths: behaviorPaths,
|
|
2268
|
+
forbiddenPaths,
|
|
2269
|
+
outputContract: "Archived behavior re-verification after repair using the same frozen commands; fail on nonzero.",
|
|
2270
|
+
subtask_prompt: "Re-run frozen behavior entrypoints after repair.",
|
|
2271
|
+
shell: {
|
|
2272
|
+
commands: behaviorShellCommands,
|
|
2273
|
+
verifyEvidence: behaviorVerifyEvidence,
|
|
2274
|
+
cwd: ".",
|
|
2275
|
+
timeoutMs: 300000,
|
|
2276
|
+
},
|
|
2277
|
+
},
|
|
2278
|
+
{
|
|
2279
|
+
id: "frontend-verification-retrace-shell",
|
|
2280
|
+
depends_on: [
|
|
2281
|
+
"frontend-behavior-reverify-shell",
|
|
2282
|
+
"frontend-static-reverify-shell",
|
|
2283
|
+
"frontend-implementation-contract-shell",
|
|
2284
|
+
],
|
|
2285
|
+
role: "verifier",
|
|
2286
|
+
executor: "shell",
|
|
2287
|
+
complexity: "LOW",
|
|
2288
|
+
writePolicy: "read-only",
|
|
2289
|
+
allowedPaths: readOnlyPaths,
|
|
2290
|
+
forbiddenPaths,
|
|
2291
|
+
outputContract: "Re-run verification trace against contract and reverify shell evidence after repair.",
|
|
2292
|
+
subtask_prompt: "Trace AC/UI/verification targets against post-repair static/behavior evidence.",
|
|
2293
|
+
shell: {
|
|
2294
|
+
commands: ["frontend-verification-trace-gate"],
|
|
2295
|
+
cwd: ".",
|
|
2296
|
+
timeoutMs: 120000,
|
|
2297
|
+
},
|
|
2298
|
+
},
|
|
2299
|
+
{
|
|
2300
|
+
id: "frontend-review-pi",
|
|
2301
|
+
depends_on: [
|
|
2302
|
+
"frontend-verification-retrace-shell",
|
|
2303
|
+
"frontend-static-reverify-shell",
|
|
2304
|
+
"frontend-behavior-reverify-shell",
|
|
2305
|
+
"frontend-repair-pi",
|
|
2306
|
+
"frontend-failure-assess-shell",
|
|
2307
|
+
implementId,
|
|
2308
|
+
"frontend-implementation-contract-shell",
|
|
1911
2309
|
"frontend-contract-pi",
|
|
1912
2310
|
"frontend-plan-pi",
|
|
1913
2311
|
"frontend-plan-revision-pi",
|
|
@@ -1929,6 +2327,8 @@ function buildFrontendHybridDagFromTask(sources) {
|
|
|
1929
2327
|
"Review the frontend implementation and verification evidence.",
|
|
1930
2328
|
"First non-empty line must be exactly VERDICT: pass or VERDICT: request-revision.",
|
|
1931
2329
|
"Any Critical or Important finding must force VERDICT: request-revision.",
|
|
2330
|
+
"Read the validated frontend-implementation-contract, frontend-verification-trace evidence, static/behavior shell facts, and actual diff. Trace proves command/file/symbol binding only—not semantic correctness.",
|
|
2331
|
+
"Flag .skip/.only, deleted or weakened tests, unauthorized config changes, Mock-only evidence claimed as real integration, and Browser/visual claims (always not-run in this workflow).",
|
|
1932
2332
|
"Use the direct contract, original plan, revision/no-op result, and final design review to reconstruct the approved plan and design verdict; do not infer them from the implementation summary.",
|
|
1933
2333
|
"Treat a commented-out real request, default-enabled Mock, production entrypoint importing test mocks, API/fixture contract drift, unauthorized Mock dependency/path, or missing behavior evidence for the selected strategy as at least Important. Mock strategies require Mock-backed evidence; not-needed requires applicable real or no-remote behavior evidence. Verify that the real request remains the default when Mock activation is absent.",
|
|
1934
2334
|
"Inspect the production/default-real-path static evidence directly and require Mock activation to be off for that check.",
|
|
@@ -1966,8 +2366,12 @@ function buildFrontendHybridDagFromTask(sources) {
|
|
|
1966
2366
|
depends_on: [
|
|
1967
2367
|
"frontend-review-gate-shell",
|
|
1968
2368
|
"frontend-review-pi",
|
|
1969
|
-
"frontend-
|
|
1970
|
-
"frontend-
|
|
2369
|
+
"frontend-verification-retrace-shell",
|
|
2370
|
+
"frontend-static-reverify-shell",
|
|
2371
|
+
"frontend-behavior-reverify-shell",
|
|
2372
|
+
"frontend-repair-pi",
|
|
2373
|
+
"frontend-failure-assess-shell",
|
|
2374
|
+
"frontend-implementation-contract-shell",
|
|
1971
2375
|
"frontend-mock-assess-pi",
|
|
1972
2376
|
...(mockMode === "required" && hasMockVerifyCommands
|
|
1973
2377
|
? ["frontend-mock-verify-shell"]
|
|
@@ -1985,6 +2389,7 @@ function buildFrontendHybridDagFromTask(sources) {
|
|
|
1985
2389
|
outputContract: "Markdown closeout summary with Changes, Mock Decision / Strategy / Files / Verification / Production Boundary, Verification Evidence, Review Result, Frontend Status, Real Integration Status, Known Risks, and Follow-up. No file writes.",
|
|
1986
2390
|
subtask_prompt: [
|
|
1987
2391
|
"Return a frontend closeout summary covering Mock decision/strategy/files/verification/production boundary, changes, verification evidence, review result, known risks, and follow-up.",
|
|
2392
|
+
"Include a coverage matrix for each requirement id, applicable UI state, and verification target/check with status passed|failed|not-run|blocked|unavailable. Always state Browser accessibility verification: not-run and Visual regression: not-run. Use frontend-verification-trace facts; do not invent Browser evidence from component tests.",
|
|
1988
2393
|
`When only Mock-backed evidence passed, state exactly Frontend status: mock-validated and Real integration: pending, summarize the Real Integration Gap, and name ${taskConfig.taskId}-real-api-integration-verify as the explicit follow-up task to create/run after backend readiness. This follow-up is not auto-created or auto-executed. Never describe Mock evidence as real API integration.`,
|
|
1989
2394
|
"Read-only: do not modify code, docs, artifacts, or .harness/dag-runs/.",
|
|
1990
2395
|
sourceContext,
|
|
@@ -1993,6 +2398,7 @@ function buildFrontendHybridDagFromTask(sources) {
|
|
|
1993
2398
|
},
|
|
1994
2399
|
],
|
|
1995
2400
|
};
|
|
2401
|
+
spec.tasks = pruneFrontendTasksForRisk(spec.tasks, frontendRisk);
|
|
1996
2402
|
applyDefaultReadOnlyRetryPolicy(spec);
|
|
1997
2403
|
parseDagSpec(spec);
|
|
1998
2404
|
assertValidDagSpec(spec);
|
|
@@ -2049,10 +2455,64 @@ function buildBackendTestAnalysisContractGateNode(sources) {
|
|
|
2049
2455
|
},
|
|
2050
2456
|
};
|
|
2051
2457
|
}
|
|
2458
|
+
function buildBackendTestEnvironmentScoutNode(sources) {
|
|
2459
|
+
return {
|
|
2460
|
+
id: "backend-test-environment-scout-pi",
|
|
2461
|
+
depends_on: ["backend-test-analysis-contract-shell"],
|
|
2462
|
+
role: "scout",
|
|
2463
|
+
executor: "pi",
|
|
2464
|
+
complexity: "MED",
|
|
2465
|
+
writePolicy: "read-only",
|
|
2466
|
+
allowedPaths: commonReadOnlyPaths(sources),
|
|
2467
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
2468
|
+
outputContract: "Pure Backend Test Execution Contract v1 JSON object matching docs/templates/backend-test-execution.schema.json. No Markdown prose and no file writes.",
|
|
2469
|
+
subtask_prompt: [
|
|
2470
|
+
"Read-only environment scout for backend-test pytest MVP.",
|
|
2471
|
+
"Return exactly one JSON object matching Backend Test Execution Contract v1 (schema docs/templates/backend-test-execution.schema.json).",
|
|
2472
|
+
"Prefer pure JSON; a single fenced json block is tolerated; no trailing prose.",
|
|
2473
|
+
"Discover only non-secret evidence: pytest config files (pytest.ini / pyproject.toml / setup.cfg test paths), candidate test roots, existing fixtures/clients, documented run commands, and env *names* (not values).",
|
|
2474
|
+
"Do NOT search the whole repo for secrets, .env values, tokens, private keys, or production credentials.",
|
|
2475
|
+
'framework must be "pytest". Default targetMode to "in-process" unless evidence clearly shows an external service base URL env name or documented managed start/stop with sourceRef.',
|
|
2476
|
+
'Do NOT select targetMode "managed-command" unless task source documents a safe start/stop command with an explicit sourceRef; otherwise leave managedCommand absent and record the gap in evidenceGaps.',
|
|
2477
|
+
"testRoot and workingDirectory must be repo-relative posix paths without .. or absolute form. Adapter default testRoot is testcase when evidence is incomplete.",
|
|
2478
|
+
"runner must not include secret values. report.format must be junit with a relativeHint under the run (e.g. reports/backend-test-junit.xml).",
|
|
2479
|
+
"requiredEnvNames lists env NAMES only. baseUrlEnvName is required only for external-running-service and must match ^[A-Z_][A-Z0-9_]*$.",
|
|
2480
|
+
"Record incomplete discovery in evidenceGaps. Populate evidenceRefs with repo-relative paths actually read.",
|
|
2481
|
+
"Required top-level keys: schemaVersion, framework, runner, testRoot, workingDirectory, report, targetMode, existingFixtures, authenticationMode, requiredEnvNames, dataIsolation, evidenceGaps, evidenceRefs.",
|
|
2482
|
+
"Read-only: do not modify code, docs, artifacts, or repository files.",
|
|
2483
|
+
buildSourceContextBlock(sources),
|
|
2484
|
+
].join("\n\n"),
|
|
2485
|
+
};
|
|
2486
|
+
}
|
|
2487
|
+
function buildBackendTestExecutionContractGateNode(sources) {
|
|
2488
|
+
return {
|
|
2489
|
+
id: "backend-test-execution-contract-shell",
|
|
2490
|
+
depends_on: ["backend-test-environment-scout-pi"],
|
|
2491
|
+
role: "verifier",
|
|
2492
|
+
executor: "shell",
|
|
2493
|
+
complexity: "LOW",
|
|
2494
|
+
writePolicy: "read-only",
|
|
2495
|
+
allowedPaths: commonReadOnlyPaths(sources),
|
|
2496
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
2497
|
+
outputContract: "Validated run-owned Backend Test Execution Contract v1 artifact pointer, schema ID, and SHA-256.",
|
|
2498
|
+
subtask_prompt: "Materialize and validate the backend-test execution contract under the current DAG run.",
|
|
2499
|
+
shell: {
|
|
2500
|
+
commands: [],
|
|
2501
|
+
jsonArtifactGate: {
|
|
2502
|
+
fromNodeId: "backend-test-environment-scout-pi",
|
|
2503
|
+
schemaId: "backend-test-execution-v1",
|
|
2504
|
+
artifactName: "backend-test-execution.json",
|
|
2505
|
+
outputDir: "contracts",
|
|
2506
|
+
},
|
|
2507
|
+
cwd: ".",
|
|
2508
|
+
timeoutMs: 60000,
|
|
2509
|
+
},
|
|
2510
|
+
};
|
|
2511
|
+
}
|
|
2052
2512
|
function buildGenerateBackendFunctionalCasesNode(sources) {
|
|
2053
2513
|
return {
|
|
2054
2514
|
id: "generate-backend-functional-cases-pi",
|
|
2055
|
-
depends_on: ["backend-test-
|
|
2515
|
+
depends_on: ["backend-test-execution-contract-shell"],
|
|
2056
2516
|
role: "implementer",
|
|
2057
2517
|
executor: "pi",
|
|
2058
2518
|
toolProfile: "write",
|
|
@@ -2064,7 +2524,8 @@ function buildGenerateBackendFunctionalCasesNode(sources) {
|
|
|
2064
2524
|
// 注意:Pi 节点超时由 executor 层控制(默认 30 分钟)
|
|
2065
2525
|
// 如需调整,在 harness.json 的 executors.pi 中配置 modelConfig.timeoutMs
|
|
2066
2526
|
subtask_prompt: [
|
|
2067
|
-
"Read the validated structured artifact pointer from backend-test-analysis-contract-shell and generate cases only from that JSON contract.",
|
|
2527
|
+
"Read the validated structured artifact pointer from backend-test-analysis-contract-shell and generate cases only from that JSON contract.",
|
|
2528
|
+
,
|
|
2068
2529
|
"",
|
|
2069
2530
|
"## Output Steps (do in order):",
|
|
2070
2531
|
"1. First, output a brief summary: how many modules, how many cases planned per module",
|
|
@@ -2090,15 +2551,92 @@ function buildGenerateBackendFunctionalCasesNode(sources) {
|
|
|
2090
2551
|
"",
|
|
2091
2552
|
"## Constraints:",
|
|
2092
2553
|
"- Stay within writeSet: testcase/md/**",
|
|
2093
|
-
"- Do NOT re-read source documents or fall back to free-form analysis — use the validated structured artifact only",
|
|
2554
|
+
"- Do NOT re-read source documents or fall back to free-form analysis — use the validated structured artifact only",
|
|
2555
|
+
,
|
|
2094
2556
|
"- Do not write root artifacts/**",
|
|
2095
2557
|
].join("\n\n"),
|
|
2096
2558
|
};
|
|
2097
2559
|
}
|
|
2560
|
+
function buildEmitBackendCaseManifestNode(sources) {
|
|
2561
|
+
return {
|
|
2562
|
+
id: "emit-backend-case-manifest-pi",
|
|
2563
|
+
depends_on: [
|
|
2564
|
+
"generate-backend-functional-cases-pi",
|
|
2565
|
+
"backend-test-analysis-contract-shell",
|
|
2566
|
+
],
|
|
2567
|
+
role: "scout",
|
|
2568
|
+
executor: "pi",
|
|
2569
|
+
complexity: "MED",
|
|
2570
|
+
writePolicy: "read-only",
|
|
2571
|
+
allowedPaths: commonReadOnlyPaths(sources),
|
|
2572
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
2573
|
+
outputContract: "Pure Backend Test Case Manifest v1 JSON (schema docs/templates/backend-test-case-manifest.schema.json). No file writes; model must not write .harness/**.",
|
|
2574
|
+
subtask_prompt: [
|
|
2575
|
+
"Emit Backend Test Case Manifest v1 as pure JSON (or one fenced json block with no trailing text).",
|
|
2576
|
+
"Read-only: use validated contracts/backend-test-analysis.json pointer + testcase/md/** only. Do not write repository files or .harness/**.",
|
|
2577
|
+
"sourceBinding must match the analysis contract / DAG source binding exactly (taskId, requirementPath, requirementSha256, referencePaths, requirementIds).",
|
|
2578
|
+
"For each functional case under testcase/md/: caseId BE-<MODULE>-<NNN>, acIds[], title, category, automationStatus.",
|
|
2579
|
+
"After case generation (pre-pytest), default automationStatus=planned. Use skipped/unsupported only with gapReason. Use generated only when file+symbol already exist.",
|
|
2580
|
+
"evidenceGaps: structured gaps for explicit AC-* that cannot be mapped to a case.",
|
|
2581
|
+
"Do NOT invent coverage percentages. Optional coverageSummary must match deterministic counts (gate recomputes/validates).",
|
|
2582
|
+
"No secrets or credential-shaped fields.",
|
|
2583
|
+
].join("\n\n"),
|
|
2584
|
+
};
|
|
2585
|
+
}
|
|
2586
|
+
function buildBackendTestCaseManifestGateNode(sources) {
|
|
2587
|
+
return {
|
|
2588
|
+
id: "backend-test-case-manifest-shell",
|
|
2589
|
+
depends_on: ["emit-backend-case-manifest-pi"],
|
|
2590
|
+
role: "verifier",
|
|
2591
|
+
executor: "shell",
|
|
2592
|
+
complexity: "LOW",
|
|
2593
|
+
writePolicy: "read-only",
|
|
2594
|
+
allowedPaths: commonReadOnlyPaths(sources),
|
|
2595
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
2596
|
+
outputContract: "Validated run-owned Backend Test Case Manifest v1 at contracts/backend-test-case-manifest.json (schemaId backend-test-case-manifest-v1) with deterministic AC coverage.",
|
|
2597
|
+
subtask_prompt: "Materialize and validate Backend Test Case Manifest v1; fail closed on duplicate IDs, unknown AC, missing AC coverage without gap, or skipped without gapReason.",
|
|
2598
|
+
shell: {
|
|
2599
|
+
commands: [],
|
|
2600
|
+
jsonArtifactGate: {
|
|
2601
|
+
fromNodeId: "emit-backend-case-manifest-pi",
|
|
2602
|
+
schemaId: "backend-test-case-manifest-v1",
|
|
2603
|
+
artifactName: "backend-test-case-manifest.json",
|
|
2604
|
+
outputDir: "contracts",
|
|
2605
|
+
},
|
|
2606
|
+
cwd: ".",
|
|
2607
|
+
timeoutMs: 60000,
|
|
2608
|
+
},
|
|
2609
|
+
};
|
|
2610
|
+
}
|
|
2611
|
+
function buildBackendTestTraceabilityGateNode(sources) {
|
|
2612
|
+
return {
|
|
2613
|
+
id: "backend-test-traceability-gate-shell",
|
|
2614
|
+
depends_on: [
|
|
2615
|
+
"generate-backend-pytest-pi",
|
|
2616
|
+
"backend-test-case-manifest-shell",
|
|
2617
|
+
],
|
|
2618
|
+
role: "verifier",
|
|
2619
|
+
executor: "shell",
|
|
2620
|
+
complexity: "LOW",
|
|
2621
|
+
writePolicy: "read-only",
|
|
2622
|
+
allowedPaths: commonReadOnlyPaths(sources),
|
|
2623
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
2624
|
+
outputContract: "Deterministic traceability: generated cases have real file/symbol; skipped/unsupported have gapReason; convention symbols scanned under testcase/**/test_*.py.",
|
|
2625
|
+
subtask_prompt: "Fail closed when generated automation claims do not resolve to workspace pytest symbols, or skip/unsupported lacks gapReason.",
|
|
2626
|
+
shell: {
|
|
2627
|
+
commands: ["backend-test-traceability-gate"],
|
|
2628
|
+
cwd: ".",
|
|
2629
|
+
timeoutMs: 60000,
|
|
2630
|
+
},
|
|
2631
|
+
};
|
|
2632
|
+
}
|
|
2098
2633
|
function buildReviewBackendCasesNode(sources) {
|
|
2099
2634
|
return {
|
|
2100
2635
|
id: "review-backend-cases-pi",
|
|
2101
|
-
depends_on: [
|
|
2636
|
+
depends_on: [
|
|
2637
|
+
"backend-test-case-manifest-shell",
|
|
2638
|
+
"backend-test-analysis-contract-shell",
|
|
2639
|
+
],
|
|
2102
2640
|
role: "reviewer",
|
|
2103
2641
|
executor: "pi",
|
|
2104
2642
|
complexity: "HIGH",
|
|
@@ -2107,7 +2645,7 @@ function buildReviewBackendCasesNode(sources) {
|
|
|
2107
2645
|
forbiddenPaths: commonForbiddenPaths(sources),
|
|
2108
2646
|
outputContract: "Plain Markdown whose first non-empty line is VERDICT: pass or VERDICT: request-revision; followed by Findings and Coverage Assessment. No file writes.",
|
|
2109
2647
|
subtask_prompt: [
|
|
2110
|
-
"Review the generated backend functional test cases under testcase/md
|
|
2648
|
+
"Review the generated backend functional test cases under testcase/md/ and the validated Case Manifest v1.",
|
|
2111
2649
|
"",
|
|
2112
2650
|
"## Mandatory First Line:",
|
|
2113
2651
|
"First non-empty line must be exactly: VERDICT: pass or VERDICT: request-revision",
|
|
@@ -2116,9 +2654,10 @@ function buildReviewBackendCasesNode(sources) {
|
|
|
2116
2654
|
"- ID format: every case uses BE-<MODULE>-<NNN>",
|
|
2117
2655
|
"- Positive coverage: each acceptance criterion (AC-xxx) has happy-path case",
|
|
2118
2656
|
"- Negative coverage: error scenarios (invalid input, not found, state violations)",
|
|
2119
|
-
"- Traceability: each AC maps to at least one case ID",
|
|
2657
|
+
"- Traceability: each AC maps to at least one case ID (prefer contracts/backend-test-case-manifest.json coverageSummary)",
|
|
2120
2658
|
"- Case structure: ID, Title, Precondition, Steps, Expected Result",
|
|
2121
2659
|
"- No duplicate IDs across files",
|
|
2660
|
+
"- Manifest consistency: MD cases align with manifest caseId/acIds; do not invent coverage %",
|
|
2122
2661
|
"",
|
|
2123
2662
|
"## Conditional Coverage (check ONLY if mentioned in upstream analysis):",
|
|
2124
2663
|
"- Boundary coverage: check ONLY if analyze-inputs-pi mentions value ranges, length limits, numeric bounds, or format constraints",
|
|
@@ -2133,13 +2672,13 @@ function buildReviewBackendCasesNode(sources) {
|
|
|
2133
2672
|
"- Any Critical fails OR Important > 2 → VERDICT: request-revision",
|
|
2134
2673
|
"",
|
|
2135
2674
|
"## Output After Verdict:",
|
|
2136
|
-
"1. Coverage Assessment table (AC → case IDs)",
|
|
2675
|
+
"1. Coverage Assessment table (AC → case IDs) using manifest + MD",
|
|
2137
2676
|
"2. Findings list (Critical/Important/Informational)",
|
|
2138
2677
|
"3. Statistics (total cases, positive/negative/boundary breakdown)",
|
|
2139
2678
|
"",
|
|
2140
2679
|
"## Constraints:",
|
|
2141
2680
|
"- Read-only: do not modify files",
|
|
2142
|
-
"- Read
|
|
2681
|
+
"- Read validated analysis + case manifest artifacts; do not recompute coverage percentages",
|
|
2143
2682
|
"- Use testcase/md/ files for case review",
|
|
2144
2683
|
].join("\n\n"),
|
|
2145
2684
|
};
|
|
@@ -2172,7 +2711,10 @@ function buildReviewBackendCasesGateNode(sources) {
|
|
|
2172
2711
|
function buildGenerateBackendPytestNode(sources) {
|
|
2173
2712
|
return {
|
|
2174
2713
|
id: "generate-backend-pytest-pi",
|
|
2175
|
-
depends_on: [
|
|
2714
|
+
depends_on: [
|
|
2715
|
+
"review-backend-cases-gate-shell",
|
|
2716
|
+
"backend-test-execution-contract-shell",
|
|
2717
|
+
],
|
|
2176
2718
|
role: "implementer",
|
|
2177
2719
|
executor: "pi",
|
|
2178
2720
|
toolProfile: "write",
|
|
@@ -2193,6 +2735,12 @@ function buildGenerateBackendPytestNode(sources) {
|
|
|
2193
2735
|
subtask_prompt: [
|
|
2194
2736
|
"Convert the reviewed test cases under testcase/md/ into pytest automation code.",
|
|
2195
2737
|
"",
|
|
2738
|
+
"## Inputs (MUST use validated contracts):",
|
|
2739
|
+
"- Reviewed cases under testcase/md/ (after review-backend-cases-gate-shell).",
|
|
2740
|
+
"- Validated Backend Test Analysis v1 under the current run contracts/ (analysis gate).",
|
|
2741
|
+
"- Validated Backend Test Execution Contract v1 under contracts/backend-test-execution.json (execution gate).",
|
|
2742
|
+
"Use only fixture names, env NAMES, testRoot, targetMode, and field/API facts already present in those contracts or reviewed cases. Do not invent production credentials or secret values.",
|
|
2743
|
+
"",
|
|
2196
2744
|
"## Output Steps (do in order):",
|
|
2197
2745
|
"1. First, output a brief summary: how many files, how many test functions planned",
|
|
2198
2746
|
"2. Then write each test file under testcase/",
|
|
@@ -2262,8 +2810,8 @@ function buildGenerateBackendPytestNode(sources) {
|
|
|
2262
2810
|
"",
|
|
2263
2811
|
"### Field Name Resolution",
|
|
2264
2812
|
"Field names MUST come from the upstream analyze-inputs-pi output (API Endpoints section) or reviewed cases, NOT guessed. For example:",
|
|
2265
|
-
|
|
2266
|
-
|
|
2813
|
+
'- If API spec defines {"ret": 0, "msg": "success"}, assert response.json()[\'ret\'] and response.json()[\'msg\']',
|
|
2814
|
+
'- If API spec defines {"code": 4001, "message": "error"}, assert response.json()[\'code\'] and response.json()[\'message\']',
|
|
2267
2815
|
"",
|
|
2268
2816
|
"## Conditional Implementation (include ONLY if test cases exist):",
|
|
2269
2817
|
"- Authentication tests: implement ONLY if testcase/md/ contains auth-related cases",
|
|
@@ -2284,26 +2832,41 @@ function buildGenerateBackendPytestNode(sources) {
|
|
|
2284
2832
|
function buildExecuteBackendPytestNode(sources) {
|
|
2285
2833
|
// Keep the target worktree read-only: JUnit is runner-owned evidence under
|
|
2286
2834
|
// the current DAG run and moves with active → completed/paused lifecycle.
|
|
2835
|
+
// Adapter default testRoot is frozen at DAG generation time (auditable) and
|
|
2836
|
+
// cross-checked against the materialized execution contract in preflight.
|
|
2837
|
+
const frozenTestRoot = BACKEND_TEST_EXECUTION_DEFAULT_TEST_ROOT;
|
|
2838
|
+
const preflightCommand = buildBackendTestExecutionPreflightShellSnippet({
|
|
2839
|
+
expectedTestRoot: frozenTestRoot,
|
|
2840
|
+
});
|
|
2841
|
+
// Map pytest exit 0/1 → node success ONLY when JUnit exists (assertion-fail is a
|
|
2842
|
+
// legal result). Do not change global shell ok semantics. Persist raw exit for parse.
|
|
2287
2843
|
const pytestCommand = [
|
|
2288
|
-
|
|
2844
|
+
preflightCommand,
|
|
2289
2845
|
'REPORT="${HARNESS_DAG_RUN_DIR}/reports/backend-test-junit.xml"',
|
|
2846
|
+
'EXIT_FILE="${HARNESS_DAG_RUN_DIR}/reports/backend-test-pytest-exit.txt"',
|
|
2290
2847
|
'mkdir -p "$(dirname "${REPORT}")"',
|
|
2291
|
-
|
|
2292
|
-
|
|
2848
|
+
`PYTHONDONTWRITEBYTECODE=1 python -m pytest ${frozenTestRoot}/ -v -p no:cacheprovider --junitxml="\${REPORT}"`,
|
|
2849
|
+
"STATUS=$?",
|
|
2850
|
+
'printf "%s" "${STATUS}" > "${EXIT_FILE}"',
|
|
2293
2851
|
'printf "JUnit report: %s\\n" "${REPORT}"',
|
|
2852
|
+
'printf "pytestExitCode=%s\\n" "${STATUS}"',
|
|
2853
|
+
'if { [ "${STATUS}" -eq 0 ] || [ "${STATUS}" -eq 1 ]; } && [ -s "${REPORT}" ]; then exit 0; fi',
|
|
2294
2854
|
'exit "${STATUS}"',
|
|
2295
2855
|
].join("; ");
|
|
2296
2856
|
return {
|
|
2297
2857
|
id: "execute-backend-pytest-shell",
|
|
2298
|
-
depends_on: [
|
|
2858
|
+
depends_on: [
|
|
2859
|
+
"backend-test-traceability-gate-shell",
|
|
2860
|
+
"backend-test-execution-contract-shell",
|
|
2861
|
+
],
|
|
2299
2862
|
role: "verifier",
|
|
2300
2863
|
executor: "shell",
|
|
2301
2864
|
complexity: "LOW",
|
|
2302
2865
|
writePolicy: "read-only",
|
|
2303
2866
|
allowedPaths: commonReadOnlyPaths(sources),
|
|
2304
2867
|
forbiddenPaths: commonForbiddenPaths(sources),
|
|
2305
|
-
outputContract: "Archived pytest stdout/stderr
|
|
2306
|
-
subtask_prompt: "Run pytest for the backend test suite; write JUnit evidence only under the current HARNESS_DAG_RUN_DIR/reports/.",
|
|
2868
|
+
outputContract: "Archived pytest stdout/stderr; raw pytestExitCode side-channel + JUnit at $HARNESS_DAG_RUN_DIR/reports/**. Exit 0/1 with non-empty JUnit finishes the node so parse/classify/retrospect can run; assertion failures remain recorded in exit file.",
|
|
2869
|
+
subtask_prompt: "Run pytest for the backend test suite; write JUnit + pytestExitCode evidence only under the current HARNESS_DAG_RUN_DIR/reports/.",
|
|
2307
2870
|
shell: {
|
|
2308
2871
|
commands: [pytestCommand],
|
|
2309
2872
|
verifyEvidence: buildVerifyEvidence({
|
|
@@ -2318,10 +2881,61 @@ function buildExecuteBackendPytestNode(sources) {
|
|
|
2318
2881
|
},
|
|
2319
2882
|
};
|
|
2320
2883
|
}
|
|
2884
|
+
function buildParseBackendTestResultNode(sources) {
|
|
2885
|
+
return {
|
|
2886
|
+
id: "parse-backend-test-result-shell",
|
|
2887
|
+
depends_on: ["execute-backend-pytest-shell"],
|
|
2888
|
+
role: "verifier",
|
|
2889
|
+
executor: "shell",
|
|
2890
|
+
complexity: "LOW",
|
|
2891
|
+
writePolicy: "read-only",
|
|
2892
|
+
allowedPaths: commonReadOnlyPaths(sources),
|
|
2893
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
2894
|
+
outputContract: "Validated run-owned Backend Test Result v1 at contracts/backend-test-result.json (schemaId backend-test-result-v1) with outcome/counts/failures from deterministic JUnit parse.",
|
|
2895
|
+
subtask_prompt: "Materialize Backend Test Result v1 from JUnit + pytestExitCode under the current DAG run (fail-closed on missing/corrupt report).",
|
|
2896
|
+
shell: {
|
|
2897
|
+
commands: [],
|
|
2898
|
+
jsonArtifactGate: {
|
|
2899
|
+
fromNodeId: "execute-backend-pytest-shell",
|
|
2900
|
+
schemaId: "backend-test-result-v1",
|
|
2901
|
+
artifactName: "backend-test-result.json",
|
|
2902
|
+
outputDir: "contracts",
|
|
2903
|
+
},
|
|
2904
|
+
cwd: ".",
|
|
2905
|
+
timeoutMs: 60000,
|
|
2906
|
+
},
|
|
2907
|
+
};
|
|
2908
|
+
}
|
|
2909
|
+
function buildClassifyBackendTestResultNode(sources) {
|
|
2910
|
+
return {
|
|
2911
|
+
id: "classify-backend-test-result-pi",
|
|
2912
|
+
depends_on: ["parse-backend-test-result-shell"],
|
|
2913
|
+
role: "reviewer",
|
|
2914
|
+
executor: "pi",
|
|
2915
|
+
complexity: "MED",
|
|
2916
|
+
writePolicy: "read-only",
|
|
2917
|
+
allowedPaths: commonReadOnlyPaths(sources),
|
|
2918
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
2919
|
+
outputContract: "Pure JSON classification: category in {ProductBug,TestBug,EnvFailure,ContractMismatch,FlakyTest,Unknown}, evidence[], confidence (capped), notes. No file writes.",
|
|
2920
|
+
subtask_prompt: [
|
|
2921
|
+
"Read-only classifier for Backend Test Result v1.",
|
|
2922
|
+
"Return exactly one JSON object (prefer pure JSON; single fenced json block tolerated; no trailing prose).",
|
|
2923
|
+
"Read contracts/backend-test-result.json (run-owned Result v1). Do NOT invent pass rates from raw logs.",
|
|
2924
|
+
"category must be one of: ProductBug, TestBug, EnvFailure, ContractMismatch, FlakyTest, Unknown.",
|
|
2925
|
+
"Hard constraints:",
|
|
2926
|
+
"- Single-run failure MUST NOT use FlakyTest (use Unknown, TestBug, or ProductBug).",
|
|
2927
|
+
"- executionStatus/outcome collection-error, command-error, or report-error MUST NOT use ProductBug.",
|
|
2928
|
+
"- Prefer EnvFailure/Unknown/TestBug for env, import, collection, and missing-report cases.",
|
|
2929
|
+
"- confidence must respect deterministic caps (≤0.75 for assertion failures; ≤0.6 for env/collection).",
|
|
2930
|
+
"Include evidence[] referencing result fields (outcome, failed, failures[].name, executionStatus).",
|
|
2931
|
+
"Read-only: do not modify code, docs, artifacts, or repository files.",
|
|
2932
|
+
].join("\n\n"),
|
|
2933
|
+
};
|
|
2934
|
+
}
|
|
2321
2935
|
function buildTestRetrospectNode(sources) {
|
|
2322
2936
|
return {
|
|
2323
2937
|
id: "test-retrospect-pi",
|
|
2324
|
-
depends_on: ["
|
|
2938
|
+
depends_on: ["classify-backend-test-result-pi"],
|
|
2325
2939
|
role: "closeout",
|
|
2326
2940
|
executor: "pi",
|
|
2327
2941
|
toolProfile: "write",
|
|
@@ -2331,23 +2945,29 @@ function buildTestRetrospectNode(sources) {
|
|
|
2331
2945
|
allowedPaths: ["docs/test-reports/**"],
|
|
2332
2946
|
forbiddenPaths: commonForbiddenPaths(sources),
|
|
2333
2947
|
subtask_prompt: [
|
|
2334
|
-
"Read upstream
|
|
2948
|
+
"Read upstream Result v1 + Case Manifest coverageSummary + classification and generate a test retrospective report.",
|
|
2335
2949
|
"",
|
|
2336
2950
|
"## Output Steps (do in order):",
|
|
2337
2951
|
"1. First, output the maturity rating on the first line: Rating: A/B/C/D",
|
|
2338
2952
|
"2. Then write the full report under docs/test-reports/",
|
|
2339
2953
|
"",
|
|
2954
|
+
"## Stats authority (deterministic only):",
|
|
2955
|
+
"- Pass rate, failed/error/skipped counts, and failure list MUST come from contracts/backend-test-result.json only.",
|
|
2956
|
+
"- AC coverage ratio / case counts MUST come from contracts/backend-test-case-manifest.json coverageSummary (or gate-derived fields). Do NOT invent coverage %.",
|
|
2957
|
+
"- Use classify-backend-test-result-pi JSON as interpretive evidence only.",
|
|
2958
|
+
"- NEVER rewrite a failed result as passed. Outcome gate (not this report) is authoritative for task success.",
|
|
2959
|
+
"",
|
|
2340
2960
|
"## Report Structure:",
|
|
2341
2961
|
"1. Maturity Rating with rationale",
|
|
2342
|
-
"2. Test Coverage Summary (
|
|
2962
|
+
"2. Test Coverage Summary (manifest coverageSummary + Result v1 pass rate)",
|
|
2343
2963
|
"3. Review Findings and resolution status",
|
|
2344
|
-
"4. Failed Test Analysis (if any)",
|
|
2964
|
+
"4. Failed Test Analysis (if any) + classification category",
|
|
2345
2965
|
"5. Recommendations for improvement",
|
|
2346
2966
|
"",
|
|
2347
2967
|
"## Rating Criteria:",
|
|
2348
|
-
"- A:
|
|
2349
|
-
"- B: ≥
|
|
2350
|
-
"- C: ≥
|
|
2968
|
+
"- A: coverageSummary.acCoverageRatio=1 + 100% pytest pass + no Critical findings",
|
|
2969
|
+
"- B: acCoverageRatio≥0.8 + ≥90% pass + Low findings only",
|
|
2970
|
+
"- C: acCoverageRatio≥0.6 + ≥70% pass + no Critical findings",
|
|
2351
2971
|
"- D: below C thresholds",
|
|
2352
2972
|
"",
|
|
2353
2973
|
"## Constraints:",
|
|
@@ -2357,6 +2977,33 @@ function buildTestRetrospectNode(sources) {
|
|
|
2357
2977
|
].join("\n\n"),
|
|
2358
2978
|
};
|
|
2359
2979
|
}
|
|
2980
|
+
function buildBackendTestOutcomeGateNode(sources) {
|
|
2981
|
+
const gateCommand = buildBackendTestOutcomeGateShellSnippet();
|
|
2982
|
+
return {
|
|
2983
|
+
id: "backend-test-outcome-gate-shell",
|
|
2984
|
+
depends_on: ["test-retrospect-pi"],
|
|
2985
|
+
role: "verifier",
|
|
2986
|
+
executor: "shell",
|
|
2987
|
+
complexity: "LOW",
|
|
2988
|
+
writePolicy: "read-only",
|
|
2989
|
+
allowedPaths: commonReadOnlyPaths(sources),
|
|
2990
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
2991
|
+
outputContract: "Shell exit 0 only when Result v1 outcome=passed with failed=0 and error=0; non-zero otherwise. Ignores retrospective Markdown.",
|
|
2992
|
+
subtask_prompt: "Gate the backend-test DAG on run-owned Result v1 shell facts only (not retrospective prose).",
|
|
2993
|
+
shell: {
|
|
2994
|
+
commands: [gateCommand],
|
|
2995
|
+
verifyEvidence: buildVerifyEvidence({
|
|
2996
|
+
phase: "final",
|
|
2997
|
+
quota: "full",
|
|
2998
|
+
commandSource: "inline",
|
|
2999
|
+
fallbackCommands: [gateCommand],
|
|
3000
|
+
finalFullRequired: true,
|
|
3001
|
+
}),
|
|
3002
|
+
cwd: ".",
|
|
3003
|
+
timeoutMs: 60000,
|
|
3004
|
+
},
|
|
3005
|
+
};
|
|
3006
|
+
}
|
|
2360
3007
|
const BACKEND_TEST_DEFAULTS = {
|
|
2361
3008
|
...HYBRID_DEFAULTS,
|
|
2362
3009
|
writePolicy: "read-only",
|
|
@@ -2388,6 +3035,9 @@ function buildBackendTestHybridDag(sources) {
|
|
|
2388
3035
|
"review-backend-cases-gate-shell must block pytest generation unless the review verdict is exactly VERDICT: pass.",
|
|
2389
3036
|
"If a target test filename already exists under testcase/, add a numeric suffix (_01, _02, ...); never overwrite or append to existing files.",
|
|
2390
3037
|
"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.",
|
|
3038
|
+
"parse-backend-test-result-shell materializes Backend Test Result v1 from JUnit + pytestExitCode; classify/retrospect run on pass and assertion-fail; backend-test-outcome-gate-shell uses result.outcome only.",
|
|
3039
|
+
"backend-test-case-manifest-shell validates schemaId backend-test-case-manifest-v1 and materializes contracts/backend-test-case-manifest.json; AC coverage is fail-closed and deterministic.",
|
|
3040
|
+
"backend-test-traceability-gate-shell verifies generated file/symbol existence after pytest generation and before execute; models must not invent coverage percentages.",
|
|
2391
3041
|
];
|
|
2392
3042
|
const spec = {
|
|
2393
3043
|
version: 3,
|
|
@@ -2409,12 +3059,20 @@ function buildBackendTestHybridDag(sources) {
|
|
|
2409
3059
|
tasks: [
|
|
2410
3060
|
buildAnalyzeInputsNode(sources),
|
|
2411
3061
|
buildBackendTestAnalysisContractGateNode(sources),
|
|
3062
|
+
buildBackendTestEnvironmentScoutNode(sources),
|
|
3063
|
+
buildBackendTestExecutionContractGateNode(sources),
|
|
2412
3064
|
buildGenerateBackendFunctionalCasesNode(sources),
|
|
3065
|
+
buildEmitBackendCaseManifestNode(sources),
|
|
3066
|
+
buildBackendTestCaseManifestGateNode(sources),
|
|
2413
3067
|
buildReviewBackendCasesNode(sources),
|
|
2414
3068
|
buildReviewBackendCasesGateNode(sources),
|
|
2415
3069
|
buildGenerateBackendPytestNode(sources),
|
|
3070
|
+
buildBackendTestTraceabilityGateNode(sources),
|
|
2416
3071
|
buildExecuteBackendPytestNode(sources),
|
|
3072
|
+
buildParseBackendTestResultNode(sources),
|
|
3073
|
+
buildClassifyBackendTestResultNode(sources),
|
|
2417
3074
|
buildTestRetrospectNode(sources),
|
|
3075
|
+
buildBackendTestOutcomeGateNode(sources),
|
|
2418
3076
|
],
|
|
2419
3077
|
};
|
|
2420
3078
|
applyDefaultReadOnlyRetryPolicy(spec);
|
|
@@ -2427,8 +3085,12 @@ function buildBackendTestHybridDag(sources) {
|
|
|
2427
3085
|
// ---------------------------------------------------------------------------
|
|
2428
3086
|
function buildFrontendTestHybridDag(sources) {
|
|
2429
3087
|
const config = sources.taskConfig.frontendTest ?? { maxCasesPerBatch: 20 };
|
|
2430
|
-
const hasFrontendTestWriteScope = sources.taskConfig.allowedPaths.some((pattern) => pattern === "testcase/frontend/**" ||
|
|
2431
|
-
|
|
3088
|
+
const hasFrontendTestWriteScope = sources.taskConfig.allowedPaths.some((pattern) => pattern === "testcase/frontend/**" ||
|
|
3089
|
+
pattern === "testcase/**" ||
|
|
3090
|
+
pattern === "**");
|
|
3091
|
+
const hasReportWriteScope = sources.taskConfig.allowedPaths.some((pattern) => pattern === "docs/test-reports/**" ||
|
|
3092
|
+
pattern === "docs/**" ||
|
|
3093
|
+
pattern === "**");
|
|
2432
3094
|
if (!hasFrontendTestWriteScope || !hasReportWriteScope) {
|
|
2433
3095
|
throw new Error('frontend-test requires task.json allowedPaths to include both "testcase/frontend/**" and "docs/test-reports/**" (or explicit containing globs).');
|
|
2434
3096
|
}
|
|
@@ -2474,44 +3136,163 @@ function buildFrontendTestHybridDag(sources) {
|
|
|
2474
3136
|
"Browser startup for generated cases must be playwright-cli open --browser=chrome --headed <base-url>.",
|
|
2475
3137
|
"Token settings are post-case stop thresholds, never a hard provider token cap. Unstarted cases after a threshold are blocked: token-budget-exhausted.",
|
|
2476
3138
|
],
|
|
2477
|
-
defaults: {
|
|
3139
|
+
defaults: {
|
|
3140
|
+
...HYBRID_DEFAULTS,
|
|
3141
|
+
writePolicy: "read-only",
|
|
3142
|
+
contextProfile: sources.taskConfig.contextProfile,
|
|
3143
|
+
},
|
|
2478
3144
|
skillsByRole: {
|
|
2479
|
-
planner: ["loop-agent"],
|
|
3145
|
+
planner: ["loop-agent"],
|
|
3146
|
+
scout: ["playwright-cli"],
|
|
3147
|
+
implementer: [
|
|
3148
|
+
"playwright-cli-case-generator",
|
|
3149
|
+
"playwright-cli",
|
|
3150
|
+
"webapp-testing",
|
|
3151
|
+
],
|
|
3152
|
+
reviewer: ["requesting-code-review"],
|
|
3153
|
+
verifier: ["playwright-cli", "webapp-testing"],
|
|
3154
|
+
closeout: ["loop-agent", "verification-before-completion"],
|
|
2480
3155
|
},
|
|
2481
3156
|
executorModels: sources.executorModelMatrix ?? DEFAULT_DAG_EXECUTOR_MODELS,
|
|
2482
3157
|
tasks: [
|
|
2483
3158
|
{
|
|
2484
|
-
id: "retrieve-frontend-test-context-pi",
|
|
3159
|
+
id: "retrieve-frontend-test-context-pi",
|
|
3160
|
+
depends_on: [],
|
|
3161
|
+
role: "planner",
|
|
3162
|
+
executor: "pi",
|
|
3163
|
+
toolProfile: "write",
|
|
3164
|
+
complexity: "HIGH",
|
|
3165
|
+
writePolicy: "exclusive",
|
|
3166
|
+
writeSet: ragWriteSet,
|
|
3167
|
+
allowedPaths: [...commonReadOnlyPaths(sources), ...ragWriteSet],
|
|
3168
|
+
forbiddenPaths: forbidden,
|
|
2485
3169
|
outputContract: "Write testcase/frontend/rag/context.md and coverage-map.md with traceable UI/API/test-environment facts.",
|
|
2486
|
-
subtask_prompt: [
|
|
3170
|
+
subtask_prompt: [
|
|
3171
|
+
"Build the frontend test RAG package.",
|
|
3172
|
+
"Read task source, relevant routes/components/API or Mock facts, existing tests, and execution contract. Write only testcase/frontend/rag/context.md and coverage-map.md.",
|
|
3173
|
+
"Record AC IDs, source paths, routes, states, roles, fixture/data prerequisites, API mapping status, risks, and isolated execution contract. Do not guess unavailable facts.",
|
|
3174
|
+
buildSourceContextBlock(sources),
|
|
3175
|
+
].join("\n\n"),
|
|
2487
3176
|
},
|
|
2488
3177
|
{
|
|
2489
|
-
id: "generate-frontend-functional-cases-pi",
|
|
3178
|
+
id: "generate-frontend-functional-cases-pi",
|
|
3179
|
+
depends_on: ["retrieve-frontend-test-context-pi"],
|
|
3180
|
+
role: "implementer",
|
|
3181
|
+
executor: "pi",
|
|
3182
|
+
toolProfile: "write",
|
|
3183
|
+
complexity: "HIGH",
|
|
3184
|
+
writePolicy: "exclusive",
|
|
3185
|
+
writeSet: casesWriteSet,
|
|
3186
|
+
allowedPaths: [...ragWriteSet, ...casesWriteSet],
|
|
3187
|
+
forbiddenPaths: forbidden,
|
|
2490
3188
|
outputContract: "Write executable Markdown frontend cases, index.md, and manifest.json schemaVersion 1; no test source code.",
|
|
2491
|
-
subtask_prompt: [
|
|
3189
|
+
subtask_prompt: [
|
|
3190
|
+
"Use skill playwright-cli-case-generator.",
|
|
3191
|
+
"Read only testcase/frontend/rag/context.md, testcase/frontend/rag/coverage-map.md, and existing testcase/frontend/cases/. Write only testcase/frontend/cases/**.",
|
|
3192
|
+
"Generate Markdown cases, index.md and manifest.json (schemaVersion 1; cases[] with caseId, casePath, dimension, acIds, evidenceDir). IDs use FE-<FEATURE>-<NNN>-<dimension>; dimensions core|boundary|flow|backend.",
|
|
3193
|
+
"Never infer API fields, constraints, SLA, credentials, or unrecorded test data. Do not create pytest or Playwright source. Every browser start command is: playwright-cli open --browser=chrome --headed <base-url>.",
|
|
3194
|
+
"Each case must be independent, declare its session/preconditions/data cleanup, UI assertions, evidence paths under testcase/frontend/evidence/<case-id>/, and mark unsafe/missing dependencies blocked.",
|
|
3195
|
+
].join("\n\n"),
|
|
2492
3196
|
},
|
|
2493
3197
|
{
|
|
2494
|
-
id: "review-frontend-cases-pi",
|
|
2495
|
-
|
|
3198
|
+
id: "review-frontend-cases-pi",
|
|
3199
|
+
depends_on: ["generate-frontend-functional-cases-pi"],
|
|
3200
|
+
role: "reviewer",
|
|
3201
|
+
executor: "pi",
|
|
3202
|
+
complexity: "HIGH",
|
|
3203
|
+
writePolicy: "read-only",
|
|
3204
|
+
allowedPaths: [...ragWriteSet, ...casesWriteSet],
|
|
3205
|
+
forbiddenPaths: forbidden,
|
|
3206
|
+
outputContract: "First line VERDICT: pass or VERDICT: request-revision, followed by AC-to-case coverage and execution risk findings; no writes.",
|
|
3207
|
+
subtask_prompt: "Review only the RAG package and frontend Markdown cases. Verify traceability, independent execution, safe data/environment handling, manifest correctness, and evidence requirements. The verdict is advisory and does not block case execution.",
|
|
2496
3208
|
},
|
|
2497
3209
|
{
|
|
2498
|
-
id: "materialize-frontend-case-manifest-shell",
|
|
2499
|
-
|
|
3210
|
+
id: "materialize-frontend-case-manifest-shell",
|
|
3211
|
+
depends_on: ["review-frontend-cases-pi"],
|
|
3212
|
+
role: "verifier",
|
|
3213
|
+
executor: "shell",
|
|
3214
|
+
complexity: "LOW",
|
|
3215
|
+
writePolicy: "read-only",
|
|
3216
|
+
allowedPaths: casesWriteSet,
|
|
3217
|
+
forbiddenPaths: forbidden,
|
|
3218
|
+
outputContract: "stdout is exactly JSON { cases: [...] } after deterministic frontend manifest validation.",
|
|
3219
|
+
subtask_prompt: "Validate and materialize the generated frontend case manifest.",
|
|
3220
|
+
shell: { commands: [manifestValidation], cwd: ".", timeoutMs: 120000 },
|
|
2500
3221
|
},
|
|
2501
3222
|
{
|
|
2502
|
-
id: "execute-frontend-cases-map",
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
3223
|
+
id: "execute-frontend-cases-map",
|
|
3224
|
+
depends_on: ["materialize-frontend-case-manifest-shell"],
|
|
3225
|
+
role: "verifier",
|
|
3226
|
+
executor: "static",
|
|
3227
|
+
complexity: "LOW",
|
|
3228
|
+
writePolicy: "none",
|
|
3229
|
+
allowedPaths: [],
|
|
3230
|
+
forbiddenPaths: forbidden,
|
|
3231
|
+
outputContract: "Serial aggregate of case execution summaries, evidence paths, tokens, and token-budget blocked cases.",
|
|
3232
|
+
subtask_prompt: "Expand and execute the validated frontend case manifest serially.",
|
|
3233
|
+
static: { resultMarkdown: "Frontend case map expansion barrier." },
|
|
3234
|
+
dynamicExpansion: {
|
|
3235
|
+
type: "map_agent",
|
|
3236
|
+
workflowNodeId: "execute-frontend-cases-map",
|
|
3237
|
+
itemsFrom: "$.nodes['materialize-frontend-case-manifest-shell'].output.cases",
|
|
3238
|
+
itemName: "case",
|
|
3239
|
+
maxItems: config.maxCasesPerBatch,
|
|
3240
|
+
maxExpandedNodes: config.maxCasesPerBatch,
|
|
3241
|
+
childIdPrefix: "execute-frontend-case",
|
|
3242
|
+
tokenBudget: {
|
|
3243
|
+
maxTokensPerCase: config.maxTokensPerCase,
|
|
3244
|
+
maxTotalTokens: config.maxTotalTokens,
|
|
3245
|
+
},
|
|
3246
|
+
childTask: {
|
|
3247
|
+
executor: "pi",
|
|
3248
|
+
role: "verifier",
|
|
3249
|
+
skills: ["playwright-cli", "webapp-testing"],
|
|
3250
|
+
toolProfile: "write",
|
|
3251
|
+
complexity: "MED",
|
|
3252
|
+
writePolicy: "exclusive",
|
|
3253
|
+
allowedPaths: [
|
|
3254
|
+
"testcase/frontend/cases/{{case.caseId}}.md",
|
|
3255
|
+
"testcase/frontend/rag/context.md",
|
|
3256
|
+
"testcase/frontend/rag/coverage-map.md",
|
|
3257
|
+
`${evidenceRoot}/{{case.caseId}}/**`,
|
|
3258
|
+
],
|
|
3259
|
+
forbiddenPaths: forbidden,
|
|
3260
|
+
writeSet: [`${evidenceRoot}/{{case.caseId}}/**`],
|
|
3261
|
+
outputContract: "Compact JSON <=1200 characters with case status, evidence paths, error summary, and tokens.",
|
|
3262
|
+
subtaskPromptTemplate: [
|
|
3263
|
+
"Execute exactly case {{case.caseId}} from {{case.casePath}} using playwright-cli and webapp-testing. This is a fresh Pi session; do not use /new.",
|
|
3264
|
+
"Use only the declared isolated test environment. If CLI/browser/base URL/credentials/fixture isolation is missing, record blocked rather than installing tools or guessing.",
|
|
3265
|
+
"Use playwright-cli open --browser=chrome --headed <base-url>. Persist execution.md, case-result.json, screenshots/trace/video/logs under {{case.evidenceDir}} before returning.",
|
|
3266
|
+
"A business failed or blocked case is a recorded result, not a node failure. Close the session and return only compact JSON (<=1200 chars): {caseId,status,evidencePaths,errorSummary,tokens}.",
|
|
3267
|
+
].join("\n\n"),
|
|
3268
|
+
},
|
|
3269
|
+
},
|
|
2507
3270
|
},
|
|
2508
3271
|
{
|
|
2509
|
-
id: "review-frontend-execution-pi",
|
|
2510
|
-
|
|
3272
|
+
id: "review-frontend-execution-pi",
|
|
3273
|
+
depends_on: ["execute-frontend-cases-map"],
|
|
3274
|
+
role: "reviewer",
|
|
3275
|
+
executor: "pi",
|
|
3276
|
+
complexity: "HIGH",
|
|
3277
|
+
writePolicy: "read-only",
|
|
3278
|
+
allowedPaths: ["testcase/frontend/**"],
|
|
3279
|
+
forbiddenPaths: forbidden,
|
|
3280
|
+
outputContract: "Read-only AC-to-case-to-browser-evidence review, including failed, blocked and token-budget-exhausted cases.",
|
|
3281
|
+
subtask_prompt: "Review the frontend case aggregate and on-disk case/evidence artifacts. A passed case requires assertion plus screenshot or equivalent browser evidence; failed/blocked cases require reasons. Do not replace browser evidence with model conclusions.",
|
|
2511
3282
|
},
|
|
2512
3283
|
{
|
|
2513
|
-
id: "frontend-test-retrospect-pi",
|
|
2514
|
-
|
|
3284
|
+
id: "frontend-test-retrospect-pi",
|
|
3285
|
+
depends_on: ["review-frontend-execution-pi"],
|
|
3286
|
+
role: "closeout",
|
|
3287
|
+
executor: "pi",
|
|
3288
|
+
toolProfile: "write",
|
|
3289
|
+
complexity: "MED",
|
|
3290
|
+
writePolicy: "exclusive",
|
|
3291
|
+
writeSet: ["docs/test-reports/**"],
|
|
3292
|
+
allowedPaths: ["testcase/frontend/**", "docs/test-reports/**"],
|
|
3293
|
+
forbiddenPaths: forbidden,
|
|
3294
|
+
outputContract: "Write frontend-test-retrospect-<date>.md with coverage, pass/fail/blocked, risks, findings, and A/B/C/D rating.",
|
|
3295
|
+
subtask_prompt: "Write the frontend test retrospective under docs/test-reports/. Summarize coverage, passed/failed/blocked cases (including token-budget-exhausted), review findings, browser anomalies, residual risks, and A/B/C/D rating. Blocked cases never count as passed.",
|
|
2515
3296
|
},
|
|
2516
3297
|
],
|
|
2517
3298
|
};
|
|
@@ -2640,7 +3421,9 @@ export function assertSafeKnowledgeSyncFeatureId(featureId) {
|
|
|
2640
3421
|
if (!KNOWLEDGE_SYNC_FEATURE_ID_RE.test(trimmed)) {
|
|
2641
3422
|
throw new Error(`knowledge-sync featureId must match F-<id> (letters/digits/._- only); got ${JSON.stringify(featureId)}`);
|
|
2642
3423
|
}
|
|
2643
|
-
if (trimmed.includes("..") ||
|
|
3424
|
+
if (trimmed.includes("..") ||
|
|
3425
|
+
trimmed.includes("/") ||
|
|
3426
|
+
trimmed.includes("\\")) {
|
|
2644
3427
|
throw new Error(`knowledge-sync featureId must not contain path segments: ${featureId}`);
|
|
2645
3428
|
}
|
|
2646
3429
|
return trimmed;
|
|
@@ -3060,7 +3843,10 @@ function buildKgBootstrapInventoryNode(sources) {
|
|
|
3060
3843
|
executor: "shell",
|
|
3061
3844
|
complexity: "LOW",
|
|
3062
3845
|
writePolicy: "exclusive",
|
|
3063
|
-
writeSet: [
|
|
3846
|
+
writeSet: [
|
|
3847
|
+
"knowledge/bootstrap/inventory.json",
|
|
3848
|
+
"knowledge/bootstrap/status.yaml",
|
|
3849
|
+
],
|
|
3064
3850
|
allowedPaths: ["knowledge/bootstrap/**", "./**"],
|
|
3065
3851
|
forbiddenPaths: commonForbiddenPaths(sources),
|
|
3066
3852
|
outputContract: "Deterministic inventory.json under knowledge/bootstrap/ from directory and feature signals.",
|
|
@@ -3087,10 +3873,7 @@ function buildKgBootstrapProposeNode(sources) {
|
|
|
3087
3873
|
toolProfile: "write",
|
|
3088
3874
|
complexity: "HIGH",
|
|
3089
3875
|
writePolicy: "exclusive",
|
|
3090
|
-
writeSet: [
|
|
3091
|
-
"knowledge/bootstrap/staging/**",
|
|
3092
|
-
"knowledge/bootstrap/runs/**",
|
|
3093
|
-
],
|
|
3876
|
+
writeSet: ["knowledge/bootstrap/staging/**", "knowledge/bootstrap/runs/**"],
|
|
3094
3877
|
allowedPaths: [
|
|
3095
3878
|
"knowledge/bootstrap/**",
|
|
3096
3879
|
"knowledge/**",
|
|
@@ -3285,11 +4068,7 @@ function buildKgBootstrapPromoteNode(sources) {
|
|
|
3285
4068
|
"knowledge/graph/**",
|
|
3286
4069
|
"features/**",
|
|
3287
4070
|
],
|
|
3288
|
-
forbiddenPaths: [
|
|
3289
|
-
...commonForbiddenPaths(sources),
|
|
3290
|
-
"src/**",
|
|
3291
|
-
"testcase/**",
|
|
3292
|
-
],
|
|
4071
|
+
forbiddenPaths: [...commonForbiddenPaths(sources), "src/**", "testcase/**"],
|
|
3293
4072
|
outputContract: "Promote staging → formal knowledge trees without overwriting existing files; copy edges.manual.yaml if absent.",
|
|
3294
4073
|
subtask_prompt: "B5 promote: merge new files only (no overwrite of existing asserted content).",
|
|
3295
4074
|
shell: {
|
|
@@ -3581,22 +4360,117 @@ function buildWriteSetAuditNode(sources) {
|
|
|
3581
4360
|
].join("\n\n"),
|
|
3582
4361
|
};
|
|
3583
4362
|
}
|
|
4363
|
+
function buildWriteSetAuditFormatRepairNode(sources, options) {
|
|
4364
|
+
return {
|
|
4365
|
+
id: options.id,
|
|
4366
|
+
depends_on: [options.auditNodeId],
|
|
4367
|
+
role: "reviewer",
|
|
4368
|
+
executor: "pi",
|
|
4369
|
+
complexity: "LOW",
|
|
4370
|
+
writePolicy: "read-only",
|
|
4371
|
+
allowedPaths: commonReadOnlyPaths(sources),
|
|
4372
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
4373
|
+
outputContract: "Plain Markdown whose first non-empty line is exactly VERDICT: pass or VERDICT: request-revision, followed by the original audit findings without substantive changes. No file writes.",
|
|
4374
|
+
subtask_prompt: [
|
|
4375
|
+
`Normalize the output format of ${options.auditNodeId}; this is the single read-only format-repair attempt for that audit.`,
|
|
4376
|
+
"First non-empty line must be exactly VERDICT: pass or VERDICT: request-revision.",
|
|
4377
|
+
"If the upstream audit already contains a valid verdict, preserve it exactly. If it omitted or malformed the verdict but states an unambiguous audit conclusion, add only the matching canonical verdict and preserve the findings.",
|
|
4378
|
+
"Do not add, remove, or reclassify substantive findings. If the upstream conclusion is ambiguous or cannot be preserved safely, emit VERDICT: request-revision and report the format ambiguity.",
|
|
4379
|
+
"Do not infer a pass from general prose, expand task allowedPaths, or edit files.",
|
|
4380
|
+
buildSourceContextBlock(sources),
|
|
4381
|
+
].join("\n\n"),
|
|
4382
|
+
};
|
|
4383
|
+
}
|
|
4384
|
+
function buildWriteSetFormatGateNode(sources) {
|
|
4385
|
+
return {
|
|
4386
|
+
id: "write-set-format-gate-shell",
|
|
4387
|
+
depends_on: ["write-set-audit-format-repair-pi"],
|
|
4388
|
+
role: "verifier",
|
|
4389
|
+
executor: "shell",
|
|
4390
|
+
complexity: "LOW",
|
|
4391
|
+
writePolicy: "read-only",
|
|
4392
|
+
allowedPaths: commonReadOnlyPaths(sources),
|
|
4393
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
4394
|
+
outputContract: "Deterministic initial write-set verdict format gate: accept pass or request-revision so the bounded plan-revision stage can run; reject missing or unexpected verdicts.",
|
|
4395
|
+
subtask_prompt: "Validate the normalized initial write-set audit verdict before the bounded plan-revision stage. This gate does not authorize implementation writes.",
|
|
4396
|
+
shell: {
|
|
4397
|
+
commands: [],
|
|
4398
|
+
verdictGate: {
|
|
4399
|
+
fromNodeId: "write-set-audit-format-repair-pi",
|
|
4400
|
+
accept: ["VERDICT: pass", "VERDICT: request-revision"],
|
|
4401
|
+
label: "initial write-set audit format",
|
|
4402
|
+
lineMode: "first-verdict-line",
|
|
4403
|
+
},
|
|
4404
|
+
cwd: ".",
|
|
4405
|
+
timeoutMs: 60000,
|
|
4406
|
+
},
|
|
4407
|
+
};
|
|
4408
|
+
}
|
|
4409
|
+
function buildPlanRevisionNode(sources) {
|
|
4410
|
+
return {
|
|
4411
|
+
id: "plan-revision-pi",
|
|
4412
|
+
depends_on: [
|
|
4413
|
+
"write-set-format-gate-shell",
|
|
4414
|
+
"plan-pi",
|
|
4415
|
+
"write-set-audit-format-repair-pi",
|
|
4416
|
+
],
|
|
4417
|
+
role: "planner",
|
|
4418
|
+
executor: "pi",
|
|
4419
|
+
complexity: "MED",
|
|
4420
|
+
writePolicy: "read-only",
|
|
4421
|
+
allowedPaths: commonReadOnlyPaths(sources),
|
|
4422
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
4423
|
+
outputContract: "PASS_NO_REVISION_NEEDED when the normalized initial audit passed, otherwise a complete revised implementation plan with a corrected WriteSet Coverage Matrix. No file writes.",
|
|
4424
|
+
subtask_prompt: [
|
|
4425
|
+
"Perform the single bounded plan-revision round after the normalized initial write-set audit.",
|
|
4426
|
+
"If the normalized verdict is VERDICT: pass, output PASS_NO_REVISION_NEEDED and do not change the original plan.",
|
|
4427
|
+
"If it is VERDICT: request-revision, return a complete revised plan that resolves every audit finding and includes a corrected WriteSet Coverage Matrix.",
|
|
4428
|
+
"Do not expand task.json.allowedPaths, weaken forbiddenPaths, or edit files.",
|
|
4429
|
+
buildSourceContextBlock(sources),
|
|
4430
|
+
].join("\n\n"),
|
|
4431
|
+
};
|
|
4432
|
+
}
|
|
4433
|
+
function buildFinalWriteSetAuditNode(sources) {
|
|
4434
|
+
return {
|
|
4435
|
+
id: "final-write-set-audit-pi",
|
|
4436
|
+
depends_on: [
|
|
4437
|
+
"plan-pi",
|
|
4438
|
+
"plan-revision-pi",
|
|
4439
|
+
"write-set-audit-format-repair-pi",
|
|
4440
|
+
],
|
|
4441
|
+
role: "reviewer",
|
|
4442
|
+
executor: "pi",
|
|
4443
|
+
complexity: "MED",
|
|
4444
|
+
writePolicy: "read-only",
|
|
4445
|
+
allowedPaths: commonReadOnlyPaths(sources),
|
|
4446
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
4447
|
+
outputContract: "Plain Markdown whose first non-empty line is VERDICT: pass or VERDICT: request-revision; includes final writeSet coverage findings after the single plan-revision round. No file writes.",
|
|
4448
|
+
subtask_prompt: [
|
|
4449
|
+
"Perform the final write-set audit after the single bounded plan-revision round.",
|
|
4450
|
+
"When plan-revision-pi returned PASS_NO_REVISION_NEEDED, audit the original plan-pi output. Otherwise audit the complete revised plan.",
|
|
4451
|
+
"First non-empty line must be exactly VERDICT: pass or VERDICT: request-revision.",
|
|
4452
|
+
"Request revision if required files still lack a single exclusive owner, writeSet is broad/placeholder, forbidden paths overlap, or any initial finding remains unresolved.",
|
|
4453
|
+
"Do not expand task allowedPaths or edit files.",
|
|
4454
|
+
buildSourceContextBlock(sources),
|
|
4455
|
+
].join("\n\n"),
|
|
4456
|
+
};
|
|
4457
|
+
}
|
|
3584
4458
|
function buildWriteSetGateNode(sources) {
|
|
3585
4459
|
return {
|
|
3586
4460
|
id: "write-set-gate-shell",
|
|
3587
|
-
depends_on: ["write-set-audit-pi"],
|
|
4461
|
+
depends_on: ["final-write-set-audit-format-repair-pi"],
|
|
3588
4462
|
role: "verifier",
|
|
3589
4463
|
executor: "shell",
|
|
3590
4464
|
complexity: "LOW",
|
|
3591
4465
|
writePolicy: "read-only",
|
|
3592
4466
|
allowedPaths: commonReadOnlyPaths(sources),
|
|
3593
4467
|
forbiddenPaths: commonForbiddenPaths(sources),
|
|
3594
|
-
outputContract: "Deterministic write-set audit verdict gate: exit 0 only when write-set-audit-pi
|
|
3595
|
-
subtask_prompt: "Deterministic gate: block the implementation writer unless write-set
|
|
4468
|
+
outputContract: "Deterministic final write-set audit verdict gate: exit 0 only when final-write-set-audit-format-repair-pi emits VERDICT: pass after the bounded revision round.",
|
|
4469
|
+
subtask_prompt: "Deterministic gate: block the implementation writer unless the normalized final write-set audit emitted VERDICT: pass.",
|
|
3596
4470
|
shell: {
|
|
3597
4471
|
commands: [],
|
|
3598
4472
|
verdictGate: {
|
|
3599
|
-
fromNodeId: "write-set-audit-pi",
|
|
4473
|
+
fromNodeId: "final-write-set-audit-format-repair-pi",
|
|
3600
4474
|
accept: ["VERDICT: pass"],
|
|
3601
4475
|
label: "write-set audit",
|
|
3602
4476
|
lineMode: "first-verdict-line",
|
|
@@ -3717,7 +4591,9 @@ function buildRepairNode(sources) {
|
|
|
3717
4591
|
"If VERDICT: pass, return no-op with evidence. Re-run focused tests when you change code.",
|
|
3718
4592
|
writerDeliveryContract(sources.taskConfig),
|
|
3719
4593
|
buildSourceContextBlock(sources),
|
|
3720
|
-
]
|
|
4594
|
+
]
|
|
4595
|
+
.filter((value) => Boolean(value))
|
|
4596
|
+
.join("\n\n"),
|
|
3721
4597
|
};
|
|
3722
4598
|
}
|
|
3723
4599
|
function buildHardVerifyNode(sources) {
|
|
@@ -3769,7 +4645,8 @@ function buildDecisionNode(sources) {
|
|
|
3769
4645
|
outputContract: "Markdown with exactly one DECISION_ENVELOPE_JSON fenced block plus evidence summary. No file writes.",
|
|
3770
4646
|
subtask_prompt: [
|
|
3771
4647
|
"Return an advisory Decision Gate envelope for the supervised DAG outcome.",
|
|
3772
|
-
"
|
|
4648
|
+
"Read-only: do not modify files. Review deterministic verification, review findings, write boundaries, and risks before deciding.",
|
|
4649
|
+
buildDecisionEnvelopePromptContract(),
|
|
3773
4650
|
buildSourceContextBlock(sources),
|
|
3774
4651
|
].join("\n\n"),
|
|
3775
4652
|
decisionGate: { enabled: true, schemaVersion: 1, mode: "record-only" },
|
|
@@ -3807,8 +4684,26 @@ function buildSupervisedHybridDag(standard, sources) {
|
|
|
3807
4684
|
cloneTask(scoutTests),
|
|
3808
4685
|
cloneTask(plan),
|
|
3809
4686
|
buildWriteSetAuditNode(sources),
|
|
4687
|
+
buildWriteSetAuditFormatRepairNode(sources, {
|
|
4688
|
+
id: "write-set-audit-format-repair-pi",
|
|
4689
|
+
auditNodeId: "write-set-audit-pi",
|
|
4690
|
+
}),
|
|
4691
|
+
buildWriteSetFormatGateNode(sources),
|
|
4692
|
+
buildPlanRevisionNode(sources),
|
|
4693
|
+
buildFinalWriteSetAuditNode(sources),
|
|
4694
|
+
buildWriteSetAuditFormatRepairNode(sources, {
|
|
4695
|
+
id: "final-write-set-audit-format-repair-pi",
|
|
4696
|
+
auditNodeId: "final-write-set-audit-pi",
|
|
4697
|
+
}),
|
|
3810
4698
|
buildWriteSetGateNode(sources),
|
|
3811
|
-
cloneTask(implement, {
|
|
4699
|
+
cloneTask(implement, {
|
|
4700
|
+
depends_on: [
|
|
4701
|
+
"write-set-gate-shell",
|
|
4702
|
+
"plan-pi",
|
|
4703
|
+
"plan-revision-pi",
|
|
4704
|
+
"final-write-set-audit-format-repair-pi",
|
|
4705
|
+
],
|
|
4706
|
+
}),
|
|
3812
4707
|
buildSoftVerifyNode(sources),
|
|
3813
4708
|
buildProcessSupervisorNode(sources),
|
|
3814
4709
|
buildProcessGateNode(sources),
|