@tea-agent/loop-agent 0.42.0-next.9 → 0.42.1
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 +111 -45
- package/dist/application/dag/run-dag.js +8 -2
- package/dist/application/evaluation/budget.js +19 -1
- package/dist/application/task-lifecycle/advance.js +26 -8
- package/dist/application/task-lifecycle/observe.js +43 -29
- package/dist/application/task-lifecycle/plan-transitions.js +5 -4
- package/dist/build-stamp.json +3 -3
- package/dist/cli/program.js +1 -1
- package/dist/commands/dag-rerun-task.js +2 -0
- package/dist/commands/task-source-prepare.js +3 -1
- package/dist/executors/dag-pi-executor.js +2584 -600
- package/dist/executors/pi-executor.js +22 -1
- package/dist/executors/pi-extension-resolver.js +14 -2
- package/dist/executors/pi-sdk-executor.js +140 -39
- package/dist/executors/shell-executor.js +135 -55
- package/dist/shared/dag-failure-category.js +6 -0
- package/dist/shared/frontend-execution-policy.js +23 -0
- package/dist/task/config-types.js +4 -0
- package/dist/task/contract/apply.js +36 -2
- package/dist/task/source-prepare/ledger-reconciliation.js +2 -2
- package/dist/task/source-prepare/ledger-review.js +6 -9
- package/dist/task/source-prepare/parse-intent.js +7 -0
- package/dist/task/source-prepare/semantic-intake.js +16 -26
- package/dist/task/source-prepare/source-fidelity-pi.js +28 -9
- package/dist/task/source-references.js +48 -23
- package/dist/worker/console/chat/assistant-content.js +23 -2
- package/dist/worker/console/chat/browser-policy.js +143 -0
- package/dist/worker/console/chat/browser-routes.js +148 -0
- package/dist/worker/console/chat/chat-event-store.js +4 -2
- package/dist/worker/console/chat/explore-tools.js +13 -0
- package/dist/worker/console/chat/pi-runtime.js +173 -94
- package/dist/worker/console/chat/resource-loader.js +4 -1
- package/dist/worker/console/chat/routes.js +208 -66
- package/dist/worker/console/chat/scm-routes.js +217 -0
- package/dist/worker/console/chat/scm-service.js +283 -0
- package/dist/worker/console/chat/scm-tools.js +111 -0
- package/dist/worker/console/chat/sdd-data-alignment.js +78 -11
- package/dist/worker/console/chat/session-catalog.js +32 -0
- package/dist/worker/console/chat/session-mode-view.js +48 -0
- package/dist/worker/console/chat/session-mode.js +218 -0
- package/dist/worker/console/chat/session-store.js +27 -7
- package/dist/worker/console/chat/shortcuts.js +6 -0
- package/dist/worker/console/chat/subagents/agent-tool.js +62 -0
- package/dist/worker/console/chat/subagents/explore-agent.js +169 -0
- package/dist/worker/console/chat/subagents/index.js +5 -0
- package/dist/worker/console/chat/subagents/orchestrator.js +273 -0
- package/dist/worker/console/chat/subagents/tool-policy.js +53 -0
- package/dist/worker/console/chat/subagents/types.js +13 -0
- package/dist/worker/console/chat/terminal-routes.js +216 -0
- package/dist/worker/console/chat/terminal-sessions.js +284 -0
- package/dist/worker/console/chat/terminal-tools.js +199 -0
- package/dist/worker/console/chat/tool-preview.js +21 -0
- package/dist/worker/console/chat/tools.js +13 -1
- package/dist/worker/console/chat/turn-process.js +1 -0
- package/dist/worker/console/interview/tools.js +1 -0
- package/dist/worker/console/server.js +2 -28
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C6n9_m0P.js → abnfDiagram-N423BO3Z-C9eI7nEo.js} +1 -1
- package/dist/worker/console/static/assets/{arc-DQh-IfZ1.js → arc-VJWsxWhB.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-54NnrwUC.js → architectureDiagram-T3A2C74G-BGcyiMSo.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-pivRALGK.js → blockDiagram-VBNYF7ZC-BScnFwyi.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BR7OV2NJ.js → c4Diagram-5PPSVZJV-Bl9_BsLi.js} +1 -1
- package/dist/worker/console/static/assets/channel-B6sQYuOE.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-B1Aq6BcK.js → chunk-2GRJ4B5K-F1uKxiUt.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-CbdU0rjo.js → chunk-2Q5K7J3B-rSRMynVu.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-Dqi2GJeD.js → chunk-5RXB4S5H-DAsJm1WD.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BYn1Hu4R.js → chunk-5VM5RSS4-DQIdVI0a.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DRLjDL0k.js → chunk-6Q2QTUOP-4A_8rJ-Q.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CY9Xx2jV.js → chunk-GF5L2VYU-BHnT-vjh.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BNCZs7_z.js → chunk-JWPE2WC7-Rq7QKBkn.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DZ8AStLL.js → chunk-KBJHAD2P-BXh-AI2u.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DsxjYrzz.js → chunk-RYQCIY6F-BblcLy8m.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-Ddx1KC1l.js → chunk-XXDRQBXY-zHZmVnB6.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Z6s5QoeI.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Z6s5QoeI.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-W9TveCnK.js → cose-bilkent-JH36ORCC-Cmwz0Wi4.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-l9j_ztZH.js → cynefin-VYW2F7L2-Dq76MHQU.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-BMJMKi4G.js → cynefinDiagram-MW4NZA55-zPahV1fM.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-Bb4mG9pH.js → dagre-VZM6K2ZE-DQJ3xPc_.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-BMgL_Qv0.js → diagram-7IWD3JNH-BwX7u6fv.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-Bvo2T4OQ.js → diagram-B4RE2ZJO-CodnDCco.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-_5kWRN9c.js → diagram-LBJQPF4R-gAjtWkxG.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DqdMrltM.js → diagram-Q27KOJAE-CGCqz0cS.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-CDDYsNkp.js → diagram-UB23O5K3-RHwYEywd.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-c4nfnZUV.js → ebnfDiagram-BXEA7PRR-B6seb_p_.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DnkUcNMs.js → erDiagram-JOGREHBK-CmwCKYe8.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-Dz0KGLZE.js → flowDiagram-UKHOOZJN-BfNsaZ0V.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-Cj1t1uka.js → ganttDiagram-PKOTCBZU-zmYgi_Z3.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-tp2FrBHd.js → gitGraphDiagram-DS77QQ5N-BDEzzSKF.js} +1 -1
- package/dist/worker/console/static/assets/index-DgenUAfc.js +468 -0
- package/dist/worker/console/static/assets/index-aF4u-Y__.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DQwJS8DH.js → infoDiagram-6WML65LV-DeSz8l42.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-eimCQWD4.js → ishikawaDiagram-WSZJBQD7-B1LV29aH.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-Dd9Gbwgv.js → journeyDiagram-NVQOT4AX-CCw4W2oU.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Qve3cyft.js → kanban-definition-27J2QSJJ-BozaMoRJ.js} +1 -1
- package/dist/worker/console/static/assets/{linear-UXKSe36Z.js → linear-G1JR40SX.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-CWhj4JXN.js → mermaid.core-CCgPF0oA.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-yTcwf4SJ.js → mindmap-definition-FAOFIHXS-CEqnCHnD.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-_7qToaZP.js → pegDiagram-VL7TDLO6-DpmQPKuI.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CXmrKmDm.js → pieDiagram-7S7Q4E2Y-BP501AxR.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-hMmrJyaS.js → quadrantDiagram-CIZ2JOQS-D1dBhl_A.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BXw8tCBe.js → railroadDiagram-AXF67PYL-DgZDS5pY.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BmjgnLZl.js → requirementDiagram-LRYGKXZP-BzB3pyIq.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Bj77SPpN.js → sankeyDiagram-W5VNT64P-B9ZprFFP.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-B2FDVysL.js → sequenceDiagram-SI44F4Z6-CC2-_XrA.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-UChNY9ZZ.js → sizeCapture-X5ZJPWSS-UVtAUqgB.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-wbAEkbd7.js → stateDiagram-OKZ733FA-CWcS5JYG.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BooX8u1Q.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DCEkU8HR.js → swimlanes-SLNWSIFB-C4p-dn06.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-D64_eqqE.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-BGp7H06U.js → timeline-definition-Z64GVDOM-Dnz52OHX.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DsCIQuWR.js → vennDiagram-T6HMQDX7-CzTE0Nfu.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-cRZLP4Xe.js → wardleyDiagram-T6FBY63Y-NAR9cjQQ.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CP0xLR9Y.js → xychartDiagram-ELKLHX3M-DVMk8413.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/chat-view-types.js +1 -1
- package/dist/worker/console/static-src/operator-chat/chat-link.js +94 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +142 -7
- package/dist/worker/console/static-src/operator-chat/pending-user-message.js +44 -0
- package/dist/worker/console/static-src/operator-chat/process-label.js +41 -0
- package/dist/worker/console/static-src/operator-chat/tools-catalog.js +21 -2
- package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +13 -0
- package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +2 -0
- package/dist/worker/console/static-src/operator-chat/use-searchable-hidden.js +51 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +33 -18
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +48 -16
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +170 -7
- package/dist/worker/console/static-src/operator-chat/useComposer.js +13 -7
- package/dist/worker/console/static-src/operator-chat/user-turn-anchor-equality.js +21 -0
- package/dist/worker/console/static-src/shell/workspace-route.js +4 -0
- package/dist/worker/console/workspace-context.js +15 -1
- package/dist/worker/observe/node-transparency.js +81 -72
- package/dist/worker/observe/routes.js +20 -1
- package/dist/worker/observe/static/constants.js +22 -22
- package/dist/worker/observe/static/dag-context-reason-labels.js +19 -0
- package/dist/worker/observe/static/dag-history-labels.js +1 -0
- package/dist/worker/observe/static/dag-inspector-humanize.d.ts +16 -0
- package/dist/worker/observe/static/dag-inspector-humanize.js +342 -0
- package/dist/worker/observe/static/dag-node-purpose.js +10 -10
- package/dist/worker/observe/static/dom.js +20 -1
- package/dist/worker/observe/static/format-pool.d.ts +2 -0
- package/dist/worker/observe/static/format-pool.js +6 -0
- package/dist/worker/observe/static/format.js +7 -0
- package/dist/worker/observe/static/index.html +4 -4
- package/dist/worker/observe/static/inspect-workspace.js +34 -7
- package/dist/worker/observe/static/inspector-submission.js +32 -0
- package/dist/worker/observe/static/kpi.js +1 -0
- package/dist/worker/observe/static/prompt-restart-candidates.js +4 -2
- package/dist/worker/observe/static/relations.js +7 -5
- package/dist/worker/observe/static/router.js +13 -0
- package/dist/worker/observe/static/run-processing.js +2 -0
- package/dist/worker/observe/static/shell-chrome.js +36 -3
- package/dist/worker/observe/static/state.js +35 -2
- package/dist/worker/observe/static/styles.css +431 -39
- package/dist/worker/observe/static/task-failure-labels.d.ts +4 -0
- package/dist/worker/observe/static/task-failure-labels.js +67 -0
- package/dist/worker/observe/static/task-history.js +12 -0
- package/dist/worker/observe/static/views/batch.js +6 -13
- package/dist/worker/observe/static/views/dag-graph.js +51 -4
- package/dist/worker/observe/static/views/dag-inspector.js +1069 -426
- package/dist/worker/observe/static/views/dag-trajectory.js +3 -0
- package/dist/worker/observe/static/views/dag.d.ts +6 -0
- package/dist/worker/observe/static/views/dag.js +64 -22
- package/dist/worker/observe/static/views/dags.js +2 -0
- package/dist/worker/observe/static/views/dashboard.js +21 -12
- package/dist/worker/observe/static/views/failures.js +21 -11
- package/dist/worker/observe/static/views/feature.js +11 -29
- package/dist/worker/observe/static/views/pool.js +37 -28
- package/dist/worker/observe/static/views/run.js +48 -5
- package/dist/worker/observe/static/views/session-timeline.js +194 -243
- package/dist/worker/observe/static/views/task.js +81 -62
- package/dist/workflows/dag/backend-test-plan-protocol.js +104 -0
- package/dist/workflows/dag/budget-enforcement.js +53 -3
- package/dist/workflows/dag/dag-retry-schema.js +3 -0
- package/dist/workflows/dag/frontend-capacity.js +9 -0
- package/dist/workflows/dag/frontend-contract-facts.js +130 -0
- package/dist/workflows/dag/frontend-design-policy.js +103 -17
- package/dist/workflows/dag/frontend-durable-tools.js +193 -0
- package/dist/workflows/dag/frontend-execution-groups.js +24 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +423 -41
- package/dist/workflows/dag/frontend-input-projection.js +76 -0
- package/dist/workflows/dag/frontend-plan-completeness.js +186 -0
- package/dist/workflows/dag/frontend-plan-recovery-policy.js +18 -0
- package/dist/workflows/dag/frontend-plan-render.js +21 -3
- package/dist/workflows/dag/frontend-prewrite-gate.js +1 -1
- package/dist/workflows/dag/frontend-recovery-controller.js +32 -8
- package/dist/workflows/dag/frontend-recovery-lineage.js +13 -0
- package/dist/workflows/dag/frontend-recovery-plan.js +4 -1
- package/dist/workflows/dag/frontend-recovery-run.js +146 -16
- package/dist/workflows/dag/frontend-review-scopes.js +139 -0
- package/dist/workflows/dag/frontend-risk.js +2 -0
- package/dist/workflows/dag/frontend-session-budget.js +249 -0
- package/dist/workflows/dag/frontend-shadow-dual-write.js +37 -3
- package/dist/workflows/dag/frontend-shape-facts.js +12 -2
- package/dist/workflows/dag/frontend-shape.js +46 -7
- package/dist/workflows/dag/frontend-test-execution-evidence.js +49 -0
- package/dist/workflows/dag/frontend-typed-event-store.js +14 -0
- package/dist/workflows/dag/frontend-verification-trace.js +38 -4
- package/dist/workflows/dag/frontend-writer-admission.js +2 -2
- package/dist/workflows/dag/init-hybrid.js +70 -35
- package/dist/workflows/dag/node-execution.js +225 -226
- package/dist/workflows/dag/prompt.js +4 -0
- package/dist/workflows/dag/recovery-lease.js +106 -16
- package/dist/workflows/dag/rerun-feedback.js +1 -0
- package/dist/workflows/dag/rerun-plan.js +96 -4
- package/dist/workflows/dag/rerun-run.js +28 -6
- package/dist/workflows/dag/rerun-task.js +221 -18
- package/dist/workflows/dag/retry-policy.js +27 -10
- package/dist/workflows/dag/runner-exit-diagnostics.js +125 -0
- package/dist/workflows/dag/runner.js +246 -19
- package/dist/workflows/dag/structured-output-repair.js +4 -1
- package/dist/workflows/dag/types.js +10 -3
- package/dist/workflows/dag/validate.js +10 -8
- package/dist/workflows/dag/workspace-checkpoint.js +66 -0
- package/docs/operations/README.md +2 -0
- package/docs/templates/agent-dag.schema.json +2 -2
- package/docs/templates/backend-test-dag.json +6 -4
- package/docs/templates/frontend-design-contract.md +4 -4
- package/docs/templates/frontend-implementation-contract.schema.json +68 -4
- package/docs/templates/frontend-implementation-dag.json +5 -5
- package/harness.json +1 -1
- package/package.json +8 -6
- package/skills/frontend-contract/SKILL.md +2 -1
- package/skills/frontend-contract/references/contract-protocol.md +19 -3
- package/skills/frontend-design-review/SKILL.md +12 -11
- package/skills/frontend-plan/SKILL.md +22 -2
- package/skills/frontend-plan/references/decision-contract.md +114 -5
- package/skills/frontend-plan/references/design-decisions.md +32 -0
- package/skills/frontend-review/SKILL.md +10 -11
- package/skills/frontend-scout/references/scout-evidence.md +4 -0
- package/dist/worker/console/static/assets/channel-DsZxrgqe.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-CuToPeGV.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-CuToPeGV.js +0 -1
- package/dist/worker/console/static/assets/index-D9Sc0f0y.js +0 -449
- package/dist/worker/console/static/assets/index-DDQc5a50.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-yPoT19ft.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-VWdarchG.js +0 -8
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import { createHash } from "node:crypto";
|
|
2
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
3
|
+
import { withFrontendTestReporter, parseFrontendTestExecutionReport } from "../workflows/dag/frontend-test-execution-evidence.js";
|
|
3
4
|
import { appendFileSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
5
|
+
import { mkdir, readFile, realpath, stat, writeFile } from "node:fs/promises";
|
|
5
6
|
import path from "node:path";
|
|
6
7
|
import { writeDagNodeJsonArtifact, writeDagNodeTextArtifact, writeDagRunJsonArtifact, } from "../infrastructure/harness/artifact-store.js";
|
|
7
8
|
import { captureWorkspaceCheckpoint } from "../workflows/dag/workspace-checkpoint.js";
|
|
@@ -15,7 +16,7 @@ import { buildRequirementCoverageGateShellCommand, expandShellPreset, buildVerdi
|
|
|
15
16
|
import { materializeBackendTestAnalysisContract } from "../workflows/dag/backend-test-analysis-contract.js";
|
|
16
17
|
import { extractBackendTestContractEnvelope } from "../workflows/dag/backend-test-contract-envelope.js";
|
|
17
18
|
import { materializeFrontendImplementationContract } from "../workflows/dag/frontend-implementation-contract.js";
|
|
18
|
-
import { assertFrontendSourceBindingFresh, buildContractFromCommittedFacts, deterministicSha256, frontendImplementationContractSchema, serializeCompactDeterministicJson, sha256Hex, writeFrontendImplementationContractArtifact, } from "../workflows/dag/frontend-implementation-contract.js";
|
|
19
|
+
import { assertFrontendSourceBindingFresh, buildContractFromCommittedFacts, deterministicSha256, frontendImplementationContractSchema, serializeCompactDeterministicJson, sha256Hex, writeFrontendImplementationContractArtifact, buildFrontendVerifyCommandDirectory, } from "../workflows/dag/frontend-implementation-contract.js";
|
|
19
20
|
import { checkFrontendSourceFidelityLedger } from "../workflows/dag/frontend-source-fidelity-ledger.js";
|
|
20
21
|
import { materializeFrontendTestResult, validateFrontendCaseEvidence, } from "../workflows/dag/frontend-test-result-contract.js";
|
|
21
22
|
import { copyFrontendTestStandardScenarios } from "../workflows/dag/frontend-test-standard-scenarios.js";
|
|
@@ -1907,11 +1908,12 @@ async function executePipelineCommands(input, meta, overrideCommands) {
|
|
|
1907
1908
|
return results;
|
|
1908
1909
|
}
|
|
1909
1910
|
/** A+B (AC-008): derive unresolved verification entrypoints from committed plan
|
|
1910
|
-
* facts. A verification target is unresolved when its
|
|
1911
|
-
* match any frozen command
|
|
1912
|
-
* Returns `{ ref, digest }` entries; a missing/unreadable
|
|
1913
|
-
* closed to [] so the router restarts the contract instead
|
|
1914
|
-
|
|
1911
|
+
* facts. A verification target is unresolved when its commandId does not
|
|
1912
|
+
* match any frozen command directory entry derived from the verify shell
|
|
1913
|
+
* evidence bundle. Returns `{ ref, digest }` entries; a missing/unreadable
|
|
1914
|
+
* plan ledger fails closed to [] so the router restarts the contract instead
|
|
1915
|
+
* of inventing refs. */
|
|
1916
|
+
async function deriveUnresolvedVerificationEntrypointRefs(runDir, frozenCommandIds) {
|
|
1915
1917
|
try {
|
|
1916
1918
|
const { restorePlanPatchFromCommittedFacts } = await import("../workflows/dag/frontend-shadow-dual-write.js");
|
|
1917
1919
|
const records = await readTypedEventStoreFromJsonl(path.join(runDir, "frontend-plan-pi", "plan-typed-facts.jsonl"));
|
|
@@ -1920,15 +1922,13 @@ async function deriveUnresolvedVerificationEntrypointRefs(runDir, frozenCommandL
|
|
|
1920
1922
|
?.verificationTargets;
|
|
1921
1923
|
if (!Array.isArray(targets))
|
|
1922
1924
|
return [];
|
|
1923
|
-
const frozen = new Set(
|
|
1924
|
-
.map((label) => label.trim().toLowerCase())
|
|
1925
|
-
.filter(Boolean));
|
|
1925
|
+
const frozen = new Set(frozenCommandIds.filter(Boolean));
|
|
1926
1926
|
const refs = [];
|
|
1927
1927
|
for (const item of targets) {
|
|
1928
1928
|
const target = item;
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1929
|
+
// v2 plan facts carry commandId as the stable directory key.
|
|
1930
|
+
const commandId = typeof target.commandId === "string" ? target.commandId : "";
|
|
1931
|
+
if (commandId && frozen.has(commandId)) {
|
|
1932
1932
|
continue;
|
|
1933
1933
|
}
|
|
1934
1934
|
const file = typeof target.file === "string" ? target.file : "";
|
|
@@ -1951,6 +1951,23 @@ async function executeFrontendVerificationBundle(input, meta) {
|
|
|
1951
1951
|
const bundle = shell.frontendVerificationBundle;
|
|
1952
1952
|
const cwd = resolveShellCwd(input.cwd, shell.cwd);
|
|
1953
1953
|
const results = [];
|
|
1954
|
+
const commandResultsByKey = new Map();
|
|
1955
|
+
const commandKey = (command) => `${cwd}\u0000${command.trim()}`;
|
|
1956
|
+
const reuseCommandResult = (result, command) => {
|
|
1957
|
+
const reusedAt = new Date().toISOString();
|
|
1958
|
+
return {
|
|
1959
|
+
...result,
|
|
1960
|
+
command,
|
|
1961
|
+
durationMs: 0,
|
|
1962
|
+
wallDurationMs: 0,
|
|
1963
|
+
executionDurationMs: result.executionDurationMs ?? result.durationMs,
|
|
1964
|
+
startedAt: reusedAt,
|
|
1965
|
+
finishedAt: reusedAt,
|
|
1966
|
+
stdoutArtifactPath: undefined,
|
|
1967
|
+
stderrArtifactPath: undefined,
|
|
1968
|
+
reused: true,
|
|
1969
|
+
};
|
|
1970
|
+
};
|
|
1954
1971
|
let beforeStatus;
|
|
1955
1972
|
try {
|
|
1956
1973
|
beforeStatus = await readGitStatusPorcelain(input.cwd);
|
|
@@ -1959,22 +1976,32 @@ async function executeFrontendVerificationBundle(input, meta) {
|
|
|
1959
1976
|
beforeStatus = undefined;
|
|
1960
1977
|
}
|
|
1961
1978
|
const lintResults = [];
|
|
1979
|
+
const successfulLabels = new Map();
|
|
1980
|
+
const successfulCommandTexts = new Map();
|
|
1981
|
+
const executedTests = new Map();
|
|
1982
|
+
const commandExecutionEvidence = new Map();
|
|
1962
1983
|
for (const command of bundle.lintCommands ?? []) {
|
|
1963
|
-
const
|
|
1964
|
-
const
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1984
|
+
const key = commandKey(command);
|
|
1985
|
+
const cached = commandResultsByKey.get(key);
|
|
1986
|
+
let result = cached ? reuseCommandResult(cached, command) : undefined;
|
|
1987
|
+
if (!result) {
|
|
1988
|
+
const commandNumber = results.length + 1;
|
|
1989
|
+
result = await executeShellCommand({
|
|
1990
|
+
command,
|
|
1991
|
+
cwd,
|
|
1992
|
+
timeoutMs: shell.timeoutMs ?? DEFAULT_SHELL_TIMEOUT_MS,
|
|
1993
|
+
envAllowlist: shell.envAllowlist,
|
|
1994
|
+
dagRunMeta: { runDir: meta.runDir, runId: meta.runId },
|
|
1995
|
+
reportActivity: input.reportActivity,
|
|
1996
|
+
outputArtifacts: {
|
|
1997
|
+
stdoutPath: path.join(meta.runDir, input.task.id, "commands", `${commandNumber}.stdout.txt`),
|
|
1998
|
+
stderrPath: path.join(meta.runDir, input.task.id, "commands", `${commandNumber}.stderr.txt`),
|
|
1999
|
+
},
|
|
2000
|
+
});
|
|
2001
|
+
commandResultsByKey.set(key, result);
|
|
2002
|
+
}
|
|
1977
2003
|
results.push(result);
|
|
2004
|
+
lintResults.push(result);
|
|
1978
2005
|
}
|
|
1979
2006
|
let lintAssessment;
|
|
1980
2007
|
if ((bundle.lintCommands?.length ?? 0) > 0 &&
|
|
@@ -2008,30 +2035,67 @@ async function executeFrontendVerificationBundle(input, meta) {
|
|
|
2008
2035
|
labels: bundle.behaviorEvidence.commandLabels,
|
|
2009
2036
|
},
|
|
2010
2037
|
];
|
|
2011
|
-
const
|
|
2038
|
+
const failedGroups = new Set();
|
|
2039
|
+
const groupFailures = [];
|
|
2012
2040
|
for (const group of lintBlocked ? [] : groups) {
|
|
2013
2041
|
for (const [index, command] of group.commands.entries()) {
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2042
|
+
if (failedGroups.has(group.name))
|
|
2043
|
+
break;
|
|
2044
|
+
const key = commandKey(command);
|
|
2045
|
+
const cached = commandResultsByKey.get(key);
|
|
2046
|
+
let result = cached ? reuseCommandResult(cached, command) : undefined;
|
|
2047
|
+
if (!result) {
|
|
2048
|
+
const commandNumber = results.length + 1;
|
|
2049
|
+
const reportPath = path.join(meta.runDir, input.task.id, "commands", `${commandNumber}-${randomUUID()}.vitest.json`);
|
|
2050
|
+
const observedCommand = await withFrontendTestReporter({ command, cwd, reportPath });
|
|
2051
|
+
if (observedCommand)
|
|
2052
|
+
await mkdir(path.dirname(reportPath), { recursive: true });
|
|
2053
|
+
result = await executeShellCommand({
|
|
2054
|
+
command: observedCommand ?? command,
|
|
2055
|
+
cwd,
|
|
2056
|
+
timeoutMs: shell.timeoutMs ?? DEFAULT_SHELL_TIMEOUT_MS,
|
|
2057
|
+
envAllowlist: shell.envAllowlist,
|
|
2058
|
+
dagRunMeta: { runDir: meta.runDir, runId: meta.runId },
|
|
2059
|
+
reportActivity: input.reportActivity,
|
|
2060
|
+
outputArtifacts: {
|
|
2061
|
+
stdoutPath: path.join(meta.runDir, input.task.id, "commands", `${commandNumber}.stdout.txt`),
|
|
2062
|
+
stderrPath: path.join(meta.runDir, input.task.id, "commands", `${commandNumber}.stderr.txt`),
|
|
2063
|
+
},
|
|
2064
|
+
});
|
|
2065
|
+
let reportError;
|
|
2066
|
+
if (observedCommand && result.ok) {
|
|
2067
|
+
try {
|
|
2068
|
+
// Vitest reports physical paths (e.g. /private/var on macOS).
|
|
2069
|
+
// Match those in workspace coordinates even when shell.cwd is
|
|
2070
|
+
// a subdirectory or the caller used a directory alias.
|
|
2071
|
+
commandExecutionEvidence.set(key, parseFrontendTestExecutionReport(JSON.parse(await readFile(reportPath, "utf8")), await realpath(cwd), await realpath(input.cwd)));
|
|
2072
|
+
}
|
|
2073
|
+
catch (error) {
|
|
2074
|
+
reportError = `frontend test execution report unavailable: ${error instanceof Error ? error.message : String(error)}`;
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
if (reportError) {
|
|
2078
|
+
result = { ...result, ok: false, failureCategory: "invalid-output", stderr: reportError };
|
|
2079
|
+
}
|
|
2080
|
+
commandResultsByKey.set(key, result);
|
|
2081
|
+
}
|
|
2027
2082
|
results.push(result);
|
|
2028
2083
|
if (result.ok && group.labels[index]) {
|
|
2084
|
+
const observations = commandExecutionEvidence.get(key);
|
|
2085
|
+
if (observations !== undefined)
|
|
2086
|
+
executedTests.set(group.labels[index], observations);
|
|
2029
2087
|
const labels = successfulLabels.get(group.name) ?? [];
|
|
2030
2088
|
labels.push(group.labels[index]);
|
|
2031
2089
|
successfulLabels.set(group.name, labels);
|
|
2090
|
+
const texts = successfulCommandTexts.get(group.name) ?? [];
|
|
2091
|
+
texts.push(command);
|
|
2092
|
+
successfulCommandTexts.set(group.name, texts);
|
|
2032
2093
|
}
|
|
2033
|
-
if (!result.ok)
|
|
2094
|
+
if (!result.ok) {
|
|
2095
|
+
groupFailures.push(result);
|
|
2096
|
+
failedGroups.add(group.name);
|
|
2034
2097
|
break;
|
|
2098
|
+
}
|
|
2035
2099
|
}
|
|
2036
2100
|
}
|
|
2037
2101
|
if (beforeStatus !== undefined) {
|
|
@@ -2061,12 +2125,15 @@ async function executeFrontendVerificationBundle(input, meta) {
|
|
|
2061
2125
|
exitCode: result.exitCode,
|
|
2062
2126
|
failureCategory: result.failureCategory,
|
|
2063
2127
|
command: result.command,
|
|
2128
|
+
reused: result.reused === true,
|
|
2064
2129
|
}));
|
|
2130
|
+
// Lint baseline-debt is tolerated, so a lint-only failure must not become the
|
|
2131
|
+
// verification failure. A shared lint/static or lint/behavior command is
|
|
2132
|
+
// different: once reused by a non-lint lane, its failure belongs to that lane
|
|
2133
|
+
// and must remain the primary failure evidence.
|
|
2065
2134
|
const firstFailure = lintBlocked
|
|
2066
2135
|
? lintResults.find((result) => !result.ok)
|
|
2067
|
-
:
|
|
2068
|
-
.filter((result) => !lintResults.includes(result))
|
|
2069
|
-
.find((result) => !result.ok);
|
|
2136
|
+
: groupFailures[0];
|
|
2070
2137
|
const lintSyntheticFailure = lintBlocked && !firstFailure
|
|
2071
2138
|
? {
|
|
2072
2139
|
failureCategory: "invalid-output",
|
|
@@ -2083,7 +2150,7 @@ async function executeFrontendVerificationBundle(input, meta) {
|
|
|
2083
2150
|
mock: {
|
|
2084
2151
|
nodeId: input.task.id,
|
|
2085
2152
|
commandLabels: successfulLabels.get("mock") ?? [],
|
|
2086
|
-
commandTexts:
|
|
2153
|
+
commandTexts: successfulCommandTexts.get("mock") ?? [],
|
|
2087
2154
|
},
|
|
2088
2155
|
static: {
|
|
2089
2156
|
nodeId: input.task.id,
|
|
@@ -2092,6 +2159,7 @@ async function executeFrontendVerificationBundle(input, meta) {
|
|
|
2092
2159
|
behavior: {
|
|
2093
2160
|
nodeId: input.task.id,
|
|
2094
2161
|
commandLabels: successfulLabels.get("behavior") ?? [],
|
|
2162
|
+
executedTests,
|
|
2095
2163
|
},
|
|
2096
2164
|
},
|
|
2097
2165
|
});
|
|
@@ -2124,7 +2192,9 @@ async function executeFrontendVerificationBundle(input, meta) {
|
|
|
2124
2192
|
nodeId: failedNodeId,
|
|
2125
2193
|
rawFailureCategory: failureCategory,
|
|
2126
2194
|
});
|
|
2127
|
-
const failureOwner = classifiedOwner === "unknown"
|
|
2195
|
+
const failureOwner = classifiedOwner === "unknown" &&
|
|
2196
|
+
["typecheck", "build", "lint", "component-test", "unit-test", "trace"].includes(failureClass)
|
|
2197
|
+
? "implementation" : classifiedOwner;
|
|
2128
2198
|
const restartPhase = classifyRestartPhase(failureOwner);
|
|
2129
2199
|
// A+B (AC-008): verification-config routing. Produce structured
|
|
2130
2200
|
// unresolved entrypoint refs from committed plan facts and the frozen
|
|
@@ -2135,12 +2205,18 @@ async function executeFrontendVerificationBundle(input, meta) {
|
|
|
2135
2205
|
// never re-classifies the same signal twice.
|
|
2136
2206
|
let verificationConfigRoute;
|
|
2137
2207
|
if (failureOwner === "verification-config") {
|
|
2138
|
-
const
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2208
|
+
const verifyDirectory = buildFrontendVerifyCommandDirectory({
|
|
2209
|
+
staticLabels: bundle.staticEvidence.commandLabels ?? [],
|
|
2210
|
+
behaviorLabels: bundle.behaviorEvidence.commandLabels ?? [],
|
|
2211
|
+
mockLabels: bundle.mockEvidence?.commandLabels ?? [],
|
|
2212
|
+
lintLabels: bundle.lintEvidence?.commandLabels ?? [],
|
|
2213
|
+
staticCommandTexts: bundle.staticEvidence.commandTexts ?? [],
|
|
2214
|
+
behaviorCommandTexts: bundle.behaviorEvidence.commandTexts ?? [],
|
|
2215
|
+
mockCommandTexts: bundle.mockEvidence?.commandTexts ?? [],
|
|
2216
|
+
lintCommandTexts: bundle.lintEvidence?.commandTexts ?? [],
|
|
2217
|
+
});
|
|
2218
|
+
const frozenCommandLabels = verifyDirectory.map((entry) => entry.label);
|
|
2219
|
+
const unresolvedEntrypointRefs = await deriveUnresolvedVerificationEntrypointRefs(meta.runDir, verifyDirectory.map((entry) => entry.commandId));
|
|
2144
2220
|
verificationConfigRoute = routeVerificationConfig({
|
|
2145
2221
|
unresolvedEntrypointRefs: unresolvedEntrypointRefs.map((ref) => ref.ref),
|
|
2146
2222
|
});
|
|
@@ -2531,7 +2607,7 @@ async function executeFrontendDesignPolicy(input, meta) {
|
|
|
2531
2607
|
};
|
|
2532
2608
|
}
|
|
2533
2609
|
const uncoveredVTs = contract.verificationTargets
|
|
2534
|
-
.filter((vt) => vt.
|
|
2610
|
+
.filter((vt) => vt.mode !== "static")
|
|
2535
2611
|
.filter((vt) => ![...writeSet].some((pattern) => pathMatchesPattern(vt.file, pattern) || vt.file === pattern));
|
|
2536
2612
|
if (uncoveredVTs.length > 0) {
|
|
2537
2613
|
return {
|
|
@@ -2598,6 +2674,10 @@ async function executeFrontendDesignPolicy(input, meta) {
|
|
|
2598
2674
|
[],
|
|
2599
2675
|
allowedDependencies: config.allowedDependencies,
|
|
2600
2676
|
writeSetPatterns: config.implementationWriteSet ?? [],
|
|
2677
|
+
// Fresh-existence evidence at gate time (Scout pathEvidence.fresh
|
|
2678
|
+
// semantics): a reuse-existing evidencePath must name a file that
|
|
2679
|
+
// exists NOW; greenfield paths are decision=new only.
|
|
2680
|
+
hasFreshPathEvidence: (candidate) => existsSync(path.resolve(input.cwd, candidate)),
|
|
2601
2681
|
});
|
|
2602
2682
|
// 8. Write canonical contract + policy result.
|
|
2603
2683
|
const contractArtifact = await writeFrontendImplementationContractArtifact({
|
|
@@ -63,6 +63,12 @@ const RAW_TO_NORMALIZED = {
|
|
|
63
63
|
// compaction / fresh context before retrying — an executor-side
|
|
64
64
|
// environment condition, not a plan-quality defect.
|
|
65
65
|
"context-overflow": "executor",
|
|
66
|
+
// stopReason=length output-capacity truncation: same executor-side
|
|
67
|
+
// capacity class as context-overflow. Legacy thinking-exhausted labels
|
|
68
|
+
// (pre-output-limit artifacts) map to the same class.
|
|
69
|
+
"output-limit": "executor",
|
|
70
|
+
"writer-thinking-exhausted": "executor",
|
|
71
|
+
"planner-thinking-exhausted": "executor",
|
|
66
72
|
// Deterministic capability/policy refusal: missing verified launcher JS
|
|
67
73
|
// entry, capability allowlist rejection, or missing bounded write tool
|
|
68
74
|
// profile. These are environment/config defects the operator repairs
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/** Execution hints and estimation reserves, never model-name capacity defaults. */
|
|
3
|
+
export const frontendExecutionPolicySchema = z.object({
|
|
4
|
+
scopeTargetBytes: z.number().int().positive().default(24_000),
|
|
5
|
+
maxScopeUnits: z.number().int().positive().default(4),
|
|
6
|
+
maxCoverageConcurrency: z.number().int().min(1).max(4).default(4),
|
|
7
|
+
outputReserveTokens: z.number().int().positive().default(4096),
|
|
8
|
+
readGrowthReserveTokens: z.number().int().nonnegative().default(2048),
|
|
9
|
+
safetyRatio: z.number().min(0).max(0.5).default(0.15),
|
|
10
|
+
}).strict();
|
|
11
|
+
export function frontendModelCapabilities(model) {
|
|
12
|
+
const value = model && typeof model === "object" ? model : {};
|
|
13
|
+
const positive = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : null;
|
|
14
|
+
return { model: typeof value.id === "string" ? `${value.provider ?? "unknown"}/${value.id}` : null, source: Object.keys(value).length ? "model-registry" : "unknown", inputLimit: positive(value.inputLimit), totalContextLimit: positive(value.contextWindow), outputLimit: positive(value.maxTokens), reasoningControllability: "unknown" };
|
|
15
|
+
}
|
|
16
|
+
export function measureFrontendRequestEnvelope(context, model, policy) {
|
|
17
|
+
const bytes = Buffer.byteLength(JSON.stringify(context));
|
|
18
|
+
const estimatedTokens = Math.ceil(bytes / 3);
|
|
19
|
+
const capabilities = frontendModelCapabilities(model);
|
|
20
|
+
const outputReserve = Math.min(policy.outputReserveTokens, capabilities.outputLimit ?? policy.outputReserveTokens);
|
|
21
|
+
const availableInputTokens = Math.min(capabilities.inputLimit ?? Infinity, capabilities.totalContextLimit === null ? Infinity : Math.floor(capabilities.totalContextLimit * (1 - policy.safetyRatio)) - outputReserve - policy.readGrowthReserveTokens);
|
|
22
|
+
return { bytes, estimatedTokens, estimatorVersion: "utf8-bytes-div3-v1-estimate", capabilities, availableInputTokens: Number.isFinite(availableInputTokens) ? availableInputTokens : null, exceedsEstimate: estimatedTokens > availableInputTokens };
|
|
23
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { frontendExecutionPolicySchema } from "../shared/frontend-execution-policy.js";
|
|
2
3
|
import { DEFAULT_FRONTEND_SPEC_ROOTS, isFrontendSpecFilePath, isValidFrontendSpecRoot, } from "../shared/openspec-spec.js";
|
|
3
4
|
export const taskFlowSchema = z.enum([
|
|
4
5
|
"auto",
|
|
@@ -300,6 +301,8 @@ const taskConfigObjectSchema = z.object({
|
|
|
300
301
|
* default; raise to 2-3 for more tolerance of transient verify failures). */
|
|
301
302
|
frontendRecovery: z
|
|
302
303
|
.object({
|
|
304
|
+
/** Frozen request quota across the original run and recovery lineage; not a model capacity. */
|
|
305
|
+
maxProviderRequests: z.number().int().positive().optional(),
|
|
303
306
|
maxContinuations: z
|
|
304
307
|
.number()
|
|
305
308
|
.int()
|
|
@@ -310,6 +313,7 @@ const taskConfigObjectSchema = z.object({
|
|
|
310
313
|
})
|
|
311
314
|
.strict()
|
|
312
315
|
.optional(),
|
|
316
|
+
frontendExecutionPolicy: frontendExecutionPolicySchema.optional(),
|
|
313
317
|
/** Outer loop auto mode never writes by default; DAG execute requires this policy plus path/writeSet gates. */
|
|
314
318
|
loopAutoExecutionPolicy: loopAutoExecutionPolicySchema
|
|
315
319
|
.optional()
|
|
@@ -147,7 +147,7 @@ export async function applyTaskContract(input) {
|
|
|
147
147
|
ref,
|
|
148
148
|
changedFiles: [],
|
|
149
149
|
idempotentReplay: true,
|
|
150
|
-
txId: existing.txId,
|
|
150
|
+
...(existing.txId ? { txId: existing.txId } : { noOp: true }),
|
|
151
151
|
};
|
|
152
152
|
}
|
|
153
153
|
const lock = await acquireTaskContractLock({
|
|
@@ -175,7 +175,9 @@ export async function applyTaskContract(input) {
|
|
|
175
175
|
ref,
|
|
176
176
|
changedFiles: [],
|
|
177
177
|
idempotentReplay: true,
|
|
178
|
-
|
|
178
|
+
...(existingUnderLock.txId
|
|
179
|
+
? { txId: existingUnderLock.txId }
|
|
180
|
+
: { noOp: true }),
|
|
179
181
|
};
|
|
180
182
|
}
|
|
181
183
|
const state = await observeTaskContractState(input.repoRoot, input.taskId);
|
|
@@ -251,6 +253,38 @@ export async function applyTaskContract(input) {
|
|
|
251
253
|
sourceHashes,
|
|
252
254
|
taskConfigSha256,
|
|
253
255
|
});
|
|
256
|
+
// Content-level no-op: when the freshly projected draft hashes
|
|
257
|
+
// identically to both the recorded contract and the on-disk observation,
|
|
258
|
+
// the journaled commit would only churn revision/updatedAt metadata and
|
|
259
|
+
// force downstream consumers (writeSet gate digest binds
|
|
260
|
+
// contractRevision+contractCanonicalHash) to re-issue approvals for
|
|
261
|
+
// zero content change. Return the current ref untouched instead. A
|
|
262
|
+
// drifted disk (observed !== ref) never reaches this short-circuit: the
|
|
263
|
+
// apply is then a repair projection and must run.
|
|
264
|
+
if (state.ref &&
|
|
265
|
+
canonicalHash === state.ref.canonicalHash &&
|
|
266
|
+
state.observedCanonicalHash === state.ref.canonicalHash) {
|
|
267
|
+
await assertLockHeld(lock);
|
|
268
|
+
await appendRequestLedgerEntry({
|
|
269
|
+
repoRoot: input.repoRoot,
|
|
270
|
+
taskId: input.taskId,
|
|
271
|
+
entry: {
|
|
272
|
+
schemaVersion: 1,
|
|
273
|
+
requestId: input.requestId,
|
|
274
|
+
requestPayloadSha256: input.requestPayloadSha256,
|
|
275
|
+
revision: state.ref.revision,
|
|
276
|
+
canonicalHash: state.ref.canonicalHash,
|
|
277
|
+
recordedAt: new Date().toISOString(),
|
|
278
|
+
resultSummary: { ok: true, operation: "apply" },
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
return {
|
|
282
|
+
ref: state.ref,
|
|
283
|
+
changedFiles: [],
|
|
284
|
+
idempotentReplay: false,
|
|
285
|
+
noOp: true,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
254
288
|
const nextRevision = currentRevision + 1;
|
|
255
289
|
const nextRef = {
|
|
256
290
|
schemaVersion: TASK_CONTRACT_REF_SCHEMA_VERSION,
|
|
@@ -3,7 +3,7 @@ import { normalizeFragmentText } from "./fragment-inventory.js";
|
|
|
3
3
|
// 顺序 reconciliation(AC-006 / AC-FIDELITY-REPAIR-006)
|
|
4
4
|
// 结构化确定性片段走代码映射(显式 AC ID / 文本精确匹配),绝不调用 executor;
|
|
5
5
|
// 仅叙述性/歧义 chunk 调用可注入 executor,按标题顺序串行、逐块有界处理
|
|
6
|
-
// (budgetChars
|
|
6
|
+
// (budgetChars 仅为调用提示,不截断判定所需的原文)。executor 超时/异常/无效
|
|
7
7
|
// 返回/identity 越界一律 fail closed(SOURCE_FIDELITY_RECONCILIATION_FAILED),
|
|
8
8
|
// 不回退 heuristics。
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
@@ -65,7 +65,7 @@ export async function reconcileLedgerFragments(input) {
|
|
|
65
65
|
path: fragment.path,
|
|
66
66
|
headingPath: fragment.headingPath,
|
|
67
67
|
lineRange: fragment.lineRange,
|
|
68
|
-
text: fragment.text
|
|
68
|
+
text: fragment.text,
|
|
69
69
|
},
|
|
70
70
|
canonicalRequirements,
|
|
71
71
|
budgetChars,
|
|
@@ -7,8 +7,7 @@ import { mergeInventoryItems } from "./ledger-reconciliation.js";
|
|
|
7
7
|
// context-only 仅 reviewer pass 放行,高风险 context-only 始终阻断;duplicate
|
|
8
8
|
// 保留全部来源但审核失败(含修订后仍拒绝)以稳定码 fail closed。
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
10
|
-
/**
|
|
11
|
-
* prompt;沿用 MAX_SOURCE_EXCERPT_CHARS 边界语义。 */
|
|
10
|
+
/** Historical caller hint; it must never clip the fragment used for a decision. */
|
|
12
11
|
export const LEDGER_REVIEW_DEFAULT_BUDGET_CHARS = 2000;
|
|
13
12
|
export const LEDGER_REVIEW_MAX_REVISIONS = 1;
|
|
14
13
|
/** review 失败(reviewer 异常/第二次不通过)统一稳定错误码。 */
|
|
@@ -180,12 +179,9 @@ export const defaultLedgerReviewer = async (request) => {
|
|
|
180
179
|
: "conflict 无有效 human resolution 覆盖,保持阻断",
|
|
181
180
|
};
|
|
182
181
|
};
|
|
183
|
-
/**
|
|
184
|
-
*
|
|
185
|
-
* (AC-004 / AC-HARD-004):请求携带非空有界原文与精确 fragment id,完整文档
|
|
186
|
-
* 绝不进入下游 prompt;原文不可得时退化为 reason / id 保证非空。 */
|
|
182
|
+
/** Resolve complete source fragments for independent review; missing original text
|
|
183
|
+
* cannot be replaced with its reason or identity. */
|
|
187
184
|
export function ledgerReviewItemsFromRecords(records, input) {
|
|
188
|
-
const budgetChars = input?.budgetChars ?? LEDGER_REVIEW_DEFAULT_BUDGET_CHARS;
|
|
189
185
|
const merged = input?.fragments
|
|
190
186
|
? mergeInventoryItems({ records, fragments: input.fragments })
|
|
191
187
|
: records.map((record) => ({
|
|
@@ -200,8 +196,9 @@ export function ledgerReviewItemsFromRecords(records, input) {
|
|
|
200
196
|
canonicalRequirementIds: record.canonicalRequirementIds,
|
|
201
197
|
}));
|
|
202
198
|
return merged.map((item) => {
|
|
203
|
-
const
|
|
204
|
-
|
|
199
|
+
const text = item.text;
|
|
200
|
+
if (!text.trim() && ["context-only", "duplicate", "conflict"].includes(item.disposition))
|
|
201
|
+
throw new LedgerReviewError(`SOURCE_FIDELITY_ORIGINAL_MISSING: ${item.id}; independent review needs the full source text`);
|
|
205
202
|
return {
|
|
206
203
|
id: item.id,
|
|
207
204
|
text,
|
|
@@ -13,6 +13,13 @@ const HEADING_ALIASES = {
|
|
|
13
13
|
"需求范围",
|
|
14
14
|
"已确认范围",
|
|
15
15
|
"已确认需求",
|
|
16
|
+
// Common zh-CN frontend-PRD variants seen in real PRDs; without these a
|
|
17
|
+
// raw-PRD fallback parse (semantic intake unavailable) yields an empty
|
|
18
|
+
// scope and flips the import to incomplete (EMPTY scope deadlock).
|
|
19
|
+
"目标表面",
|
|
20
|
+
"交付范围",
|
|
21
|
+
"实现范围",
|
|
22
|
+
"交付内容",
|
|
16
23
|
]),
|
|
17
24
|
nonGoals: new Set(["非目标", "out of scope", "non-goals", "non goals"]),
|
|
18
25
|
acceptance: new Set([
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { observeFrontendSession } from "../../workflows/dag/frontend-session-budget.js";
|
|
1
3
|
/**
|
|
2
4
|
* P2 Semantic Intake: one-shot, read-only Pi structuring of imported PRDs
|
|
3
5
|
* into TaskContractDraftV1. Does not write source/需求.md directly — caller
|
|
@@ -411,24 +413,6 @@ export async function buildRepoHint(repoRoot) {
|
|
|
411
413
|
}
|
|
412
414
|
return lines.join("\n");
|
|
413
415
|
}
|
|
414
|
-
/**
|
|
415
|
-
* 在 maxChars 附近回退到最近的标题边界再截断(AC-005 / AC-FIDELITY-REPAIR-005
|
|
416
|
-
* 回归点移除):避免把可执行项/列表项从中间切开吞掉。仅用于模型提示词预算;
|
|
417
|
-
* ledger fragment inventory 始终消费完整原文,不经过此裁剪。
|
|
418
|
-
*/
|
|
419
|
-
function clipAtSectionBoundary(content, maxChars) {
|
|
420
|
-
if (content.length <= maxChars)
|
|
421
|
-
return content;
|
|
422
|
-
const prefix = content.slice(0, maxChars);
|
|
423
|
-
const headingRe = /^(?:#{1,6})\s+.+$/gm;
|
|
424
|
-
let boundary = prefix.length;
|
|
425
|
-
let match;
|
|
426
|
-
while ((match = headingRe.exec(prefix)) !== null) {
|
|
427
|
-
if (match.index >= maxChars * 0.4)
|
|
428
|
-
boundary = match.index;
|
|
429
|
-
}
|
|
430
|
-
return `${content.slice(0, boundary)}\n\n[truncated at section boundary]`;
|
|
431
|
-
}
|
|
432
416
|
async function buildPrompt(input) {
|
|
433
417
|
const bodies = input.documents
|
|
434
418
|
.map((doc, index) => {
|
|
@@ -436,14 +420,14 @@ async function buildPrompt(input) {
|
|
|
436
420
|
const metaLine = meta.recognized
|
|
437
421
|
? `artifact_type=${meta.artifactType} status=${meta.requirementStatus ?? meta.analysisStatus ?? "n/a"}`
|
|
438
422
|
: "artifact_type=unknown";
|
|
439
|
-
const
|
|
423
|
+
const complete = doc.content;
|
|
440
424
|
return [
|
|
441
425
|
`### Document ${index + 1}`,
|
|
442
426
|
`role: ${doc.role}`,
|
|
443
427
|
`path: ${doc.materializedPath}`,
|
|
444
428
|
metaLine,
|
|
445
429
|
"```markdown",
|
|
446
|
-
|
|
430
|
+
complete,
|
|
447
431
|
"```",
|
|
448
432
|
].join("\n");
|
|
449
433
|
})
|
|
@@ -609,9 +593,7 @@ export async function runSemanticIntake(input) {
|
|
|
609
593
|
message: "semantic intake has no MED/HIGH/LOW or Pi-supported models to try",
|
|
610
594
|
};
|
|
611
595
|
}
|
|
612
|
-
|
|
613
|
-
.map((d) => d.absolutePath)
|
|
614
|
-
.filter((p) => Boolean(p));
|
|
596
|
+
// Complete text is already inline; attaching the same documents duplicates it.
|
|
615
597
|
const prompt = await buildPrompt({
|
|
616
598
|
taskId: input.taskId,
|
|
617
599
|
title: input.title,
|
|
@@ -623,8 +605,12 @@ export async function runSemanticIntake(input) {
|
|
|
623
605
|
let chosenText;
|
|
624
606
|
for (const modelRef of queue) {
|
|
625
607
|
try {
|
|
626
|
-
const result = await
|
|
627
|
-
|
|
608
|
+
const result = await observeFrontendSession({
|
|
609
|
+
phase: "semantic-intake", userMessage: "Structure the imported requirement documents into the required JSON only.", prompt, model: modelRef, taskId: input.taskId,
|
|
610
|
+
artifactPath: path.join(getTaskPaths(input.repoRoot, input.taskId).taskDir, "artifacts", "session-budget", `${randomUUID()}.json`),
|
|
611
|
+
}, observer => execute({
|
|
612
|
+
onAttemptObservation: observer,
|
|
613
|
+
attachedFiles: [],
|
|
628
614
|
modelConfig: resolveDagPiModelConfig(modelRef),
|
|
629
615
|
prompt,
|
|
630
616
|
repoRoot: input.repoRoot,
|
|
@@ -643,7 +629,7 @@ export async function runSemanticIntake(input) {
|
|
|
643
629
|
return [error instanceof Error ? error.message : String(error)];
|
|
644
630
|
}
|
|
645
631
|
},
|
|
646
|
-
});
|
|
632
|
+
}));
|
|
647
633
|
attempted.push({
|
|
648
634
|
model: result.modelDisplay || modelRef,
|
|
649
635
|
ok: result.ok,
|
|
@@ -656,6 +642,10 @@ export async function runSemanticIntake(input) {
|
|
|
656
642
|
}
|
|
657
643
|
lastFailure =
|
|
658
644
|
`${result.failureCategory}: ${result.stderr || result.assistantText}`.slice(0, 2000);
|
|
645
|
+
if (result.stopReason === "length" || ["context-overflow", "context-budget-exhausted"].includes(result.failureCategory)) {
|
|
646
|
+
lastFailure = `SEMANTIC_INTAKE_CAPACITY_EXHAUSTED: refine the complete source into independently reviewable documents; ${lastFailure}`;
|
|
647
|
+
break;
|
|
648
|
+
}
|
|
659
649
|
}
|
|
660
650
|
catch (error) {
|
|
661
651
|
const message = error instanceof Error ? error.message : String(error);
|