@tea-agent/loop-agent 0.42.0-next.1 → 0.42.0-next.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +319 -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 +3976 -76
- package/dist/executors/pi-executor.js +15 -4
- 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/dag-helpers.js +4 -2
- package/dist/worker/observe/static/dag-node-purpose.js +5 -0
- 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/relations.js +2 -2
- 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 +95 -0
- package/dist/worker/observe/static/views/dag-inspector.js +14 -7
- package/dist/worker/observe/static/views/dag.js +10 -2
- 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 +69 -10
- 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 +106 -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 +1028 -562
- package/dist/workflows/dag/interrupt-request.js +7 -0
- package/dist/workflows/dag/node-execution.js +828 -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 +219 -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 +23 -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
|
@@ -13,6 +13,8 @@ import { runDagUseCase } from "./run-dag.js";
|
|
|
13
13
|
import { getTaskPaths, loadTaskConfig } from "../../task/runtime.js";
|
|
14
14
|
import { writeTaskConfig } from "../../infrastructure/harness/task-store.js";
|
|
15
15
|
import { runFrontendPreflight } from "../../task/frontend-preflight.js";
|
|
16
|
+
import { discoverFrontendProjectCapability } from "../../task/frontend-project-capability.js";
|
|
17
|
+
import { classifyTaskDemand } from "../../task/task-demand-routing.js";
|
|
16
18
|
import { observeTaskContract } from "../../task/contract/observe.js";
|
|
17
19
|
import { adoptTaskContract } from "../../task/contract/adopt.js";
|
|
18
20
|
const PLACEHOLDER_WRITESET_MARKER = "REPLACE/WITH";
|
|
@@ -88,6 +90,63 @@ function buildBasicValidationLevel(outputPath) {
|
|
|
88
90
|
note: "Internal generation validation is basic only; standalone strict governance validation must be confirmed separately.",
|
|
89
91
|
};
|
|
90
92
|
}
|
|
93
|
+
async function promoteAutomaticFrontendTaskKind(input) {
|
|
94
|
+
if (input.taskConfig.taskKind !== "standard" ||
|
|
95
|
+
input.taskConfig.taskKindExplicit) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
let requirementMarkdown = "";
|
|
99
|
+
try {
|
|
100
|
+
requirementMarkdown = await readFile(path.join(getTaskPaths(input.repoRoot, input.taskId).sourceDir, "需求.md"), "utf8");
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
if (!requirementMarkdown.trim())
|
|
106
|
+
return false;
|
|
107
|
+
let frontendProject;
|
|
108
|
+
try {
|
|
109
|
+
frontendProject = await discoverFrontendProjectCapability(input.repoRoot);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// The existing standard route remains the safe fallback when capability
|
|
113
|
+
// discovery is unavailable.
|
|
114
|
+
}
|
|
115
|
+
const classification = classifyTaskDemand({
|
|
116
|
+
title: input.taskConfig.title,
|
|
117
|
+
requirementMarkdown,
|
|
118
|
+
allowedPaths: input.taskConfig.allowedPaths,
|
|
119
|
+
frontendProject: frontendProject
|
|
120
|
+
? {
|
|
121
|
+
framework: frontendProject.framework,
|
|
122
|
+
evidencePaths: frontendProject.evidencePaths,
|
|
123
|
+
}
|
|
124
|
+
: undefined,
|
|
125
|
+
});
|
|
126
|
+
if (classification.kind !== "frontend")
|
|
127
|
+
return false;
|
|
128
|
+
await writeTaskConfig(input.repoRoot, input.taskId, {
|
|
129
|
+
...input.taskConfig,
|
|
130
|
+
taskKind: "frontend-implementation",
|
|
131
|
+
dagFallbackReason: "Automatically classified as a high-confidence frontend implementation task.",
|
|
132
|
+
});
|
|
133
|
+
const state = await observeTaskContract({
|
|
134
|
+
repoRoot: input.repoRoot,
|
|
135
|
+
taskId: input.taskId,
|
|
136
|
+
});
|
|
137
|
+
if (!state.ref || !state.observedCanonicalHash) {
|
|
138
|
+
throw new Error("automatic frontend routing could not reconcile the managed Task Contract");
|
|
139
|
+
}
|
|
140
|
+
await adoptTaskContract({
|
|
141
|
+
repoRoot: input.repoRoot,
|
|
142
|
+
taskId: input.taskId,
|
|
143
|
+
expectedRevision: state.ref.revision,
|
|
144
|
+
expectedObservedHash: state.observedCanonicalHash,
|
|
145
|
+
requestId: `automatic-frontend-route-${input.taskId}-${randomUUID()}`,
|
|
146
|
+
requestPayloadSha256: randomUUID().replaceAll("-", "").padEnd(64, "0"),
|
|
147
|
+
});
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
91
150
|
function isBroadWriteSetEntryForPacket(entry) {
|
|
92
151
|
const normalized = entry.trim().replace(/\\/g, "/").replace(/^\.\//, "");
|
|
93
152
|
if (!normalized || normalized === "." || normalized === "./")
|
|
@@ -280,12 +339,25 @@ export async function generateTaskDagUseCase(input) {
|
|
|
280
339
|
requestPayloadSha256: randomUUID().replaceAll("-", "").padEnd(64, "0"),
|
|
281
340
|
});
|
|
282
341
|
}
|
|
283
|
-
const
|
|
342
|
+
const routingTaskConfig = await loadTaskConfig(repoRoot, parsed.taskId);
|
|
343
|
+
let initialTemplate = routingTaskConfig.taskKind === "frontend-implementation"
|
|
344
|
+
? "frontend-implementation"
|
|
345
|
+
: "standard-dag";
|
|
346
|
+
if (initialTemplate === "standard-dag") {
|
|
347
|
+
const promoted = await promoteAutomaticFrontendTaskKind({
|
|
348
|
+
repoRoot,
|
|
349
|
+
taskId: parsed.taskId,
|
|
350
|
+
taskConfig: routingTaskConfig,
|
|
351
|
+
});
|
|
352
|
+
if (promoted)
|
|
353
|
+
initialTemplate = "frontend-implementation";
|
|
354
|
+
}
|
|
355
|
+
let candidateResult = await initHybridDagFromTask(repoRoot, parsed.taskId, {
|
|
284
356
|
outputPath: parsed.outputPath,
|
|
285
|
-
template:
|
|
357
|
+
template: initialTemplate,
|
|
286
358
|
rerunFeedback: parsed.rerunFeedback,
|
|
287
359
|
});
|
|
288
|
-
|
|
360
|
+
let candidateValidateSummary = await validateDagUseCase(buildValidateInput(repoRoot, candidateResult.outputPath, parsed));
|
|
289
361
|
const profileRouting = await resolveProfileRouting(repoRoot, parsed, candidateValidateSummary.governanceProfile ?? {
|
|
290
362
|
profile: "standard",
|
|
291
363
|
process: [],
|
|
@@ -308,22 +380,6 @@ export async function generateTaskDagUseCase(input) {
|
|
|
308
380
|
profileRouting.selectedByProfile = parsed.profile;
|
|
309
381
|
}
|
|
310
382
|
}
|
|
311
|
-
// Persist the automatic business workflow route while preserving the
|
|
312
|
-
// governance profile as a separate routing dimension.
|
|
313
|
-
if (hasAutomaticTaskSourceRoute &&
|
|
314
|
-
candidateResult.template === "frontend-implementation") {
|
|
315
|
-
const taskConfig = await loadTaskConfig(repoRoot, parsed.taskId);
|
|
316
|
-
// Human-explicit taskKind (incl. explicit standard) is a hard contract:
|
|
317
|
-
// never rewrite task.json taskKind via semantic classification.
|
|
318
|
-
if (!taskConfig.taskKindExplicit &&
|
|
319
|
-
(!taskConfig.taskKind || taskConfig.taskKind === "standard")) {
|
|
320
|
-
await writeTaskConfig(repoRoot, parsed.taskId, {
|
|
321
|
-
...taskConfig,
|
|
322
|
-
taskKind: "frontend-implementation",
|
|
323
|
-
dagFallbackReason: "Automatically classified as a high-confidence frontend implementation task.",
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
383
|
const initResult = profileRouting.selectedTemplate === candidateResult.template
|
|
328
384
|
? candidateResult
|
|
329
385
|
: await initHybridDagFromTask(repoRoot, parsed.taskId, {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from "node:path";
|
|
1
2
|
import { createDagEventObserver } from "../../workflows/dag/event-observer.js";
|
|
2
3
|
import { composeDagRunObservers } from "../../workflows/dag/observer-compose.js";
|
|
3
4
|
import { createDagCanvasObserver, resolveCanvasPath, } from "../../workflows/dag/canvas-observer.js";
|
|
@@ -28,6 +29,41 @@ function buildRunDagNextSteps(runId) {
|
|
|
28
29
|
`loop-agent dag report --run-id ${runId} --markdown # advanced forensic`,
|
|
29
30
|
];
|
|
30
31
|
}
|
|
32
|
+
/** Shared production recovery seam for fresh runs and recovery continuations. */
|
|
33
|
+
export async function buildDefaultFrontendRecoveryDeps(repoRoot) {
|
|
34
|
+
// Keep the application layer free of a static worker→application cycle. The
|
|
35
|
+
// recovery controller is only needed after a frontend terminal failure.
|
|
36
|
+
const [{ openConsoleAppData }, { OperationStore }, { createOperationEventStore }, { LoopAgentClient }, { resolveSiblingLoopAgentBin },] = await Promise.all([
|
|
37
|
+
import("../../infrastructure/console/app-data.js"),
|
|
38
|
+
import("../../infrastructure/console/operation-store.js"),
|
|
39
|
+
import("../../worker/console/operation-sse.js"),
|
|
40
|
+
import("../../worker/loop-agent/loop-agent-client.js"),
|
|
41
|
+
import("../../worker/console/sibling-controller.js"),
|
|
42
|
+
]);
|
|
43
|
+
const appData = openConsoleAppData({ repoRoot });
|
|
44
|
+
const store = new OperationStore(appData);
|
|
45
|
+
const events = createOperationEventStore({
|
|
46
|
+
persistenceDir: path.join(appData.operations, "events"),
|
|
47
|
+
});
|
|
48
|
+
const client = new LoopAgentClient({
|
|
49
|
+
loopAgentBin: resolveSiblingLoopAgentBin(),
|
|
50
|
+
artifactRoot: path.join(appData.root, "client-artifacts", appData.fingerprint),
|
|
51
|
+
resolveIdentity: true,
|
|
52
|
+
});
|
|
53
|
+
return {
|
|
54
|
+
store,
|
|
55
|
+
operationRunnerDeps: {
|
|
56
|
+
store,
|
|
57
|
+
events,
|
|
58
|
+
client,
|
|
59
|
+
repoRoot,
|
|
60
|
+
env: {
|
|
61
|
+
...process.env,
|
|
62
|
+
LOOP_CONSOLE_APP_DATA: appData.root,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
31
67
|
export async function runDagUseCase(input) {
|
|
32
68
|
const spec = await loadDagSpecFromFile(input.dagPath);
|
|
33
69
|
assertValidDagSpec(spec);
|
|
@@ -80,6 +116,10 @@ export async function runDagUseCase(input) {
|
|
|
80
116
|
...(canvasError ? { canvasError } : {}),
|
|
81
117
|
};
|
|
82
118
|
}
|
|
119
|
+
const recovery = input.recovery ??
|
|
120
|
+
(!input.initOnly && spec.tasks.some((task) => task.id === "frontend-implement-pi")
|
|
121
|
+
? await buildDefaultFrontendRecoveryDeps(input.cwd)
|
|
122
|
+
: undefined);
|
|
83
123
|
const summary = await runDag(spec, {
|
|
84
124
|
cwd: input.cwd,
|
|
85
125
|
initOnly: input.initOnly,
|
|
@@ -90,6 +130,7 @@ export async function runDagUseCase(input) {
|
|
|
90
130
|
...(input.workerAssociation
|
|
91
131
|
? { workerAssociation: input.workerAssociation }
|
|
92
132
|
: {}),
|
|
133
|
+
...(recovery ? { recovery } : {}),
|
|
93
134
|
});
|
|
94
135
|
const canvasError = await flushCanvasSafely(canvas);
|
|
95
136
|
if (resolvedCanvasPath) {
|
|
@@ -260,6 +260,10 @@ async function monitorRun(input) {
|
|
|
260
260
|
failureCategory: "monitor-timeout",
|
|
261
261
|
});
|
|
262
262
|
}
|
|
263
|
+
function isStartupGateEligible(snapshot) {
|
|
264
|
+
const status = snapshot.source?.sourceFidelityStartup?.status;
|
|
265
|
+
return status === undefined || status === "complete" || status === "not-applicable";
|
|
266
|
+
}
|
|
263
267
|
function nextMutationAction(actions) {
|
|
264
268
|
for (const action of actions) {
|
|
265
269
|
if (action.type !== "stop")
|
|
@@ -379,7 +383,10 @@ export async function advanceTaskLifecycle(input) {
|
|
|
379
383
|
};
|
|
380
384
|
}
|
|
381
385
|
const token = parseGateToken(input.rejectGateToken);
|
|
382
|
-
const currentGate = snapshot.gate ??
|
|
386
|
+
const currentGate = snapshot.gate ??
|
|
387
|
+
(isStartupGateEligible(snapshot)
|
|
388
|
+
? await buildCurrentGate(input.repoRoot, input.taskId)
|
|
389
|
+
: null);
|
|
383
390
|
if (!currentGate || !gateMatches(currentGate, token)) {
|
|
384
391
|
return {
|
|
385
392
|
taskId: input.taskId,
|
|
@@ -443,11 +450,12 @@ export async function advanceTaskLifecycle(input) {
|
|
|
443
450
|
dryRun,
|
|
444
451
|
skipFinalize: Boolean(input.skipFinalize),
|
|
445
452
|
forcePrepareContract: Boolean(input.fromDraftPath) && !preparedContractThisCall,
|
|
453
|
+
startupRepairAttempted: preparedContractThisCall,
|
|
446
454
|
});
|
|
447
455
|
if (dryRun) {
|
|
448
456
|
const plan = planOnce(snapshot);
|
|
449
457
|
const plannedGate = snapshot.gate ??
|
|
450
|
-
(snapshot.dagDraft?.exists
|
|
458
|
+
(isStartupGateEligible(snapshot) && snapshot.dagDraft?.exists
|
|
451
459
|
? await buildCurrentGate(input.repoRoot, input.taskId)
|
|
452
460
|
: null);
|
|
453
461
|
return {
|
|
@@ -499,7 +507,10 @@ export async function advanceTaskLifecycle(input) {
|
|
|
499
507
|
}
|
|
500
508
|
if (action.type === "create-task") {
|
|
501
509
|
if (!snapshot.exists) {
|
|
502
|
-
await createTask(input.repoRoot, input.taskId, input.title
|
|
510
|
+
await createTask(input.repoRoot, input.taskId, input.title, {
|
|
511
|
+
taskKind: input.taskKind,
|
|
512
|
+
taskKindExplicit: input.taskKindExplicit,
|
|
513
|
+
});
|
|
503
514
|
record = appendTransition(record, "task-created");
|
|
504
515
|
completed.push("task-created");
|
|
505
516
|
await saveLifecycleRecord(input.repoRoot, record);
|
|
@@ -586,6 +597,10 @@ export async function advanceTaskLifecycle(input) {
|
|
|
586
597
|
continue;
|
|
587
598
|
}
|
|
588
599
|
if (action.type === "prepare-contract") {
|
|
600
|
+
const startupStatus = snapshot.source?.sourceFidelityStartup?.status;
|
|
601
|
+
const startupRepairAction = startupStatus === "missing" ||
|
|
602
|
+
startupStatus === "invalid" ||
|
|
603
|
+
startupStatus === "coverage-incomplete";
|
|
589
604
|
// 生产 source fidelity Pi 接线(AC-PROD-001/005):仅 frontend-implementation
|
|
590
605
|
// 任务注入 reconcile executor + 独立 reviewer;ineligible 返回 undefined 不接线。
|
|
591
606
|
// 主路径与 semantic-intake retry 路径共用同一接线,保证任意一条生产路径都实际
|
|
@@ -731,7 +746,9 @@ export async function advanceTaskLifecycle(input) {
|
|
|
731
746
|
message: "structured requirement draft via semantic intake; projected managed source",
|
|
732
747
|
});
|
|
733
748
|
preparedContractThisCall = true;
|
|
734
|
-
if (
|
|
749
|
+
if (startupRepairAction ||
|
|
750
|
+
refreshManagedContractThisCall ||
|
|
751
|
+
input.fromDraftPath) {
|
|
735
752
|
refreshManagedContractThisCall = false;
|
|
736
753
|
regenerateDagThisCall = true;
|
|
737
754
|
}
|
|
@@ -825,7 +842,9 @@ export async function advanceTaskLifecycle(input) {
|
|
|
825
842
|
break;
|
|
826
843
|
}
|
|
827
844
|
preparedContractThisCall = true;
|
|
828
|
-
if (
|
|
845
|
+
if (startupRepairAction ||
|
|
846
|
+
refreshManagedContractThisCall ||
|
|
847
|
+
input.fromDraftPath) {
|
|
829
848
|
// The old DAG and gate were bound to the pre-mutation contract.
|
|
830
849
|
refreshManagedContractThisCall = false;
|
|
831
850
|
regenerateDagThisCall = true;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { access, readFile } from "node:fs/promises";
|
|
1
|
+
import { access, readFile, readdir } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { getTaskDir, getTaskPaths, getTaskStatus, loadTaskConfig, } from "../../task/runtime.js";
|
|
4
4
|
import { logicalRevisionForState, observeTaskContractState, } from "../../task/contract/index.js";
|
|
5
5
|
import { assessDagRunLiveness, listAllDagRunEntries, readDagRunSpec, readDagRunState, } from "../../workflows/dag/lifecycle.js";
|
|
6
6
|
import { resolveRunningControllerIdentity } from "../../shared/package-metadata.js";
|
|
7
|
+
import { parseLedgerJson, validateRequirementLedger, } from "../../task/source-prepare/ledger.js";
|
|
8
|
+
import { REQUIREMENT_LEDGER_FILE_NAME } from "../../task/source-prepare/types.js";
|
|
7
9
|
import { loadLifecycleRecord } from "./record.js";
|
|
8
10
|
import { buildGateBinding, buildWriteSetGate, buildWriteSetGateDelta, controllerIdentityAnchor, hashCanonicalPayload, normalizeWriteSet, sha256Hex, } from "./gates.js";
|
|
9
11
|
async function exists(filePath) {
|
|
@@ -47,6 +49,140 @@ async function taskExists(repoRoot, taskId) {
|
|
|
47
49
|
const paths = getTaskPaths(repoRoot, taskId);
|
|
48
50
|
return exists(paths.taskConfigPath);
|
|
49
51
|
}
|
|
52
|
+
async function assessReferenceEvidence(referencesDir) {
|
|
53
|
+
try {
|
|
54
|
+
const entries = await readdir(referencesDir);
|
|
55
|
+
const importedPrdCount = entries.filter((name) => name.endsWith(".md")).length;
|
|
56
|
+
return importedPrdCount > 0
|
|
57
|
+
? { status: "present", importedPrdCount }
|
|
58
|
+
: { status: "absent-or-empty", importedPrdCount: 0 };
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
if (error &&
|
|
62
|
+
typeof error === "object" &&
|
|
63
|
+
"code" in error &&
|
|
64
|
+
error.code === "ENOENT") {
|
|
65
|
+
return { status: "absent-or-empty", importedPrdCount: 0 };
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
status: "unknown",
|
|
69
|
+
importedPrdCount: 0,
|
|
70
|
+
error: error instanceof Error ? error.message : String(error),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function hasLedgerStructure(value) {
|
|
75
|
+
if (!value || typeof value !== "object")
|
|
76
|
+
return false;
|
|
77
|
+
const ledger = value;
|
|
78
|
+
return (typeof ledger.taskId === "string" &&
|
|
79
|
+
typeof ledger.inputDigest === "string" &&
|
|
80
|
+
typeof ledger.requirementSha256 === "string" &&
|
|
81
|
+
Array.isArray(ledger.sourcePaths) &&
|
|
82
|
+
Array.isArray(ledger.fragments) &&
|
|
83
|
+
Array.isArray(ledger.canonicalRequirements) &&
|
|
84
|
+
Boolean(ledger.stats && typeof ledger.stats === "object") &&
|
|
85
|
+
Array.isArray(ledger.unresolved) &&
|
|
86
|
+
Array.isArray(ledger.conflicts) &&
|
|
87
|
+
Array.isArray(ledger.sourceResolutions) &&
|
|
88
|
+
typeof ledger.schemaReconcilerVersion === "string" &&
|
|
89
|
+
typeof ledger.builtAt === "string");
|
|
90
|
+
}
|
|
91
|
+
async function assessSourceFidelityStartup(input) {
|
|
92
|
+
if (!input.applicable) {
|
|
93
|
+
return {
|
|
94
|
+
status: "not-applicable",
|
|
95
|
+
ledgerPath: input.ledgerPath,
|
|
96
|
+
diagnostics: [],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (input.referenceEvidenceError) {
|
|
100
|
+
return {
|
|
101
|
+
status: "invalid",
|
|
102
|
+
ledgerPath: input.ledgerPath,
|
|
103
|
+
diagnostics: [
|
|
104
|
+
{
|
|
105
|
+
code: "SOURCE_FIDELITY_LEDGER_STALE",
|
|
106
|
+
message: `imported PRD reference evidence is unreadable: ${input.referenceEvidenceError}`,
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
let raw;
|
|
112
|
+
try {
|
|
113
|
+
raw = await readFile(input.ledgerPath, "utf-8");
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
const missing = !(await exists(input.ledgerPath));
|
|
117
|
+
return {
|
|
118
|
+
status: missing ? "missing" : "invalid",
|
|
119
|
+
ledgerPath: input.ledgerPath,
|
|
120
|
+
diagnostics: [
|
|
121
|
+
{
|
|
122
|
+
code: missing
|
|
123
|
+
? "SOURCE_FIDELITY_LEDGER_MISSING"
|
|
124
|
+
: "SOURCE_FIDELITY_LEDGER_STALE",
|
|
125
|
+
message: missing
|
|
126
|
+
? "managed frontend task has no requirement-ledger.json"
|
|
127
|
+
: `requirement ledger is unreadable: ${error instanceof Error ? error.message : String(error)}`,
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
const ledger = parseLedgerJson(raw);
|
|
134
|
+
if (!hasLedgerStructure(ledger)) {
|
|
135
|
+
return {
|
|
136
|
+
status: "invalid",
|
|
137
|
+
ledgerPath: input.ledgerPath,
|
|
138
|
+
diagnostics: [
|
|
139
|
+
{
|
|
140
|
+
code: "SOURCE_FIDELITY_LEDGER_STALE",
|
|
141
|
+
message: "requirement ledger is missing required structural fields",
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
const nonDecisionErrors = validateRequirementLedger(ledger, {
|
|
147
|
+
allowedUnresolved: true,
|
|
148
|
+
allowedConflicts: true,
|
|
149
|
+
});
|
|
150
|
+
if (nonDecisionErrors.length > 0) {
|
|
151
|
+
const coverageCodes = new Set([
|
|
152
|
+
"SOURCE_FIDELITY_HIGH_RISK_UNCOVERED",
|
|
153
|
+
"SOURCE_FIDELITY_REQUIREMENT_NOT_COVERED",
|
|
154
|
+
]);
|
|
155
|
+
return {
|
|
156
|
+
status: nonDecisionErrors.every((error) => coverageCodes.has(error.code))
|
|
157
|
+
? "coverage-incomplete"
|
|
158
|
+
: "invalid",
|
|
159
|
+
ledgerPath: input.ledgerPath,
|
|
160
|
+
diagnostics: nonDecisionErrors,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
const decisionErrors = validateRequirementLedger(ledger);
|
|
164
|
+
if (decisionErrors.length > 0) {
|
|
165
|
+
return {
|
|
166
|
+
status: "human-decision-blocked",
|
|
167
|
+
ledgerPath: input.ledgerPath,
|
|
168
|
+
diagnostics: decisionErrors,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
return { status: "complete", ledgerPath: input.ledgerPath, diagnostics: [] };
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
return {
|
|
175
|
+
status: "invalid",
|
|
176
|
+
ledgerPath: input.ledgerPath,
|
|
177
|
+
diagnostics: [
|
|
178
|
+
{
|
|
179
|
+
code: "SOURCE_FIDELITY_LEDGER_STALE",
|
|
180
|
+
message: error instanceof Error ? error.message : String(error),
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
50
186
|
/**
|
|
51
187
|
* Explicit Task↔DAG association only:
|
|
52
188
|
* - lifecycle.json recorded runAssociations
|
|
@@ -182,10 +318,12 @@ export async function observeTaskLifecycle(repoRoot, taskId) {
|
|
|
182
318
|
const record = await loadLifecycleRecord(repoRoot, taskId);
|
|
183
319
|
const contractState = await observeTaskContractState(repoRoot, taskId);
|
|
184
320
|
let title;
|
|
321
|
+
let taskKind;
|
|
185
322
|
let taskConfigAllowed = [];
|
|
186
323
|
try {
|
|
187
324
|
const config = await loadTaskConfig(repoRoot, taskId);
|
|
188
325
|
title = config.title;
|
|
326
|
+
taskKind = config.taskKind;
|
|
189
327
|
taskConfigAllowed = config.allowedPaths ?? [];
|
|
190
328
|
}
|
|
191
329
|
catch {
|
|
@@ -199,19 +337,22 @@ export async function observeTaskLifecycle(repoRoot, taskId) {
|
|
|
199
337
|
...(requirementReady ? [] : [requirementPath]),
|
|
200
338
|
...(constraintsReady ? [] : [constraintsPath]),
|
|
201
339
|
];
|
|
202
|
-
|
|
340
|
+
const contractManaged = contractState.effectiveStatus === "managed" || Boolean(contractState.ref);
|
|
203
341
|
const referencesDir = path.join(paths.sourceDir, "references");
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
342
|
+
const referenceEvidence = await assessReferenceEvidence(referencesDir);
|
|
343
|
+
const importedPrdCount = referenceEvidence.importedPrdCount;
|
|
344
|
+
const ledgerPath = path.join(paths.sourceDir, REQUIREMENT_LEDGER_FILE_NAME);
|
|
345
|
+
const sourceFidelityStartup = await assessSourceFidelityStartup({
|
|
346
|
+
ledgerPath,
|
|
347
|
+
applicable: contractManaged &&
|
|
348
|
+
taskKind === "frontend-implementation" &&
|
|
349
|
+
referenceEvidence.status !== "absent-or-empty",
|
|
350
|
+
referenceEvidenceError: referenceEvidence.status === "unknown"
|
|
351
|
+
? referenceEvidence.error
|
|
352
|
+
: undefined,
|
|
353
|
+
});
|
|
354
|
+
const sourceFidelityReady = sourceFidelityStartup.status === "complete" ||
|
|
355
|
+
sourceFidelityStartup.status === "not-applicable";
|
|
215
356
|
const dagDraftPath = paths.dagDraftPath;
|
|
216
357
|
const dagDraftExists = await exists(dagDraftPath);
|
|
217
358
|
let specHash = null;
|
|
@@ -285,7 +426,11 @@ export async function observeTaskLifecycle(repoRoot, taskId) {
|
|
|
285
426
|
// Open gate only when draft is strict-validated and the current binding is
|
|
286
427
|
// not already approved/rejected. Rejected same-digest gates stay closed
|
|
287
428
|
// until contract/source/DAG/writeSet/controller binding changes.
|
|
288
|
-
const gate = gateBound &&
|
|
429
|
+
const gate = gateBound &&
|
|
430
|
+
strictValidated &&
|
|
431
|
+
sourceFidelityReady &&
|
|
432
|
+
!approvedCurrent &&
|
|
433
|
+
!rejectedCurrent
|
|
289
434
|
? buildWriteSetGate({ taskId, bound: gateBound, delta: gateDelta })
|
|
290
435
|
: null;
|
|
291
436
|
const gateOpen = Boolean(gate);
|
|
@@ -310,11 +455,10 @@ export async function observeTaskLifecycle(repoRoot, taskId) {
|
|
|
310
455
|
activeRun.failureCategory === "needs-attention" ||
|
|
311
456
|
activeRun.failureCategory === "suspected-stall" ||
|
|
312
457
|
activeRun.failureCategory === "monitor-timeout"));
|
|
313
|
-
const contractManaged = contractState.effectiveStatus === "managed" || Boolean(contractState.ref);
|
|
314
458
|
const lifecycleState = deriveLifecycleState({
|
|
315
459
|
exists: true,
|
|
316
460
|
contractManaged,
|
|
317
|
-
sourceReady: requirementReady,
|
|
461
|
+
sourceReady: requirementReady && sourceFidelityReady,
|
|
318
462
|
dagValidated: strictValidated,
|
|
319
463
|
gateOpen: Boolean(gate),
|
|
320
464
|
activeRun,
|
|
@@ -352,6 +496,7 @@ export async function observeTaskLifecycle(repoRoot, taskId) {
|
|
|
352
496
|
constraintsReady,
|
|
353
497
|
importedPrdCount,
|
|
354
498
|
missing,
|
|
499
|
+
sourceFidelityStartup,
|
|
355
500
|
},
|
|
356
501
|
dagDraft: {
|
|
357
502
|
path: dagDraftPath,
|
|
@@ -375,7 +520,16 @@ export async function observeTaskLifecycle(repoRoot, taskId) {
|
|
|
375
520
|
exists: closeoutExists || Boolean(record?.closeout),
|
|
376
521
|
},
|
|
377
522
|
completedTransitions: record?.completedTransitions ?? [],
|
|
378
|
-
blockers:
|
|
523
|
+
blockers: sourceFidelityReady
|
|
524
|
+
? []
|
|
525
|
+
: sourceFidelityStartup.diagnostics.map((diagnostic) => ({
|
|
526
|
+
code: diagnostic.code,
|
|
527
|
+
message: diagnostic.message,
|
|
528
|
+
details: {
|
|
529
|
+
startupStatus: sourceFidelityStartup.status,
|
|
530
|
+
ledgerPath: sourceFidelityStartup.ledgerPath,
|
|
531
|
+
},
|
|
532
|
+
})),
|
|
379
533
|
warnings: [],
|
|
380
534
|
next,
|
|
381
535
|
};
|
|
@@ -46,6 +46,37 @@ export function planTransitions(input) {
|
|
|
46
46
|
actions.push({ type: "recover-contract" });
|
|
47
47
|
expectedTransitions.push("contract-recovered");
|
|
48
48
|
}
|
|
49
|
+
const startupStatus = snapshot.source?.sourceFidelityStartup?.status;
|
|
50
|
+
const startupRepairDetected = startupStatus === "missing" ||
|
|
51
|
+
startupStatus === "invalid" ||
|
|
52
|
+
startupStatus === "coverage-incomplete";
|
|
53
|
+
if (startupRepairDetected && input.startupRepairAttempted) {
|
|
54
|
+
actions.push({ type: "stop", reason: "blocker" });
|
|
55
|
+
return {
|
|
56
|
+
actions,
|
|
57
|
+
gate: null,
|
|
58
|
+
blockers,
|
|
59
|
+
next: {
|
|
60
|
+
kind: "advance",
|
|
61
|
+
command: `loop-agent task advance ${snapshot.taskId} --json`,
|
|
62
|
+
},
|
|
63
|
+
expectedTransitions,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const startupRepairRequired = startupRepairDetected;
|
|
67
|
+
if (startupStatus === "human-decision-blocked") {
|
|
68
|
+
actions.push({ type: "stop", reason: "blocker" });
|
|
69
|
+
return {
|
|
70
|
+
actions,
|
|
71
|
+
gate: null,
|
|
72
|
+
blockers,
|
|
73
|
+
next: {
|
|
74
|
+
kind: "external-action",
|
|
75
|
+
description: "Requirement ledger requires an existing human source decision; preserve sourceResolutions, resolve the blocker, then advance again.",
|
|
76
|
+
},
|
|
77
|
+
expectedTransitions,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
49
80
|
const sourceReady = snapshot.source?.requirementReady === true;
|
|
50
81
|
const contractManaged = snapshot.contract?.effectiveStatus === "managed" ||
|
|
51
82
|
Boolean(snapshot.contract?.revision && snapshot.contract.revision > 0);
|
|
@@ -53,7 +84,9 @@ export function planTransitions(input) {
|
|
|
53
84
|
// Prefer prepare once PRDs are on disk; re-import only when caller still
|
|
54
85
|
// signals hasPrdInput (advance.ts latches this after one import per call).
|
|
55
86
|
const shouldImportPrd = input.hasPrdInput;
|
|
56
|
-
if (input.forcePrepareContract ||
|
|
87
|
+
if (input.forcePrepareContract ||
|
|
88
|
+
input.refreshManagedContract ||
|
|
89
|
+
startupRepairRequired) {
|
|
57
90
|
if (shouldImportPrd) {
|
|
58
91
|
actions.push({ type: "import-prd" });
|
|
59
92
|
expectedTransitions.push("prd-imported");
|
|
@@ -124,7 +157,8 @@ export function planTransitions(input) {
|
|
|
124
157
|
const strictValidated = snapshot.dagDraft?.strictValidated === true;
|
|
125
158
|
const dagRefreshRequired = Boolean(input.forceDagRefresh ||
|
|
126
159
|
input.refreshManagedContract ||
|
|
127
|
-
input.forcePrepareContract
|
|
160
|
+
input.forcePrepareContract ||
|
|
161
|
+
startupRepairRequired);
|
|
128
162
|
if (!dagExists || dagRefreshRequired) {
|
|
129
163
|
actions.push({ type: "generate-dag" });
|
|
130
164
|
expectedTransitions.push("dag-generated");
|
|
@@ -145,10 +179,11 @@ export function planTransitions(input) {
|
|
|
145
179
|
snapshot.lifecycleState === "evidence-closed" ||
|
|
146
180
|
snapshot.lifecycleState === "needs-attention" ||
|
|
147
181
|
snapshot.lifecycleState === "awaiting-decision";
|
|
148
|
-
const
|
|
182
|
+
const approveCurrentGate = input.approveGate && !dagRefreshRequired;
|
|
183
|
+
const approvedForCurrent = postRunState || (approveCurrentGate && gate !== null);
|
|
149
184
|
if (!approvedForCurrent && (strictValidated || !dagExists || dagRefreshRequired)) {
|
|
150
185
|
// Gate opens after generate+validate in the advance loop; plan includes open.
|
|
151
|
-
if (!
|
|
186
|
+
if (!approveCurrentGate) {
|
|
152
187
|
actions.push({ type: "open-write-set-gate" });
|
|
153
188
|
expectedTransitions.push("write-set-gate-opened");
|
|
154
189
|
if (input.dryRun) {
|
|
@@ -177,14 +212,14 @@ export function planTransitions(input) {
|
|
|
177
212
|
(snapshot.lifecycleState === "run-failed" && !failedRecoverable) ||
|
|
178
213
|
snapshot.lifecycleState === "evidence-closed";
|
|
179
214
|
if (!alreadyTerminalRun) {
|
|
180
|
-
if (
|
|
215
|
+
if (approveCurrentGate && !postRunState) {
|
|
181
216
|
expectedTransitions.push("write-set-approved");
|
|
182
217
|
actions.push({ type: "start-or-monitor-run" });
|
|
183
218
|
expectedTransitions.push("dag-run-started", "dag-run-monitored");
|
|
184
219
|
}
|
|
185
220
|
else if (snapshot.lifecycleState === "running" ||
|
|
186
221
|
snapshot.activeRun ||
|
|
187
|
-
(
|
|
222
|
+
(approveCurrentGate && Boolean(snapshot.activeRun))) {
|
|
188
223
|
actions.push({ type: "start-or-monitor-run" });
|
|
189
224
|
expectedTransitions.push("dag-run-monitored");
|
|
190
225
|
}
|
|
@@ -218,7 +253,7 @@ export function planTransitions(input) {
|
|
|
218
253
|
actions,
|
|
219
254
|
gate,
|
|
220
255
|
blockers,
|
|
221
|
-
next: deriveNext(snapshot, gate,
|
|
256
|
+
next: deriveNext(snapshot, gate, approveCurrentGate),
|
|
222
257
|
expectedTransitions,
|
|
223
258
|
};
|
|
224
259
|
}
|
|
@@ -42,6 +42,7 @@ export async function buildTaskLifecycleRecommendations(input) {
|
|
|
42
42
|
const allowedPaths = [];
|
|
43
43
|
const verifyCommands = [];
|
|
44
44
|
// 1) managed task config
|
|
45
|
+
let hasDeclaredManagedVerify = false;
|
|
45
46
|
try {
|
|
46
47
|
const config = await loadTaskConfig(input.repoRoot, input.taskId);
|
|
47
48
|
for (const glob of config.allowedPaths ?? []) {
|
|
@@ -53,7 +54,9 @@ export async function buildTaskLifecycleRecommendations(input) {
|
|
|
53
54
|
source: "managed-task-config",
|
|
54
55
|
});
|
|
55
56
|
}
|
|
56
|
-
|
|
57
|
+
const managedVerify = (config.verifyCommands ?? []).filter((cmd) => Boolean(cmd.command?.trim()));
|
|
58
|
+
hasDeclaredManagedVerify = managedVerify.length > 0;
|
|
59
|
+
for (const cmd of managedVerify) {
|
|
57
60
|
const label = cmd.label?.trim() || "verify";
|
|
58
61
|
const command = cmd.command?.trim();
|
|
59
62
|
if (!command)
|
|
@@ -70,14 +73,22 @@ export async function buildTaskLifecycleRecommendations(input) {
|
|
|
70
73
|
// task may not exist yet
|
|
71
74
|
}
|
|
72
75
|
// 2) package.json scripts (only if not already covered by managed config)
|
|
76
|
+
// The full-suite `test` script is recommended only when the task declares
|
|
77
|
+
// NO managed verifyCommands at all. Once a task configures a targeted
|
|
78
|
+
// verify set (e.g. per-change typecheck + a scoped vitest run), the full
|
|
79
|
+
// `npm run test` must never be auto-added: it validates the whole repo,
|
|
80
|
+
// costs far more than the change under review, and can fail on unrelated
|
|
81
|
+
// pre-existing breakage, turning a targeted verify green into a hard fail.
|
|
73
82
|
const scripts = await readPackageScripts(input.repoRoot);
|
|
74
83
|
const hasVerifyLabel = (label) => verifyCommands.some((entry) => entry.label === label);
|
|
75
84
|
const scriptCandidates = [
|
|
76
85
|
{ label: "typecheck", script: "typecheck" },
|
|
77
|
-
{ label: "test", script: "test" },
|
|
78
86
|
{ label: "check-repo", script: "check:repo" },
|
|
79
87
|
{ label: "check-repo", script: "check-repo" },
|
|
80
88
|
];
|
|
89
|
+
if (!hasDeclaredManagedVerify) {
|
|
90
|
+
scriptCandidates.push({ label: "test", script: "test" });
|
|
91
|
+
}
|
|
81
92
|
for (const candidate of scriptCandidates) {
|
|
82
93
|
const scriptBody = scripts[candidate.script];
|
|
83
94
|
if (!scriptBody)
|
package/dist/build-stamp.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"version": "0.42.0-next.
|
|
4
|
-
"gitSha": "
|
|
5
|
-
"builtAt": "2026-
|
|
3
|
+
"version": "0.42.0-next.10",
|
|
4
|
+
"gitSha": "610477f8b0bb4b8e4972bb540abdf5ad438c236a",
|
|
5
|
+
"builtAt": "2026-09-03T14:48:16.726Z"
|
|
6
6
|
}
|