@tea-agent/loop-agent 0.43.0-next.17 → 0.43.0-next.3
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 +9 -140
- package/dist/application/task-lifecycle/advance.js +4 -17
- package/dist/application/task-lifecycle/observe.js +29 -43
- package/dist/application/task-lifecycle/plan-transitions.js +4 -5
- package/dist/build-stamp.json +3 -3
- package/dist/commands/client-recovery.js +55 -20
- package/dist/commands/new-task.js +5 -0
- package/dist/commands/status.js +57 -0
- package/dist/commands/task-contract.js +269 -0
- package/dist/executors/dag-pi-executor.js +230 -269
- package/dist/executors/pi-executor.js +0 -2
- package/dist/executors/pi-sdk-executor.js +39 -77
- package/dist/executors/shell-executor.js +2 -3
- package/dist/executors/shell-write-guard.js +32 -27
- package/dist/shared/pi-context-pressure/extension.js +16 -26
- package/dist/shared/pi-retry-settings.js +0 -40
- package/dist/shared/prompts.js +26 -0
- package/dist/task/config.js +2 -0
- package/dist/task/contract/validate-draft.js +106 -0
- package/dist/task/goal.js +3 -0
- package/dist/task/index.js +12 -0
- package/dist/task/lifecycle.js +1 -0
- package/dist/task/operator/capabilities.js +6 -0
- package/dist/task/operator/envelope.js +2 -0
- package/dist/task/operator/index.js +5 -0
- package/dist/task/operator/registry.js +2 -0
- package/dist/task/operator/types.js +1 -0
- package/dist/task/paths.js +1 -0
- package/dist/task/source-prepare/source-fidelity-pi.js +2 -2
- package/dist/task/source-references.js +23 -48
- package/dist/task/source-state.js +1 -0
- package/dist/task/subagent-guidance.js +1 -0
- package/dist/worker/console/chat/assistant-content.js +2 -23
- package/dist/worker/console/chat/explore-tools.js +0 -13
- package/dist/worker/console/chat/pi-runtime.js +1182 -221
- package/dist/worker/console/chat/routes.js +71 -213
- package/dist/worker/console/chat/sdd-data-alignment.js +9 -65
- package/dist/worker/console/chat/session-catalog.js +0 -32
- package/dist/worker/console/chat/session-store.js +6 -22
- package/dist/worker/console/chat/shortcuts.js +0 -6
- package/dist/worker/console/chat/sift-bridge.js +1 -0
- package/dist/worker/console/chat/tool-preview.js +0 -11
- package/dist/worker/console/chat/tools.js +0 -6
- package/dist/worker/console/console-update-and-init.js +19 -1
- package/dist/worker/console/console-update-runtime.js +20 -5
- package/dist/worker/console/index.js +20 -0
- package/dist/worker/console/operator-actions.js +1 -13
- package/dist/worker/console/routes.js +0 -8
- package/dist/worker/console/server.js +29 -11
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C4NXyedy.js → abnfDiagram-N423BO3Z-hUmM_GRf.js} +1 -1
- package/dist/worker/console/static/assets/{arc-NZ3qXvBI.js → arc-Bt-xuieW.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-COZSRvml.js → architectureDiagram-T3A2C74G-DTqkL4DJ.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-Ck0Gxi9C.js → blockDiagram-VBNYF7ZC-Cy0MNnzV.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BX5HQ4Ox.js → c4Diagram-5PPSVZJV-DDdlT0Pd.js} +1 -1
- package/dist/worker/console/static/assets/channel-BpzRnZmU.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-CoviemSL.js → chunk-2GRJ4B5K-3JawwNZ6.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DbOdOkbZ.js → chunk-2Q5K7J3B-DoTp7Cri.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BsG2pmdQ.js → chunk-5RXB4S5H-CcirhZaS.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-DGdvrNm8.js → chunk-5VM5RSS4-VrG21gnP.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BAy7Yd6y.js → chunk-6Q2QTUOP-DDOgIS70.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-D9UA7hUb.js → chunk-GF5L2VYU-CBjmgvJ_.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-DPz7SZ8V.js → chunk-JWPE2WC7-CCQSht8M.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BmuEOIh7.js → chunk-KBJHAD2P-BpGMkUUV.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CIhBWnd8.js → chunk-RYQCIY6F-DNDT41QL.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CScR6mDD.js → chunk-XXDRQBXY-euXnDFbn.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BhlMVqCc.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BhlMVqCc.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BjpfWaUr.js → cose-bilkent-JH36ORCC-BJ6NAeql.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BFQohJgT.js → cynefin-VYW2F7L2-F5JKIvIq.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-D3gzRJnS.js → cynefinDiagram-MW4NZA55-Cwmkl_Gi.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-5eojkhoA.js → dagre-VZM6K2ZE-BmCZ8815.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-CpD8LWu_.js → diagram-7IWD3JNH-zWA3hUnX.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B8_4qJ2a.js → diagram-B4RE2ZJO-BzA-888F.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-HOsXf0eu.js → diagram-LBJQPF4R-tPNheoch.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-BqwRwiWP.js → diagram-Q27KOJAE-Cdi7Fyqp.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BRVCNZe1.js → diagram-UB23O5K3-DFK9VBf1.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-Cvb1TgGh.js → ebnfDiagram-BXEA7PRR-xCUr1kpg.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DS0O0qG9.js → erDiagram-JOGREHBK-BANQHY_A.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-BHIRvRr3.js → flowDiagram-UKHOOZJN-VHJYEMkP.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-DcCJ1JWx.js → ganttDiagram-PKOTCBZU-DiyZQ0C6.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-B9ia5ony.js → gitGraphDiagram-DS77QQ5N-CZtIeBBa.js} +1 -1
- package/dist/worker/console/static/assets/index-BlN6ytLr.css +1 -0
- package/dist/worker/console/static/assets/index-CA-jfWlE.js +449 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-3ogpgegn.js → infoDiagram-6WML65LV-BuSspC0X.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-4tgpQ5SL.js → ishikawaDiagram-WSZJBQD7-DRJZyPFy.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DNfgHqFx.js → journeyDiagram-NVQOT4AX-DIfXIVuE.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Dz7ngC8z.js → kanban-definition-27J2QSJJ-CT10_7Lt.js} +1 -1
- package/dist/worker/console/static/assets/{linear-DI7Jv62E.js → linear-ufemj74H.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-CQA4Nzmo.js → mermaid.core-DEQe8-_m.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-CtsMc8Mf.js → mindmap-definition-FAOFIHXS-BRLUrbrC.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-ByppE9yH.js → pegDiagram-VL7TDLO6-BgibKtiF.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-B5PtgFgt.js → pieDiagram-7S7Q4E2Y-D3eyfdV9.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-Cq4p0k2K.js → quadrantDiagram-CIZ2JOQS-DXeTk5Px.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-DjfkyljN.js → railroadDiagram-AXF67PYL-BgXdFEe7.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DzynNRSl.js → requirementDiagram-LRYGKXZP-CseC2A7R.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Cj7TCpTJ.js → sankeyDiagram-W5VNT64P-D80PFzfJ.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-C73C6ykA.js → sequenceDiagram-SI44F4Z6-BWzHW7LS.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-BiNLgN2x.js → sizeCapture-X5ZJPWSS-xWQYTLAn.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-UwvjS64-.js → stateDiagram-OKZ733FA-CLxtpZeL.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-CiVoq4G-.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CC7DH98r.js → swimlanes-SLNWSIFB-DwugtmWV.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BkdgdpfR.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-LSBXTR70.js → timeline-definition-Z64GVDOM-DHEOwHIe.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BAuxWwGP.js → vennDiagram-T6HMQDX7-pmbZKlBK.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y--E5_gjZx.js → wardleyDiagram-T6FBY63Y-CivHoM5q.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-dJWNmyzF.js → xychartDiagram-ELKLHX3M-BfHIE7o6.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +10 -122
- package/dist/worker/console/static-src/operator-chat/compaction-message.js +0 -7
- package/dist/worker/console/static-src/operator-chat/pending-user-message.js +15 -35
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +62 -136
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +68 -159
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +56 -75
- package/dist/worker/console/static-src/operator-chat/useComposer.js +78 -206
- package/dist/worker/console/workspace-context.js +1 -21
- package/dist/worker/observe/static/router.js +1 -1
- package/dist/worker/observe/static/styles.css +4 -7
- package/dist/worker/observe/static/views/dag-inspector.js +19 -12
- package/dist/worker/observe/static/views/dag.js +2 -2
- package/dist/worker/scheduler/index.js +25 -0
- package/dist/workflows/dag/facts.js +4 -0
- package/dist/workflows/dag/frontend-closeout.js +1 -1
- package/dist/workflows/dag/frontend-design-policy.js +1 -2
- package/dist/workflows/dag/frontend-implementation-contract.js +4 -8
- package/dist/workflows/dag/frontend-prewrite-gate.js +3 -3
- package/dist/workflows/dag/frontend-repair.js +76 -55
- package/dist/workflows/dag/frontend-review-context.js +1 -2
- package/dist/workflows/dag/frontend-review-scopes.js +12 -34
- package/dist/workflows/dag/frontend-shape-facts.js +2 -12
- package/dist/workflows/dag/frontend-verification-trace.js +42 -8
- package/dist/workflows/dag/index.js +6 -0
- package/dist/workflows/dag/init-hybrid.js +160 -97
- package/dist/workflows/dynamic/index.js +7 -0
- package/docs/architecture/runtime-boundaries.md +3 -3
- package/docs/templates/frontend-implementation-dag.json +8 -9
- package/package.json +3 -7
- package/skills/frontend-bounded-implement/SKILL.md +3 -4
- package/skills/frontend-bounded-implement/references/code-standards.md +3 -3
- package/skills/frontend-contract/references/contract-protocol.md +1 -3
- package/skills/frontend-design-review/SKILL.md +7 -12
- package/skills/frontend-design-review/references/review-checklist.md +7 -7
- package/skills/frontend-plan/SKILL.md +0 -19
- package/skills/frontend-plan/references/decision-contract.md +19 -26
- package/skills/frontend-review/SKILL.md +18 -12
- package/skills/frontend-review/references/review-findings.md +43 -24
- package/dist/shared/pi-provider-recovery.js +0 -182
- package/dist/worker/console/chat/browser-automation.js +0 -363
- package/dist/worker/console/chat/browser-policy.js +0 -113
- package/dist/worker/console/chat/browser-routes.js +0 -162
- package/dist/worker/console/chat/pi-runtime/custom-tools/ask-user-question.js +0 -44
- package/dist/worker/console/chat/pi-runtime/custom-tools/browser-tools.js +0 -89
- package/dist/worker/console/chat/pi-runtime/custom-tools/explore-tools.js +0 -202
- package/dist/worker/console/chat/pi-runtime/custom-tools/goal-tools.js +0 -79
- package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +0 -86
- package/dist/worker/console/chat/pi-runtime/custom-tools/todo-write.js +0 -38
- package/dist/worker/console/chat/pi-runtime/inspection-types.js +0 -28
- package/dist/worker/console/chat/pi-runtime/inspection.js +0 -439
- package/dist/worker/console/chat/pi-runtime/sdk-bindings.js +0 -153
- package/dist/worker/console/chat/pi-runtime/sdk-session.js +0 -5
- package/dist/worker/console/chat/scm-routes.js +0 -217
- package/dist/worker/console/chat/scm-service.js +0 -283
- package/dist/worker/console/chat/scm-tools.js +0 -111
- package/dist/worker/console/chat/session-mode-view.js +0 -49
- package/dist/worker/console/chat/session-mode.js +0 -220
- package/dist/worker/console/chat/terminal-routes.js +0 -216
- package/dist/worker/console/chat/terminal-sessions.js +0 -348
- package/dist/worker/console/chat/terminal-tools.js +0 -211
- package/dist/worker/console/static/assets/channel-BxdTlb_5.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-_ByA1Nf8.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-_ByA1Nf8.js +0 -1
- package/dist/worker/console/static/assets/index-B9goN3S5.css +0 -1
- package/dist/worker/console/static/assets/index-wXlQohNs.js +0 -469
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-nLgMZ0D3.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BcYqTady.js +0 -8
- package/dist/worker/console/static-src/operator-chat/active-turn-tail.js +0 -10
- package/dist/worker/console/static-src/operator-chat/chat-link.js +0 -94
- package/dist/worker/console/static-src/operator-chat/chat-scroll-position.js +0 -24
- package/dist/worker/console/static-src/operator-chat/composer-session-ledger.js +0 -192
- package/dist/worker/console/static-src/operator-chat/process-label.js +0 -41
- package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +0 -13
- package/dist/worker/console/static-src/operator-chat/turn-process-disclosure.js +0 -10
- package/dist/worker/console/static-src/operator-chat/use-searchable-hidden.js +0 -51
- package/dist/worker/console/static-src/operator-chat/user-turn-anchor-equality.js +0 -21
- package/dist/worker/console/workspace-initialization.js +0 -209
- package/dist/workflows/dag/frontend-capacity.js +0 -9
- package/dist/workflows/dag/frontend-plan-completeness.js +0 -186
- package/dist/workflows/dag/frontend-plan-recovery-policy.js +0 -18
|
@@ -699,6 +699,7 @@ export function frontendSourceMentionsMock(sources) {
|
|
|
699
699
|
*/
|
|
700
700
|
export function resolveFrontendMockMode(capability, taskConfig, hasApiDep) {
|
|
701
701
|
const policy = taskConfig.frontendMock?.policy ?? "auto";
|
|
702
|
+
const hasDeterministicMockVerification = capability.verifyCommands.length > 0;
|
|
702
703
|
if (capability.safetyViolation ||
|
|
703
704
|
!frontendMockServiceRootAllowed(taskConfig)) {
|
|
704
705
|
return "blocked";
|
|
@@ -714,7 +715,7 @@ export function resolveFrontendMockMode(capability, taskConfig, hasApiDep) {
|
|
|
714
715
|
}
|
|
715
716
|
// required policy
|
|
716
717
|
if (policy === "required") {
|
|
717
|
-
if (capability.status === "present") {
|
|
718
|
+
if (capability.status === "present" && hasDeterministicMockVerification) {
|
|
718
719
|
return "required";
|
|
719
720
|
}
|
|
720
721
|
return "blocked";
|
|
@@ -723,8 +724,13 @@ export function resolveFrontendMockMode(capability, taskConfig, hasApiDep) {
|
|
|
723
724
|
if (!hasApiDep) {
|
|
724
725
|
return "not-required";
|
|
725
726
|
}
|
|
726
|
-
//
|
|
727
|
-
|
|
727
|
+
// In auto mode, frontend Mock is optional. Only enter Mock-backed "required"
|
|
728
|
+
// mode when the project has a confirmed native Mock capability AND
|
|
729
|
+
// deterministic Mock verification commands. Without executable verification
|
|
730
|
+
// commands a non-not-needed strategy could never be verified, so auto falls
|
|
731
|
+
// back to not-required; the prewrite gate then forces not-needed and the
|
|
732
|
+
// plan records the Real Integration Gap instead of inventing commands.
|
|
733
|
+
return capability.status === "present" && hasDeterministicMockVerification
|
|
728
734
|
? "required"
|
|
729
735
|
: "not-required";
|
|
730
736
|
}
|
|
@@ -1020,51 +1026,6 @@ function extractFrontendVerifyCommandsFromMarkdown(input) {
|
|
|
1020
1026
|
}
|
|
1021
1027
|
return { staticCommands, behaviorCommands };
|
|
1022
1028
|
}
|
|
1023
|
-
/** Reuse the project's test entrypoint; never install a runner for an untested project. */
|
|
1024
|
-
async function resolveFrontendExistingTests(sources) {
|
|
1025
|
-
const repoRoot = sources.repoRoot ?? process.cwd();
|
|
1026
|
-
const explicit = buildExplicitFrontendVerifyCommands(sources.taskConfig, repoRoot).behaviorCommands;
|
|
1027
|
-
if (explicit.length > 0)
|
|
1028
|
-
return explicit;
|
|
1029
|
-
const declared = extractFrontendVerifyCommandsFromMarkdown({ ...sources, repoRoot }).behaviorCommands;
|
|
1030
|
-
if (declared.length > 0)
|
|
1031
|
-
return declared;
|
|
1032
|
-
let manifest;
|
|
1033
|
-
try {
|
|
1034
|
-
manifest = JSON.parse(await readFile(path.join(repoRoot, "package.json"), "utf8"));
|
|
1035
|
-
}
|
|
1036
|
-
catch (error) {
|
|
1037
|
-
if (error.code === "ENOENT")
|
|
1038
|
-
return [];
|
|
1039
|
-
throw error;
|
|
1040
|
-
}
|
|
1041
|
-
const script = manifest.scripts?.test;
|
|
1042
|
-
if (!script || /no test specified/i.test(script))
|
|
1043
|
-
return [];
|
|
1044
|
-
// Focus concrete existing tests; unknown runners and broad scopes retain the
|
|
1045
|
-
// project entrypoint instead of receiving guessed filtering flags.
|
|
1046
|
-
const files = [];
|
|
1047
|
-
for (const file of sources.taskConfig.allowedPaths) {
|
|
1048
|
-
if (/[*?\[\]{}]/.test(file) || !/\.(?:test|spec)\.[cm]?[jt]sx?$/.test(file))
|
|
1049
|
-
continue;
|
|
1050
|
-
if (sources.taskConfig.forbiddenPaths.some((pattern) => pathMatchesPattern(file, pattern)))
|
|
1051
|
-
continue;
|
|
1052
|
-
if (existsSync(path.join(repoRoot, file)))
|
|
1053
|
-
files.push(file);
|
|
1054
|
-
}
|
|
1055
|
-
const runner = /(?:^|[\s/])jest(?:[\s/.]|$)/.test(script) ? "jest"
|
|
1056
|
-
: /(?:^|[\s/])vitest(?:[\s/.]|$)/.test(script) ? "vitest" : undefined;
|
|
1057
|
-
const args = ["npm", "test"];
|
|
1058
|
-
if (runner) {
|
|
1059
|
-
args.push("--");
|
|
1060
|
-
if (runner === "jest")
|
|
1061
|
-
args.push("--runInBand");
|
|
1062
|
-
else if (!/\b(?:run|--run)\b/.test(script))
|
|
1063
|
-
args.push("--run");
|
|
1064
|
-
args.push(...files);
|
|
1065
|
-
}
|
|
1066
|
-
return [{ label: "existing unit tests", args, cwd: repoRoot }];
|
|
1067
|
-
}
|
|
1068
1029
|
function extractFrontendMockVerifyCommandsFromMarkdown(input) {
|
|
1069
1030
|
const commands = [];
|
|
1070
1031
|
const markdown = [
|
|
@@ -2583,7 +2544,7 @@ function resolveFrontendMockContextBlock(sources) {
|
|
|
2583
2544
|
parts.push(`Evidence Paths: ${capability.evidencePaths.join(", ")}`);
|
|
2584
2545
|
}
|
|
2585
2546
|
if (capability.verifyCommands.length > 0) {
|
|
2586
|
-
parts.push(`
|
|
2547
|
+
parts.push(`Frozen Mock Verify Commands: ${capability.verifyCommands
|
|
2587
2548
|
.map((command) => command.label)
|
|
2588
2549
|
.join(", ")}`);
|
|
2589
2550
|
}
|
|
@@ -2594,13 +2555,13 @@ function resolveFrontendMockContextBlock(sources) {
|
|
|
2594
2555
|
parts.push(`Reasons: ${capability.reasons.join("; ")}`);
|
|
2595
2556
|
}
|
|
2596
2557
|
if (mode === "required") {
|
|
2597
|
-
parts.push("
|
|
2558
|
+
parts.push("Mock-backed frontend verification is required. Prefer the detected native service; otherwise the assessment may select an existing browser interception harness or reversible request adapter. Any handler, fixture, adapter, and UI changes stay in the single frontend-implement-pi writeSet.");
|
|
2598
2559
|
}
|
|
2599
2560
|
if (mode === "not-required") {
|
|
2600
2561
|
parts.push("Generation-time evidence does not require Mock. The assessment must still use contract/scout evidence: select not-needed when Mock is intentionally skipped, or select a safe Mock strategy if project evidence supports one.");
|
|
2601
2562
|
if (frontendMockStrategyMustBeNotNeeded(sources)) {
|
|
2602
|
-
parts.push('Auto mode has no confirmed project Mock capability. The structured contract must set mockApi.strategy to "not-needed". Keep the real request path as the default, record any unproved backend behavior as Real Integration Gap, and do not add Mock files or dependencies within this run.');
|
|
2603
|
-
parts.push('HARD CONSTRAINT (frozen at generation time): this DAG allows only mockApi.strategy "not-needed"; the prewrite gate rejects any other strategy. If project governance (openspec / ai_workspace / decision records, e.g. a DEC rule requiring native) demands Mock-backed verification, that is a generation-time contract gap, not a plan-revision defect:
|
|
2563
|
+
parts.push('Auto mode has no confirmed project Mock capability or no deterministic Mock verification command. The structured contract must set mockApi.strategy to "not-needed". Keep the real request path as the default, record any unproved backend behavior as Real Integration Gap, and do not add Mock files or dependencies within this run.');
|
|
2564
|
+
parts.push('HARD CONSTRAINT (frozen at generation time): this DAG allows only mockApi.strategy "not-needed"; the prewrite gate rejects any other strategy. If project governance (openspec / ai_workspace / decision records, e.g. a DEC rule requiring native) demands Mock-backed verification, that is a generation-time contract gap, not a plan-revision defect: declare frontendMock.verifyCommands (or policy: "required") in task.json and regenerate the DAG.');
|
|
2604
2565
|
}
|
|
2605
2566
|
}
|
|
2606
2567
|
if (mode === "blocked") {
|
|
@@ -2612,10 +2573,12 @@ function frontendMockStrategyMustBeNotNeeded(sources) {
|
|
|
2612
2573
|
const policy = sources.taskConfig.frontendMock?.policy ?? "auto";
|
|
2613
2574
|
const capability = sources.frontendMockCapability;
|
|
2614
2575
|
const capabilityStatus = capability?.status;
|
|
2576
|
+
const hasDeterministicMockVerification = (capability?.verifyCommands.length ?? 0) > 0;
|
|
2615
2577
|
return (policy === "auto" &&
|
|
2616
2578
|
(sources.frontendMockMode ?? "not-required") === "not-required" &&
|
|
2617
2579
|
(capabilityStatus === "absent" ||
|
|
2618
|
-
capabilityStatus === "ambiguous"
|
|
2580
|
+
capabilityStatus === "ambiguous" ||
|
|
2581
|
+
!hasDeterministicMockVerification));
|
|
2619
2582
|
}
|
|
2620
2583
|
/**
|
|
2621
2584
|
* 候选规范懒加载:prompt 只内联与任务相关的候选,不全部塞给模型。
|
|
@@ -3083,7 +3046,7 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
3083
3046
|
"- interactions[]: name, trigger, expectedBehavior, implementationTargets, verificationTargetIds",
|
|
3084
3047
|
"- targets: routes, publicApiChanges (files are runtime-owned)",
|
|
3085
3048
|
"- mockApi: strategy, productionDefaultOff, activation, endpoints[]",
|
|
3086
|
-
"- verificationTargets[]: id (
|
|
3049
|
+
"- verificationTargets[]: id (stable test-title trace token for behavior targets), commandId (frozen directory key), mode + commandLabel (runtime-resolved), file, requirementIds, uiStates, scope? (display-only)",
|
|
3087
3050
|
"- designEvidence: source, paths, conflicts; evidenceGaps[] (optional)",
|
|
3088
3051
|
"- optional: stylingStrategy, uiComponentChoices[], dependencyPolicy, residualRisks[], realIntegrationGap",
|
|
3089
3052
|
"- uiComponentChoices[]: purpose, component, decision (specified|reuse-existing|new), specReference { path, section, line } | null, rationale",
|
|
@@ -3110,8 +3073,12 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
3110
3073
|
includeReferenceDocuments: false,
|
|
3111
3074
|
}),
|
|
3112
3075
|
};
|
|
3076
|
+
const hasMockVerifyCommands = (taskConfig.frontendMock?.verifyCommands.length ?? 0) > 0 ||
|
|
3077
|
+
mockCapability.verifyCommands.length > 0;
|
|
3113
3078
|
const requirementIds = frontendSourceBinding.requirementIds;
|
|
3079
|
+
const strategy = resolveDagVerifyStrategy(taskConfig);
|
|
3114
3080
|
const readOnlyPaths = taskConfig.allowedPaths.length > 0 ? taskConfig.allowedPaths : ["**"];
|
|
3081
|
+
const behaviorPaths = deriveFrontendBehaviorPaths(taskConfig);
|
|
3115
3082
|
const globalConstraints = [
|
|
3116
3083
|
...taskConfig.hardConstraints,
|
|
3117
3084
|
...(sources.constraintMarkdown
|
|
@@ -3125,7 +3092,7 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
3125
3092
|
"Frontend planning must consume the read-only Mock assessment strategy produced after scouting; MOCK_STRATEGY: blocked must not pass the deterministic Mock contract gate.",
|
|
3126
3093
|
"Mock implementations must preserve the real request path as the default, require explicit test/dev activation, and never rely on commenting out the real request.",
|
|
3127
3094
|
"Mock-backed behavior evidence proves only the documented frontend contract, never real API integration.",
|
|
3128
|
-
"frontend-implementation
|
|
3095
|
+
"frontend-implementation DAGs must complete deterministic static verification before final review. Behavior verification is also required when the task declares a behavior entrypoint or the implementation contract contains a behavior-mode verification target; static-only contracts must map every target to the declared static entrypoint.",
|
|
3129
3096
|
"Frontend closeout renders only from committed facts; a weak status (failed / not-run / baseline-debt / mock-backed / pending) can never be rewritten into a stronger one (passed / real-integrated).",
|
|
3130
3097
|
`Frontend risk classification: ${frontendRisk.selectedRisk} — ${frontendRisk.reason}`,
|
|
3131
3098
|
frontendRisk.forceFullGates
|
|
@@ -3137,34 +3104,111 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
3137
3104
|
const blockedReason = mockCapability.safetyViolation
|
|
3138
3105
|
? `Mock contract blocked: ${mockCapability.safetyViolation}`
|
|
3139
3106
|
: (taskConfig.frontendMock?.policy ?? "auto") === "required"
|
|
3140
|
-
? "Mock strategy is required, but no authorized Mock
|
|
3107
|
+
? "Mock strategy is required, but no authorized Mock verification command was found."
|
|
3141
3108
|
: "Mock contract is blocked by deterministic generation-time Mock safety constraints.";
|
|
3142
3109
|
return buildBlockedFrontendMockDag(frontendSources, readOnlyPaths, forbiddenPaths, globalConstraints, blockedReason);
|
|
3143
3110
|
}
|
|
3144
|
-
|
|
3145
|
-
//
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3111
|
+
const fallbackVerifyCommands = await discoverFrontendFallbackVerifyCommands(sources.repoRoot);
|
|
3112
|
+
// The verification bundle schema requires at least one static command. When
|
|
3113
|
+
// a real project exposes no usable verification command at all, run an
|
|
3114
|
+
// explicit no-op marker instead of a command that is guaranteed to fail:
|
|
3115
|
+
// the trace then records not-run honestly and the advisory asks the task
|
|
3116
|
+
// to declare verification commands and regenerate.
|
|
3117
|
+
const staticFallbackCommands = fallbackVerifyCommands.staticCommands.length > 0
|
|
3118
|
+
? fallbackVerifyCommands.staticCommands
|
|
3119
|
+
: [FRONTEND_NO_STATIC_VERIFICATION_MARKER];
|
|
3120
|
+
const behaviorFallbackCommands = fallbackVerifyCommands.behaviorCommands;
|
|
3121
|
+
const parsedFrontendVerifyCommands = extractFrontendVerifyCommandsFromMarkdown({
|
|
3122
|
+
repoRoot: sources.repoRoot,
|
|
3123
|
+
requirementMarkdown: sources.requirementMarkdown,
|
|
3124
|
+
constraintMarkdown: sources.constraintMarkdown,
|
|
3125
|
+
});
|
|
3126
|
+
const explicitFrontendVerifyCommands = buildExplicitFrontendVerifyCommands(taskConfig, sources.repoRoot);
|
|
3127
|
+
const explicitCommandKeys = new Set([
|
|
3128
|
+
...explicitFrontendVerifyCommands.staticCommands,
|
|
3129
|
+
...explicitFrontendVerifyCommands.behaviorCommands,
|
|
3130
|
+
].map(verifyCommandKey));
|
|
3131
|
+
const adapterVerifyCommands = (sources.verifyCommands?.final ?? []).filter((command) => !explicitCommandKeys.has(verifyCommandKey(command)));
|
|
3132
|
+
const hasDeclaredFrontendVerification = explicitFrontendVerifyCommands.staticCommands.length > 0 ||
|
|
3133
|
+
explicitFrontendVerifyCommands.behaviorCommands.length > 0 ||
|
|
3134
|
+
parsedFrontendVerifyCommands.staticCommands.length > 0 ||
|
|
3135
|
+
parsedFrontendVerifyCommands.behaviorCommands.length > 0;
|
|
3136
|
+
const staticVerifyCommands = chooseFrontendVerifyCommands({
|
|
3137
|
+
explicitCommands: explicitFrontendVerifyCommands.staticCommands,
|
|
3138
|
+
parsedCommands: parsedFrontendVerifyCommands.staticCommands,
|
|
3139
|
+
adapterCommands: adapterVerifyCommands,
|
|
3140
|
+
});
|
|
3141
|
+
const partitionedStaticVerifyCommands = partitionFrontendStaticVerifyCommands({
|
|
3142
|
+
repoRoot: sources.repoRoot,
|
|
3143
|
+
commands: staticVerifyCommands.commands,
|
|
3144
|
+
commandSource: staticVerifyCommands.commandSource,
|
|
3145
|
+
});
|
|
3146
|
+
const behaviorVerifyCommands = chooseFrontendVerifyCommands({
|
|
3147
|
+
explicitCommands: explicitFrontendVerifyCommands.behaviorCommands,
|
|
3148
|
+
parsedCommands: parsedFrontendVerifyCommands.behaviorCommands,
|
|
3149
|
+
adapterCommands: adapterVerifyCommands,
|
|
3150
|
+
// A declared task verifier owns this task's verification boundary. A
|
|
3151
|
+
// static-only task must not inherit unrelated root-level test commands.
|
|
3152
|
+
allowAdapter: !hasDeclaredFrontendVerification,
|
|
3153
|
+
});
|
|
3151
3154
|
const staticShellCommands = buildVerifyShellCommands({
|
|
3152
|
-
repoRoot: sources.repoRoot
|
|
3155
|
+
repoRoot: sources.repoRoot,
|
|
3156
|
+
commands: partitionedStaticVerifyCommands.static.commands,
|
|
3157
|
+
fallbackCommands: staticFallbackCommands,
|
|
3158
|
+
});
|
|
3159
|
+
const lintShellCommands = buildVerifyShellCommands({
|
|
3160
|
+
repoRoot: sources.repoRoot,
|
|
3161
|
+
commands: partitionedStaticVerifyCommands.lint.commands,
|
|
3162
|
+
fallbackCommands: [],
|
|
3163
|
+
});
|
|
3164
|
+
const behaviorShellCommands = buildVerifyShellCommands({
|
|
3165
|
+
repoRoot: sources.repoRoot,
|
|
3166
|
+
commands: behaviorVerifyCommands.commands,
|
|
3167
|
+
fallbackCommands: hasDeclaredFrontendVerification
|
|
3168
|
+
? []
|
|
3169
|
+
: behaviorFallbackCommands,
|
|
3153
3170
|
});
|
|
3171
|
+
const effectiveBehaviorFallbackCommands = hasDeclaredFrontendVerification
|
|
3172
|
+
? []
|
|
3173
|
+
: behaviorFallbackCommands;
|
|
3154
3174
|
const staticVerifyEvidence = buildVerifyEvidence({
|
|
3155
|
-
phase: "
|
|
3156
|
-
|
|
3157
|
-
|
|
3175
|
+
phase: "intermediate",
|
|
3176
|
+
quota: strategy.intermediateQuota ?? "full",
|
|
3177
|
+
commandSource: partitionedStaticVerifyCommands.static.commandSource,
|
|
3178
|
+
commands: partitionedStaticVerifyCommands.static.commands,
|
|
3179
|
+
fallbackCommands: staticFallbackCommands,
|
|
3180
|
+
commandTexts: staticShellCommands,
|
|
3181
|
+
commandTimeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
|
|
3182
|
+
preflight: sources.verificationPreflight,
|
|
3158
3183
|
});
|
|
3159
|
-
const
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3184
|
+
const lintVerifyEvidence = lintShellCommands.length > 0
|
|
3185
|
+
? buildVerifyEvidence({
|
|
3186
|
+
phase: "intermediate",
|
|
3187
|
+
quota: strategy.intermediateQuota ?? "full",
|
|
3188
|
+
commandSource: partitionedStaticVerifyCommands.lint.commandSource,
|
|
3189
|
+
commands: partitionedStaticVerifyCommands.lint.commands,
|
|
3190
|
+
fallbackCommands: [],
|
|
3191
|
+
commandTexts: lintShellCommands,
|
|
3192
|
+
commandTimeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
|
|
3193
|
+
preflight: sources.verificationPreflight,
|
|
3194
|
+
})
|
|
3195
|
+
: undefined;
|
|
3163
3196
|
const behaviorVerifyEvidence = buildVerifyEvidence({
|
|
3164
|
-
phase: "final",
|
|
3165
|
-
|
|
3197
|
+
phase: "final",
|
|
3198
|
+
quota: "full",
|
|
3199
|
+
commandSource: behaviorVerifyCommands.commandSource,
|
|
3200
|
+
commands: behaviorVerifyCommands.commands,
|
|
3201
|
+
fallbackCommands: effectiveBehaviorFallbackCommands,
|
|
3202
|
+
commandTexts: behaviorShellCommands,
|
|
3203
|
+
finalFullRequired: true,
|
|
3166
3204
|
commandTimeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
|
|
3205
|
+
preflight: sources.verificationPreflight,
|
|
3167
3206
|
});
|
|
3207
|
+
const mockVerifyTemplate = mockMode === "required" && hasMockVerifyCommands
|
|
3208
|
+
? buildFrontendMockVerifyNode(frontendSources, implementId, readOnlyPaths, forbiddenPaths)
|
|
3209
|
+
: undefined;
|
|
3210
|
+
const mockShellCommands = mockVerifyTemplate?.shell?.commands ?? [];
|
|
3211
|
+
const mockVerifyEvidence = mockVerifyTemplate?.shell?.verifyEvidence;
|
|
3168
3212
|
// Contract v2: the frozen command directory is the only command reference
|
|
3169
3213
|
// the plan may use. Modes are assigned here once from the generation-time
|
|
3170
3214
|
// lane split; downstream materialization resolves the same directory from
|
|
@@ -3172,23 +3216,28 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
3172
3216
|
const frontendVerifyDirectory = buildFrontendVerifyCommandDirectory({
|
|
3173
3217
|
staticLabels: staticVerifyEvidence.commandLabels,
|
|
3174
3218
|
behaviorLabels: behaviorVerifyEvidence.commandLabels,
|
|
3175
|
-
mockLabels: [],
|
|
3219
|
+
mockLabels: mockVerifyEvidence?.commandLabels ?? [],
|
|
3176
3220
|
staticCommandTexts: staticVerifyEvidence.commandTexts,
|
|
3177
3221
|
behaviorCommandTexts: behaviorVerifyEvidence.commandTexts,
|
|
3178
|
-
mockCommandTexts: [],
|
|
3222
|
+
mockCommandTexts: mockVerifyEvidence?.commandTexts ?? [],
|
|
3179
3223
|
});
|
|
3180
3224
|
const fixedVerificationContext = [
|
|
3181
3225
|
"## Fixed frontend verification entrypoints",
|
|
3182
|
-
"
|
|
3226
|
+
"These shell entrypoints are fixed at DAG generation and are the only commands the static and behavior shell nodes execute. A strategy or plan may add tests behind an existing entrypoint inside writeSet, but must not invent or replace commands or assume subtask_prompt executes a command.",
|
|
3183
3227
|
"Reference frozen commands ONLY by commandId (record_plan_verification_target entry.commandId). The runtime resolves mode and label; never invent a mode or type.",
|
|
3184
3228
|
...frontendVerifyDirectory.map((entry) => ` - ${entry.commandId} [${entry.mode}]: ${JSON.stringify(entry.label)}`),
|
|
3185
3229
|
`- Static command source: ${staticVerifyEvidence.commandSource}`,
|
|
3186
3230
|
`- Behavior command source: ${behaviorVerifyEvidence.commandSource}`,
|
|
3187
3231
|
].join("\n");
|
|
3188
3232
|
const advisories = [];
|
|
3233
|
+
if (!hasDeclaredFrontendVerification &&
|
|
3234
|
+
fallbackVerifyCommands.staticCommands.length === 0 &&
|
|
3235
|
+
fallbackVerifyCommands.behaviorCommands.length === 0) {
|
|
3236
|
+
advisories.push("未发现可用的前端验证命令:目标项目没有可读取的 package.json scripts,也未探测到本地 TypeScript,verify 节点将没有静态/行为命令可执行。请在 task.json --verify 或执行约束.md 的验证约束中显式声明命令(例如 node --check src/app.js),然后重新生成 DAG。");
|
|
3237
|
+
}
|
|
3189
3238
|
if (frontendSourceMentionsMock(frontendSources) &&
|
|
3190
3239
|
frontendMockStrategyMustBeNotNeeded(frontendSources)) {
|
|
3191
|
-
advisories.push("auto 模式已将 Mock 策略收窄为 not-needed:任务源提到接口/API/Mock 需求,但仓库无确认 Mock
|
|
3240
|
+
advisories.push("auto 模式已将 Mock 策略收窄为 not-needed:任务源提到接口/API/Mock 需求,但仓库无确认 Mock 能力或无确定性 Mock 验证命令。若项目规范要求 Mock,请声明 frontendMock.verifyCommands 或 policy:required 后重新生成 DAG。");
|
|
3192
3241
|
}
|
|
3193
3242
|
const openspecGate = await resolveFrontendOpenspecGateConfig(sources);
|
|
3194
3243
|
const requiresOpenspecClassification = openspecGate.openspecPolicy === "cited" &&
|
|
@@ -3300,7 +3349,7 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
3300
3349
|
"Consume the complete injected input scope and produce a concise frontend implementation contract as typed requirement facts from complete injected scopes.",
|
|
3301
3350
|
"Confirm each requirement by 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). record_requirement takes the canonical id and optional execution:{groupId,kind,summary}; the runtime commits the authoritative text and sourceFragmentIds from the frozen ledger. Never pass text/statement/sourceFragmentIds yourself — model rewrites and JSON-stringified fragment arrays are rejected.",
|
|
3302
3351
|
"Requirement semantics, source spans, dispositions, and fragment bindings are ledger/runtime-owned. If a canonical requirement is genuinely blocked, say so in the Markdown contract narrative and finalize with the matching disposition instead of trying to encode it in the requirement fact.",
|
|
3303
|
-
"
|
|
3352
|
+
"Register evidence expectations for each requirement across static, behavior, Mock, and real integration as required | optional | not-applicable; required must follow from user requirements, task risk, or project governance, never from model convenience. For UI-visible or interactive requirements, register a non-blocking frontend-test handoff intent.",
|
|
3304
3353
|
'Use record_evidence_expectation with {requirementId,evidence:{static,behavior,mock,"real-integration"}}; every lane is required | optional | not-applicable. Requirement text and provenance remain runtime-owned.',
|
|
3305
3354
|
'Before finalize_contract ready, call record_required_deliverables once with the complete {items:[{path,requirementId,sourceFragmentId}]} inventory, or {items:[]} when no file delivery is mandatory. Interpret the original source, including lists and tables: allowedPaths/only-allowed-to-modify is permission, not an obligation; do not promote prohibited files, examples or references into deliverables. Paths must occur exactly in a frozen source fragment bound to that canonical requirement. Correct the whole inventory before finalizing if needed.',
|
|
3306
3355
|
"Authoritative UI states: when the task source declares a UI-state table (state id / trigger / observable outcome), extract it VERBATIM through record_ui_state, one call per state, using the source's own state ids. The planner must bind these ids later — do not rename, merge, or invent states.",
|
|
@@ -3360,10 +3409,10 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
3360
3409
|
},
|
|
3361
3410
|
outputContract: "Typed decision patch only: map frozen requirements to implementation/verification targets and select the needed component, state, data/Mock, styling, and dependency decisions. Use only the record_* tools needed to express those decisions, then call finalize_plan; correct rejected facts until one successful terminal. Contract owns requirement semantics; Scout owns repository discovery; deterministic runtime owns schema, protected fields, path containment, and command validation. No Markdown narrative or file writes.",
|
|
3362
3411
|
subtask_prompt: [
|
|
3363
|
-
"
|
|
3412
|
+
"Plan only the delta between the frozen frontend-contract-pi facts and frontend-scout-pi target surface. Do not reinterpret the task, repeat requirements, search the repository, or choose implementation order.",
|
|
3364
3413
|
"Record only: requirement-to-file/verification coverage; component/styling choices; applicable UI state and interaction behavior; data/Mock strategy; and a dependency policy or genuine evidence gap. Reuse Scout paths. If scope is missing, record a blocking gap instead of inventing a path.",
|
|
3365
3414
|
"Use the typed tool schemas as the field contract. Runtime owns schemaVersion, sourceBinding, riskLevel, targets.files, mockApi.productionDefaultOff, aliases, command allowlisting, path containment, and final validation; do not restate those rules or emit a full JSON contract.",
|
|
3366
|
-
`Cover each frozen requirement ID exactly once: ${requirementIds.join(", ") || "(none)"}. Bind every verification target to a frozen commandId from the directory above plus a Scout-confirmed file.
|
|
3415
|
+
`Cover each frozen requirement ID exactly once: ${requirementIds.join(", ") || "(none)"}. Bind every verification target to a frozen commandId from the directory above plus a Scout-confirmed file. Behavior commands prove observable behavior: one target may cover multiple related requirementIds when one test behavior proves them together; do not mechanically create one target per requirement. A behavior target id is the stable machine trace token and its file must be a test file. Static commands are project-wide checks traced by file and command only.`,
|
|
3367
3416
|
"UX vocabulary protocol: record_state_registry FIRST with the full global vocabulary — one stable kebab-case behavior-domain name per UI state/interaction (e.g. planner-task-edit, focus-queue-move), never one name per AC number and never a rename of an already-recorded concept. Details consume complete execution-group scopes and reuse the same global names across scopes. Constraints/exclusions must not manufacture UI. Then record_state_flow entries whose names all come from that registry; uiState names must use the contract's declared authoritative ids (declaredUiStates in the plan input) when present. Retry attempts see committedUx in this input — reuse those exact names. Components: one choice may cover many state/interaction ids via covers; reuse-existing requires evidencePath naming an existing repo file (greenfield must be decision=new).",
|
|
3368
3417
|
...(requiresOpenspecClassification ? ["When a component choice uses an OpenSpec selection, cite that selection; otherwise do not classify unrelated candidates."] : []),
|
|
3369
3418
|
"Call finalize_plan; correct rejected facts until one successful terminal after the necessary typed facts. Return no Markdown narrative.",
|
|
@@ -3408,7 +3457,7 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
3408
3457
|
"request-adapter",
|
|
3409
3458
|
"not-needed",
|
|
3410
3459
|
],
|
|
3411
|
-
mockCommandLabels: [],
|
|
3460
|
+
mockCommandLabels: mockVerifyEvidence?.commandLabels ?? [],
|
|
3412
3461
|
artifactName: "frontend-implementation-contract.json",
|
|
3413
3462
|
outputDir: "contracts",
|
|
3414
3463
|
requireSourceFreshness: true,
|
|
@@ -3452,15 +3501,15 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
3452
3501
|
skills: FRONTEND_DESIGN_REVIEW_SKILLS,
|
|
3453
3502
|
outputContract: "Authoritative typed design terminal via approve_design / request_design_changes tools. No JSON verdict; the committed typed design fact is the only authority. No file writes.",
|
|
3454
3503
|
subtask_prompt: [
|
|
3455
|
-
"Submit findings individually with record_design_finding and stable IDs; terminal tools aggregate saved findings. Correct rejected calls, stop after a successful terminal. Check execution groups against every member source outcome, including permission, threshold and failure-path differences;
|
|
3504
|
+
"Submit findings individually with record_design_finding and stable IDs; terminal tools aggregate saved findings. Correct rejected calls, stop after a successful terminal. Check execution groups against every member source outcome, including permission, threshold and failure-path differences; shared verification is valid only when it proves each independent AC.",
|
|
3456
3505
|
"Audit the frontend plan before implementation. frontend-plan-pi is emitted to you as canonical full-contract JSON after the runtime applied and validated the planner's editable patch against its protected skeleton; there is no separate plan prose.",
|
|
3457
3506
|
"Your authoritative terminal verdict is exactly one committed typed tool call: approve_design or request_design_changes. Call exactly one of them; after calling one, do not call the other.",
|
|
3458
3507
|
"request_design_changes must carry a typed issueCategory, at least one evidenceRef, and non-empty findings.",
|
|
3459
3508
|
"Your verdict is consumed as deterministic data input by frontend-writer-admission-shell. approve_design permits admission; request_design_changes blocks writer admission until a recovery plan incorporates every Critical/Important finding.",
|
|
3460
|
-
"Request design changes
|
|
3461
|
-
"
|
|
3509
|
+
"Request design changes when the Mock strategy is 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 requires applicable real/no-remote behavior evidence unless auto mode explicitly skipped Mock because no project Mock capability exists, in which case the plan must preserve the real request path and record the Real Integration Gap.",
|
|
3510
|
+
"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.",
|
|
3462
3511
|
"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. 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, and you must not reject it merely for matching an interaction or component identifier.",
|
|
3463
|
-
"
|
|
3512
|
+
"You must NOT make authoritative assertions about the execution result of frozen verification commands: command results are deterministically established by frontend-verify-shell. Record a verification-feasibility concern only as a non-blocking finding (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.",
|
|
3464
3513
|
"Read-only: do not modify repository files.",
|
|
3465
3514
|
"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.",
|
|
3466
3515
|
"Canonical contract reading: frontend-design-policy-shell prints absolute paths for Contract, Contract index, and the non-blocking Capacity diagnostic. Read the capacity diagnostic first. When it recommends full-contract, read the exact Contract path. When it recommends indexed-sections, read the Contract index and its hash-bound section files instead of opening the full contract. Never resolve a bare contracts/... path against the repository root or hunt for substitutes. Implementation target files inside the writeSet are created later by the implement node: do not read them and do not treat their absence as a design defect.",
|
|
@@ -3486,7 +3535,11 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
3486
3535
|
frontendWriterAdmission: {
|
|
3487
3536
|
schemaVersion: 1,
|
|
3488
3537
|
designReviewFromNodeId: "frontend-design-review-pi",
|
|
3489
|
-
frozenCommandLabels:
|
|
3538
|
+
frozenCommandLabels: [
|
|
3539
|
+
...staticVerifyEvidence.commandLabels,
|
|
3540
|
+
...behaviorVerifyEvidence.commandLabels,
|
|
3541
|
+
...(mockVerifyEvidence?.commandLabels ?? []),
|
|
3542
|
+
],
|
|
3490
3543
|
allowedMockStrategies: taskConfig.frontendMock?.policy === "disabled" ||
|
|
3491
3544
|
frontendMockStrategyMustBeNotNeeded(frontendSources)
|
|
3492
3545
|
? ["not-needed"]
|
|
@@ -3502,6 +3555,12 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
3502
3555
|
"request-adapter",
|
|
3503
3556
|
"not-needed",
|
|
3504
3557
|
],
|
|
3558
|
+
...(lintShellCommands.length > 0 && lintVerifyEvidence
|
|
3559
|
+
? {
|
|
3560
|
+
lintCommands: lintShellCommands,
|
|
3561
|
+
lintEvidence: lintVerifyEvidence,
|
|
3562
|
+
}
|
|
3563
|
+
: {}),
|
|
3505
3564
|
},
|
|
3506
3565
|
cwd: ".",
|
|
3507
3566
|
timeoutMs: 60000,
|
|
@@ -3517,16 +3576,18 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
3517
3576
|
forbiddenPaths,
|
|
3518
3577
|
writerOutcomePolicyType: "frontend-facts-v1",
|
|
3519
3578
|
}),
|
|
3520
|
-
outputContract: "The
|
|
3579
|
+
outputContract: "The implementation status is derived by the executor from mechanical facts (write-tool events, run delta, write guard, requirement coverage, focused-check), not from any IMPLEMENTATION_OUTCOME first line. Deliver a Markdown summary with Contract Ref (path/schema/hash), Changed Files, Requirements Implemented, UI States, Tests Changed, Verification Attempts, Deviations, and Residual Risks. Follow fixed stages: contract confirm → tests → component/state → API/Mock → focused checks → diff cleanup.",
|
|
3521
3580
|
subtask_prompt: [
|
|
3522
3581
|
"Implement against the validated run-owned Frontend Implementation Contract materialized by frontend-design-policy-shell (path/schema/hash) and authorized by frontend-writer-admission-shell. Do not rebuild the contract from Markdown alone.",
|
|
3523
3582
|
"WRITER TOOL PROTOCOL (hard): the response text is not delivery. Never paste source code, test code, or full file contents into chat. After reading the canonical contract, make the first implementation change with the structured write/edit tool (one file per call); continue writing through those tools until the writeSet is complete. If a write/edit tool is unavailable, stop and report the blocked capability instead of drafting code in the response.",
|
|
3524
3583
|
"The canonical contract already contains the approved requirement, target-file, UI-state, verification, design, and Mock/API decisions. Do not re-open task sources, OpenSpec, AI workspace, plan/revision, or design-review prose, and do not repeat broad repository research. Inspect only contract target files and directly related local code needed to implement them.",
|
|
3525
|
-
"
|
|
3526
|
-
"Map every requirement id, expectedOutcome, interaction and applicable UI state to concrete
|
|
3584
|
+
"Execute in fixed stages and report each in the delivery summary: (1) Contract confirm, (2) Tests sync, (3) Component/UI state implementation, (4) API/Mock wiring per contract.mockApi, (5) Focused checks behind frozen entrypoints only, (6) Diff cleanup.",
|
|
3585
|
+
"Map every requirement id, expectedOutcome, interaction trigger/expectedBehavior, and applicable UI state from the contract to concrete files. Do not invent shell verification commands; only frozen static/behavior entrypoints will run.",
|
|
3586
|
+
"For every behavior verification target, treat target.id as a stable trace token and include that exact token in a real describe/it/test literal title (for example, it('[VT-DASHBOARD-SHELL] renders the dashboard', ...)). One test title may carry multiple target ids when it proves multiple grouped behaviors; comments and ordinary strings do not count as trace evidence.",
|
|
3587
|
+
"Before reporting Tests Changed as done, self-audit with the executor's rule: collect ONLY the string literals passed directly to describe(/it(/test( calls in each test file and confirm every behavior target id for that file appears inside one of those literals. A token in a comment, a variable, or a non-title string does not satisfy the trace check; if any id is missing from the literal titles, edit the title strings before finishing.",
|
|
3527
3588
|
"Begin implementation after the contract and its target files are confirmed. Do not spend the turn collecting optional context. If the canonical contract lacks behavior needed to edit safely, stop and state the blocking reason in the summary instead of reopening broad discovery.",
|
|
3528
3589
|
"Your implementation status is derived by the executor from mechanical facts (persisted write-tool events, run delta, write guard, requirement coverage, focused-check failures), never from any IMPLEMENTATION_OUTCOME first line. Do not emit an IMPLEMENTATION_OUTCOME first line.",
|
|
3529
|
-
"The node
|
|
3590
|
+
"The node runs a bounded micro-loop: after each write attempt the executor re-runs frozen focused checks and records a per-round diff checkpoint; the write guard stays active every round. Only repair local issues attributable to the current diff (syntax/type/import/format/unit-assert/obvious omission). Never change requirements, design, writeSet, or verification strictness inside the loop.",
|
|
3530
3591
|
"Implement only the approved Mock strategy carried by the validated contract. Preserve the real request path as the default, require explicit test/dev activation, and never comment out or replace the real request with inline data.",
|
|
3531
3592
|
"frontend-design-policy-shell materialized and validated the canonical contract; frontend-writer-admission-shell authorized the writeSet. Stay within writeSet and preserve unrelated files.",
|
|
3532
3593
|
"For native, browser-intercept, or request-adapter, implement contract-aligned fixtures/states and a dev/test-only activation boundary in this same writer. For not-needed, do not add Mock files or a framework and state the positive reason.",
|
|
@@ -3534,7 +3595,7 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
3534
3595
|
"Edit existing files with the structured edit/write tools. NEVER rewrite Markdown (or any file with quoting/backticks/indentation-sensitive content) via bash sed/awk/echo redirection: escaping mistakes silently corrupt the file and self-repair loops burn the run.",
|
|
3535
3596
|
...(implementPaths.docIndexCompanions.length > 0
|
|
3536
3597
|
? [
|
|
3537
|
-
`Doc index sync is MANDATORY: ${implementPaths.docIndexCompanions.join(", ")} are catalog index files for this writeSet. When you add, rename, or remove any indexed file, you MUST also update ${implementPaths.docIndexCompanions.join(" and ")} in the same run (append or fix the matching index entry following the existing line style).
|
|
3598
|
+
`Doc index sync is MANDATORY: ${implementPaths.docIndexCompanions.join(", ")} are catalog index files for this writeSet. When you add, rename, or remove any indexed file, you MUST also update ${implementPaths.docIndexCompanions.join(" and ")} in the same run (append or fix the matching index entry following the existing line style). Verification runs check-doc-index and fails the run when a new file is missing from the index.`,
|
|
3538
3599
|
]
|
|
3539
3600
|
: []),
|
|
3540
3601
|
writerDeliveryContract(taskConfig),
|
|
@@ -3551,7 +3612,7 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
3551
3612
|
writePolicy: "read-only",
|
|
3552
3613
|
allowedPaths: readOnlyPaths,
|
|
3553
3614
|
forbiddenPaths,
|
|
3554
|
-
outputContract: "Run
|
|
3615
|
+
outputContract: "Run frozen Mock/static/behavior commands and materialize the verification trace. Any failure is terminal: no same-run repair branch, failure ownership facts are materialized for recovery.",
|
|
3555
3616
|
subtask_prompt: "Execute the frontend verification bundle. Preserve per-command evidence; a failure fails this node (terminal) and routes to recovery.",
|
|
3556
3617
|
shell: {
|
|
3557
3618
|
commands: [],
|
|
@@ -3561,6 +3622,8 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
3561
3622
|
lintCommands: lintShellCommands,
|
|
3562
3623
|
staticCommands: staticShellCommands,
|
|
3563
3624
|
behaviorCommands: behaviorShellCommands,
|
|
3625
|
+
mockEvidence: mockVerifyEvidence,
|
|
3626
|
+
lintEvidence: lintVerifyEvidence,
|
|
3564
3627
|
staticEvidence: staticVerifyEvidence,
|
|
3565
3628
|
behaviorEvidence: behaviorVerifyEvidence,
|
|
3566
3629
|
lintBaselineNodeId: lintShellCommands.length > 0
|
|
@@ -3604,20 +3667,20 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
3604
3667
|
skills: FRONTEND_REVIEW_SKILLS,
|
|
3605
3668
|
outputContract: 'Authoritative typed review terminal via approve_review / request_review_changes tools. No JSON verdict is required in the response text; the typed terminal fact is the only authority. No file writes.',
|
|
3606
3669
|
subtask_prompt: [
|
|
3607
|
-
"Submit findings individually with record_review_finding and stable IDs; terminal tools aggregate saved findings. Correct rejected calls, stop after a successful terminal. Check execution groups against every member source outcome, including permission, threshold and failure-path differences;
|
|
3608
|
-
"
|
|
3670
|
+
"Submit findings individually with record_review_finding and stable IDs; terminal tools aggregate saved findings. Correct rejected calls, stop after a successful terminal. Check execution groups against every member source outcome, including permission, threshold and failure-path differences; shared verification is valid only when it proves each independent AC.",
|
|
3671
|
+
"Review the frontend implementation and verification evidence.",
|
|
3609
3672
|
"Your authoritative terminal verdict is exactly one committed typed tool call: approve_review or request_review_changes. Call it once and do not call the other afterwards.",
|
|
3610
3673
|
"approve_review means the implementation passes; it must not carry Critical or Important findings. request_review_changes must carry a typed issueCategory, at least one evidenceRef, and non-empty findings.",
|
|
3611
3674
|
"Do NOT emit an equivalent JSON verdict in the response text: the committed typed terminal fact is the only authority and no branch or gate reads response-text JSON verdicts.",
|
|
3612
3675
|
"Read contracts/frontend-review-context.json from frontend-review-context-shell. It binds a hash-verified canonical contract reference, a field-to-section index, frontend lint assessment when configured, the effective verification trace, and the run-owned actual diff. Read contractRef.capacityDiagnosticPath first: use contractRef.path only when full-contract is recommended; otherwise read only the hash-bound contractRef.sections needed for the changed surface and verification claims. Then read diff.reviewSummaryPath. The full artifacts/diff_patch.patch is retained only as audit evidence: do NOT read it in full. For semantic review, read only the named per-file diff fragment in the summary/index (in part order when needed) and then the current source file when necessary. Do not claim actual diff is missing when those artifacts exist; do not invent a diff from the implementation summary alone. Trace proves command/file/stable-target-id binding only—not semantic correctness.",
|
|
3613
|
-
"
|
|
3676
|
+
"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.",
|
|
3614
3677
|
"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).",
|
|
3615
3678
|
"The contract referenced and hash-bound by 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 indexed contract sections, its bound diff, and diff-referenced files when semantic review requires source code.",
|
|
3616
3679
|
"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.",
|
|
3617
|
-
"
|
|
3680
|
+
"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.",
|
|
3618
3681
|
"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.",
|
|
3619
3682
|
"Distinguish Mock-backed evidence from real API integration evidence and preserve the Real Integration Gap when the backend was not exercised.",
|
|
3620
|
-
"Review
|
|
3683
|
+
"Review implementation quality, behavior/state coverage, verification evidence, and maintainability. Read-only: do not modify files.",
|
|
3621
3684
|
].join("\n\n"),
|
|
3622
3685
|
},
|
|
3623
3686
|
{
|
|
@@ -205,15 +205,15 @@ Runtime 变更另需 `npm run typecheck` 及对应 targeted Vitest(见 exec pl
|
|
|
205
205
|
|
|
206
206
|
| 面 | 路径 / 入口 | 边界 |
|
|
207
207
|
| --- | --- | --- |
|
|
208
|
-
| OpenCode transient-retry | `.opencode/plugins/loop-agent-transient-retry.js`(init surface `generated`) |
|
|
208
|
+
| OpenCode transient-retry | `.opencode/plugins/loop-agent-transient-retry.js`(init surface `generated`) | 仅瞬态 UnknownError resume;busy/retry 门控 + backoff;overflow/401/配额/取消/业务标 permanent(`plugin-ignore-permanent-error`),**不** compact |
|
|
209
209
|
| OpenCode overflow-compact | `.opencode/plugins/loop-agent-context-overflow-compact.js`(init surface `generated`) | 仅 context overflow → `session.compact`(无则 `summarize`)+ 有限 `promptAsync` 续接;`MAX_OVERFLOW_RECOVERIES ≤ 2`;session 锁;非 overflow 不动作 |
|
|
210
210
|
| Pi project extension | `.pi/extensions/loop-agent-context-overflow.js`(init surface `generated`) | `message_end` 上将 overflow `errorMessage` 幂等改写为 `context_length_exceeded:` 前缀,触发 Pi 原生 compact/retry;分类器与控制器共享 P1–P10 / N1–N5 语料(含嵌套 JSON);**不**写 `~/.pi` settings / compaction / 模型窗口,**不**投影 sift native |
|
|
211
|
-
| Console / DAG 运行时 | npm 内置 inline extension + 可选 `@agent-context/sift` |
|
|
211
|
+
| Console / DAG 运行时 | npm 内置 inline extension + 可选 `@agent-context/sift` | 默认开启 L0/L1/L2;`HARNESS_CONTEXT_PRESSURE_MODE=off` 关 L0/L2;`HARNESS_CONSOLE_SIFT` / `HARNESS_DAG_SIFT` 的 `0\|false\|off` 关 L1。DAG 不做 streaming 主动 compact。不进入目标项目 init 文件 |
|
|
212
212
|
| Pi 用户配置 | `~/.pi/agent/settings.json` | **不**进入项目 `.harness/init-surface.json` hash;仅 `--client-recovery=user` 可字段级补缺并原子写;`auto`/`project`/`off` 与 `check-update` 默认零写 home;读取时只有 `ENOENT` 视为缺文件,其他 I/O 错误 fail closed |
|
|
213
213
|
| CLI mode | `--client-recovery=auto\|project\|user\|off`(默认 `auto`) | `auto`/`project` 安装上述三份项目产物;`user` = 三产物 + 显式 Pi retry 合并;`off` 全跳过 |
|
|
214
214
|
| Ownership | recorded sha256 + apply-safe | 三份 generated 缺失可补、与 recorded hash 一致可升级;用户改过 → model merge / human decision,禁止静默覆盖 |
|
|
215
215
|
|
|
216
|
-
实现落点:`src/
|
|
216
|
+
实现落点:`src/commands/client-recovery.ts`(共享 `isContextOverflow`、生成器与 install)+ `src/commands/init.ts`(surface 编排与 `written` 投影)。
|
|
217
217
|
|
|
218
218
|
## 演进里程碑
|
|
219
219
|
|