@tea-agent/loop-agent 0.39.0-beta.0 → 0.39.0-beta.10
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 +248 -95
- 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 +2306 -61
- package/dist/executors/pi-executor.js +7 -0
- 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 +180 -33
- package/dist/executors/shell-executor.js +1249 -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 +62 -4
- 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 +264 -16
- package/dist/worker/console/chat/provider-error.js +98 -0
- package/dist/worker/console/chat/routes.js +298 -32
- 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 +14 -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-BAsjg7ew.js} +1 -1
- package/dist/worker/console/static/assets/{arc-Bj2M8iO5.js → arc-CyIlrwZw.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CLlXe4-6.js → architectureDiagram-T3A2C74G-Bo4tobhH.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-psEp5xH0.js → blockDiagram-VBNYF7ZC-Dfpow9gu.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DeKcOCGJ.js → c4Diagram-5PPSVZJV-DPmzG_B4.js} +1 -1
- package/dist/worker/console/static/assets/channel-DSrJAn0-.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-K0C744p1.js → chunk-2GRJ4B5K-pKQpB1UW.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-xbNw4J9-.js → chunk-2Q5K7J3B-5n9YYquZ.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DjLaSDUO.js → chunk-5RXB4S5H-DP3CtA2a.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CtiPlKel.js → chunk-5VM5RSS4-DYxMJPUy.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DZtolir7.js → chunk-6Q2QTUOP-CS3D50Io.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CPcdNeew.js → chunk-GF5L2VYU-gSdDfo5_.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-NgHc6V37.js → chunk-JWPE2WC7-A48q0ZNt.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DQ_T-hg8.js → chunk-KBJHAD2P-ow8Mm3DY.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DgLsxcVP.js → chunk-RYQCIY6F-CMKfLWYE.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-UrVoM9z1.js → chunk-XXDRQBXY-BiTBdQPX.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DeMmPl0_.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DeMmPl0_.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CLkYvTb3.js → cose-bilkent-JH36ORCC-Cm7exRz_.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BwT_xKrE.js → cynefin-VYW2F7L2-D9O5qGOu.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Bi9tuzif.js → cynefinDiagram-MW4NZA55-CkvQnako.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BCIqkWBV.js → dagre-VZM6K2ZE-B1rzp7CB.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Dip6l9_Z.js → diagram-7IWD3JNH-BP07Ipcc.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B-xkh_wK.js → diagram-B4RE2ZJO-BlPzweQb.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DZO0kTt_.js → diagram-LBJQPF4R-CxPa7n0S.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D6ZplNbZ.js → diagram-Q27KOJAE-CMTZs53A.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BPekbhoS.js → diagram-UB23O5K3-CxG_X52r.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BtmaJpK5.js → ebnfDiagram-BXEA7PRR-D8axtJM0.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-6DS0Xc44.js → erDiagram-JOGREHBK-DIsGzn7b.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CihnROSm.js → flowDiagram-UKHOOZJN-CU9zkDG0.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CZC4zOE2.js → ganttDiagram-PKOTCBZU-DCjTmz5P.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Dq1fhzGN.js → gitGraphDiagram-DS77QQ5N-BksdmFzg.js} +1 -1
- package/dist/worker/console/static/assets/index-B-O5ChYq.css +1 -0
- package/dist/worker/console/static/assets/index-CfnY8AQx.js +407 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DlSl4BGx.js → infoDiagram-6WML65LV-CGzzzJtL.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-snnzFl_U.js → ishikawaDiagram-WSZJBQD7-BdseB80N.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-BDE7qpMx.js → journeyDiagram-NVQOT4AX-DM9gsgha.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CD2Ci04G.js → kanban-definition-27J2QSJJ-3q-_9R2b.js} +1 -1
- package/dist/worker/console/static/assets/{linear-DdnapKIH.js → linear-B8N-Mxsd.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BVjAT9b8.js → mermaid.core-EPIHUUsu.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-D0yJk-zA.js → mindmap-definition-FAOFIHXS-CxwO12aa.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BVo9tFP-.js → pegDiagram-VL7TDLO6-bPqbVX6Y.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DnOV-mZ_.js → pieDiagram-7S7Q4E2Y-KzqIvzeX.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-CzUJo58i.js → quadrantDiagram-CIZ2JOQS-CyofpWiV.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Bvikrolh.js → railroadDiagram-AXF67PYL-cTWJvLTP.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DtaSVCap.js → requirementDiagram-LRYGKXZP-BK--q27B.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-C2c9A0wW.js → sankeyDiagram-W5VNT64P-C0SDOHXK.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DmXJcU7r.js → sequenceDiagram-SI44F4Z6-Cong6YIo.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-B4GUFW92.js → sizeCapture-X5ZJPWSS-DUVkgZx8.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Dsad2MXf.js → stateDiagram-OKZ733FA-C7XgxuZg.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Dzbnd5oX.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DGq48Fbi.js → swimlanes-SLNWSIFB-D9Ayvwcy.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BY0hHk6G.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CSSi6OFf.js → timeline-definition-Z64GVDOM-Byb000cH.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BqyzPrWv.js → vennDiagram-T6HMQDX7-DMLegPt4.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DlznVb6S.js → wardleyDiagram-T6FBY63Y-ChMMeOsP.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CfBcgJ_K.js → xychartDiagram-ELKLHX3M-bEMEPisW.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 +140 -8
- 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/turn-submission.js +16 -3
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +168 -72
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +102 -21
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +184 -4
- 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 +337 -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 +10 -5
- 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 +1 -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 +534 -178
- package/dist/workflows/dag/frontend-plan-render.js +24 -0
- package/dist/workflows/dag/frontend-prewrite-gate.js +371 -393
- 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 +984 -618
- package/dist/workflows/dag/interrupt-request.js +10 -3
- package/dist/workflows/dag/lifecycle.js +3 -3
- package/dist/workflows/dag/node-execution.js +468 -34
- 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 +321 -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 +45 -1
- 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 +3 -3
- 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
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface DagNodeRemark {
|
|
2
|
+
failed: boolean;
|
|
3
|
+
text: string;
|
|
4
|
+
isErrorSummary: boolean;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function resolveNodeRemark(node: {
|
|
8
|
+
status?: string | null;
|
|
9
|
+
errorPreview?: string | null;
|
|
10
|
+
label?: string | null;
|
|
11
|
+
nodeId: string;
|
|
12
|
+
rank?: string | number | null;
|
|
13
|
+
} | null | undefined): DagNodeRemark;
|
|
@@ -70,6 +70,7 @@ import {
|
|
|
70
70
|
dashboardVisibleFeatures,
|
|
71
71
|
findDagForRun,
|
|
72
72
|
} from "../dag-model.js";
|
|
73
|
+
import { describeDagNodePurpose } from "../dag-node-purpose.js";
|
|
73
74
|
import { layoutDag } from "../dag-layout.js";
|
|
74
75
|
import {
|
|
75
76
|
showView,
|
|
@@ -90,6 +91,7 @@ import {
|
|
|
90
91
|
closeDagInspector,
|
|
91
92
|
selectDagNode,
|
|
92
93
|
renderDagInspector,
|
|
94
|
+
mergeNodeProcess,
|
|
93
95
|
} from "./dag-inspector.js";
|
|
94
96
|
import { mergeSessionEvents } from "./session-timeline.js";
|
|
95
97
|
|
|
@@ -135,7 +137,7 @@ export function extractFirstErrorSummary(errorPreview) {
|
|
|
135
137
|
return effective ?? "";
|
|
136
138
|
}
|
|
137
139
|
|
|
138
|
-
/** Resolve 备注 cell content for a DAG node (failed → summary; else
|
|
140
|
+
/** Resolve 备注 cell content for a DAG node (failed → summary; else purpose). */
|
|
139
141
|
export function resolveNodeRemark(node) {
|
|
140
142
|
const failed = isNodeFailed(node?.status);
|
|
141
143
|
if (failed) {
|
|
@@ -146,10 +148,10 @@ export function resolveNodeRemark(node) {
|
|
|
146
148
|
isErrorSummary: Boolean(summary),
|
|
147
149
|
};
|
|
148
150
|
}
|
|
149
|
-
const
|
|
151
|
+
const purpose = describeDagNodePurpose(node?.nodeId, node);
|
|
150
152
|
return {
|
|
151
153
|
failed: false,
|
|
152
|
-
text:
|
|
154
|
+
text: purpose || "—",
|
|
153
155
|
isErrorSummary: false,
|
|
154
156
|
};
|
|
155
157
|
}
|
|
@@ -362,7 +364,7 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
362
364
|
"page-action-btn",
|
|
363
365
|
"在操作台中止此运行 →",
|
|
364
366
|
);
|
|
365
|
-
interruptLink.href = `/#/recovery?dagRunId=${encodeURIComponent(dag.dagRunId)}&intent=interrupt`;
|
|
367
|
+
interruptLink.href = `/#/operate/recovery?dagRunId=${encodeURIComponent(dag.dagRunId)}&intent=interrupt`;
|
|
366
368
|
interruptLink.setAttribute("data-testid", "inspect-dag-interrupt-link");
|
|
367
369
|
interruptLink.title = "只跳转到操作台 Recovery,不会在观测页执行中止";
|
|
368
370
|
primaryEntries.push(["协作中止", interruptLink]);
|
|
@@ -451,13 +453,19 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
451
453
|
nodesEl.scrollLeft = nodesScrollLeft;
|
|
452
454
|
|
|
453
455
|
if (uiState.dagInspectorOpen && uiState.selectedDagNodeId) {
|
|
456
|
+
const selectedNode = nodes.find(
|
|
457
|
+
(node) => node.nodeId === uiState.selectedDagNodeId,
|
|
458
|
+
);
|
|
454
459
|
if (!uiState.sessionEventIdentity) {
|
|
455
460
|
uiState.sessionEventIdentity = makeSessionEventIdentity(
|
|
456
461
|
dagRunId,
|
|
457
462
|
uiState.selectedDagNodeId,
|
|
458
463
|
);
|
|
459
464
|
}
|
|
460
|
-
await
|
|
465
|
+
await Promise.all([
|
|
466
|
+
mergeSessionEvents(dagRunId, uiState.selectedDagNodeId),
|
|
467
|
+
selectedNode ? mergeNodeProcess(dagRunId, selectedNode) : Promise.resolve(),
|
|
468
|
+
]);
|
|
461
469
|
}
|
|
462
470
|
renderDagInspector(
|
|
463
471
|
dagRunId,
|
|
@@ -474,6 +482,7 @@ export function startDagPolling(dagRunId, initial = true) {
|
|
|
474
482
|
const active = await renderDagDetail(dagRunId, isInitial);
|
|
475
483
|
if (generation !== uiState.pollingGeneration || !active) return;
|
|
476
484
|
const delay = detailPollDelay("running", isPageVisible());
|
|
485
|
+
if (delay == null) return;
|
|
477
486
|
uiState.dagPollTimer = setTimeout(() => {
|
|
478
487
|
uiState.dagPollTimer = null;
|
|
479
488
|
void poll(false);
|
|
@@ -84,7 +84,7 @@ let dashboardLoadingNode = null;
|
|
|
84
84
|
|
|
85
85
|
/**
|
|
86
86
|
* 「昨夜班次」条带:夜间结果在观测侧的唯一一级呈现(替代旧「夜间任务」tab)。
|
|
87
|
-
* 管理动作(晨审处置)始终落在操作侧 /#/night,保持 Inspect 只读不变式。
|
|
87
|
+
* 管理动作(晨审处置)始终落在操作侧 /#/operate/night,保持 Inspect 只读不变式。
|
|
88
88
|
*/
|
|
89
89
|
async function renderNightStrip() {
|
|
90
90
|
const strip = document.getElementById("dashboard-night");
|
|
@@ -118,14 +118,14 @@ async function renderNightStrip() {
|
|
|
118
118
|
|
|
119
119
|
const actions = el("div", "night-strip-actions");
|
|
120
120
|
const detail = el("a", "page-action-btn", "夜间详情");
|
|
121
|
-
detail.href = "
|
|
121
|
+
detail.href = "/#/inspect/night";
|
|
122
122
|
detail.addEventListener("click", (e) => {
|
|
123
123
|
e.preventDefault();
|
|
124
124
|
navigate("/night");
|
|
125
125
|
});
|
|
126
126
|
actions.appendChild(detail);
|
|
127
127
|
const manage = el("a", "page-action-btn night-strip-manage", "晨审处置 →");
|
|
128
|
-
manage.href = "/#/night";
|
|
128
|
+
manage.href = "/#/operate/night";
|
|
129
129
|
manage.title = "夜间计划的管理与晨审处置在操作台完成(Inspect 保持只读)";
|
|
130
130
|
actions.appendChild(manage);
|
|
131
131
|
strip.appendChild(actions);
|
|
@@ -738,10 +738,12 @@ export function startDashboardPolling(scrollTo) {
|
|
|
738
738
|
const poll = async (initialScrollTo) => {
|
|
739
739
|
await renderDashboard(initialScrollTo);
|
|
740
740
|
if (generation !== uiState.pollingGeneration) return;
|
|
741
|
+
const delay = dashboardPollDelay();
|
|
742
|
+
if (delay == null) return;
|
|
741
743
|
uiState.dashboardTimer = setTimeout(() => {
|
|
742
744
|
uiState.dashboardTimer = null;
|
|
743
745
|
void poll();
|
|
744
|
-
},
|
|
746
|
+
}, delay);
|
|
745
747
|
};
|
|
746
748
|
void poll(scrollTo);
|
|
747
749
|
}
|
|
@@ -101,11 +101,14 @@ export async function renderFailures() {
|
|
|
101
101
|
|
|
102
102
|
if (tasks.length === 0) {
|
|
103
103
|
clearNode(listEl);
|
|
104
|
+
// 2026-08-22 (B-FIND-02): 空态文案与静态 #failures-empty / 页面标题
|
|
105
|
+
// 「异常 Task」保持一致;旧文案「当前没有失败或阻塞的 Task。」与静态
|
|
106
|
+
// HTML 及回归目录 AC-FE-INS-004 漂移,导致 frontend-test 字面 find 0 命中。
|
|
104
107
|
if (emptyEl) {
|
|
105
108
|
emptyEl.hidden = false;
|
|
106
|
-
mountViewState(emptyEl, "empty", "
|
|
109
|
+
mountViewState(emptyEl, "empty", "当前没有异常 Task");
|
|
107
110
|
} else {
|
|
108
|
-
mountViewState(listEl, "empty", "
|
|
111
|
+
mountViewState(listEl, "empty", "当前没有异常 Task");
|
|
109
112
|
}
|
|
110
113
|
return;
|
|
111
114
|
}
|
|
@@ -46,8 +46,8 @@ export async function renderNightJobs(scheduleId) {
|
|
|
46
46
|
showView("night");
|
|
47
47
|
bindNightToolbar();
|
|
48
48
|
const crumbs = [
|
|
49
|
-
{ label: UI_TEXT.dashboard, href: "
|
|
50
|
-
{ label: UI_TEXT.nightJobs, href: "
|
|
49
|
+
{ label: UI_TEXT.dashboard, href: "/#/inspect" },
|
|
50
|
+
{ label: UI_TEXT.nightJobs, href: "/#/inspect/night" },
|
|
51
51
|
];
|
|
52
52
|
if (scheduleId) crumbs.push({ label: scheduleId });
|
|
53
53
|
setBreadcrumb(crumbs);
|
|
@@ -93,6 +93,7 @@ function startPoolAutoPolling() {
|
|
|
93
93
|
if (generation !== uiState.pollingGeneration) return;
|
|
94
94
|
if (!poolAutoRefreshEnabled()) return;
|
|
95
95
|
const delay = isPageVisible() ? POOL_POLL_MS : dashboardPollDelay();
|
|
96
|
+
if (delay == null) return;
|
|
96
97
|
uiState.poolTimer = setTimeout(() => {
|
|
97
98
|
uiState.poolTimer = null;
|
|
98
99
|
void poll();
|
|
@@ -50,6 +50,7 @@ import {
|
|
|
50
50
|
} from "../relations.js";
|
|
51
51
|
import { copyText, renderRecommendedCommand } from "../copy.js";
|
|
52
52
|
import {
|
|
53
|
+
fetchWithWorkspaceScope,
|
|
53
54
|
isPageVisible,
|
|
54
55
|
detailPollDelay,
|
|
55
56
|
dashboardPollDelay,
|
|
@@ -126,7 +127,7 @@ export function renderInlineArtifactList(container, artifacts) {
|
|
|
126
127
|
list.appendChild(li);
|
|
127
128
|
void (async () => {
|
|
128
129
|
try {
|
|
129
|
-
const res = await
|
|
130
|
+
const res = await fetchWithWorkspaceScope(artifactUrl(artifactPath));
|
|
130
131
|
const text = await res.text();
|
|
131
132
|
if (!res.ok) {
|
|
132
133
|
let message = text;
|
|
@@ -444,6 +445,7 @@ export function startRunPolling(workerRunId, initial = true) {
|
|
|
444
445
|
const active = await renderRun(workerRunId, isInitial);
|
|
445
446
|
if (generation !== uiState.pollingGeneration || !active) return;
|
|
446
447
|
const delay = detailPollDelay("running", isPageVisible());
|
|
448
|
+
if (delay == null) return;
|
|
447
449
|
uiState.runPollTimer = setTimeout(() => {
|
|
448
450
|
uiState.runPollTimer = null;
|
|
449
451
|
void poll(false);
|
|
@@ -250,6 +250,54 @@ export function buildSessionTimelineItems(events) {
|
|
|
250
250
|
return items;
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
+
/**
|
|
254
|
+
* Split a node's shared session-events log into per-attempt segments. Every
|
|
255
|
+
* attempt appends one agent run beginning with agent_start; events recorded
|
|
256
|
+
* before the first agent_start (defensive) stay in an implicit first segment.
|
|
257
|
+
*/
|
|
258
|
+
export function splitSessionEventsByAttempt(events) {
|
|
259
|
+
const segments = [];
|
|
260
|
+
let current = null;
|
|
261
|
+
for (const event of events) {
|
|
262
|
+
if (typeof event?.type === "string" && event.type === "agent_start") {
|
|
263
|
+
current = [];
|
|
264
|
+
segments.push(current);
|
|
265
|
+
}
|
|
266
|
+
if (current === null) {
|
|
267
|
+
current = [];
|
|
268
|
+
segments.push(current);
|
|
269
|
+
}
|
|
270
|
+
current.push(event);
|
|
271
|
+
}
|
|
272
|
+
return segments;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Resolve the events visible in the「执行过程」tab for the selected execution
|
|
277
|
+
* key. attempt-N shows only that attempt's segment when the shared log carries
|
|
278
|
+
* multiple attempts; other keys (pass-N, null) keep the full stream.
|
|
279
|
+
*/
|
|
280
|
+
export function resolveTimelineVisibleEvents(events, executionKey) {
|
|
281
|
+
const match =
|
|
282
|
+
typeof executionKey === "string"
|
|
283
|
+
? /^attempt-(\d+)$/.exec(executionKey)
|
|
284
|
+
: null;
|
|
285
|
+
if (!match) return { events, note: null };
|
|
286
|
+
const segments = splitSessionEventsByAttempt(events);
|
|
287
|
+
if (segments.length <= 1) return { events, note: null };
|
|
288
|
+
const requested = Number(match[1]);
|
|
289
|
+
if (requested >= 1 && requested <= segments.length) {
|
|
290
|
+
return {
|
|
291
|
+
events: segments[requested - 1],
|
|
292
|
+
note: `已按第 ${requested} 次执行过滤 · 共 ${segments.length} 次执行`,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
events,
|
|
297
|
+
note: `未检测到第 ${requested} 次执行的边界(共 ${segments.length} 次执行),暂显示全部事件`,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
253
301
|
/**
|
|
254
302
|
* Build rich timeline items: merge tool start/end by toolCallId, collapse
|
|
255
303
|
* protocol noise, surface turn thinking/text without dumping agent_end.messages.
|
|
@@ -439,12 +487,25 @@ function ensureOpenDetailsSet() {
|
|
|
439
487
|
return uiState.sessionTimelineOpenDetails;
|
|
440
488
|
}
|
|
441
489
|
|
|
442
|
-
function
|
|
490
|
+
function closedDetailKey(detailKey) {
|
|
491
|
+
return `${detailKey}::user-closed`;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function bindDetailsToggle(details, detailKey, defaultOpen = false) {
|
|
443
495
|
const openSet = ensureOpenDetailsSet();
|
|
444
|
-
if (
|
|
496
|
+
if (defaultOpen) {
|
|
497
|
+
if (!openSet.has(closedDetailKey(detailKey))) details.open = true;
|
|
498
|
+
} else if (openSet.has(detailKey)) {
|
|
499
|
+
details.open = true;
|
|
500
|
+
}
|
|
445
501
|
details.addEventListener("toggle", () => {
|
|
446
|
-
if (details.open)
|
|
447
|
-
|
|
502
|
+
if (details.open) {
|
|
503
|
+
openSet.add(detailKey);
|
|
504
|
+
openSet.delete(closedDetailKey(detailKey));
|
|
505
|
+
} else {
|
|
506
|
+
openSet.delete(detailKey);
|
|
507
|
+
openSet.add(closedDetailKey(detailKey));
|
|
508
|
+
}
|
|
448
509
|
});
|
|
449
510
|
}
|
|
450
511
|
|
|
@@ -599,7 +660,7 @@ function renderProtocolCard(item) {
|
|
|
599
660
|
const details = document.createElement("details");
|
|
600
661
|
details.className = "process-timeline-details";
|
|
601
662
|
details.dataset.detailKey = item.detailKey;
|
|
602
|
-
bindDetailsToggle(details, item.detailKey);
|
|
663
|
+
bindDetailsToggle(details, item.detailKey, true);
|
|
603
664
|
const summary = document.createElement("summary");
|
|
604
665
|
summary.className = "process-timeline-summary";
|
|
605
666
|
summary.textContent = `消息收发 · ${item.protocolCount} 条协议事件`;
|
|
@@ -644,7 +705,7 @@ export async function mergeSessionEvents(dagRunId, nodeId) {
|
|
|
644
705
|
}
|
|
645
706
|
}
|
|
646
707
|
|
|
647
|
-
export function renderSessionTimeline(content, nodeId) {
|
|
708
|
+
export function renderSessionTimeline(content, nodeId, options = {}) {
|
|
648
709
|
content.dataset.dagRunId = uiState.currentDagRunId ?? "";
|
|
649
710
|
content.dataset.nodeId = nodeId ?? "";
|
|
650
711
|
content.classList.add("process-timeline-scroll");
|
|
@@ -653,6 +714,14 @@ export function renderSessionTimeline(content, nodeId) {
|
|
|
653
714
|
return;
|
|
654
715
|
}
|
|
655
716
|
|
|
717
|
+
const visible = resolveTimelineVisibleEvents(
|
|
718
|
+
uiState.sessionEvents,
|
|
719
|
+
options.executionKey,
|
|
720
|
+
);
|
|
721
|
+
if (visible.note) {
|
|
722
|
+
content.appendChild(el("p", "process-timeline-note", visible.note));
|
|
723
|
+
}
|
|
724
|
+
|
|
656
725
|
const showProtocol = Boolean(uiState.sessionTimelineShowProtocol);
|
|
657
726
|
const toolbar = el("div", "process-timeline-toolbar");
|
|
658
727
|
const protocolToggle = document.createElement("button");
|
|
@@ -668,17 +737,17 @@ export function renderSessionTimeline(content, nodeId) {
|
|
|
668
737
|
const scrollTop = parent.scrollTop;
|
|
669
738
|
const scrollLeft = parent.scrollLeft;
|
|
670
739
|
while (parent.firstChild) parent.removeChild(parent.firstChild);
|
|
671
|
-
renderSessionTimeline(parent, nodeId);
|
|
740
|
+
renderSessionTimeline(parent, nodeId, options);
|
|
672
741
|
parent.scrollTop = scrollTop;
|
|
673
742
|
parent.scrollLeft = scrollLeft;
|
|
674
743
|
});
|
|
675
744
|
toolbar.appendChild(protocolToggle);
|
|
676
745
|
content.appendChild(toolbar);
|
|
677
746
|
|
|
678
|
-
const timelineItems = buildRichTimelineItems(
|
|
747
|
+
const timelineItems = buildRichTimelineItems(visible.events, {
|
|
679
748
|
showProtocol,
|
|
680
749
|
});
|
|
681
|
-
const hasEventTimes =
|
|
750
|
+
const hasEventTimes = visible.events.some((event) =>
|
|
682
751
|
Boolean(sessionEventTimestamp(event)),
|
|
683
752
|
);
|
|
684
753
|
if (!hasEventTimes) {
|
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
repoBaseName,
|
|
35
35
|
parseMarkdownBlocks,
|
|
36
36
|
markdownLinkHref,
|
|
37
|
+
formatLedgerSummary,
|
|
37
38
|
} from "../format.js";
|
|
38
39
|
import { parseRoute, navigate, buildHashPath } from "../router.js";
|
|
39
40
|
import {
|
|
@@ -85,7 +86,7 @@ export async function renderTaskDetail(featureId, taskId) {
|
|
|
85
86
|
showView("task");
|
|
86
87
|
setBreadcrumb([
|
|
87
88
|
{ label: UI_TEXT.dashboard, href: "#/" },
|
|
88
|
-
{ label: "资源池", href: "
|
|
89
|
+
{ label: "资源池", href: "/#/inspect/pool" },
|
|
89
90
|
{ label: displayLabel },
|
|
90
91
|
]);
|
|
91
92
|
|
|
@@ -94,6 +95,7 @@ export async function renderTaskDetail(featureId, taskId) {
|
|
|
94
95
|
const linksEl = document.getElementById("task-links");
|
|
95
96
|
const evidenceEl = document.getElementById("task-evidence");
|
|
96
97
|
const runsEl = document.getElementById("task-runs");
|
|
98
|
+
const ledgerEl = document.getElementById("task-ledger");
|
|
97
99
|
if (!metaEl || !linksEl || !evidenceEl || !runsEl) return false;
|
|
98
100
|
if (titleEl) titleEl.textContent = displayLabel;
|
|
99
101
|
mountViewState(metaEl, "loading", "加载 Task…");
|
|
@@ -252,9 +254,62 @@ export async function renderTaskDetail(featureId, taskId) {
|
|
|
252
254
|
const active = ["running", "pending", "blocked", "stale"].includes(
|
|
253
255
|
(task.status ?? "").toLowerCase(),
|
|
254
256
|
);
|
|
257
|
+
if (ledgerEl) {
|
|
258
|
+
void renderTaskLedger(ledgerEl, taskId);
|
|
259
|
+
}
|
|
255
260
|
return active;
|
|
256
261
|
}
|
|
257
262
|
|
|
263
|
+
/**
|
|
264
|
+
* Task 详情页 ledger 安全摘要(AC-009):从只读 GET /api/tasks/:id/ledger 拉取,
|
|
265
|
+
* 计数/状态/failure code + 有界排除理由;无 ledger 显示空态,失败安全降级。
|
|
266
|
+
*/
|
|
267
|
+
async function renderTaskLedger(ledgerEl, taskId) {
|
|
268
|
+
clearNode(ledgerEl);
|
|
269
|
+
ledgerEl.appendChild(el("h3", "section-heading", "Source fidelity ledger"));
|
|
270
|
+
mountViewState(ledgerEl, "loading", "加载 ledger…");
|
|
271
|
+
let body = null;
|
|
272
|
+
try {
|
|
273
|
+
body = await fetchJson(`/api/tasks/${encodeURIComponent(taskId)}/ledger`);
|
|
274
|
+
} catch {
|
|
275
|
+
body = null;
|
|
276
|
+
}
|
|
277
|
+
if (!body) {
|
|
278
|
+
mountViewState(ledgerEl, "error", "ledger 只读接口失败(安全降级)。");
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (body.present !== true) {
|
|
282
|
+
mountViewState(
|
|
283
|
+
ledgerEl,
|
|
284
|
+
body.error ? "error" : "empty",
|
|
285
|
+
body.error
|
|
286
|
+
? `ledger 不可用(${body.error})。`
|
|
287
|
+
: "无 source fidelity ledger(任务无引用或 ledger 缺失,不伪造计数)。",
|
|
288
|
+
);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
const pre = el(
|
|
292
|
+
"pre",
|
|
293
|
+
"cmd",
|
|
294
|
+
formatLedgerSummary({
|
|
295
|
+
present: true,
|
|
296
|
+
fragmentCount: body.fragmentCount,
|
|
297
|
+
canonicalRequirementCount: body.canonicalRequirementCount,
|
|
298
|
+
contextOnlyCount: body.contextOnlyCount,
|
|
299
|
+
duplicateCount: body.duplicateCount,
|
|
300
|
+
unresolvedCount: body.unresolvedCount,
|
|
301
|
+
conflictCount: body.conflictCount,
|
|
302
|
+
gateStatus: body.gateStatus,
|
|
303
|
+
failureCode: body.failureCode,
|
|
304
|
+
exclusions: body.exclusions ?? [],
|
|
305
|
+
}),
|
|
306
|
+
);
|
|
307
|
+
pre.dataset.testid = "task-ledger-summary";
|
|
308
|
+
clearNode(ledgerEl);
|
|
309
|
+
ledgerEl.appendChild(el("h3", "section-heading", "Source fidelity ledger"));
|
|
310
|
+
ledgerEl.appendChild(pre);
|
|
311
|
+
}
|
|
312
|
+
|
|
258
313
|
export async function loadOlderTaskRuns(featureId, taskId, before, runsEl) {
|
|
259
314
|
if (runsEl === undefined) {
|
|
260
315
|
runsEl = before;
|
|
@@ -5,6 +5,7 @@ import path from "node:path";
|
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import YAML from "yaml";
|
|
7
7
|
import { resolveBackendTestLayout, } from "./backend-test-layout.js";
|
|
8
|
+
import { resolveBackendTestManifestModules, backendManifestStatusFinding } from "./backend-test-markdown-workflow.js";
|
|
8
9
|
import { applyOpenApiPartitionDomainPolicy, assessScenarioPartitionCoverage, parseScenarioPartitions, } from "./backend-test-scenario-partitions.js";
|
|
9
10
|
import { backendTestCaseManifestSchema, computeCaseManifestCoverageSummary, } from "./backend-test-case-manifest.js";
|
|
10
11
|
const CASE_HEADING = /^##\s+(BE-[A-Z0-9_-]+-\d{3})\b.*$/gm;
|
|
@@ -659,10 +660,18 @@ async function markdownModuleFiles(workspaceRoot, layout) {
|
|
|
659
660
|
const resolved = layout ?? resolveBackendTestLayout(undefined);
|
|
660
661
|
const dir = path.join(workspaceRoot, ...resolved.markdownDir.split("/"));
|
|
661
662
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
662
|
-
|
|
663
|
+
const all = entries
|
|
663
664
|
.filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith(".md") && entry.name.toLowerCase() !== "readme.md")
|
|
664
665
|
.map((entry) => path.join(dir, entry.name))
|
|
665
666
|
.sort();
|
|
667
|
+
// Current-round Markdown scope is the README Module Index manifest: unmanifested
|
|
668
|
+
// legacy .md files must not enter Case/coverage/partition statistics. Workspaces
|
|
669
|
+
// without a Module Index fall back to the whole directory (prior behaviour).
|
|
670
|
+
const manifest = await resolveBackendTestManifestModules(workspaceRoot, resolved);
|
|
671
|
+
if (manifest.manifestStatus !== "valid")
|
|
672
|
+
return [];
|
|
673
|
+
const declaredAbs = new Set(manifest.declaredMdFiles.map((rel) => path.join(workspaceRoot, ...rel.split("/")).toLowerCase()));
|
|
674
|
+
return all.filter((file) => declaredAbs.has(file.toLowerCase())).sort();
|
|
666
675
|
}
|
|
667
676
|
async function parseMarkdownCases(workspaceRoot, layout) {
|
|
668
677
|
const result = [];
|
|
@@ -874,6 +883,16 @@ export async function analyzeBackendTestCaseCoverage(input) {
|
|
|
874
883
|
findings.push(...coverageScope.findings);
|
|
875
884
|
const matrix = parseCoverageMatrix(readme);
|
|
876
885
|
findings.push(...matrix.findings);
|
|
886
|
+
const manifest = await resolveBackendTestManifestModules(input.workspaceRoot, layout);
|
|
887
|
+
// Fail closed on README Module Index entries declared but never materialized.
|
|
888
|
+
if (manifest.declaredMissing.length > 0) {
|
|
889
|
+
findings.push(`declared Markdown module is missing (Module Index): ${manifest.declaredMissing.join(", ")}`);
|
|
890
|
+
}
|
|
891
|
+
// Fail closed on every non-valid Module Index state: without a valid unique
|
|
892
|
+
// Module Index the current round must not re-scan historical `.md` files.
|
|
893
|
+
if (manifest.manifestStatus !== "valid") {
|
|
894
|
+
findings.push(backendManifestStatusFinding(manifest.manifestStatus, manifest.moduleIndexHeadingCount));
|
|
895
|
+
}
|
|
877
896
|
const cases = await parseMarkdownCases(input.workspaceRoot, layout);
|
|
878
897
|
for (const file of await markdownModuleFiles(input.workspaceRoot, layout)) {
|
|
879
898
|
const markdown = await readFile(file, "utf8");
|
|
@@ -1109,6 +1128,15 @@ export async function analyzeBackendTestCaseCoverage(input) {
|
|
|
1109
1128
|
"",
|
|
1110
1129
|
facts.status,
|
|
1111
1130
|
"",
|
|
1131
|
+
`- Manifest scope: ${manifest.manifestStatus === "valid" ? "declared-modules" : `fails-closed:${manifest.manifestStatus}`} (declared-missing=${manifest.declaredMissing.length}, unmanifested-advisory=${manifest.unmanifested.length}, module-index-heading-count=${manifest.moduleIndexHeadingCount})`,
|
|
1132
|
+
"",
|
|
1133
|
+
"## Unmanifested Markdown Modules",
|
|
1134
|
+
"",
|
|
1135
|
+
"- Advisory: existing .md files not declared by the README Module Index are excluded from Case/coverage/partition statistics for this round.",
|
|
1136
|
+
...(manifest.unmanifested.length > 0
|
|
1137
|
+
? manifest.unmanifested.map((item) => `- ${item}`)
|
|
1138
|
+
: ["- None"]),
|
|
1139
|
+
"",
|
|
1112
1140
|
"## Coverage Scope",
|
|
1113
1141
|
"",
|
|
1114
1142
|
`- Change Classification: ${facts.coverageScope.changeClassification}`,
|
|
@@ -1500,8 +1528,26 @@ print(json.dumps(out,ensure_ascii=True))
|
|
|
1500
1528
|
// Windows Python may expose stdin as GBK; decoding generated Chinese/emoji
|
|
1501
1529
|
// source through that locale can create surrogateescape code points and make
|
|
1502
1530
|
// ast.parse fail with "surrogates not allowed".
|
|
1503
|
-
|
|
1504
|
-
|
|
1531
|
+
// POSIX images usually expose only `python3` (modern macOS has no `python`);
|
|
1532
|
+
// Windows often has a real `python` plus a Microsoft Store `python3` stub.
|
|
1533
|
+
// A missing interpreter must not silently degrade to empty payload shapes.
|
|
1534
|
+
const interpreterCandidates = process.platform === "win32" ? ["python", "python3"] : ["python3", "python"];
|
|
1535
|
+
let result;
|
|
1536
|
+
for (const interpreter of interpreterCandidates) {
|
|
1537
|
+
const attempt = spawnSync(interpreter, ["-c", program], {
|
|
1538
|
+
input: Buffer.from(source, "utf8"),
|
|
1539
|
+
encoding: "utf8",
|
|
1540
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
1541
|
+
windowsHide: true,
|
|
1542
|
+
});
|
|
1543
|
+
// Interpreter missing/unspawnable → try the next candidate. A spawned
|
|
1544
|
+
// interpreter that exits nonzero analyzed real input; do not retry then.
|
|
1545
|
+
if (attempt.error)
|
|
1546
|
+
continue;
|
|
1547
|
+
result = attempt;
|
|
1548
|
+
break;
|
|
1549
|
+
}
|
|
1550
|
+
if (!result || result.status !== 0 || !result.stdout.trim())
|
|
1505
1551
|
return new Map();
|
|
1506
1552
|
try {
|
|
1507
1553
|
const parsed = JSON.parse(result.stdout);
|