@tea-agent/loop-agent 0.39.0-beta.1 → 0.39.0-beta.11
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 +4 -1
- package/CHANGELOG.md +349 -97
- package/README.md +9 -5
- package/bin/loop-agent.js +7 -3
- package/dist/application/task-lifecycle/advance.js +12 -0
- package/dist/application/task-lifecycle/recommendations.js +13 -2
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +23 -5
- package/dist/cli/program.js +21 -2
- package/dist/cli/update/notifier.js +2 -2
- package/dist/cli/update/runtime-activity.js +1 -29
- package/dist/cli.js +2 -1
- package/dist/commands/client-recovery.js +3 -0
- package/dist/commands/dag-follow-up.js +138 -0
- package/dist/commands/dag-rerun.js +55 -1
- package/dist/commands/init-model-catalog.js +464 -0
- package/dist/commands/init-upgrade.js +265 -97
- package/dist/commands/init.js +455 -28
- package/dist/commands/inspect-next.js +8 -0
- package/dist/commands/task-advance.js +19 -0
- package/dist/executors/dag-pi-executor.js +2370 -64
- package/dist/executors/pi-executor.js +11 -2
- package/dist/executors/pi-extension-resolver.js +233 -0
- package/dist/executors/pi-playwright-cli-tool.js +74 -28
- package/dist/executors/pi-read-budget-policy.js +239 -0
- package/dist/executors/pi-sdk-executor.js +225 -33
- package/dist/executors/pi-writer-tool-policy.js +57 -1
- package/dist/executors/shell-executor.js +1258 -224
- package/dist/executors/shell-write-guard.js +14 -0
- package/dist/executors/workspace-write-snapshot.js +68 -0
- package/dist/infrastructure/harness/atomic-write.js +23 -0
- package/dist/shared/dag-failure-category.js +150 -0
- package/dist/shared/dag-prompt-override.js +27 -0
- package/dist/shared/openspec-spec.js +70 -4
- package/dist/shared/operator/capabilities.js +39 -0
- package/dist/shared/playwright-cli-command-policy.js +15 -0
- package/dist/shared/update/console-notifier.js +100 -0
- package/dist/{cli → shared}/update/npm-client.js +70 -15
- package/dist/{cli → shared}/update/state.js +41 -13
- package/dist/task/config-types.js +50 -5
- package/dist/task/contract/apply.js +11 -1
- package/dist/task/contract/constants.js +2 -0
- package/dist/task/contract/hash.js +3 -0
- package/dist/task/contract/observe.js +25 -4
- package/dist/task/contract/paths.js +2 -1
- package/dist/task/contract/project.js +18 -1
- package/dist/task/contract/schema.js +4 -1
- package/dist/task/contract/transaction.js +12 -0
- package/dist/task/frontend-project-capability.js +199 -18
- package/dist/task/source-prepare/completeness.js +188 -0
- package/dist/task/source-prepare/fragment-inventory.js +477 -0
- package/dist/task/source-prepare/index.js +3 -0
- package/dist/task/source-prepare/ledger-reconciliation.js +127 -0
- package/dist/task/source-prepare/ledger-review.js +214 -0
- package/dist/task/source-prepare/ledger.js +545 -0
- package/dist/task/source-prepare/parse-intent.js +24 -4
- package/dist/task/source-prepare/prepare.js +298 -1
- package/dist/task/source-prepare/semantic-intake.js +25 -5
- package/dist/task/source-prepare/source-fidelity-pi.js +384 -0
- package/dist/task/source-prepare/types.js +22 -0
- package/dist/task/source-references.js +22 -1
- package/dist/worker/cli.js +18 -2
- package/dist/worker/console/chat/chat-event-store.js +75 -5
- package/dist/worker/console/chat/context-panel.js +7 -0
- package/dist/worker/console/chat/deferred-turn.js +12 -0
- package/dist/worker/console/chat/operation-card.js +8 -1
- package/dist/worker/console/chat/pi-console-config.js +81 -10
- package/dist/worker/console/chat/pi-runtime.js +331 -18
- package/dist/worker/console/chat/provider-error.js +98 -0
- package/dist/worker/console/chat/routes.js +395 -59
- package/dist/worker/console/chat/session-stats.js +179 -0
- package/dist/worker/console/chat/session-store.js +93 -63
- package/dist/worker/console/chat/todos.js +115 -0
- package/dist/worker/console/chat/tool-preview.js +42 -0
- package/dist/worker/console/chat/turn-process.js +20 -55
- package/dist/worker/console/chat/user-questions.js +266 -0
- package/dist/worker/console/chat/workspace-landing.js +79 -0
- package/dist/worker/console/console-handoff.js +82 -0
- package/dist/worker/console/console-update-and-init.js +115 -0
- package/dist/worker/console/console-update-runtime.js +84 -0
- package/dist/worker/console/dag-execution-receipt.js +33 -0
- package/dist/worker/console/draft-store.js +49 -0
- package/dist/worker/console/frontend-human-decision-adapter.js +19 -0
- package/dist/worker/console/frontend-split-operation-adapter.js +20 -0
- package/dist/worker/console/index.js +3 -0
- package/dist/worker/console/inspect-split.js +18 -0
- package/dist/worker/console/native-directory-picker.js +13 -0
- package/dist/worker/console/operation-run-facts.js +19 -3
- package/dist/worker/console/operation-store.js +227 -7
- package/dist/worker/console/operator-actions.js +388 -0
- package/dist/worker/console/operator-surface-health.js +1 -0
- package/dist/worker/console/operator-user-error.js +2 -2
- package/dist/worker/console/prd-intake-bridge.js +13 -0
- package/dist/worker/console/routes.js +355 -3
- package/dist/worker/console/security.js +67 -13
- package/dist/worker/console/server.js +609 -164
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C4kVYweA.js → abnfDiagram-N423BO3Z-CXj_GnSb.js} +1 -1
- package/dist/worker/console/static/assets/{arc-Bj2M8iO5.js → arc-BZp6JAp7.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CLlXe4-6.js → architectureDiagram-T3A2C74G-DfpcEuYU.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-psEp5xH0.js → blockDiagram-VBNYF7ZC-_Gf0xadb.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DeKcOCGJ.js → c4Diagram-5PPSVZJV-Ct2QPCmv.js} +1 -1
- package/dist/worker/console/static/assets/channel-3TxJgYaH.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-K0C744p1.js → chunk-2GRJ4B5K-BfklkzKl.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-xbNw4J9-.js → chunk-2Q5K7J3B-Dy25vZJV.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DjLaSDUO.js → chunk-5RXB4S5H-BFlCRZep.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CtiPlKel.js → chunk-5VM5RSS4-op3oVxIE.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DZtolir7.js → chunk-6Q2QTUOP-C0R7rzF2.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CPcdNeew.js → chunk-GF5L2VYU-Bt44TCGy.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-NgHc6V37.js → chunk-JWPE2WC7-_uEE_XFx.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DQ_T-hg8.js → chunk-KBJHAD2P-C3TOYGZ9.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DgLsxcVP.js → chunk-RYQCIY6F-Cz60oBPV.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-UrVoM9z1.js → chunk-XXDRQBXY-8Bik0qis.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BHIkXpp3.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BHIkXpp3.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CLkYvTb3.js → cose-bilkent-JH36ORCC-C2QIOA4a.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BwT_xKrE.js → cynefin-VYW2F7L2-CSH_yUUd.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Bi9tuzif.js → cynefinDiagram-MW4NZA55-BDLw2XFM.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BCIqkWBV.js → dagre-VZM6K2ZE-CcD9ZtF4.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Dip6l9_Z.js → diagram-7IWD3JNH-DqwtkBsI.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B-xkh_wK.js → diagram-B4RE2ZJO-BWVEcqsC.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DZO0kTt_.js → diagram-LBJQPF4R-M-WAbtQi.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D6ZplNbZ.js → diagram-Q27KOJAE-DKW7SixP.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BPekbhoS.js → diagram-UB23O5K3-PHHPPtrj.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BtmaJpK5.js → ebnfDiagram-BXEA7PRR-BQD-B4RX.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-6DS0Xc44.js → erDiagram-JOGREHBK-BFvULb51.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CihnROSm.js → flowDiagram-UKHOOZJN-Bw-aXTCb.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CZC4zOE2.js → ganttDiagram-PKOTCBZU-BGKw04Qx.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Dq1fhzGN.js → gitGraphDiagram-DS77QQ5N-RqKaPR-I.js} +1 -1
- package/dist/worker/console/static/assets/index-B_D8rbWc.js +407 -0
- package/dist/worker/console/static/assets/index-BdNx6fj0.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DlSl4BGx.js → infoDiagram-6WML65LV-YO5dnzrJ.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-snnzFl_U.js → ishikawaDiagram-WSZJBQD7-Bi1VZHMq.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-BDE7qpMx.js → journeyDiagram-NVQOT4AX-Bszls1DD.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CD2Ci04G.js → kanban-definition-27J2QSJJ-PhzeaZ09.js} +1 -1
- package/dist/worker/console/static/assets/{linear-DdnapKIH.js → linear-BsjbDoXi.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BVjAT9b8.js → mermaid.core-0B7NnWKk.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-D0yJk-zA.js → mindmap-definition-FAOFIHXS-BJr4Fj-q.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BVo9tFP-.js → pegDiagram-VL7TDLO6-moC4fpGB.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DnOV-mZ_.js → pieDiagram-7S7Q4E2Y-BEw37-2c.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-CzUJo58i.js → quadrantDiagram-CIZ2JOQS-Cq6LyasU.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Bvikrolh.js → railroadDiagram-AXF67PYL-DUCMcK0D.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DtaSVCap.js → requirementDiagram-LRYGKXZP-C3upTZm7.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-C2c9A0wW.js → sankeyDiagram-W5VNT64P-BI_gMsCW.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DmXJcU7r.js → sequenceDiagram-SI44F4Z6-YFOIRzfN.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-B4GUFW92.js → sizeCapture-X5ZJPWSS-dOnB7UDD.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Dsad2MXf.js → stateDiagram-OKZ733FA-BXUniaIh.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Cdi6UhLa.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DGq48Fbi.js → swimlanes-SLNWSIFB-2tA4wTNu.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-D-RJBbb0.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CSSi6OFf.js → timeline-definition-Z64GVDOM-DO0HkJXC.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BqyzPrWv.js → vennDiagram-T6HMQDX7-DvIOzixv.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DlznVb6S.js → wardleyDiagram-T6FBY63Y-DXDZ0cTj.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CfBcgJ_K.js → xychartDiagram-ELKLHX3M-B32Ark0D.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/active-run-badge.js +17 -0
- package/dist/worker/console/static-src/app/console-types.js +68 -4
- package/dist/worker/console/static-src/app/useConsoleShell.js +30 -12
- package/dist/worker/console/static-src/app/useOperatorActions.js +41 -6
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +45 -10
- package/dist/worker/console/static-src/app/useRunProgress.js +47 -0
- package/dist/worker/console/static-src/app/useTaskWizard.js +74 -2
- package/dist/worker/console/static-src/night/useNightBoard.js +8 -5
- package/dist/worker/console/static-src/operator-chat/cards/failure-category-advice.js +25 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +141 -8
- package/dist/worker/console/static-src/operator-chat/compaction-message.js +79 -0
- package/dist/worker/console/static-src/operator-chat/details-dag-actions.js +122 -0
- package/dist/worker/console/static-src/operator-chat/input-history.js +76 -0
- package/dist/worker/console/static-src/operator-chat/mutation-gate.js +1 -1
- package/dist/worker/console/static-src/operator-chat/refs.js +3 -0
- package/dist/worker/console/static-src/operator-chat/runtime-selection-labels.js +27 -0
- package/dist/worker/console/static-src/operator-chat/spatial-overlay.js +1 -2
- package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +25 -0
- package/dist/worker/console/static-src/operator-chat/turn-submission.js +16 -3
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +181 -87
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +183 -48
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +177 -8
- package/dist/worker/console/static-src/operator-chat/useComposer.js +32 -0
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +63 -9
- package/dist/worker/console/static-src/operator-chat/useWorkspaceBrowserGate.js +4 -0
- package/dist/worker/console/static-src/operator-chat/workspace-layout-mode.js +3 -3
- package/dist/worker/console/static-src/pages/tasks/run-id-resolution.js +79 -0
- package/dist/worker/console/static-src/pages/tasks/run-ownership-verify.js +23 -0
- package/dist/worker/console/static-src/pages/tasks/run-panel-progress.js +110 -0
- package/dist/worker/console/static-src/shell/useWorkspaces.js +299 -0
- package/dist/worker/console/static-src/shell/workspace-route.js +339 -0
- package/dist/worker/console/workspace-context.js +344 -0
- package/dist/worker/console/workspace-registry.js +214 -0
- package/dist/worker/loop-agent/loop-agent-client.js +7 -3
- package/dist/worker/materialize/frontend-split-task-materializer.js +72 -0
- package/dist/worker/materialize/harness-task-lineage.js +5 -2
- package/dist/worker/observability/init-runtime-activity.js +26 -0
- package/dist/worker/observability/read-model.js +22 -0
- package/dist/worker/observe/node-input.js +160 -3
- package/dist/worker/observe/node-process.js +377 -0
- package/dist/worker/observe/routes.js +224 -3
- package/dist/worker/observe/server.js +4 -0
- package/dist/worker/observe/shell-handler-keys.js +34 -0
- package/dist/worker/observe/static/api.js +90 -4
- package/dist/worker/observe/static/app.js +18 -70
- package/dist/worker/observe/static/constants.js +8 -1
- package/dist/worker/observe/static/dag-history-labels.js +4 -0
- package/dist/worker/observe/static/dag-node-purpose.d.ts +6 -0
- package/dist/worker/observe/static/dag-node-purpose.js +208 -0
- package/dist/worker/observe/static/format.js +60 -0
- package/dist/worker/observe/static/index.html +6 -1
- package/dist/worker/observe/static/inspect-workspace.js +307 -0
- package/dist/worker/observe/static/markdown-render.js +20 -1
- package/dist/worker/observe/static/operator-chrome.css +145 -20
- package/dist/worker/observe/static/operator-chrome.d.ts +20 -3
- package/dist/worker/observe/static/operator-chrome.js +330 -96
- package/dist/worker/observe/static/prompt-restart-candidates.d.ts +18 -0
- package/dist/worker/observe/static/prompt-restart-candidates.js +36 -0
- package/dist/worker/observe/static/router.d.ts +46 -0
- package/dist/worker/observe/static/router.js +25 -1
- package/dist/worker/observe/static/state.d.ts +50 -0
- package/dist/worker/observe/static/state.js +184 -1
- package/dist/worker/observe/static/styles.css +395 -0
- package/dist/worker/observe/static/views/dag-graph.js +4 -2
- package/dist/worker/observe/static/views/dag-inspector.js +1067 -9
- package/dist/worker/observe/static/views/dag.d.ts +13 -0
- package/dist/worker/observe/static/views/dag.js +14 -5
- package/dist/worker/observe/static/views/dashboard.js +6 -4
- package/dist/worker/observe/static/views/failures.js +5 -2
- package/dist/worker/observe/static/views/night.js +2 -2
- package/dist/worker/observe/static/views/pool.js +1 -0
- package/dist/worker/observe/static/views/run.js +3 -1
- package/dist/worker/observe/static/views/session-timeline.js +78 -9
- package/dist/worker/observe/static/views/task.js +56 -1
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +49 -3
- package/dist/workflows/dag/backend-test-markdown-workflow.js +176 -1
- package/dist/workflows/dag/backend-test-pytest-collection.js +98 -14
- package/dist/workflows/dag/backend-test-writer-completeness.js +17 -17
- package/dist/workflows/dag/contract-output-registry.js +15 -0
- package/dist/workflows/dag/contract-validator-registrations.js +1 -2
- package/dist/workflows/dag/dynamic-runtime/loop-until.js +1 -1
- package/dist/workflows/dag/dynamic-runtime/map.js +2 -1
- package/dist/workflows/dag/failure-category.js +7 -116
- package/dist/workflows/dag/frontend-closeout.js +221 -0
- package/dist/workflows/dag/frontend-design-policy.js +400 -0
- package/dist/workflows/dag/frontend-human-decision.js +182 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +538 -166
- package/dist/workflows/dag/frontend-plan-render.js +24 -0
- package/dist/workflows/dag/frontend-prewrite-gate.js +388 -392
- package/dist/workflows/dag/frontend-provider-capability-matrix.js +159 -0
- package/dist/workflows/dag/frontend-recovery-capsule.js +455 -0
- package/dist/workflows/dag/frontend-recovery-controller.js +202 -0
- package/dist/workflows/dag/frontend-recovery-lineage.js +178 -0
- package/dist/workflows/dag/frontend-recovery-plan.js +17 -10
- package/dist/workflows/dag/frontend-recovery-run.js +33 -20
- package/dist/workflows/dag/frontend-repair.js +7 -424
- package/dist/workflows/dag/frontend-review-context.js +288 -14
- package/dist/workflows/dag/frontend-review-findings.js +270 -0
- package/dist/workflows/dag/frontend-risk.js +15 -2
- package/dist/workflows/dag/frontend-shadow-dual-write.js +814 -0
- package/dist/workflows/dag/frontend-shape-capsule-store.js +191 -0
- package/dist/workflows/dag/frontend-shape-facts.js +409 -0
- package/dist/workflows/dag/frontend-shape.js +427 -0
- package/dist/workflows/dag/frontend-source-fidelity-ledger.js +108 -0
- package/dist/workflows/dag/frontend-split-application-service.js +203 -0
- package/dist/workflows/dag/frontend-split-orchestrator.js +899 -0
- package/dist/workflows/dag/frontend-test-case-checklist.js +30 -4
- package/dist/workflows/dag/frontend-test-case-manifest.js +11 -4
- package/dist/workflows/dag/frontend-test-case-quality.js +11 -16
- package/dist/workflows/dag/frontend-test-environment-probe.js +230 -0
- package/dist/workflows/dag/frontend-test-html-report.js +3 -1
- package/dist/workflows/dag/frontend-test-l5-report.js +3 -1
- package/dist/workflows/dag/frontend-test-layout.js +159 -0
- package/dist/workflows/dag/frontend-test-markdown.js +61 -0
- package/dist/workflows/dag/frontend-test-result-contract.js +188 -42
- package/dist/workflows/dag/frontend-test-standard-scenarios.js +70 -0
- package/dist/workflows/dag/frontend-typed-event-store.js +452 -0
- package/dist/workflows/dag/frontend-typed-event-transaction.js +180 -0
- package/dist/workflows/dag/frontend-verification-trace.js +52 -17
- package/dist/workflows/dag/frontend-worktree-diff.js +250 -17
- package/dist/workflows/dag/frontend-writer-admission.js +272 -0
- package/dist/workflows/dag/frontend-writer-status.js +244 -0
- package/dist/workflows/dag/init-hybrid.js +1072 -660
- package/dist/workflows/dag/interrupt-request.js +10 -3
- package/dist/workflows/dag/lifecycle.js +3 -3
- package/dist/workflows/dag/node-execution.js +552 -58
- package/dist/workflows/dag/prompt.js +44 -19
- package/dist/workflows/dag/recovery-lease.js +80 -0
- package/dist/workflows/dag/report.js +37 -1
- package/dist/workflows/dag/rerun-feedback.js +124 -1
- package/dist/workflows/dag/rerun-plan.js +144 -5
- package/dist/workflows/dag/rerun-run.js +81 -6
- package/dist/workflows/dag/rerun-task.js +29 -0
- package/dist/workflows/dag/retry-policy.js +318 -8
- package/dist/workflows/dag/runner.js +429 -122
- package/dist/workflows/dag/scheduler.js +114 -16
- package/dist/workflows/dag/structured-output-repair.js +712 -0
- package/dist/workflows/dag/types.js +338 -31
- package/dist/workflows/dag/upstream-artifacts.js +0 -4
- package/dist/workflows/dag/validate.js +92 -32
- package/docs/README.md +2 -0
- package/docs/architecture/evolution.md +45 -0
- package/docs/init-surface.manifest.json +9 -0
- package/docs/operations/README.md +1 -1
- package/docs/operations/local-development-environment.md +21 -0
- package/docs/templates/README.md +2 -1
- package/docs/templates/agent-dag-report.schema.json +8 -2
- package/docs/templates/agent-dag.schema.json +63 -1
- package/docs/templates/backend-test-dag.json +265 -237
- package/docs/templates/frontend-implementation-contract.schema.json +684 -24
- package/docs/templates/frontend-test-case-checklist.md +2 -2
- package/docs/templates/frontend-test-dag.json +8 -10
- package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +1 -1
- package/docs/templates/init-managed-agents.md +13 -3
- package/docs/templates/spec-registry.schema.json +45 -0
- package/harness.json +1 -1
- package/package.json +7 -1
- package/scripts/next-info.mjs +356 -0
- package/scripts/next-publish-gate.mjs +238 -0
- package/scripts/release-source-binding.mjs +251 -0
- package/skills/codebase-scout/SKILL.md +1 -1
- package/skills/fe-test-ui-scout/SKILL.md +65 -0
- package/skills/fe-test-ui-scout/references/ledger-schema.md +62 -0
- package/skills/fe-test-ui-scout/references/recon-protocol.md +54 -0
- package/skills/frontend-bounded-implement/SKILL.md +8 -14
- package/skills/frontend-design-review/SKILL.md +20 -41
- package/skills/frontend-implementation/SKILL.md +3 -4
- package/skills/frontend-implementation/references/design-spec.md +15 -10
- package/skills/frontend-implementation/references/node-contracts.md +23 -13
- package/skills/frontend-review/SKILL.md +22 -14
- package/skills/frontend-review/references/review-findings.md +6 -7
- package/skills/loop-agent/references/command-reference.md +12 -4
- package/skills/loop-agent/references/hybrid-dag.md +8 -1
- package/skills/playwright-cli/SKILL.md +23 -1
- package/skills/playwright-cli-case-generator/SKILL.md +29 -8
- package/dist/worker/console/static/assets/channel-CPF4N7pf.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C2DwA_Y1.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C2DwA_Y1.js +0 -1
- package/dist/worker/console/static/assets/index-DTZOKgAn.js +0 -404
- package/dist/worker/console/static/assets/index-Ya5FE7cD.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-B8FB_cNk.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CrZYaWfx.js +0 -8
- package/dist/worker/console/static-src/operator-chat/activity-rail-presentation.js +0 -73
- package/dist/worker/console/static-src/operator-chat/useActivityRailTransition.js +0 -59
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
* Strong evidence only: package.json deps, config files, source imports.
|
|
4
4
|
* Lockfile-only or directory-name-only must NOT activate adapters.
|
|
5
5
|
*/
|
|
6
|
-
import { readFile, readdir, stat } from "node:fs/promises";
|
|
6
|
+
import { lstat, readFile, readdir, realpath, stat } from "node:fs/promises";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import { z } from "zod";
|
|
9
|
-
import { DEFAULT_OPENSPEC_GOVERNANCE_ROOT, MAX_OPENSPEC_CANDIDATE_PATHS, OPENSPEC_SPEC_DIRS, OPENSPEC_SPEC_EXT_RE, isGovernanceSubtreePath,
|
|
9
|
+
import { DEFAULT_OPENSPEC_GOVERNANCE_ROOT, MAX_OPENSPEC_CANDIDATE_PATHS, OPENSPEC_SPEC_DIRS, OPENSPEC_SPEC_EXT_RE, isGovernanceSubtreePath, isValidFrontendSpecRoot, } from "../shared/openspec-spec.js";
|
|
10
10
|
const classifiedPathSchema = z
|
|
11
11
|
.string()
|
|
12
|
-
.refine((candidate) =>
|
|
12
|
+
.refine((candidate) => OPENSPEC_SPEC_EXT_RE.test(candidate), "classified path must be a repo-relative supported spec file");
|
|
13
13
|
/**
|
|
14
14
|
* Stable semantic partition of `designEvidence.normativePaths`. Keys are a
|
|
15
15
|
* generic directory convention (not project-specific file names); the same
|
|
@@ -81,6 +81,105 @@ async function exists(filePath) {
|
|
|
81
81
|
return false;
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
+
export const FRONTEND_SPEC_REGISTRY_PATH = "ai_workspace/loop-agent/spec-registry.json";
|
|
85
|
+
const SPEC_SIGNAL_RE = /(?:规范|规约|标准|约定|guideline|standard|convention|design[- ]?system|rule|token|component|style|theme)/i;
|
|
86
|
+
const SPEC_EXCLUDE_RE = /(?:^|\/)(?:archive|history|changelog|changes|proposals?|release[-_ ]notes?)(?:\/|$)/i;
|
|
87
|
+
/** Stable, bounded heuristic used only for candidate ordering/classification. */
|
|
88
|
+
export function scoreFrontendSpecCandidate(filePath, contentPreview = "", options = {}) {
|
|
89
|
+
const normalized = filePath.replaceAll("\\", "/");
|
|
90
|
+
const lower = normalized.toLowerCase();
|
|
91
|
+
const reasons = [];
|
|
92
|
+
let score = 0;
|
|
93
|
+
if (options.registryMode === "normative") {
|
|
94
|
+
score += 100;
|
|
95
|
+
reasons.push("registry-normative");
|
|
96
|
+
}
|
|
97
|
+
else if (options.registryMode === "advisory") {
|
|
98
|
+
score += 30;
|
|
99
|
+
reasons.push("registry-advisory");
|
|
100
|
+
}
|
|
101
|
+
if (options.taskRelated) {
|
|
102
|
+
score += 25;
|
|
103
|
+
reasons.push("task-related");
|
|
104
|
+
}
|
|
105
|
+
if (/(?:design-system|guidelines?|standards?|conventions?|rules?|specs?)(?:\/|$)/i.test(lower)) {
|
|
106
|
+
score += 20;
|
|
107
|
+
reasons.push("spec-directory");
|
|
108
|
+
}
|
|
109
|
+
if (/(?:guidelines?|standards?|conventions?|rules?|tokens?|components?|theme|style)/i.test(path.basename(lower))) {
|
|
110
|
+
score += 15;
|
|
111
|
+
reasons.push("spec-filename");
|
|
112
|
+
}
|
|
113
|
+
if (SPEC_SIGNAL_RE.test(contentPreview)) {
|
|
114
|
+
score += 20;
|
|
115
|
+
reasons.push("spec-content");
|
|
116
|
+
}
|
|
117
|
+
if (/(?:^|\n)\s*(?:#{1,6}\s*)?(?:rules?|do|don't|必须|禁止|约定|规范)\b/im.test(contentPreview)) {
|
|
118
|
+
score += 15;
|
|
119
|
+
reasons.push("normative-structure");
|
|
120
|
+
}
|
|
121
|
+
if (SPEC_EXCLUDE_RE.test(lower)) {
|
|
122
|
+
score -= 80;
|
|
123
|
+
reasons.push("excluded-history-path");
|
|
124
|
+
}
|
|
125
|
+
const kind = options.registryMode === "normative" || score >= 80
|
|
126
|
+
? "normative"
|
|
127
|
+
: score >= 35
|
|
128
|
+
? "advisory"
|
|
129
|
+
: "unknown";
|
|
130
|
+
return { path: normalized, kind, score, reasons, source: options.registryMode ? "registry" : "auto-discovered" };
|
|
131
|
+
}
|
|
132
|
+
/** Read the optional project-level registry without making it mandatory. */
|
|
133
|
+
export async function readFrontendSpecRegistry(repoRoot) {
|
|
134
|
+
const raw = await readJson(path.join(repoRoot, FRONTEND_SPEC_REGISTRY_PATH));
|
|
135
|
+
if (!raw || typeof raw !== "object")
|
|
136
|
+
return null;
|
|
137
|
+
const value = raw;
|
|
138
|
+
if (value.version !== 1 || !Array.isArray(value.roots))
|
|
139
|
+
return null;
|
|
140
|
+
const roots = value.roots
|
|
141
|
+
.filter((item) => Boolean(item) && typeof item === "object")
|
|
142
|
+
.map((item) => ({
|
|
143
|
+
path: typeof item.path === "string" ? item.path.replaceAll("\\", "/").replace(/\/+$/, "") : "",
|
|
144
|
+
scope: Array.isArray(item.scope) ? item.scope.filter((v) => typeof v === "string") : undefined,
|
|
145
|
+
mode: (item.mode === "normative" || item.mode === "advisory" ? item.mode : undefined),
|
|
146
|
+
description: typeof item.description === "string" ? item.description : undefined,
|
|
147
|
+
}))
|
|
148
|
+
.filter((item) => isValidFrontendSpecRoot(item.path));
|
|
149
|
+
return {
|
|
150
|
+
version: 1,
|
|
151
|
+
roots,
|
|
152
|
+
discovery: value.discovery && typeof value.discovery === "object" ? value.discovery : undefined,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Resolve only unambiguous symlink-root aliases. The physical target is never
|
|
157
|
+
* emitted as a candidate path: it merely lets task-source wording such as
|
|
158
|
+
* `ai_doc/rules.md` resolve to the stable repo-local path `ai_workspace/rules.md`.
|
|
159
|
+
*/
|
|
160
|
+
export async function resolveFrontendSpecRootAliases(repoRoot, specRoots) {
|
|
161
|
+
const candidates = [];
|
|
162
|
+
for (const logicalRoot of specRoots) {
|
|
163
|
+
const full = path.join(repoRoot, logicalRoot);
|
|
164
|
+
try {
|
|
165
|
+
if (!(await lstat(full)).isSymbolicLink())
|
|
166
|
+
continue;
|
|
167
|
+
const alias = path.basename(await realpath(full));
|
|
168
|
+
if (alias && alias !== path.basename(logicalRoot)) {
|
|
169
|
+
candidates.push({ alias, logicalRoot });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
// Missing/broken links are handled by normal candidate discovery drift.
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const counts = new Map();
|
|
177
|
+
for (const item of candidates)
|
|
178
|
+
counts.set(item.alias, (counts.get(item.alias) ?? 0) + 1);
|
|
179
|
+
return candidates
|
|
180
|
+
.filter((item) => counts.get(item.alias) === 1)
|
|
181
|
+
.sort((a, b) => a.alias.localeCompare(b.alias));
|
|
182
|
+
}
|
|
84
183
|
/**
|
|
85
184
|
* Resolve the governance subtree root from harness.json.governanceRoot.
|
|
86
185
|
* Falls back to DEFAULT_OPENSPEC_GOVERNANCE_ROOT when the manifest is missing,
|
|
@@ -105,27 +204,52 @@ export async function listOpenspecDiscovery(repoRoot, options = {}) {
|
|
|
105
204
|
.filter(Boolean);
|
|
106
205
|
const collected = [];
|
|
107
206
|
let excludedGovernanceCount = 0;
|
|
207
|
+
let scannedFiles = 0;
|
|
208
|
+
let scannedBytes = 0;
|
|
209
|
+
const warnings = [];
|
|
210
|
+
const maxDepth = options.maxDepth ?? 4;
|
|
211
|
+
const maxFiles = options.maxFiles ?? MAX_OPENSPEC_CANDIDATE_PATHS;
|
|
212
|
+
const maxBytes = options.maxBytes ?? 1024 * 1024;
|
|
213
|
+
const timeoutMs = options.timeoutMs ?? 2000;
|
|
214
|
+
const startedAt = Date.now();
|
|
215
|
+
// Metadata / bookkeeping directories that are not normative design specs:
|
|
216
|
+
// changelogs, ADR/history archives, proposals under review, and scratch
|
|
217
|
+
// state. Reading them inflates the candidate list (and the plan prompt)
|
|
218
|
+
// with files no component selection should cite. A task that genuinely
|
|
219
|
+
// needs one can still reference it explicitly — the prewrite gate
|
|
220
|
+
// candidates come from this discovery, but explicit task-source citations
|
|
221
|
+
// flow through openspecMandatoryPaths independently.
|
|
222
|
+
const DEFAULT_EXCLUDED_SPEC_PREFIXES = [
|
|
223
|
+
"openspec/changes",
|
|
224
|
+
"openspec/project-specs/changes",
|
|
225
|
+
"openspec/project-specs/changelog",
|
|
226
|
+
"openspec/project-specs/history",
|
|
227
|
+
"openspec/project-specs/archive",
|
|
228
|
+
"openspec/project-specs/decisions",
|
|
229
|
+
"openspec/project-specs/proposals",
|
|
230
|
+
];
|
|
108
231
|
const isExcluded = (rel) => {
|
|
109
232
|
if (isGovernanceSubtreePath(rel, governanceRoot))
|
|
110
233
|
return true;
|
|
234
|
+
if (DEFAULT_EXCLUDED_SPEC_PREFIXES.some((prefix) => rel === prefix || rel.startsWith(prefix + "/"))) {
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
111
237
|
return extraExcludedPrefixes.some((prefix) => rel === prefix || rel.startsWith(prefix + "/"));
|
|
112
238
|
};
|
|
113
|
-
for (const specDir of OPENSPEC_SPEC_DIRS) {
|
|
239
|
+
for (const specDir of options.specRoots ?? [...OPENSPEC_SPEC_DIRS]) {
|
|
114
240
|
const root = path.join(repoRoot, specDir);
|
|
115
241
|
if (!(await exists(root)))
|
|
116
242
|
continue;
|
|
117
243
|
await walk(root, 0);
|
|
118
244
|
}
|
|
119
|
-
//
|
|
120
|
-
//
|
|
245
|
+
// Keep the established default-root priority for deterministic compatibility;
|
|
246
|
+
// configured extra roots are appended in lexical order.
|
|
121
247
|
const unique = [...new Set(collected)];
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
.
|
|
125
|
-
const aiWorkspacePaths = unique
|
|
126
|
-
.filter((candidate) => candidate.startsWith("ai_workspace/"))
|
|
248
|
+
const defaultPaths = OPENSPEC_SPEC_DIRS.flatMap((root) => unique.filter((candidate) => candidate.startsWith(root + "/")).sort());
|
|
249
|
+
const customPaths = unique
|
|
250
|
+
.filter((candidate) => !defaultPaths.includes(candidate))
|
|
127
251
|
.sort();
|
|
128
|
-
const deduped = [...
|
|
252
|
+
const deduped = [...defaultPaths, ...customPaths];
|
|
129
253
|
const truncated = deduped.length > MAX_OPENSPEC_CANDIDATE_PATHS;
|
|
130
254
|
const paths = truncated
|
|
131
255
|
? deduped.slice(0, MAX_OPENSPEC_CANDIDATE_PATHS)
|
|
@@ -136,10 +260,19 @@ export async function listOpenspecDiscovery(repoRoot, options = {}) {
|
|
|
136
260
|
excludedGovernanceCount,
|
|
137
261
|
truncated,
|
|
138
262
|
maxCandidates: MAX_OPENSPEC_CANDIDATE_PATHS,
|
|
263
|
+
warnings,
|
|
139
264
|
};
|
|
140
265
|
async function walk(dir, depth) {
|
|
141
|
-
if (
|
|
266
|
+
if (Date.now() - startedAt >= timeoutMs) {
|
|
267
|
+
if (!warnings.includes("timeout-exceeded"))
|
|
268
|
+
warnings.push("timeout-exceeded");
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
if (depth > maxDepth) {
|
|
272
|
+
if (!warnings.includes("max-depth-exceeded"))
|
|
273
|
+
warnings.push("max-depth-exceeded");
|
|
142
274
|
return;
|
|
275
|
+
}
|
|
143
276
|
let entries = [];
|
|
144
277
|
try {
|
|
145
278
|
entries = (await readdir(dir)).sort();
|
|
@@ -148,12 +281,30 @@ export async function listOpenspecDiscovery(repoRoot, options = {}) {
|
|
|
148
281
|
return;
|
|
149
282
|
}
|
|
150
283
|
for (const name of entries) {
|
|
284
|
+
if (Date.now() - startedAt >= timeoutMs) {
|
|
285
|
+
if (!warnings.includes("timeout-exceeded"))
|
|
286
|
+
warnings.push("timeout-exceeded");
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
if (scannedFiles >= maxFiles) {
|
|
290
|
+
if (!warnings.includes("max-files-exceeded"))
|
|
291
|
+
warnings.push("max-files-exceeded");
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
151
294
|
const full = path.join(dir, name);
|
|
152
295
|
try {
|
|
153
296
|
const info = await stat(full);
|
|
154
297
|
if (info.isDirectory())
|
|
155
298
|
await walk(full, depth + 1);
|
|
156
299
|
else if (OPENSPEC_SPEC_EXT_RE.test(name)) {
|
|
300
|
+
const size = info.size;
|
|
301
|
+
if (scannedBytes + size > maxBytes) {
|
|
302
|
+
if (!warnings.includes("max-bytes-exceeded"))
|
|
303
|
+
warnings.push("max-bytes-exceeded");
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
scannedFiles++;
|
|
307
|
+
scannedBytes += size;
|
|
157
308
|
const rel = path.relative(repoRoot, full).replace(/\\/g, "/");
|
|
158
309
|
if (isExcluded(rel)) {
|
|
159
310
|
excludedGovernanceCount++;
|
|
@@ -241,6 +392,14 @@ function emptyClassified() {
|
|
|
241
392
|
* project-specific file names. Basename stems use word-boundary matching so
|
|
242
393
|
* e.g. `*api*` (case-insensitive) maps to `rule.api` without hardcoding names.
|
|
243
394
|
*/
|
|
395
|
+
/** True when any path segment (other than the file itself) carries a
|
|
396
|
+
* component/theme kind marker. Directory-organized libraries such as
|
|
397
|
+
* `ui/ucp-components-md/AdvancedSearch.md` rely on this; templates/ and
|
|
398
|
+
* rules/ are handled by their own branches before this fallback runs. */
|
|
399
|
+
function segmentMatchesComponentKind(segments) {
|
|
400
|
+
const dirSegments = segments.slice(0, -1);
|
|
401
|
+
return dirSegments.some((segment) => /\bcomponents?\b/.test(segment.toLowerCase()));
|
|
402
|
+
}
|
|
244
403
|
function classifyOpenspecPaths(paths) {
|
|
245
404
|
const classified = emptyClassified();
|
|
246
405
|
const ruleStem = (basename) => {
|
|
@@ -260,7 +419,7 @@ function classifyOpenspecPaths(paths) {
|
|
|
260
419
|
return "other";
|
|
261
420
|
};
|
|
262
421
|
for (const candidate of paths) {
|
|
263
|
-
if (!
|
|
422
|
+
if (!OPENSPEC_SPEC_EXT_RE.test(candidate))
|
|
264
423
|
continue;
|
|
265
424
|
const segments = candidate.split("/");
|
|
266
425
|
const basename = segments[segments.length - 1] ?? "";
|
|
@@ -279,8 +438,17 @@ function classifyOpenspecPaths(paths) {
|
|
|
279
438
|
classified.theme.push(candidate);
|
|
280
439
|
else if (/\bcomponents?\b/.test(lower))
|
|
281
440
|
classified.component.push(candidate);
|
|
282
|
-
else
|
|
441
|
+
else if (segmentMatchesComponentKind(segments)) {
|
|
442
|
+
// Directory-organized component libraries (e.g.
|
|
443
|
+
// ui/ucp-components-md/AdvancedSearch.md) carry the kind in the
|
|
444
|
+
// parent directory name, not the file name. The parent-dir rule
|
|
445
|
+
// is a fallback so such layouts still land in the component
|
|
446
|
+
// bucket instead of being dropped into uiOther.
|
|
447
|
+
classified.component.push(candidate);
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
283
450
|
classified.uiOther.push(candidate);
|
|
451
|
+
}
|
|
284
452
|
}
|
|
285
453
|
else {
|
|
286
454
|
classified.advisoryOther.push(candidate);
|
|
@@ -325,6 +493,7 @@ function toOpenspecDiscovery(discovery, designEvidence) {
|
|
|
325
493
|
excludedGovernanceCount: discovery.excludedGovernanceCount,
|
|
326
494
|
truncated: discovery.truncated,
|
|
327
495
|
maxCandidates: discovery.maxCandidates,
|
|
496
|
+
warnings: discovery.warnings,
|
|
328
497
|
};
|
|
329
498
|
}
|
|
330
499
|
export function buildAdapterGuidance(capability) {
|
|
@@ -361,13 +530,25 @@ export function buildAdapterGuidance(capability) {
|
|
|
361
530
|
* Discover frontend project capability from repo root.
|
|
362
531
|
* Strong evidence: package.json direct deps + config/bootstrap files.
|
|
363
532
|
*/
|
|
364
|
-
export async function discoverFrontendProjectCapability(repoRoot) {
|
|
533
|
+
export async function discoverFrontendProjectCapability(repoRoot, options = {}) {
|
|
534
|
+
const registry = await readFrontendSpecRegistry(repoRoot);
|
|
535
|
+
const registryRoots = registry?.roots
|
|
536
|
+
.filter((root) => !root.scope || root.scope.includes("frontend"))
|
|
537
|
+
.map((root) => root.path) ?? [];
|
|
538
|
+
const discoveryOptions = {
|
|
539
|
+
...options,
|
|
540
|
+
specRoots: [...new Set([...OPENSPEC_SPEC_DIRS, ...(options.specRoots ?? []), ...registryRoots])],
|
|
541
|
+
maxDepth: registry?.discovery?.maxDepth ?? options.maxDepth,
|
|
542
|
+
maxFiles: registry?.discovery?.maxFiles ?? options.maxFiles,
|
|
543
|
+
maxBytes: registry?.discovery?.maxBytes ?? options.maxBytes,
|
|
544
|
+
timeoutMs: registry?.discovery?.timeoutMs ?? options.timeoutMs,
|
|
545
|
+
};
|
|
365
546
|
const evidencePaths = [];
|
|
366
547
|
const reasons = [];
|
|
367
548
|
const pkgPath = path.join(repoRoot, "package.json");
|
|
368
549
|
const pkgRaw = await readJson(pkgPath);
|
|
369
550
|
if (!pkgRaw || typeof pkgRaw !== "object") {
|
|
370
|
-
const discovery = await listOpenspecDiscovery(repoRoot);
|
|
551
|
+
const discovery = await listOpenspecDiscovery(repoRoot, discoveryOptions);
|
|
371
552
|
const designEvidence = buildDesignEvidence(discovery.paths);
|
|
372
553
|
const base = {
|
|
373
554
|
schemaVersion: 1,
|
|
@@ -540,7 +721,7 @@ export async function discoverFrontendProjectCapability(repoRoot) {
|
|
|
540
721
|
router = router ?? "next-router";
|
|
541
722
|
if (hasDep(deps, "vue-router"))
|
|
542
723
|
router = "vue-router";
|
|
543
|
-
const discovery = await listOpenspecDiscovery(repoRoot);
|
|
724
|
+
const discovery = await listOpenspecDiscovery(repoRoot, discoveryOptions);
|
|
544
725
|
const designEvidence = buildDesignEvidence(discovery.paths);
|
|
545
726
|
if (discovery.paths.length)
|
|
546
727
|
evidencePaths.push(...discovery.paths.slice(0, 5));
|
|
@@ -55,6 +55,25 @@ export function observationBlocksPrepare(state) {
|
|
|
55
55
|
}
|
|
56
56
|
return null;
|
|
57
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* 精确 identity 绑定的人类 resolution 判定(AC-001 / AC-005):decidedBy==='human'、
|
|
60
|
+
* decision trim 后非空、sourceRef 与冲突 id 精确匹配三者同时成立才算有效;model/
|
|
61
|
+
* system 伪造判定、stale sourceRef(不匹配当前冲突 identity)或空/空白 decision 一律
|
|
62
|
+
* 不解锁。resolution 信息取自 draft 层(不重排流水线,listPrepareGaps 仍先于 ledger
|
|
63
|
+
* 构建执行)。
|
|
64
|
+
*/
|
|
65
|
+
export function hasValidHumanConflictResolution(draft, conflictId) {
|
|
66
|
+
for (const resolution of draft.sourceResolutions ?? []) {
|
|
67
|
+
if (resolution.sourceRef !== conflictId)
|
|
68
|
+
continue;
|
|
69
|
+
if (resolution.decidedBy !== "human")
|
|
70
|
+
continue;
|
|
71
|
+
if (!resolution.decision?.trim())
|
|
72
|
+
continue;
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
58
77
|
export function listPrepareGaps(input) {
|
|
59
78
|
const gaps = [];
|
|
60
79
|
const risks = [];
|
|
@@ -108,6 +127,12 @@ export function listPrepareGaps(input) {
|
|
|
108
127
|
}
|
|
109
128
|
if (input.acceptanceConflicts && input.acceptanceConflicts.length > 0) {
|
|
110
129
|
for (const conflict of input.acceptanceConflicts) {
|
|
130
|
+
// AC-001:有效 human resolution(human + 非空 decision + sourceRef 与冲突 id
|
|
131
|
+
// 精确匹配)解除该 id 的 legacy blocking gap;无 resolution 或 resolution 不
|
|
132
|
+
// 满足三条件(AC-005:伪造判定 / stale sourceRef / 空 decision)仍发射阻断。
|
|
133
|
+
if (hasValidHumanConflictResolution(input.draft, conflict.id)) {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
111
136
|
gaps.push({
|
|
112
137
|
code: "ACCEPTANCE_ID_CONFLICT",
|
|
113
138
|
level: "blocking",
|
|
@@ -246,3 +271,166 @@ export function hasBlockingGaps(gaps) {
|
|
|
246
271
|
export function hasBlockingRisks(risks) {
|
|
247
272
|
return risks.some((risk) => risk.level === "blocking");
|
|
248
273
|
}
|
|
274
|
+
/**
|
|
275
|
+
* Source fidelity ledger 分级 fail-closed 阻断(AC-003 / AC-FIDELITY-003):
|
|
276
|
+
* 高风险未映射/排除、unresolved、冲突返回稳定错误并阻断;低风险 context-only 仅当
|
|
277
|
+
* 理由完整且独立 reviewer pass 后继续;duplicate 保留全部来源(warning 可见)。
|
|
278
|
+
* 仅 taskKind 为 frontend-implementation 时全量阻断,其他 taskKind 保持 warning。
|
|
279
|
+
*/
|
|
280
|
+
export function appendLedgerGaps(input) {
|
|
281
|
+
const { gaps, risks } = input;
|
|
282
|
+
if (!input.ledgerSummary?.present)
|
|
283
|
+
return;
|
|
284
|
+
const blocking = input.taskKind === "frontend-implementation";
|
|
285
|
+
const level = blocking ? "blocking" : "warning";
|
|
286
|
+
const summary = input.ledgerSummary;
|
|
287
|
+
if (summary.unresolvedCount > 0) {
|
|
288
|
+
const gap = {
|
|
289
|
+
code: "SOURCE_FIDELITY_UNRESOLVED",
|
|
290
|
+
level,
|
|
291
|
+
message: `ledger 有 ${summary.unresolvedCount} 个 unresolved fragment(需人工 sourceResolutions 确认后才可继续)`,
|
|
292
|
+
details: {
|
|
293
|
+
fragmentIds: summary.exclusions
|
|
294
|
+
.filter((e) => /^REQ-SRC-/.test(e.fragmentId) || e.fragmentId.startsWith("AC-"))
|
|
295
|
+
.slice(0, 10)
|
|
296
|
+
.map((e) => e.fragmentId),
|
|
297
|
+
},
|
|
298
|
+
selfHealing: blocking
|
|
299
|
+
? {
|
|
300
|
+
action: "reimport-prd",
|
|
301
|
+
steps: [
|
|
302
|
+
"检查原始 PRD 中无法映射到 canonical AC 的可执行片段(unresolved)",
|
|
303
|
+
"补齐/修正 PRD 使每个可执行片段都能绑定 AC,或由人工在 sourceResolutions 中确认排除理由",
|
|
304
|
+
"重新 importPrd(同 taskId)后 task advance 重建 ledger",
|
|
305
|
+
],
|
|
306
|
+
}
|
|
307
|
+
: undefined,
|
|
308
|
+
};
|
|
309
|
+
gaps.push(gap);
|
|
310
|
+
risks.push({
|
|
311
|
+
code: "SOURCE_FIDELITY_UNRESOLVED",
|
|
312
|
+
level: blocking ? "blocking" : "elevated",
|
|
313
|
+
message: gap.message,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
if (summary.conflictCount > 0) {
|
|
317
|
+
const gap = {
|
|
318
|
+
code: "SOURCE_FIDELITY_CONFLICT",
|
|
319
|
+
level,
|
|
320
|
+
message: `ledger 检测到 ${summary.conflictCount} 个 AC 文本冲突(同一 ID 不同文本)`,
|
|
321
|
+
selfHealing: blocking
|
|
322
|
+
? {
|
|
323
|
+
action: "reimport-prd",
|
|
324
|
+
steps: [
|
|
325
|
+
"统一同一 AC ID 的文本后重新 importPrd(同 taskId)",
|
|
326
|
+
"或由人工在 sourceResolutions 中声明冲突取舍(conflict → decision)",
|
|
327
|
+
],
|
|
328
|
+
}
|
|
329
|
+
: undefined,
|
|
330
|
+
};
|
|
331
|
+
gaps.push(gap);
|
|
332
|
+
risks.push({
|
|
333
|
+
code: "SOURCE_FIDELITY_CONFLICT",
|
|
334
|
+
level: blocking ? "blocking" : "elevated",
|
|
335
|
+
message: gap.message,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
if (summary.failureCode === "SOURCE_FIDELITY_HIGH_RISK_UNCOVERED") {
|
|
339
|
+
const gap = {
|
|
340
|
+
code: "SOURCE_FIDELITY_HIGH_RISK_UNCOVERED",
|
|
341
|
+
level,
|
|
342
|
+
message: "ledger 存在高风险 acceptance 片段未映射到 canonical requirement",
|
|
343
|
+
selfHealing: blocking
|
|
344
|
+
? {
|
|
345
|
+
action: "reimport-prd",
|
|
346
|
+
steps: [
|
|
347
|
+
"为每个未覆盖的高风险片段补齐 canonical AC(或人工 sourceResolutions 确认)",
|
|
348
|
+
"重新 importPrd(同 taskId)后 task advance 重建 ledger",
|
|
349
|
+
],
|
|
350
|
+
}
|
|
351
|
+
: undefined,
|
|
352
|
+
};
|
|
353
|
+
gaps.push(gap);
|
|
354
|
+
risks.push({
|
|
355
|
+
code: "SOURCE_FIDELITY_HIGH_RISK_UNCOVERED",
|
|
356
|
+
level: blocking ? "blocking" : "elevated",
|
|
357
|
+
message: gap.message,
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
for (const error of input.ledgerErrors ?? []) {
|
|
361
|
+
if (error.code === "SOURCE_FIDELITY_LEDGER_MISSING") {
|
|
362
|
+
const gap = {
|
|
363
|
+
code: "SOURCE_FIDELITY_LEDGER_MISSING",
|
|
364
|
+
level,
|
|
365
|
+
message: error.message,
|
|
366
|
+
};
|
|
367
|
+
gaps.push(gap);
|
|
368
|
+
}
|
|
369
|
+
// AC-BLOCK-001:canonical requirement 缺少 fragment/sourceRef 证据、来源引用
|
|
370
|
+
// 无效、fragment 无效均为 blocking gap(frontend task),保证 prepare/apply
|
|
371
|
+
// 以稳定码 fail closed(appendLedgerGaps 是 prepare 补洞的唯一入口)。
|
|
372
|
+
if (error.code === "SOURCE_FIDELITY_REQUIREMENT_NOT_COVERED" ||
|
|
373
|
+
error.code === "SOURCE_FIDELITY_SOURCE_REF_INVALID" ||
|
|
374
|
+
error.code === "SOURCE_FIDELITY_FRAGMENT_INVALID") {
|
|
375
|
+
const gap = {
|
|
376
|
+
code: error.code,
|
|
377
|
+
level,
|
|
378
|
+
message: error.message,
|
|
379
|
+
selfHealing: blocking
|
|
380
|
+
? {
|
|
381
|
+
action: "reimport-prd",
|
|
382
|
+
steps: [
|
|
383
|
+
"检查 PRD 中每个 canonical requirement 是否都有可审计的 fragment/sourceRef 证据",
|
|
384
|
+
"补齐缺失的验收标准映射或修正引用后重新 importPrd(同 taskId)",
|
|
385
|
+
"重新 importPrd(同 taskId)后 task advance 重建 ledger",
|
|
386
|
+
],
|
|
387
|
+
}
|
|
388
|
+
: undefined,
|
|
389
|
+
};
|
|
390
|
+
gaps.push(gap);
|
|
391
|
+
risks.push({
|
|
392
|
+
code: error.code,
|
|
393
|
+
level: blocking ? "blocking" : "elevated",
|
|
394
|
+
message: error.message,
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
if (error.code === "SOURCE_FIDELITY_CONTEXT_ONLY_UNREVIEWED" &&
|
|
398
|
+
blocking) {
|
|
399
|
+
// 低风险 context-only 仅独立 reviewer pass 放行;未通过则 frontend 任务阻断。
|
|
400
|
+
const gap = {
|
|
401
|
+
code: "SOURCE_FIDELITY_CONTEXT_ONLY_UNREVIEWED",
|
|
402
|
+
level: "blocking",
|
|
403
|
+
message: error.message,
|
|
404
|
+
selfHealing: {
|
|
405
|
+
action: "reimport-prd",
|
|
406
|
+
steps: [
|
|
407
|
+
"检查 context-only 片段是否缺少完整排除理由,或由人工在 sourceResolutions 中确认",
|
|
408
|
+
"重新 importPrd(同 taskId)后 task advance 重建 ledger 并复核 reviewer 判定",
|
|
409
|
+
],
|
|
410
|
+
},
|
|
411
|
+
};
|
|
412
|
+
gaps.push(gap);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
// 低风险 context-only:记录并提示排除理由/独立 reviewer 要求(不阻断普通任务)。
|
|
416
|
+
if (summary.contextOnlyCount > 0) {
|
|
417
|
+
gaps.push({
|
|
418
|
+
code: "SOURCE_FIDELITY_CONTEXT_ONLY_UNREVIEWED",
|
|
419
|
+
level: "warning",
|
|
420
|
+
message: `ledger 有 ${summary.contextOnlyCount} 个 context-only fragment(低风险排除;frontend 任务需完整理由且独立 reviewer pass)`,
|
|
421
|
+
details: {
|
|
422
|
+
fragmentIds: summary.exclusions
|
|
423
|
+
.filter((e) => e.fragmentId.startsWith("REQ-SRC-"))
|
|
424
|
+
.slice(0, 10)
|
|
425
|
+
.map((e) => e.fragmentId),
|
|
426
|
+
},
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
if (summary.duplicateCount > 0) {
|
|
430
|
+
gaps.push({
|
|
431
|
+
code: "SOURCE_FIDELITY_DUPLICATE_PRESERVED",
|
|
432
|
+
level: "warning",
|
|
433
|
+
message: `ledger 保留 ${summary.duplicateCount} 个 duplicate fragment(duplicate 保留全部来源)`,
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}
|