@tea-agent/loop-agent 0.39.0-next.8 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -1
- package/CHANGELOG.md +264 -67
- package/README.md +11 -5
- package/bin/loop-agent.js +7 -3
- package/dist/application/dag/generate-task-dag.js +9 -0
- package/dist/application/task-lifecycle/advance.js +4 -0
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +16 -5
- package/dist/cli/program.js +16 -2
- package/dist/cli/update/notifier.js +2 -2
- package/dist/cli.js +2 -1
- package/dist/commands/dag-rerun.js +55 -1
- package/dist/commands/init-model-catalog.js +464 -0
- package/dist/commands/init-upgrade.js +265 -97
- package/dist/commands/init.js +428 -27
- package/dist/commands/inspect-next.js +8 -0
- package/dist/commands/task-advance.js +31 -0
- package/dist/executors/dag-pi-executor.js +124 -18
- package/dist/executors/pi-event-serializer.js +42 -1
- package/dist/executors/pi-executor.js +51 -0
- package/dist/executors/pi-extension-resolver.js +233 -0
- package/dist/executors/pi-playwright-cli-tool.js +74 -28
- package/dist/executors/pi-sdk-executor.js +187 -59
- package/dist/executors/shell-executor.js +554 -33
- package/dist/executors/shell-write-guard.js +7 -0
- package/dist/shared/dag-failure-category.js +138 -0
- package/dist/shared/dag-prompt-override.js +28 -0
- package/dist/shared/operator/capabilities.js +181 -27
- package/dist/shared/playwright-cli-command-policy.js +15 -0
- package/dist/shared/update/console-notifier.js +100 -0
- package/dist/{cli → shared}/update/npm-client.js +41 -8
- package/dist/{cli → shared}/update/state.js +41 -13
- package/dist/task/config-types.js +28 -0
- package/dist/task/contract/constants.js +1 -0
- package/dist/task/contract/diff.js +26 -0
- package/dist/task/contract/project.js +5 -0
- package/dist/task/contract/schema.js +6 -1
- package/dist/task/source-prepare/build-draft.js +15 -0
- package/dist/task/source-prepare/parse-intent.js +24 -4
- package/dist/worker/cli.js +31 -1
- package/dist/worker/console/chat/deferred-turn.js +91 -0
- package/dist/worker/console/chat/mcp-inventory.js +221 -0
- package/dist/worker/console/chat/operation-card.js +8 -1
- package/dist/worker/console/chat/pi-console-config.js +81 -10
- package/dist/worker/console/chat/pi-runtime.js +165 -83
- package/dist/worker/console/chat/repo-walk.js +10 -1
- package/dist/worker/console/chat/routes.js +480 -55
- package/dist/worker/console/chat/semantic-activity.js +20 -8
- package/dist/worker/console/chat/session-store.js +146 -2
- package/dist/worker/console/chat/turn-process.js +10 -10
- package/dist/worker/console/dag-execution-receipt.js +33 -0
- package/dist/worker/console/inspect-split.js +18 -0
- package/dist/worker/console/operation-run-facts.js +19 -3
- package/dist/worker/console/operator-actions.js +195 -4
- package/dist/worker/console/operator-user-error.js +2 -2
- package/dist/worker/console/pi-readiness.js +5 -4
- package/dist/worker/console/recovery-cta.js +4 -4
- package/dist/worker/console/recovery-error-copy.js +4 -4
- package/dist/worker/console/routes.js +43 -2
- package/dist/worker/console/security.js +63 -9
- package/dist/worker/console/server.js +42 -6
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-BLXFe4qI.js → abnfDiagram-N423BO3Z-Br92EGb7.js} +1 -1
- package/dist/worker/console/static/assets/{arc-7iOJeqho.js → arc-oulImtQq.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CYoyj5K7.js → architectureDiagram-T3A2C74G-tyHc6aqO.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-fK01JNlR.js → blockDiagram-VBNYF7ZC-CY86BSpT.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-bzbw5D_Z.js → c4Diagram-5PPSVZJV-BKKsDmNs.js} +1 -1
- package/dist/worker/console/static/assets/channel-TY36zt-i.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-Cf6hJFFG.js → chunk-2GRJ4B5K-CXZdjl3B.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DSFXR-et.js → chunk-2Q5K7J3B-B-aIFDQ5.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BagJechl.js → chunk-5RXB4S5H-BDgAW_B2.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-E7QNMFaW.js → chunk-5VM5RSS4-DABa4UPR.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BG_eOBrC.js → chunk-6Q2QTUOP-aGbysVOx.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-Bo42dqol.js → chunk-GF5L2VYU-B8MSZDVs.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-dibM-87v.js → chunk-JWPE2WC7-CpelV73g.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-Bytrp3Oe.js → chunk-KBJHAD2P-BE04ty3z.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DBABILrr.js → chunk-RYQCIY6F-BUxTkaaR.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-DCUk5Pf9.js → chunk-XXDRQBXY-CYYBVKfX.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Dk7QCChM.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Dk7QCChM.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BlGE9v6E.js → cose-bilkent-JH36ORCC-CEQF_xmQ.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-DGl5ewph.js → cynefin-VYW2F7L2-CqpxN5IT.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-DsJ_SVH_.js → cynefinDiagram-MW4NZA55-B3PzanVi.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BIhjFdzv.js → dagre-VZM6K2ZE-BSUGCNx1.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Dmy1ctTb.js → diagram-7IWD3JNH-CdLevU5T.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-DYCtWUF3.js → diagram-B4RE2ZJO-BjpF_Kok.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-BMZFHVd2.js → diagram-LBJQPF4R-C4I6NJny.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-b_U8gfaO.js → diagram-Q27KOJAE-C8hecCr5.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-Dzy_bn-I.js → diagram-UB23O5K3-Daod6tOS.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DJCiMNEI.js → ebnfDiagram-BXEA7PRR-BvvmazGv.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-28lD016B.js → erDiagram-JOGREHBK-DeN_vfV4.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-DGyHJmIw.js → flowDiagram-UKHOOZJN-D6dq1t9z.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CzdwUjrO.js → ganttDiagram-PKOTCBZU-DziIdNY4.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-D9bGwuo8.js → gitGraphDiagram-DS77QQ5N-Dy4s5CXr.js} +1 -1
- package/dist/worker/console/static/assets/index-C3GgHg-g.css +1 -0
- package/dist/worker/console/static/assets/index-Ci8ksvBm.js +406 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-LJgJUQFC.js → infoDiagram-6WML65LV-Czv5eYx2.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-98LaB8zb.js → ishikawaDiagram-WSZJBQD7-Bsk3nFRT.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-D-ISzwlQ.js → journeyDiagram-NVQOT4AX-DxGmNj5K.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-gfYOI-EL.js → kanban-definition-27J2QSJJ-CYCFqkO_.js} +1 -1
- package/dist/worker/console/static/assets/{linear-CyntZgKV.js → linear-B0YzRwrd.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BBOR7dg0.js → mermaid.core-t3EZtIGh.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-CQAgwPMk.js → mindmap-definition-FAOFIHXS-BTXpTv0T.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DkMiZFuV.js → pegDiagram-VL7TDLO6-DU8UkKqA.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DLNXa_0J.js → pieDiagram-7S7Q4E2Y-DP4y8ohp.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-Dc7bdzjm.js → quadrantDiagram-CIZ2JOQS-B07vNaYR.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BxMB0QqW.js → railroadDiagram-AXF67PYL-xDWktb2i.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-FjumoKn4.js → requirementDiagram-LRYGKXZP-BLNMKqeS.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-CkhKc_E3.js → sankeyDiagram-W5VNT64P-n0adalxH.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DoBWqTTH.js → sequenceDiagram-SI44F4Z6-BbnS-2DL.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-CQeh_YeE.js → sizeCapture-X5ZJPWSS-B4uPpCLu.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-LuHmFW-I.js → stateDiagram-OKZ733FA--MMbB0fX.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BqOigMQ4.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CHwbDctY.js → swimlanes-SLNWSIFB-BXUgp2QY.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Caxz9UA2.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CoMngk0X.js → timeline-definition-Z64GVDOM-CsaMxki7.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-CURYbtbO.js → vennDiagram-T6HMQDX7-C2IuG9iH.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-BdqSECjU.js → wardleyDiagram-T6FBY63Y-CmEWOXCW.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-DbMUYuAg.js → xychartDiagram-ELKLHX3M-D_9LE0MG.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/useOperatorActions.js +1 -1
- package/dist/worker/console/static-src/app/useRecoveryActions.js +2 -2
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +38 -2
- package/dist/worker/console/static-src/app/useRunProgress.js +46 -0
- package/dist/worker/console/static-src/operator-chat/cards/failure-category-advice.js +25 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +74 -26
- package/dist/worker/console/static-src/operator-chat/input-history.js +76 -0
- package/dist/worker/console/static-src/operator-chat/mutation-gate.js +1 -1
- package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +328 -0
- package/dist/worker/console/static-src/operator-chat/resource-diagnostics.js +204 -0
- package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +8 -1
- package/dist/worker/console/static-src/operator-chat/turn-submission.js +54 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +3 -0
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +237 -11
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +50 -22
- package/dist/worker/console/static-src/operator-chat/useComposer.js +32 -0
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +26 -1
- package/dist/worker/console/static-src/pages/tasks/run-id-resolution.js +79 -0
- package/dist/worker/console/static-src/pages/tasks/run-ownership-verify.js +23 -0
- package/dist/worker/console/static-src/pages/tasks/run-panel-progress.js +110 -0
- package/dist/worker/loop-agent/loop-agent-client.js +7 -3
- package/dist/worker/materialize/harness-task-lineage.js +5 -2
- package/dist/worker/observability/read-model.js +22 -0
- package/dist/worker/observe/node-input.js +74 -3
- package/dist/worker/observe/node-process.js +377 -0
- package/dist/worker/observe/routes.js +36 -3
- package/dist/worker/observe/shell-handler-keys.js +34 -0
- package/dist/worker/observe/static/api.js +66 -0
- package/dist/worker/observe/static/constants.js +8 -1
- package/dist/worker/observe/static/dag-history-labels.js +4 -0
- package/dist/worker/observe/static/format.js +23 -0
- package/dist/worker/observe/static/markdown-render.js +20 -1
- package/dist/worker/observe/static/operator-chrome.js +4 -0
- package/dist/worker/observe/static/state.js +13 -2
- package/dist/worker/observe/static/styles.css +73 -0
- package/dist/worker/observe/static/views/dag-inspector.js +612 -8
- package/dist/worker/observe/static/views/dag.js +8 -1
- package/dist/worker/observe/static/views/failures.js +5 -2
- package/dist/worker/observe/static/views/session-timeline.js +78 -9
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +827 -53
- package/dist/workflows/dag/backend-test-case-manifest.js +4 -0
- package/dist/workflows/dag/backend-test-execution-contract.js +18 -9
- package/dist/workflows/dag/backend-test-gap-fill.js +205 -0
- package/dist/workflows/dag/backend-test-intake-context.js +32 -0
- package/dist/workflows/dag/backend-test-layout.js +133 -0
- package/dist/workflows/dag/backend-test-markdown-workflow.js +167 -32
- package/dist/workflows/dag/backend-test-module-stem.js +5 -0
- package/dist/workflows/dag/backend-test-pytest-collection.js +368 -33
- package/dist/workflows/dag/backend-test-result-contract.js +1 -1
- package/dist/workflows/dag/backend-test-scenario-param.js +884 -140
- package/dist/workflows/dag/backend-test-scenario-partitions.js +321 -0
- package/dist/workflows/dag/backend-test-writer-completeness.js +100 -56
- package/dist/workflows/dag/contract-output-registry.js +15 -0
- package/dist/workflows/dag/contract-validator-registrations.js +3 -1
- package/dist/workflows/dag/convergence/controller.js +141 -0
- package/dist/workflows/dag/dynamic-runtime/loop-until.js +1 -1
- package/dist/workflows/dag/dynamic-runtime/map.js +25 -9
- package/dist/workflows/dag/dynamic-runtime/shared.js +1 -1
- package/dist/workflows/dag/failure-category.js +7 -116
- package/dist/workflows/dag/frontend-implementation-contract.js +377 -4
- package/dist/workflows/dag/frontend-plan-render.js +1 -1
- package/dist/workflows/dag/frontend-prewrite-gate.js +96 -84
- package/dist/workflows/dag/frontend-repair.js +14 -0
- package/dist/workflows/dag/frontend-risk.js +15 -2
- package/dist/workflows/dag/frontend-test-case-checklist.js +30 -4
- package/dist/workflows/dag/frontend-test-case-manifest.js +11 -4
- package/dist/workflows/dag/frontend-test-case-quality.js +11 -16
- package/dist/workflows/dag/frontend-test-environment-probe.js +230 -0
- package/dist/workflows/dag/frontend-test-html-report.js +3 -1
- package/dist/workflows/dag/frontend-test-l5-report.js +3 -1
- package/dist/workflows/dag/frontend-test-layout.js +159 -0
- package/dist/workflows/dag/frontend-test-markdown.js +61 -0
- package/dist/workflows/dag/frontend-test-result-contract.js +188 -42
- package/dist/workflows/dag/frontend-test-standard-scenarios.js +70 -0
- package/dist/workflows/dag/frontend-verification-trace.js +29 -4
- package/dist/workflows/dag/init-hybrid.js +894 -205
- package/dist/workflows/dag/node-execution.js +183 -15
- package/dist/workflows/dag/prompt.js +60 -1
- package/dist/workflows/dag/rerun-feedback.js +212 -0
- package/dist/workflows/dag/rerun-plan.js +224 -27
- package/dist/workflows/dag/rerun-run.js +61 -6
- package/dist/workflows/dag/rerun-task.js +61 -2
- package/dist/workflows/dag/retry-policy.js +109 -0
- package/dist/workflows/dag/structured-output-repair.js +712 -0
- package/dist/workflows/dag/types.js +143 -1
- package/dist/workflows/dag/validate.js +64 -20
- package/docs/init-surface.manifest.json +5 -0
- package/docs/operations/README.md +1 -1
- package/docs/operations/local-development-environment.md +1 -5
- package/docs/skills/vetted-skill-registry.md +14 -0
- package/docs/templates/README.md +1 -1
- package/docs/templates/agent-dag.schema.json +44 -0
- package/docs/templates/backend-test-dag.json +60 -35
- package/docs/templates/frontend-implementation-contract.schema.json +1 -1
- package/docs/templates/frontend-test-case-checklist.md +2 -2
- package/docs/templates/frontend-test-dag.json +8 -10
- package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +1 -1
- package/docs/templates/init-managed-agents.md +13 -3
- package/harness.json +6 -4
- package/package.json +12 -4
- package/scripts/next-info.mjs +356 -0
- package/scripts/next-publish-gate.mjs +238 -0
- package/scripts/release-source-binding.mjs +251 -0
- package/skills/analyze-product-requirements/SKILL.md +8 -5
- package/skills/analyze-product-requirements/references/acceptance-criteria.md +1 -1
- package/skills/analyze-product-requirements/references/clarification-and-knowledge.md +10 -6
- package/skills/analyze-product-requirements/references/example.md +50 -0
- package/skills/analyze-product-requirements/references/forward-test-cases.md +11 -7
- package/skills/analyze-product-requirements/references/kb-integration.md +5 -5
- package/skills/analyze-product-requirements/references/product-analysis-schema.md +8 -4
- package/skills/analyze-product-requirements/references/product-requirement-schema.md +2 -2
- package/skills/analyze-product-requirements/references/requirement-clarification-schema.md +1 -1
- package/skills/analyze-product-requirements/scripts/test-validators.mjs +11 -1
- package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +20 -1
- package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +9 -0
- package/skills/codebase-scout/SKILL.md +1 -1
- package/skills/fe-test-ui-scout/SKILL.md +65 -0
- package/skills/fe-test-ui-scout/references/ledger-schema.md +62 -0
- package/skills/fe-test-ui-scout/references/recon-protocol.md +54 -0
- package/skills/frontend-implementation/references/node-contracts.md +2 -2
- package/skills/improve-codebase-architecture/SKILL.md +81 -0
- package/skills/improve-codebase-architecture/deepening.md +37 -0
- package/skills/improve-codebase-architecture/html-report.md +123 -0
- package/skills/improve-codebase-architecture/interface-design.md +44 -0
- package/skills/improve-codebase-architecture/language.md +53 -0
- package/skills/loop-agent/references/command-reference.md +11 -4
- package/skills/playwright-cli/SKILL.md +23 -1
- package/skills/playwright-cli-case-generator/SKILL.md +29 -8
- package/dist/worker/console/static/assets/channel-BBOTJHvD.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Cm8Yf8tD.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Cm8Yf8tD.js +0 -1
- package/dist/worker/console/static/assets/index-B-qWGThd.js +0 -325
- package/dist/worker/console/static/assets/index-C-xRgY_0.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DZihfdKc.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CKnXCjHX.js +0 -8
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { assertTaskContractBindingFresh } from "./task-contract-binding.js";
|
|
4
|
-
import { isSafeReadOnlyPiRetryCandidate } from "./retry-policy.js";
|
|
4
|
+
import { isSafeReadOnlyPiRetryCandidate, isTransportCrashedExclusiveWriter } from "./retry-policy.js";
|
|
5
5
|
import { resolveDagTaskSourcePath } from "../../task/dag-source-paths.js";
|
|
6
|
+
import { composeDagPromptOverridePrompt, MAX_DAG_RERUN_PROMPT_OVERRIDE_CHARS, } from "../../shared/dag-prompt-override.js";
|
|
6
7
|
const REVIEW_VERDICT_RECOVERY_NODE_ID = "review-verdict-recovery-pi";
|
|
7
8
|
const SAFE_SHELL_GATE_KEYS = [
|
|
8
9
|
"verdictGate",
|
|
@@ -122,32 +123,56 @@ function hasDynamicRuntime(task) {
|
|
|
122
123
|
task.dynamicCondition ||
|
|
123
124
|
task.dynamicLoopUntil);
|
|
124
125
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
/**
|
|
127
|
+
* Narrow admission for the standard DAG's generated final verification node.
|
|
128
|
+
* `verifyEvidence` alone is metadata, not proof that an arbitrary shell is
|
|
129
|
+
* read-only, so custom shell ids and non-canonical shapes stay blocked.
|
|
130
|
+
*/
|
|
131
|
+
function isCanonicalGeneratedVerifyShell(task) {
|
|
132
|
+
if (task.executor !== "shell")
|
|
133
|
+
return false;
|
|
134
|
+
const shell = task.shell;
|
|
135
|
+
if (!shell)
|
|
136
|
+
return false;
|
|
137
|
+
const evidence = shell.verifyEvidence;
|
|
138
|
+
if (task.id !== "verify-shell")
|
|
139
|
+
return false;
|
|
140
|
+
if (task.role !== "verifier")
|
|
141
|
+
return false;
|
|
142
|
+
if (task.writePolicy !== "read-only")
|
|
143
|
+
return false;
|
|
144
|
+
if (!evidence)
|
|
145
|
+
return false;
|
|
146
|
+
if (evidence.phase !== "final")
|
|
147
|
+
return false;
|
|
148
|
+
if (evidence.quota !== "full")
|
|
149
|
+
return false;
|
|
150
|
+
if (evidence.commandSource !== "adapter")
|
|
151
|
+
return false;
|
|
152
|
+
if (evidence.finalFullRequired !== true)
|
|
153
|
+
return false;
|
|
154
|
+
if ((shell.commands?.length ?? 0) === 0)
|
|
155
|
+
return false;
|
|
156
|
+
if (evidence.commandCount !== shell.commands.length)
|
|
157
|
+
return false;
|
|
158
|
+
// Any side-effect-capable config disqualifies the node.
|
|
128
159
|
if (shell.preset)
|
|
129
|
-
return
|
|
160
|
+
return false;
|
|
130
161
|
if (shell.projectGovernanceGate)
|
|
131
|
-
return
|
|
162
|
+
return false;
|
|
132
163
|
if (shell.frontendPrewriteGate)
|
|
133
|
-
return
|
|
164
|
+
return false;
|
|
134
165
|
if (shell.frontendVerificationBundle)
|
|
135
|
-
return
|
|
166
|
+
return false;
|
|
136
167
|
if (shell.frontendReviewContext)
|
|
137
|
-
return
|
|
168
|
+
return false;
|
|
138
169
|
if (shell.backendTestPipeline)
|
|
139
|
-
return
|
|
140
|
-
if (shell.
|
|
141
|
-
return
|
|
142
|
-
if (shell.nonZeroExitPolicy)
|
|
143
|
-
return true;
|
|
170
|
+
return false;
|
|
171
|
+
if (shell.nonZeroExitPolicy === "record")
|
|
172
|
+
return false;
|
|
144
173
|
if (shell.envAllowlist?.length)
|
|
145
|
-
return
|
|
146
|
-
|
|
147
|
-
return true;
|
|
148
|
-
if (shell.cwd)
|
|
149
|
-
return true;
|
|
150
|
-
return false;
|
|
174
|
+
return false;
|
|
175
|
+
return true;
|
|
151
176
|
}
|
|
152
177
|
function isSafeGateOnlyShell(task) {
|
|
153
178
|
if (task.executor !== "shell")
|
|
@@ -155,9 +180,32 @@ function isSafeGateOnlyShell(task) {
|
|
|
155
180
|
const shell = task.shell;
|
|
156
181
|
if (!shell)
|
|
157
182
|
return false;
|
|
158
|
-
if (
|
|
183
|
+
if (!SAFE_SHELL_GATE_KEYS.some((key) => shell[key] !== undefined)) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
// No side-effect-capable config may ride along on a gate-only shell.
|
|
187
|
+
// Deterministic/read-only riders are admitted: projectGovernanceGate (repo
|
|
188
|
+
// governance context path), cwd, and timeoutMs never execute commands, so
|
|
189
|
+
// the governance-standard gate stays rerun-eligible.
|
|
190
|
+
if ((shell.commands?.length ?? 0) > 0)
|
|
159
191
|
return false;
|
|
160
|
-
|
|
192
|
+
if (shell.preset)
|
|
193
|
+
return false;
|
|
194
|
+
if (shell.frontendPrewriteGate)
|
|
195
|
+
return false;
|
|
196
|
+
if (shell.frontendVerificationBundle)
|
|
197
|
+
return false;
|
|
198
|
+
if (shell.frontendReviewContext)
|
|
199
|
+
return false;
|
|
200
|
+
if (shell.backendTestPipeline)
|
|
201
|
+
return false;
|
|
202
|
+
if (shell.verifyEvidence)
|
|
203
|
+
return false;
|
|
204
|
+
if (shell.nonZeroExitPolicy)
|
|
205
|
+
return false;
|
|
206
|
+
if (shell.envAllowlist?.length)
|
|
207
|
+
return false;
|
|
208
|
+
return true;
|
|
161
209
|
}
|
|
162
210
|
function isStaticTask(task) {
|
|
163
211
|
return task.executor === "static";
|
|
@@ -259,7 +307,10 @@ export function computeResetClosure(spec, effectiveFromNodeId) {
|
|
|
259
307
|
...collectReachableDescendants(spec, effectiveFromNodeId),
|
|
260
308
|
].sort();
|
|
261
309
|
}
|
|
262
|
-
|
|
310
|
+
function isNeverStartedNodeStatus(status) {
|
|
311
|
+
return status === undefined || status === "SKIPPED" || status === "PENDING";
|
|
312
|
+
}
|
|
313
|
+
export function assessResetClosureSafety(spec, resetNodeIds, options = {}) {
|
|
263
314
|
const tasks = taskById(spec);
|
|
264
315
|
const blockingNodes = [];
|
|
265
316
|
const reasonCodes = [];
|
|
@@ -267,7 +318,17 @@ export function assessResetClosureSafety(spec, resetNodeIds) {
|
|
|
267
318
|
const task = tasks.get(nodeId);
|
|
268
319
|
if (!task)
|
|
269
320
|
continue;
|
|
321
|
+
if (options.parentNodes !== undefined &&
|
|
322
|
+
isNeverStartedNodeStatus(options.parentNodes[nodeId]?.status)) {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
270
325
|
if (isWriterTask(task, spec)) {
|
|
326
|
+
if (nodeId === options.allowedTransportWriterNodeId)
|
|
327
|
+
continue;
|
|
328
|
+
if (nodeId === options.allowedExclusiveShellFromNodeId &&
|
|
329
|
+
task.executor === "shell") {
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
271
332
|
blockingNodes.push({
|
|
272
333
|
nodeId,
|
|
273
334
|
reasonCode: "restart-subgraph-contains-writer",
|
|
@@ -305,7 +366,8 @@ export function assessResetClosureSafety(spec, resetNodeIds) {
|
|
|
305
366
|
continue;
|
|
306
367
|
}
|
|
307
368
|
if (task.executor === "shell") {
|
|
308
|
-
if (!isSafeGateOnlyShell(task)
|
|
369
|
+
if (!isSafeGateOnlyShell(task) &&
|
|
370
|
+
!isCanonicalGeneratedVerifyShell(task)) {
|
|
309
371
|
blockingNodes.push({
|
|
310
372
|
nodeId,
|
|
311
373
|
reasonCode: "restart-subgraph-contains-unsafe-shell",
|
|
@@ -320,6 +382,100 @@ export function assessResetClosureSafety(spec, resetNodeIds) {
|
|
|
320
382
|
reasonCodes: [...new Set(reasonCodes)],
|
|
321
383
|
};
|
|
322
384
|
}
|
|
385
|
+
/**
|
|
386
|
+
* Experimental prompt-override admission (fail-closed):
|
|
387
|
+
* - requires the internal switch (see shared/dag-prompt-override.ts);
|
|
388
|
+
* - the target must be a failed (ERROR) node inside the reset closure, so an
|
|
389
|
+
* override can only adjust how an already-failed node retries — never a
|
|
390
|
+
* finished, skipped, or not-yet-started node;
|
|
391
|
+
* - only `subtask_prompt` semantics change: "append" keeps the frozen base
|
|
392
|
+
* and adds a marked addendum block, "replace" swaps the whole prompt;
|
|
393
|
+
* base/final sha256 are recorded for provenance and bound into planHash.
|
|
394
|
+
*/
|
|
395
|
+
function assessPromptOverrides(input) {
|
|
396
|
+
const blockingNodes = [];
|
|
397
|
+
const reasonCodes = [];
|
|
398
|
+
const tasks = taskById(input.spec);
|
|
399
|
+
const resetSet = new Set(input.resetNodeIds);
|
|
400
|
+
const seen = new Set();
|
|
401
|
+
const entries = [];
|
|
402
|
+
if (!input.enabled) {
|
|
403
|
+
reasonCodes.push("prompt-override-disabled");
|
|
404
|
+
}
|
|
405
|
+
for (const override of input.overrides) {
|
|
406
|
+
const nodeId = override.nodeId?.trim() ?? "";
|
|
407
|
+
if (seen.has(nodeId)) {
|
|
408
|
+
blockingNodes.push({
|
|
409
|
+
nodeId,
|
|
410
|
+
reasonCode: "prompt-override-duplicate-node",
|
|
411
|
+
});
|
|
412
|
+
reasonCodes.push("prompt-override-duplicate-node");
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
seen.add(nodeId);
|
|
416
|
+
const task = tasks.get(nodeId);
|
|
417
|
+
if (!task) {
|
|
418
|
+
blockingNodes.push({
|
|
419
|
+
nodeId,
|
|
420
|
+
reasonCode: "prompt-override-node-missing",
|
|
421
|
+
});
|
|
422
|
+
reasonCodes.push("prompt-override-node-missing");
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
if (!resetSet.has(nodeId)) {
|
|
426
|
+
blockingNodes.push({
|
|
427
|
+
nodeId,
|
|
428
|
+
reasonCode: "prompt-override-outside-reset-closure",
|
|
429
|
+
});
|
|
430
|
+
reasonCodes.push("prompt-override-outside-reset-closure");
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
if (input.state.nodes[nodeId]?.status !== "ERROR") {
|
|
434
|
+
blockingNodes.push({
|
|
435
|
+
nodeId,
|
|
436
|
+
reasonCode: "prompt-override-node-not-failed",
|
|
437
|
+
});
|
|
438
|
+
reasonCodes.push("prompt-override-node-not-failed");
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
const mode = override.mode === "append" ? "append" : "replace";
|
|
442
|
+
const prompt = override.prompt ?? "";
|
|
443
|
+
if (!prompt.trim() ||
|
|
444
|
+
prompt.length > MAX_DAG_RERUN_PROMPT_OVERRIDE_CHARS) {
|
|
445
|
+
blockingNodes.push({
|
|
446
|
+
nodeId,
|
|
447
|
+
reasonCode: "prompt-override-invalid-prompt",
|
|
448
|
+
});
|
|
449
|
+
reasonCodes.push("prompt-override-invalid-prompt");
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
const basePromptSha256 = createHash("sha256")
|
|
453
|
+
.update(task.subtask_prompt, "utf8")
|
|
454
|
+
.digest("hex");
|
|
455
|
+
const finalPrompt = composeDagPromptOverridePrompt({
|
|
456
|
+
basePrompt: task.subtask_prompt,
|
|
457
|
+
text: prompt,
|
|
458
|
+
mode,
|
|
459
|
+
});
|
|
460
|
+
const promptSha256 = createHash("sha256")
|
|
461
|
+
.update(finalPrompt, "utf8")
|
|
462
|
+
.digest("hex");
|
|
463
|
+
if (promptSha256 === basePromptSha256) {
|
|
464
|
+
blockingNodes.push({
|
|
465
|
+
nodeId,
|
|
466
|
+
reasonCode: "prompt-override-unchanged",
|
|
467
|
+
});
|
|
468
|
+
reasonCodes.push("prompt-override-unchanged");
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
entries.push({ nodeId, mode, basePromptSha256, promptSha256 });
|
|
472
|
+
}
|
|
473
|
+
return {
|
|
474
|
+
entries: entries.length > 0 ? entries : undefined,
|
|
475
|
+
blockingNodes,
|
|
476
|
+
reasonCodes: [...new Set(reasonCodes)],
|
|
477
|
+
};
|
|
478
|
+
}
|
|
323
479
|
export function computePlanHash(planWithoutHash) {
|
|
324
480
|
const canonical = JSON.stringify(sortKeysDeep(planWithoutHash));
|
|
325
481
|
return createHash("sha256").update(canonical, "utf8").digest("hex");
|
|
@@ -448,6 +604,20 @@ export async function evaluateDagRerunPlan(input) {
|
|
|
448
604
|
: [];
|
|
449
605
|
const resetSet = new Set(resetNodeIds);
|
|
450
606
|
const importedNodeIds = allNodeIds.filter((nodeId) => !resetSet.has(nodeId));
|
|
607
|
+
const tasksByIdForPlan = taskById(input.parentSpec);
|
|
608
|
+
const effectiveTask = effectiveFromNodeId
|
|
609
|
+
? tasksByIdForPlan.get(effectiveFromNodeId)
|
|
610
|
+
: undefined;
|
|
611
|
+
const effectiveRecord = effectiveFromNodeId
|
|
612
|
+
? input.parentState.nodes[effectiveFromNodeId]
|
|
613
|
+
: undefined;
|
|
614
|
+
const transportWriterRestart = Boolean(effectiveFromNodeId &&
|
|
615
|
+
effectiveRecord?.status === "ERROR" &&
|
|
616
|
+
isTransportCrashedExclusiveWriter(effectiveTask, effectiveRecord.failureCategory));
|
|
617
|
+
const convergenceWriterRestart = Boolean(transportWriterRestart &&
|
|
618
|
+
input.parentSpec.convergence?.enabled === true &&
|
|
619
|
+
input.parentState.convergence?.enabled === true &&
|
|
620
|
+
!input.parentState.convergence.terminalReason);
|
|
451
621
|
const controllerIdentityMatched = input.currentControllerFingerprint === undefined ||
|
|
452
622
|
input.parentControllerFingerprint === undefined ||
|
|
453
623
|
input.currentControllerFingerprint === input.parentControllerFingerprint;
|
|
@@ -482,11 +652,11 @@ export async function evaluateDagRerunPlan(input) {
|
|
|
482
652
|
reasonCodes.push("parent-lifecycle-ineligible");
|
|
483
653
|
blockedReasons.push("parent-lifecycle-ineligible");
|
|
484
654
|
}
|
|
485
|
-
if (input.parentSpec.convergence?.enabled) {
|
|
655
|
+
if (input.parentSpec.convergence?.enabled && !convergenceWriterRestart) {
|
|
486
656
|
reasonCodes.push("convergence-rerun-unsupported");
|
|
487
657
|
blockedReasons.push("convergence-rerun-unsupported");
|
|
488
658
|
}
|
|
489
|
-
if (input.parentState.convergence?.enabled) {
|
|
659
|
+
if (input.parentState.convergence?.enabled && !convergenceWriterRestart) {
|
|
490
660
|
reasonCodes.push("convergence-rerun-unsupported");
|
|
491
661
|
blockedReasons.push("convergence-rerun-unsupported");
|
|
492
662
|
}
|
|
@@ -530,7 +700,17 @@ export async function evaluateDagRerunPlan(input) {
|
|
|
530
700
|
blockedReasons.push("ambiguous-skipped-rewrite");
|
|
531
701
|
}
|
|
532
702
|
}
|
|
533
|
-
const closureSafety = assessResetClosureSafety(input.parentSpec, resetNodeIds
|
|
703
|
+
const closureSafety = assessResetClosureSafety(input.parentSpec, resetNodeIds, {
|
|
704
|
+
...(transportWriterRestart && effectiveFromNodeId
|
|
705
|
+
? { allowedTransportWriterNodeId: effectiveFromNodeId }
|
|
706
|
+
: {}),
|
|
707
|
+
parentNodes: input.parentState.nodes,
|
|
708
|
+
...(effectiveFromNodeId &&
|
|
709
|
+
effectiveTask?.executor === "shell" &&
|
|
710
|
+
effectiveRecord?.status === "ERROR"
|
|
711
|
+
? { allowedExclusiveShellFromNodeId: effectiveFromNodeId }
|
|
712
|
+
: {}),
|
|
713
|
+
});
|
|
534
714
|
blockingNodes.push(...closureSafety.blockingNodes);
|
|
535
715
|
for (const code of closureSafety.reasonCodes) {
|
|
536
716
|
reasonCodes.push(code);
|
|
@@ -563,6 +743,22 @@ export async function evaluateDagRerunPlan(input) {
|
|
|
563
743
|
blockingNodes.push({ nodeId, reasonCode: "parent-facts-invalid" });
|
|
564
744
|
}
|
|
565
745
|
}
|
|
746
|
+
let promptOverrideEntries;
|
|
747
|
+
if (input.promptOverrides !== undefined && input.promptOverrides.length > 0) {
|
|
748
|
+
const overrideAssessment = assessPromptOverrides({
|
|
749
|
+
spec: input.parentSpec,
|
|
750
|
+
state: input.parentState,
|
|
751
|
+
resetNodeIds,
|
|
752
|
+
overrides: input.promptOverrides,
|
|
753
|
+
enabled: input.promptOverrideEnabled === true,
|
|
754
|
+
});
|
|
755
|
+
blockingNodes.push(...overrideAssessment.blockingNodes);
|
|
756
|
+
for (const code of overrideAssessment.reasonCodes) {
|
|
757
|
+
reasonCodes.push(code);
|
|
758
|
+
blockedReasons.push(code);
|
|
759
|
+
}
|
|
760
|
+
promptOverrideEntries = overrideAssessment.entries;
|
|
761
|
+
}
|
|
566
762
|
const uniqueReasonCodes = [...new Set(reasonCodes)];
|
|
567
763
|
const uniqueBlockedReasons = [...new Set(blockedReasons)];
|
|
568
764
|
const eligible = uniqueBlockedReasons.length === 0 &&
|
|
@@ -606,6 +802,7 @@ export async function evaluateDagRerunPlan(input) {
|
|
|
606
802
|
parentLifecycle: input.parentLifecycle,
|
|
607
803
|
workerManaged: input.workerManaged,
|
|
608
804
|
}),
|
|
805
|
+
...(promptOverrideEntries ? { promptOverrides: promptOverrideEntries } : {}),
|
|
609
806
|
};
|
|
610
807
|
return {
|
|
611
808
|
...planWithoutHash,
|
|
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { access, copyFile, mkdir, readdir, readFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { writeJsonAtomic } from "../../infrastructure/harness/atomic-write.js";
|
|
5
|
+
import { composeDagPromptOverridePrompt, isDagRerunPromptOverrideEnabled, } from "../../shared/dag-prompt-override.js";
|
|
5
6
|
import { resolveRunningControllerIdentity } from "../../shared/package-metadata.js";
|
|
6
7
|
import { readControllerIdentityArtifact, captureControllerIdentity } from "./controller-identity.js";
|
|
7
8
|
import { DAG_RUNS_DIR, getDagRunDir, isDagNonExecutionLifecycle, locateDagRun, readDagRunSpec, readDagRunState, } from "./lifecycle.js";
|
|
@@ -190,6 +191,8 @@ export async function planDagRerun(input) {
|
|
|
190
191
|
parentSpec: nonCompletedSpec,
|
|
191
192
|
parentState: nonCompletedState,
|
|
192
193
|
parentLifecycle,
|
|
194
|
+
promptOverrides: input.promptOverrides,
|
|
195
|
+
promptOverrideEnabled: isDagRerunPromptOverrideEnabled(),
|
|
193
196
|
workerManaged: nonCompletedWorkerHints.workerManaged,
|
|
194
197
|
workerAssociation: nonCompletedWorkerHints.workerManaged
|
|
195
198
|
? {
|
|
@@ -229,6 +232,8 @@ export async function planDagRerun(input) {
|
|
|
229
232
|
parentTerminalWorkspace,
|
|
230
233
|
currentWorkspace,
|
|
231
234
|
skillSnapshotOk,
|
|
235
|
+
promptOverrides: input.promptOverrides,
|
|
236
|
+
promptOverrideEnabled: isDagRerunPromptOverrideEnabled(),
|
|
232
237
|
workerManaged: workerHints.workerManaged,
|
|
233
238
|
workerAssociation: workerHints.workerManaged
|
|
234
239
|
? {
|
|
@@ -384,15 +389,57 @@ function buildPendingNodeRecord(task) {
|
|
|
384
389
|
...(task.outputMode ? { outputMode: task.outputMode } : {}),
|
|
385
390
|
};
|
|
386
391
|
}
|
|
392
|
+
/**
|
|
393
|
+
* Stage the continuation spec with operator prompt overrides applied. The
|
|
394
|
+
* parent spec stays frozen; every override must match the plan entry (sha256
|
|
395
|
+
* of the composed final prompt and of the parent base prompt) so the
|
|
396
|
+
* two-phase planHash remains the single source of truth for what changed.
|
|
397
|
+
* "append" entries keep the frozen base and append the operator addendum in a
|
|
398
|
+
* marked block; "replace" entries swap the whole prompt.
|
|
399
|
+
*/
|
|
400
|
+
function applyPromptOverridesToSpec(input) {
|
|
401
|
+
const planned = new Map((input.plan.promptOverrides ?? []).map((entry) => [entry.nodeId, entry]));
|
|
402
|
+
const spec = structuredClone(input.spec);
|
|
403
|
+
for (const override of input.overrides) {
|
|
404
|
+
const entry = planned.get(override.nodeId);
|
|
405
|
+
if (!entry) {
|
|
406
|
+
throw new Error(`prompt override is not present in the rerun plan: ${override.nodeId}`);
|
|
407
|
+
}
|
|
408
|
+
const task = spec.tasks.find((candidate) => candidate.id === override.nodeId);
|
|
409
|
+
if (!task) {
|
|
410
|
+
throw new Error(`prompt override node missing from spec: ${override.nodeId}`);
|
|
411
|
+
}
|
|
412
|
+
if (sha256Hex(task.subtask_prompt) !== entry.basePromptSha256) {
|
|
413
|
+
throw new Error(`prompt override base prompt drifted from plan: ${override.nodeId}`);
|
|
414
|
+
}
|
|
415
|
+
const finalPrompt = composeDagPromptOverridePrompt({
|
|
416
|
+
basePrompt: task.subtask_prompt,
|
|
417
|
+
text: override.prompt,
|
|
418
|
+
mode: override.mode === "append" ? "append" : "replace",
|
|
419
|
+
});
|
|
420
|
+
if (sha256Hex(finalPrompt) !== entry.promptSha256) {
|
|
421
|
+
throw new Error(`prompt override text does not match the planned sha256: ${override.nodeId}`);
|
|
422
|
+
}
|
|
423
|
+
task.subtask_prompt = finalPrompt;
|
|
424
|
+
}
|
|
425
|
+
return spec;
|
|
426
|
+
}
|
|
387
427
|
async function stageContinuationRun(input) {
|
|
388
|
-
const
|
|
389
|
-
|
|
428
|
+
const specForRun = input.promptOverrides !== undefined && input.promptOverrides.length > 0
|
|
429
|
+
? applyPromptOverridesToSpec({
|
|
430
|
+
spec: input.parentSpec,
|
|
431
|
+
plan: input.plan,
|
|
432
|
+
overrides: input.promptOverrides,
|
|
433
|
+
})
|
|
434
|
+
: input.parentSpec;
|
|
435
|
+
const { ranks } = topoSortToRanks(specForRun);
|
|
436
|
+
const runId = await buildDagRunId(specForRun, { cwd: input.cwd });
|
|
390
437
|
const runDir = getDagRunDir(input.cwd, "active", runId);
|
|
391
438
|
await prepareActiveRunDir(runDir);
|
|
392
|
-
await writeRunSpec(runDir,
|
|
439
|
+
await writeRunSpec(runDir, specForRun);
|
|
393
440
|
const resetSet = new Set(input.plan.resetNodeIds);
|
|
394
441
|
const importedSet = new Set(input.plan.importedNodeIds);
|
|
395
|
-
const tasksById = new Map(
|
|
442
|
+
const tasksById = new Map(specForRun.tasks.map((task) => [task.id, task]));
|
|
396
443
|
const importManifestNodes = [];
|
|
397
444
|
const nodes = {};
|
|
398
445
|
for (const nodeId of input.plan.importedNodeIds) {
|
|
@@ -433,6 +480,9 @@ async function stageContinuationRun(input) {
|
|
|
433
480
|
reason: input.reason,
|
|
434
481
|
requestId: input.requestId,
|
|
435
482
|
planHash: input.plan.planHash,
|
|
483
|
+
...(input.plan.promptOverrides
|
|
484
|
+
? { promptOverrides: input.plan.promptOverrides }
|
|
485
|
+
: {}),
|
|
436
486
|
createdAt,
|
|
437
487
|
};
|
|
438
488
|
const state = {
|
|
@@ -445,6 +495,9 @@ async function stageContinuationRun(input) {
|
|
|
445
495
|
ranks,
|
|
446
496
|
nodes,
|
|
447
497
|
continuation,
|
|
498
|
+
...(input.parentState.convergence
|
|
499
|
+
? { convergence: structuredClone(input.parentState.convergence) }
|
|
500
|
+
: {}),
|
|
448
501
|
...(input.parentState.evaluation
|
|
449
502
|
? { evaluation: structuredClone(input.parentState.evaluation) }
|
|
450
503
|
: {}),
|
|
@@ -479,7 +532,7 @@ async function stageContinuationRun(input) {
|
|
|
479
532
|
await writeJsonAtomic(path.join(runDir, IMPORT_MANIFEST_REL_PATH), importManifest);
|
|
480
533
|
await writeJsonAtomic(path.join(runDir, RERUN_PLAN_REL_PATH), input.plan);
|
|
481
534
|
await writeRunState(runDir, state);
|
|
482
|
-
return { runId, runDir, state };
|
|
535
|
+
return { runId, runDir, state, spec: specForRun };
|
|
483
536
|
}
|
|
484
537
|
export async function executeDagRerun(input) {
|
|
485
538
|
const existing = await readExistingOperatorRequest(input.cwd, input.requestId);
|
|
@@ -490,6 +543,7 @@ export async function executeDagRerun(input) {
|
|
|
490
543
|
cwd: input.cwd,
|
|
491
544
|
parentRunId: input.parentRunId,
|
|
492
545
|
selectedNodeId: input.selectedNodeId,
|
|
546
|
+
promptOverrides: input.promptOverrides,
|
|
493
547
|
});
|
|
494
548
|
if (plan.planHash !== input.planHash) {
|
|
495
549
|
return {
|
|
@@ -527,10 +581,11 @@ export async function executeDagRerun(input) {
|
|
|
527
581
|
plan,
|
|
528
582
|
reason: input.reason,
|
|
529
583
|
requestId: input.requestId,
|
|
584
|
+
promptOverrides: input.promptOverrides,
|
|
530
585
|
});
|
|
531
586
|
const continuationOptions = {
|
|
532
587
|
cwd: input.cwd,
|
|
533
|
-
spec:
|
|
588
|
+
spec: staged.spec,
|
|
534
589
|
state: staged.state,
|
|
535
590
|
runDir: staged.runDir,
|
|
536
591
|
...(input.maxConcurrent !== undefined
|
|
@@ -3,7 +3,8 @@ import path from "node:path";
|
|
|
3
3
|
import { generateTaskDagUseCase } from "../../application/dag/generate-task-dag.js";
|
|
4
4
|
import { writeJsonAtomic } from "../../infrastructure/harness/atomic-write.js";
|
|
5
5
|
import { DAG_RUNS_DIR, isTerminalDagRunStatus, locateDagRun, readDagRunSpec, readDagRunState, } from "./lifecycle.js";
|
|
6
|
-
import {
|
|
6
|
+
import { deriveDagRerunFeedback } from "./rerun-feedback.js";
|
|
7
|
+
import { parseDagSpec, } from "./types.js";
|
|
7
8
|
export const RERUN_TASK_LINEAGE_REL_PATH = ".runtime/rerun-task-lineage.json";
|
|
8
9
|
export function parseDagRerunTaskArgs(args) {
|
|
9
10
|
if (args.length === 0) {
|
|
@@ -218,6 +219,7 @@ async function writeRerunTaskLineage(cwd, newRunId, input) {
|
|
|
218
219
|
parentRunId: input.parentRunId,
|
|
219
220
|
reason: input.reason,
|
|
220
221
|
requestId: input.requestId,
|
|
222
|
+
feedback: input.feedback,
|
|
221
223
|
createdAt: new Date().toISOString(),
|
|
222
224
|
}, { repoRoot: cwd, allowCompletedFactsWrite: true });
|
|
223
225
|
}
|
|
@@ -228,12 +230,30 @@ function blockedResult(input) {
|
|
|
228
230
|
reason: input.reason,
|
|
229
231
|
requestId: input.requestId,
|
|
230
232
|
mode: "blocked",
|
|
233
|
+
feedback: input.feedback ?? {
|
|
234
|
+
inherited: false,
|
|
235
|
+
reason: "no-unresolved-verify-review-feedback",
|
|
236
|
+
},
|
|
231
237
|
blockedReason: input.blockedReason,
|
|
232
238
|
...(input.note ? { note: input.note } : {}),
|
|
233
239
|
...(input.taskId ? { taskId: input.taskId } : {}),
|
|
234
240
|
...(input.conflictPaths ? { conflictPaths: input.conflictPaths } : {}),
|
|
235
241
|
};
|
|
236
242
|
}
|
|
243
|
+
async function readGeneratedRerunFeedback(outputPath) {
|
|
244
|
+
const raw = JSON.parse(await readFile(outputPath, "utf8"));
|
|
245
|
+
return parseDagSpec(raw).rerunFeedback;
|
|
246
|
+
}
|
|
247
|
+
function summarizeFeedback(feedback, fallbackReason) {
|
|
248
|
+
return feedback
|
|
249
|
+
? {
|
|
250
|
+
inherited: true,
|
|
251
|
+
sourceNodeId: feedback.sourceNodeId,
|
|
252
|
+
digest: feedback.digest,
|
|
253
|
+
bindingStatus: feedback.binding.status,
|
|
254
|
+
}
|
|
255
|
+
: { inherited: false, reason: fallbackReason };
|
|
256
|
+
}
|
|
237
257
|
function extractRunSummary(generateResult) {
|
|
238
258
|
if (generateResult.mode === "generate+basic-validate")
|
|
239
259
|
return undefined;
|
|
@@ -277,7 +297,26 @@ export async function rerunStandaloneTask(input) {
|
|
|
277
297
|
note: eligibility.note,
|
|
278
298
|
});
|
|
279
299
|
}
|
|
300
|
+
let parentFeedback;
|
|
301
|
+
let missingFeedbackReason = "no-unresolved-verify-review-feedback";
|
|
302
|
+
try {
|
|
303
|
+
parentFeedback = await deriveDagRerunFeedback({
|
|
304
|
+
parentRunId: input.parentRunId,
|
|
305
|
+
taskId: taskResolution.taskId,
|
|
306
|
+
runDir: located.runDir,
|
|
307
|
+
spec,
|
|
308
|
+
state,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
// Feedback is additive recovery context. A corrupt/missing historical
|
|
313
|
+
// artifact must be reported honestly but must not block an otherwise
|
|
314
|
+
// eligible full rerun.
|
|
315
|
+
missingFeedbackReason = "feedback-unavailable";
|
|
316
|
+
}
|
|
280
317
|
const execute = input.execute ?? true;
|
|
318
|
+
let effectiveFeedback;
|
|
319
|
+
let expectedRerunFeedbackDigest;
|
|
281
320
|
// AC-001 generated-output-conflict preflight: when a real execution is
|
|
282
321
|
// requested, first generate the DAG draft (execute:false) so we can inspect
|
|
283
322
|
// the regenerated spec's requireChangedFiles writer write sets against
|
|
@@ -296,15 +335,23 @@ export async function rerunStandaloneTask(input) {
|
|
|
296
335
|
dryRun: false,
|
|
297
336
|
profile: input.profile ?? "auto",
|
|
298
337
|
profileExplicit: input.profileExplicit ?? false,
|
|
338
|
+
rerunFeedback: parentFeedback,
|
|
299
339
|
});
|
|
300
340
|
if (preflight.mode === "generate+basic-validate") {
|
|
301
341
|
let conflictPaths = [];
|
|
302
342
|
try {
|
|
303
343
|
const raw = await readFile(preflight.outputPath, "utf8");
|
|
304
344
|
const preflightSpec = parseDagSpec(JSON.parse(raw));
|
|
345
|
+
effectiveFeedback = preflightSpec.rerunFeedback;
|
|
346
|
+
expectedRerunFeedbackDigest = effectiveFeedback?.digest;
|
|
347
|
+
if (parentFeedback && !effectiveFeedback) {
|
|
348
|
+
throw new Error("rerun feedback missing from generated preflight DAG");
|
|
349
|
+
}
|
|
305
350
|
conflictPaths = await assessGeneratedOutputConflict(preflightSpec, input.cwd);
|
|
306
351
|
}
|
|
307
|
-
catch {
|
|
352
|
+
catch (error) {
|
|
353
|
+
if (parentFeedback)
|
|
354
|
+
throw error;
|
|
308
355
|
// If the draft cannot be parsed, fall through to the real execution
|
|
309
356
|
// path: execution's own validation will surface the structural defect.
|
|
310
357
|
conflictPaths = [];
|
|
@@ -332,7 +379,17 @@ export async function rerunStandaloneTask(input) {
|
|
|
332
379
|
dryRun: false,
|
|
333
380
|
profile: input.profile ?? "auto",
|
|
334
381
|
profileExplicit: input.profileExplicit ?? false,
|
|
382
|
+
rerunFeedback: parentFeedback,
|
|
383
|
+
expectedRerunFeedbackDigest,
|
|
335
384
|
});
|
|
385
|
+
if (parentFeedback && !effectiveFeedback) {
|
|
386
|
+
effectiveFeedback = await readGeneratedRerunFeedback(generateResult.outputPath);
|
|
387
|
+
}
|
|
388
|
+
if (expectedRerunFeedbackDigest &&
|
|
389
|
+
effectiveFeedback?.digest !== expectedRerunFeedbackDigest) {
|
|
390
|
+
throw new Error(`rerun feedback drift after generation: expected ${expectedRerunFeedbackDigest}, got ${effectiveFeedback?.digest ?? "missing"}`);
|
|
391
|
+
}
|
|
392
|
+
const feedbackSummary = summarizeFeedback(effectiveFeedback, missingFeedbackReason);
|
|
336
393
|
const newRunId = extractNewRunId(generateResult);
|
|
337
394
|
const runSummary = extractRunSummary(generateResult);
|
|
338
395
|
const completedSuccessfully = !execute ||
|
|
@@ -342,6 +399,7 @@ export async function rerunStandaloneTask(input) {
|
|
|
342
399
|
parentRunId: input.parentRunId,
|
|
343
400
|
reason: input.reason,
|
|
344
401
|
requestId: input.requestId,
|
|
402
|
+
feedback: feedbackSummary,
|
|
345
403
|
});
|
|
346
404
|
}
|
|
347
405
|
const result = {
|
|
@@ -350,6 +408,7 @@ export async function rerunStandaloneTask(input) {
|
|
|
350
408
|
reason: input.reason,
|
|
351
409
|
requestId: input.requestId,
|
|
352
410
|
mode: "generate+validate+execute",
|
|
411
|
+
feedback: feedbackSummary,
|
|
353
412
|
taskId: taskResolution.taskId,
|
|
354
413
|
...(newRunId ? { newRunId } : {}),
|
|
355
414
|
...(runSummary && !completedSuccessfully && "status" in runSummary
|