@tea-agent/loop-agent 0.42.0-next.1 → 0.42.0-next.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +321 -1
- package/dist/application/dag/generate-task-dag.js +75 -19
- package/dist/application/dag/run-dag.js +41 -0
- package/dist/application/task-lifecycle/advance.js +24 -5
- package/dist/application/task-lifecycle/observe.js +171 -17
- package/dist/application/task-lifecycle/plan-transitions.js +42 -7
- package/dist/application/task-lifecycle/recommendations.js +13 -2
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +7 -0
- package/dist/cli/program.js +6 -1
- package/dist/commands/client-recovery.js +3 -0
- package/dist/commands/dag-follow-up.js +138 -0
- package/dist/commands/dag-rerun-task.js +2 -0
- package/dist/commands/init.js +27 -1
- package/dist/commands/task-advance.js +19 -0
- package/dist/executors/dag-pi-executor.js +4010 -78
- package/dist/executors/pi-executor.js +15 -4
- package/dist/executors/pi-extension-resolver.js +14 -2
- package/dist/executors/pi-read-budget-policy.js +239 -0
- package/dist/executors/shell-executor.js +978 -188
- package/dist/executors/shell-write-guard.js +7 -0
- package/dist/infrastructure/console/operation-store.js +226 -6
- package/dist/shared/dag-failure-category.js +12 -0
- package/dist/shared/openspec-spec.js +70 -4
- package/dist/shared/operator/capabilities.js +7 -0
- package/dist/task/config-types.js +105 -5
- package/dist/task/contract/adopt.js +4 -0
- package/dist/task/contract/import-revision.js +4 -0
- package/dist/task/contract/project.js +3 -0
- package/dist/task/contract/schema.js +2 -1
- package/dist/task/frontend-project-capability.js +203 -20
- package/dist/task/runtime.js +5 -2
- package/dist/task/source-prepare/build-draft.js +3 -3
- package/dist/task/source-prepare/fragment-inventory.js +64 -25
- package/dist/task/source-prepare/prepare.js +126 -21
- package/dist/task/source-prepare/semantic-intake.js +6 -2
- package/dist/task/source-references.js +22 -1
- package/dist/worker/console/chat/chat-ui-policy.js +4 -2
- package/dist/worker/console/chat/model-resolver.js +7 -4
- package/dist/worker/console/chat/pi-runtime.js +258 -11
- package/dist/worker/console/chat/repo-browser.js +10 -3
- package/dist/worker/console/chat/routes.js +165 -71
- package/dist/worker/console/chat/sdd-data-alignment.js +222 -0
- package/dist/worker/console/chat/session-catalog.js +2 -0
- package/dist/worker/console/chat/session-store.js +159 -70
- package/dist/worker/console/chat/shortcuts.js +19 -3
- package/dist/worker/console/chat/turn-process.js +111 -56
- 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/operator-actions.js +169 -7
- package/dist/worker/console/pi-readiness.js +3 -2
- package/dist/worker/console/server.js +23 -1
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-DZZ8m3PO.js → abnfDiagram-N423BO3Z-CcS17TBr.js} +1 -1
- package/dist/worker/console/static/assets/{arc-D6PvaVd-.js → arc-COptKq2S.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-B_OTOiI8.js → architectureDiagram-T3A2C74G-h4LKMHjP.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-Bv6rqHBg.js → blockDiagram-VBNYF7ZC-COA1MOH0.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-B8eHr0oz.js → c4Diagram-5PPSVZJV-DJUf0QPm.js} +1 -1
- package/dist/worker/console/static/assets/channel-DdBCaOJ6.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-DYwR0im2.js → chunk-2GRJ4B5K-mtWfKrUX.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-D2WPGqXt.js → chunk-2Q5K7J3B-B8pXsxDQ.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-CQISJ_I7.js → chunk-5RXB4S5H-ipKzByl1.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-C0o2Du1e.js → chunk-5VM5RSS4-DYi3Ald_.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-4f8kr-U7.js → chunk-6Q2QTUOP-DQtGYoty.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-D_OWvXzX.js → chunk-GF5L2VYU-BU0qS2YV.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-CasdPz5X.js → chunk-JWPE2WC7-DVH9dIGE.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-Cj8lRrla.js → chunk-KBJHAD2P-Dt60SmgM.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CUvD1FSd.js → chunk-RYQCIY6F-BoaUGuEY.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CqLw_eqB.js → chunk-XXDRQBXY-_WHFDTp2.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DZFra1GO.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DZFra1GO.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-ku-WqJPx.js → cose-bilkent-JH36ORCC-BxkbTIRd.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-Bq_PYDhl.js → cynefin-VYW2F7L2-Bf2UVnoG.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-BiGZV641.js → cynefinDiagram-MW4NZA55-Bo23q1J_.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-C4JRlglF.js → dagre-VZM6K2ZE-Dd_UU49i.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-B0dNeWYG.js → diagram-7IWD3JNH-ebUa1a9y.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-Cj35YvYM.js → diagram-B4RE2ZJO-BucthU8r.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-uzQoJ2-8.js → diagram-LBJQPF4R-BgqNpAkm.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D1a-Buoz.js → diagram-Q27KOJAE-DC4q5NGa.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-XjRrLRSs.js → diagram-UB23O5K3-CBqCaMeb.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-Da_O24cW.js → ebnfDiagram-BXEA7PRR-CGtnbQ3-.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-BLJ8jrYU.js → erDiagram-JOGREHBK-CG3LUao5.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-B9GrLjM0.js → flowDiagram-UKHOOZJN-D2ZVgoFS.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CeJ0TqiK.js → ganttDiagram-PKOTCBZU-DoYFZiKt.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Bm2eNNFX.js → gitGraphDiagram-DS77QQ5N-CKoP1s6j.js} +1 -1
- package/dist/worker/console/static/assets/index-CAZ2fC_X.css +1 -0
- package/dist/worker/console/static/assets/index-vbTcFnFs.js +449 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DB26i3d8.js → infoDiagram-6WML65LV-Duofv8p2.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-BOGRyeZT.js → ishikawaDiagram-WSZJBQD7-D2nlCkA1.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-CZRoBO_6.js → journeyDiagram-NVQOT4AX-Dd4IHdus.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-qlWhJyoB.js → kanban-definition-27J2QSJJ-Bi20AOdt.js} +1 -1
- package/dist/worker/console/static/assets/{linear-CfUiDDB3.js → linear-D59YJ9kB.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BQe6fpqj.js → mermaid.core-Bl13LpNa.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-zFzWHw64.js → mindmap-definition-FAOFIHXS-C6DLP5eY.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DFGUqjZO.js → pegDiagram-VL7TDLO6-BjO4cy64.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-BB5i0l8Z.js → pieDiagram-7S7Q4E2Y-D7i2qZTG.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-obbVZ_X5.js → quadrantDiagram-CIZ2JOQS-Dx8o_h0y.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BwdaNzc1.js → railroadDiagram-AXF67PYL-B55orI_t.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-CkTJaugh.js → requirementDiagram-LRYGKXZP-zWaehp6f.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-BwJ-hIgq.js → sankeyDiagram-W5VNT64P-CcA-pjvD.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BzSgmm7w.js → sequenceDiagram-SI44F4Z6-BOFbzNHI.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-Bn3oj5Q1.js → sizeCapture-X5ZJPWSS-BjAejah1.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-dxhGtL8K.js → stateDiagram-OKZ733FA-DXYwxJwZ.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Clg3V9t1.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-BLpEInya.js → swimlanes-SLNWSIFB-OYiOch8n.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DX1dxAAW.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-ChJGYcXy.js → timeline-definition-Z64GVDOM-cZfH3nmU.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DK-qjRer.js → vennDiagram-T6HMQDX7-BDE7b3E1.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-3qeaWAg-.js → wardleyDiagram-T6FBY63Y-kyyGy9WJ.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CootlyP9.js → xychartDiagram-ELKLHX3M-CJj6VTog.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/useRecoveryConsole.js +5 -5
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +19 -8
- package/dist/worker/console/static-src/operator-chat/input-history.js +8 -6
- package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +15 -7
- package/dist/worker/console/static-src/operator-chat/runtime-snapshot-store.js +10 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +258 -23
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +112 -4
- package/dist/worker/console/static-src/operator-chat/useComposer.js +13 -4
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +29 -6
- package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +8 -2
- package/dist/worker/console/static-src/shell/console-update-reload.js +36 -9
- package/dist/worker/console/static-src/shell/workspace-route.js +11 -0
- package/dist/worker/console/workspace-context.js +115 -1
- package/dist/worker/materialize/frontend-split-task-materializer.js +72 -0
- package/dist/worker/observe/routes.js +4 -0
- package/dist/worker/observe/static/constants.js +22 -22
- package/dist/worker/observe/static/dag-context-reason-labels.js +19 -0
- package/dist/worker/observe/static/dag-helpers.js +4 -2
- package/dist/worker/observe/static/dag-history-labels.js +1 -0
- package/dist/worker/observe/static/dag-inspector-humanize.d.ts +16 -0
- package/dist/worker/observe/static/dag-inspector-humanize.js +339 -0
- package/dist/worker/observe/static/dag-node-purpose.js +13 -8
- package/dist/worker/observe/static/index.html +4 -4
- package/dist/worker/observe/static/inspect-workspace.js +23 -0
- package/dist/worker/observe/static/kpi.js +2 -2
- package/dist/worker/observe/static/operator-chrome.d.ts +10 -2
- package/dist/worker/observe/static/operator-chrome.js +37 -23
- package/dist/worker/observe/static/prompt-restart-candidates.js +4 -2
- package/dist/worker/observe/static/relations.js +7 -7
- package/dist/worker/observe/static/router.d.ts +12 -1
- package/dist/worker/observe/static/router.js +48 -6
- package/dist/worker/observe/static/run-processing.js +4 -2
- package/dist/worker/observe/static/shell-chrome.js +2 -2
- package/dist/worker/observe/static/state.js +5 -0
- package/dist/worker/observe/static/styles.css +231 -0
- package/dist/worker/observe/static/views/dag-graph.js +1 -1
- package/dist/worker/observe/static/views/dag-inspector.js +336 -181
- package/dist/worker/observe/static/views/dag.js +26 -14
- package/dist/worker/observe/static/views/dags.js +3 -1
- package/dist/worker/observe/static/views/dashboard.js +14 -6
- package/dist/worker/observe/static/views/pool.js +7 -2
- package/dist/worker/observe/static/views/run.js +12 -3
- package/dist/worker/observe/static/views/session-timeline.js +74 -13
- package/dist/worker/observe/static/views/task.js +7 -2
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +215 -28
- package/dist/workflows/dag/backend-test-markdown-workflow.js +13 -1
- package/dist/workflows/dag/backend-test-plan-protocol.js +210 -0
- package/dist/workflows/dag/backend-test-scenario-param.js +172 -25
- package/dist/workflows/dag/backend-test-scenario-partitions.js +62 -1
- package/dist/workflows/dag/backend-test-writer-completeness.js +104 -12
- package/dist/workflows/dag/contract-validator-registrations.js +1 -2
- package/dist/workflows/dag/dag-retry-schema.js +138 -0
- 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 +1237 -192
- package/dist/workflows/dag/frontend-plan-render.js +2 -1
- package/dist/workflows/dag/frontend-prewrite-gate.js +256 -350
- 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 +226 -0
- package/dist/workflows/dag/frontend-recovery-lineage.js +178 -0
- package/dist/workflows/dag/frontend-recovery-plan.js +21 -10
- package/dist/workflows/dag/frontend-recovery-run.js +166 -34
- package/dist/workflows/dag/frontend-repair.js +1 -432
- package/dist/workflows/dag/frontend-review-context.js +261 -15
- package/dist/workflows/dag/frontend-review-findings.js +270 -0
- package/dist/workflows/dag/frontend-shadow-dual-write.js +941 -0
- package/dist/workflows/dag/frontend-shape-capsule-store.js +191 -0
- package/dist/workflows/dag/frontend-shape-facts.js +419 -0
- package/dist/workflows/dag/frontend-shape.js +435 -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-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 +252 -25
- package/dist/workflows/dag/frontend-worktree-diff.js +250 -17
- package/dist/workflows/dag/frontend-writer-admission.js +319 -0
- package/dist/workflows/dag/frontend-writer-status.js +256 -0
- package/dist/workflows/dag/init-hybrid.js +1031 -564
- package/dist/workflows/dag/interrupt-request.js +7 -0
- package/dist/workflows/dag/node-execution.js +854 -4
- package/dist/workflows/dag/prompt.js +70 -3
- package/dist/workflows/dag/recovery-lease.js +170 -0
- package/dist/workflows/dag/report.js +37 -1
- package/dist/workflows/dag/rerun-feedback.js +315 -1
- package/dist/workflows/dag/rerun-plan.js +17 -6
- package/dist/workflows/dag/rerun-run.js +44 -6
- package/dist/workflows/dag/rerun-task.js +251 -13
- package/dist/workflows/dag/retry-policy.js +230 -104
- package/dist/workflows/dag/runner.js +580 -124
- package/dist/workflows/dag/scheduler.js +133 -20
- package/dist/workflows/dag/types.js +244 -16
- package/dist/workflows/dag/validate.js +28 -12
- package/docs/examples/README.md +5 -0
- package/docs/init-surface.manifest.json +30 -12
- package/docs/skills/vetted-skill-registry.md +4 -2
- package/docs/templates/README.md +2 -0
- package/docs/templates/agent-dag-report.schema.json +8 -2
- package/docs/templates/agent-dag.schema.json +1 -1
- package/docs/templates/backend-test-dag.json +25 -20
- package/docs/templates/frontend-implementation-contract.schema.json +4 -1
- package/docs/templates/frontend-implementation-dag.json +89 -0
- package/docs/templates/spec-registry.schema.json +45 -0
- package/harness.json +1 -1
- package/package.json +4 -3
- package/skills/frontend-bounded-implement/SKILL.md +15 -14
- package/skills/frontend-bounded-implement/references/code-standards.md +19 -0
- package/skills/frontend-contract/SKILL.md +23 -0
- package/skills/frontend-contract/references/contract-protocol.md +34 -0
- package/skills/frontend-design-review/SKILL.md +22 -41
- package/skills/frontend-plan/SKILL.md +26 -0
- package/skills/frontend-plan/references/decision-contract.md +37 -0
- package/skills/frontend-plan/references/design-decisions.md +17 -0
- package/skills/frontend-review/SKILL.md +20 -15
- package/skills/frontend-review/references/review-findings.md +6 -7
- package/skills/frontend-scout/SKILL.md +25 -0
- package/skills/frontend-scout/references/design-evidence.md +16 -0
- package/skills/frontend-scout/references/scout-evidence.md +23 -0
- package/skills/frontend-verification/SKILL.md +1 -1
- package/skills/loop-agent/references/command-reference.md +1 -0
- package/skills/loop-agent/references/hybrid-dag.md +2 -2
- package/dist/worker/console/static/assets/channel-BU5gOilw.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DIzKJGHr.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DIzKJGHr.js +0 -1
- package/dist/worker/console/static/assets/index-H9rFJiGL.css +0 -1
- package/dist/worker/console/static/assets/index-xwu9GxEc.js +0 -451
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Cld2qK9v.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-TIHpiT7w.js +0 -8
- package/skills/frontend-implementation/SKILL.md +0 -52
- package/skills/frontend-implementation/references/code-standards.md +0 -33
- package/skills/frontend-implementation/references/design-spec.md +0 -56
- package/skills/frontend-implementation/references/node-contracts.md +0 -31
|
@@ -4,7 +4,8 @@ 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
|
+
import { BACKEND_TEST_SCENARIO_PARTITION_SLOTS_SCHEMA_ID, canonicalizeBackendTestPlanProtocolHeadings, collectMarkdownPartitionSlotCoverage, lookupRequiredVariantSlotsForMarkdownPath, } from "./backend-test-plan-protocol.js";
|
|
8
9
|
/**
|
|
9
10
|
* Maps a backend-test generation writer task id to its writer-progress role.
|
|
10
11
|
* Returns undefined for nodes that are not completeness-gated generators.
|
|
@@ -47,8 +48,16 @@ const progressSchema = z
|
|
|
47
48
|
path: z.string().optional(),
|
|
48
49
|
detail: z.string(),
|
|
49
50
|
recoverable: z.boolean(),
|
|
51
|
+
failureSubreason: z.literal("markdown-test-point-binding-format").optional(),
|
|
52
|
+
affectedCases: z.array(z.string()).optional(),
|
|
53
|
+
testPointIds: z.array(z.string()).optional(),
|
|
54
|
+
canonicalizationEligible: z.boolean().optional(),
|
|
55
|
+
canonicalizationOutcome: z.enum(["not-attempted", "changed", "already-canonical", "unsafe", "revalidation-failed"]).optional(),
|
|
50
56
|
})
|
|
51
57
|
.strict()),
|
|
58
|
+
canonicalizationOutcome: z.enum(["not-attempted", "changed", "already-canonical", "unsafe", "revalidation-failed"]).optional(),
|
|
59
|
+
canonicalizedTestPointCount: z.number().int().min(0).optional(),
|
|
60
|
+
lightweightRepairEligible: z.boolean().optional(),
|
|
52
61
|
})
|
|
53
62
|
.strict();
|
|
54
63
|
async function exists(filePath) {
|
|
@@ -119,10 +128,8 @@ function looksLikeValidModuleStem(raw) {
|
|
|
119
128
|
return moduleStemInvalidReason(raw) === undefined;
|
|
120
129
|
}
|
|
121
130
|
function extractCanonicalModuleIndexSection(readme) {
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
.replaceAll("\r", "\n")
|
|
125
|
-
.split("\n");
|
|
131
|
+
const canonical = canonicalizeBackendTestPlanProtocolHeadings(readme).markdown;
|
|
132
|
+
const lines = canonical.split("\n");
|
|
126
133
|
const headings = lines.flatMap((line, index) => line.trim() === "## Module Index" ? [index] : []);
|
|
127
134
|
if (headings.length === 0) {
|
|
128
135
|
return { section: "", structureIssues: ["missing-module-index"] };
|
|
@@ -362,8 +369,6 @@ function pythonParseable(source) {
|
|
|
362
369
|
const normalized = source.replace(/\r\n/g, "\n");
|
|
363
370
|
if (!normalized.trim())
|
|
364
371
|
return false;
|
|
365
|
-
if (/^\s*(?:def|class|import|from)\b/m.test(normalized) === false)
|
|
366
|
-
return false;
|
|
367
372
|
const structural = pythonStructureOutsideStrings(normalized);
|
|
368
373
|
if (!structural.stringsClosed)
|
|
369
374
|
return false;
|
|
@@ -380,7 +385,13 @@ function pythonParseable(source) {
|
|
|
380
385
|
return false;
|
|
381
386
|
if (openBraces !== closeBraces)
|
|
382
387
|
return false;
|
|
383
|
-
|
|
388
|
+
// Business modules still need a statement. Evidence-only mapped scripts may
|
|
389
|
+
// be a closed module docstring + Case-ID with no `def test_` (R10 health).
|
|
390
|
+
if (/^\s*(?:def|class|import|from|__all__\s*=)/m.test(normalized))
|
|
391
|
+
return true;
|
|
392
|
+
return (/^\s*(?:'''|""")/.test(normalized) &&
|
|
393
|
+
/\bCase-ID\s*:/i.test(normalized) &&
|
|
394
|
+
!/^\s*(?:async\s+)?def\s+test_/m.test(normalized));
|
|
384
395
|
}
|
|
385
396
|
/**
|
|
386
397
|
* Forbidden generated shared helper/factory namespaces for the self-contained
|
|
@@ -511,6 +522,19 @@ export async function loadBackendTestWriterProgressForRetry(runDir, taskId) {
|
|
|
511
522
|
* child's own target file(s): md children are checked for structural
|
|
512
523
|
* completeness, pytest children for parseability. Sibling modules are ignored.
|
|
513
524
|
*/
|
|
525
|
+
async function loadScenarioPartitionSlotInventory(runDir) {
|
|
526
|
+
if (!runDir)
|
|
527
|
+
return undefined;
|
|
528
|
+
try {
|
|
529
|
+
const raw = JSON.parse(await readFile(path.join(runDir, "contracts", "backend-test-scenario-partition-slots.json"), "utf8"));
|
|
530
|
+
if (raw?.schemaId !== BACKEND_TEST_SCENARIO_PARTITION_SLOTS_SCHEMA_ID)
|
|
531
|
+
return undefined;
|
|
532
|
+
return raw;
|
|
533
|
+
}
|
|
534
|
+
catch {
|
|
535
|
+
return undefined;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
514
538
|
export async function assessBackendTestShardChildCompleteness(input) {
|
|
515
539
|
const role = input.role;
|
|
516
540
|
const targetPaths = input.writeSet
|
|
@@ -520,6 +544,12 @@ export async function assessBackendTestShardChildCompleteness(input) {
|
|
|
520
544
|
const actualPaths = [];
|
|
521
545
|
const missingPaths = [];
|
|
522
546
|
const brokenPaths = [];
|
|
547
|
+
let canonicalizationOutcome = role === "md-generate" ? "already-canonical" : undefined;
|
|
548
|
+
let canonicalizedTestPointCount = 0;
|
|
549
|
+
let lightweightRepairEligible = false;
|
|
550
|
+
const slotInventory = role === "md-generate"
|
|
551
|
+
? await loadScenarioPartitionSlotInventory(input.runDir)
|
|
552
|
+
: undefined;
|
|
523
553
|
for (const target of targetPaths) {
|
|
524
554
|
// Rendered map-child writeSet entries are concrete (e.g.
|
|
525
555
|
// testcase/md/health.md / testcase/test_orders.py); glob-only entries are
|
|
@@ -538,16 +568,65 @@ export async function assessBackendTestShardChildCompleteness(input) {
|
|
|
538
568
|
continue;
|
|
539
569
|
}
|
|
540
570
|
actualPaths.push(target);
|
|
541
|
-
|
|
571
|
+
let body = await readFile(absolute, "utf8");
|
|
542
572
|
if (role === "md-generate") {
|
|
573
|
+
const beforeBindings = inspectBackendTestMarkdownTestPointBindings(body);
|
|
574
|
+
const canonicalization = canonicalizeBackendTestMarkdownTestPointBindings(body);
|
|
575
|
+
if (canonicalization.changed) {
|
|
576
|
+
await writeFile(absolute, canonicalization.markdown, "utf8");
|
|
577
|
+
body = canonicalization.markdown;
|
|
578
|
+
canonicalizationOutcome = "changed";
|
|
579
|
+
canonicalizedTestPointCount += canonicalization.canonicalizedTestPoints.length;
|
|
580
|
+
}
|
|
581
|
+
const afterBindings = inspectBackendTestMarkdownTestPointBindings(body);
|
|
582
|
+
const unresolved = afterBindings.flatMap((entry) => entry.unclassifiedTestPoints);
|
|
583
|
+
const bindingCases = afterBindings
|
|
584
|
+
.filter((entry) => entry.unclassifiedTestPoints.length > 0 || entry.duplicateBindingTestPoints.length > 0)
|
|
585
|
+
.map((entry) => entry.caseId);
|
|
543
586
|
const structural = moduleStructurallyComplete(body);
|
|
587
|
+
const requiredSlots = lookupRequiredVariantSlotsForMarkdownPath(slotInventory, target);
|
|
588
|
+
const slotCoverage = requiredSlots.length > 0
|
|
589
|
+
? collectMarkdownPartitionSlotCoverage(body, requiredSlots)
|
|
590
|
+
: undefined;
|
|
591
|
+
const missingSlots = orderedUnique([
|
|
592
|
+
...(slotCoverage?.missingFromDeclared ?? []),
|
|
593
|
+
...(slotCoverage?.missingFromVariant ?? []),
|
|
594
|
+
]);
|
|
595
|
+
if (missingSlots.length > 0) {
|
|
596
|
+
lightweightRepairEligible = true;
|
|
597
|
+
if (!brokenPaths.includes(target))
|
|
598
|
+
brokenPaths.push(target);
|
|
599
|
+
issues.push({
|
|
600
|
+
code: "T5",
|
|
601
|
+
path: target,
|
|
602
|
+
detail: `incomplete-partition-slot-set: missing ${missingSlots.join(", ")}; Scheme A requires each exact slot as an independent variant Test Point, not Case count or SINGLE/MULTIPLE aliases`,
|
|
603
|
+
recoverable: true,
|
|
604
|
+
affectedCases: afterBindings.map((entry) => entry.caseId),
|
|
605
|
+
testPointIds: missingSlots,
|
|
606
|
+
});
|
|
607
|
+
}
|
|
544
608
|
if (!structural.ok) {
|
|
609
|
+
lightweightRepairEligible = bindingCases.length > 0;
|
|
610
|
+
if (bindingCases.length > 0)
|
|
611
|
+
canonicalizationOutcome = canonicalization.changed ? "revalidation-failed" : "unsafe";
|
|
545
612
|
brokenPaths.push(target);
|
|
613
|
+
const bindingFormatIssue = bindingCases.length > 0;
|
|
546
614
|
issues.push({
|
|
547
615
|
code: "T5",
|
|
548
616
|
path: target,
|
|
549
617
|
detail: moduleStructuralDetail(structural),
|
|
550
618
|
recoverable: true,
|
|
619
|
+
...(bindingFormatIssue
|
|
620
|
+
? {
|
|
621
|
+
failureSubreason: "markdown-test-point-binding-format",
|
|
622
|
+
affectedCases: orderedUnique(bindingCases),
|
|
623
|
+
testPointIds: orderedUnique(unresolved),
|
|
624
|
+
canonicalizationEligible: canonicalization.canonicalizedTestPoints.length > 0,
|
|
625
|
+
canonicalizationOutcome: canonicalization.changed
|
|
626
|
+
? (unresolved.length === 0 ? "changed" : "revalidation-failed")
|
|
627
|
+
: (beforeBindings.some((entry) => entry.unclassifiedTestPoints.length > 0) ? "unsafe" : "already-canonical"),
|
|
628
|
+
}
|
|
629
|
+
: {}),
|
|
551
630
|
});
|
|
552
631
|
}
|
|
553
632
|
}
|
|
@@ -586,6 +665,9 @@ export async function assessBackendTestShardChildCompleteness(input) {
|
|
|
586
665
|
brokenPaths: orderedUnique(brokenPaths),
|
|
587
666
|
targetPaths: recoverableTargets,
|
|
588
667
|
issues,
|
|
668
|
+
...(role === "md-generate"
|
|
669
|
+
? { canonicalizationOutcome, canonicalizedTestPointCount, lightweightRepairEligible }
|
|
670
|
+
: {}),
|
|
589
671
|
};
|
|
590
672
|
}
|
|
591
673
|
export async function assessBackendTestMdWriterCompleteness(workspaceRoot, layout) {
|
|
@@ -609,7 +691,7 @@ export async function assessBackendTestMdWriterCompleteness(workspaceRoot, layou
|
|
|
609
691
|
}
|
|
610
692
|
else {
|
|
611
693
|
actualPaths.push(readmeRel);
|
|
612
|
-
readme = await readFile(readmePath, "utf8");
|
|
694
|
+
readme = canonicalizeBackendTestPlanProtocolHeadings(await readFile(readmePath, "utf8")).markdown;
|
|
613
695
|
if (!readme.includes("## Coverage Scope")) {
|
|
614
696
|
brokenPaths.push(readmeRel);
|
|
615
697
|
issues.push({
|
|
@@ -853,7 +935,7 @@ export async function writeBackendTestWriterProgressArtifacts(input) {
|
|
|
853
935
|
"## Issues",
|
|
854
936
|
"",
|
|
855
937
|
...(input.progress.issues.length
|
|
856
|
-
? input.progress.issues.map((issue) => `- [${issue.code}] ${issue.path ?? "(workspace)"}: ${issue.detail}`)
|
|
938
|
+
? 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
939
|
: ["- None"]),
|
|
858
940
|
"",
|
|
859
941
|
`<!-- sha256:${createHash("sha256").update(JSON.stringify(parsed)).digest("hex")} -->`,
|
|
@@ -862,6 +944,16 @@ export async function writeBackendTestWriterProgressArtifacts(input) {
|
|
|
862
944
|
await writeFile(reportPath, body, "utf8");
|
|
863
945
|
return { factsPath, reportPath };
|
|
864
946
|
}
|
|
947
|
+
export function isBackendTestPytestCollectionRepairOutcomeRecoveryCandidate(task) {
|
|
948
|
+
return (task.id === "repair-backend-pytest-collection-pi" &&
|
|
949
|
+
task.executor === "pi" &&
|
|
950
|
+
task.role === "implementer" &&
|
|
951
|
+
task.toolProfile === "write" &&
|
|
952
|
+
task.writePolicy === "exclusive" &&
|
|
953
|
+
(task.writeSet?.length ?? 0) > 0 &&
|
|
954
|
+
task.writerOutcomePolicy?.type === "implementation-outcome-v1" &&
|
|
955
|
+
task.writerOutcomePolicy.requireChangedFiles === true);
|
|
956
|
+
}
|
|
865
957
|
export function isBackendTestCompletenessRetryCandidate(task) {
|
|
866
958
|
if (task.executor !== "pi" || task.role !== "implementer")
|
|
867
959
|
return false;
|
|
@@ -933,7 +1025,7 @@ export async function assessBackendTestMdPlanCompleteness(workspaceRoot, layout)
|
|
|
933
1025
|
}
|
|
934
1026
|
else {
|
|
935
1027
|
actualPaths.push(planReadmeRel);
|
|
936
|
-
const readme = await readFile(readmePath, "utf8");
|
|
1028
|
+
const readme = canonicalizeBackendTestPlanProtocolHeadings(await readFile(readmePath, "utf8")).markdown;
|
|
937
1029
|
if (!readme.includes("## Coverage Scope")) {
|
|
938
1030
|
brokenPaths.push(planReadmeRel);
|
|
939
1031
|
issues.push({
|
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* its schemaId in the contract output registry. Imported once by the node
|
|
4
4
|
* executor; keeps per-contract imports out of the executor.
|
|
5
5
|
*/
|
|
6
|
-
import { validateFrontendContractNodeOutput, validateFrontendPlanPatchNodeOutput,
|
|
6
|
+
import { validateFrontendContractNodeOutput, validateFrontendPlanPatchNodeOutput, } from "./frontend-implementation-contract.js";
|
|
7
7
|
import { registerStructuredContractValidator } from "./contract-output-registry.js";
|
|
8
8
|
registerStructuredContractValidator("frontend-implementation-contract-v1", validateFrontendContractNodeOutput);
|
|
9
9
|
registerStructuredContractValidator("frontend-implementation-contract-plan-patch-v1", validateFrontendPlanPatchNodeOutput);
|
|
10
|
-
registerStructuredContractValidator("frontend-implementation-contract-revision-patch-v1", validateFrontendRevisionPatchNodeOutput);
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Leaf module for the retry-policy schema surface: failure-category constants,
|
|
4
|
+
* the zod retry-policy schema, and the target-template transient retry profile
|
|
5
|
+
* literal.
|
|
6
|
+
*
|
|
7
|
+
* `types.ts` reads `dagRetryPolicySchema` / `TARGET_TEMPLATE_TRANSIENT_RETRY_PROFILE`
|
|
8
|
+
* during its own module initialization, and the retry-policy module transitively
|
|
9
|
+
* reaches `types.ts` (retry-policy → frontend-recovery-capsule →
|
|
10
|
+
* frontend-recovery-lineage → frontend-implementation-contract → types). With
|
|
11
|
+
* these definitions living in retry-policy itself, a run whose entry module is
|
|
12
|
+
* retry-policy (e.g. test/dag-node-retry.test.ts) evaluates `types.ts` while
|
|
13
|
+
* retry-policy is still initializing and reads an undefined
|
|
14
|
+
* `dagRetryPolicySchema`. Keeping this module a leaf (zod is its only import)
|
|
15
|
+
* makes the cycle resolve order-independently; retry-policy re-exports
|
|
16
|
+
* everything for source compatibility.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Failure categories that are safe to auto-retry for read-only Pi nodes.
|
|
20
|
+
*
|
|
21
|
+
* These are failures with no repository write side-effects: model connection
|
|
22
|
+
* interruption, provider rate-limiting, temporary provider unavailability,
|
|
23
|
+
* request timeout, or a settled read-only turn with no assistant text.
|
|
24
|
+
*
|
|
25
|
+
* `quota` is intentionally NOT included: a quota exhaustion is an account
|
|
26
|
+
* billing/plan state, not a transient rate limit, and retrying immediately
|
|
27
|
+
* will not help and may waste budget.
|
|
28
|
+
*/
|
|
29
|
+
export const DEFAULT_DAG_RETRY_CATEGORIES = [
|
|
30
|
+
"timeout",
|
|
31
|
+
"network",
|
|
32
|
+
"rate-limit",
|
|
33
|
+
"unavailable",
|
|
34
|
+
"empty-output",
|
|
35
|
+
];
|
|
36
|
+
export const STRUCTURED_OUTPUT_RETRY_CATEGORY = "output-too-large";
|
|
37
|
+
export const PROTOCOL_INVALID_RETRY_CATEGORY = "protocol-invalid";
|
|
38
|
+
/** Recoverable model artifact/schema formatting failure on read-only structured nodes. */
|
|
39
|
+
export const STRUCTURED_ARTIFACT_INVALID_RETRY_CATEGORY = "invalid-output";
|
|
40
|
+
/** Provider stopReason=length truncated the response before the JSON contract completed. */
|
|
41
|
+
export const STRUCTURED_OUTPUT_TRUNCATED_RETRY_CATEGORY = "structured-output-truncated";
|
|
42
|
+
/**
|
|
43
|
+
* The review node emitted a verdict in response text (e.g. "VERDICT: pass")
|
|
44
|
+
* but never committed the authoritative typed terminal tool call
|
|
45
|
+
* (approve_review / request_review_changes). Read-only and safe to retry with
|
|
46
|
+
* a corrected instruction; the typed terminal is the only authority.
|
|
47
|
+
*/
|
|
48
|
+
export const REVIEW_TERMINAL_MISSING_RETRY_CATEGORY = "review-terminal-missing";
|
|
49
|
+
/**
|
|
50
|
+
* The plan node issued too many read-only tool calls (read/grep/ls/find),
|
|
51
|
+
* blowing up the context window and eventually a 400 request-too-large. The
|
|
52
|
+
* plan should trust upstream typed facts instead of re-reading contract/scout
|
|
53
|
+
* outputs and source files repeatedly. Read-only and safe to retry with a
|
|
54
|
+
* reduced-reading instruction.
|
|
55
|
+
*/
|
|
56
|
+
export const READ_BURST_RETRY_CATEGORY = "read-burst";
|
|
57
|
+
/**
|
|
58
|
+
* Provider returned a context-overflow error (400 request-too-large / context
|
|
59
|
+
* window exceeded). The SDK compacts and auto-retries once internally; when it
|
|
60
|
+
* still surfaces, the executor classifies this so retry policies and reports
|
|
61
|
+
* treat it as overflow (not empty-output) and can retry with a compacted
|
|
62
|
+
* session / fresh context.
|
|
63
|
+
*/
|
|
64
|
+
export const CONTEXT_OVERFLOW_RETRY_CATEGORY = "context-overflow";
|
|
65
|
+
/** Retry only a proven no-op from an explicitly opt-in bounded Pi writer. */
|
|
66
|
+
export const WRITER_EMPTY_DIFF_RETRY_CATEGORY = "writer-empty-diff";
|
|
67
|
+
/** Retry when a backend-test writer finished but Completeness Gate found missing/broken targets. */
|
|
68
|
+
export const INCOMPLETE_WRITE_SET_RETRY_CATEGORY = "incomplete-write-set";
|
|
69
|
+
/**
|
|
70
|
+
* Provider/transport stall or absolute timeout on a Pi exclusive writer that
|
|
71
|
+
* left zero attributed workspace writes and zero write-tool calls. Safe to
|
|
72
|
+
* retry once because the attempt was a pure provider flake (no side effects).
|
|
73
|
+
* Partial writes keep the original `timeout` category and do NOT retry.
|
|
74
|
+
*/
|
|
75
|
+
export const WRITER_CLEAN_TIMEOUT_RETRY_CATEGORY = "writer-clean-timeout";
|
|
76
|
+
export const STRUCTURED_REQUIRED_DAG_RETRY_CATEGORIES = [
|
|
77
|
+
...DEFAULT_DAG_RETRY_CATEGORIES,
|
|
78
|
+
STRUCTURED_OUTPUT_RETRY_CATEGORY,
|
|
79
|
+
STRUCTURED_ARTIFACT_INVALID_RETRY_CATEGORY,
|
|
80
|
+
STRUCTURED_OUTPUT_TRUNCATED_RETRY_CATEGORY,
|
|
81
|
+
];
|
|
82
|
+
/** Categories allowed on nodes that declare a machine-readable outputProtocol. */
|
|
83
|
+
export const PROTOCOL_AWARE_DAG_RETRY_CATEGORIES = [
|
|
84
|
+
...DEFAULT_DAG_RETRY_CATEGORIES,
|
|
85
|
+
PROTOCOL_INVALID_RETRY_CATEGORY,
|
|
86
|
+
STRUCTURED_ARTIFACT_INVALID_RETRY_CATEGORY,
|
|
87
|
+
];
|
|
88
|
+
export const ALL_DAG_RETRY_CATEGORIES = [
|
|
89
|
+
...DEFAULT_DAG_RETRY_CATEGORIES,
|
|
90
|
+
STRUCTURED_OUTPUT_RETRY_CATEGORY,
|
|
91
|
+
PROTOCOL_INVALID_RETRY_CATEGORY,
|
|
92
|
+
STRUCTURED_ARTIFACT_INVALID_RETRY_CATEGORY,
|
|
93
|
+
STRUCTURED_OUTPUT_TRUNCATED_RETRY_CATEGORY,
|
|
94
|
+
WRITER_EMPTY_DIFF_RETRY_CATEGORY,
|
|
95
|
+
INCOMPLETE_WRITE_SET_RETRY_CATEGORY,
|
|
96
|
+
WRITER_CLEAN_TIMEOUT_RETRY_CATEGORY,
|
|
97
|
+
REVIEW_TERMINAL_MISSING_RETRY_CATEGORY,
|
|
98
|
+
READ_BURST_RETRY_CATEGORY,
|
|
99
|
+
CONTEXT_OVERFLOW_RETRY_CATEGORY,
|
|
100
|
+
];
|
|
101
|
+
export const dagRetryCategorySchema = z.enum(ALL_DAG_RETRY_CATEGORIES);
|
|
102
|
+
export const dagRetryBackoffSchema = z.enum(["exponential"]);
|
|
103
|
+
/**
|
|
104
|
+
* Opt-in retry policy for a DagTask. Generated for safe read-only Pi nodes,
|
|
105
|
+
* except the single validation-gated writer-empty-diff policy. Validation
|
|
106
|
+
* rejects all other writers, dynamic, shell, static, and decision-gate nodes.
|
|
107
|
+
*/
|
|
108
|
+
export const dagRetryPolicySchema = z
|
|
109
|
+
.object({
|
|
110
|
+
/**
|
|
111
|
+
* Total number of attempts (including the first try). Must be 1..5.
|
|
112
|
+
* A value of 1 disables retry (one attempt, no retry).
|
|
113
|
+
*/
|
|
114
|
+
maxAttempts: z.number().int().positive().max(5),
|
|
115
|
+
backoff: dagRetryBackoffSchema.default("exponential"),
|
|
116
|
+
/** Initial delay before the second attempt, in milliseconds. */
|
|
117
|
+
initialDelayMs: z.number().int().nonnegative().default(2000),
|
|
118
|
+
/** Upper bound on a single backoff delay, in milliseconds. */
|
|
119
|
+
maxDelayMs: z.number().int().nonnegative().default(30000),
|
|
120
|
+
/**
|
|
121
|
+
* Explicit set of failure categories eligible for retry. Defaults to
|
|
122
|
+
* {@link DEFAULT_DAG_RETRY_CATEGORIES}. `quota` and other categories
|
|
123
|
+
* are never retried.
|
|
124
|
+
*/
|
|
125
|
+
retryCategories: z
|
|
126
|
+
.array(dagRetryCategorySchema)
|
|
127
|
+
.default([...DEFAULT_DAG_RETRY_CATEGORIES]),
|
|
128
|
+
})
|
|
129
|
+
.superRefine((policy, ctx) => {
|
|
130
|
+
if (policy.maxDelayMs < policy.initialDelayMs) {
|
|
131
|
+
ctx.addIssue({
|
|
132
|
+
code: z.ZodIssueCode.custom,
|
|
133
|
+
message: "retryPolicy.maxDelayMs must be >= retryPolicy.initialDelayMs",
|
|
134
|
+
path: ["maxDelayMs"],
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
export const TARGET_TEMPLATE_TRANSIENT_RETRY_PROFILE = "standard-reviewed-supervised-v1";
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export { buildFrontendShapeTransitionCapsule, computeFrontendShapeSourceDigest, evaluateFrontendShapeEscalation, FRONTEND_SHAPE_NODE_COUNTS, frontendShapeTransitionCapsuleSchema, frontendShapeTransitionReshapeCapsuleSchema, frontendShapeTransitionSplitCapsuleSchema, parseFrontendShapeTransitionCapsule, resolveFrontendTaskShape, } from "./frontend-shape.js";
|
|
3
|
+
/**
|
|
4
|
+
* Deterministic frontend closeout facts + renderer (M7).
|
|
5
|
+
*
|
|
6
|
+
* The functions here are pure (no I/O, no model, no side effects). The closeout
|
|
7
|
+
* renderer reads only committed facts, never model summaries or node prose, so
|
|
8
|
+
* a weak conclusion (failed / not-run / baseline-debt / mock-backed / pending)
|
|
9
|
+
* can never be rewritten into a stronger one (passed / real-integrated).
|
|
10
|
+
*
|
|
11
|
+
* Canonical shape routing and transition capsules live in `frontend-shape.ts`
|
|
12
|
+
* and are re-exported here for callers that also consume closeout contracts.
|
|
13
|
+
*/
|
|
14
|
+
export const FRONTEND_CLOSEOUT_FACTS_SCHEMA_ID = "frontend-closeout-facts-v1";
|
|
15
|
+
/* ------------------------------------------------------------------ *
|
|
16
|
+
* Enums + facts schema *
|
|
17
|
+
* ------------------------------------------------------------------ */
|
|
18
|
+
export const frontendCoverageStatusSchema = z.enum([
|
|
19
|
+
"passed",
|
|
20
|
+
"failed",
|
|
21
|
+
"not-run",
|
|
22
|
+
"blocked",
|
|
23
|
+
"unavailable",
|
|
24
|
+
]);
|
|
25
|
+
export const frontendLintStatusSchema = z.enum([
|
|
26
|
+
"passed",
|
|
27
|
+
"baseline-debt",
|
|
28
|
+
"failed",
|
|
29
|
+
"unavailable",
|
|
30
|
+
]);
|
|
31
|
+
export const frontendIntegrationFactsSchema = z.enum([
|
|
32
|
+
"real-api",
|
|
33
|
+
"mock-backed",
|
|
34
|
+
"local-no-remote",
|
|
35
|
+
"unavailable",
|
|
36
|
+
]);
|
|
37
|
+
export const frontendCloseoutFactsSchema = z
|
|
38
|
+
.object({
|
|
39
|
+
schemaVersion: z.literal(1),
|
|
40
|
+
schemaId: z.literal(FRONTEND_CLOSEOUT_FACTS_SCHEMA_ID),
|
|
41
|
+
coverageMatrix: z.array(z
|
|
42
|
+
.object({
|
|
43
|
+
verificationTargetId: z.string().min(1),
|
|
44
|
+
status: frontendCoverageStatusSchema,
|
|
45
|
+
})
|
|
46
|
+
.strict()),
|
|
47
|
+
lintStatus: frontendLintStatusSchema,
|
|
48
|
+
integrationFacts: frontendIntegrationFactsSchema,
|
|
49
|
+
reviewApproval: z
|
|
50
|
+
.object({
|
|
51
|
+
typedVerdict: z
|
|
52
|
+
.enum(["approve_review", "request_review_changes"])
|
|
53
|
+
.nullable(),
|
|
54
|
+
legacyVerdict: z.enum(["pass", "request-revision"]).nullable(),
|
|
55
|
+
match: z.boolean(),
|
|
56
|
+
})
|
|
57
|
+
.strict(),
|
|
58
|
+
cumulativeDiff: z
|
|
59
|
+
.object({
|
|
60
|
+
cumulativeDigest: z.string().min(1),
|
|
61
|
+
empty: z.boolean(),
|
|
62
|
+
})
|
|
63
|
+
.strict(),
|
|
64
|
+
notRun: z
|
|
65
|
+
.object({
|
|
66
|
+
browser: z.literal("not-run"),
|
|
67
|
+
visual: z.literal("not-run"),
|
|
68
|
+
})
|
|
69
|
+
.strict(),
|
|
70
|
+
pendingRealIntegration: z.boolean(),
|
|
71
|
+
residualRisks: z.array(z.string().min(1)).optional(),
|
|
72
|
+
evidenceGaps: z.array(z.string().min(1)).optional(),
|
|
73
|
+
})
|
|
74
|
+
.strict();
|
|
75
|
+
/* ------------------------------------------------------------------ *
|
|
76
|
+
* FrontendTestHandoff (A+B AC-010) *
|
|
77
|
+
* ------------------------------------------------------------------ */
|
|
78
|
+
export const FRONTEND_TEST_HANDOFF_SCHEMA_ID = "frontend-test-handoff-v1";
|
|
79
|
+
/**
|
|
80
|
+
* A+B (AC-010): deterministic frontend-test handoff. Bound to the task id,
|
|
81
|
+
* source digest, and candidate hash; browser/visual/runtime-a11y are always
|
|
82
|
+
* `not-run` and the handoff is always `deferred-to-frontend-test`, so a weak
|
|
83
|
+
* closeout can never block implementation and can never be rewritten as
|
|
84
|
+
* passed.
|
|
85
|
+
*/
|
|
86
|
+
export const frontendTestHandoffSchema = z
|
|
87
|
+
.object({
|
|
88
|
+
schemaVersion: z.literal(1),
|
|
89
|
+
schemaId: z.literal(FRONTEND_TEST_HANDOFF_SCHEMA_ID),
|
|
90
|
+
taskId: z.string().min(1),
|
|
91
|
+
sourceDigest: z.string().regex(/^[a-f0-9]{64}$/),
|
|
92
|
+
candidateHash: z.string().regex(/^[a-f0-9]{64}$/),
|
|
93
|
+
executionStatus: z.literal("not-run"),
|
|
94
|
+
handoffStatus: z.literal("deferred-to-frontend-test"),
|
|
95
|
+
nonBlocking: z.literal(true),
|
|
96
|
+
})
|
|
97
|
+
.strict();
|
|
98
|
+
export function buildFrontendTestHandoff(input) {
|
|
99
|
+
return frontendTestHandoffSchema.parse({
|
|
100
|
+
schemaVersion: 1,
|
|
101
|
+
schemaId: FRONTEND_TEST_HANDOFF_SCHEMA_ID,
|
|
102
|
+
taskId: input.taskId,
|
|
103
|
+
sourceDigest: input.sourceDigest,
|
|
104
|
+
candidateHash: input.candidateHash,
|
|
105
|
+
executionStatus: "not-run",
|
|
106
|
+
handoffStatus: "deferred-to-frontend-test",
|
|
107
|
+
nonBlocking: true,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Deterministic, fail-closed mapping from structured evidence to integration
|
|
112
|
+
* facts. Never reads model text. `real-api` is the only value that may report
|
|
113
|
+
* real integration, and it requires both real passed evidence and satisfied
|
|
114
|
+
* required verification evidence. Anything else downgrades to pending.
|
|
115
|
+
*/
|
|
116
|
+
export function deriveFrontendIntegrationFacts(input) {
|
|
117
|
+
const requiredEvidenceSatisfied = input.verificationTargets.length > 0 &&
|
|
118
|
+
input.verificationTargets.every((target) => target.status === "ok");
|
|
119
|
+
if (input.realApiEvidence.present &&
|
|
120
|
+
input.realApiEvidence.passed &&
|
|
121
|
+
requiredEvidenceSatisfied) {
|
|
122
|
+
return "real-api";
|
|
123
|
+
}
|
|
124
|
+
// Required evidence not satisfied → never report real-integrated or
|
|
125
|
+
// mock/locally-validated; evidence is unavailable.
|
|
126
|
+
if (!requiredEvidenceSatisfied) {
|
|
127
|
+
return "unavailable";
|
|
128
|
+
}
|
|
129
|
+
if (input.mockStrategy !== "not-needed" && input.mockEvidenceBound) {
|
|
130
|
+
return "mock-backed";
|
|
131
|
+
}
|
|
132
|
+
if (input.mockStrategy === "not-needed" && !input.realApiEvidence.present) {
|
|
133
|
+
return "local-no-remote";
|
|
134
|
+
}
|
|
135
|
+
return "unavailable";
|
|
136
|
+
}
|
|
137
|
+
/* ------------------------------------------------------------------ *
|
|
138
|
+
* Deterministic closeout renderer *
|
|
139
|
+
* ------------------------------------------------------------------ */
|
|
140
|
+
function bulletList(items) {
|
|
141
|
+
if (!items || items.length === 0)
|
|
142
|
+
return "";
|
|
143
|
+
return items.map((item) => `- ${item}`).join("\n");
|
|
144
|
+
}
|
|
145
|
+
function coverageLines(facts) {
|
|
146
|
+
const lines = facts.coverageMatrix.map((entry) => `- ${entry.verificationTargetId}: ${entry.status}`);
|
|
147
|
+
return lines.length > 0 ? lines.join("\n") : "_(none)_";
|
|
148
|
+
}
|
|
149
|
+
function frontendStatus(facts) {
|
|
150
|
+
switch (facts.integrationFacts) {
|
|
151
|
+
case "real-api":
|
|
152
|
+
return "Frontend status: real-integrated";
|
|
153
|
+
case "mock-backed":
|
|
154
|
+
return "Frontend status: mock-validated";
|
|
155
|
+
case "local-no-remote":
|
|
156
|
+
return "Frontend status: locally-validated";
|
|
157
|
+
case "unavailable":
|
|
158
|
+
return "Frontend status: unverified";
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function integrationStatus(facts) {
|
|
162
|
+
return facts.integrationFacts === "real-api"
|
|
163
|
+
? "Real integration: validated"
|
|
164
|
+
: `Real integration: pending (${facts.integrationFacts})`;
|
|
165
|
+
}
|
|
166
|
+
function reviewApprovalLines(facts) {
|
|
167
|
+
const { typedVerdict, legacyVerdict, match } = facts.reviewApproval;
|
|
168
|
+
return [
|
|
169
|
+
`typed: ${typedVerdict ?? "none"}`,
|
|
170
|
+
`legacy: ${legacyVerdict ?? "none"}`,
|
|
171
|
+
`match: ${match}`,
|
|
172
|
+
].join("\n");
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Render a deterministic closeout Markdown document from committed facts only.
|
|
176
|
+
* The same facts always render byte-identical output; weak statuses are
|
|
177
|
+
* emitted verbatim and never promoted to `passed` / real-integrated.
|
|
178
|
+
*/
|
|
179
|
+
export function renderFrontendCloseout(facts) {
|
|
180
|
+
const lintLine = facts.lintStatus === "baseline-debt"
|
|
181
|
+
? `${facts.lintStatus} (explicit debt; not passed)`
|
|
182
|
+
: facts.lintStatus;
|
|
183
|
+
return [
|
|
184
|
+
"# Frontend Closeout",
|
|
185
|
+
"",
|
|
186
|
+
"## Coverage Matrix",
|
|
187
|
+
coverageLines(facts),
|
|
188
|
+
"",
|
|
189
|
+
"## Lint Status",
|
|
190
|
+
lintLine,
|
|
191
|
+
"",
|
|
192
|
+
"## Integration Facts",
|
|
193
|
+
facts.integrationFacts,
|
|
194
|
+
"",
|
|
195
|
+
"## Frontend Status",
|
|
196
|
+
frontendStatus(facts),
|
|
197
|
+
"",
|
|
198
|
+
"## Real Integration Status",
|
|
199
|
+
integrationStatus(facts),
|
|
200
|
+
"",
|
|
201
|
+
"## Real Integration Pending",
|
|
202
|
+
facts.pendingRealIntegration ? "yes" : "no",
|
|
203
|
+
"",
|
|
204
|
+
"## Review Approval",
|
|
205
|
+
reviewApprovalLines(facts),
|
|
206
|
+
"",
|
|
207
|
+
"## Cumulative Diff",
|
|
208
|
+
`digest: ${facts.cumulativeDiff.cumulativeDigest}`,
|
|
209
|
+
`empty: ${facts.cumulativeDiff.empty}`,
|
|
210
|
+
"",
|
|
211
|
+
"## Browser / Visual",
|
|
212
|
+
`Browser: ${facts.notRun.browser}`,
|
|
213
|
+
`Visual: ${facts.notRun.visual}`,
|
|
214
|
+
"",
|
|
215
|
+
"## Residual Risks",
|
|
216
|
+
bulletList(facts.residualRisks) || "_(none)_",
|
|
217
|
+
"",
|
|
218
|
+
"## Evidence Gaps",
|
|
219
|
+
bulletList(facts.evidenceGaps) || "_(none)_",
|
|
220
|
+
].join("\n") + "\n";
|
|
221
|
+
}
|