@tea-agent/loop-agent 0.16.18 → 0.16.20
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/CHANGELOG.md +28 -5
- package/README.md +8 -0
- package/dist/cli/command-definitions.js +4 -2
- package/dist/cli/program.js +2 -1
- package/dist/cli/update/init-surface-notifier.js +167 -0
- package/dist/cli/update/policy.js +36 -1
- package/dist/cli/update/runtime-activity.js +29 -0
- package/dist/cli.js +14 -2
- package/dist/commands/init.js +85 -2
- package/dist/executors/shell-executor.js +92 -66
- package/dist/executors/shell-write-guard.js +5 -0
- package/dist/shared/runtime-activity.js +6 -0
- package/dist/worker/observability/read-model.js +10 -7
- package/dist/worker/observe/static/views/session-timeline.js +1 -1
- package/dist/workflows/dag/backend-test-case-manifest.js +13 -3
- package/dist/workflows/dag/backend-test-classification-contract.js +38 -0
- package/dist/workflows/dag/backend-test-contract-envelope.js +167 -0
- package/dist/workflows/dag/backend-test-semantic-review-contract.js +2 -2
- package/dist/workflows/dag/frontend-test-result-contract.js +16 -10
- package/dist/workflows/dag/init-hybrid.js +47 -408
- package/dist/workflows/dag/node-execution.js +4 -3
- package/dist/workflows/dag/types.js +1 -5
- package/docs/README.md +1 -0
- package/docs/templates/agent-dag.schema.json +1 -1
- package/docs/templates/backend-test-case-manifest.schema.json +35 -2
- package/docs/templates/backend-test-dag.json +39 -340
- package/docs/templates/backend-test-dag.review-cases.prompt.md +4 -4
- package/package.json +1 -1
- package/skills/loop-agent/references/command-reference.md +4 -0
- package/skills/loop-agent/references/hybrid-dag.md +2 -2
- package/dist/workflows/dag/backend-test-repair-contract.js +0 -94
|
@@ -18,7 +18,6 @@ import { resolveVerifyPreset } from "../../executors/shell-verification.js";
|
|
|
18
18
|
import { resolveExecutorModelMatrices } from "../../executors/model-routing.js";
|
|
19
19
|
import { normalizeTaskRequirementText, resolveTaskDagTemplateSelection, } from "./task-demand-routing.js";
|
|
20
20
|
import { BACKEND_TEST_EXECUTION_DEFAULT_TEST_ROOT, buildBackendTestExecutionPreflightShellSnippet, } from "./backend-test-execution-contract.js";
|
|
21
|
-
import { buildBackendTestEffectiveResultSelectorShellSnippet, buildBackendTestRepairEligibilityShellSnippet, buildBackendTestRepairSafetyShellSnippet, } from "./backend-test-repair-contract.js";
|
|
22
21
|
import { buildBackendTestOutcomeGateShellSnippet } from "./backend-test-result-contract.js";
|
|
23
22
|
import { buildFrontendTestOutcomeGateShellSnippet } from "./frontend-test-result-contract.js";
|
|
24
23
|
import { classifyFrontendRisk, } from "./frontend-risk.js";
|
|
@@ -2682,7 +2681,7 @@ function buildGenerateBackendFunctionalCasesNode(sources) {
|
|
|
2682
2681
|
}
|
|
2683
2682
|
const BACKEND_TEST_CASE_MANIFEST_OUTPUT_INSTRUCTIONS = [
|
|
2684
2683
|
"The final fenced JSON block is authoritative and MUST conform exactly to Backend Test Case Manifest v1.",
|
|
2685
|
-
"Top-level keys MUST be exactly: schemaVersion, sourceBinding, cases, evidenceGaps
|
|
2684
|
+
"Top-level keys MUST be exactly: schemaVersion, sourceBinding, cases, evidenceGaps. Do NOT emit coverageSummary — the shell materializer always computes it from sourceBinding/cases/evidenceGaps. Set schemaVersion to numeric 1. Do NOT emit schemaId, manifestType, taskId, modules, acCoverage, brCoverage, dataIsolation, readiness, or other custom top-level keys.",
|
|
2686
2685
|
"Copy sourceBinding exactly from contracts/backend-test-analysis.json: taskId, requirementPath, requirementSha256, referencePaths, requirementIds. Preserve Unicode paths exactly; never replace characters in source/需求.md or other paths.",
|
|
2687
2686
|
"Each cases[] item MUST use exactly: caseId, non-empty acIds, title, category, automationStatus; optional endpointRef, ruleRefs, file, symbol, gapReason, evidenceRef. Do NOT use id, module, brIds, endpoint, or priority.",
|
|
2688
2687
|
"category MUST be exactly one of: positive, negative, boundary, state-transition, auth, timeout, concurrency, other.",
|
|
@@ -2690,7 +2689,7 @@ const BACKEND_TEST_CASE_MANIFEST_OUTPUT_INSTRUCTIONS = [
|
|
|
2690
2689
|
"Each evidenceGaps[] item MUST use exactly: optional acId, optional caseId, required description, optional evidenceRef. Every gap requires at least acId or caseId. Do NOT use requirementId, relatedBrIds, or sourceRef.",
|
|
2691
2690
|
"Every case must map to at least one semantically applicable explicit AC-* in acIds. If no AC applies, omit that case and bind an evidence gap to the nearest applicable acId or caseId; never emit an unbound informational gap.",
|
|
2692
2691
|
"acIds MUST exactly match the explicit AC-* values in the written case body; do not infer ACs from Business Rules or summary matrices.",
|
|
2693
|
-
"Use full BE-<MODULE>-<NNN> caseId strings. Do not invent coverage percentages
|
|
2692
|
+
"Use full BE-<MODULE>-<NNN> caseId strings. Do not invent coverage percentages and do not emit coverageSummary; shell always writes the canonical summary.",
|
|
2694
2693
|
"Minimal shape example: {\"schemaVersion\":1,\"sourceBinding\":{\"taskId\":\"...\",\"requirementPath\":\"source/需求.md\",\"requirementSha256\":\"<64 lowercase hex>\",\"referencePaths\":[],\"requirementIds\":[\"AC-001\"]},\"cases\":[{\"caseId\":\"BE-MODULE-001\",\"acIds\":[\"AC-001\"],\"title\":\"...\",\"category\":\"positive\",\"automationStatus\":\"planned\",\"evidenceRef\":\"testcase/md/module.md\"}],\"evidenceGaps\":[]}",
|
|
2695
2694
|
].join("\n\n");
|
|
2696
2695
|
function buildEmitBackendCaseManifestNode(sources, options) {
|
|
@@ -2769,10 +2768,8 @@ function buildBackendTestTraceabilityGateNode(sources, options = {}) {
|
|
|
2769
2768
|
};
|
|
2770
2769
|
}
|
|
2771
2770
|
function buildReviewBackendCasesNode(sources, options) {
|
|
2772
|
-
const phase = options?.phase ?? "first";
|
|
2773
|
-
const isFinal = phase === "final";
|
|
2774
2771
|
return {
|
|
2775
|
-
id:
|
|
2772
|
+
id: "review-backend-cases-pi",
|
|
2776
2773
|
depends_on: options?.dependsOn ?? [
|
|
2777
2774
|
"backend-test-case-manifest-shell",
|
|
2778
2775
|
"backend-test-analysis-contract-shell",
|
|
@@ -2783,13 +2780,9 @@ function buildReviewBackendCasesNode(sources, options) {
|
|
|
2783
2780
|
writePolicy: "read-only",
|
|
2784
2781
|
allowedPaths: commonReadOnlyPaths(sources),
|
|
2785
2782
|
forbiddenPaths: commonForbiddenPaths(sources),
|
|
2786
|
-
outputContract:
|
|
2787
|
-
? "Plain Markdown whose first non-empty line is VERDICT: pass or VERDICT: request-revision after at most one revision; followed by Findings and Coverage Assessment. No file writes. Final gate accepts pass only."
|
|
2788
|
-
: "Plain Markdown whose first non-empty line is VERDICT: pass or VERDICT: request-revision; followed by Findings, Coverage Assessment, and Required revisions when requesting revision. No file writes.",
|
|
2783
|
+
outputContract: "Plain Markdown whose first non-empty line is VERDICT: pass or VERDICT: request-revision; followed by Findings and Coverage Assessment. No file writes. The deterministic gate accepts pass only; request-revision ends this run.",
|
|
2789
2784
|
subtask_prompt: [
|
|
2790
|
-
|
|
2791
|
-
? "Final review of backend functional cases under testcase/md/ and the re-validated Case Manifest v1 after at most one revision pass."
|
|
2792
|
-
: "Review the generated backend functional test cases under testcase/md/ and the validated Case Manifest v1.",
|
|
2785
|
+
"Review the generated backend functional test cases under testcase/md/ and the validated Case Manifest v1.",
|
|
2793
2786
|
"",
|
|
2794
2787
|
"## Mandatory First Line:",
|
|
2795
2788
|
"First non-empty line must be exactly: VERDICT: pass or VERDICT: request-revision",
|
|
@@ -2818,15 +2811,13 @@ function buildReviewBackendCasesNode(sources, options) {
|
|
|
2818
2811
|
"## Verdict Rules:",
|
|
2819
2812
|
"- All Critical checks pass + Important findings ≤ 2 → VERDICT: pass",
|
|
2820
2813
|
"- Any Critical fails OR Important > 2 → VERDICT: request-revision",
|
|
2821
|
-
|
|
2822
|
-
? "- This is the FINAL review after one revision opportunity; remaining Critical issues must still request-revision (final gate will stop the DAG)."
|
|
2823
|
-
: "- When requesting revision, list numbered Required revisions concrete enough for revise-backend-cases-pi to edit testcase/md/**.",
|
|
2814
|
+
"- Any request-revision verdict ends the current run at the deterministic gate; describe findings clearly for an independent follow-up task.",
|
|
2824
2815
|
"",
|
|
2825
2816
|
"## Output After Verdict:",
|
|
2826
2817
|
"1. Coverage Assessment table (AC → full BE-* case IDs) using manifest + MD",
|
|
2827
2818
|
"2. Findings list (Critical/Important/Informational)",
|
|
2828
2819
|
"3. Statistics (total cases, positive/negative/boundary breakdown)",
|
|
2829
|
-
|
|
2820
|
+
"4. Required follow-up actions (only when request-revision; no in-run writer)",
|
|
2830
2821
|
"",
|
|
2831
2822
|
"## Constraints:",
|
|
2832
2823
|
"- Read-only: do not modify files",
|
|
@@ -2837,69 +2828,6 @@ function buildReviewBackendCasesNode(sources, options) {
|
|
|
2837
2828
|
.join("\n\n"),
|
|
2838
2829
|
};
|
|
2839
2830
|
}
|
|
2840
|
-
function buildReviewBackendCasesBranchConditionNode(sources) {
|
|
2841
|
-
return {
|
|
2842
|
-
id: "review-backend-cases-branch-condition",
|
|
2843
|
-
depends_on: ["review-backend-cases-pi"],
|
|
2844
|
-
role: "verifier",
|
|
2845
|
-
executor: "static",
|
|
2846
|
-
complexity: "LOW",
|
|
2847
|
-
writePolicy: "none",
|
|
2848
|
-
allowedPaths: commonReadOnlyPaths(sources),
|
|
2849
|
-
forbiddenPaths: commonForbiddenPaths(sources),
|
|
2850
|
-
outputContract: "Dynamic condition: select direct pass vs single revision chain from first review firstVerdictLine; malformed VERDICT fails closed (no default). The pass target is the already-finished first review, so the effective gate remains outside the exclusive branch targets.",
|
|
2851
|
-
subtask_prompt: "Branch on review-backend-cases-pi VERDICT: pass → already-finished first review (revision chain skipped); request-revision → revise-backend-cases-pi.",
|
|
2852
|
-
static: {
|
|
2853
|
-
resultMarkdown: "Backend case review branch condition (direct pass vs single revision).",
|
|
2854
|
-
},
|
|
2855
|
-
dynamicCondition: {
|
|
2856
|
-
workflowNodeId: "review-backend-cases-branch-condition",
|
|
2857
|
-
cases: [
|
|
2858
|
-
{
|
|
2859
|
-
when: "$.nodes['review-backend-cases-pi'].firstVerdictLine == 'VERDICT: pass'",
|
|
2860
|
-
then: "review-backend-cases-pi",
|
|
2861
|
-
},
|
|
2862
|
-
{
|
|
2863
|
-
when: "$.nodes['review-backend-cases-pi'].firstVerdictLine == 'VERDICT: request-revision'",
|
|
2864
|
-
then: "revise-backend-cases-pi",
|
|
2865
|
-
},
|
|
2866
|
-
],
|
|
2867
|
-
},
|
|
2868
|
-
};
|
|
2869
|
-
}
|
|
2870
|
-
function buildReviseBackendCasesNode(sources) {
|
|
2871
|
-
return {
|
|
2872
|
-
id: "revise-backend-cases-pi",
|
|
2873
|
-
depends_on: ["review-backend-cases-branch-condition"],
|
|
2874
|
-
role: "implementer",
|
|
2875
|
-
executor: "pi",
|
|
2876
|
-
toolProfile: "write",
|
|
2877
|
-
complexity: "MED",
|
|
2878
|
-
writePolicy: "exclusive",
|
|
2879
|
-
writeSet: ["testcase/md/**"],
|
|
2880
|
-
allowedPaths: ["testcase/md/**"],
|
|
2881
|
-
forbiddenPaths: commonForbiddenPaths(sources),
|
|
2882
|
-
outputContract: "Only scheduled when first review is VERDICT: request-revision. Apply Required revisions under testcase/md/** then summarize changes (single revision pass).",
|
|
2883
|
-
subtask_prompt: [
|
|
2884
|
-
"You are the single backend case revision pass (max one per DAG run).",
|
|
2885
|
-
"This node is only scheduled when review-backend-cases-pi emitted VERDICT: request-revision.",
|
|
2886
|
-
"",
|
|
2887
|
-
"## Inputs",
|
|
2888
|
-
"- First review: review-backend-cases-pi Markdown (VERDICT + Findings + Required revisions)",
|
|
2889
|
-
"- Current cases: testcase/md/**",
|
|
2890
|
-
"- Validated analysis + case manifest under the current run contracts/",
|
|
2891
|
-
"",
|
|
2892
|
-
"## Required work",
|
|
2893
|
-
"1. Edit only testcase/md/** to address every Critical finding and Required revision item",
|
|
2894
|
-
"2. Fix AC matrices to list FULL BE-* ids matching case bodies; remove false 'all cases' AC claims",
|
|
2895
|
-
"3. Keep BE-<MODULE>-<NNN> ids stable when possible; do not invent out-of-scope AC coverage",
|
|
2896
|
-
"4. Stay within writeSet; do not write pytest or production code",
|
|
2897
|
-
"5. End with a short summary of files touched",
|
|
2898
|
-
"",
|
|
2899
|
-
"Downstream will re-emit and re-validate Case Manifest v1, then run a FINAL review (pass-only gate).",
|
|
2900
|
-
].join("\n\n"),
|
|
2901
|
-
};
|
|
2902
|
-
}
|
|
2903
2831
|
function buildReviewBackendCasesGateNode(sources) {
|
|
2904
2832
|
return {
|
|
2905
2833
|
id: "review-backend-cases-gate-shell",
|
|
@@ -3053,21 +2981,18 @@ function buildGenerateBackendPytestNode(sources) {
|
|
|
3053
2981
|
"- If a test filename exists, add suffix: test_order.py → test_order_01.py",
|
|
3054
2982
|
"- Do NOT re-read source documents — use reviewed cases under testcase/md/ and upstream analyze-inputs-pi output only",
|
|
3055
2983
|
"- Read existing conftest.py/pytest.ini to understand conventions, but do NOT modify them",
|
|
3056
|
-
"- Do NOT execute pytest/python -m pytest or npm test in this node;
|
|
2984
|
+
"- Do NOT execute pytest/python -m pytest or npm test in this node; the single execution is owned by the dedicated shell node. Local smoke runs create __pycache__/.pytest_cache and are unnecessary here.",
|
|
3057
2985
|
].join("\n\n"),
|
|
3058
2986
|
};
|
|
3059
2987
|
}
|
|
3060
2988
|
function buildBackendTestSemanticReviewNode(sources, options = {}) {
|
|
3061
|
-
const final = options.final ?? false;
|
|
3062
2989
|
return {
|
|
3063
|
-
id:
|
|
2990
|
+
id: "review-generated-backend-pytest-pi",
|
|
3064
2991
|
depends_on: options.dependsOn ?? [
|
|
3065
2992
|
"generate-backend-pytest-pi",
|
|
3066
2993
|
"backend-test-analysis-contract-shell",
|
|
3067
2994
|
"backend-test-case-manifest-shell",
|
|
3068
|
-
"backend-test-case-manifest-final-shell",
|
|
3069
2995
|
],
|
|
3070
|
-
dependsPolicy: "all-or-condition-skip",
|
|
3071
2996
|
role: "reviewer",
|
|
3072
2997
|
executor: "pi",
|
|
3073
2998
|
complexity: "MED",
|
|
@@ -3076,7 +3001,7 @@ function buildBackendTestSemanticReviewNode(sources, options = {}) {
|
|
|
3076
3001
|
forbiddenPaths: commonForbiddenPaths(sources),
|
|
3077
3002
|
outputContract: "Pure Backend Test Semantic Review v1 JSON: verdict, findings[], summary. No file writes.",
|
|
3078
3003
|
subtask_prompt: [
|
|
3079
|
-
|
|
3004
|
+
"Review generated pytest semantics before the single execution.",
|
|
3080
3005
|
"Use only compact authoritative inputs: contracts/backend-test-analysis.json, contracts/backend-test-case-manifest.json, testcase/md/**, and generated testcase/**/test_*.py/helpers/factories.",
|
|
3081
3006
|
"Return exactly one pure JSON object with only verdict, findings, summary; no Markdown fence or surrounding prose.",
|
|
3082
3007
|
"verdict must be pass or request-revision. Each findings[] item must contain exactly severity, caseId, testFile, testSymbol, contractRefs, issue, requiredChange.",
|
|
@@ -3089,98 +3014,6 @@ function buildBackendTestSemanticReviewNode(sources, options = {}) {
|
|
|
3089
3014
|
].join("\n\n"),
|
|
3090
3015
|
};
|
|
3091
3016
|
}
|
|
3092
|
-
function buildBackendTestSemanticReviewGateNode(sources, options = {}) {
|
|
3093
|
-
const fromNodeId = options.fromNodeId ?? "review-generated-backend-pytest-pi";
|
|
3094
|
-
return {
|
|
3095
|
-
id: options.id ?? "backend-test-semantic-review-shell",
|
|
3096
|
-
depends_on: options.dependsOn ?? [fromNodeId],
|
|
3097
|
-
role: "verifier",
|
|
3098
|
-
executor: "shell",
|
|
3099
|
-
complexity: "LOW",
|
|
3100
|
-
writePolicy: "read-only",
|
|
3101
|
-
allowedPaths: commonReadOnlyPaths(sources),
|
|
3102
|
-
forbiddenPaths: commonForbiddenPaths(sources),
|
|
3103
|
-
outputContract: "Validated run-owned Backend Test Semantic Review v1 artifact.",
|
|
3104
|
-
subtask_prompt: "Validate semantic review JSON before branch selection.",
|
|
3105
|
-
shell: {
|
|
3106
|
-
commands: [],
|
|
3107
|
-
jsonArtifactGate: {
|
|
3108
|
-
fromNodeId,
|
|
3109
|
-
schemaId: "backend-test-semantic-review-v1",
|
|
3110
|
-
artifactName: options.id?.includes("final") ? "backend-test-semantic-review-final.json" : "backend-test-semantic-review.json",
|
|
3111
|
-
outputDir: "contracts",
|
|
3112
|
-
},
|
|
3113
|
-
cwd: ".",
|
|
3114
|
-
timeoutMs: 60000,
|
|
3115
|
-
},
|
|
3116
|
-
};
|
|
3117
|
-
}
|
|
3118
|
-
function buildBackendTestSemanticReviewConditionNode(sources) {
|
|
3119
|
-
return {
|
|
3120
|
-
id: "backend-test-semantic-review-condition",
|
|
3121
|
-
depends_on: ["backend-test-semantic-review-shell"],
|
|
3122
|
-
role: "verifier",
|
|
3123
|
-
executor: "static",
|
|
3124
|
-
complexity: "LOW",
|
|
3125
|
-
writePolicy: "none",
|
|
3126
|
-
allowedPaths: [],
|
|
3127
|
-
forbiddenPaths: commonForbiddenPaths(sources),
|
|
3128
|
-
outputContract: "Select direct semantic pass or one testcase-only revision; the pass target is the already-finished validated semantic review so the final gate remains outside the exclusive branch targets.",
|
|
3129
|
-
subtask_prompt: "Branch deterministically from semantic review verdict.",
|
|
3130
|
-
static: { resultMarkdown: "Backend pytest semantic review selector." },
|
|
3131
|
-
dynamicCondition: {
|
|
3132
|
-
workflowNodeId: "backend-test-semantic-review-condition",
|
|
3133
|
-
cases: [
|
|
3134
|
-
{ when: "$.nodes['backend-test-semantic-review-shell'].json.verdict == 'pass'", then: "backend-test-semantic-review-shell" },
|
|
3135
|
-
],
|
|
3136
|
-
default: "revise-generated-backend-pytest-pi",
|
|
3137
|
-
},
|
|
3138
|
-
};
|
|
3139
|
-
}
|
|
3140
|
-
function buildReviseGeneratedBackendPytestNode(sources) {
|
|
3141
|
-
const writeSet = ["testcase/**/test_*.py", "testcase/**/helpers/**", "testcase/**/factories/**"];
|
|
3142
|
-
return {
|
|
3143
|
-
id: "revise-generated-backend-pytest-pi",
|
|
3144
|
-
depends_on: ["backend-test-semantic-review-condition"],
|
|
3145
|
-
role: "implementer",
|
|
3146
|
-
executor: "pi",
|
|
3147
|
-
toolProfile: "write",
|
|
3148
|
-
complexity: "MED",
|
|
3149
|
-
writePolicy: "exclusive",
|
|
3150
|
-
writeSet,
|
|
3151
|
-
allowedPaths: writeSet,
|
|
3152
|
-
forbiddenPaths: [...commonForbiddenPaths(sources), "apps/**", "src/**", "testcase/md/**", "**/conftest.py", "**/pytest.ini"],
|
|
3153
|
-
outputContract: "Apply one bounded semantic correction to generated pytest from validated findings only.",
|
|
3154
|
-
subtask_prompt: [
|
|
3155
|
-
"This is the only pre-execution generated-pytest semantic revision (1/1).",
|
|
3156
|
-
"Read contracts/backend-test-semantic-review.json and edit only the cited generated test files/helpers/factories.",
|
|
3157
|
-
"Preserve case IDs, symbols, test count, target mode, base URL and real-service path.",
|
|
3158
|
-
"Do not delete tests, add skip/xfail, swallow failures, substitute mocks, or weaken assertions beyond the formal comparison/shape contract.",
|
|
3159
|
-
"Do not modify product code, testcase/md/**, conftest.py, pytest.ini, or .harness/**.",
|
|
3160
|
-
].join("\n\n"),
|
|
3161
|
-
};
|
|
3162
|
-
}
|
|
3163
|
-
function buildBackendTestSemanticFinalGateNode(sources) {
|
|
3164
|
-
return {
|
|
3165
|
-
id: "backend-test-semantic-final-gate-shell",
|
|
3166
|
-
depends_on: ["backend-test-semantic-review-condition", "backend-test-semantic-review-final-shell"],
|
|
3167
|
-
dependsPolicy: "all-or-condition-skip",
|
|
3168
|
-
role: "verifier",
|
|
3169
|
-
executor: "shell",
|
|
3170
|
-
complexity: "LOW",
|
|
3171
|
-
writePolicy: "read-only",
|
|
3172
|
-
allowedPaths: commonReadOnlyPaths(sources),
|
|
3173
|
-
forbiddenPaths: commonForbiddenPaths(sources),
|
|
3174
|
-
outputContract: "Pass-only semantic authorization for traceability and initial pytest.",
|
|
3175
|
-
subtask_prompt: "Accept initial pass path or final semantic review pass; fail closed otherwise.",
|
|
3176
|
-
shell: {
|
|
3177
|
-
commands: [],
|
|
3178
|
-
backendTestPipeline: "semantic-effective",
|
|
3179
|
-
cwd: ".",
|
|
3180
|
-
timeoutMs: 60000,
|
|
3181
|
-
},
|
|
3182
|
-
};
|
|
3183
|
-
}
|
|
3184
3017
|
function collectBackendTestShellEnvAllowlist(sources) {
|
|
3185
3018
|
const names = new Set();
|
|
3186
3019
|
for (const verify of sources.taskConfig.verifyCommands) {
|
|
@@ -3228,7 +3061,7 @@ function buildExecuteBackendPytestNode(sources, options = {}) {
|
|
|
3228
3061
|
return {
|
|
3229
3062
|
id: nodeId,
|
|
3230
3063
|
depends_on: options.dependsOn ?? [
|
|
3231
|
-
"backend-test-semantic-
|
|
3064
|
+
"backend-test-semantic-gate-shell",
|
|
3232
3065
|
"backend-test-execution-contract-shell",
|
|
3233
3066
|
],
|
|
3234
3067
|
role: "verifier",
|
|
@@ -3254,41 +3087,10 @@ function buildExecuteBackendPytestNode(sources, options = {}) {
|
|
|
3254
3087
|
},
|
|
3255
3088
|
};
|
|
3256
3089
|
}
|
|
3257
|
-
function buildParseBackendTestResultNode(sources, options = {}) {
|
|
3258
|
-
const id = options.id ?? "parse-backend-test-result-shell";
|
|
3259
|
-
const fromNodeId = options.fromNodeId ?? "execute-backend-pytest-shell";
|
|
3260
|
-
const artifactName = options.artifactName ?? "backend-test-result.json";
|
|
3261
|
-
return {
|
|
3262
|
-
id,
|
|
3263
|
-
depends_on: options.dependsOn ?? [fromNodeId],
|
|
3264
|
-
role: "verifier",
|
|
3265
|
-
executor: "shell",
|
|
3266
|
-
complexity: "LOW",
|
|
3267
|
-
writePolicy: "read-only",
|
|
3268
|
-
allowedPaths: commonReadOnlyPaths(sources),
|
|
3269
|
-
forbiddenPaths: commonForbiddenPaths(sources),
|
|
3270
|
-
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.",
|
|
3271
|
-
subtask_prompt: "Materialize Backend Test Result v1 from JUnit + pytestExitCode under the current DAG run (fail-closed on missing/corrupt report).",
|
|
3272
|
-
shell: {
|
|
3273
|
-
commands: [],
|
|
3274
|
-
jsonArtifactGate: {
|
|
3275
|
-
fromNodeId,
|
|
3276
|
-
schemaId: "backend-test-result-v1",
|
|
3277
|
-
artifactName,
|
|
3278
|
-
outputDir: "contracts",
|
|
3279
|
-
...(options.junitRelativePath
|
|
3280
|
-
? { junitRelativePath: options.junitRelativePath }
|
|
3281
|
-
: {}),
|
|
3282
|
-
},
|
|
3283
|
-
cwd: ".",
|
|
3284
|
-
timeoutMs: 60000,
|
|
3285
|
-
},
|
|
3286
|
-
};
|
|
3287
|
-
}
|
|
3288
3090
|
function buildClassifyBackendTestResultNode(sources) {
|
|
3289
3091
|
return {
|
|
3290
3092
|
id: "classify-backend-test-result-pi",
|
|
3291
|
-
depends_on: ["parse-backend-
|
|
3093
|
+
depends_on: ["execute-and-parse-backend-pytest-shell"],
|
|
3292
3094
|
role: "reviewer",
|
|
3293
3095
|
executor: "pi",
|
|
3294
3096
|
complexity: "MED",
|
|
@@ -3313,155 +3115,6 @@ function buildClassifyBackendTestResultNode(sources) {
|
|
|
3313
3115
|
].join("\n\n"),
|
|
3314
3116
|
};
|
|
3315
3117
|
}
|
|
3316
|
-
function buildBackendTestClassificationGateNode(sources) {
|
|
3317
|
-
return {
|
|
3318
|
-
id: "backend-test-classification-shell",
|
|
3319
|
-
depends_on: ["classify-backend-test-result-pi"],
|
|
3320
|
-
role: "verifier",
|
|
3321
|
-
executor: "shell",
|
|
3322
|
-
complexity: "LOW",
|
|
3323
|
-
writePolicy: "read-only",
|
|
3324
|
-
allowedPaths: commonReadOnlyPaths(sources),
|
|
3325
|
-
forbiddenPaths: commonForbiddenPaths(sources),
|
|
3326
|
-
outputContract: "Validated run-owned Backend Test Classification v1 at contracts/backend-test-classification.json.",
|
|
3327
|
-
subtask_prompt: "Validate and materialize the read-only backend-test classification for deterministic repair routing.",
|
|
3328
|
-
shell: {
|
|
3329
|
-
commands: [],
|
|
3330
|
-
jsonArtifactGate: {
|
|
3331
|
-
fromNodeId: "classify-backend-test-result-pi",
|
|
3332
|
-
schemaId: "backend-test-classification-v1",
|
|
3333
|
-
artifactName: "backend-test-classification.json",
|
|
3334
|
-
outputDir: "contracts",
|
|
3335
|
-
},
|
|
3336
|
-
cwd: ".",
|
|
3337
|
-
timeoutMs: 60000,
|
|
3338
|
-
},
|
|
3339
|
-
};
|
|
3340
|
-
}
|
|
3341
|
-
function buildBackendTestRepairEligibilityNode(sources) {
|
|
3342
|
-
const command = buildBackendTestRepairEligibilityShellSnippet();
|
|
3343
|
-
return {
|
|
3344
|
-
id: "backend-test-repair-eligibility-shell",
|
|
3345
|
-
depends_on: ["backend-test-classification-shell"],
|
|
3346
|
-
role: "verifier",
|
|
3347
|
-
executor: "shell",
|
|
3348
|
-
complexity: "LOW",
|
|
3349
|
-
writePolicy: "read-only",
|
|
3350
|
-
allowedPaths: commonReadOnlyPaths(sources),
|
|
3351
|
-
forbiddenPaths: commonForbiddenPaths(sources),
|
|
3352
|
-
outputContract: "Pure JSON {schemaVersion,eligible,reason,revisionAttempt,category,confidence}; eligible only for completed TestBug assertion failures with error=0.",
|
|
3353
|
-
subtask_prompt: "Deterministically decide whether this run may use its single testcase-only repair attempt.",
|
|
3354
|
-
shell: { commands: [command], cwd: ".", timeoutMs: 60000 },
|
|
3355
|
-
};
|
|
3356
|
-
}
|
|
3357
|
-
function buildBackendTestRepairConditionNode(sources) {
|
|
3358
|
-
return {
|
|
3359
|
-
id: "backend-test-repair-condition",
|
|
3360
|
-
depends_on: ["backend-test-repair-eligibility-shell"],
|
|
3361
|
-
role: "verifier",
|
|
3362
|
-
executor: "static",
|
|
3363
|
-
complexity: "LOW",
|
|
3364
|
-
writePolicy: "none",
|
|
3365
|
-
allowedPaths: [],
|
|
3366
|
-
forbiddenPaths: commonForbiddenPaths(sources),
|
|
3367
|
-
outputContract: "Select repair or skip repair exactly once; ineligible runs select the already-finished eligibility evidence so the effective-result shell remains outside the exclusive branch targets.",
|
|
3368
|
-
subtask_prompt: "Route eligible TestBug to one repair attempt; otherwise select the already-finished eligibility evidence and skip repair.",
|
|
3369
|
-
static: { resultMarkdown: "Backend-test repair branch selector." },
|
|
3370
|
-
dynamicCondition: {
|
|
3371
|
-
workflowNodeId: "backend-test-repair-condition",
|
|
3372
|
-
cases: [
|
|
3373
|
-
{
|
|
3374
|
-
when: "$.nodes['backend-test-repair-eligibility-shell'].json.eligible == true",
|
|
3375
|
-
then: "repair-backend-pytest-pi",
|
|
3376
|
-
},
|
|
3377
|
-
],
|
|
3378
|
-
default: "backend-test-repair-eligibility-shell",
|
|
3379
|
-
},
|
|
3380
|
-
};
|
|
3381
|
-
}
|
|
3382
|
-
function buildRepairBackendPytestNode(sources) {
|
|
3383
|
-
const writeSet = [
|
|
3384
|
-
"testcase/**/test_*.py",
|
|
3385
|
-
"testcase/**/helpers/**",
|
|
3386
|
-
"testcase/**/factories/**",
|
|
3387
|
-
];
|
|
3388
|
-
return {
|
|
3389
|
-
id: "repair-backend-pytest-pi",
|
|
3390
|
-
depends_on: ["backend-test-repair-condition"],
|
|
3391
|
-
role: "implementer",
|
|
3392
|
-
executor: "pi",
|
|
3393
|
-
toolProfile: "write",
|
|
3394
|
-
complexity: "HIGH",
|
|
3395
|
-
writePolicy: "exclusive",
|
|
3396
|
-
writeSet,
|
|
3397
|
-
allowedPaths: writeSet,
|
|
3398
|
-
forbiddenPaths: [
|
|
3399
|
-
...commonForbiddenPaths(sources),
|
|
3400
|
-
"apps/**",
|
|
3401
|
-
"src/**",
|
|
3402
|
-
".env*",
|
|
3403
|
-
"**/migrations/**",
|
|
3404
|
-
],
|
|
3405
|
-
outputContract: "Repair only existing generated pytest tests/helpers/factories for classified TestBug findings. No production/config/runtime evidence writes.",
|
|
3406
|
-
subtask_prompt: [
|
|
3407
|
-
"This is the only automatic TestBug repair attempt (1/1).",
|
|
3408
|
-
"Read contracts/backend-test-result-initial.json, contracts/backend-test-classification.json, contracts/backend-test-analysis.json, contracts/backend-test-case-manifest.json, and only the generated pytest files named in failures[].name or manifest mappings.",
|
|
3409
|
-
"Do not re-read the full task source tree: the run-owned analysis/manifest are the compact authoritative context.",
|
|
3410
|
-
"Repair only test implementation defects directly supported by reviewed Expected Results or formal API contract evidence.",
|
|
3411
|
-
"Never modify product code, migrations, service configuration, conftest.py, pytest.ini, pyproject.toml, setup.cfg, source requirements, testcase/md/**, or .harness/**.",
|
|
3412
|
-
"Never delete a test, remove a case mapping, add skip/skipif/xfail, swallow AssertionError/network exceptions, switch to a mock server, or weaken documented status/value assertions.",
|
|
3413
|
-
"Keep the full suite runnable and preserve each BE-* case ID and pytest symbol mapping.",
|
|
3414
|
-
"Stay within writeSet: testcase/**/test_*.py, helpers/**, factories/**.",
|
|
3415
|
-
].join("\n\n"),
|
|
3416
|
-
};
|
|
3417
|
-
}
|
|
3418
|
-
function buildBackendTestRepairSafetyGateNode(sources) {
|
|
3419
|
-
const command = buildBackendTestRepairSafetyShellSnippet();
|
|
3420
|
-
return {
|
|
3421
|
-
id: "backend-test-repair-safety-gate-shell",
|
|
3422
|
-
depends_on: ["repair-backend-pytest-pi"],
|
|
3423
|
-
role: "verifier",
|
|
3424
|
-
executor: "shell",
|
|
3425
|
-
complexity: "LOW",
|
|
3426
|
-
writePolicy: "read-only",
|
|
3427
|
-
allowedPaths: ["testcase/**"],
|
|
3428
|
-
forbiddenPaths: commonForbiddenPaths(sources),
|
|
3429
|
-
outputContract: "Fail-closed repair safety gate rejecting skip/xfail and broad failure swallowing before final traceability and pytest.",
|
|
3430
|
-
subtask_prompt: "Check that automatic TestBug repair did not manufacture success.",
|
|
3431
|
-
shell: { commands: [command], cwd: ".", timeoutMs: 60000 },
|
|
3432
|
-
};
|
|
3433
|
-
}
|
|
3434
|
-
function buildBackendTestEffectiveResultNode(sources) {
|
|
3435
|
-
const command = buildBackendTestEffectiveResultSelectorShellSnippet();
|
|
3436
|
-
// Inline retrospective context: relocated verbatim from the former
|
|
3437
|
-
// buildBackendTestRetrospectiveContextNode so test-retrospect-pi keeps a
|
|
3438
|
-
// single upstream. Runs after `command` which materializes
|
|
3439
|
-
// contracts/backend-test-result.json from the effective (final|initial) result.
|
|
3440
|
-
const retroCommand = [
|
|
3441
|
-
'test -n "${HARNESS_DAG_RUN_DIR:-}" || { echo "missing HARNESS_DAG_RUN_DIR for backend-test retrospective context" >&2; exit 2; }',
|
|
3442
|
-
'node -e \'const fs=require("fs"),path=require("path"); const root=process.env.HARNESS_DAG_RUN_DIR; const read=(name)=>JSON.parse(fs.readFileSync(path.join(root,"contracts",name),"utf8")); process.stdout.write(JSON.stringify({schemaVersion:1,result:read("backend-test-result.json"),initialResult:read("backend-test-result-initial.json"),finalResult:fs.existsSync(path.join(root,"contracts","backend-test-result-final.json"))?read("backend-test-result-final.json"):null,manifest:read("backend-test-case-manifest.json"),classification:read("backend-test-classification.json")},null,2));\'',
|
|
3443
|
-
].join("; ");
|
|
3444
|
-
return {
|
|
3445
|
-
id: "select-effective-backend-test-result-shell",
|
|
3446
|
-
depends_on: [
|
|
3447
|
-
"backend-test-repair-condition",
|
|
3448
|
-
"parse-backend-test-result-final-shell",
|
|
3449
|
-
"backend-test-case-manifest-shell",
|
|
3450
|
-
"backend-test-case-manifest-final-shell",
|
|
3451
|
-
"backend-test-classification-shell",
|
|
3452
|
-
],
|
|
3453
|
-
dependsPolicy: "all-or-condition-skip",
|
|
3454
|
-
role: "verifier",
|
|
3455
|
-
executor: "shell",
|
|
3456
|
-
complexity: "LOW",
|
|
3457
|
-
writePolicy: "read-only",
|
|
3458
|
-
allowedPaths: commonReadOnlyPaths(sources),
|
|
3459
|
-
forbiddenPaths: commonForbiddenPaths(sources),
|
|
3460
|
-
outputContract: "Materialize contracts/backend-test-result.json from final result when present, otherwise initial result; emit effective result metadata, and emit the complete retrospective context (Result v1 + Case Manifest v1 + classifier output) for downstream retrospective consumption.",
|
|
3461
|
-
subtask_prompt: "Select the effective backend-test result deterministically without changing initial/final evidence, then emit the complete retrospective context.",
|
|
3462
|
-
shell: { commands: [command, retroCommand], cwd: ".", timeoutMs: 60000 },
|
|
3463
|
-
};
|
|
3464
|
-
}
|
|
3465
3118
|
function buildTestRetrospectNode(sources) {
|
|
3466
3119
|
const canWriteReport = taskAllowsBackendTestReportWrite(sources);
|
|
3467
3120
|
return {
|
|
@@ -3567,10 +3220,11 @@ function buildBackendTestHybridDag(sources) {
|
|
|
3567
3220
|
const globalConstraints = [
|
|
3568
3221
|
...taskConfig.hardConstraints,
|
|
3569
3222
|
...STANDARD_GLOBAL_CONSTRAINTS,
|
|
3570
|
-
"backend-test-dag uses exactly
|
|
3571
|
-
"
|
|
3223
|
+
"backend-test-dag uses exactly 15 real top-level tasks and executes pytest exactly once.",
|
|
3224
|
+
"Case and semantic request-revision verdicts fail at deterministic gates; no in-run revision or repair writer is authorized.",
|
|
3225
|
+
"Analysis, execution, manifest, semantic review, single-run result, classification, canonical result, retrospective and outcome evidence remain run-owned and fail-closed.",
|
|
3572
3226
|
"Functional test case IDs must use BE-<MODULE>-<NNN> format.",
|
|
3573
|
-
"pytest writers may only
|
|
3227
|
+
"pytest writers may only create the initially declared testcase assets; production code, config, skip/xfail, swallowed failures and mock substitution are forbidden.",
|
|
3574
3228
|
];
|
|
3575
3229
|
const analyze = buildAnalyzeInputsNode(sources);
|
|
3576
3230
|
analyze.id = "analyze-and-discover-backend-test-pi";
|
|
@@ -3589,64 +3243,49 @@ function buildBackendTestHybridDag(sources) {
|
|
|
3589
3243
|
generateCases.subtask_prompt += `\n\nAfter writing Markdown, end assistant output with exactly one fenced json block containing Backend Test Case Manifest v1 derived from the written cases.\n\n${BACKEND_TEST_CASE_MANIFEST_OUTPUT_INSTRUCTIONS}`;
|
|
3590
3244
|
const manifest = buildBackendTestCaseManifestGateNode(sources, { dependsOn: [generateCases.id], fromNodeId: generateCases.id });
|
|
3591
3245
|
const reviewCases = buildReviewBackendCasesNode(sources, { dependsOn: [manifest.id, contracts.id] });
|
|
3592
|
-
const reviseCases = buildReviseBackendCasesNode(sources);
|
|
3593
|
-
reviseCases.depends_on = [reviewCases.id];
|
|
3594
|
-
reviseCases.runIf = "$.nodes['review-backend-cases-pi'].firstVerdictLine == 'VERDICT: request-revision'";
|
|
3595
|
-
reviseCases.outputContract = "Apply one case revision and end with one fenced json final Case Manifest v1 block matching the strict field contract.";
|
|
3596
|
-
reviseCases.subtask_prompt += `\n\nAfter edits, end assistant output with exactly one fenced json block containing the complete final Backend Test Case Manifest v1.\n\n${BACKEND_TEST_CASE_MANIFEST_OUTPUT_INSTRUCTIONS}`;
|
|
3597
|
-
const finalManifest = buildBackendTestCaseManifestGateNode(sources, { id: "backend-test-case-manifest-final-shell", dependsOn: [reviseCases.id], fromNodeId: reviseCases.id });
|
|
3598
|
-
finalManifest.runIf = reviseCases.runIf;
|
|
3599
|
-
const finalCaseReview = buildReviewBackendCasesNode(sources, { id: "review-backend-cases-final-pi", phase: "final", dependsOn: [finalManifest.id, contracts.id] });
|
|
3600
|
-
finalCaseReview.runIf = reviseCases.runIf;
|
|
3601
3246
|
const caseGate = buildReviewBackendCasesGateNode(sources);
|
|
3602
|
-
caseGate.depends_on = [reviewCases.id
|
|
3603
|
-
caseGate.
|
|
3247
|
+
caseGate.depends_on = [reviewCases.id];
|
|
3248
|
+
caseGate.dependsPolicy = undefined;
|
|
3249
|
+
caseGate.outputContract = "Deterministic backend case review gate: exit 0 only when the first and only review emits VERDICT: pass.";
|
|
3250
|
+
caseGate.subtask_prompt = "Block pytest generation when backend case review requests revision; do not authorize an in-run writer.";
|
|
3251
|
+
caseGate.shell.verdictGate = { fromNodeId: reviewCases.id, accept: ["VERDICT: pass"], label: "backend case review", lineMode: "first-verdict-line" };
|
|
3604
3252
|
const generatePytest = buildGenerateBackendPytestNode(sources);
|
|
3605
3253
|
generatePytest.depends_on = [caseGate.id, contracts.id];
|
|
3606
|
-
const semanticReview = buildBackendTestSemanticReviewNode(sources, { dependsOn: [generatePytest.id, contracts.id, manifest.id
|
|
3607
|
-
const
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
const
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
role: "verifier",
|
|
3619
|
-
executor: "shell",
|
|
3620
|
-
complexity: "LOW",
|
|
3621
|
-
writePolicy: "read-only",
|
|
3622
|
-
allowedPaths: commonReadOnlyPaths(sources),
|
|
3623
|
-
forbiddenPaths: commonForbiddenPaths(sources),
|
|
3624
|
-
outputContract: "Pass-only effective semantic review gate with final traceability after revision.",
|
|
3625
|
-
subtask_prompt: "Accept initial semantic pass or validate the single final review and traceability.",
|
|
3254
|
+
const semanticReview = buildBackendTestSemanticReviewNode(sources, { dependsOn: [generatePytest.id, contracts.id, manifest.id] });
|
|
3255
|
+
const semanticMaterialize = {
|
|
3256
|
+
id: "validate-semantic-review-and-traceability-shell", depends_on: [semanticReview.id, manifest.id], role: "verifier", executor: "shell", complexity: "LOW",
|
|
3257
|
+
writePolicy: "read-only", allowedPaths: commonReadOnlyPaths(sources), forbiddenPaths: commonForbiddenPaths(sources),
|
|
3258
|
+
outputContract: "Materialize the only semantic review and validate pytest traceability.", subtask_prompt: "Materialize semantic facts and traceability; verdict authorization is handled by the next deterministic gate.",
|
|
3259
|
+
shell: { commands: [], backendTestPipeline: "semantic-initial", cwd: ".", timeoutMs: 60000 },
|
|
3260
|
+
};
|
|
3261
|
+
const semanticGate = {
|
|
3262
|
+
id: "backend-test-semantic-gate-shell", depends_on: [semanticMaterialize.id, semanticReview.id], role: "verifier", executor: "shell", complexity: "LOW",
|
|
3263
|
+
writePolicy: "read-only", allowedPaths: commonReadOnlyPaths(sources), forbiddenPaths: commonForbiddenPaths(sources),
|
|
3264
|
+
outputContract: "Pass-only authorization by reading contracts/backend-test-semantic-review.json; only verdict=pass proceeds to the single pytest execution.",
|
|
3265
|
+
subtask_prompt: "Read the canonical semantic review artifact written by validate-semantic-review-and-traceability-shell. Authorize only when verdict is pass. Do not materialize, do not parse raw Pi Markdown or VERDICT lines, and do not authorize an in-run pytest writer.",
|
|
3626
3266
|
shell: {
|
|
3627
3267
|
commands: [],
|
|
3628
|
-
backendTestPipeline: "semantic-
|
|
3268
|
+
backendTestPipeline: "semantic-initial",
|
|
3629
3269
|
cwd: ".",
|
|
3630
3270
|
timeoutMs: 60000,
|
|
3631
3271
|
},
|
|
3632
3272
|
};
|
|
3633
|
-
const
|
|
3634
|
-
|
|
3273
|
+
const execute = buildExecuteBackendPytestNode(sources, { id: "execute-and-parse-backend-pytest-shell", dependsOn: [semanticGate.id, contracts.id], reportStem: "backend-test-initial" });
|
|
3274
|
+
execute.shell.backendTestPipeline = "execute-parse-initial";
|
|
3635
3275
|
const classify = buildClassifyBackendTestResultNode(sources);
|
|
3636
|
-
classify.depends_on = [
|
|
3637
|
-
const
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
finalize.shell.backendTestPipeline = "finalize-effective-result";
|
|
3645
|
-
finalize.outputContract = "If repaired, execute/parse final pytest; always materialize contracts/backend-test-result.json from final or initial result.";
|
|
3276
|
+
classify.depends_on = [execute.id];
|
|
3277
|
+
const context = {
|
|
3278
|
+
id: "materialize-classification-and-result-context-shell", depends_on: [classify.id, manifest.id], role: "verifier", executor: "shell", complexity: "LOW",
|
|
3279
|
+
writePolicy: "read-only", allowedPaths: commonReadOnlyPaths(sources), forbiddenPaths: commonForbiddenPaths(sources),
|
|
3280
|
+
outputContract: "Materialize Classification v1, copy the unique initial Result to canonical contracts/backend-test-result.json, and emit Result + Manifest + Classification context.",
|
|
3281
|
+
subtask_prompt: "Validate classification and materialize canonical single-run result context without repair eligibility or rerun.",
|
|
3282
|
+
shell: { commands: [], backendTestPipeline: "classification-result-context", cwd: ".", timeoutMs: 60000 },
|
|
3283
|
+
};
|
|
3646
3284
|
const retrospect = buildTestRetrospectNode(sources);
|
|
3647
|
-
retrospect.depends_on = [
|
|
3285
|
+
retrospect.depends_on = [context.id];
|
|
3286
|
+
retrospect.subtask_prompt = retrospect.subtask_prompt.replaceAll("select-effective-backend-test-result-shell", context.id);
|
|
3648
3287
|
const outcome = buildBackendTestOutcomeGateNode(sources);
|
|
3649
|
-
const tasks = [analyze, contracts, generateCases, manifest, reviewCases,
|
|
3288
|
+
const tasks = [analyze, contracts, generateCases, manifest, reviewCases, caseGate, generatePytest, semanticReview, semanticMaterialize, semanticGate, execute, classify, context, retrospect, outcome];
|
|
3650
3289
|
const spec = { version: 3, title: `Backend test DAG: ${taskConfig.title}`, runtimeContract: GENERATED_DAG_RUNTIME_CONTRACT, outputLanguage: sources.outputLanguage ?? DEFAULT_DAG_OUTPUT_LANGUAGE, objective: extractObjective(sources.requirementMarkdown, taskConfig.title), successCriteria: extractSuccessCriteria(sources.requirementMarkdown, sources.taskId), globalConstraints, defaults: { ...BACKEND_TEST_DEFAULTS, contextProfile: taskConfig.contextProfile }, skillsByRole: BACKEND_TEST_SKILLS_BY_ROLE, executorModels: sources.executorModelMatrix ?? DEFAULT_DAG_EXECUTOR_MODELS, tasks };
|
|
3651
3290
|
applyDefaultReadOnlyRetryPolicy(spec);
|
|
3652
3291
|
parseDagSpec(spec);
|
|
@@ -422,9 +422,10 @@ export async function executeDagNode(input) {
|
|
|
422
422
|
node.structuredArtifactSha256 = createHash("sha256").update(bytes).digest("hex");
|
|
423
423
|
node.structuredArtifactSchemaId = task.shell.jsonArtifactGate.schemaId;
|
|
424
424
|
}
|
|
425
|
-
else if (task.shell?.backendTestPipeline === "
|
|
426
|
-
//
|
|
427
|
-
//
|
|
425
|
+
else if (task.shell?.backendTestPipeline === "classification-result-context") {
|
|
426
|
+
// The current 15-node single-run pipeline materializes the canonical
|
|
427
|
+
// contracts/backend-test-result.json without jsonArtifactGate. Bind it
|
|
428
|
+
// so Outcome adapters project kind=backend-test-result for Ready Planner.
|
|
428
429
|
const artifactPath = path.join(runDir, "contracts", "backend-test-result.json");
|
|
429
430
|
if (existsSync(artifactPath)) {
|
|
430
431
|
const bytes = await readFile(artifactPath);
|
|
@@ -145,12 +145,8 @@ export const dagNodeStatusSchema = z.enum([
|
|
|
145
145
|
export const dagBackendTestPipelineSchema = z.enum([
|
|
146
146
|
"contracts",
|
|
147
147
|
"semantic-initial",
|
|
148
|
-
"semantic-final",
|
|
149
|
-
"semantic-effective",
|
|
150
148
|
"execute-parse-initial",
|
|
151
|
-
"classification-
|
|
152
|
-
"repair-safety-traceability",
|
|
153
|
-
"finalize-effective-result",
|
|
149
|
+
"classification-result-context",
|
|
154
150
|
]);
|
|
155
151
|
export const dagShellConfigSchema = z.object({
|
|
156
152
|
commands: z.array(z.string()).default([]),
|