@tea-agent/loop-agent 0.39.0-beta.11 → 0.39.0-beta.13
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 +26 -0
- package/dist/application/dag/generate-task-dag.js +6 -2
- package/dist/build-stamp.json +3 -3
- package/dist/commands/client-recovery.js +8 -36
- package/dist/executors/dag-pi-executor.js +343 -40
- package/dist/executors/pi-executor.js +8 -4
- package/dist/executors/pi-sdk-executor.js +33 -5
- package/dist/executors/shell-executor.js +102 -32
- package/dist/governance/checks.js +1 -0
- package/dist/shared/pi-context-pressure/checkpoint.js +116 -0
- package/dist/shared/pi-context-pressure/compaction-policy.js +151 -0
- package/dist/shared/pi-context-pressure/env.js +58 -0
- package/dist/shared/pi-context-pressure/extension.js +100 -0
- package/dist/shared/pi-context-pressure/index.js +7 -0
- package/dist/shared/pi-context-pressure/overflow.js +252 -0
- package/dist/shared/pi-context-pressure/sift-bridge.js +386 -0
- package/dist/shared/pi-context-pressure/telemetry.js +51 -0
- package/dist/task/frontend-project-capability.js +3 -1
- package/dist/task/source-prepare/fragment-inventory.js +4 -1
- package/dist/worker/console/chat/pi-runtime.js +146 -5
- package/dist/worker/console/chat/provider-error.js +2 -1
- package/dist/worker/console/chat/routes.js +3 -0
- package/dist/worker/console/chat/sift-bridge.js +1 -0
- package/dist/worker/console/dag-execution-receipt.js +20 -2
- package/dist/worker/console/operator-actions.js +4 -3
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-CXj_GnSb.js → abnfDiagram-N423BO3Z-DC863mud.js} +1 -1
- package/dist/worker/console/static/assets/{arc-BZp6JAp7.js → arc-CftC38G9.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DfpcEuYU.js → architectureDiagram-T3A2C74G-B3PAPQCw.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-_Gf0xadb.js → blockDiagram-VBNYF7ZC-C9UDJNRv.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-Ct2QPCmv.js → c4Diagram-5PPSVZJV--BJ76fv_.js} +1 -1
- package/dist/worker/console/static/assets/channel-CUz-Bg86.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-BfklkzKl.js → chunk-2GRJ4B5K--hiIqoGp.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-Dy25vZJV.js → chunk-2Q5K7J3B-DgTzpIa3.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BFlCRZep.js → chunk-5RXB4S5H-DIwpJziP.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-op3oVxIE.js → chunk-5VM5RSS4-BJzbdUi0.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-C0R7rzF2.js → chunk-6Q2QTUOP-BbGouI1Z.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-Bt44TCGy.js → chunk-GF5L2VYU-B9247w69.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-_uEE_XFx.js → chunk-JWPE2WC7-CXob_wYy.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-C3TOYGZ9.js → chunk-KBJHAD2P-C6FUel1g.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-Cz60oBPV.js → chunk-RYQCIY6F-DGKRYKHu.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-8Bik0qis.js → chunk-XXDRQBXY-kNBqNPfx.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BhlUacmD.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BhlUacmD.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-C2QIOA4a.js → cose-bilkent-JH36ORCC--xmkDwfD.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-CSH_yUUd.js → cynefin-VYW2F7L2-Cw5FMMuG.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-BDLw2XFM.js → cynefinDiagram-MW4NZA55-CuLslt2t.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-CcD9ZtF4.js → dagre-VZM6K2ZE-D9ngqrs2.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-DqwtkBsI.js → diagram-7IWD3JNH-RDRSbHQp.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-BWVEcqsC.js → diagram-B4RE2ZJO-BgQ1P9aV.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-M-WAbtQi.js → diagram-LBJQPF4R-D3WWyPtn.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DKW7SixP.js → diagram-Q27KOJAE-L2k28wTR.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-PHHPPtrj.js → diagram-UB23O5K3-DDNrA5Qw.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BQD-B4RX.js → ebnfDiagram-BXEA7PRR-BaRFCOdM.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-BFvULb51.js → erDiagram-JOGREHBK-CM33DVVM.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-Bw-aXTCb.js → flowDiagram-UKHOOZJN-CVBSjOxe.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-BGKw04Qx.js → ganttDiagram-PKOTCBZU-BdscewE_.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-RqKaPR-I.js → gitGraphDiagram-DS77QQ5N-HRuSZb7g.js} +1 -1
- package/dist/worker/console/static/assets/{index-B_D8rbWc.js → index-B9JJQsVK.js} +102 -72
- package/dist/worker/console/static/assets/index-rWaGv4jz.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-YO5dnzrJ.js → infoDiagram-6WML65LV-cYfHvfAR.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-Bi1VZHMq.js → ishikawaDiagram-WSZJBQD7-CZmaoOuy.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-Bszls1DD.js → journeyDiagram-NVQOT4AX-ntYijh7g.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-PhzeaZ09.js → kanban-definition-27J2QSJJ-Cz3b0DeD.js} +1 -1
- package/dist/worker/console/static/assets/{linear-BsjbDoXi.js → linear-DvGonpsP.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-0B7NnWKk.js → mermaid.core-B-3vjfyW.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-BJr4Fj-q.js → mindmap-definition-FAOFIHXS-5iKxlsX3.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-moC4fpGB.js → pegDiagram-VL7TDLO6-C8BUwapW.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-BEw37-2c.js → pieDiagram-7S7Q4E2Y-B2rPoQQG.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-Cq6LyasU.js → quadrantDiagram-CIZ2JOQS-jDeVUAy4.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-DUCMcK0D.js → railroadDiagram-AXF67PYL-DV140Dwm.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-C3upTZm7.js → requirementDiagram-LRYGKXZP-CnYgHtYC.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-BI_gMsCW.js → sankeyDiagram-W5VNT64P-SIK3CdWw.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-YFOIRzfN.js → sequenceDiagram-SI44F4Z6-DW_vZix7.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-dOnB7UDD.js → sizeCapture-X5ZJPWSS-NBAtkg0C.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-BXUniaIh.js → stateDiagram-OKZ733FA-Bi1bQxpi.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DjWKYjQ1.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-2tA4wTNu.js → swimlanes-SLNWSIFB-8PT_uP_i.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-B4cdm7bH.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-DO0HkJXC.js → timeline-definition-Z64GVDOM-CD0ZZPk0.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DvIOzixv.js → vennDiagram-T6HMQDX7-BbEgWdhK.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DXDZ0cTj.js → wardleyDiagram-T6FBY63Y-DCwPKdLl.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-B32Ark0D.js → xychartDiagram-ELKLHX3M-sfC5PcNg.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/observe/static/styles.css +9 -0
- package/dist/worker/observe/static/views/dag-inspector.js +40 -0
- package/dist/worker/observe/static/views/session-timeline.js +135 -0
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +157 -7
- package/dist/workflows/dag/backend-test-pytest-collection.js +70 -2
- package/dist/workflows/dag/backend-test-result-contract.js +4 -0
- package/dist/workflows/dag/backend-test-scenario-param.js +339 -53
- package/dist/workflows/dag/backend-test-writer-completeness.js +11 -0
- package/dist/workflows/dag/dag-retry-schema.js +138 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +118 -22
- package/dist/workflows/dag/frontend-shadow-dual-write.js +1 -1
- package/dist/workflows/dag/frontend-writer-admission.js +13 -0
- package/dist/workflows/dag/init-hybrid.js +67 -3
- package/dist/workflows/dag/node-execution.js +384 -26
- package/dist/workflows/dag/rerun-feedback.js +135 -3
- package/dist/workflows/dag/retry-policy.js +13 -122
- package/dist/workflows/dag/types.js +6 -1
- package/docs/architecture/runtime-boundaries.md +2 -1
- package/docs/templates/README.md +1 -0
- package/docs/templates/backend-test-dag.json +4 -3
- package/docs/templates/frontend-implementation-dag.json +89 -0
- package/package.json +4 -3
- package/skills/loop-agent/references/hybrid-dag.md +1 -1
- package/dist/worker/console/static/assets/channel-3TxJgYaH.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BHIkXpp3.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BHIkXpp3.js +0 -1
- package/dist/worker/console/static/assets/index-BdNx6fj0.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Cdi6UhLa.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-D-RJBbb0.js +0 -8
|
@@ -1124,11 +1124,40 @@ function deriveFrontendVerificationCoverage(value, canonicalBinding) {
|
|
|
1124
1124
|
])];
|
|
1125
1125
|
const evidenceGap = asRecord(requirement.evidenceGap);
|
|
1126
1126
|
const hasProof = provenRequirementIds.has(requirementId);
|
|
1127
|
+
// A committed evidenceGap with a blank description means "no gap":
|
|
1128
|
+
// small-output models emit the slot defensively with description
|
|
1129
|
+
// "" and the strict gap schema (description min 1 char) would
|
|
1130
|
+
// fail the whole compile. Strip the incoming slot first and only
|
|
1131
|
+
// re-add it when usable — the requirement either has proof (no
|
|
1132
|
+
// gap needed) or receives the derived blocking gap below.
|
|
1133
|
+
const evidenceGapDescription = asString(evidenceGap?.description).trim();
|
|
1134
|
+
const hasUsableEvidenceGap = evidenceGap !== undefined && evidenceGapDescription !== "";
|
|
1135
|
+
// Source fidelity bindings are deterministic ledger data, not
|
|
1136
|
+
// model-authored content: when the plan requirement carries no
|
|
1137
|
+
// binding (r6 — the contract input block degraded, the model
|
|
1138
|
+
// correctly refused to invent ids), inject the ledger binding
|
|
1139
|
+
// for the requirement id. The strict schema fails a ledger-bound
|
|
1140
|
+
// contract on any requirement without sourceFragmentIds.
|
|
1141
|
+
const declaredSourceFragmentIds = Array.isArray(requirement.sourceFragmentIds)
|
|
1142
|
+
? requirement.sourceFragmentIds
|
|
1143
|
+
: undefined;
|
|
1144
|
+
const ledgerBoundFragmentIds = Array.isArray(canonicalBinding.requirementToFragments?.[requirementId])
|
|
1145
|
+
? canonicalBinding.requirementToFragments[requirementId]
|
|
1146
|
+
: undefined;
|
|
1147
|
+
const sourceFragmentIds = declaredSourceFragmentIds && declaredSourceFragmentIds.length > 0
|
|
1148
|
+
? declaredSourceFragmentIds
|
|
1149
|
+
: ledgerBoundFragmentIds;
|
|
1150
|
+
const { evidenceGap: _incomingGap, sourceFragmentIds: _incomingFragmentIds, ...requirementWithoutGap } = requirement;
|
|
1151
|
+
void _incomingGap;
|
|
1152
|
+
void _incomingFragmentIds;
|
|
1127
1153
|
return {
|
|
1128
|
-
...
|
|
1154
|
+
...requirementWithoutGap,
|
|
1155
|
+
...(sourceFragmentIds ? { sourceFragmentIds } : {}),
|
|
1129
1156
|
...(verificationTargetIds.length > 0 ? { verificationTargetIds } : {}),
|
|
1130
1157
|
...(hasProof
|
|
1131
|
-
? (
|
|
1158
|
+
? (hasUsableEvidenceGap
|
|
1159
|
+
? { evidenceGap: { ...evidenceGap, blocking: false } }
|
|
1160
|
+
: {})
|
|
1132
1161
|
: {
|
|
1133
1162
|
evidenceGap: {
|
|
1134
1163
|
requirementId,
|
|
@@ -1140,10 +1169,16 @@ function deriveFrontendVerificationCoverage(value, canonicalBinding) {
|
|
|
1140
1169
|
})
|
|
1141
1170
|
: record.requirements;
|
|
1142
1171
|
const modelEvidenceGaps = Array.isArray(record.evidenceGaps)
|
|
1143
|
-
? record.evidenceGaps
|
|
1172
|
+
? record.evidenceGaps
|
|
1173
|
+
.map((item) => {
|
|
1144
1174
|
const gap = asRecord(item);
|
|
1145
1175
|
if (!gap)
|
|
1146
1176
|
return item;
|
|
1177
|
+
// Blank-description gaps are meaningless statements that would
|
|
1178
|
+
// fail the strict gap schema; drop them like the embedded
|
|
1179
|
+
// per-requirement empty slots.
|
|
1180
|
+
if (asString(gap.description).trim() === "")
|
|
1181
|
+
return undefined;
|
|
1147
1182
|
const requirementId = canonicalizeRequirementId(asString(gap.requirementId));
|
|
1148
1183
|
// Model gaps are advisory. Blocking status is reconstructed below
|
|
1149
1184
|
// from the source binding and executable verification targets.
|
|
@@ -1156,6 +1191,7 @@ function deriveFrontendVerificationCoverage(value, canonicalBinding) {
|
|
|
1156
1191
|
blocking: false,
|
|
1157
1192
|
};
|
|
1158
1193
|
})
|
|
1194
|
+
.filter((item) => item !== undefined)
|
|
1159
1195
|
: [];
|
|
1160
1196
|
const derivedBlockingGaps = canonicalBinding.requirementIds
|
|
1161
1197
|
.filter((requirementId) => !provenRequirementIds.has(requirementId))
|
|
@@ -1820,6 +1856,35 @@ export async function analyzeFrontendImplementationContract(input) {
|
|
|
1820
1856
|
const result = frontendImplementationContractSchema.safeParse(candidate);
|
|
1821
1857
|
if (!result.success)
|
|
1822
1858
|
fail("retryable-invalid", `invalid-output: ${result.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")}`, candidateJsonSha256);
|
|
1859
|
+
// targets.files is runtime-owned (the skeleton derives it from the task
|
|
1860
|
+
// writeSet, a glob the model must not edit). Narrow it AFTER the full
|
|
1861
|
+
// schema validation — never before, so model-authored unsafe/absolute
|
|
1862
|
+
// declarations are still rejected — to every CONCRETE path the validated
|
|
1863
|
+
// contract references (requirement implementationTargets,
|
|
1864
|
+
// verification-target files, Mock endpoint fixture/consumer paths). The
|
|
1865
|
+
// prewrite containment semantics keep covering everything the contract
|
|
1866
|
+
// references because every narrowed entry already matched the declared
|
|
1867
|
+
// patterns during validation. Satisfies frozen requirements like "deliver
|
|
1868
|
+
// exactly these files" without asking the model to edit a protected
|
|
1869
|
+
// field (r10/r11 design-review findings).
|
|
1870
|
+
const referencedConcretePaths = [
|
|
1871
|
+
...new Set([
|
|
1872
|
+
...result.data.requirements.flatMap((requirement) => requirement.implementationTargets),
|
|
1873
|
+
// Mock endpoint paths stay in the set: the zod refine validated
|
|
1874
|
+
// them against the declared targets.files patterns, so the
|
|
1875
|
+
// narrowing must keep covering them. Verification-target files
|
|
1876
|
+
// deliberately do NOT join: they are test artifacts, not
|
|
1877
|
+
// deliverables, and pulling them in would change which gate
|
|
1878
|
+
// fires for an out-of-writeSet VT (AC-3a semantics).
|
|
1879
|
+
...(result.data.mockApi?.endpoints ?? []).flatMap((endpoint) => [endpoint.fixture, endpoint.consumer].filter((value) => typeof value === "string" && value.length > 0)),
|
|
1880
|
+
].filter((value) => value.length > 0)),
|
|
1881
|
+
].sort();
|
|
1882
|
+
if (referencedConcretePaths.length > 0) {
|
|
1883
|
+
result.data.targets = {
|
|
1884
|
+
...result.data.targets,
|
|
1885
|
+
files: referencedConcretePaths,
|
|
1886
|
+
};
|
|
1887
|
+
}
|
|
1823
1888
|
const blockingGaps = [
|
|
1824
1889
|
...(result.data.evidenceGaps ?? []),
|
|
1825
1890
|
...result.data.requirements.flatMap((item) => item.evidenceGap ? [item.evidenceGap] : []),
|
|
@@ -2038,6 +2103,55 @@ function assertVerificationSymbolShapes(contract) {
|
|
|
2038
2103
|
}
|
|
2039
2104
|
}
|
|
2040
2105
|
}
|
|
2106
|
+
/**
|
|
2107
|
+
* Thrown by analyzeFrontendPlanPatchCandidate when the design-policy
|
|
2108
|
+
* pre-checks report findings. Carries the structured findings so callers
|
|
2109
|
+
* (finalize_plan receipt) can synthesize fix suggestions instead of making
|
|
2110
|
+
* the model re-derive them from prose.
|
|
2111
|
+
*/
|
|
2112
|
+
export class PlanPolicyPrecheckFailure extends Error {
|
|
2113
|
+
findings;
|
|
2114
|
+
constructor(message, findings) {
|
|
2115
|
+
super(message);
|
|
2116
|
+
this.name = "PlanPolicyPrecheckFailure";
|
|
2117
|
+
this.findings = findings;
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
/**
|
|
2121
|
+
* Shared tail of the plan patch validation: analyze the merged contract and
|
|
2122
|
+
* front-load the plan-attributable design-policy checks. Throws the same
|
|
2123
|
+
* errors the node self-check throws (FrontendContractFailure for schema
|
|
2124
|
+
* failures, Error for policy findings) so every caller — the node validator
|
|
2125
|
+
* and the finalize_plan receipt — surfaces identical diagnostics.
|
|
2126
|
+
*/
|
|
2127
|
+
export async function analyzeFrontendPlanPatchCandidate(input) {
|
|
2128
|
+
const analysis = await analyzeFrontendImplementationContract(input);
|
|
2129
|
+
// Front-load the verification-symbol shape check so fabricated symbols
|
|
2130
|
+
// are fixed by the plan retry ladder in-node instead of failing the
|
|
2131
|
+
// verify trace gate at the end of the run.
|
|
2132
|
+
assertVerificationSymbolShapes(analysis.canonical);
|
|
2133
|
+
// Front-load the plan-attributable design-policy checks so the §5.1
|
|
2134
|
+
// retry ladder can fix these facts in-node with diagnostics instead of
|
|
2135
|
+
// the run terminating at the policy shell. The policy shell remains the
|
|
2136
|
+
// final authority and re-runs the identical checks on committed facts.
|
|
2137
|
+
const policyPreFindings = [
|
|
2138
|
+
...checkUiDesignCoverage(analysis.canonical),
|
|
2139
|
+
...checkUiStateAttribution(analysis.canonical),
|
|
2140
|
+
...checkDependencies(analysis.canonical, await deriveAllowedDependenciesFromRun(input.runDir)),
|
|
2141
|
+
...checkTargetPaths(analysis.canonical, await deriveWriteSetFromRun(input.runDir)),
|
|
2142
|
+
];
|
|
2143
|
+
if (policyPreFindings.length > 0) {
|
|
2144
|
+
const message = `frontend plan policy pre-check failed (fix these plan facts, then re-commit and finalize): ${policyPreFindings
|
|
2145
|
+
.map((finding) => `${finding.code}: ${finding.message}`)
|
|
2146
|
+
.join("; ")}`;
|
|
2147
|
+
throw new PlanPolicyPrecheckFailure(message, policyPreFindings.map((finding) => ({
|
|
2148
|
+
code: finding.code,
|
|
2149
|
+
message: finding.message,
|
|
2150
|
+
...(finding.path ? { path: finding.path } : {}),
|
|
2151
|
+
})));
|
|
2152
|
+
}
|
|
2153
|
+
return analysis;
|
|
2154
|
+
}
|
|
2041
2155
|
export async function validateFrontendPlanPatchNodeOutput(input) {
|
|
2042
2156
|
const nodeId = input.nodeId ?? "frontend-plan-pi";
|
|
2043
2157
|
const attempt = input.attempt ?? 1;
|
|
@@ -2089,29 +2203,11 @@ export async function validateFrontendPlanPatchNodeOutput(input) {
|
|
|
2089
2203
|
runDir: input.runDir,
|
|
2090
2204
|
contract: merged,
|
|
2091
2205
|
});
|
|
2092
|
-
const analysis = await
|
|
2206
|
+
const analysis = await analyzeFrontendPlanPatchCandidate({
|
|
2093
2207
|
runDir: input.runDir,
|
|
2094
2208
|
rawContractText: serializeDeterministicJson(merged),
|
|
2095
2209
|
sourceBinding: input.sourceBinding,
|
|
2096
2210
|
});
|
|
2097
|
-
// Front-load the verification-symbol shape check so fabricated symbols
|
|
2098
|
-
// are fixed by the plan retry ladder in-node instead of failing the
|
|
2099
|
-
// verify trace gate at the end of the run.
|
|
2100
|
-
assertVerificationSymbolShapes(analysis.canonical);
|
|
2101
|
-
// Front-load the plan-attributable design-policy checks so the §5.1
|
|
2102
|
-
// retry ladder can fix these facts in-node with diagnostics instead of
|
|
2103
|
-
// the run terminating at the policy shell. The policy shell remains the
|
|
2104
|
-
// final authority and re-runs the identical checks on committed facts.
|
|
2105
|
-
const policyPreFindings = [
|
|
2106
|
-
...checkUiDesignCoverage(analysis.canonical),
|
|
2107
|
-
...checkUiStateAttribution(analysis.canonical),
|
|
2108
|
-
...checkDependencies(analysis.canonical, await deriveAllowedDependenciesFromRun(input.runDir)),
|
|
2109
|
-
...checkTargetPaths(analysis.canonical, await deriveWriteSetFromRun(input.runDir)),
|
|
2110
|
-
];
|
|
2111
|
-
if (policyPreFindings.length > 0)
|
|
2112
|
-
throw new Error(`frontend plan policy pre-check failed (fix these plan facts, then re-commit and finalize): ${policyPreFindings
|
|
2113
|
-
.map((finding) => `${finding.code}: ${finding.message}`)
|
|
2114
|
-
.join("; ")}`);
|
|
2115
2211
|
const normalizedArtifact = await writeDeterministicJsonArtifact(input.runDir, path.posix.join(candidateDir, `attempt-${attempt}.normalized.json`), analysis.canonical);
|
|
2116
2212
|
await writeReport({
|
|
2117
2213
|
classification: "accepted-normalized",
|
|
@@ -607,7 +607,7 @@ export function readCompleteScoutTargetSurface(records) {
|
|
|
607
607
|
if (!namedPaths.some((candidate) => freshPaths.has(candidate)))
|
|
608
608
|
return {
|
|
609
609
|
ok: false,
|
|
610
|
-
reason: "frontend scout target surface lacks fresh runtime evidence for a named target path",
|
|
610
|
+
reason: "frontend scout target surface lacks fresh runtime evidence for a named target path; record_target_surface enriches every declared entrypoint/implementation/test path against the real workspace, so at least one named path must exist on disk (an existing directory counts; a file to be created later cannot count)",
|
|
611
611
|
};
|
|
612
612
|
return { ok: true, surface };
|
|
613
613
|
}
|
|
@@ -78,6 +78,19 @@ export const frontendWriterAdmissionResultV1Schema = z
|
|
|
78
78
|
path: z.string().optional(),
|
|
79
79
|
})
|
|
80
80
|
.strict()),
|
|
81
|
+
// r12 policy change: a request_design_changes verdict no longer blocks
|
|
82
|
+
// writer admission. The verdict and its findings ride along as
|
|
83
|
+
// advisory context for the implement node; blocking is owned by the
|
|
84
|
+
// deterministic gates (prewrite, write guard) and the final code
|
|
85
|
+
// review.
|
|
86
|
+
designReviewAdvisory: z
|
|
87
|
+
.object({
|
|
88
|
+
verdict: z.enum(["approve_design", "request_design_changes"]),
|
|
89
|
+
restartPhase: z.string().min(1).optional(),
|
|
90
|
+
findingCount: z.number().int().nonnegative(),
|
|
91
|
+
})
|
|
92
|
+
.strict()
|
|
93
|
+
.optional(),
|
|
81
94
|
failureSource: frontendAdmissionFailureSourceSchema.optional(),
|
|
82
95
|
})
|
|
83
96
|
.strict();
|
|
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { access, readdir, readFile, realpath } from "node:fs/promises";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
5
6
|
import { writeJsonAtomic } from "../../infrastructure/harness/atomic-write.js";
|
|
6
7
|
import { assertValidDagSpec } from "./validate.js";
|
|
7
8
|
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";
|
|
@@ -2563,9 +2564,10 @@ async function resolveFrontendOpenspecGateConfig(sources) {
|
|
|
2563
2564
|
const frontendComponentConformanceInstruction = [
|
|
2564
2565
|
"## Component Selection conformance (uiComponentChoices; hard rule)",
|
|
2565
2566
|
"每个 UI 用途必须在契约的 uiComponentChoices[] 中声明组件选型:{ purpose, component, decision, specReference, rationale }。",
|
|
2567
|
+
"purpose is the stable coverage key:它应精确匹配 interaction.name 或 uiState.name;职责语义由对应 interaction.expectedBehavior / uiState.expectedBehavior 与 rationale 表达。不得仅因 purpose 与 interaction 或 component 标识符相同而判缺陷。",
|
|
2566
2568
|
"- decision=specified:前端规范(候选组件/主题桶 + 任务源显式引用)已定义该用途组件 → 必须使用该组件,并给精确 specReference { path, section, line }(path 必须是 openspec/ai_workspace 受支持规范路径)。",
|
|
2567
2569
|
"- decision=reuse-existing:仅当该组件/惯例**确实已存在于仓库当前代码**(如复用现有 ActiveRunBadge 的 oc- class 惯例)→ specReference 可为 null,rationale 必须指明复用的具体现有组件/文件与依据。",
|
|
2568
|
-
"- decision=new:任务源/PRD 要求**新增**该组件(仓库当前不存在该组件文件)→ decision 必须为 new,不得标 reuse-existing;调用 record_component_choice 时传 sourceRequirementIds(关联的 frozen requirement ID
|
|
2570
|
+
"- decision=new:任务源/PRD 要求**新增**该组件(仓库当前不存在该组件文件)→ decision 必须为 new,不得标 reuse-existing;调用 record_component_choice 时传 sourceRequirementIds(关联的 frozen requirement ID)与 plan checklist 列出的 sourceFragmentId,runtime 校验其隶属关系并物化精确的任务源 PRD { path, section, line }。不要读取 PRD 或手填/猜测 specReference;rationale 说明新增纯展示组件、复用既有 CSS 命名与主题变量约定。",
|
|
2569
2571
|
"不得静默替换规范组件或自创组件而无偏差声明;spec 已定义该用途组件时不得改选其它组件。",
|
|
2570
2572
|
"prewrite gate 确定性交叉校验:仅 decision=specified 的 specReference 必须是候选 OpenSpec 路径、在 typed decision ledger 中声明且有成功 read 事件;decision=new 的 PRD 引用走任务源可追溯性审查,不得按 OpenSpec 候选拒绝。候选桶非空且契约有 UI 可见工作而 uiComponentChoices 缺失/空 → component-choices-missing。",
|
|
2571
2573
|
].join("\n");
|
|
@@ -2764,6 +2766,37 @@ function resolveFrontendShapeCapsuleGenerationInput(sources) {
|
|
|
2764
2766
|
}
|
|
2765
2767
|
return explicit ?? autoloaded;
|
|
2766
2768
|
}
|
|
2769
|
+
/** Standard frontend DAG template (docs/templates/frontend-implementation-dag.json).
|
|
2770
|
+
* It is the topology source of truth: node set, execution order, and depends_on
|
|
2771
|
+
* come from the template; the runtime generator assembles each node's full
|
|
2772
|
+
* configuration (budgets, retry, skeleton, skills, dynamic prompt sections).
|
|
2773
|
+
* Resolution order: repoRoot copy (dev workspace) → bundled package copy. */
|
|
2774
|
+
export async function loadFrontendDagTemplate(repoRoot) {
|
|
2775
|
+
const candidates = [];
|
|
2776
|
+
if (repoRoot) {
|
|
2777
|
+
candidates.push(path.join(repoRoot, "docs", "templates", "frontend-implementation-dag.json"));
|
|
2778
|
+
}
|
|
2779
|
+
candidates.push(fileURLToPath(new URL("../../../docs/templates/frontend-implementation-dag.json", import.meta.url)));
|
|
2780
|
+
for (const candidate of candidates) {
|
|
2781
|
+
try {
|
|
2782
|
+
const parsed = JSON.parse(await readFile(candidate, "utf8"));
|
|
2783
|
+
if (!Array.isArray(parsed.tasks))
|
|
2784
|
+
continue;
|
|
2785
|
+
const tasks = parsed.tasks.filter((item) => typeof item === "object" &&
|
|
2786
|
+
item !== null &&
|
|
2787
|
+
typeof item.id === "string" &&
|
|
2788
|
+
Array.isArray(item.depends_on) &&
|
|
2789
|
+
typeof item.executor === "string");
|
|
2790
|
+
if (tasks.length === 0)
|
|
2791
|
+
continue;
|
|
2792
|
+
return { tasks };
|
|
2793
|
+
}
|
|
2794
|
+
catch {
|
|
2795
|
+
// try next candidate
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
return null;
|
|
2799
|
+
}
|
|
2767
2800
|
async function buildFrontendHybridDagFromTask(sources) {
|
|
2768
2801
|
const { taskConfig } = sources;
|
|
2769
2802
|
const mockCapability = sources.frontendMockCapability ?? {
|
|
@@ -3112,6 +3145,7 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
3112
3145
|
skills: FRONTEND_IMPLEMENTATION_SKILLS,
|
|
3113
3146
|
outputContract: "Typed requirement facts plus a concise Markdown contract. Submit through the incremental typed tools record_requirement / record_constraint / record_evidence_expectation / record_handoff_intent / record_open_question / record_split_proposal / record_openspec_selection, then call finalize_contract exactly once. Requirements use stable REQ/BR/AC identifiers with source spans and a disposition (explicit | repository-resolvable | assumption | blocking); each requirement registers evidence expectations across static/behavior/Mock/real-integration (required | optional | not-applicable), and UI-visible or interactive requirements register a non-blocking frontend-test handoff intent. End finalize_contract with a single contract disposition of ready | ready-with-assumptions | blocked. Cover scope, non-goals, acceptance criteria, UI states, target runtime environment, risks, and verification expectations; do not fix target files, components, or implementation methods as requirements. When OpenSpec candidates exist, classify only the ones you actually use: call record_openspec_selection once per required/relevant path; never enumerate irrelevant candidates (unmentioned defaults to irrelevant) and never emit a fenced selection JSON. No file writes.",
|
|
3114
3147
|
subtask_prompt: [
|
|
3148
|
+
"OUTPUT BUDGET DISCIPLINE (hard requirement, extreme-environment safe): the provider output window is small — NEVER attempt to emit the whole contract in one response; a single large JSON dump will be truncated and rejected. Incremental submission through the typed tools is the ONLY supported output mode. Start submitting with the FIRST tool call: after each read, call record_requirement for the requirements you have already confirmed, one or a few per call. Every tool-call round MUST make progress by submitting at least one record_* fact. Do not re-read the same source file that is already materialized in this session; read each file at most once.",
|
|
3115
3149
|
"Read task source and produce a concise frontend implementation contract as typed requirement facts plus narrative Markdown.",
|
|
3116
3150
|
"Assign each requirement the SAME id as the ledger canonical requirement it covers (sourceBinding.requirementIds, e.g. AC-001) — do NOT invent new REQ/BR prefixed ids for canonical requirements: the compiled contract must match the ledger canonical requirement ids exactly or schema validation rejects it (unknown requirement id). Requirements use the canonical id with a source span (task-source section or repository file:line). Label each requirement's disposition as explicit | repository-resolvable | assumption | blocking; a blocking requirement must name its owner (human-decision or external-state) and evidence refs.",
|
|
3117
3151
|
"Source fidelity ledger: when the DAG sourceBinding carries a requirement→fragment mapping (requirementToFragments, e.g. REQ-SRC-* ids from the managed ledger), each record_requirement MUST declare the fragments that requirement is bound to: set sourceFragmentIds to the mapped fragment ids (the authoritative provenance evidence the design policy verifies). sourceRefs (fragment→path display refs) are optional — declare them only when you have the exact path from the materialized source; otherwise omit them rather than inventing paths. Declare exactly what the ledger binds — do not invent ids, do not omit them, and do not re-derive them from prose. A requirement that the ledger binds but the contract omits (or fabricates) fails writer admission.",
|
|
@@ -3267,6 +3301,7 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
3267
3301
|
"Request design changes when the Mock strategy is MOCK_STRATEGY: blocked, missing, unsupported by repository evidence, inconsistent with the API contract, outside authorized paths/dependencies, unable to prove production-default-off behavior with the fixed production/default-real-path static check, or missing deterministic behavior verification for a declared behavior target or selected Mock strategy. Mock strategies require Mock-backed evidence. A static-only contract is allowed only when every verification target is static and maps to a declared static entrypoint. not-needed otherwise requires applicable real/no-remote behavior evidence unless auto mode explicitly skipped Mock because no project Mock capability exists; in that case the plan must preserve the real request path and record the Real Integration Gap.",
|
|
3268
3302
|
"Also request design changes for missing applicable UI states, unsupported dependency additions, design-system drift without reason, weak interaction coverage, broad scope, inline fake data, schema drift, or missing deterministic verification commands.",
|
|
3269
3303
|
"Component selection conformance is a hard blocking condition: request_design_changes when the frontend spec (component/theme/rule.components bucket) already defines a component for a purpose but the plan selects another or self-invents one without a declared deviation; when uiComponentChoices is missing/empty for UI-visible work while the frozen component/theme bucket is non-empty; when a decision=specified specReference.path is missing a ledger OpenSpec reference or successful read event; or when a decision=new component lacks a traceable task-source/PRD specReference. A PRD reference for decision=new is not an OpenSpec citation and must not be rejected merely for lacking an OpenSpec read event.",
|
|
3304
|
+
"For uiComponentChoices, purpose is the stable coverage key and must match interaction.name or uiState.name. Responsibility is expressed by the matched expectedBehavior plus rationale; you must not reject it merely for matching an interaction or component identifier.",
|
|
3270
3305
|
"You must NOT make authoritative assertions about the execution result of frozen verification commands (typecheck/test/build/lint/etc.). Predicting that a command will necessarily pass or fail, or declaring an acceptance criterion unreachable on that basis, is out of your authority: command results are deterministically established by frontend-verify-shell. Any concern about verification feasibility must be recorded only as a non-blocking verification concern in findings (severity must not be Critical, and it must never be the sole fatal basis for request_design_changes). Only semantic design defects (component selection, state flow, interaction contract, or conflicts with the specification) may be Critical. A pure command-will-fail prediction must not be classified as contract-requirement-gap.",
|
|
3271
3306
|
"Read-only: do not modify repository files.",
|
|
3272
3307
|
"LARGE-FILE AUDIT (avoid full reads): style/theme audit files can be large (e.g. styles.css is often hundreds of KB). Prefer grep to locate the exact rules/variables you must verify (e.g. grep the oc- class, is-* modifier, or --oc- theme variables with their line numbers), then read only the narrow line range when surrounding context is needed. Do not read a large style/test file in full — a single full read can exhaust the read budget and fail the attempt.",
|
|
@@ -3432,6 +3467,7 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
3432
3467
|
"Treat lint status exactly as passed | baseline-debt | failed | unavailable. baseline-debt may continue only with intact evidence and zero diagnostics on writer-changed files; report the tolerated debt count and never rewrite it as lint passed. Typecheck, build, and test still require successful final exits.",
|
|
3433
3468
|
"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).",
|
|
3434
3469
|
"The contract embedded in frontend-review-context.json is the effective plan materialized by frontend-design-policy-shell. Do not re-open task sources, OpenSpec, AI workspace, design-review, writer summary, or verification node prose. Inspect only the canonical review context, its bound diff, and diff-referenced files when semantic review requires source code.",
|
|
3470
|
+
"For uiComponentChoices, purpose is the stable coverage key and must match interaction.name or uiState.name. Responsibility is expressed by the matched expectedBehavior plus rationale; you must not reject it merely for matching an interaction or component identifier.",
|
|
3435
3471
|
"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/no-remote behavior evidence unless auto mode explicitly skipped Mock because no project Mock capability exists; in that case verify that the real request remains the default and the Real Integration Gap is preserved.",
|
|
3436
3472
|
"Inspect the frontend-verify-shell evidence in the review context directly, including the production/default-real-path static check, and require Mock activation to be off for that check.",
|
|
3437
3473
|
"Distinguish Mock-backed evidence from real API integration evidence and preserve the Real Integration Gap when the backend was not exercised.",
|
|
@@ -3461,6 +3497,34 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
3461
3497
|
},
|
|
3462
3498
|
],
|
|
3463
3499
|
};
|
|
3500
|
+
// The static DAG template is the topology source of truth: the generated
|
|
3501
|
+
// chain must match the template's node set and order, and the template's
|
|
3502
|
+
// depends_on must be a subset of the generated one (the runtime may add
|
|
3503
|
+
// dependencies, e.g. Mock-required planning depending on the contract
|
|
3504
|
+
// node's Mock facts). The template's own tasks carry simplified placeholder
|
|
3505
|
+
// configs; the generated node definitions (budgets, retry, skeleton,
|
|
3506
|
+
// skills, prompts) and any runtime-added dependencies win.
|
|
3507
|
+
const frontendTemplate = await loadFrontendDagTemplate(sources.repoRoot);
|
|
3508
|
+
if (frontendTemplate) {
|
|
3509
|
+
const byId = new Map(spec.tasks.map((task) => [task.id, task]));
|
|
3510
|
+
const templateIds = frontendTemplate.tasks.map((task) => task.id);
|
|
3511
|
+
const missing = templateIds.filter((id) => !byId.has(id));
|
|
3512
|
+
if (missing.length > 0) {
|
|
3513
|
+
throw new Error(`frontend DAG template topology drift: generated chain is missing template node(s): ${missing.join(", ")}`);
|
|
3514
|
+
}
|
|
3515
|
+
for (const templateTask of frontendTemplate.tasks) {
|
|
3516
|
+
const generated = byId.get(templateTask.id);
|
|
3517
|
+
const generatedDeps = new Set(generated.depends_on);
|
|
3518
|
+
const templateOnly = templateTask.depends_on.filter((dep) => !generatedDeps.has(dep));
|
|
3519
|
+
if (templateOnly.length > 0) {
|
|
3520
|
+
throw new Error(`frontend DAG template topology drift: generated node ${templateTask.id} is missing template dependency ${templateOnly.join(", ")}`);
|
|
3521
|
+
}
|
|
3522
|
+
}
|
|
3523
|
+
const templateSet = new Set(templateIds);
|
|
3524
|
+
const ordered = templateIds.map((id) => byId.get(id));
|
|
3525
|
+
const extra = spec.tasks.filter((task) => !templateSet.has(task.id));
|
|
3526
|
+
spec.tasks = [...ordered, ...extra];
|
|
3527
|
+
}
|
|
3464
3528
|
if (frontendTaskShape.shape === "split-required") {
|
|
3465
3529
|
spec.tasks = pruneFrontendTasksForSplitRequired(spec.tasks);
|
|
3466
3530
|
}
|
|
@@ -4649,7 +4713,7 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
4649
4713
|
"Coverage priority is strict inside the declared scope: P0 product requirements/task hard constraints always remain in scope; P1 exhaustively supplements documented operations, fields, business rules, statuses and errors only for Affected Operations; P2 adds bounded protocol robustness only when it is relevant to the change and does not invent product behavior. Coverage percentages describe the declared affected scope, never whole-API completeness unless every operation is explicitly listed. Conflicts or undefined expectations must stay visible as GAP/CONFLICT with precise source pointers, never guessed.",
|
|
4650
4714
|
"For uniqueness/lifecycle rules cover absent, active-existing, deleted-existing, create-delete-recreate, restore-then-recreate and documented scope/case-normalization states. For every enum cover every valid value plus bounded invalid equivalence classes (unknown, case variant, whitespace, empty, null/missing and wrong types as applicable). For every length/number rule cover min-1, min, nominal, max and max+1. For format rules cover each allowed class separately plus a valid mixed value, and representative forbidden classes including uppercase, internal/leading/trailing whitespace, tab/newline, unsupported punctuation, slash, emoji or control characters when the source contract supports that expectation.",
|
|
4651
4715
|
"Mandatory module index: include a `## Module Index` table in the plan artifact that lists every planned module as a canonical relative link of the exact form `[label](./<stem>.md)` plus a `testcase/md/<stem>.md` path cell, so a downstream deterministic manifest can parse the module list. Group by stable business resource/domain, not by CRUD operation: one resource's list/detail/create/update/delete cases belong in one module such as `resource_notes`; split only when a single module would exceed the per-child 16K output protocol, keep the total module count at the smallest safe value, and never exceed 8 modules. Name each module file with a stable lowercase business stem such as `health` or `resource_notes`. Pure hexadecimal/hash-like opaque stems such as `a401606` or `deadbeef` are forbidden. Do not use priority-only stems `p0`, `p1` or `p2`; Priority belongs only in the Coverage Matrix and never defines module files. Do not use Case-ID-like module filenames such as `BE-HEALTH.md` or `BE-NOTES.md`. The relative link target MUST equal the on-disk filename stem the sharded writer will create. For every automatable case, `自动化映射` must name exactly `testcase/test_<module>.py`, where <module> is that Markdown filename without `.md`, lowercased, with non-alphanumeric characters replaced by underscores. Example: `testcase/md/health.md` → `testcase/test_health.py`; `testcase/md/resource_notes.md` → `testcase/test_resource_notes.py`. Never invent a different pytest path in Markdown than the module stem implies.",
|
|
4652
|
-
"Scenario Partitions (query/filter axes): for every affected GET/list operation, declare one row per enum or classification axis used for filtering (query/path parameters such as type/status/category). Add a mandatory machine-readable `## Scenario Partitions` section after the Coverage Matrix using exactly `| Partition ID | Operation | Axis | Domain | Required Slots | Expected by Slot | Bind Rule |` with the separator row. Partition ID is a stable `SP-<OPERATION>-<AXIS>` token; Domain must copy the legal values verbatim from the bound OpenAPI enum or requirement sentence (never guess), using bare semicolon-separated identifier values inside the single table cell (for example `ACTIVE; ARCHIVED`, with no Markdown backticks or prose); Required Slots
|
|
4716
|
+
"Scenario Partitions (query/filter axes): for every affected GET/list operation, declare one row per enum or classification axis used for filtering (query/path parameters such as type/status/category). Add a mandatory machine-readable `## Scenario Partitions` section after the Coverage Matrix using exactly `| Partition ID | Operation | Axis | Domain | Required Slots | Expected by Slot | Bind Rule |` with the separator row. Partition ID is a stable `SP-<OPERATION>-<AXIS>` token; Domain must copy the legal values verbatim from the bound OpenAPI enum or requirement sentence (never guess), using bare semicolon-separated identifier values inside the single table cell (for example `ACTIVE; ARCHIVED`, with no Markdown backticks or prose); Required Slots must contain `each-value` and exactly one `not-in-set`, plus `omitted` only when the parameter is optional; Expected by Slot states the documented expectation per slot kind (`domain-value`, `default-behavior`, `empty-result`/`excluded-result` when documented, or `GAP` when the source does not document the complement expectation — never invent 空列表/400). POST/PUT body field-validation enums stay in the Coverage Matrix as `TP-<FIELD>-ENUM-*` and MUST NOT get a Scenario Partition row. Do not create partitions for axes without a documented legal-value domain. Only GET/list query or path parameters whose bound source documents a finite enum or classification set may become a Scenario Partition. Do not create partitions for free-form strings, primary keys, required-or-optional-only parameters, or boundary/format-only axes. If an axis has no finite legal-value domain, do not declare a Partition row and do not invent NOT-IN-SET cases. Cross-axis combinations stay as ONE nominal Case; never declare a cross-axis cartesian partition.",
|
|
4653
4717
|
"Before finalizing README, calculate the predicted collected-item count as `sum(max(1, number of variant Test Points in each Case))`. If the task declares an item budget, the prediction must not exceed it. Reduce excess only by removing duplicate execution and converting same-request checkpoints to assertions; never drop required rules, boundaries, enums, operation-specific inputs, or business states. Record the prediction in README. Use only environment-supported fixtures/targets/isolation, record evidence gaps in Chinese, and do not emit JSON, pytest, or execute commands.",
|
|
4654
4718
|
...(sharedSetupPrompt ? [sharedSetupPrompt] : []),
|
|
4655
4719
|
intake.boundedSourceContext,
|
|
@@ -4944,7 +5008,7 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
4944
5008
|
writerOutcomePolicy: { type: "implementation-outcome-v1", requireChangedFiles: true },
|
|
4945
5009
|
outputContract: "First non-empty line is IMPLEMENTATION_OUTCOME: changed|blocked, followed by a concise repair summary. This node runs only for REPAIRABLE initial facts, so already-satisfied is invalid and a successful outcome requires a non-empty bounded diff. Modify only generated pytest scripts/helpers/factories and preserve every Markdown Case, Test Point, primary symbol and assertion meaning.",
|
|
4946
5010
|
subtask_prompt: [
|
|
4947
|
-
"Repair the generated backend pytest asset as one bounded program using the direct upstream collection assessment. This is the only repair attempt and happens before any business test body execution. Treat any upstream line such as `Repair paths: testcase/test_x.py` as
|
|
5011
|
+
"Repair the generated backend pytest asset as one bounded program using the direct upstream collection assessment. This is the only repair attempt and happens before any business test body execution. The direct upstream JSON includes authoritative `repairPaths` and bounded `repairFindings`; treat both as the complete mandatory checklist without searching for a run directory or report file. Treat any upstream line such as `Repair paths: testcase/test_x.py` as equivalent authoritative repairPaths evidence. Directly read and edit that testcase path; do not search for separate root-level `contracts/**`, guess a DAG run directory, or require another report artifact. If the read tool successfully returns the testcase file, the path exists—continue the bounded repair and never later claim that file is absent.",
|
|
4948
5012
|
"Initial status REPAIRABLE means at least one listed finding remains: `already-satisfied` is forbidden, and you must produce a non-empty bounded diff on repairPaths before returning `IMPLEMENTATION_OUTCOME: changed`. Fix only readiness-proven generated testcase-local defects on initial facts repairPaths: create exact safe missing mapped test_*.py paths, repair syntax/import/symbol/decorator/parameterization, close generated fixture dependencies/plugin registration, and repair initial Markdown-to-pytest correspondence findings. Use this deterministic repair map instead of reading analyzer implementation: findings about `Case-ID`, `Assertion-Test-Points`, or `Cross-Cutting-Test-Points` are fixed by editing the declared primary symbol docstring metadata lines; variant binding findings are fixed in the literal direct `pytest.param(..., id=\"TP-...\")` row; primary-symbol cardinality/name findings are fixed in the function name or duplicate primary symbols; script mismatch is fixed only on the authoritative assessment repairPaths; payload findings are fixed in request payload construction. Do not read controller `src/**` or inspect JS/TS analyzer code. Do not search for `testcase/**/README.md`. Never invent a business pytest symbol for evidence-only Markdown Cases that declare `脚本/primary symbol=无` with empty variants. For fixture defects inspect both provider and importer listed by repairPaths; fix ScopeMismatch by aligning fixture scopes or inlining request-scoped values so module fixtures never depend on function fixtures; when a shared fixture depends on sibling fixtures, register the whole provider module through an exact pytest_plugins declaration rather than importing only the outer fixture. Do not create unrelated pytest scripts.",
|
|
4949
5013
|
"This is the single pytest incremental synchronization round. The `Findings` in `reports/backend-test-pytest-collection-initial.md` are the mandatory repair checklist: resolve every repairable listed finding on every authoritative `Repair paths` file before considering any other advisory evidence, and never substitute an unrelated scenario-param cleanup for a listed correspondence/collection defect. For every assessment-listed path, compare the effective Markdown Case/Test Points/test data and its `Payload Contract`/`Payload Required Paths`/`Payload Allowed Paths`/`Payload Enum` labels with the generated module. Incrementally add or repair only missing symbols, params, assertions and payload builders. Repair every assessment-listed missing nested path, unexpected key and enum mismatch; preserve exact DTO keys, nested shapes, enum/boundary literals, operation transport and business preconditions; remove guessed replacement keys only when the effective Markdown proves the exact contract. Keep path/query/header identifiers and scenario-control metadata separate from DTO patches and JSON bodies; an `id` used for a path target must be passed to the request path/helper, never inserted into a body patch unless `id` is explicitly listed in Payload Allowed Paths. Flatten every variant into a literal direct `pytest.param(..., id=\"TP-...\")` row; replace `_post_case`/`_put_case` or other parameter-row factories because correspondence and scenario readiness require the actual row values and IDs to be statically visible. Also repair helper call sites to match their defined return signatures; do not tuple-unpack a helper that returns one scalar value.",
|
|
4950
5014
|
"Preserve final testcase/md/** semantics, every Case ID, Rule/Test Point binding, primary symbol, parameter ID, expected status/body/schema assertion, HTTP logging, redaction and truncation behavior.",
|