@tea-agent/loop-agent 0.41.1-beta.0 → 0.42.0-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +78 -206
- package/dist/adapters/context-transfer/optional-pi-handoff.js +31 -0
- package/dist/adapters/context-transfer/pi-session.js +61 -0
- package/dist/application/dag/generate-task-dag.js +19 -75
- package/dist/application/task-lifecycle/advance.js +5 -24
- package/dist/application/task-lifecycle/observe.js +17 -171
- package/dist/application/task-lifecycle/plan-transitions.js +7 -42
- package/dist/application/task-lifecycle/recommendations.js +2 -13
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +12 -7
- package/dist/cli/program.js +23 -6
- package/dist/commands/client-recovery.js +0 -3
- package/dist/commands/dag-artifact.js +284 -0
- package/dist/commands/dag-context.js +184 -0
- package/dist/commands/dag-rerun.js +206 -1
- package/dist/commands/init.js +1 -27
- package/dist/commands/task-advance.js +0 -19
- package/dist/executors/dag-pi-executor.js +266 -3792
- package/dist/executors/pi-executor.js +4 -15
- package/dist/executors/pi-sdk-executor.js +3 -0
- package/dist/executors/shell-executor.js +217 -945
- package/dist/executors/shell-write-guard.js +0 -7
- package/dist/{worker → infrastructure}/console/app-data.js +4 -0
- package/dist/infrastructure/console/artifact-revision-store.js +430 -0
- package/dist/infrastructure/console/context-export-store.js +160 -0
- package/dist/infrastructure/console/dir-lock.js +132 -0
- package/dist/infrastructure/console/operation-store.js +197 -0
- package/dist/infrastructure/harness/artifact-store.js +10 -1
- package/dist/infrastructure/harness/atomic-write.js +12 -2
- package/dist/shared/context-transfer/artifact-revision.js +172 -0
- package/dist/shared/context-transfer.js +418 -0
- package/dist/shared/dag-failure-category.js +0 -12
- package/dist/shared/openspec-spec.js +4 -70
- package/dist/shared/operator/capabilities.js +14 -7
- package/dist/shared/operator/safe-run-summary.js +1 -0
- package/dist/shared/path-safety.js +93 -0
- package/dist/shared/preview.js +28 -4
- package/dist/task/config-types.js +80 -35
- package/dist/task/contract/adopt.js +0 -4
- package/dist/task/contract/import-revision.js +0 -4
- package/dist/task/contract/project.js +0 -3
- package/dist/task/contract/schema.js +1 -2
- package/dist/task/frontend-project-capability.js +20 -203
- package/dist/task/runtime.js +2 -5
- package/dist/task/source-prepare/build-draft.js +3 -3
- package/dist/task/source-prepare/fragment-inventory.js +9 -15
- package/dist/task/source-prepare/prepare.js +1 -89
- package/dist/task/source-prepare/semantic-intake.js +2 -6
- package/dist/task/source-references.js +1 -22
- package/dist/task/task-demand-routing.js +3 -0
- package/dist/worker/console/chat/chat-event-store.js +2 -2
- package/dist/worker/console/chat/pi-runtime.js +3 -2
- package/dist/worker/console/chat/resource-preferences-store.js +1 -1
- package/dist/worker/console/chat/routes.js +13 -4
- package/dist/worker/console/chat/semantic-activity.js +12 -11
- package/dist/worker/console/chat/session-stats.js +96 -0
- package/dist/worker/console/chat/session-store.js +1 -1
- package/dist/worker/console/chat/turn-process.js +28 -4
- package/dist/worker/console/chat/user-questions.js +1 -1
- package/dist/worker/console/console-update-runtime.js +1 -1
- package/dist/worker/console/context-transfer-diagnostics.js +198 -0
- package/dist/worker/console/dag-confirmation.js +1 -1
- package/dist/worker/console/dag-execution-receipt.js +1 -1
- package/dist/worker/console/doctor.js +1 -1
- package/dist/worker/console/draft-store.js +1 -1
- package/dist/worker/console/human-gate-token.js +1 -1
- package/dist/worker/console/index.js +3 -6
- package/dist/worker/console/interview/assessment.js +1 -1
- package/dist/worker/console/interview/session.js +1 -1
- package/dist/worker/console/operation-runner.js +45 -4
- package/dist/worker/console/operation-sse.js +1 -1
- package/dist/worker/console/operation-wait.js +1 -1
- package/dist/worker/console/operator-actions.js +107 -168
- package/dist/worker/console/operator-surface-health.js +1 -1
- package/dist/worker/console/operator-user-error.js +169 -0
- package/dist/worker/console/pi-plugins.js +60 -1
- package/dist/worker/console/routes.js +637 -8
- package/dist/worker/console/security.js +35 -0
- package/dist/worker/console/server.js +6 -6
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-Bho4we4c.js → abnfDiagram-N423BO3Z-DZZ8m3PO.js} +1 -1
- package/dist/worker/console/static/assets/{arc-TBDkTeK0.js → arc-D6PvaVd-.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-BF_fZrn2.js → architectureDiagram-T3A2C74G-B_OTOiI8.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-CCnPgUe2.js → blockDiagram-VBNYF7ZC-Bv6rqHBg.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BCYZJLAT.js → c4Diagram-5PPSVZJV-B8eHr0oz.js} +1 -1
- package/dist/worker/console/static/assets/channel-BU5gOilw.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-D8K6adhU.js → chunk-2GRJ4B5K-DYwR0im2.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DkT1Q78b.js → chunk-2Q5K7J3B-D2WPGqXt.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DuhvpIr7.js → chunk-5RXB4S5H-CQISJ_I7.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BnK6dC49.js → chunk-5VM5RSS4-C0o2Du1e.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DPz__z7r.js → chunk-6Q2QTUOP-4f8kr-U7.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-0kkppM2j.js → chunk-GF5L2VYU-D_OWvXzX.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BB41iK5t.js → chunk-JWPE2WC7-CasdPz5X.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-CiVrxKHU.js → chunk-KBJHAD2P-Cj8lRrla.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-C2h9Xje1.js → chunk-RYQCIY6F-CUvD1FSd.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-3iOSHdbM.js → chunk-XXDRQBXY-CqLw_eqB.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DIzKJGHr.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DIzKJGHr.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BN3A7tkk.js → cose-bilkent-JH36ORCC-ku-WqJPx.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-CTuBy0Or.js → cynefin-VYW2F7L2-Bq_PYDhl.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-wb6tUlMN.js → cynefinDiagram-MW4NZA55-BiGZV641.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-GP1Ivx8B.js → dagre-VZM6K2ZE-C4JRlglF.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-ejrcK6L0.js → diagram-7IWD3JNH-B0dNeWYG.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-YFkCCvUU.js → diagram-B4RE2ZJO-Cj35YvYM.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-B4rrvIaL.js → diagram-LBJQPF4R-uzQoJ2-8.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-C-YIZcUf.js → diagram-Q27KOJAE-D1a-Buoz.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BPF5wcL1.js → diagram-UB23O5K3-XjRrLRSs.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-CU5SzXjO.js → ebnfDiagram-BXEA7PRR-Da_O24cW.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-CpL414DE.js → erDiagram-JOGREHBK-BLJ8jrYU.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-MrhLzNau.js → flowDiagram-UKHOOZJN-B9GrLjM0.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-Bg4qFuUo.js → ganttDiagram-PKOTCBZU-CeJ0TqiK.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-ColxSXLR.js → gitGraphDiagram-DS77QQ5N-Bm2eNNFX.js} +1 -1
- package/dist/worker/console/static/assets/index-H9rFJiGL.css +1 -0
- package/dist/worker/console/static/assets/index-xwu9GxEc.js +451 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-Czvtm6gc.js → infoDiagram-6WML65LV-DB26i3d8.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-B7Xp8HyJ.js → ishikawaDiagram-WSZJBQD7-BOGRyeZT.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DWYZcvph.js → journeyDiagram-NVQOT4AX-CZRoBO_6.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-KTLZRUjx.js → kanban-definition-27J2QSJJ-qlWhJyoB.js} +1 -1
- package/dist/worker/console/static/assets/{linear-_Exn7bfl.js → linear-CfUiDDB3.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-Cxg8kX8V.js → mermaid.core-BQe6fpqj.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-oKrlo2Bt.js → mindmap-definition-FAOFIHXS-zFzWHw64.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-XAeteiNd.js → pegDiagram-VL7TDLO6-DFGUqjZO.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-BTf1K-lp.js → pieDiagram-7S7Q4E2Y-BB5i0l8Z.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-C0IlY0Jf.js → quadrantDiagram-CIZ2JOQS-obbVZ_X5.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BVbKtZhW.js → railroadDiagram-AXF67PYL-BwdaNzc1.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-C5e4JFcr.js → requirementDiagram-LRYGKXZP-CkTJaugh.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Cz30hc29.js → sankeyDiagram-W5VNT64P-BwJ-hIgq.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DS33n6d0.js → sequenceDiagram-SI44F4Z6-BzSgmm7w.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-46w7v-9T.js → sizeCapture-X5ZJPWSS-Bn3oj5Q1.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-pbcD5SsZ.js → stateDiagram-OKZ733FA-dxhGtL8K.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Cld2qK9v.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DuivYBSj.js → swimlanes-SLNWSIFB-BLpEInya.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-TIHpiT7w.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-Cb9VZVIl.js → timeline-definition-Z64GVDOM-ChJGYcXy.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-D2-mfOgR.js → vennDiagram-T6HMQDX7-DK-qjRer.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DeS2aC76.js → wardleyDiagram-T6FBY63Y-3qeaWAg-.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-WGsrFjxH.js → xychartDiagram-ELKLHX3M-CootlyP9.js} +1 -1
- package/dist/worker/console/static/index.html +7 -2
- package/dist/worker/console/static-src/app/useOperatorActions.js +3 -2
- package/dist/worker/console/static-src/app/useRunProgress.js +9 -2
- package/dist/worker/console/static-src/operator-chat/dag-progress-link.js +22 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +2 -0
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +6 -0
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +28 -3
- package/dist/worker/console/static-src/pages/tasks/run-panel-progress.js +106 -0
- package/dist/worker/console/static-src/shell/console-update-reload.js +25 -0
- package/dist/worker/console/workspace-context.js +28 -112
- package/dist/worker/console/workspace-registry.js +2 -2
- package/dist/worker/continuation/worker-continuation.js +278 -0
- package/dist/worker/observability/read-model.js +4 -0
- package/dist/worker/observe/health.js +1 -1
- package/dist/worker/observe/node-transparency.js +572 -0
- package/dist/worker/observe/routes.js +51 -1
- package/dist/worker/observe/static/api.js +69 -5
- package/dist/worker/observe/static/dag-context-reason-labels.d.ts +9 -0
- package/dist/worker/observe/static/dag-context-reason-labels.js +120 -0
- package/dist/worker/observe/static/dag-node-purpose.js +0 -5
- package/dist/worker/observe/static/state.js +3 -1
- package/dist/worker/observe/static/styles.css +1037 -126
- package/dist/worker/observe/static/views/dag-inspector.js +1217 -65
- package/dist/worker/observe/static/views/session-timeline.js +121 -25
- package/dist/worker/outcomes/adapters.js +19 -0
- package/dist/worker/outcomes/types.js +1 -0
- package/dist/worker/pool/attempt-lease.js +97 -66
- package/dist/worker/pool/begin-attempt-with-lease.js +1 -0
- package/dist/worker/pool/reconcile.js +46 -1
- package/dist/worker/pool/run-store.js +2 -0
- package/dist/worker/pool/state-projection.js +2 -0
- package/dist/worker/task-spec/workflow-routing.js +8 -3
- package/dist/workflows/dag/artifact-bindings.js +149 -0
- package/dist/workflows/dag/artifact-revision-schema-registry.js +31 -0
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +99 -4
- package/dist/workflows/dag/backend-test-markdown-workflow.js +13 -1
- package/dist/workflows/dag/backend-test-result-contract.js +1 -1
- package/dist/workflows/dag/backend-test-writer-completeness.js +45 -3
- package/dist/workflows/dag/budget-enforcement.js +10 -0
- package/dist/workflows/dag/context-receipt.js +305 -0
- package/dist/workflows/dag/context-transfer/context-bundle.js +423 -0
- package/dist/workflows/dag/context-transfer/operator-actions.js +61 -0
- package/dist/workflows/dag/context-transfer/renderers.js +93 -0
- package/dist/workflows/dag/contract-validator-registrations.js +2 -1
- package/dist/workflows/dag/frontend-implementation-contract.js +162 -932
- package/dist/workflows/dag/frontend-plan-render.js +1 -2
- package/dist/workflows/dag/frontend-prewrite-gate.js +349 -255
- package/dist/workflows/dag/frontend-recovery-plan.js +10 -17
- package/dist/workflows/dag/frontend-recovery-run.js +20 -33
- package/dist/workflows/dag/frontend-repair.js +432 -1
- package/dist/workflows/dag/frontend-review-context.js +15 -261
- package/dist/workflows/dag/frontend-verification-trace.js +24 -249
- package/dist/workflows/dag/frontend-worktree-diff.js +17 -250
- package/dist/workflows/dag/frontend-writer-rollback.js +32 -0
- package/dist/workflows/dag/init-hybrid.js +740 -718
- package/dist/workflows/dag/interrupt-request.js +0 -7
- package/dist/workflows/dag/node-execution.js +136 -759
- package/dist/workflows/dag/path-safety.js +1 -0
- package/dist/workflows/dag/prompt.js +30 -38
- package/dist/workflows/dag/report.js +1 -37
- package/dist/workflows/dag/rerun-feedback.js +1 -256
- package/dist/workflows/dag/rerun-plan.js +557 -11
- package/dist/workflows/dag/rerun-run.js +601 -38
- package/dist/workflows/dag/rerun-task.js +0 -29
- package/dist/workflows/dag/retry-policy.js +109 -214
- package/dist/workflows/dag/runner.js +142 -430
- package/dist/workflows/dag/scheduler.js +16 -114
- package/dist/workflows/dag/skill-snapshot.js +17 -0
- package/dist/workflows/dag/types.js +195 -246
- package/dist/workflows/dag/validate.js +12 -28
- package/docs/architecture/runtime-boundaries.md +6 -6
- package/docs/architecture/worker-and-feature.md +1 -1
- package/docs/init-surface.manifest.json +12 -30
- package/docs/skills/vetted-skill-registry.md +2 -4
- package/docs/templates/README.md +0 -2
- package/docs/templates/agent-dag-report.schema.json +2 -8
- package/docs/templates/agent-dag.schema.json +33 -1
- package/docs/templates/backend-test-dag.json +18 -18
- package/docs/templates/frontend-implementation-contract.schema.json +1 -4
- package/docs/templates/product-line/task.yaml +1 -1
- package/harness.json +3 -3
- package/package.json +3 -1
- package/skills/frontend-bounded-implement/SKILL.md +14 -15
- package/skills/frontend-design-review/SKILL.md +41 -22
- package/skills/frontend-implementation/SKILL.md +52 -0
- package/skills/frontend-implementation/references/code-standards.md +33 -0
- package/skills/frontend-implementation/references/design-spec.md +56 -0
- package/skills/frontend-implementation/references/node-contracts.md +31 -0
- package/skills/frontend-review/SKILL.md +15 -20
- package/skills/frontend-review/references/review-findings.md +7 -6
- package/skills/frontend-verification/SKILL.md +1 -1
- package/skills/loop-agent/references/command-reference.md +6 -1
- package/skills/loop-agent/references/hybrid-dag.md +2 -2
- package/dist/commands/dag-follow-up.js +0 -138
- package/dist/executors/pi-read-budget-policy.js +0 -239
- package/dist/worker/console/frontend-human-decision-adapter.js +0 -19
- package/dist/worker/console/frontend-split-operation-adapter.js +0 -20
- package/dist/worker/console/operation-store.js +0 -389
- package/dist/worker/console/static/assets/channel-bnXW0U3C.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-rZpq_twR.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-rZpq_twR.js +0 -1
- package/dist/worker/console/static/assets/index-D3CC4eUz.css +0 -1
- package/dist/worker/console/static/assets/index-DLt4ZFvD.js +0 -437
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-zBMYiUyi.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-d_rms-5i.js +0 -8
- package/dist/worker/console/static-src/active-run-badge.js +0 -17
- package/dist/worker/materialize/frontend-split-task-materializer.js +0 -72
- package/dist/workflows/dag/dag-retry-schema.js +0 -138
- package/dist/workflows/dag/frontend-closeout.js +0 -221
- package/dist/workflows/dag/frontend-design-policy.js +0 -400
- package/dist/workflows/dag/frontend-human-decision.js +0 -182
- package/dist/workflows/dag/frontend-provider-capability-matrix.js +0 -159
- package/dist/workflows/dag/frontend-read-budgets.js +0 -12
- package/dist/workflows/dag/frontend-recovery-capsule.js +0 -455
- package/dist/workflows/dag/frontend-recovery-controller.js +0 -202
- package/dist/workflows/dag/frontend-recovery-lineage.js +0 -178
- package/dist/workflows/dag/frontend-review-findings.js +0 -270
- package/dist/workflows/dag/frontend-shadow-dual-write.js +0 -914
- package/dist/workflows/dag/frontend-shape-capsule-store.js +0 -191
- package/dist/workflows/dag/frontend-shape-facts.js +0 -409
- package/dist/workflows/dag/frontend-shape.js +0 -427
- package/dist/workflows/dag/frontend-source-fidelity-ledger.js +0 -108
- package/dist/workflows/dag/frontend-split-application-service.js +0 -203
- package/dist/workflows/dag/frontend-split-orchestrator.js +0 -899
- package/dist/workflows/dag/frontend-typed-event-store.js +0 -452
- package/dist/workflows/dag/frontend-typed-event-transaction.js +0 -180
- package/dist/workflows/dag/frontend-writer-admission.js +0 -285
- package/dist/workflows/dag/frontend-writer-status.js +0 -256
- package/dist/workflows/dag/recovery-lease.js +0 -80
- package/docs/templates/frontend-implementation-dag.json +0 -89
- package/docs/templates/spec-registry.schema.json +0 -45
- package/skills/frontend-bounded-implement/references/code-standards.md +0 -19
- package/skills/frontend-contract/SKILL.md +0 -23
- package/skills/frontend-contract/references/contract-protocol.md +0 -34
- package/skills/frontend-plan/SKILL.md +0 -26
- package/skills/frontend-plan/references/decision-contract.md +0 -37
- package/skills/frontend-plan/references/design-decisions.md +0 -17
- package/skills/frontend-scout/SKILL.md +0 -25
- package/skills/frontend-scout/references/design-evidence.md +0 -16
- package/skills/frontend-scout/references/scout-evidence.md +0 -23
- /package/dist/{worker → infrastructure}/console/repo-fingerprint.js +0 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { canonicalSha256 } from "../../shared/context-transfer.js";
|
|
5
|
+
export function buildArtifactBindingGraph(spec) {
|
|
6
|
+
const consumers = new Map();
|
|
7
|
+
for (const task of spec.tasks) {
|
|
8
|
+
for (const binding of task.consumesArtifacts ?? []) {
|
|
9
|
+
const nodeIds = consumers.get(binding.artifactId) ?? [];
|
|
10
|
+
nodeIds.push(task.id);
|
|
11
|
+
consumers.set(binding.artifactId, nodeIds);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const artifacts = spec.tasks
|
|
15
|
+
.flatMap((task) => (task.producesArtifacts ?? []).map((artifact) => ({
|
|
16
|
+
...artifact,
|
|
17
|
+
producerNodeId: task.id,
|
|
18
|
+
directConsumerNodeIds: [...(consumers.get(artifact.artifactId) ?? [])].sort(),
|
|
19
|
+
})))
|
|
20
|
+
.sort((a, b) => a.artifactId.localeCompare(b.artifactId));
|
|
21
|
+
const available = artifacts.length > 0;
|
|
22
|
+
const payload = { schemaVersion: 1, artifacts };
|
|
23
|
+
return {
|
|
24
|
+
...payload,
|
|
25
|
+
available,
|
|
26
|
+
...(available ? {} : { reasonCode: "ARTIFACT_BINDING_UNAVAILABLE" }),
|
|
27
|
+
artifactGraphSha256: canonicalSha256(payload),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function collectDescendants(spec, roots) {
|
|
31
|
+
const children = new Map();
|
|
32
|
+
for (const task of spec.tasks) {
|
|
33
|
+
for (const parent of task.depends_on) {
|
|
34
|
+
const values = children.get(parent) ?? [];
|
|
35
|
+
values.push(task.id);
|
|
36
|
+
children.set(parent, values);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const queue = [...roots];
|
|
40
|
+
const seen = new Set();
|
|
41
|
+
while (queue.length > 0) {
|
|
42
|
+
const nodeId = queue.shift();
|
|
43
|
+
if (seen.has(nodeId))
|
|
44
|
+
continue;
|
|
45
|
+
seen.add(nodeId);
|
|
46
|
+
queue.push(...(children.get(nodeId) ?? []));
|
|
47
|
+
}
|
|
48
|
+
return spec.tasks.map((task) => task.id).filter((nodeId) => seen.has(nodeId));
|
|
49
|
+
}
|
|
50
|
+
export function computeArtifactImpact(spec, artifactId) {
|
|
51
|
+
const graph = buildArtifactBindingGraph(spec);
|
|
52
|
+
const artifact = graph.artifacts.find((entry) => entry.artifactId === artifactId);
|
|
53
|
+
if (!artifact || artifact.directConsumerNodeIds.length === 0)
|
|
54
|
+
return undefined;
|
|
55
|
+
const affectedNodeIds = collectDescendants(spec, artifact.directConsumerNodeIds);
|
|
56
|
+
const affectedSet = new Set(affectedNodeIds);
|
|
57
|
+
const importedNodeIds = spec.tasks
|
|
58
|
+
.map((task) => task.id)
|
|
59
|
+
.filter((nodeId) => !affectedSet.has(nodeId));
|
|
60
|
+
return {
|
|
61
|
+
artifactId,
|
|
62
|
+
producerNodeId: artifact.producerNodeId,
|
|
63
|
+
directConsumerNodeIds: artifact.directConsumerNodeIds,
|
|
64
|
+
effectiveFromNodeId: affectedNodeIds[0],
|
|
65
|
+
affectedNodeIds,
|
|
66
|
+
importedNodeIds,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function runtimeArtifactFields(node, kind) {
|
|
70
|
+
if (kind === "structured") {
|
|
71
|
+
return {
|
|
72
|
+
path: node.structuredArtifactPath,
|
|
73
|
+
sha256: node.structuredArtifactSha256,
|
|
74
|
+
schemaId: node.structuredArtifactSchemaId,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (kind === "assistant") {
|
|
78
|
+
return { path: node.assistantArtifactPath, sha256: node.assistantSha256 };
|
|
79
|
+
}
|
|
80
|
+
return { path: node.stdoutArtifactPath, sha256: node.stdoutSha256 };
|
|
81
|
+
}
|
|
82
|
+
export async function materializeDeclaredArtifactFacts(input) {
|
|
83
|
+
const facts = [];
|
|
84
|
+
for (const declaration of input.task.producesArtifacts ?? []) {
|
|
85
|
+
const runtime = runtimeArtifactFields(input.node, declaration.kind);
|
|
86
|
+
const base = { ...declaration, revisionEligible: false };
|
|
87
|
+
if (!runtime.path || !runtime.sha256) {
|
|
88
|
+
facts.push({
|
|
89
|
+
...base,
|
|
90
|
+
status: "missing",
|
|
91
|
+
reasonCode: "ARTIFACT_NOT_MATERIALIZED",
|
|
92
|
+
});
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const relativePath = path
|
|
96
|
+
.relative(input.runDir, runtime.path)
|
|
97
|
+
.split(path.sep)
|
|
98
|
+
.join("/");
|
|
99
|
+
if (relativePath !== declaration.path) {
|
|
100
|
+
facts.push({
|
|
101
|
+
...base,
|
|
102
|
+
status: "mismatch",
|
|
103
|
+
sha256: runtime.sha256,
|
|
104
|
+
reasonCode: "ARTIFACT_PATH_MISMATCH",
|
|
105
|
+
});
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (declaration.schemaId !== runtime.schemaId) {
|
|
109
|
+
facts.push({
|
|
110
|
+
...base,
|
|
111
|
+
status: "mismatch",
|
|
112
|
+
sha256: runtime.sha256,
|
|
113
|
+
reasonCode: "ARTIFACT_SCHEMA_MISMATCH",
|
|
114
|
+
});
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
let bytes;
|
|
118
|
+
try {
|
|
119
|
+
bytes = await readFile(runtime.path);
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
facts.push({
|
|
123
|
+
...base,
|
|
124
|
+
status: "missing",
|
|
125
|
+
reasonCode: "ARTIFACT_NOT_MATERIALIZED",
|
|
126
|
+
});
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const actualSha256 = createHash("sha256").update(bytes).digest("hex");
|
|
130
|
+
if (actualSha256 !== runtime.sha256) {
|
|
131
|
+
facts.push({
|
|
132
|
+
...base,
|
|
133
|
+
status: "mismatch",
|
|
134
|
+
sha256: actualSha256,
|
|
135
|
+
bytes: bytes.byteLength,
|
|
136
|
+
reasonCode: "ARTIFACT_HASH_MISMATCH",
|
|
137
|
+
});
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
facts.push({
|
|
141
|
+
...base,
|
|
142
|
+
status: "materialized",
|
|
143
|
+
sha256: actualSha256,
|
|
144
|
+
bytes: bytes.byteLength,
|
|
145
|
+
revisionEligible: declaration.revisionPolicy === "replace-file",
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return facts;
|
|
149
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { backendPytestCollectionFactsSchema, backendTestExecutionReadinessSchema } from "./backend-test-pytest-collection.js";
|
|
2
|
+
import { backendTestCaseManifestSchema } from "./backend-test-case-manifest.js";
|
|
3
|
+
import { backendTestAnalysisContractSchema } from "./backend-test-analysis-contract.js";
|
|
4
|
+
import { backendTestExecutionContractSchema } from "./backend-test-execution-contract.js";
|
|
5
|
+
import { backendTestCaseCoverageFactsSchema } from "./backend-test-case-coverage-analysis.js";
|
|
6
|
+
import { frontendImplementationContractSchema } from "./frontend-implementation-contract.js";
|
|
7
|
+
/**
|
|
8
|
+
* Standalone artifact revision schema registry (exec-plan
|
|
9
|
+
* 2026-08-25-console-node-context-artifact-rerun W4.3 / M4 exit criteria).
|
|
10
|
+
*
|
|
11
|
+
* Maps a frozen produced-artifact `schemaId` to a deterministic zod validator
|
|
12
|
+
* that can check candidate revision bytes without run context. Declared
|
|
13
|
+
* schemaIds without an entry here are rejected fail-closed with
|
|
14
|
+
* ARTIFACT_REVISION_SCHEMA_INVALID — an artifact cannot be staged until its
|
|
15
|
+
* schema is registered. Never guessed, never silently passed.
|
|
16
|
+
*/
|
|
17
|
+
const ARTIFACT_REVISION_SCHEMAS = Object.freeze({
|
|
18
|
+
"backend-test-pytest-collection-v3": backendPytestCollectionFactsSchema,
|
|
19
|
+
"backend-test-execution-readiness-v2": backendTestExecutionReadinessSchema,
|
|
20
|
+
"backend-test-case-manifest-v1": backendTestCaseManifestSchema,
|
|
21
|
+
"backend-test-analysis-v2": backendTestAnalysisContractSchema,
|
|
22
|
+
"backend-test-execution-v1": backendTestExecutionContractSchema,
|
|
23
|
+
"backend-test-case-coverage-facts-v4": backendTestCaseCoverageFactsSchema,
|
|
24
|
+
"frontend-implementation-contract-v1": frontendImplementationContractSchema,
|
|
25
|
+
});
|
|
26
|
+
export function resolveArtifactRevisionSchema(schemaId) {
|
|
27
|
+
return ARTIFACT_REVISION_SCHEMAS[schemaId];
|
|
28
|
+
}
|
|
29
|
+
export function listArtifactRevisionSchemaIds() {
|
|
30
|
+
return Object.keys(ARTIFACT_REVISION_SCHEMAS).sort();
|
|
31
|
+
}
|
|
@@ -668,6 +668,92 @@ function automationLabelBody(body, labels) {
|
|
|
668
668
|
const match = new RegExp(`^\\s*[-*+]\\s*(?:${escaped})\\s*[::]\\s*(.*)$`, "mi").exec(automation);
|
|
669
669
|
return match?.[1]?.replaceAll("`", "").trim() ?? "";
|
|
670
670
|
}
|
|
671
|
+
const BINDING_NEGATION_OR_AMBIGUITY = /(?:不是|并非|非变体|非断言|非横切|可能|待定|或者|\b(?:TBD|unknown)\b)/i;
|
|
672
|
+
function explicitBindingModeFromLine(line) {
|
|
673
|
+
if (BINDING_NEGATION_OR_AMBIGUITY.test(line))
|
|
674
|
+
return undefined;
|
|
675
|
+
const modes = TEST_POINT_BINDING_MODES.filter((mode) => AUTOMATION_BINDING_LABELS[mode].some((label) => line.includes(label)));
|
|
676
|
+
return modes.length === 1 ? modes[0] : undefined;
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Canonicalize only explicit, unambiguous Test Point classification marks.
|
|
680
|
+
* The model occasionally writes `TP-X(变体测试点)`, `[场景断言测试点] TP-X`
|
|
681
|
+
* or a pipe-delimited equivalent. Those forms carry deterministic semantics,
|
|
682
|
+
* but are not the machine contract consumed by automationLabelBody(). Keep the
|
|
683
|
+
* original prose and materialize the same binding under `### 自动化映射` as the
|
|
684
|
+
* canonical single-line labels. Never infer a mode from ordinary prose.
|
|
685
|
+
*/
|
|
686
|
+
export function canonicalizeBackendTestMarkdownTestPointBindings(markdown) {
|
|
687
|
+
const headings = [...markdown.matchAll(CASE_HEADING)];
|
|
688
|
+
let output = "";
|
|
689
|
+
let cursor = 0;
|
|
690
|
+
const unsafeTestPoints = [];
|
|
691
|
+
const canonicalizedTestPoints = [];
|
|
692
|
+
for (let index = 0; index < headings.length; index += 1) {
|
|
693
|
+
const heading = headings[index];
|
|
694
|
+
const start = heading.index;
|
|
695
|
+
const end = headings[index + 1]?.index ?? markdown.length;
|
|
696
|
+
const body = markdown.slice(start, end);
|
|
697
|
+
output += markdown.slice(cursor, start);
|
|
698
|
+
cursor = end;
|
|
699
|
+
const declared = listTokens(body, CASE_SECTION_ALIASES.testPoints, TEST_POINT);
|
|
700
|
+
const current = testPointBindingFacts(body, declared);
|
|
701
|
+
const currentByMode = new Map(TEST_POINT_BINDING_MODES.map((mode) => [
|
|
702
|
+
mode,
|
|
703
|
+
current.testPointBindings.filter((item) => item.mode === mode).map((item) => item.testPoint),
|
|
704
|
+
]));
|
|
705
|
+
const unresolved = new Set(current.unclassifiedTestPoints);
|
|
706
|
+
for (const line of body.split(/\r?\n/)) {
|
|
707
|
+
const mode = explicitBindingModeFromLine(line);
|
|
708
|
+
if (!mode)
|
|
709
|
+
continue;
|
|
710
|
+
const tokens = orderedUnique(line.match(/\bTP-[A-Z0-9]+(?:-[A-Z0-9]+)*\b/g) ?? []);
|
|
711
|
+
for (const token of tokens) {
|
|
712
|
+
if (!unresolved.has(token))
|
|
713
|
+
continue;
|
|
714
|
+
const existingModes = TEST_POINT_BINDING_MODES.filter((candidate) => (currentByMode.get(candidate) ?? []).includes(token));
|
|
715
|
+
if (existingModes.length > 0)
|
|
716
|
+
continue;
|
|
717
|
+
currentByMode.set(mode, orderedUnique([...(currentByMode.get(mode) ?? []), token]));
|
|
718
|
+
unresolved.delete(token);
|
|
719
|
+
canonicalizedTestPoints.push(token);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
unsafeTestPoints.push(...unresolved);
|
|
723
|
+
if (unresolved.size === current.unclassifiedTestPoints.length) {
|
|
724
|
+
output += body;
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
const automationMarker = /^###\s+(?:自动化映射|Automation Mapping)\s*$/mi.exec(body);
|
|
728
|
+
if (!automationMarker) {
|
|
729
|
+
output += body;
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
const automationStart = automationMarker.index + automationMarker[0].length;
|
|
733
|
+
const rest = body.slice(automationStart);
|
|
734
|
+
const nextSection = /^###\s+/m.exec(rest);
|
|
735
|
+
const automationEnd = automationStart + (nextSection?.index ?? rest.length);
|
|
736
|
+
let automation = body.slice(automationStart, automationEnd);
|
|
737
|
+
for (const mode of TEST_POINT_BINDING_MODES) {
|
|
738
|
+
const labels = AUTOMATION_BINDING_LABELS[mode];
|
|
739
|
+
const escaped = labels.map((label) => label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
|
|
740
|
+
const canonicalLine = `- ${AUTOMATION_BINDING_LABELS[mode][0]}:${(currentByMode.get(mode) ?? []).join("; ") || "无"}`;
|
|
741
|
+
const linePattern = new RegExp(`^\\s*[-*+]\\s*(?:${escaped})\\s*[::].*$`, "mi");
|
|
742
|
+
if (linePattern.test(automation))
|
|
743
|
+
automation = automation.replace(linePattern, canonicalLine);
|
|
744
|
+
else
|
|
745
|
+
automation = `${automation.replace(/\s*$/, "")}\n${canonicalLine}\n`;
|
|
746
|
+
}
|
|
747
|
+
output += body.slice(0, automationStart) + automation + body.slice(automationEnd);
|
|
748
|
+
}
|
|
749
|
+
output += markdown.slice(cursor);
|
|
750
|
+
return {
|
|
751
|
+
markdown: output,
|
|
752
|
+
changed: output !== markdown,
|
|
753
|
+
unsafeTestPoints: orderedUnique(unsafeTestPoints),
|
|
754
|
+
canonicalizedTestPoints: orderedUnique(canonicalizedTestPoints),
|
|
755
|
+
};
|
|
756
|
+
}
|
|
671
757
|
export function inspectBackendTestMarkdownTestPointBindings(markdown) {
|
|
672
758
|
const headings = [...markdown.matchAll(CASE_HEADING)];
|
|
673
759
|
return headings.map((heading, index) => {
|
|
@@ -1312,8 +1398,16 @@ export async function analyzeBackendTestCaseCoverage(input) {
|
|
|
1312
1398
|
function normalizeModuleStem(markdownPath) {
|
|
1313
1399
|
return path.posix.basename(markdownPath).replace(/\.md$/i, "").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").replace(/_+/g, "_");
|
|
1314
1400
|
}
|
|
1315
|
-
function expectedScript(markdownPath, layout) {
|
|
1401
|
+
function expectedScript(markdownPath, layout, declaredScripts = []) {
|
|
1316
1402
|
const resolved = layout ?? resolveBackendTestLayout(undefined);
|
|
1403
|
+
if (declaredScripts.length === 1) {
|
|
1404
|
+
const candidate = declaredScripts[0].replaceAll("\\", "/").replace(/^\.\//, "");
|
|
1405
|
+
const prefix = resolved.scriptDir === "." ? "" : `${resolved.scriptDir}/`;
|
|
1406
|
+
const relative = prefix && candidate.startsWith(prefix) ? candidate.slice(prefix.length) : candidate;
|
|
1407
|
+
if ((!prefix || candidate.startsWith(prefix)) && !relative.includes("..") && /^test_[A-Za-z0-9_.-]+\.py$/.test(path.posix.basename(candidate))) {
|
|
1408
|
+
return candidate;
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1317
1411
|
return `${resolved.scriptDir}/test_${normalizeModuleStem(markdownPath)}.py`;
|
|
1318
1412
|
}
|
|
1319
1413
|
function symbolCaseId(symbol) {
|
|
@@ -1863,7 +1957,7 @@ export async function analyzeBackendTestMarkdownPytestCorrespondence(input) {
|
|
|
1863
1957
|
const layout = input.layout ?? resolveBackendTestLayout(undefined);
|
|
1864
1958
|
const cases = await parseMarkdownCases(input.workspaceRoot, layout);
|
|
1865
1959
|
const modulePaths = orderedUnique(cases.map((item) => item.markdownPath));
|
|
1866
|
-
const scripts = orderedUnique(cases.flatMap((item) => [
|
|
1960
|
+
const scripts = orderedUnique(cases.flatMap((item) => [expectedScript(item.markdownPath, layout, item.declaredScripts)]));
|
|
1867
1961
|
const allSymbols = [];
|
|
1868
1962
|
const scriptFiles = [];
|
|
1869
1963
|
for (const script of scripts) {
|
|
@@ -1876,7 +1970,7 @@ export async function analyzeBackendTestMarkdownPytestCorrespondence(input) {
|
|
|
1876
1970
|
const findings = [];
|
|
1877
1971
|
const entries = [];
|
|
1878
1972
|
for (const testCase of cases) {
|
|
1879
|
-
const expected = expectedScript(testCase.markdownPath, layout);
|
|
1973
|
+
const expected = expectedScript(testCase.markdownPath, layout, testCase.declaredScripts);
|
|
1880
1974
|
const declared = testCase.declaredScripts[0];
|
|
1881
1975
|
const refs = allSymbols.filter((item) => item.caseIds.includes(testCase.caseId));
|
|
1882
1976
|
const actualScripts = orderedUnique(refs.map((item) => item.script));
|
|
@@ -2034,8 +2128,9 @@ export async function analyzeBackendTestMarkdownPytestCorrespondence(input) {
|
|
|
2034
2128
|
for (const [signature, owners] of duplicateSignatureGroups)
|
|
2035
2129
|
findings.push(`duplicate execution signature ${signature}: ${orderedUnique(owners).join(", ")}`);
|
|
2036
2130
|
const exactModuleCount = modulePaths.filter((modulePath) => {
|
|
2037
|
-
const expected = expectedScript(modulePath, layout);
|
|
2038
2131
|
const moduleCases = cases.filter((item) => item.markdownPath === modulePath);
|
|
2132
|
+
const moduleDeclaredScripts = orderedUnique(moduleCases.flatMap((item) => item.declaredScripts));
|
|
2133
|
+
const expected = expectedScript(modulePath, layout, moduleDeclaredScripts);
|
|
2039
2134
|
return moduleCases.every((item) => item.declaredScripts.length === 1 && item.declaredScripts[0] === expected) && scriptFiles.some((file) => path.relative(input.workspaceRoot, file).replaceAll(path.sep, "/") === expected);
|
|
2040
2135
|
}).length;
|
|
2041
2136
|
const caseEntries = entries.filter((entry) => entry.cardinality !== "0:1");
|
|
@@ -285,6 +285,7 @@ export async function resolveBackendTestManifestModules(workspaceRoot, layout) {
|
|
|
285
285
|
moduleIndexHeadingCount,
|
|
286
286
|
declaredStems: [],
|
|
287
287
|
declaredMdFiles: [],
|
|
288
|
+
declaredScriptByMarkdown: {},
|
|
288
289
|
declaredMissing: [],
|
|
289
290
|
unmanifested: [],
|
|
290
291
|
});
|
|
@@ -306,6 +307,15 @@ export async function resolveBackendTestManifestModules(workspaceRoot, layout) {
|
|
|
306
307
|
return invalid("duplicate", moduleIndexHeadingCount);
|
|
307
308
|
}
|
|
308
309
|
const declaredStems = extractDeclaredModuleIndexFilenames(readme);
|
|
310
|
+
const declaredScriptByMarkdown = {};
|
|
311
|
+
const mdPrefix = escapeRegExp(`${resolved.markdownDir}/`);
|
|
312
|
+
const scriptPrefix = escapeRegExp(`${resolved.scriptDir}/`);
|
|
313
|
+
for (const line of readme.split(/\r?\n/)) {
|
|
314
|
+
const markdown = new RegExp(`(${mdPrefix}[A-Za-z0-9_.-]+\\.md)`).exec(line)?.[1];
|
|
315
|
+
const pytest = new RegExp(`(${scriptPrefix}[A-Za-z0-9_./-]+\\.py)`).exec(line)?.[1];
|
|
316
|
+
if (markdown && pytest)
|
|
317
|
+
declaredScriptByMarkdown[markdown] = pytest;
|
|
318
|
+
}
|
|
309
319
|
if (declaredStems.length === 0) {
|
|
310
320
|
return invalid("empty-unparsable", moduleIndexHeadingCount);
|
|
311
321
|
}
|
|
@@ -345,6 +355,7 @@ export async function resolveBackendTestManifestModules(workspaceRoot, layout) {
|
|
|
345
355
|
moduleIndexHeadingCount,
|
|
346
356
|
declaredStems,
|
|
347
357
|
declaredMdFiles: declaredMdFiles.sort(),
|
|
358
|
+
declaredScriptByMarkdown,
|
|
348
359
|
declaredMissing: declaredMissing.sort(),
|
|
349
360
|
unmanifested: unmanifested.sort(),
|
|
350
361
|
};
|
|
@@ -694,7 +705,8 @@ export async function validateBackendMarkdownCases(input) {
|
|
|
694
705
|
if (!hasAssertableExpectedResult(expected)) {
|
|
695
706
|
findings.push(`${testCase.id} has no structured expected result`);
|
|
696
707
|
}
|
|
697
|
-
const expectedScript =
|
|
708
|
+
const expectedScript = manifest.declaredScriptByMarkdown[relativeFile] ??
|
|
709
|
+
expectedBackendTestPytestScriptForMarkdownModule(relativeFile, layout);
|
|
698
710
|
const mappedScripts = extractMappedPytestScripts(testCase.body);
|
|
699
711
|
if (isEvidenceOnlyMarkdownCaseBody(testCase.body)) {
|
|
700
712
|
// Evidence-only meta cases intentionally declare 脚本/primary symbol as 无 and keep only assertion/cross-cutting TPs.
|
|
@@ -934,7 +934,7 @@ export async function materializeBackendTestResultFromPytestHtml(input) {
|
|
|
934
934
|
outcome = "command-error";
|
|
935
935
|
}
|
|
936
936
|
const commandSummary = input.commandSummary ??
|
|
937
|
-
`PYTHONUTF8=1 PYTHONIOENCODING=utf-8 PYTHONDONTWRITEBYTECODE=1 python -
|
|
937
|
+
`PYTHONUTF8=1 PYTHONIOENCODING=utf-8 PYTHONDONTWRITEBYTECODE=1 python reports/backend-test-pytest-launcher.py reports/backend-test-pytest-targets.txt ${htmlRelativePath} -> pytest.main(-v -p no:cacheprovider --self-contained-html)`;
|
|
938
938
|
assertNoSecrets("commandSummary", commandSummary);
|
|
939
939
|
const result = backendTestResultContractSchema.parse({
|
|
940
940
|
schemaVersion: 1,
|
|
@@ -4,7 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { BACKEND_TEST_MAX_MODULE_COUNT, isOpaqueHashBackendTestModuleStem, isPriorityOnlyBackendTestModuleStem, } from "./backend-test-module-stem.js";
|
|
6
6
|
import { resolveBackendTestLayout, } from "./backend-test-layout.js";
|
|
7
|
-
import { inspectBackendTestMarkdownTestPointBindings } from "./backend-test-case-coverage-analysis.js";
|
|
7
|
+
import { canonicalizeBackendTestMarkdownTestPointBindings, inspectBackendTestMarkdownTestPointBindings, } from "./backend-test-case-coverage-analysis.js";
|
|
8
8
|
/**
|
|
9
9
|
* Maps a backend-test generation writer task id to its writer-progress role.
|
|
10
10
|
* Returns undefined for nodes that are not completeness-gated generators.
|
|
@@ -47,8 +47,16 @@ const progressSchema = z
|
|
|
47
47
|
path: z.string().optional(),
|
|
48
48
|
detail: z.string(),
|
|
49
49
|
recoverable: z.boolean(),
|
|
50
|
+
failureSubreason: z.literal("markdown-test-point-binding-format").optional(),
|
|
51
|
+
affectedCases: z.array(z.string()).optional(),
|
|
52
|
+
testPointIds: z.array(z.string()).optional(),
|
|
53
|
+
canonicalizationEligible: z.boolean().optional(),
|
|
54
|
+
canonicalizationOutcome: z.enum(["not-attempted", "changed", "already-canonical", "unsafe", "revalidation-failed"]).optional(),
|
|
50
55
|
})
|
|
51
56
|
.strict()),
|
|
57
|
+
canonicalizationOutcome: z.enum(["not-attempted", "changed", "already-canonical", "unsafe", "revalidation-failed"]).optional(),
|
|
58
|
+
canonicalizedTestPointCount: z.number().int().min(0).optional(),
|
|
59
|
+
lightweightRepairEligible: z.boolean().optional(),
|
|
52
60
|
})
|
|
53
61
|
.strict();
|
|
54
62
|
async function exists(filePath) {
|
|
@@ -520,6 +528,9 @@ export async function assessBackendTestShardChildCompleteness(input) {
|
|
|
520
528
|
const actualPaths = [];
|
|
521
529
|
const missingPaths = [];
|
|
522
530
|
const brokenPaths = [];
|
|
531
|
+
let canonicalizationOutcome = role === "md-generate" ? "already-canonical" : undefined;
|
|
532
|
+
let canonicalizedTestPointCount = 0;
|
|
533
|
+
let lightweightRepairEligible = false;
|
|
523
534
|
for (const target of targetPaths) {
|
|
524
535
|
// Rendered map-child writeSet entries are concrete (e.g.
|
|
525
536
|
// testcase/md/health.md / testcase/test_orders.py); glob-only entries are
|
|
@@ -538,16 +549,44 @@ export async function assessBackendTestShardChildCompleteness(input) {
|
|
|
538
549
|
continue;
|
|
539
550
|
}
|
|
540
551
|
actualPaths.push(target);
|
|
541
|
-
|
|
552
|
+
let body = await readFile(absolute, "utf8");
|
|
542
553
|
if (role === "md-generate") {
|
|
554
|
+
const beforeBindings = inspectBackendTestMarkdownTestPointBindings(body);
|
|
555
|
+
const canonicalization = canonicalizeBackendTestMarkdownTestPointBindings(body);
|
|
556
|
+
if (canonicalization.changed) {
|
|
557
|
+
await writeFile(absolute, canonicalization.markdown, "utf8");
|
|
558
|
+
body = canonicalization.markdown;
|
|
559
|
+
canonicalizationOutcome = "changed";
|
|
560
|
+
canonicalizedTestPointCount += canonicalization.canonicalizedTestPoints.length;
|
|
561
|
+
}
|
|
562
|
+
const afterBindings = inspectBackendTestMarkdownTestPointBindings(body);
|
|
563
|
+
const unresolved = afterBindings.flatMap((entry) => entry.unclassifiedTestPoints);
|
|
564
|
+
const bindingCases = afterBindings
|
|
565
|
+
.filter((entry) => entry.unclassifiedTestPoints.length > 0 || entry.duplicateBindingTestPoints.length > 0)
|
|
566
|
+
.map((entry) => entry.caseId);
|
|
543
567
|
const structural = moduleStructurallyComplete(body);
|
|
544
568
|
if (!structural.ok) {
|
|
569
|
+
lightweightRepairEligible = bindingCases.length > 0;
|
|
570
|
+
if (bindingCases.length > 0)
|
|
571
|
+
canonicalizationOutcome = canonicalization.changed ? "revalidation-failed" : "unsafe";
|
|
545
572
|
brokenPaths.push(target);
|
|
573
|
+
const bindingFormatIssue = bindingCases.length > 0;
|
|
546
574
|
issues.push({
|
|
547
575
|
code: "T5",
|
|
548
576
|
path: target,
|
|
549
577
|
detail: moduleStructuralDetail(structural),
|
|
550
578
|
recoverable: true,
|
|
579
|
+
...(bindingFormatIssue
|
|
580
|
+
? {
|
|
581
|
+
failureSubreason: "markdown-test-point-binding-format",
|
|
582
|
+
affectedCases: orderedUnique(bindingCases),
|
|
583
|
+
testPointIds: orderedUnique(unresolved),
|
|
584
|
+
canonicalizationEligible: canonicalization.canonicalizedTestPoints.length > 0,
|
|
585
|
+
canonicalizationOutcome: canonicalization.changed
|
|
586
|
+
? (unresolved.length === 0 ? "changed" : "revalidation-failed")
|
|
587
|
+
: (beforeBindings.some((entry) => entry.unclassifiedTestPoints.length > 0) ? "unsafe" : "already-canonical"),
|
|
588
|
+
}
|
|
589
|
+
: {}),
|
|
551
590
|
});
|
|
552
591
|
}
|
|
553
592
|
}
|
|
@@ -586,6 +625,9 @@ export async function assessBackendTestShardChildCompleteness(input) {
|
|
|
586
625
|
brokenPaths: orderedUnique(brokenPaths),
|
|
587
626
|
targetPaths: recoverableTargets,
|
|
588
627
|
issues,
|
|
628
|
+
...(role === "md-generate"
|
|
629
|
+
? { canonicalizationOutcome, canonicalizedTestPointCount, lightweightRepairEligible }
|
|
630
|
+
: {}),
|
|
589
631
|
};
|
|
590
632
|
}
|
|
591
633
|
export async function assessBackendTestMdWriterCompleteness(workspaceRoot, layout) {
|
|
@@ -853,7 +895,7 @@ export async function writeBackendTestWriterProgressArtifacts(input) {
|
|
|
853
895
|
"## Issues",
|
|
854
896
|
"",
|
|
855
897
|
...(input.progress.issues.length
|
|
856
|
-
? input.progress.issues.map((issue) => `- [${issue.code}] ${issue.path ?? "(workspace)"}: ${issue.detail}`)
|
|
898
|
+
? input.progress.issues.map((issue) => `- [${issue.code}] ${issue.path ?? "(workspace)"}: ${issue.detail}${issue.failureSubreason ? `; failureSubreason=${issue.failureSubreason}; canonicalizationOutcome=${issue.canonicalizationOutcome ?? "not-attempted"}; canonicalizationEligible=${issue.canonicalizationEligible ?? false}; affectedCases=${(issue.affectedCases ?? []).join(",") || "none"}; testPointIds=${(issue.testPointIds ?? []).join(",") || "none"}` : ""}`)
|
|
857
899
|
: ["- None"]),
|
|
858
900
|
"",
|
|
859
901
|
`<!-- sha256:${createHash("sha256").update(JSON.stringify(parsed)).digest("hex")} -->`,
|
|
@@ -8,9 +8,19 @@ export function initRunBudgetLedger(state, budget) {
|
|
|
8
8
|
state.budgetLedger = createBudgetLedger(budget);
|
|
9
9
|
}
|
|
10
10
|
function wallTimeMs(state, now = Date.now()) {
|
|
11
|
+
const lineage = state.continuation?.budgetLineage;
|
|
12
|
+
if (lineage) {
|
|
13
|
+
return (lineage.inheritedConsumed.wallTimeMs +
|
|
14
|
+
Math.max(0, now - new Date(lineage.inheritedAt).getTime()));
|
|
15
|
+
}
|
|
11
16
|
return Math.max(0, now - new Date(state.startedAt).getTime());
|
|
12
17
|
}
|
|
13
18
|
function repairPasses(state) {
|
|
19
|
+
const lineage = state.continuation?.budgetLineage;
|
|
20
|
+
if (lineage) {
|
|
21
|
+
return (lineage.inheritedConsumed.repairPasses +
|
|
22
|
+
Math.max(0, (state.convergence?.currentPass ?? 1) - 1));
|
|
23
|
+
}
|
|
14
24
|
return state.convergence?.currentPass ?? 0;
|
|
15
25
|
}
|
|
16
26
|
export function applyHardBudgetBreach(state, breach) {
|