@tea-agent/loop-agent 0.43.0-next.8 → 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +41 -2312
- package/README.md +7 -0
- package/dist/application/dag/args.js +8 -0
- package/dist/application/task-lifecycle/advance.js +38 -7
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +2 -0
- package/dist/cli/program.js +3 -1
- package/dist/cli/update/init-surface-notifier.js +2 -0
- package/dist/cli/update/policy.js +2 -0
- package/dist/commands/client-recovery.js +20 -55
- package/dist/commands/cursor-prompt.js +2 -0
- package/dist/commands/dag-approve.js +2 -0
- package/dist/commands/dag-final-verification.js +2 -0
- package/dist/commands/dag-init-hybrid.js +2 -0
- package/dist/commands/dag-reconcile-tasks.js +2 -0
- package/dist/commands/dag-reject.js +2 -0
- package/dist/commands/dag-report.js +2 -0
- package/dist/commands/dag-rerun.js +2 -0
- package/dist/commands/dag-resume.js +2 -0
- package/dist/commands/dag-workflow-compile.js +2 -0
- package/dist/commands/dag-workflow-plan.js +2 -0
- package/dist/commands/dag-workflow-validate.js +2 -0
- package/dist/commands/delegate.js +2 -0
- package/dist/commands/examples.js +2 -0
- package/dist/commands/import-prd.js +2 -0
- package/dist/commands/init-model-catalog.js +2 -0
- package/dist/commands/init.js +19 -13
- package/dist/commands/instructions.js +2 -0
- package/dist/commands/knowledge.js +2 -0
- package/dist/commands/loop-benchmark.js +2 -0
- package/dist/commands/pi-prompt.js +2 -0
- package/dist/commands/pi-reuse-benchmark.js +2 -0
- package/dist/commands/promote-run.js +2 -0
- package/dist/commands/stats.js +1 -1
- package/dist/commands/study-init.js +2 -0
- package/dist/commands/task-advance.js +34 -0
- package/dist/commands/task-source-prepare.js +2 -0
- package/dist/commands/worktree-create.js +2 -0
- package/dist/commands/worktree-remove.js +2 -0
- package/dist/executors/dag-pi-executor.js +1650 -198
- package/dist/executors/pi-executor.js +85 -3
- package/dist/executors/pi-sdk-executor.js +18 -0
- package/dist/executors/shell-executor.js +93 -7
- package/dist/executors/shell-verification.js +3 -0
- package/dist/executors/shell-write-guard.js +27 -32
- package/dist/governance/exec-plans.js +6 -3
- package/dist/infrastructure/console/app-data.js +6 -0
- package/dist/shared/artifacts-core.js +2 -0
- package/dist/shared/backend-dogfood-preflight.js +47 -0
- package/dist/shared/dag-failure-category.js +3 -0
- package/dist/shared/git-progress.js +2 -0
- package/dist/shared/one-shot-prompt-args.js +2 -0
- package/dist/shared/operator/capabilities.js +180 -0
- package/dist/shared/package-metadata.js +6 -4
- package/dist/shared/pi-context-pressure/extension.js +26 -16
- package/dist/shared/pi-context-pressure/sift-bridge.js +1 -1
- package/dist/shared/pi-provider-recovery.js +182 -0
- package/dist/shared/pi-retry-settings.js +40 -0
- package/dist/shared/reference-context.js +2 -0
- package/dist/task/config-types.js +29 -0
- package/dist/task/contract/project.js +9 -0
- package/dist/task/contract/recover.js +6 -0
- package/dist/task/contract/schema.js +17 -0
- package/dist/task/source-prepare/artifact-meta.js +9 -2
- package/dist/task/source-prepare/build-draft.js +60 -0
- package/dist/task/source-prepare/fragment-inventory.js +4 -1
- package/dist/task/source-prepare/parse-intent.js +36 -11
- package/dist/task/task-demand-routing.js +5 -2
- package/dist/worker/console/chat/browser-automation.js +523 -0
- package/dist/worker/console/chat/browser-policy.js +6 -36
- package/dist/worker/console/chat/browser-routes.js +188 -56
- package/dist/worker/console/chat/explore-tools.js +5 -2
- package/dist/worker/console/chat/pi-mode-loop-isolation.js +155 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/ask-user-question.js +44 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/browser-tools.js +184 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/explore-tools.js +202 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/goal-tools.js +79 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +94 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/scheduled-goal-tools.js +22 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/todo-write.js +38 -0
- package/dist/worker/console/chat/pi-runtime/inspection-types.js +28 -0
- package/dist/worker/console/chat/pi-runtime/inspection.js +439 -0
- package/dist/worker/console/chat/pi-runtime/progressive-tools.js +195 -0
- package/dist/worker/console/chat/pi-runtime/sdk-bindings.js +153 -0
- package/dist/worker/console/chat/pi-runtime/sdk-session.js +5 -0
- package/dist/worker/console/chat/pi-runtime.js +208 -1138
- package/dist/worker/console/chat/routes.js +59 -24
- package/dist/worker/console/chat/scheduled-goal-booking.js +59 -0
- package/dist/worker/console/chat/scheduled-goal-delivery.js +27 -0
- package/dist/worker/console/chat/scheduled-goal-request.js +190 -0
- package/dist/worker/console/chat/sdd-data-alignment.js +1 -1
- package/dist/worker/console/chat/session-mode-view.js +5 -4
- package/dist/worker/console/chat/session-mode.js +18 -12
- package/dist/worker/console/chat/session-store.js +42 -9
- package/dist/worker/console/chat/terminal-sessions.js +65 -1
- package/dist/worker/console/chat/terminal-tools.js +17 -5
- package/dist/worker/console/chat/tool-preview.js +115 -0
- package/dist/worker/console/chat/tools.js +10 -0
- package/dist/worker/console/chat/turn-order.js +13 -0
- package/dist/worker/console/chat/turn-process.js +32 -30
- package/dist/worker/console/console-update-and-init.js +1 -19
- package/dist/worker/console/console-update-runtime.js +5 -20
- package/dist/worker/console/operator-actions.js +63 -1
- package/dist/worker/console/prd-intake-bridge.js +54 -1
- package/dist/worker/console/routes.js +8 -0
- package/dist/worker/console/scheduled-goal-host.js +98 -0
- package/dist/worker/console/scheduled-goal-operation-adapter.js +127 -0
- package/dist/worker/console/server.js +24 -1
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-CwAxHSAM.js → abnfDiagram-N423BO3Z-8-j6y-sd.js} +1 -1
- package/dist/worker/console/static/assets/{arc-Bozf-oeF.js → arc-eoQiMvuk.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DEypbEQv.js → architectureDiagram-T3A2C74G-C4A3uMcI.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-B1BQWCbE.js → blockDiagram-VBNYF7ZC-D4zD2F-Q.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-B3u5-yZz.js → c4Diagram-5PPSVZJV-j1RkJziL.js} +1 -1
- package/dist/worker/console/static/assets/channel-ChE7y-cx.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-DSUc2NTh.js → chunk-2GRJ4B5K-YBHmhik1.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DZouMIvT.js → chunk-2Q5K7J3B-COfWyo9P.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-B3bhB5hh.js → chunk-5RXB4S5H-I99OUkHY.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CZFP_BOz.js → chunk-5VM5RSS4-XKxoJNJ4.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DnE_wb-X.js → chunk-6Q2QTUOP-DLT_cYx1.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-C2OfoiWm.js → chunk-GF5L2VYU-CxcKZ9mV.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BKUTL8mm.js → chunk-JWPE2WC7-V1EqXdjY.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BT5oz0tg.js → chunk-KBJHAD2P-DebTtdFp.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-D8vk-GJi.js → chunk-RYQCIY6F-B-ivNRDf.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CkUIm3zu.js → chunk-XXDRQBXY-DC_Ds11b.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C01TCf2X.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C01TCf2X.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CO9QNHmR.js → cose-bilkent-JH36ORCC-sWEqKwIb.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-s52EPl2Z.js → cynefin-VYW2F7L2-BXa_dcu4.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-BHaseJfW.js → cynefinDiagram-MW4NZA55-C-Mle84F.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-CTVhRG_R.js → dagre-VZM6K2ZE-CXPDBITe.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-B6pYeOMR.js → diagram-7IWD3JNH-CC-WJQfa.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-CXXRzw5j.js → diagram-B4RE2ZJO-CDAV5Vs4.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-BOeJHiLA.js → diagram-LBJQPF4R-CmFiAcNz.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DbEg6h_l.js → diagram-Q27KOJAE-DPBZHuyn.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-CjlEcbX6.js → diagram-UB23O5K3-jUlm_Ds3.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BcqMPbD_.js → ebnfDiagram-BXEA7PRR-DWhQ3mfY.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DWfRyHe5.js → erDiagram-JOGREHBK-Tr2gMqet.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-Bz7TeIc8.js → flowDiagram-UKHOOZJN-DJjVQHPA.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-BD-JxYjU.js → ganttDiagram-PKOTCBZU-D74dQ4u0.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Cv16vMHK.js → gitGraphDiagram-DS77QQ5N-DwW0tZ0X.js} +1 -1
- package/dist/worker/console/static/assets/index-BWkIfcrK.css +1 -0
- package/dist/worker/console/static/assets/index-Cdkvw_H6.js +469 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-BlWO0HQQ.js → infoDiagram-6WML65LV-ILCbxJyb.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-C4mbsn97.js → ishikawaDiagram-WSZJBQD7-DeuWBQ89.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-CBPvVKjw.js → journeyDiagram-NVQOT4AX-CF5ih8Fk.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-DPHLp54K.js → kanban-definition-27J2QSJJ-C7yOSuRO.js} +1 -1
- package/dist/worker/console/static/assets/{linear-CXx_9o6k.js → linear-BDZ9riWi.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-DU3IxMq9.js → mermaid.core-7pKqYtpZ.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-COm4a2ud.js → mindmap-definition-FAOFIHXS-LeJDybSU.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-Cbt7OA5d.js → pegDiagram-VL7TDLO6-BJvT3pMD.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-BiYV_4Wl.js → pieDiagram-7S7Q4E2Y-_rGqpMan.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-lg2zhvbp.js → quadrantDiagram-CIZ2JOQS-DPcSv5aA.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-cBLvtf0p.js → railroadDiagram-AXF67PYL-Drxx4hkJ.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BoekU3Bg.js → requirementDiagram-LRYGKXZP-BCTUdU4z.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-D_DMZYga.js → sankeyDiagram-W5VNT64P-B6wzbZmB.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-Lzmzn8lz.js → sequenceDiagram-SI44F4Z6-BGt8d3QQ.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-Cos3HpIe.js → sizeCapture-X5ZJPWSS-7nlhJKo7.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-D-hFBGsx.js → stateDiagram-OKZ733FA-Cv2stqMA.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DC7V5vcq.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-BY95sCVI.js → swimlanes-SLNWSIFB-qDAo4Yc1.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Bwy4QUTO.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-D1iCgWHS.js → timeline-definition-Z64GVDOM-CKbDNKTr.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-D9IYy4WK.js → vennDiagram-T6HMQDX7-DI-9EHic.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-C9yJwO-R.js → wardleyDiagram-T6FBY63Y-BrzzRDpX.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CC2llyou.js → xychartDiagram-ELKLHX3M-BclH5hGh.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/operator-chat/active-turn-tail.js +10 -0
- package/dist/worker/console/static-src/operator-chat/chat-scroll-position.js +24 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +41 -11
- package/dist/worker/console/static-src/operator-chat/compaction-message.js +10 -17
- package/dist/worker/console/static-src/operator-chat/composer-session-ledger.js +192 -0
- package/dist/worker/console/static-src/operator-chat/pending-user-message.js +9 -1
- package/dist/worker/console/static-src/operator-chat/slash-palette-nav.js +15 -5
- package/dist/worker/console/static-src/operator-chat/timeline-merge.js +29 -0
- package/dist/worker/console/static-src/operator-chat/turn-process-disclosure.js +10 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +105 -44
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +145 -70
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +38 -57
- package/dist/worker/console/static-src/operator-chat/useComposer.js +195 -73
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +2 -0
- package/dist/worker/console/static-src/prd-file-import.js +3 -1
- package/dist/worker/console/workspace-context.js +22 -0
- package/dist/worker/console/workspace-initialization.js +209 -0
- package/dist/worker/delivery/git-transaction.js +2 -0
- package/dist/worker/feature/fullstack-validate.js +2 -0
- package/dist/worker/loop-agent/loop-agent-client.js +8 -2
- package/dist/worker/materialize/harness-task-materializer.js +4 -1
- package/dist/worker/metrics/projector.js +1 -1
- package/dist/worker/observability/event-store.js +4 -1
- package/dist/worker/observability/read-model.js +4 -1
- package/dist/worker/observe/node-transparency.js +23 -1
- package/dist/worker/observe/routes.js +4 -0
- package/dist/worker/observe/static/operator-chrome.js +3 -1
- package/dist/worker/observe/static/styles.css +7 -4
- package/dist/worker/observe/static/views/dag-inspector.js +5 -19
- package/dist/worker/outcomes/declared-artifacts.js +2 -0
- package/dist/worker/preflight.js +3 -0
- package/dist/worker/scheduler/scheduled-goal-dispatch.js +117 -0
- package/dist/worker/scheduler/scheduled-goal-evidence.js +167 -0
- package/dist/worker/scheduler/scheduled-goal-recovery.js +62 -0
- package/dist/worker/scheduler/scheduled-goal-store.js +699 -0
- package/dist/worker/scheduler/scheduled-goal-supervisor.js +132 -0
- package/dist/worker/scheduler/scheduled-goal-time.js +102 -0
- package/dist/worker/scheduler/scheduled-goal-types.js +95 -0
- package/dist/worker/task-spec/validate.js +4 -1
- package/dist/workflows/dag/backend-test-markdown-workflow.js +6 -24
- package/dist/workflows/dag/backend-test-plan-protocol.js +82 -5
- package/dist/workflows/dag/dag-retry-schema.js +11 -0
- package/dist/workflows/dag/decision-envelope.js +2 -0
- package/dist/workflows/dag/dynamic-runtime/shared.js +2 -2
- package/dist/workflows/dag/frontend-closeout.js +3 -1
- package/dist/workflows/dag/frontend-committed-facts.js +461 -0
- package/dist/workflows/dag/frontend-durable-tools.js +15 -3
- package/dist/workflows/dag/frontend-implementation-contract.js +369 -12
- package/dist/workflows/dag/frontend-plan-canary.js +53 -0
- package/dist/workflows/dag/frontend-plan-decision-contract.js +803 -0
- package/dist/workflows/dag/frontend-plan-render.js +0 -2
- package/dist/workflows/dag/frontend-provider-capability-matrix.js +8 -61
- package/dist/workflows/dag/frontend-recovery-run.js +57 -0
- package/dist/workflows/dag/frontend-repair.js +55 -76
- package/dist/workflows/dag/frontend-review-context.js +42 -68
- package/dist/workflows/dag/frontend-review-scopes.js +2 -2
- package/dist/workflows/dag/frontend-risk.js +92 -10
- package/dist/workflows/dag/frontend-session-budget.js +117 -3
- package/dist/workflows/dag/frontend-shape.js +11 -55
- package/dist/workflows/dag/frontend-test-execution-evidence.js +35 -10
- package/dist/workflows/dag/frontend-typed-event-store.js +32 -25
- package/dist/workflows/dag/frontend-verification-trace.js +33 -54
- package/dist/workflows/dag/frontend-writer-admission.js +3 -47
- package/dist/workflows/dag/frontend-writer-status.js +0 -23
- package/dist/workflows/dag/init-hybrid.js +47 -19
- package/dist/workflows/dag/interrupt-request.js +2 -0
- package/dist/workflows/dag/lifecycle.js +11 -2
- package/dist/workflows/dag/node-execution.js +50 -13
- package/dist/workflows/dag/reconcile-run.js +2 -0
- package/dist/workflows/dag/repair-artifact.js +3 -1
- package/dist/workflows/dag/report.js +2 -0
- package/dist/workflows/dag/rerun-plan.js +10 -0
- package/dist/workflows/dag/rerun-task.js +79 -1
- package/dist/workflows/dag/retry-policy.js +18 -0
- package/dist/workflows/dag/scheduler.js +2 -4
- package/dist/workflows/dag/types.js +44 -6
- package/dist/workflows/dag/validate.js +4 -0
- package/docs/README.md +1 -0
- package/docs/architecture/runtime-boundaries.md +3 -3
- package/docs/governance/README.md +1 -0
- package/docs/init-surface.manifest.json +1 -0
- package/docs/operations/README.md +2 -0
- package/docs/templates/README.md +1 -1
- package/docs/templates/agent-dag.schema.json +2 -2
- package/docs/templates/backend-test-dag.json +14 -10
- package/docs/templates/frontend-implementation-contract.schema.json +0 -7
- package/package.json +9 -3
- package/skills/frontend-bounded-implement/references/code-standards.md +3 -3
- package/skills/frontend-contract/references/contract-protocol.md +3 -1
- package/skills/frontend-plan/SKILL.md +8 -6
- package/skills/frontend-plan/references/decision-contract.md +3 -3
- package/skills/frontend-review/SKILL.md +18 -28
- package/skills/frontend-review/references/review-findings.md +8 -3
- package/dist/commands/new-task.js +0 -5
- package/dist/commands/status.js +0 -57
- package/dist/commands/task-contract.js +0 -269
- package/dist/shared/prompts.js +0 -26
- package/dist/task/config.js +0 -2
- package/dist/task/contract/validate-draft.js +0 -106
- package/dist/task/goal.js +0 -3
- package/dist/task/index.js +0 -12
- package/dist/task/lifecycle.js +0 -1
- package/dist/task/operator/capabilities.js +0 -6
- package/dist/task/operator/envelope.js +0 -2
- package/dist/task/operator/index.js +0 -5
- package/dist/task/operator/registry.js +0 -2
- package/dist/task/operator/types.js +0 -1
- package/dist/task/paths.js +0 -1
- package/dist/task/source-state.js +0 -1
- package/dist/task/subagent-guidance.js +0 -1
- package/dist/worker/console/chat/sift-bridge.js +0 -1
- package/dist/worker/console/index.js +0 -20
- package/dist/worker/console/static/assets/channel-JM-I_b0s.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DuQQAYEg.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DuQQAYEg.js +0 -1
- package/dist/worker/console/static/assets/index-B5H-giS2.js +0 -468
- package/dist/worker/console/static/assets/index-Bf7dxfm-.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-img0hVJ4.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-B4acZJvk.js +0 -8
- package/dist/worker/scheduler/index.js +0 -25
- package/dist/workflows/dag/facts.js +0 -4
- package/dist/workflows/dag/frontend-shadow-dual-write.js +0 -975
- package/dist/workflows/dag/index.js +0 -6
- package/dist/workflows/dynamic/index.js +0 -7
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
-
import { mkdir, rename, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { mkdir, readdir, rename, rm, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { readFrontendToolProgress } from "./frontend-durable-tools.js";
|
|
5
5
|
import { frontendModelCapabilities } from "../../shared/frontend-execution-policy.js";
|
|
@@ -229,7 +229,7 @@ export async function observeFrontendSession(input, execute) {
|
|
|
229
229
|
await waitForWrite(save());
|
|
230
230
|
return result;
|
|
231
231
|
}
|
|
232
|
-
|
|
232
|
+
function uniqueFrontendSessions(receipts) {
|
|
233
233
|
const byId = new Map();
|
|
234
234
|
for (const receipt of receipts) {
|
|
235
235
|
const previous = byId.get(receipt.identity.sessionId);
|
|
@@ -240,10 +240,124 @@ export function summarizeFrontendSessions(receipts) {
|
|
|
240
240
|
if (!previous || difference > 0)
|
|
241
241
|
byId.set(receipt.identity.sessionId, receipt);
|
|
242
242
|
}
|
|
243
|
-
|
|
243
|
+
return [...byId.values()];
|
|
244
|
+
}
|
|
245
|
+
export function summarizeFrontendSessions(receipts) {
|
|
246
|
+
const unique = uniqueFrontendSessions(receipts);
|
|
244
247
|
return { sessions: unique.length, failedSessions: unique.filter(r => r.terminal?.ok === false).length, incompleteSessions: unique.filter(r => r.completion === "incomplete").length,
|
|
245
248
|
failureLayers: Object.fromEntries([...new Set(unique.flatMap(r => r.terminal && r.terminal.failureLayer !== "none" ? [r.terminal.failureLayer] : []))].sort().map(layer => [layer, unique.filter(r => r.terminal?.failureLayer === layer).length])),
|
|
246
249
|
progress: Object.fromEntries(["durableCommittedDelta", "durableSubmissions", "durableReplacements", "failedRecords", "duplicateRecords", "successfulReadBytes", "duplicateReadBytes"].map(key => [key, sumKnown(unique.map(r => r.progress[key]))])),
|
|
247
250
|
totalTokens: sumKnown(unique.flatMap(r => r.attempts.length ? r.attempts.map(a => a.actual?.totalTokens ?? null) : [r.actual.totalTokens])),
|
|
248
251
|
sessionDurationSumMs: sumKnown(unique.map(r => r.terminal?.durationMs ?? null)) };
|
|
249
252
|
}
|
|
253
|
+
/** Read the persisted session receipts for one Plan node. This is deliberately
|
|
254
|
+
* an observation-only projection: missing or malformed receipts remain
|
|
255
|
+
* missing, never zero, so a canary cannot manufacture an improvement. */
|
|
256
|
+
export async function readFrontendSessionBudgetReceipts(runDir, nodeId = "frontend-plan-pi") {
|
|
257
|
+
const root = path.join(runDir, nodeId);
|
|
258
|
+
const files = [];
|
|
259
|
+
const unreadable = [];
|
|
260
|
+
const visit = async (dir, insideBudget = false) => {
|
|
261
|
+
let entries;
|
|
262
|
+
try {
|
|
263
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
264
|
+
}
|
|
265
|
+
catch (error) {
|
|
266
|
+
if (error.code !== "ENOENT")
|
|
267
|
+
unreadable.push(dir);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
for (const entry of entries) {
|
|
271
|
+
const absolute = path.join(dir, entry.name);
|
|
272
|
+
if (entry.isDirectory() && (insideBudget || entry.name === "session-budget" || entry.name === "parallel" || /^coverage-\d+$/.test(entry.name)))
|
|
273
|
+
await visit(absolute, insideBudget || entry.name === "session-budget");
|
|
274
|
+
else if (insideBudget)
|
|
275
|
+
files.push(absolute);
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
await visit(root);
|
|
279
|
+
const receipts = [];
|
|
280
|
+
for (const file of [...files, ...unreadable].sort()) {
|
|
281
|
+
try {
|
|
282
|
+
const value = JSON.parse(await readFile(file, "utf8"));
|
|
283
|
+
if (!isCanaryReceipt(value))
|
|
284
|
+
throw new Error("Invalid session receipt");
|
|
285
|
+
receipts.push(value);
|
|
286
|
+
}
|
|
287
|
+
catch {
|
|
288
|
+
// Keep a visible incomplete receipt so canary aggregation cannot turn a
|
|
289
|
+
// truncated artifact into an apparent zero-cost improvement.
|
|
290
|
+
receipts.push({
|
|
291
|
+
schemaVersion: 1,
|
|
292
|
+
policyVersion: "frontend-session-observation-v1",
|
|
293
|
+
identity: { sessionId: `invalid:${file}`, phase: "unknown", scopeIds: [], taskId: null, runId: null, nodeId, attempt: null, sourceDigest: null, contractDigest: null },
|
|
294
|
+
startedAt: new Date(0).toISOString(), finishedAt: null, completion: "incomplete",
|
|
295
|
+
planned: { prompt: { sha256: "", chars: 0, bytes: 0, estimatedTokens: 0 }, userMessage: { sha256: "", chars: 0, bytes: 0, estimatedTokens: 0 }, toolSchemas: null, estimatorVersion: "unknown", capabilities: { ...frontendModelCapabilities(undefined), model: null }, unmeasuredComponents: [] },
|
|
296
|
+
actual: { ...collectFrontendUsage([]) }, attempts: [],
|
|
297
|
+
progress: { memoryCommittedDelta: null, durableCommittedDelta: null, failedRecords: null, duplicateRecords: null, durableSubmissions: null, durableReplacements: null, successfulReadBytes: null, duplicateReadBytes: null }, terminal: null,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return receipts;
|
|
302
|
+
}
|
|
303
|
+
export function summarizeFrontendPlanCanary(receipts) {
|
|
304
|
+
const unique = uniqueFrontendSessions(receipts);
|
|
305
|
+
const complete = unique.length > 0 && unique.every(receipt => isCanaryReceipt(receipt) && receipt.completion === "complete");
|
|
306
|
+
const known = (values, sum) => {
|
|
307
|
+
if (!complete || values.length === 0)
|
|
308
|
+
return null;
|
|
309
|
+
const present = values.filter((value) => value !== null && value !== undefined);
|
|
310
|
+
return present.length === values.length ? sum(present) : null;
|
|
311
|
+
};
|
|
312
|
+
return {
|
|
313
|
+
planModelRequests: known(unique.map((receipt) => receipt.actual.requestCount), (items) => items.reduce((total, value) => total + value, 0)),
|
|
314
|
+
planSessions: complete ? unique.length : null,
|
|
315
|
+
// Receipt envelope includes gaps and overlapping sessions only once. The
|
|
316
|
+
// run reader below replaces this with the authoritative node interval.
|
|
317
|
+
planWallClockMs: complete ? Math.max(...unique.map(r => Date.parse(r.finishedAt))) - Math.min(...unique.map(r => Date.parse(r.startedAt))) : null,
|
|
318
|
+
planTokens: known(unique.map((receipt) => receipt.actual.totalTokens), (items) => items.reduce((total, value) => total + value, 0)),
|
|
319
|
+
toolRejections: known(unique.map((receipt) => receipt.progress.failedRecords), (items) => items.reduce((total, value) => total + value, 0)),
|
|
320
|
+
retries: complete ? unique.reduce((total, receipt) => total + Math.max(0, receipt.attempts.length - 1), 0) : null,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
/** Validate the fields consumed by aggregation before accessing nested values.
|
|
324
|
+
* Valid JSON with the wrong shape is missing evidence too. */
|
|
325
|
+
function isCanaryReceipt(value) {
|
|
326
|
+
const r = record(value), identity = record(r.identity), actual = record(r.actual), progress = record(r.progress);
|
|
327
|
+
const nullableCount = (v) => v === null || (typeof v === "number" && Number.isFinite(v) && Number.isInteger(v) && v >= 0);
|
|
328
|
+
return r.schemaVersion === 1 && r.policyVersion === "frontend-session-observation-v1"
|
|
329
|
+
&& typeof identity.sessionId === "string" && identity.sessionId.length > 0
|
|
330
|
+
&& typeof r.startedAt === "string" && Number.isFinite(Date.parse(r.startedAt))
|
|
331
|
+
&& (r.completion === "incomplete" || (r.completion === "complete" && typeof r.finishedAt === "string" && Date.parse(r.finishedAt) >= Date.parse(r.startedAt)))
|
|
332
|
+
&& Array.isArray(r.attempts) && r.attempts.every(a => a !== null && typeof a === "object")
|
|
333
|
+
&& nullableCount(actual.requestCount) && nullableCount(actual.totalTokens) && nullableCount(progress.failedRecords);
|
|
334
|
+
}
|
|
335
|
+
/** Read one frozen canary run. The optional artifact may add only run-level
|
|
336
|
+
* timing/review/recovery observations; Plan session counts, retries, tokens,
|
|
337
|
+
* request usage and tool rejections always come from session receipts. */
|
|
338
|
+
export async function readFrontendPlanCanaryRunMetrics(runDir, nodeId = "frontend-plan-pi") {
|
|
339
|
+
const sessionMetrics = summarizeFrontendPlanCanary(await readFrontendSessionBudgetReceipts(runDir, nodeId));
|
|
340
|
+
let supplemental = {};
|
|
341
|
+
try {
|
|
342
|
+
const parsed = JSON.parse(await readFile(path.join(runDir, "frontend-plan-canary-metrics.json"), "utf8"));
|
|
343
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
344
|
+
supplemental = parsed;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
catch {
|
|
348
|
+
// Missing or malformed supplemental evidence remains unavailable.
|
|
349
|
+
}
|
|
350
|
+
const optionalNumber = (key) => {
|
|
351
|
+
const value = supplemental[key];
|
|
352
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0
|
|
353
|
+
? value
|
|
354
|
+
: null;
|
|
355
|
+
};
|
|
356
|
+
return {
|
|
357
|
+
...sessionMetrics,
|
|
358
|
+
dagWallClockMs: optionalNumber("dagWallClockMs"),
|
|
359
|
+
designReviewRejectRate: optionalNumber("designReviewRejectRate"),
|
|
360
|
+
finalReviewDefectRate: optionalNumber("finalReviewDefectRate"),
|
|
361
|
+
recoveryCount: optionalNumber("recoveryCount"),
|
|
362
|
+
};
|
|
363
|
+
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { createHash } from "node:crypto";
|
|
11
11
|
import { z } from "zod";
|
|
12
|
+
import { collectFrontendSharedSignals, isNarrowProductSurface } from "./frontend-risk.js";
|
|
12
13
|
/** Fixture-only node counts. They are NOT the authoritative 15/13/11
|
|
13
14
|
* topology: split-required has no count. */
|
|
14
15
|
export const FRONTEND_SHAPE_NODE_COUNTS = {
|
|
@@ -17,16 +18,6 @@ export const FRONTEND_SHAPE_NODE_COUNTS = {
|
|
|
17
18
|
standard: 11,
|
|
18
19
|
high: 11,
|
|
19
20
|
};
|
|
20
|
-
const SHAPE_ESCALATION_PATTERNS = [
|
|
21
|
-
{ id: "auth-permission", re: /\bauth\b|\bpermission\b|\brbac\b|登录|权限|\bjwt\b|\bsession\b/i },
|
|
22
|
-
{ id: "sensitive-action", re: /\bpayment\b|delete\s+account|支付|删除用户|\bdestructive\b/i },
|
|
23
|
-
{ id: "ssr-boundary", re: /\bssr\b|\brsc\b|server\s+component|\bhydration\b|server-client/i },
|
|
24
|
-
{ id: "new-route", re: /new\s+route|新增路由|createBrowserRouter|add\s+route/i },
|
|
25
|
-
{ id: "shared-public-api", re: /public\s+api|shared\s+component|公共组件|破坏性|breaking\s+change/i },
|
|
26
|
-
{ id: "schema-api-change", re: /\bopenapi\b|graphql\s+schema|api\s+contract|接口变更|schema\s+change/i },
|
|
27
|
-
{ id: "new-dependency", re: /new\s+dependency|add\s+dependency|npm\s+install|新增依赖/i },
|
|
28
|
-
{ id: "cross-domain-paths", re: /多个领域|cross-module|multiple\s+packages/i },
|
|
29
|
-
];
|
|
30
21
|
const SHAPE_SMALL_PATTERNS = [
|
|
31
22
|
{ id: "single-component", re: /single\s+component|单一组件|局部样式|style\s+tweak|css\s+only|文案|copy\s+change/i },
|
|
32
23
|
{ id: "no-remote", re: /no\s+api|无\s*api|无\s*接口|本地状态|local\s+state\s+only|pure\s+ui/i },
|
|
@@ -59,48 +50,13 @@ function pathSpreadScore(paths) {
|
|
|
59
50
|
return tops.size;
|
|
60
51
|
}
|
|
61
52
|
function collectEscalationSignals(blob, supervised) {
|
|
62
|
-
const signals =
|
|
63
|
-
for (const pattern of SHAPE_ESCALATION_PATTERNS) {
|
|
64
|
-
if (hasAffirmativeShapeSignal(blob, pattern))
|
|
65
|
-
signals.push(pattern.id);
|
|
66
|
-
}
|
|
67
|
-
if (supervised)
|
|
68
|
-
signals.push("supervised-profile");
|
|
53
|
+
const signals = collectFrontendSharedSignals(blob, supervised);
|
|
69
54
|
if (SHAPE_SPLIT_REQUIRED_PATTERN.test(blob) &&
|
|
70
55
|
!SHAPE_SPLIT_REQUIRED_NEGATION.test(blob)) {
|
|
71
56
|
signals.push("large-requirement-split");
|
|
72
57
|
}
|
|
73
58
|
return [...new Set(signals)];
|
|
74
59
|
}
|
|
75
|
-
function isExplicitShapeExclusion(clause, pattern) {
|
|
76
|
-
const match = new RegExp(pattern.re.source, "i").exec(clause);
|
|
77
|
-
if (!match)
|
|
78
|
-
return false;
|
|
79
|
-
const prefix = clause.slice(0, match.index).trim();
|
|
80
|
-
const suffix = clause.slice(match.index + match[0].length).trim();
|
|
81
|
-
const actionInSignal = ["new-route", "new-dependency"].includes(pattern.id);
|
|
82
|
-
if (suffix && !(actionInSignal && /^no$/i.test(prefix) && /^is\s+(?:required|needed)$/i.test(suffix)))
|
|
83
|
-
return false;
|
|
84
|
-
if (/^(?:不涉及|不(?:修改|变更|调整|改变|新增|添加)|无需(?:修改|变更|调整|改变|新增|添加)|no\s+changes?\s+to|(?:do\s+not|don't)\s+(?:change|modify))$/i.test(prefix))
|
|
85
|
-
return true;
|
|
86
|
-
return actionInSignal && /^(?:不|禁止|无需|无须|不需要|no|without|do\s+not|don't)$/i.test(prefix);
|
|
87
|
-
}
|
|
88
|
-
/** Only a complete, independent scope exclusion can suppress a risk signal.
|
|
89
|
-
* Nested negation, conditions and trailing consequences remain risk evidence;
|
|
90
|
-
* an excluded occurrence never cancels another occurrence elsewhere. */
|
|
91
|
-
function hasAffirmativeShapeSignal(blob, pattern) {
|
|
92
|
-
for (const sentence of blob.split(/[\n。.;;!?!?]/)) {
|
|
93
|
-
if (!pattern.re.test(sentence))
|
|
94
|
-
continue;
|
|
95
|
-
// Commas can attach a condition to an exclusion. A comma-separated list
|
|
96
|
-
// is independent only when every item is itself an explicit exclusion.
|
|
97
|
-
const clauses = sentence.split(/[,,、]/).map(part => part.trim().replace(/^(?:[-*+]|\d+\))\s+/, ""));
|
|
98
|
-
if (clauses.every(clause => SHAPE_ESCALATION_PATTERNS.some(candidate => isExplicitShapeExclusion(clause, candidate))))
|
|
99
|
-
continue;
|
|
100
|
-
return true;
|
|
101
|
-
}
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
60
|
/** Negation applies to the API mention itself, never to unrelated Mock policy. */
|
|
105
61
|
function hasFrontendRemoteSignal(blob) {
|
|
106
62
|
const remaining = blob
|
|
@@ -126,7 +82,7 @@ function resolveBaselineFrontendTaskShape(input) {
|
|
|
126
82
|
}
|
|
127
83
|
const paths = input.allowedPaths ?? [];
|
|
128
84
|
const spread = pathSpreadScore(paths);
|
|
129
|
-
const concentrated = spread <= 1 && paths.length <= 4;
|
|
85
|
+
const concentrated = paths.length > 0 && spread <= 1 && paths.length <= 4;
|
|
130
86
|
const hasApiSignal = hasFrontendRemoteSignal(blob);
|
|
131
87
|
const complexity = (input.complexity ?? "").toLowerCase();
|
|
132
88
|
// split-required wins over everything: it is a termination/escalation
|
|
@@ -172,17 +128,17 @@ function resolveBaselineFrontendTaskShape(input) {
|
|
|
172
128
|
reason: "micro scope: trivial single-file change without remote API",
|
|
173
129
|
};
|
|
174
130
|
}
|
|
175
|
-
// small: no escalation, no supervised,
|
|
176
|
-
// no remote API signal.
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
131
|
+
// small: no escalation, no supervised, narrow single-domain delivery
|
|
132
|
+
// surface, no remote API signal, and not an explicitly large task. Keyword
|
|
133
|
+
// and explicit "small/low" complexity hints are no longer required — they
|
|
134
|
+
// were a fragile proxy for "this is genuinely a small change" and blocked
|
|
135
|
+
// genuinely-small tasks whose authors used different wording.
|
|
136
|
+
if (isNarrowProductSurface(paths) && !hasApiSignal && complexity !== "large") {
|
|
181
137
|
return {
|
|
182
138
|
shape: "small",
|
|
183
|
-
signals: smallHits,
|
|
139
|
+
signals: smallHits.length > 0 ? smallHits : ["structured-small"],
|
|
184
140
|
rejectedSignals: [],
|
|
185
|
-
reason:
|
|
141
|
+
reason: "narrow single-domain delivery surface without remote API or escalation; small topology",
|
|
186
142
|
};
|
|
187
143
|
}
|
|
188
144
|
const rejected = smallHits.map((id) => `small:${id}:blocked-by-${hasApiSignal ? "api-signal" : "path-spread"}`);
|
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return undefined;
|
|
10
|
-
const direct = /^(?:(?:npx|pnpm exec|yarn exec)\s+)?vitest\s+run(?:\s|$)/.test(input.command.trim());
|
|
11
|
-
const npm = /^npm\s+(?:run\s+([\w:-]+)|(test))(?=\s|$)(.*)$/.exec(input.command.trim());
|
|
4
|
+
/** Decide whether a bare vitest/npm-test command is observable and, if so,
|
|
5
|
+
* return the command with the separator (" --") a reporter would need. */
|
|
6
|
+
async function adaptPlainTestCommand(command, cwd) {
|
|
7
|
+
const direct = /^(?:(?:npx|pnpm exec|yarn exec)\s+)?vitest\s+run(?:\s|$)/.test(command.trim());
|
|
8
|
+
const npm = /^npm\s+(?:run\s+([\w:-]+)|(test))(?=\s|$)(.*)$/.exec(command.trim());
|
|
12
9
|
let separator = "";
|
|
13
10
|
if (!direct) {
|
|
14
11
|
if (!npm)
|
|
15
12
|
return undefined;
|
|
16
13
|
let manifest;
|
|
17
14
|
try {
|
|
18
|
-
manifest = JSON.parse(await readFile(path.join(
|
|
15
|
+
manifest = JSON.parse(await readFile(path.join(cwd, "package.json"), "utf8"));
|
|
19
16
|
}
|
|
20
17
|
catch {
|
|
21
18
|
return undefined;
|
|
@@ -27,8 +24,36 @@ export async function withFrontendTestReporter(input) {
|
|
|
27
24
|
return undefined;
|
|
28
25
|
separator = npm[3]?.trim() ? "" : " --";
|
|
29
26
|
}
|
|
27
|
+
return `${command}${separator}`;
|
|
28
|
+
}
|
|
29
|
+
/** Observe supported frozen Vitest commands without changing their selection. */
|
|
30
|
+
export async function withFrontendTestReporter(input) {
|
|
31
|
+
// Compound commands/wrappers need their own adapter: never append flags to
|
|
32
|
+
// a different shell command, change selection, or override a user reporter.
|
|
33
|
+
if (/[;&|<>`\n\r$]/.test(input.command) || /--(?:reporter|outputFile)/.test(input.command))
|
|
34
|
+
return undefined;
|
|
35
|
+
// Frozen verify commands are env-prefixed bash wrappers —
|
|
36
|
+
// `HARNESS_ALLOW_ACTIVE_DAG_RUNS=1 'bash' '-lc' 'npm test'` — so the JSON
|
|
37
|
+
// reporter must be injected INSIDE the quoted payload; appending it to the
|
|
38
|
+
// wrapper would target bash's own CLI and produce no test report at all
|
|
39
|
+
// (smoke r54: every behavior verification target then failed the trace
|
|
40
|
+
// gate with "test execution report unavailable").
|
|
41
|
+
const bashWrapped = /^((?:[A-Za-z_][A-Za-z0-9_]*=(?:'[^']*'|[^\s'"]+)\s+)*)'bash'\s+'-lc'\s+'([^']*)'\s*$/.exec(input.command);
|
|
42
|
+
if (bashWrapped) {
|
|
43
|
+
const inner = bashWrapped[2];
|
|
44
|
+
if (/[;&|<>`\n\r$]/.test(inner) || /--(?:reporter|outputFile)/.test(inner))
|
|
45
|
+
return undefined;
|
|
46
|
+
const adaptedInner = await adaptPlainTestCommand(inner, input.cwd);
|
|
47
|
+
if (!adaptedInner)
|
|
48
|
+
return undefined;
|
|
49
|
+
const quote = (value) => `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
50
|
+
return input.command.replace(`'${inner}'`, `'${adaptedInner} --reporter=json --outputFile=${quote(input.reportPath)}'`);
|
|
51
|
+
}
|
|
52
|
+
const adapted = await adaptPlainTestCommand(input.command, input.cwd);
|
|
53
|
+
if (!adapted)
|
|
54
|
+
return undefined;
|
|
30
55
|
const quote = (value) => `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
31
|
-
return `${
|
|
56
|
+
return `${adapted} --reporter=json --outputFile=${quote(input.reportPath)}`;
|
|
32
57
|
}
|
|
33
58
|
const resultSchema = z.object({
|
|
34
59
|
testResults: z.array(z.object({
|
|
@@ -31,18 +31,11 @@ export const TYPED_EVENT_FACT_KINDS = [
|
|
|
31
31
|
"provider-ended-without-terminal",
|
|
32
32
|
"read-only-fact-channel",
|
|
33
33
|
];
|
|
34
|
-
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
|
|
39
|
-
*
|
|
40
|
-
* Kept deliberately separate from `TYPED_EVENT_FACT_KINDS`: widening the shadow
|
|
41
|
-
* catalog must never widen the provider capability matrix (which stays frozen
|
|
42
|
-
* at its seven kinds). `target-surface` is shared by the scout and plan
|
|
43
|
-
* origins, so the union of the two catalogs is 12 unique kinds.
|
|
44
|
-
*/
|
|
45
|
-
export const SHADOW_FACT_KINDS = [
|
|
34
|
+
/** Frontend DAG decision facts. These are committed runtime facts, not a
|
|
35
|
+
* secondary text channel; Policy, Admission and Plan compilation read them
|
|
36
|
+
* directly. Kept separate from provider capability facts so the provider
|
|
37
|
+
* matrix remains frozen. */
|
|
38
|
+
export const FRONTEND_DAG_FACT_KINDS = [
|
|
46
39
|
"requirement",
|
|
47
40
|
"constraint",
|
|
48
41
|
"open-question",
|
|
@@ -56,11 +49,26 @@ export const SHADOW_FACT_KINDS = [
|
|
|
56
49
|
"design-deviation",
|
|
57
50
|
"dependency",
|
|
58
51
|
];
|
|
52
|
+
/** Experimental decision Plan fact catalog. The decision tools emit semantic
|
|
53
|
+
* decisions (module placement, reuse, state ownership, Mock boundary, behavior
|
|
54
|
+
* verification focus); `data-flow` and `dependency` reuse the DAG catalog above.
|
|
55
|
+
* Kept as a dedicated catalog so widening the decision surface never widens the
|
|
56
|
+
* frozen provider / event / review / design catalogs. */
|
|
57
|
+
export const FRONTEND_DECISION_FACT_KINDS = [
|
|
58
|
+
"module-placement",
|
|
59
|
+
"reuse-decision",
|
|
60
|
+
"state-ownership",
|
|
61
|
+
"api-mock-boundary",
|
|
62
|
+
"verification-focus",
|
|
63
|
+
];
|
|
64
|
+
/** Terminal fact for the experimental decision Plan path. Kept separate from the
|
|
65
|
+
* decision fact catalog so it never gets collected as a decision entry. */
|
|
66
|
+
export const DECISION_TERMINAL_FACT_KINDS = ["finalize_decision"];
|
|
59
67
|
/**
|
|
60
68
|
* M5 review terminal fact catalog (AC-001). These two committed typed tools are
|
|
61
69
|
* the authoritative terminal verdict of `frontend-review-pi`. They are kept in
|
|
62
70
|
* a dedicated catalog so widening the review terminal surface never widens the
|
|
63
|
-
* frozen
|
|
71
|
+
* frozen provider/event catalogs.
|
|
64
72
|
*/
|
|
65
73
|
export const REVIEW_TERMINAL_FACT_KINDS = [
|
|
66
74
|
"approve_review",
|
|
@@ -72,7 +80,7 @@ export const REVIEW_TERMINAL_FACT_KINDS = [
|
|
|
72
80
|
* `approve_design` / `request_design_changes` instead of the legacy first-line
|
|
73
81
|
* `VERDICT: pass|request-revision` text protocol. Kept in a dedicated catalog
|
|
74
82
|
* so widening the design terminal surface never widens the frozen provider /
|
|
75
|
-
*
|
|
83
|
+
* event / review catalogs.
|
|
76
84
|
*/
|
|
77
85
|
export const DESIGN_TERMINAL_FACT_KINDS = [
|
|
78
86
|
"approve_design",
|
|
@@ -84,7 +92,7 @@ export const DESIGN_TERMINAL_FACT_KINDS = [
|
|
|
84
92
|
* open-question/split-proposal facts plus the `contract-finalized` terminal
|
|
85
93
|
* fact carrying the terminal disposition (`ready | ready-with-assumptions |
|
|
86
94
|
* blocked` + optional `blockingOwner`). Kept in a dedicated catalog so
|
|
87
|
-
* widening the contract surface never widens the frozen provider /
|
|
95
|
+
* widening the contract surface never widens the frozen provider /
|
|
88
96
|
* event / review / design catalogs.
|
|
89
97
|
*/
|
|
90
98
|
export const CONTRACT_FACT_KINDS = [
|
|
@@ -142,8 +150,8 @@ export const FRONTEND_ISSUE_CATEGORY_VALUES = [
|
|
|
142
150
|
];
|
|
143
151
|
export const frontendIssueCategorySchema = z.enum(FRONTEND_ISSUE_CATEGORY_VALUES);
|
|
144
152
|
/** Runtime control facts committed at frontend shape boundaries. Kept outside
|
|
145
|
-
* the frozen provider
|
|
146
|
-
*
|
|
153
|
+
* the frozen provider catalog so adding control authority cannot widen that
|
|
154
|
+
* capability surface. */
|
|
147
155
|
export const FRONTEND_SHAPE_FACT_KINDS = ["frontend-shape"];
|
|
148
156
|
export const typedEventPhaseSchema = z.enum([
|
|
149
157
|
"staged",
|
|
@@ -153,7 +161,9 @@ export const typedEventPhaseSchema = z.enum([
|
|
|
153
161
|
const ALL_TYPED_EVENT_FACT_KIND_VALUES = [
|
|
154
162
|
...new Set([
|
|
155
163
|
...TYPED_EVENT_FACT_KINDS,
|
|
156
|
-
...
|
|
164
|
+
...FRONTEND_DAG_FACT_KINDS,
|
|
165
|
+
...FRONTEND_DECISION_FACT_KINDS,
|
|
166
|
+
...DECISION_TERMINAL_FACT_KINDS,
|
|
157
167
|
...REVIEW_TERMINAL_FACT_KINDS,
|
|
158
168
|
...DESIGN_TERMINAL_FACT_KINDS,
|
|
159
169
|
...FRONTEND_SHAPE_FACT_KINDS,
|
|
@@ -166,7 +176,7 @@ const ALL_TYPED_EVENT_FACT_KIND_VALUES = [
|
|
|
166
176
|
export const typedEventFactKindSchema = z.enum(ALL_TYPED_EVENT_FACT_KIND_VALUES);
|
|
167
177
|
/** A typed event fact is a `kind`-discriminated record. The schema stays
|
|
168
178
|
* permissive beyond `kind` (arbitrary JSON-safe payload) so provider/model
|
|
169
|
-
*
|
|
179
|
+
* provider facts can carry their own fields, but `kind` is always validated. */
|
|
170
180
|
export const typedEventFactSchema = z
|
|
171
181
|
.record(z.unknown())
|
|
172
182
|
.superRefine((value, ctx) => {
|
|
@@ -178,8 +188,7 @@ export const typedEventFactSchema = z
|
|
|
178
188
|
});
|
|
179
189
|
}
|
|
180
190
|
});
|
|
181
|
-
/** A single review finding shared by both typed terminal verdicts.
|
|
182
|
-
* legacy JSON review finding shape so typed↔JSON equivalence stays total. */
|
|
191
|
+
/** A single review finding shared by both typed terminal verdicts. */
|
|
183
192
|
export const reviewFindingSchema = z
|
|
184
193
|
.object({
|
|
185
194
|
severity: z.enum(["Critical", "Important", "Minor", "Info"]),
|
|
@@ -190,8 +199,7 @@ export const reviewFindingSchema = z
|
|
|
190
199
|
})
|
|
191
200
|
.strict();
|
|
192
201
|
/** `approve_review` terminal fact payload. Approval may carry informational
|
|
193
|
-
* findings (Minor/Info) but never Critical/Important
|
|
194
|
-
* JSON verdict applies to `verdict: "pass"`. */
|
|
202
|
+
* findings (Minor/Info) but never Critical/Important. */
|
|
195
203
|
export const approveReviewFactSchema = z
|
|
196
204
|
.object({
|
|
197
205
|
kind: z.literal("approve_review"),
|
|
@@ -221,8 +229,7 @@ export const requestReviewChangesFactSchema = z
|
|
|
221
229
|
})
|
|
222
230
|
.strict();
|
|
223
231
|
/** `approve_design` terminal fact payload. Approval may carry informational
|
|
224
|
-
* findings (Minor/Info) but never Critical/Important
|
|
225
|
-
* text protocol applies to `VERDICT: pass`. */
|
|
232
|
+
* findings (Minor/Info) but never Critical/Important. */
|
|
226
233
|
export const approveDesignFactSchema = z
|
|
227
234
|
.object({
|
|
228
235
|
kind: z.literal("approve_design"),
|
|
@@ -170,6 +170,8 @@ export function extractFrontendTestTitles(content) {
|
|
|
170
170
|
}
|
|
171
171
|
for (let index = 0; index < tokens.length; index += 1) {
|
|
172
172
|
const token = tokens[index];
|
|
173
|
+
if (token === undefined)
|
|
174
|
+
continue;
|
|
173
175
|
if (token?.type !== "identifier" ||
|
|
174
176
|
!["describe", "it", "test"].includes(token.value) ||
|
|
175
177
|
(tokens[index - 1]?.type === "punctuation" &&
|
|
@@ -183,15 +185,16 @@ export function extractFrontendTestTitles(content) {
|
|
|
183
185
|
while (tokens[cursor]?.type === "punctuation" &&
|
|
184
186
|
tokens[cursor]?.value === "." &&
|
|
185
187
|
tokens[cursor + 1]?.type === "identifier") {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
+
const modifier = tokens[cursor + 1];
|
|
189
|
+
if (modifier === undefined)
|
|
190
|
+
break;
|
|
191
|
+
if (configurationModifiers.has(modifier.value)) {
|
|
188
192
|
hasConfigurationModifier = true;
|
|
189
|
-
if (["runIf", "skipIf"].includes(
|
|
190
|
-
conditionalModifier =
|
|
193
|
+
if (["runIf", "skipIf"].includes(modifier.value)) {
|
|
194
|
+
conditionalModifier = modifier.value;
|
|
191
195
|
}
|
|
192
196
|
}
|
|
193
|
-
if (
|
|
194
|
-
nonExecutingModifiers.has(tokens[cursor + 1].value)) {
|
|
197
|
+
if (nonExecutingModifiers.has(modifier.value)) {
|
|
195
198
|
hasNonExecutingModifier = true;
|
|
196
199
|
}
|
|
197
200
|
cursor += 2;
|
|
@@ -221,54 +224,23 @@ export function extractFrontendTestTitles(content) {
|
|
|
221
224
|
index = closingParenIndex(cursor) ?? tokens.length;
|
|
222
225
|
continue;
|
|
223
226
|
}
|
|
224
|
-
|
|
227
|
+
const titleToken = tokens[cursor + 1];
|
|
228
|
+
if (titleToken?.type !== "string")
|
|
225
229
|
continue;
|
|
226
|
-
titles.push(
|
|
230
|
+
titles.push(titleToken.value);
|
|
227
231
|
}
|
|
228
232
|
return titles;
|
|
229
233
|
}
|
|
230
|
-
function traceKeyMatchesTitle(traceKey, title) {
|
|
231
|
-
const escaped = traceKey.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
232
|
-
return new RegExp(`(?:^|[^A-Za-z0-9_-])${escaped}(?:$|[^A-Za-z0-9_-])`).test(title);
|
|
233
|
-
}
|
|
234
|
-
function formatObservedTestTitles(titles) {
|
|
235
|
-
if (titles.length === 0)
|
|
236
|
-
return "(none)";
|
|
237
|
-
const limit = 8;
|
|
238
|
-
const preview = titles
|
|
239
|
-
.slice(0, limit)
|
|
240
|
-
.map((title) => title.length > 120 ? `${title.slice(0, 117)}...` : title)
|
|
241
|
-
.join(" | ");
|
|
242
|
-
return titles.length > limit
|
|
243
|
-
? `${preview} | ... (${titles.length - limit} more)`
|
|
244
|
-
: preview;
|
|
245
|
-
}
|
|
246
234
|
async function evaluateTargetFileTrace(input) {
|
|
247
235
|
const fileIssues = await assertFileAndSymbol({
|
|
248
236
|
workspaceRoot: input.workspaceRoot,
|
|
249
237
|
file: input.target.file,
|
|
250
238
|
});
|
|
251
|
-
|
|
252
|
-
return { issues: fileIssues };
|
|
253
|
-
}
|
|
254
|
-
const absolute = path.resolve(input.workspaceRoot, input.target.file);
|
|
255
|
-
const content = await readFile(absolute, "utf8");
|
|
256
|
-
const observedTestTitles = extractFrontendTestTitles(content);
|
|
257
|
-
if (observedTestTitles.some((title) => traceKeyMatchesTitle(input.target.id, title))) {
|
|
258
|
-
return { issues: [], observedTestTitles };
|
|
259
|
-
}
|
|
260
|
-
const observed = formatObservedTestTitles(observedTestTitles);
|
|
261
|
-
return {
|
|
262
|
-
issues: [
|
|
263
|
-
`trace key not found in test title: ${input.target.id} in ${input.target.file}; observed test titles: ${observed}`,
|
|
264
|
-
],
|
|
265
|
-
observedTestTitles,
|
|
266
|
-
};
|
|
239
|
+
return { issues: fileIssues };
|
|
267
240
|
}
|
|
268
241
|
/**
|
|
269
|
-
* Lightweight writer-boundary check. It validates file existence
|
|
270
|
-
*
|
|
271
|
-
* responsibility after command execution.
|
|
242
|
+
* Lightweight writer-boundary check. It validates declared file existence;
|
|
243
|
+
* frozen commands remain the final trace gate's responsibility after execution.
|
|
272
244
|
*/
|
|
273
245
|
export async function evaluateFrontendWriterTracePreflight(input) {
|
|
274
246
|
const targets = [];
|
|
@@ -287,8 +259,9 @@ export async function evaluateFrontendWriterTracePreflight(input) {
|
|
|
287
259
|
commandLabel: target.commandLabel,
|
|
288
260
|
file: target.file,
|
|
289
261
|
...(target.symbol ? { symbol: target.symbol } : {}),
|
|
290
|
-
...(target.mode !== "static" ? { traceKey: target.id } : {}),
|
|
291
262
|
status: fileTrace.issues.length ? "failed" : "ok",
|
|
263
|
+
evidence: fileTrace.issues.length ? "unavailable" : "file-exists",
|
|
264
|
+
behaviorCoverage: "unconfirmed",
|
|
292
265
|
matchedNodeIds: [],
|
|
293
266
|
issues: fileTrace.issues,
|
|
294
267
|
});
|
|
@@ -298,8 +271,8 @@ export async function evaluateFrontendWriterTracePreflight(input) {
|
|
|
298
271
|
/**
|
|
299
272
|
* Pure verification-target evaluation shared by the trace gate and the repair
|
|
300
273
|
* assess gate. It derives, for each contract verification target, whether the
|
|
301
|
-
* target bound to frozen evidence (commandLabel owners),
|
|
302
|
-
* and
|
|
274
|
+
* target bound to frozen evidence (commandLabel owners), whether its file exists,
|
|
275
|
+
* and whether the declared test file was exercised by the observed runner. It performs no
|
|
303
276
|
* writes and throws nothing: failures are returned as `hardIssues` plus
|
|
304
277
|
* per-target `status`.
|
|
305
278
|
*/
|
|
@@ -325,10 +298,13 @@ export async function evaluateVerificationTargets(input) {
|
|
|
325
298
|
});
|
|
326
299
|
issues.push(...fileTrace.issues);
|
|
327
300
|
const executed = input.executedTests?.get(target.commandLabel);
|
|
328
|
-
if (target.mode !== "static" && executed
|
|
301
|
+
if (target.mode !== "static" && executed === undefined) {
|
|
302
|
+
issues.push(`test execution report unavailable for behavior target: ${target.id}`);
|
|
303
|
+
}
|
|
304
|
+
else if (target.mode !== "static" && executed !== undefined) {
|
|
329
305
|
// safePath permits "./" prefixes; reporter files are cwd-relative.
|
|
330
306
|
const targetFile = path.posix.normalize(target.file.replace(/\\/g, "/"));
|
|
331
|
-
const matching = executed.filter((test) => test.file === targetFile
|
|
307
|
+
const matching = executed.filter((test) => test.file === targetFile);
|
|
332
308
|
if (matching.length === 0 || matching.some((test) => test.status !== "passed")) {
|
|
333
309
|
issues.push(`behavior target was not fully executed and passed: ${target.id} in ${target.file}`);
|
|
334
310
|
}
|
|
@@ -342,8 +318,11 @@ export async function evaluateVerificationTargets(input) {
|
|
|
342
318
|
commandLabel: target.commandLabel,
|
|
343
319
|
file: target.file,
|
|
344
320
|
symbol: target.symbol,
|
|
345
|
-
...(target.mode !== "static" ? { traceKey: target.id } : {}),
|
|
346
321
|
status,
|
|
322
|
+
evidence: issues.length
|
|
323
|
+
? "unavailable"
|
|
324
|
+
: target.mode === "static" ? "command-execution" : "test-file-execution",
|
|
325
|
+
behaviorCoverage: "unconfirmed",
|
|
347
326
|
matchedNodeIds: owners ?? [],
|
|
348
327
|
issues,
|
|
349
328
|
});
|
|
@@ -353,16 +332,16 @@ export async function evaluateVerificationTargets(input) {
|
|
|
353
332
|
/**
|
|
354
333
|
* Legacy deterministic symbol resolvability check retained by canonical
|
|
355
334
|
* normalization (`dropUnresolvedVerificationSymbols`). New plan tools do not
|
|
356
|
-
* expose symbol and runtime trace binding uses the
|
|
335
|
+
* expose symbol and runtime trace binding uses the command and test file. This
|
|
357
336
|
* helper still handles old `all describe blocks`, `describe('title')`, and
|
|
358
337
|
* slash-delimited labels so existing run artifacts normalize consistently.
|
|
359
338
|
*/
|
|
360
339
|
export function verificationSymbolMatchesContent(symbol, content) {
|
|
361
340
|
if (symbol.trim().toLowerCase() === "all describe blocks")
|
|
362
341
|
return content.includes("describe(");
|
|
342
|
+
const separatorHead = symbol.split(/[((]/, 1)[0];
|
|
363
343
|
const symbols = symbol.includes(" / ")
|
|
364
|
-
? symbol
|
|
365
|
-
.split(/[((]/, 1)[0]
|
|
344
|
+
? (separatorHead ?? symbol)
|
|
366
345
|
.split("/")
|
|
367
346
|
.map((item) => item.trim())
|
|
368
347
|
.filter(Boolean)
|
|
@@ -462,7 +441,7 @@ export async function runFrontendVerificationTraceGate(input) {
|
|
|
462
441
|
const mockCommandLabels = input.evidence?.mock?.commandLabels ?? [];
|
|
463
442
|
const mockCommandNodeId = input.evidence?.mock?.nodeId;
|
|
464
443
|
const mockCommandTexts = input.evidence?.mock?.commandTexts ?? [];
|
|
465
|
-
if (input.evidence && contract.mockApi.strategy !== "not-needed" && mockCommandLabels.length === 0) {
|
|
444
|
+
if (input.evidence && contract.verificationTargets.some((target) => target.mode === "behavior") && contract.mockApi.strategy !== "not-needed" && mockCommandLabels.length === 0) {
|
|
466
445
|
throw new Error("trace: selected Mock strategy requires successful Mock verification commands");
|
|
467
446
|
}
|
|
468
447
|
if (input.evidence) {
|
|
@@ -550,7 +529,7 @@ export function formatFrontendVerificationTraceStdout(result) {
|
|
|
550
529
|
`Browser: ${result.browserStatus}`,
|
|
551
530
|
`Visual: ${result.visualStatus}`,
|
|
552
531
|
`Targets: ${result.targets.length}`,
|
|
553
|
-
...result.targets.map((target) => `- ${target.id}:
|
|
532
|
+
...result.targets.map((target) => `- ${target.id}: execution=${target.status} evidence=${target.evidence} behavior-coverage=${target.behaviorCoverage} label=${JSON.stringify(target.commandLabel)} file=${target.file}`),
|
|
554
533
|
];
|
|
555
534
|
if (result.artifactPath) {
|
|
556
535
|
lines.push(`Artifact: ${result.artifactPath}`);
|