@tea-agent/loop-agent 0.38.1 → 0.38.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +126 -12
- package/README.md +2 -0
- 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/commands/init.js +2 -14
- package/dist/commands/task-advance.js +31 -0
- package/dist/executors/dag-pi-executor.js +99 -5
- 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-sdk-executor.js +187 -59
- package/dist/executors/shell-executor.js +324 -16
- package/dist/executors/shell-write-guard.js +7 -0
- package/dist/governance/manifest-types.js +0 -2
- package/dist/shared/operator/capabilities.js +87 -22
- package/dist/task/config-types.js +22 -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/worker/console/chat/deferred-turn.js +91 -0
- package/dist/worker/console/chat/pi-runtime.js +10 -5
- package/dist/worker/console/chat/routes.js +458 -47
- package/dist/worker/console/chat/semantic-activity.js +13 -13
- package/dist/worker/console/chat/session-store.js +146 -2
- package/dist/worker/console/harness-pi-model-matrix.js +2 -11
- package/dist/worker/console/operator-actions.js +5 -4
- 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/server.js +6 -1
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-Hfsot6rP.js → abnfDiagram-N423BO3Z-C4kVYweA.js} +1 -1
- package/dist/worker/console/static/assets/{arc-YGwEASin.js → arc-Bj2M8iO5.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CmOM0F92.js → architectureDiagram-T3A2C74G-CLlXe4-6.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-DDLuaHC5.js → blockDiagram-VBNYF7ZC-psEp5xH0.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-B0Um43W4.js → c4Diagram-5PPSVZJV-DeKcOCGJ.js} +1 -1
- package/dist/worker/console/static/assets/channel-CPF4N7pf.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-DvBN4JZc.js → chunk-2GRJ4B5K-K0C744p1.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-B1UVgW4E.js → chunk-2Q5K7J3B-xbNw4J9-.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-Cf0GgXzX.js → chunk-5RXB4S5H-DjLaSDUO.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-DbNA24sj.js → chunk-5VM5RSS4-CtiPlKel.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-Bi_0Be06.js → chunk-6Q2QTUOP-DZtolir7.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-C8pPGvRd.js → chunk-GF5L2VYU-CPcdNeew.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BitQY8Z-.js → chunk-JWPE2WC7-NgHc6V37.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-U67GCiEo.js → chunk-KBJHAD2P-DQ_T-hg8.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-IoJwXPBA.js → chunk-RYQCIY6F-DgLsxcVP.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-YDZ_UHgE.js → chunk-XXDRQBXY-UrVoM9z1.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C2DwA_Y1.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C2DwA_Y1.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CUZgsiRC.js → cose-bilkent-JH36ORCC-CLkYvTb3.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-COmCh4pT.js → cynefin-VYW2F7L2-BwT_xKrE.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Ta6FSm68.js → cynefinDiagram-MW4NZA55-Bi9tuzif.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-UdcR-VW9.js → dagre-VZM6K2ZE-BCIqkWBV.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-DybcO3Et.js → diagram-7IWD3JNH-Dip6l9_Z.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-wErDFWLw.js → diagram-B4RE2ZJO-B-xkh_wK.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DkVHZWqz.js → diagram-LBJQPF4R-DZO0kTt_.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DdqWs7QN.js → diagram-Q27KOJAE-D6ZplNbZ.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-C9dMVmDo.js → diagram-UB23O5K3-BPekbhoS.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-B2T2WcBK.js → ebnfDiagram-BXEA7PRR-BtmaJpK5.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-Bt9rjDFq.js → erDiagram-JOGREHBK-6DS0Xc44.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-U8ic_tvi.js → flowDiagram-UKHOOZJN-CihnROSm.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-BfQJdQoO.js → ganttDiagram-PKOTCBZU-CZC4zOE2.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-CpMftH4O.js → gitGraphDiagram-DS77QQ5N-Dq1fhzGN.js} +1 -1
- package/dist/worker/console/static/assets/index-DTZOKgAn.js +404 -0
- package/dist/worker/console/static/assets/index-Ya5FE7cD.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DzqWXNO_.js → infoDiagram-6WML65LV-DlSl4BGx.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-CJmGQ5Yp.js → ishikawaDiagram-WSZJBQD7-snnzFl_U.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-ChPtKFLz.js → journeyDiagram-NVQOT4AX-BDE7qpMx.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-PRe7wB5u.js → kanban-definition-27J2QSJJ-CD2Ci04G.js} +1 -1
- package/dist/worker/console/static/assets/{linear-DpKX6QNp.js → linear-DdnapKIH.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-Dnl76b7Y.js → mermaid.core-BVjAT9b8.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-S9PCXc3J.js → mindmap-definition-FAOFIHXS-D0yJk-zA.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-Du8e-FSX.js → pegDiagram-VL7TDLO6-BVo9tFP-.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CY6OmHTc.js → pieDiagram-7S7Q4E2Y-DnOV-mZ_.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-C65GPhpj.js → quadrantDiagram-CIZ2JOQS-CzUJo58i.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-U6IUiTCL.js → railroadDiagram-AXF67PYL-Bvikrolh.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-D-I_9Qhi.js → requirementDiagram-LRYGKXZP-DtaSVCap.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-B57Fhed0.js → sankeyDiagram-W5VNT64P-C2c9A0wW.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-CJOtiw21.js → sequenceDiagram-SI44F4Z6-DmXJcU7r.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-FyX8zxSh.js → sizeCapture-X5ZJPWSS-B4GUFW92.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-eF-pbs9t.js → stateDiagram-OKZ733FA-Dsad2MXf.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-B8FB_cNk.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CcfkA-Dq.js → swimlanes-SLNWSIFB-DGq48Fbi.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CrZYaWfx.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-DH8FjnOK.js → timeline-definition-Z64GVDOM-CSSi6OFf.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-beoTagJg.js → vennDiagram-T6HMQDX7-BqyzPrWv.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-CQSfw4cK.js → wardleyDiagram-T6FBY63Y-DlznVb6S.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CQgzQ6jn.js → xychartDiagram-ELKLHX3M-CfBcgJ_K.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/console-types.js +0 -12
- 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/operator-chat/chat-sse-events.js +38 -1
- package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +328 -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 +42 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +3 -0
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +204 -11
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +23 -1
- package/dist/worker/observe/node-input.js +72 -3
- package/dist/worker/observe/static/dag-history-labels.js +4 -0
- package/dist/worker/observe/static/state.js +2 -2
- package/dist/worker/observe/static/views/dag-inspector.js +51 -0
- package/dist/worker/observe/static/views/session-timeline.js +18 -5
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +809 -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 +2 -1
- package/dist/workflows/dag/convergence/controller.js +141 -0
- package/dist/workflows/dag/dynamic-runtime/map.js +24 -8
- package/dist/workflows/dag/dynamic-runtime/shared.js +1 -1
- package/dist/workflows/dag/failure-category.js +4 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +355 -26
- package/dist/workflows/dag/frontend-prewrite-gate.js +96 -84
- package/dist/workflows/dag/frontend-test-case-quality.js +5 -13
- package/dist/workflows/dag/frontend-test-environment-probe.js +227 -0
- package/dist/workflows/dag/frontend-test-markdown.js +61 -0
- package/dist/workflows/dag/frontend-test-result-contract.js +10 -18
- package/dist/workflows/dag/frontend-test-standard-scenarios.js +68 -0
- package/dist/workflows/dag/init-hybrid.js +720 -177
- package/dist/workflows/dag/node-execution.js +164 -26
- package/dist/workflows/dag/prompt.js +60 -1
- package/dist/workflows/dag/rerun-feedback.js +212 -0
- package/dist/workflows/dag/rerun-plan.js +112 -27
- package/dist/workflows/dag/rerun-run.js +3 -0
- package/dist/workflows/dag/rerun-task.js +61 -2
- package/dist/workflows/dag/retry-policy.js +18 -0
- package/dist/workflows/dag/structured-output-repair.js +712 -0
- package/dist/workflows/dag/types.js +121 -0
- package/dist/workflows/dag/validate.js +41 -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-test-dag.json +8 -10
- package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +1 -1
- package/docs/templates/harness.schema.json +0 -8
- package/harness.json +1 -8
- package/package.json +8 -4
- 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/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/dist/worker/console/static/assets/channel-Dp_2Aqf1.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL--CODy37j.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE--CODy37j.js +0 -1
- package/dist/worker/console/static/assets/index-BA5-N5ER.css +0 -1
- package/dist/worker/console/static/assets/index-DKPXBrdc.js +0 -325
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-B-mvnLEc.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-JtpBmIv4.js +0 -8
|
@@ -1,7 +1,7 @@
|
|
|
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
6
|
const REVIEW_VERDICT_RECOVERY_NODE_ID = "review-verdict-recovery-pi";
|
|
7
7
|
const SAFE_SHELL_GATE_KEYS = [
|
|
@@ -122,32 +122,56 @@ function hasDynamicRuntime(task) {
|
|
|
122
122
|
task.dynamicCondition ||
|
|
123
123
|
task.dynamicLoopUntil);
|
|
124
124
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
/**
|
|
126
|
+
* Narrow admission for the standard DAG's generated final verification node.
|
|
127
|
+
* `verifyEvidence` alone is metadata, not proof that an arbitrary shell is
|
|
128
|
+
* read-only, so custom shell ids and non-canonical shapes stay blocked.
|
|
129
|
+
*/
|
|
130
|
+
function isCanonicalGeneratedVerifyShell(task) {
|
|
131
|
+
if (task.executor !== "shell")
|
|
132
|
+
return false;
|
|
133
|
+
const shell = task.shell;
|
|
134
|
+
if (!shell)
|
|
135
|
+
return false;
|
|
136
|
+
const evidence = shell.verifyEvidence;
|
|
137
|
+
if (task.id !== "verify-shell")
|
|
138
|
+
return false;
|
|
139
|
+
if (task.role !== "verifier")
|
|
140
|
+
return false;
|
|
141
|
+
if (task.writePolicy !== "read-only")
|
|
142
|
+
return false;
|
|
143
|
+
if (!evidence)
|
|
144
|
+
return false;
|
|
145
|
+
if (evidence.phase !== "final")
|
|
146
|
+
return false;
|
|
147
|
+
if (evidence.quota !== "full")
|
|
148
|
+
return false;
|
|
149
|
+
if (evidence.commandSource !== "adapter")
|
|
150
|
+
return false;
|
|
151
|
+
if (evidence.finalFullRequired !== true)
|
|
152
|
+
return false;
|
|
153
|
+
if ((shell.commands?.length ?? 0) === 0)
|
|
154
|
+
return false;
|
|
155
|
+
if (evidence.commandCount !== shell.commands.length)
|
|
156
|
+
return false;
|
|
157
|
+
// Any side-effect-capable config disqualifies the node.
|
|
128
158
|
if (shell.preset)
|
|
129
|
-
return
|
|
159
|
+
return false;
|
|
130
160
|
if (shell.projectGovernanceGate)
|
|
131
|
-
return
|
|
161
|
+
return false;
|
|
132
162
|
if (shell.frontendPrewriteGate)
|
|
133
|
-
return
|
|
163
|
+
return false;
|
|
134
164
|
if (shell.frontendVerificationBundle)
|
|
135
|
-
return
|
|
165
|
+
return false;
|
|
136
166
|
if (shell.frontendReviewContext)
|
|
137
|
-
return
|
|
167
|
+
return false;
|
|
138
168
|
if (shell.backendTestPipeline)
|
|
139
|
-
return
|
|
140
|
-
if (shell.
|
|
141
|
-
return
|
|
142
|
-
if (shell.nonZeroExitPolicy)
|
|
143
|
-
return true;
|
|
169
|
+
return false;
|
|
170
|
+
if (shell.nonZeroExitPolicy === "record")
|
|
171
|
+
return false;
|
|
144
172
|
if (shell.envAllowlist?.length)
|
|
145
|
-
return
|
|
146
|
-
|
|
147
|
-
return true;
|
|
148
|
-
if (shell.cwd)
|
|
149
|
-
return true;
|
|
150
|
-
return false;
|
|
173
|
+
return false;
|
|
174
|
+
return true;
|
|
151
175
|
}
|
|
152
176
|
function isSafeGateOnlyShell(task) {
|
|
153
177
|
if (task.executor !== "shell")
|
|
@@ -155,9 +179,32 @@ function isSafeGateOnlyShell(task) {
|
|
|
155
179
|
const shell = task.shell;
|
|
156
180
|
if (!shell)
|
|
157
181
|
return false;
|
|
158
|
-
if (
|
|
182
|
+
if (!SAFE_SHELL_GATE_KEYS.some((key) => shell[key] !== undefined)) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
// No side-effect-capable config may ride along on a gate-only shell.
|
|
186
|
+
// Deterministic/read-only riders are admitted: projectGovernanceGate (repo
|
|
187
|
+
// governance context path), cwd, and timeoutMs never execute commands, so
|
|
188
|
+
// the governance-standard gate stays rerun-eligible.
|
|
189
|
+
if ((shell.commands?.length ?? 0) > 0)
|
|
190
|
+
return false;
|
|
191
|
+
if (shell.preset)
|
|
192
|
+
return false;
|
|
193
|
+
if (shell.frontendPrewriteGate)
|
|
194
|
+
return false;
|
|
195
|
+
if (shell.frontendVerificationBundle)
|
|
196
|
+
return false;
|
|
197
|
+
if (shell.frontendReviewContext)
|
|
198
|
+
return false;
|
|
199
|
+
if (shell.backendTestPipeline)
|
|
200
|
+
return false;
|
|
201
|
+
if (shell.verifyEvidence)
|
|
202
|
+
return false;
|
|
203
|
+
if (shell.nonZeroExitPolicy)
|
|
204
|
+
return false;
|
|
205
|
+
if (shell.envAllowlist?.length)
|
|
159
206
|
return false;
|
|
160
|
-
return
|
|
207
|
+
return true;
|
|
161
208
|
}
|
|
162
209
|
function isStaticTask(task) {
|
|
163
210
|
return task.executor === "static";
|
|
@@ -259,7 +306,10 @@ export function computeResetClosure(spec, effectiveFromNodeId) {
|
|
|
259
306
|
...collectReachableDescendants(spec, effectiveFromNodeId),
|
|
260
307
|
].sort();
|
|
261
308
|
}
|
|
262
|
-
|
|
309
|
+
function isNeverStartedNodeStatus(status) {
|
|
310
|
+
return status === undefined || status === "SKIPPED" || status === "PENDING";
|
|
311
|
+
}
|
|
312
|
+
export function assessResetClosureSafety(spec, resetNodeIds, options = {}) {
|
|
263
313
|
const tasks = taskById(spec);
|
|
264
314
|
const blockingNodes = [];
|
|
265
315
|
const reasonCodes = [];
|
|
@@ -267,7 +317,17 @@ export function assessResetClosureSafety(spec, resetNodeIds) {
|
|
|
267
317
|
const task = tasks.get(nodeId);
|
|
268
318
|
if (!task)
|
|
269
319
|
continue;
|
|
320
|
+
if (options.parentNodes !== undefined &&
|
|
321
|
+
isNeverStartedNodeStatus(options.parentNodes[nodeId]?.status)) {
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
270
324
|
if (isWriterTask(task, spec)) {
|
|
325
|
+
if (nodeId === options.allowedTransportWriterNodeId)
|
|
326
|
+
continue;
|
|
327
|
+
if (nodeId === options.allowedExclusiveShellFromNodeId &&
|
|
328
|
+
task.executor === "shell") {
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
271
331
|
blockingNodes.push({
|
|
272
332
|
nodeId,
|
|
273
333
|
reasonCode: "restart-subgraph-contains-writer",
|
|
@@ -305,7 +365,8 @@ export function assessResetClosureSafety(spec, resetNodeIds) {
|
|
|
305
365
|
continue;
|
|
306
366
|
}
|
|
307
367
|
if (task.executor === "shell") {
|
|
308
|
-
if (!isSafeGateOnlyShell(task)
|
|
368
|
+
if (!isSafeGateOnlyShell(task) &&
|
|
369
|
+
!isCanonicalGeneratedVerifyShell(task)) {
|
|
309
370
|
blockingNodes.push({
|
|
310
371
|
nodeId,
|
|
311
372
|
reasonCode: "restart-subgraph-contains-unsafe-shell",
|
|
@@ -448,6 +509,20 @@ export async function evaluateDagRerunPlan(input) {
|
|
|
448
509
|
: [];
|
|
449
510
|
const resetSet = new Set(resetNodeIds);
|
|
450
511
|
const importedNodeIds = allNodeIds.filter((nodeId) => !resetSet.has(nodeId));
|
|
512
|
+
const tasksByIdForPlan = taskById(input.parentSpec);
|
|
513
|
+
const effectiveTask = effectiveFromNodeId
|
|
514
|
+
? tasksByIdForPlan.get(effectiveFromNodeId)
|
|
515
|
+
: undefined;
|
|
516
|
+
const effectiveRecord = effectiveFromNodeId
|
|
517
|
+
? input.parentState.nodes[effectiveFromNodeId]
|
|
518
|
+
: undefined;
|
|
519
|
+
const transportWriterRestart = Boolean(effectiveFromNodeId &&
|
|
520
|
+
effectiveRecord?.status === "ERROR" &&
|
|
521
|
+
isTransportCrashedExclusiveWriter(effectiveTask, effectiveRecord.failureCategory));
|
|
522
|
+
const convergenceWriterRestart = Boolean(transportWriterRestart &&
|
|
523
|
+
input.parentSpec.convergence?.enabled === true &&
|
|
524
|
+
input.parentState.convergence?.enabled === true &&
|
|
525
|
+
!input.parentState.convergence.terminalReason);
|
|
451
526
|
const controllerIdentityMatched = input.currentControllerFingerprint === undefined ||
|
|
452
527
|
input.parentControllerFingerprint === undefined ||
|
|
453
528
|
input.currentControllerFingerprint === input.parentControllerFingerprint;
|
|
@@ -482,11 +557,11 @@ export async function evaluateDagRerunPlan(input) {
|
|
|
482
557
|
reasonCodes.push("parent-lifecycle-ineligible");
|
|
483
558
|
blockedReasons.push("parent-lifecycle-ineligible");
|
|
484
559
|
}
|
|
485
|
-
if (input.parentSpec.convergence?.enabled) {
|
|
560
|
+
if (input.parentSpec.convergence?.enabled && !convergenceWriterRestart) {
|
|
486
561
|
reasonCodes.push("convergence-rerun-unsupported");
|
|
487
562
|
blockedReasons.push("convergence-rerun-unsupported");
|
|
488
563
|
}
|
|
489
|
-
if (input.parentState.convergence?.enabled) {
|
|
564
|
+
if (input.parentState.convergence?.enabled && !convergenceWriterRestart) {
|
|
490
565
|
reasonCodes.push("convergence-rerun-unsupported");
|
|
491
566
|
blockedReasons.push("convergence-rerun-unsupported");
|
|
492
567
|
}
|
|
@@ -530,7 +605,17 @@ export async function evaluateDagRerunPlan(input) {
|
|
|
530
605
|
blockedReasons.push("ambiguous-skipped-rewrite");
|
|
531
606
|
}
|
|
532
607
|
}
|
|
533
|
-
const closureSafety = assessResetClosureSafety(input.parentSpec, resetNodeIds
|
|
608
|
+
const closureSafety = assessResetClosureSafety(input.parentSpec, resetNodeIds, {
|
|
609
|
+
...(transportWriterRestart && effectiveFromNodeId
|
|
610
|
+
? { allowedTransportWriterNodeId: effectiveFromNodeId }
|
|
611
|
+
: {}),
|
|
612
|
+
parentNodes: input.parentState.nodes,
|
|
613
|
+
...(effectiveFromNodeId &&
|
|
614
|
+
effectiveTask?.executor === "shell" &&
|
|
615
|
+
effectiveRecord?.status === "ERROR"
|
|
616
|
+
? { allowedExclusiveShellFromNodeId: effectiveFromNodeId }
|
|
617
|
+
: {}),
|
|
618
|
+
});
|
|
534
619
|
blockingNodes.push(...closureSafety.blockingNodes);
|
|
535
620
|
for (const code of closureSafety.reasonCodes) {
|
|
536
621
|
reasonCodes.push(code);
|
|
@@ -445,6 +445,9 @@ async function stageContinuationRun(input) {
|
|
|
445
445
|
ranks,
|
|
446
446
|
nodes,
|
|
447
447
|
continuation,
|
|
448
|
+
...(input.parentState.convergence
|
|
449
|
+
? { convergence: structuredClone(input.parentState.convergence) }
|
|
450
|
+
: {}),
|
|
448
451
|
...(input.parentState.evaluation
|
|
449
452
|
? { evaluation: structuredClone(input.parentState.evaluation) }
|
|
450
453
|
: {}),
|
|
@@ -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
|
|
@@ -201,6 +201,24 @@ function isExclusivePiImplementer(task) {
|
|
|
201
201
|
!task.dynamicCondition &&
|
|
202
202
|
!task.dynamicLoopUntil);
|
|
203
203
|
}
|
|
204
|
+
/**
|
|
205
|
+
* Provider/transport failure categories that can crash a writer executor
|
|
206
|
+
* after some workspace writes already landed on disk. A continuation may
|
|
207
|
+
* restart that writer itself; it must never import or normalize the ERROR fact.
|
|
208
|
+
*/
|
|
209
|
+
export const WRITER_TRANSPORT_FAILURE_CATEGORIES = new Set([
|
|
210
|
+
"unavailable",
|
|
211
|
+
"network",
|
|
212
|
+
"rate-limit",
|
|
213
|
+
"timeout",
|
|
214
|
+
]);
|
|
215
|
+
export function isTransportCrashedExclusiveWriter(task, failureCategory) {
|
|
216
|
+
if (!task)
|
|
217
|
+
return false;
|
|
218
|
+
return (isExclusivePiImplementer(task) &&
|
|
219
|
+
failureCategory !== undefined &&
|
|
220
|
+
WRITER_TRANSPORT_FAILURE_CATEGORIES.has(failureCategory));
|
|
221
|
+
}
|
|
204
222
|
/**
|
|
205
223
|
* Static eligibility for the requireChangedFiles writer-empty-diff class.
|
|
206
224
|
* The Pi executor assigns writer-empty-diff only after post-write-guard
|