@telora/daemon 0.20.2 → 0.20.9
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/build-info.json +7 -3
- package/dist/ai-inspection-context.d.ts.map +1 -1
- package/dist/ai-inspection-context.js +5 -3
- package/dist/ai-inspection-context.js.map +1 -1
- package/dist/assembly-engine.d.ts.map +1 -1
- package/dist/assembly-engine.js +4 -2
- package/dist/assembly-engine.js.map +1 -1
- package/dist/audit-hooks.d.ts.map +1 -1
- package/dist/audit-hooks.js +3 -1
- package/dist/audit-hooks.js.map +1 -1
- package/dist/auth-liveness.d.ts.map +1 -1
- package/dist/auth-liveness.js +4 -2
- package/dist/auth-liveness.js.map +1 -1
- package/dist/auto-update.d.ts.map +1 -1
- package/dist/auto-update.js +14 -12
- package/dist/auto-update.js.map +1 -1
- package/dist/backends/codex/codex-backend.d.ts.map +1 -1
- package/dist/backends/codex/codex-backend.js +4 -2
- package/dist/backends/codex/codex-backend.js.map +1 -1
- package/dist/backends/codex/codex-mcp-proxy.d.ts.map +1 -1
- package/dist/backends/codex/codex-mcp-proxy.js +3 -1
- package/dist/backends/codex/codex-mcp-proxy.js.map +1 -1
- package/dist/backends/lacuna/lifecycle.d.ts.map +1 -1
- package/dist/backends/lacuna/lifecycle.js +4 -2
- package/dist/backends/lacuna/lifecycle.js.map +1 -1
- package/dist/backends/resolve.d.ts.map +1 -1
- package/dist/backends/resolve.js +3 -1
- package/dist/backends/resolve.js.map +1 -1
- package/dist/cascade-restatement.d.ts.map +1 -1
- package/dist/cascade-restatement.js +15 -13
- package/dist/cascade-restatement.js.map +1 -1
- package/dist/ci-escalation.d.ts.map +1 -1
- package/dist/ci-escalation.js +4 -2
- package/dist/ci-escalation.js.map +1 -1
- package/dist/ci-sensor-loop.d.ts.map +1 -1
- package/dist/ci-sensor-loop.js +6 -4
- package/dist/ci-sensor-loop.js.map +1 -1
- package/dist/ci-status-git.d.ts +5 -6
- package/dist/ci-status-git.d.ts.map +1 -1
- package/dist/ci-status-git.js +7 -7
- package/dist/ci-status-git.js.map +1 -1
- package/dist/cli/connect.d.ts.map +1 -1
- package/dist/cli/connect.js +9 -2
- package/dist/cli/connect.js.map +1 -1
- package/dist/cli/open.d.ts.map +1 -1
- package/dist/cli/open.js +9 -2
- package/dist/cli/open.js.map +1 -1
- package/dist/cli/session-state.d.ts.map +1 -1
- package/dist/cli/session-state.js +2 -0
- package/dist/cli/session-state.js.map +1 -1
- package/dist/cli/verify-setup.d.ts.map +1 -1
- package/dist/cli/verify-setup.js +9 -4
- package/dist/cli/verify-setup.js.map +1 -1
- package/dist/close-loop-dispatcher.d.ts.map +1 -1
- package/dist/close-loop-dispatcher.js +6 -4
- package/dist/close-loop-dispatcher.js.map +1 -1
- package/dist/completion/advanceability.d.ts.map +1 -1
- package/dist/completion/advanceability.js +3 -1
- package/dist/completion/advanceability.js.map +1 -1
- package/dist/completion/agent-completion.d.ts.map +1 -1
- package/dist/completion/agent-completion.js +11 -9
- package/dist/completion/agent-completion.js.map +1 -1
- package/dist/completion/event-escalations.d.ts.map +1 -1
- package/dist/completion/event-escalations.js +10 -8
- package/dist/completion/event-escalations.js.map +1 -1
- package/dist/completion/event-phases.d.ts +104 -0
- package/dist/completion/event-phases.d.ts.map +1 -0
- package/dist/completion/event-phases.js +572 -0
- package/dist/completion/event-phases.js.map +1 -0
- package/dist/completion/event.d.ts +1 -1
- package/dist/completion/event.d.ts.map +1 -1
- package/dist/completion/event.js +36 -592
- package/dist/completion/event.js.map +1 -1
- package/dist/completion/merge-phase.d.ts.map +1 -1
- package/dist/completion/merge-phase.js +14 -12
- package/dist/completion/merge-phase.js.map +1 -1
- package/dist/completion/read-only-pristine.d.ts.map +1 -1
- package/dist/completion/read-only-pristine.js +6 -4
- package/dist/completion/read-only-pristine.js.map +1 -1
- package/dist/completion/review-exit-phase.d.ts.map +1 -1
- package/dist/completion/review-exit-phase.js +28 -26
- package/dist/completion/review-exit-phase.js.map +1 -1
- package/dist/completion/status-advance-phase.d.ts.map +1 -1
- package/dist/completion/status-advance-phase.js +16 -14
- package/dist/completion/status-advance-phase.js.map +1 -1
- package/dist/completion/team-completion.d.ts.map +1 -1
- package/dist/completion/team-completion.js +16 -14
- package/dist/completion/team-completion.js.map +1 -1
- package/dist/completion/team-lifecycle.d.ts.map +1 -1
- package/dist/completion/team-lifecycle.js +32 -33
- package/dist/completion/team-lifecycle.js.map +1 -1
- package/dist/completion/terminated-work-reconcile.d.ts.map +1 -1
- package/dist/completion/terminated-work-reconcile.js +11 -9
- package/dist/completion/terminated-work-reconcile.js.map +1 -1
- package/dist/condition-evaluators.d.ts.map +1 -1
- package/dist/condition-evaluators.js +3 -1
- package/dist/condition-evaluators.js.map +1 -1
- package/dist/config-reconcile.d.ts.map +1 -1
- package/dist/config-reconcile.js +3 -1
- package/dist/config-reconcile.js.map +1 -1
- package/dist/config.d.ts +4 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +56 -42
- package/dist/config.js.map +1 -1
- package/dist/control-state.d.ts.map +1 -1
- package/dist/control-state.js +26 -24
- package/dist/control-state.js.map +1 -1
- package/dist/crash-recovery.d.ts.map +1 -1
- package/dist/crash-recovery.js +43 -41
- package/dist/crash-recovery.js.map +1 -1
- package/dist/daemon-logs.d.ts.map +1 -1
- package/dist/daemon-logs.js +21 -3
- package/dist/daemon-logs.js.map +1 -1
- package/dist/daemon-process.d.ts +0 -1
- package/dist/daemon-process.d.ts.map +1 -1
- package/dist/daemon-process.js +15 -10
- package/dist/daemon-process.js.map +1 -1
- package/dist/delivery-advance-block.d.ts +4 -4
- package/dist/delivery-advance-block.d.ts.map +1 -1
- package/dist/delivery-advance-block.js +25 -18
- package/dist/delivery-advance-block.js.map +1 -1
- package/dist/delivery-guards.d.ts.map +1 -1
- package/dist/delivery-guards.js +11 -9
- package/dist/delivery-guards.js.map +1 -1
- package/dist/delivery-lifecycle.d.ts.map +1 -1
- package/dist/delivery-lifecycle.js +23 -21
- package/dist/delivery-lifecycle.js.map +1 -1
- package/dist/delivery-merge.d.ts.map +1 -1
- package/dist/delivery-merge.js +10 -8
- package/dist/delivery-merge.js.map +1 -1
- package/dist/delivery-reconciler-apply.d.ts.map +1 -1
- package/dist/delivery-reconciler-apply.js +5 -8
- package/dist/delivery-reconciler-apply.js.map +1 -1
- package/dist/delivery-reconciler-shadow.d.ts.map +1 -1
- package/dist/delivery-reconciler-shadow.js +5 -6
- package/dist/delivery-reconciler-shadow.js.map +1 -1
- package/dist/directive/close-loop-stage.d.ts.map +1 -1
- package/dist/directive/close-loop-stage.js +9 -7
- package/dist/directive/close-loop-stage.js.map +1 -1
- package/dist/directive/directive-assembly.d.ts.map +1 -1
- package/dist/directive/directive-assembly.js +3 -1
- package/dist/directive/directive-assembly.js.map +1 -1
- package/dist/directive/directive-dispatch.d.ts.map +1 -1
- package/dist/directive/directive-dispatch.js +16 -14
- package/dist/directive/directive-dispatch.js.map +1 -1
- package/dist/directive/directive-queue.d.ts +27 -0
- package/dist/directive/directive-queue.d.ts.map +1 -1
- package/dist/directive/directive-queue.js +28 -0
- package/dist/directive/directive-queue.js.map +1 -1
- package/dist/directive/phase-sync.d.ts.map +1 -1
- package/dist/directive/phase-sync.js +8 -6
- package/dist/directive/phase-sync.js.map +1 -1
- package/dist/directive-executor.d.ts +1 -1
- package/dist/directive-executor.d.ts.map +1 -1
- package/dist/directive-executor.js +12 -10
- package/dist/directive-executor.js.map +1 -1
- package/dist/discovery-poll.d.ts.map +1 -1
- package/dist/discovery-poll.js +8 -6
- package/dist/discovery-poll.js.map +1 -1
- package/dist/drain-teardown.d.ts.map +1 -1
- package/dist/drain-teardown.js +6 -19
- package/dist/drain-teardown.js.map +1 -1
- package/dist/drift-eval-loop.d.ts.map +1 -1
- package/dist/drift-eval-loop.js +10 -8
- package/dist/drift-eval-loop.js.map +1 -1
- package/dist/escalation-provenance.d.ts.map +1 -1
- package/dist/escalation-provenance.js +8 -3
- package/dist/escalation-provenance.js.map +1 -1
- package/dist/focus-engine.d.ts.map +1 -1
- package/dist/focus-engine.js +36 -34
- package/dist/focus-engine.js.map +1 -1
- package/dist/focus-executor.d.ts +2 -1
- package/dist/focus-executor.d.ts.map +1 -1
- package/dist/focus-executor.js +17 -15
- package/dist/focus-executor.js.map +1 -1
- package/dist/focus-lifecycle.d.ts.map +1 -1
- package/dist/focus-lifecycle.js +56 -44
- package/dist/focus-lifecycle.js.map +1 -1
- package/dist/focus-loop-registry.d.ts.map +1 -1
- package/dist/focus-loop-registry.js +38 -36
- package/dist/focus-loop-registry.js.map +1 -1
- package/dist/focus-merge.d.ts.map +1 -1
- package/dist/focus-merge.js +40 -34
- package/dist/focus-merge.js.map +1 -1
- package/dist/focus-provisioning.d.ts.map +1 -1
- package/dist/focus-provisioning.js +15 -13
- package/dist/focus-provisioning.js.map +1 -1
- package/dist/focus-respawn-guard.d.ts.map +1 -1
- package/dist/focus-respawn-guard.js +4 -2
- package/dist/focus-respawn-guard.js.map +1 -1
- package/dist/focus-stage-lifecycle/planning-release.d.ts.map +1 -1
- package/dist/focus-stage-lifecycle/planning-release.js +10 -8
- package/dist/focus-stage-lifecycle/planning-release.js.map +1 -1
- package/dist/focus-stage-lifecycle/review-convergence.d.ts.map +1 -1
- package/dist/focus-stage-lifecycle/review-convergence.js +11 -9
- package/dist/focus-stage-lifecycle/review-convergence.js.map +1 -1
- package/dist/focus-stage-lifecycle/wedge-watchdog.d.ts.map +1 -1
- package/dist/focus-stage-lifecycle/wedge-watchdog.js +7 -5
- package/dist/focus-stage-lifecycle/wedge-watchdog.js.map +1 -1
- package/dist/focus-stage-lifecycle.d.ts.map +1 -1
- package/dist/focus-stage-lifecycle.js +7 -5
- package/dist/focus-stage-lifecycle.js.map +1 -1
- package/dist/focus-team-state.d.ts +18 -9
- package/dist/focus-team-state.d.ts.map +1 -1
- package/dist/focus-team-state.js +37 -18
- package/dist/focus-team-state.js.map +1 -1
- package/dist/focus-worktree-state.d.ts.map +1 -1
- package/dist/focus-worktree-state.js +6 -4
- package/dist/focus-worktree-state.js.map +1 -1
- package/dist/git/branch.d.ts.map +1 -1
- package/dist/git/branch.js +7 -5
- package/dist/git/branch.js.map +1 -1
- package/dist/git/genesis.d.ts.map +1 -1
- package/dist/git/genesis.js +3 -1
- package/dist/git/genesis.js.map +1 -1
- package/dist/git/integration.d.ts.map +1 -1
- package/dist/git/integration.js +4 -2
- package/dist/git/integration.js.map +1 -1
- package/dist/git/merge-helpers.d.ts.map +1 -1
- package/dist/git/merge-helpers.js +6 -4
- package/dist/git/merge-helpers.js.map +1 -1
- package/dist/git/merge-lock.d.ts +0 -7
- package/dist/git/merge-lock.d.ts.map +1 -1
- package/dist/git/merge-lock.js +5 -3
- package/dist/git/merge-lock.js.map +1 -1
- package/dist/git/merge.d.ts.map +1 -1
- package/dist/git/merge.js +10 -8
- package/dist/git/merge.js.map +1 -1
- package/dist/git/state-detector.d.ts.map +1 -1
- package/dist/git/state-detector.js +9 -7
- package/dist/git/state-detector.js.map +1 -1
- package/dist/git/utils.d.ts.map +1 -1
- package/dist/git/utils.js +6 -4
- package/dist/git/utils.js.map +1 -1
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +16 -14
- package/dist/git.js.map +1 -1
- package/dist/guard-evaluator.d.ts.map +1 -1
- package/dist/guard-evaluator.js +3 -1
- package/dist/guard-evaluator.js.map +1 -1
- package/dist/heartbeat.d.ts.map +1 -1
- package/dist/heartbeat.js +14 -11
- package/dist/heartbeat.js.map +1 -1
- package/dist/host-control/catalog.d.ts.map +1 -1
- package/dist/host-control/catalog.js +4 -2
- package/dist/host-control/catalog.js.map +1 -1
- package/dist/host-control/client.d.ts.map +1 -1
- package/dist/host-control/client.js +3 -1
- package/dist/host-control/client.js.map +1 -1
- package/dist/host-control/sizing.d.ts.map +1 -1
- package/dist/host-control/sizing.js +4 -1
- package/dist/host-control/sizing.js.map +1 -1
- package/dist/index.js +110 -127
- package/dist/index.js.map +1 -1
- package/dist/install-probe-tick.d.ts.map +1 -1
- package/dist/install-probe-tick.js +6 -4
- package/dist/install-probe-tick.js.map +1 -1
- package/dist/intake-control.d.ts +0 -16
- package/dist/intake-control.d.ts.map +1 -1
- package/dist/intake-control.js +4 -2
- package/dist/intake-control.js.map +1 -1
- package/dist/listener-auto-advance.d.ts.map +1 -1
- package/dist/listener-auto-advance.js +26 -24
- package/dist/listener-auto-advance.js.map +1 -1
- package/dist/listener.d.ts.map +1 -1
- package/dist/listener.js +59 -39
- package/dist/listener.js.map +1 -1
- package/dist/log.d.ts +84 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +182 -0
- package/dist/log.js.map +1 -0
- package/dist/loops/context-audit-pass.d.ts.map +1 -1
- package/dist/loops/context-audit-pass.js +8 -5
- package/dist/loops/context-audit-pass.js.map +1 -1
- package/dist/loops/engine.d.ts.map +1 -1
- package/dist/loops/engine.js +26 -24
- package/dist/loops/engine.js.map +1 -1
- package/dist/loops/llm-client.d.ts.map +1 -1
- package/dist/loops/llm-client.js +4 -2
- package/dist/loops/llm-client.js.map +1 -1
- package/dist/loops/llm-flag.d.ts +4 -28
- package/dist/loops/llm-flag.d.ts.map +1 -1
- package/dist/loops/llm-flag.js +11 -30
- package/dist/loops/llm-flag.js.map +1 -1
- package/dist/loops/prompt-builder.d.ts.map +1 -1
- package/dist/loops/prompt-builder.js +3 -1
- package/dist/loops/prompt-builder.js.map +1 -1
- package/dist/merge-back-loop.d.ts.map +1 -1
- package/dist/merge-back-loop.js +20 -18
- package/dist/merge-back-loop.js.map +1 -1
- package/dist/merge-detector.d.ts.map +1 -1
- package/dist/merge-detector.js +7 -5
- package/dist/merge-detector.js.map +1 -1
- package/dist/nexus/endpoint-clearance.d.ts.map +1 -1
- package/dist/nexus/endpoint-clearance.js +3 -1
- package/dist/nexus/endpoint-clearance.js.map +1 -1
- package/dist/nexus/escalation-bridge.d.ts.map +1 -1
- package/dist/nexus/escalation-bridge.js +3 -1
- package/dist/nexus/escalation-bridge.js.map +1 -1
- package/dist/nexus/incoming.d.ts.map +1 -1
- package/dist/nexus/incoming.js +3 -1
- package/dist/nexus/incoming.js.map +1 -1
- package/dist/nexus/intake-spawn.d.ts.map +1 -1
- package/dist/nexus/intake-spawn.js +6 -4
- package/dist/nexus/intake-spawn.js.map +1 -1
- package/dist/nexus/intake.d.ts.map +1 -1
- package/dist/nexus/intake.js +3 -1
- package/dist/nexus/intake.js.map +1 -1
- package/dist/nexus/return.d.ts.map +1 -1
- package/dist/nexus/return.js +3 -1
- package/dist/nexus/return.js.map +1 -1
- package/dist/non-interactive-init.d.ts.map +1 -1
- package/dist/non-interactive-init.js +0 -0
- package/dist/non-interactive-init.js.map +1 -1
- package/dist/otlp/port-manager.d.ts.map +1 -1
- package/dist/otlp/port-manager.js +7 -5
- package/dist/otlp/port-manager.js.map +1 -1
- package/dist/otlp/receiver.d.ts.map +1 -1
- package/dist/otlp/receiver.js +14 -12
- package/dist/otlp/receiver.js.map +1 -1
- package/dist/output-monitor.d.ts.map +1 -1
- package/dist/output-monitor.js +6 -4
- package/dist/output-monitor.js.map +1 -1
- package/dist/prd/controller.d.ts.map +1 -1
- package/dist/prd/controller.js +7 -5
- package/dist/prd/controller.js.map +1 -1
- package/dist/prd/focus-pushdown.d.ts.map +1 -1
- package/dist/prd/focus-pushdown.js +3 -1
- package/dist/prd/focus-pushdown.js.map +1 -1
- package/dist/product-scaffold.d.ts.map +1 -1
- package/dist/product-scaffold.js +8 -6
- package/dist/product-scaffold.js.map +1 -1
- package/dist/queries/context-assembly.d.ts.map +1 -1
- package/dist/queries/context-assembly.js +3 -1
- package/dist/queries/context-assembly.js.map +1 -1
- package/dist/queries/deliveries.d.ts.map +1 -1
- package/dist/queries/deliveries.js +7 -5
- package/dist/queries/deliveries.js.map +1 -1
- package/dist/queries/focuses.d.ts.map +1 -1
- package/dist/queries/focuses.js +3 -1
- package/dist/queries/focuses.js.map +1 -1
- package/dist/queries/guards.d.ts.map +1 -1
- package/dist/queries/guards.js +3 -1
- package/dist/queries/guards.js.map +1 -1
- package/dist/queries/issues.d.ts.map +1 -1
- package/dist/queries/issues.js +3 -1
- package/dist/queries/issues.js.map +1 -1
- package/dist/queries/system-session.d.ts.map +1 -1
- package/dist/queries/system-session.js +5 -3
- package/dist/queries/system-session.js.map +1 -1
- package/dist/queries/verification.d.ts.map +1 -1
- package/dist/queries/verification.js +6 -4
- package/dist/queries/verification.js.map +1 -1
- package/dist/reality-pole-capture.d.ts.map +1 -1
- package/dist/reality-pole-capture.js +7 -4
- package/dist/reality-pole-capture.js.map +1 -1
- package/dist/reality-pole.d.ts.map +1 -1
- package/dist/reality-pole.js +4 -2
- package/dist/reality-pole.js.map +1 -1
- package/dist/resolvers/agent-escalations.d.ts.map +1 -1
- package/dist/resolvers/agent-escalations.js +3 -1
- package/dist/resolvers/agent-escalations.js.map +1 -1
- package/dist/resolvers/agent-session-summaries.d.ts.map +1 -1
- package/dist/resolvers/agent-session-summaries.js +3 -1
- package/dist/resolvers/agent-session-summaries.js.map +1 -1
- package/dist/resolvers/deployment-profile.d.ts.map +1 -1
- package/dist/resolvers/deployment-profile.js +3 -1
- package/dist/resolvers/deployment-profile.js.map +1 -1
- package/dist/resolvers/escalation-answers.d.ts.map +1 -1
- package/dist/resolvers/escalation-answers.js +4 -2
- package/dist/resolvers/escalation-answers.js.map +1 -1
- package/dist/resolvers/focus-anchoring-injections.d.ts.map +1 -1
- package/dist/resolvers/focus-anchoring-injections.js +3 -1
- package/dist/resolvers/focus-anchoring-injections.js.map +1 -1
- package/dist/resolvers/focus-injections.d.ts.map +1 -1
- package/dist/resolvers/focus-injections.js +3 -1
- package/dist/resolvers/focus-injections.js.map +1 -1
- package/dist/resolvers/focus-last-review-report.d.ts.map +1 -1
- package/dist/resolvers/focus-last-review-report.js +3 -1
- package/dist/resolvers/focus-last-review-report.js.map +1 -1
- package/dist/resolvers/focus-reality-tree.d.ts.map +1 -1
- package/dist/resolvers/focus-reality-tree.js +3 -1
- package/dist/resolvers/focus-reality-tree.js.map +1 -1
- package/dist/resolvers/git-diff-against-base.d.ts.map +1 -1
- package/dist/resolvers/git-diff-against-base.js +3 -1
- package/dist/resolvers/git-diff-against-base.js.map +1 -1
- package/dist/resolvers/guards-evaluation-results.d.ts.map +1 -1
- package/dist/resolvers/guards-evaluation-results.js +3 -1
- package/dist/resolvers/guards-evaluation-results.js.map +1 -1
- package/dist/resolvers/loop-context.d.ts.map +1 -1
- package/dist/resolvers/loop-context.js +6 -2
- package/dist/resolvers/loop-context.js.map +1 -1
- package/dist/resolvers/loop-delivery-index.d.ts.map +1 -1
- package/dist/resolvers/loop-delivery-index.js +3 -1
- package/dist/resolvers/loop-delivery-index.js.map +1 -1
- package/dist/resolvers/loop-documents.d.ts.map +1 -1
- package/dist/resolvers/loop-documents.js +3 -1
- package/dist/resolvers/loop-documents.js.map +1 -1
- package/dist/resolvers/loop-expected-effects.d.ts.map +1 -1
- package/dist/resolvers/loop-expected-effects.js +3 -1
- package/dist/resolvers/loop-expected-effects.js.map +1 -1
- package/dist/resolvers/loop-frt-statement.d.ts.map +1 -1
- package/dist/resolvers/loop-frt-statement.js +3 -1
- package/dist/resolvers/loop-frt-statement.js.map +1 -1
- package/dist/resolvers/loop-injection.d.ts.map +1 -1
- package/dist/resolvers/loop-injection.js +3 -1
- package/dist/resolvers/loop-injection.js.map +1 -1
- package/dist/resolvers/loop-persona.d.ts.map +1 -1
- package/dist/resolvers/loop-persona.js +3 -1
- package/dist/resolvers/loop-persona.js.map +1 -1
- package/dist/resolvers/loop-questions.d.ts.map +1 -1
- package/dist/resolvers/loop-questions.js +3 -1
- package/dist/resolvers/loop-questions.js.map +1 -1
- package/dist/resolvers/loop-upstream-udes.d.ts.map +1 -1
- package/dist/resolvers/loop-upstream-udes.js +3 -1
- package/dist/resolvers/loop-upstream-udes.js.map +1 -1
- package/dist/resolvers/prd-context.d.ts.map +1 -1
- package/dist/resolvers/prd-context.js +3 -1
- package/dist/resolvers/prd-context.js.map +1 -1
- package/dist/resolvers/reality-metrics.d.ts.map +1 -1
- package/dist/resolvers/reality-metrics.js +3 -1
- package/dist/resolvers/reality-metrics.js.map +1 -1
- package/dist/resolvers/reality-projections.d.ts.map +1 -1
- package/dist/resolvers/reality-projections.js +3 -1
- package/dist/resolvers/reality-projections.js.map +1 -1
- package/dist/resolvers/reality-tree-snapshot.d.ts.map +1 -1
- package/dist/resolvers/reality-tree-snapshot.js +3 -1
- package/dist/resolvers/reality-tree-snapshot.js.map +1 -1
- package/dist/resolvers/retired-injections.d.ts.map +1 -1
- package/dist/resolvers/retired-injections.js +3 -1
- package/dist/resolvers/retired-injections.js.map +1 -1
- package/dist/resolvers/review-outcomes.d.ts.map +1 -1
- package/dist/resolvers/review-outcomes.js +3 -1
- package/dist/resolvers/review-outcomes.js.map +1 -1
- package/dist/resolvers/security-advisory.d.ts.map +1 -1
- package/dist/resolvers/security-advisory.js +3 -1
- package/dist/resolvers/security-advisory.js.map +1 -1
- package/dist/resolvers/shared/wiki.d.ts.map +1 -1
- package/dist/resolvers/shared/wiki.js +5 -3
- package/dist/resolvers/shared/wiki.js.map +1 -1
- package/dist/resolvers/wiki-topic.d.ts.map +1 -1
- package/dist/resolvers/wiki-topic.js +3 -1
- package/dist/resolvers/wiki-topic.js.map +1 -1
- package/dist/resolvers/workflow-stages.d.ts.map +1 -1
- package/dist/resolvers/workflow-stages.js +3 -1
- package/dist/resolvers/workflow-stages.js.map +1 -1
- package/dist/schema-version-gate.d.ts.map +1 -1
- package/dist/schema-version-gate.js +16 -18
- package/dist/schema-version-gate.js.map +1 -1
- package/dist/security-finding-gate.d.ts.map +1 -1
- package/dist/security-finding-gate.js +3 -1
- package/dist/security-finding-gate.js.map +1 -1
- package/dist/security-rescan-resolution.d.ts.map +1 -1
- package/dist/security-rescan-resolution.js +4 -2
- package/dist/security-rescan-resolution.js.map +1 -1
- package/dist/security-scan-engine.d.ts.map +1 -1
- package/dist/security-scan-engine.js +9 -6
- package/dist/security-scan-engine.js.map +1 -1
- package/dist/self-update.d.ts.map +1 -1
- package/dist/self-update.js +48 -39
- package/dist/self-update.js.map +1 -1
- package/dist/sensor-sweep.d.ts.map +1 -1
- package/dist/sensor-sweep.js +5 -2
- package/dist/sensor-sweep.js.map +1 -1
- package/dist/session-stamp.d.ts.map +1 -1
- package/dist/session-stamp.js +4 -2
- package/dist/session-stamp.js.map +1 -1
- package/dist/spawn/model-routing.d.ts +18 -0
- package/dist/spawn/model-routing.d.ts.map +1 -1
- package/dist/spawn/model-routing.js +53 -18
- package/dist/spawn/model-routing.js.map +1 -1
- package/dist/spawn/process-spawn.d.ts.map +1 -1
- package/dist/spawn/process-spawn.js +29 -22
- package/dist/spawn/process-spawn.js.map +1 -1
- package/dist/spawn/prompt-assembly.d.ts.map +1 -1
- package/dist/spawn/prompt-assembly.js +6 -4
- package/dist/spawn/prompt-assembly.js.map +1 -1
- package/dist/spawn/spawn-decision.d.ts.map +1 -1
- package/dist/spawn/spawn-decision.js +16 -14
- package/dist/spawn/spawn-decision.js.map +1 -1
- package/dist/spawn/worktree-session.d.ts.map +1 -1
- package/dist/spawn/worktree-session.js +13 -11
- package/dist/spawn/worktree-session.js.map +1 -1
- package/dist/spawn-cooldown.d.ts +0 -7
- package/dist/spawn-cooldown.d.ts.map +1 -1
- package/dist/spawn-cooldown.js +3 -1
- package/dist/spawn-cooldown.js.map +1 -1
- package/dist/spawn-environment.d.ts.map +1 -1
- package/dist/spawn-environment.js +4 -1
- package/dist/spawn-environment.js.map +1 -1
- package/dist/spawner/helpers.d.ts.map +1 -1
- package/dist/spawner/helpers.js +19 -17
- package/dist/spawner/helpers.js.map +1 -1
- package/dist/spawner/lifecycle.d.ts.map +1 -1
- package/dist/spawner/lifecycle.js +17 -13
- package/dist/spawner/lifecycle.js.map +1 -1
- package/dist/spawner/liveness.d.ts.map +1 -1
- package/dist/spawner/liveness.js +7 -5
- package/dist/spawner/liveness.js.map +1 -1
- package/dist/spawner/resolution.d.ts.map +1 -1
- package/dist/spawner/resolution.js +7 -5
- package/dist/spawner/resolution.js.map +1 -1
- package/dist/spawner/stream-handlers.d.ts +6 -55
- package/dist/spawner/stream-handlers.d.ts.map +1 -1
- package/dist/spawner/stream-handlers.js +10 -172
- package/dist/spawner/stream-handlers.js.map +1 -1
- package/dist/spawner/timeout.d.ts.map +1 -1
- package/dist/spawner/timeout.js +11 -9
- package/dist/spawner/timeout.js.map +1 -1
- package/dist/staleness.d.ts.map +1 -1
- package/dist/staleness.js +4 -1
- package/dist/staleness.js.map +1 -1
- package/dist/stall-detector-config.d.ts +6 -17
- package/dist/stall-detector-config.d.ts.map +1 -1
- package/dist/stall-detector-config.js +10 -36
- package/dist/stall-detector-config.js.map +1 -1
- package/dist/stall-detectors.d.ts.map +1 -1
- package/dist/stall-detectors.js +6 -4
- package/dist/stall-detectors.js.map +1 -1
- package/dist/state-cascade.d.ts.map +1 -1
- package/dist/state-cascade.js +9 -7
- package/dist/state-cascade.js.map +1 -1
- package/dist/stop-request.d.ts.map +1 -1
- package/dist/stop-request.js +6 -2
- package/dist/stop-request.js.map +1 -1
- package/dist/supabase.d.ts.map +1 -1
- package/dist/supabase.js +3 -1
- package/dist/supabase.js.map +1 -1
- package/dist/telemetry-writer.d.ts.map +1 -1
- package/dist/telemetry-writer.js +11 -9
- package/dist/telemetry-writer.js.map +1 -1
- package/dist/trigger-executor.d.ts.map +1 -1
- package/dist/trigger-executor.js +28 -26
- package/dist/trigger-executor.js.map +1 -1
- package/dist/unified-engine-lifecycle.d.ts +19 -11
- package/dist/unified-engine-lifecycle.d.ts.map +1 -1
- package/dist/unified-engine-lifecycle.js +71 -70
- package/dist/unified-engine-lifecycle.js.map +1 -1
- package/dist/unified-init.d.ts.map +1 -1
- package/dist/unified-init.js +41 -36
- package/dist/unified-init.js.map +1 -1
- package/dist/unified-shell-config.d.ts +8 -1
- package/dist/unified-shell-config.d.ts.map +1 -1
- package/dist/unified-shell-config.js +57 -34
- package/dist/unified-shell-config.js.map +1 -1
- package/dist/unified-shell-status.d.ts.map +1 -1
- package/dist/unified-shell-status.js +39 -37
- package/dist/unified-shell-status.js.map +1 -1
- package/dist/unified-shell.d.ts.map +1 -1
- package/dist/unified-shell.js +37 -41
- package/dist/unified-shell.js.map +1 -1
- package/dist/verification-baseline.d.ts.map +1 -1
- package/dist/verification-baseline.js +7 -4
- package/dist/verification-baseline.js.map +1 -1
- package/dist/verification-config.d.ts +3 -3
- package/dist/verification-config.d.ts.map +1 -1
- package/dist/verification-config.js +8 -19
- package/dist/verification-config.js.map +1 -1
- package/dist/verification-default-on.d.ts +6 -6
- package/dist/verification-default-on.d.ts.map +1 -1
- package/dist/verification-default-on.js +9 -7
- package/dist/verification-default-on.js.map +1 -1
- package/dist/verification-deterministic.d.ts.map +1 -1
- package/dist/verification-deterministic.js +6 -3
- package/dist/verification-deterministic.js.map +1 -1
- package/dist/verification-engine.d.ts.map +1 -1
- package/dist/verification-engine.js +20 -16
- package/dist/verification-engine.js.map +1 -1
- package/dist/verification-feedback.d.ts.map +1 -1
- package/dist/verification-feedback.js +4 -2
- package/dist/verification-feedback.js.map +1 -1
- package/dist/version-check.d.ts.map +1 -1
- package/dist/version-check.js +5 -3
- package/dist/version-check.js.map +1 -1
- package/dist/worker-pidfile.d.ts.map +1 -1
- package/dist/worker-pidfile.js +8 -3
- package/dist/worker-pidfile.js.map +1 -1
- package/dist/worktree/focus.d.ts.map +1 -1
- package/dist/worktree/focus.js +15 -13
- package/dist/worktree/focus.js.map +1 -1
- package/dist/worktree/safety.d.ts.map +1 -1
- package/dist/worktree/safety.js +13 -11
- package/dist/worktree/safety.js.map +1 -1
- package/dist/worktree.d.ts.map +1 -1
- package/dist/worktree.js +41 -39
- package/dist/worktree.js.map +1 -1
- package/package.json +3 -3
package/dist/completion/event.js
CHANGED
|
@@ -5,18 +5,15 @@
|
|
|
5
5
|
* team lead completes a work cycle: terminate if done, send more work, or
|
|
6
6
|
* (for planning teams) refresh from MCP-written deliveries / escalate.
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
9
|
-
import { isStatusCascadable
|
|
8
|
+
import { loadEnvConfig } from '@telora/daemon-core';
|
|
9
|
+
import { isStatusCascadable } from '../stage-classifier.js';
|
|
10
10
|
import { getFocusDeliveries, getFocusIssues } from '../queries/focuses.js';
|
|
11
11
|
import { getActiveFocuses } from '../supabase.js';
|
|
12
|
-
import { buildWakeMessage, buildReEngagementMessage } from '../focus-prompt-builder.js';
|
|
13
|
-
import { OPEN_ISSUE_STATUSES } from '../constants.js';
|
|
14
|
-
import { isAuditPhaseFocus } from '../audit-shape.js';
|
|
15
12
|
import { terminateTeam as defaultTerminateTeam } from './team-lifecycle.js';
|
|
16
13
|
import { createEscalation as defaultCreateEscalation, hasOpenTeamFailureEscalation as defaultHasOpenTeamFailureEscalation, } from '../queries/issues.js';
|
|
17
14
|
import { advanceDeliveryStatuses as defaultAdvanceDeliveryStatuses, runReviewExitHandler as defaultRunReviewExitHandler, } from './team-completion.js';
|
|
18
15
|
import { fetchEffectiveWorkflowWithTransitions as defaultFetchEffectiveWorkflowWithTransitions } from '../queries/workflows.js';
|
|
19
|
-
import { decideCompletionAction
|
|
16
|
+
import { decideCompletionAction } from './completion-decision.js';
|
|
20
17
|
import { takeProgressSnapshot, snapshotsEqual } from './progress-snapshot.js';
|
|
21
18
|
// Review-window + non-advanceability predicates extracted to ./advanceability.ts
|
|
22
19
|
// (keeps this file under the structural size lint). Imported for use below and
|
|
@@ -27,29 +24,21 @@ export { isInReviewWindow, isDeliveryNonAdvanceable, findNonAdvanceableDelivery
|
|
|
27
24
|
import { getFocusWorktree as defaultGetFocusWorktree } from '../focus-worktree-state.js';
|
|
28
25
|
import { worktreeHasUncommittedChanges as defaultWorktreeHasUncommittedChanges } from '../worktree/index.js';
|
|
29
26
|
import { readWorktreeHeadSha as defaultReadWorktreeHeadSha } from '../git/index.js';
|
|
30
|
-
import { resolveAutoRemediationBudget } from '../delivery-advance-block.js';
|
|
31
27
|
// Escalation builders + their reason-string constants live in
|
|
32
28
|
// event-escalations.ts; re-exported here so existing importers are unaffected.
|
|
33
|
-
import { escalatePlanningFailure, escalateNoProgress, escalateNonAdvanceable, escalateDeliveryAdvancementStalled, } from './event-escalations.js';
|
|
34
29
|
export { PLANNING_NO_DELIVERIES_REASON, NO_PROGRESS_ESCALATION_REASON, NON_ADVANCEABLE_ESCALATION_REASON, NON_ADVANCEABLE_CAUSE, DELIVERY_ADVANCEMENT_STALLED_REASON, DELIVERY_ADVANCEMENT_STALLED_CAUSE, escalatePlanningFailure, escalateNoProgress, escalateNonAdvanceable, escalateDeliveryAdvancementStalled, } from './event-escalations.js';
|
|
35
|
-
import { recordDeliveryAdvancementCycle } from './delivery-advancement-guard.js';
|
|
36
30
|
import { reconcileTerminatedTeamWork as defaultReconcileTerminatedTeamWork, } from './terminated-work-reconcile.js';
|
|
31
|
+
import { applyPlanningRefresh, routeReviewWindowCompletion, runWakeCycleSelfHeal, computeCompletionFacts, checkDeliveryAdvancementStall, executeCompletionAction, } from './event-phases.js';
|
|
32
|
+
import { createLogger } from '../log.js';
|
|
33
|
+
const log = createLogger({ module: 'focus-executor' });
|
|
37
34
|
/**
|
|
38
35
|
* Maximum consecutive re-engagement cycles with no measurable progress before
|
|
39
36
|
* the scope-coverage guard escalates. Configurable via env so operators can
|
|
40
37
|
* relax or tighten the bound without a code change.
|
|
41
38
|
*/
|
|
42
39
|
export const SCOPE_COVERAGE_NO_PROGRESS_LIMIT = loadEnvConfig().TELORA_SCOPE_COVERAGE_NO_PROGRESS_LIMIT;
|
|
43
|
-
export function resolveDeliveryAdvancementNoProgressLimit(envConfig = loadEnvConfig()
|
|
44
|
-
|
|
45
|
-
if (typeof typedConfig.TELORA_DELIVERY_ADVANCEMENT_NO_PROGRESS_LIMIT === 'number') {
|
|
46
|
-
return typedConfig.TELORA_DELIVERY_ADVANCEMENT_NO_PROGRESS_LIMIT;
|
|
47
|
-
}
|
|
48
|
-
const raw = env.TELORA_DELIVERY_ADVANCEMENT_NO_PROGRESS_LIMIT;
|
|
49
|
-
if (raw === undefined || raw === '')
|
|
50
|
-
return 3;
|
|
51
|
-
const parsed = Number(raw);
|
|
52
|
-
return Number.isInteger(parsed) && parsed > 0 ? parsed : 3;
|
|
40
|
+
export function resolveDeliveryAdvancementNoProgressLimit(envConfig = loadEnvConfig()) {
|
|
41
|
+
return envConfig.TELORA_DELIVERY_ADVANCEMENT_NO_PROGRESS_LIMIT;
|
|
53
42
|
}
|
|
54
43
|
export const DELIVERY_ADVANCEMENT_NO_PROGRESS_LIMIT = resolveDeliveryAdvancementNoProgressLimit();
|
|
55
44
|
async function defaultGetReviewRequestedAt(config, focusId) {
|
|
@@ -93,594 +82,49 @@ const defaultDeps = {
|
|
|
93
82
|
* deep nesting inside an async Promise chain inside an event listener.
|
|
94
83
|
*/
|
|
95
84
|
export async function handleCompletionEvent(ctx, deps = defaultDeps) {
|
|
96
|
-
const { focusId, focusName, teamState
|
|
85
|
+
const { focusId, focusName, teamState } = ctx;
|
|
97
86
|
const [currentDeliveries, currentIssues] = await Promise.all([
|
|
98
87
|
deps.getFocusDeliveries(focusId),
|
|
99
88
|
deps.getFocusIssues(focusId),
|
|
100
89
|
]);
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
// at the set. WHY Option A: keeps the refresh on the existing wake path
|
|
105
|
-
// instead of polling, so it runs exactly when new deliveries become visible.
|
|
106
|
-
if (teamState.planningPhase) {
|
|
107
|
-
if (currentDeliveries.length === 0) {
|
|
108
|
-
console.warn(`[focus-executor] Planning team for "${focusName}" produced no deliveries ` +
|
|
109
|
-
`-- escalating and terminating`);
|
|
110
|
-
await escalatePlanningFailure({
|
|
111
|
-
organizationId: teamState.organizationId,
|
|
112
|
-
sessionId: teamState.leadSessionId ?? '',
|
|
113
|
-
focusId,
|
|
114
|
-
focusName,
|
|
115
|
-
agentRoleId: teamState.roleId,
|
|
116
|
-
}, deps);
|
|
117
|
-
teamState.shutdownReason = 'work_complete';
|
|
118
|
-
deps.terminateTeam(focusId);
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
for (const d of currentDeliveries) {
|
|
122
|
-
teamState.knownDeliveryIds.add(d.id);
|
|
123
|
-
teamState.deliveryStageIds.set(d.id, d.currentWorkflowStageId);
|
|
124
|
-
}
|
|
125
|
-
teamState.planningPhase = false;
|
|
126
|
-
console.log(`[focus-executor] Planning refresh for "${focusName}": ` +
|
|
127
|
-
`${currentDeliveries.length} delivery(ies) discovered`);
|
|
128
|
-
}
|
|
129
|
-
// Review-window handling. Resolve review_requested_at FRESH because it can be
|
|
130
|
-
// set/cleared between spawn and this event.
|
|
131
|
-
let reviewRequestedAt = null;
|
|
132
|
-
if (deps.getReviewRequestedAt) {
|
|
133
|
-
try {
|
|
134
|
-
reviewRequestedAt = await deps.getReviewRequestedAt(ctx.config, focusId);
|
|
135
|
-
}
|
|
136
|
-
catch (err) {
|
|
137
|
-
console.warn(`[focus-executor] getReviewRequestedAt failed for "${focusName}": ${err.message}`);
|
|
138
|
-
// Fail open: a read failure must not block the existing teardown logic.
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
if (isInReviewWindow(reviewRequestedAt, currentDeliveries)) {
|
|
142
|
-
const verifyCount = currentDeliveries.filter(d => d.executionStatus === DELIVERY_STATUS.VERIFY).length;
|
|
143
|
-
if (teamState.sessionType === 'review') {
|
|
144
|
-
// A REVIEW pass just completed -- it emitted its result, which IS the
|
|
145
|
-
// "a review cycle ran" signal. Close the focus DETERMINISTICALLY now,
|
|
146
|
-
// keyed on THIS completion event rather than on process exit. A review
|
|
147
|
-
// resumes its prior session to preserve context (INJ-D) and runs as an
|
|
148
|
-
// interactive stream-json process that emits a result but never exits;
|
|
149
|
-
// the old exit-keyed close therefore waited forever (the review-resume
|
|
150
|
-
// deadlock). runReviewExitHandler routes verify deliveries by the
|
|
151
|
-
// deterministic signal -- a pass that filed zero new issues and has no
|
|
152
|
-
// open work closes verify->done; open work routes verify->verify_failed
|
|
153
|
-
// to re-iterate -- and clears review_requested_at. The review team is then
|
|
154
|
-
// torn down: closure has ALREADY happened, so it no longer matters whether
|
|
155
|
-
// the process exits cleanly or is SIGTERM'd, and the next review cycle
|
|
156
|
-
// resumes the same session id to review only the new delta.
|
|
157
|
-
//
|
|
158
|
-
// succeeded is the ACTUAL result of the review turn (!isError), NOT a
|
|
159
|
-
// hardcoded true. A review that erred (e.g. the CLI failed to start, or a
|
|
160
|
-
// model/engine mismatch) emits a result event with isError=true and files
|
|
161
|
-
// no defects -- the old `succeeded: true` made that indistinguishable from
|
|
162
|
-
// a genuinely clean review, so an errored/no-op review auto-approved
|
|
163
|
-
// verify->done WITHOUT a real review. With the real flag, an errored
|
|
164
|
-
// review leaves the delivery in verify; the next auto-review poll
|
|
165
|
-
// re-triggers, and a persistently failing review is bounded by the
|
|
166
|
-
// review_window_stalled escalation.
|
|
167
|
-
const reviewSucceeded = !ctx.completionInfo.result.isError;
|
|
168
|
-
console.log(`[focus-executor] Review pass completed for "${focusName}" (${verifyCount} ` +
|
|
169
|
-
`delivery(ies) in verify, succeeded=${reviewSucceeded}) -- routing deterministically ` +
|
|
170
|
-
`by filed issues, then terminating.`);
|
|
171
|
-
// Coverage evidence: spawn-tip (what the review saw, carried on the team
|
|
172
|
-
// state) is stamped as the watermark; exit-tip (HEAD now) is compared to
|
|
173
|
-
// it to detect mid-review drift. Exit-tip is best-effort via injected git
|
|
174
|
-
// deps; null makes coverage unconfirmable (auto-approve withheld).
|
|
175
|
-
const reviewSpawnTip = teamState.reviewSpawnTip;
|
|
176
|
-
let currentBranchTip = null;
|
|
177
|
-
try {
|
|
178
|
-
const wt = deps.getFocusWorktree?.(focusId);
|
|
179
|
-
if (wt?.worktreePath) {
|
|
180
|
-
currentBranchTip = deps.readWorktreeHeadSha?.(wt.worktreePath) ?? null;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
catch (err) {
|
|
184
|
-
console.warn(`[focus-executor] Could not resolve exit-tip for "${focusName}" coverage gate: ${err.message}`);
|
|
185
|
-
}
|
|
186
|
-
try {
|
|
187
|
-
await deps.runReviewExitHandler({
|
|
188
|
-
focusId,
|
|
189
|
-
focusName,
|
|
190
|
-
organizationId: teamState.organizationId,
|
|
191
|
-
productId: teamState.productId,
|
|
192
|
-
sessionId: teamState.leadSessionId ?? '',
|
|
193
|
-
sessionType: 'review',
|
|
194
|
-
succeeded: reviewSucceeded,
|
|
195
|
-
reviewSpawnTip,
|
|
196
|
-
currentBranchTip,
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
catch (err) {
|
|
200
|
-
console.warn(`[focus-executor] Deterministic review routing failed for "${focusName}": ${err.message}`);
|
|
201
|
-
}
|
|
202
|
-
teamState.shutdownReason = 'work_complete';
|
|
203
|
-
deps.terminateTeam(focusId);
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
// A non-review (coding) team completed a cycle while a review is in flight.
|
|
207
|
-
// Do NOT tear it down on the all-terminal / no-actionable-work branches
|
|
208
|
-
// below: `verify` is cascadable, so they would SIGTERM the team mid-review.
|
|
209
|
-
// Leave it running; the review team's own completion closes the focus.
|
|
210
|
-
console.log(`[focus-executor] "${focusName}" is in a review window (review_requested_at set, ` +
|
|
211
|
-
`${verifyCount} delivery(ies) in verify) and the completing session is a ` +
|
|
212
|
-
`${teamState.sessionType} pass -- not tearing down; the review pass completion routes ` +
|
|
213
|
-
`the verify deliveries.`);
|
|
90
|
+
// 1. Planning refresh: discover MCP-written deliveries (or escalate an
|
|
91
|
+
// empty planning result and terminate).
|
|
92
|
+
if (await applyPlanningRefresh(ctx, deps, currentDeliveries))
|
|
214
93
|
return;
|
|
215
|
-
|
|
216
|
-
//
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
//
|
|
94
|
+
// 2. Review-window routing: a review pass closes the focus deterministically
|
|
95
|
+
// and terminates; a coding pass mid-review is left running.
|
|
96
|
+
if (await routeReviewWindowCompletion(ctx, deps, currentDeliveries) !== 'not_in_review')
|
|
97
|
+
return;
|
|
98
|
+
// 3. Scope-coverage termination: only terminate when EVERY delivery is in a
|
|
99
|
+
// terminal-for-the-team state (verify, done, cancelled) -- non-terminal
|
|
100
|
+
// states keep the team engaged instead of idle-quitting.
|
|
222
101
|
const nonTerminal = currentDeliveries.filter(d => !isStatusCascadable(d.executionStatus ?? ''));
|
|
223
102
|
if (nonTerminal.length === 0) {
|
|
224
|
-
|
|
103
|
+
log.info(`All deliveries terminal for "${focusName}" -- terminating team`);
|
|
225
104
|
teamState.shutdownReason = 'work_complete';
|
|
226
105
|
deps.terminateTeam(focusId);
|
|
227
106
|
return;
|
|
228
107
|
}
|
|
229
|
-
//
|
|
230
|
-
//
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
const hasActionableWithOpenIssues = currentIssues.some(i => actionableDeliveryIds.has(i.deliveryId) && OPEN_ISSUE_STATUSES.has(i.status));
|
|
237
|
-
// Self-heal BEFORE the no-progress guard. When actionable deliveries exist
|
|
238
|
-
// but none carry open issues, the team has nothing left to code. The common
|
|
239
|
-
// cause is a review that bounced a delivery to verify_failed while all its
|
|
240
|
-
// issues are already Done. The daemon's transparent
|
|
241
|
-
// verify_failed -> coding -> verify stepping (advanceDeliveryStatuses) clears
|
|
242
|
-
// that. Running it here -- before snapshotting -- means a benign "no work to
|
|
243
|
-
// do" cycle either terminates the team cleanly (all deliveries now terminal)
|
|
244
|
-
// or shows up as real progress (the delivery status moved), instead of being
|
|
245
|
-
// counted as coder no-progress. Either way it can no longer accrue toward a
|
|
246
|
-
// spurious team_failure escalation. (Previously this advance ran only AFTER
|
|
247
|
-
// the guard, so the counter could reach the limit and escalate first.)
|
|
248
|
-
let effectiveDeliveries = currentDeliveries;
|
|
249
|
-
if (actionableDeliveryIds.size > 0 && !hasActionableWithOpenIssues) {
|
|
250
|
-
try {
|
|
251
|
-
await deps.advanceDeliveryStatuses(ctx.config, teamState, teamState.leadSessionId ?? '');
|
|
252
|
-
}
|
|
253
|
-
catch (err) {
|
|
254
|
-
console.warn(`[focus-executor] Wake-cycle advanceDeliveryStatuses failed for "${focusName}": ${err.message}`);
|
|
255
|
-
}
|
|
256
|
-
// Re-fetch and re-check termination after the advance -- if every delivery
|
|
257
|
-
// is now terminal-for-the-team, exit cleanly and hand off to review
|
|
258
|
-
// routing / verification instead of sending a stale re-engagement.
|
|
259
|
-
try {
|
|
260
|
-
effectiveDeliveries = await deps.getFocusDeliveries(focusId);
|
|
261
|
-
const stillNonTerminal = effectiveDeliveries.filter(d => !isStatusCascadable(d.executionStatus ?? ''));
|
|
262
|
-
if (stillNonTerminal.length === 0) {
|
|
263
|
-
console.log(`[focus-executor] All deliveries terminal after wake-cycle advance for "${focusName}" -- terminating team`);
|
|
264
|
-
teamState.shutdownReason = 'work_complete';
|
|
265
|
-
deps.terminateTeam(focusId);
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
catch (err) {
|
|
270
|
-
console.warn(`[focus-executor] Failed to re-fetch deliveries after wake-cycle advance for "${focusName}": ${err.message}`);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
// Recompute the picture on the post-advance state. The team owns exactly two
|
|
274
|
-
// kinds of non-terminal work: planning deliveries (it scopes them) and
|
|
275
|
-
// agent-actionable deliveries that still carry open issues (it codes them).
|
|
276
|
-
const postNonTerminal = effectiveDeliveries.filter(d => !isStatusCascadable(d.executionStatus ?? ''));
|
|
277
|
-
const postActionableIds = new Set(effectiveDeliveries
|
|
278
|
-
.filter(d => isStatusAgentActionable(d.executionStatus ?? ''))
|
|
279
|
-
.map(d => d.id));
|
|
280
|
-
const postHasActionableOpen = currentIssues.some(i => postActionableIds.has(i.deliveryId) && OPEN_ISSUE_STATUSES.has(i.status));
|
|
281
|
-
// A read_only focus is awaiting the human ratify gate (audit intake): its
|
|
282
|
-
// `planning` deliveries are held, not team work the team is failing to
|
|
283
|
-
// advance. Counting them as actionable made a not-yet-ratified audit focus
|
|
284
|
-
// accrue no-progress cycles it could never clear -> a spurious no-progress
|
|
285
|
-
// team_failure (escalation aa58d9c3, focus "Test Quality Audit"). Resolve the
|
|
286
|
-
// ratification state FRESH (it can flip read_only=false the moment a human
|
|
287
|
-
// ratifies) and fail open to readOnly=false so a lookup failure preserves the
|
|
288
|
-
// prior (escalate-eligible) behavior rather than silently muting the guard.
|
|
289
|
-
// Resolve the audit-phase state FRESH: an explicit read_only=true, OR the
|
|
290
|
-
// second structural signal (audit-origin AND not-yet-ratified). The second
|
|
291
|
-
// signal makes a flag-absent audit focus (read_only silently null) still count
|
|
292
|
-
// as audit-phase here, so its held `planning` deliveries are NOT treated as
|
|
293
|
-
// team-actionable -- degrading gracefully instead of the spurious no-progress
|
|
294
|
-
// team_failure (injection #7; escalation aa58d9c3 does not reproduce). Fail
|
|
295
|
-
// open to NOT-audit-phase so a lookup failure preserves the prior
|
|
296
|
-
// (escalate-eligible) behavior rather than silently muting the guard.
|
|
297
|
-
let focusAuditPhase = false;
|
|
298
|
-
if (deps.getFocusRatificationState) {
|
|
299
|
-
try {
|
|
300
|
-
const st = await deps.getFocusRatificationState(ctx.config, focusId);
|
|
301
|
-
focusAuditPhase = isAuditPhaseFocus({
|
|
302
|
-
readOnly: st.readOnly,
|
|
303
|
-
auditOrigin: st.auditOrigin ?? false,
|
|
304
|
-
ratified: st.ratifiedAt != null,
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
catch (err) {
|
|
308
|
-
console.warn(`[focus-executor] getFocusRatificationState failed for "${focusName}", ` +
|
|
309
|
-
`treating as not-audit-phase: ${err.message}`);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
// Planning deliveries count as team-actionable ONLY on a non-audit-phase focus
|
|
313
|
-
// (where the team is expected to scope them). On an audit-phase focus they await
|
|
314
|
-
// ratification, so they do not keep the team on the no-progress path. A
|
|
315
|
-
// genuinely stuck planning delivery on a NORMAL focus still escalates
|
|
316
|
-
// (true-positive preserved). Queued deliveries with zero open issues already
|
|
317
|
-
// contribute nothing here -- postHasActionableOpen requires an open issue on an
|
|
318
|
-
// agent-actionable delivery -- so a freshly-scoped, not-yet-started queued
|
|
319
|
-
// delivery does not, on its own, accrue no-progress cycles.
|
|
320
|
-
const hasPlanning = !focusAuditPhase && effectiveDeliveries.some(d => (d.executionStatus ?? '') === 'planning');
|
|
321
|
-
const teamHasActionableWork = hasPlanning || postHasActionableOpen;
|
|
322
|
-
// Resolve the focus worktree once for BOTH the git-activity snapshot
|
|
323
|
-
// dimension and the dirty re-wake guard below. A team that wrote AND committed
|
|
324
|
-
// code this cycle moved the focus branch HEAD even when no DB state moved;
|
|
325
|
-
// feeding that sha into the snapshot makes the commit reset the no-progress
|
|
326
|
-
// counter through the EXISTING madeProgress/snapshotsEqual machinery (no
|
|
327
|
-
// change to the madeProgress logic is needed). null when no worktree is
|
|
328
|
-
// registered or git could not be read -- the watchdog then behaves exactly as
|
|
329
|
-
// before on the git axis.
|
|
330
|
-
const resolveWorktree = deps.getFocusWorktree ?? defaultGetFocusWorktree;
|
|
331
|
-
const readHeadSha = deps.readWorktreeHeadSha ?? defaultReadWorktreeHeadSha;
|
|
332
|
-
const focusWorktreePath = resolveWorktree(focusId)?.worktreePath ?? null;
|
|
333
|
-
const focusHeadSha = focusWorktreePath !== null ? readHeadSha(focusWorktreePath) : null;
|
|
334
|
-
// Capture the (post-advance) snapshot for progress detection. Treat both null
|
|
335
|
-
// and undefined as "no prior snapshot" -- legacy test fixtures may not set
|
|
336
|
-
// the field, and a fresh team has lastProgressSnapshot=null. Snapshotting the
|
|
337
|
-
// effective (refreshed) deliveries ensures a self-heal that moved a delivery
|
|
338
|
-
// out of verify_failed registers as progress and resets the counter.
|
|
339
|
-
//
|
|
340
|
-
// Computed BEFORE the no-actionable-work branch so the non-advanceable guard
|
|
341
|
-
// below can gate on "no progress this cycle" -- the branch returns early, so
|
|
342
|
-
// the original (post-branch) snapshot location was never reached on this path.
|
|
343
|
-
const snapshot = takeProgressSnapshot(effectiveDeliveries, currentIssues, focusHeadSha);
|
|
344
|
-
const priorSnapshot = teamState.lastProgressSnapshot ?? null;
|
|
345
|
-
// NB: a GENUINE review window is already handled ABOVE by the isInReviewWindow
|
|
346
|
-
// early-return (review_requested_at set AND a delivery still in `verify` ->
|
|
347
|
-
// the coding team does not tear down and never reaches this counter logic;
|
|
348
|
-
// deliveries stay in `verify` throughout a review per the lifecycle). So a
|
|
349
|
-
// blanket "review_requested_at => progress" flag here would NOT protect a
|
|
350
|
-
// genuine review (that path never gets here) -- it would only fire in the
|
|
351
|
-
// degenerate state "review_requested_at set, NO verify delivery" (deliveries
|
|
352
|
-
// already bounced to verify_failed, or an in_review delivery). Treating THAT
|
|
353
|
-
// as focus-wide progress masks a co-resident genuinely-stuck queued/coding
|
|
354
|
-
// delivery the team owns and is not moving (the team-stall the watchdog must
|
|
355
|
-
// still catch). It is unnecessary besides: the bounced-to-verify_failed case
|
|
356
|
-
// is covered by verifyFailedPendingRequeue below, an in_review delivery is
|
|
357
|
-
// non-actionable and routes to terminate_no_actionable_work, and a review
|
|
358
|
-
// that never runs is caught by the review-window staleness detector. So there
|
|
359
|
-
// is deliberately NO review_requested_at progress flag here.
|
|
360
|
-
// verify_failed-pending-requeue is daemon-owned advancement. A delivery left
|
|
361
|
-
// in verify_failed is waiting on the daemon's transparent
|
|
362
|
-
// verify_failed -> queued requeue (listener-auto-advance); the TEAM cannot
|
|
363
|
-
// move it until that requeue lands, even though verify_failed is
|
|
364
|
-
// agent-actionable and keeps teamHasActionableWork true. Without this, a
|
|
365
|
-
// verify_failed delivery that sits flat across cycles (same status, same
|
|
366
|
-
// gate_state, same iteration_count -- the entry bump already happened) would
|
|
367
|
-
// accrue toward a spurious team_failure no-progress escalation purely on
|
|
368
|
-
// daemon requeue latency. The injection calls this out explicitly: exclude
|
|
369
|
-
// verify_failed-pending-requeue from the team_failure no-progress path. The
|
|
370
|
-
// safety backfill that makes this patience safe is the D3 verify_failed
|
|
371
|
-
// iteration-cap detector -- a genuinely unbounded re-code cycle escalates with
|
|
372
|
-
// its own verify_gate_unclearable reason, so excluding it here cannot hide a
|
|
373
|
-
// real wedge.
|
|
374
|
-
//
|
|
375
|
-
// CRITICAL SCOPING: this resets the no-progress counter (madeProgress) but is
|
|
376
|
-
// deliberately NOT folded into advancedThisCycle below. advancedThisCycle
|
|
377
|
-
// gates the structural dead-end guard, which fires only on the !actionable
|
|
378
|
-
// path. A verify_failed delivery that is genuinely non-advanceable (NULL
|
|
379
|
-
// workflow stage -> the daemon can never requeue it) survives the pre-guard
|
|
380
|
-
// self-heal advance and lands on that !actionable path; it MUST still escalate
|
|
381
|
-
// as a non-advanceable dead end (the spawn -> idle-exit -> respawn thrash).
|
|
382
|
-
// "Pending requeue" means the daemon can requeue it; a NULL-stage verify_failed
|
|
383
|
-
// is NOT pending requeue, it is wedged. Counting it as advancement would
|
|
384
|
-
// re-open the very thrash this watchdog exists to stop.
|
|
385
|
-
const verifyFailedPendingRequeue = effectiveDeliveries.some(d => (d.executionStatus ?? '') === 'verify_failed');
|
|
386
|
-
const madeProgress = verifyFailedPendingRequeue || priorSnapshot === null
|
|
387
|
-
? true
|
|
388
|
-
: !snapshotsEqual(priorSnapshot, snapshot);
|
|
389
|
-
// Did the team *positively advance* state this cycle? Distinct from
|
|
390
|
-
// madeProgress: a null prior snapshot (a FRESH or RESPAWNED team) is NOT
|
|
391
|
-
// evidence of advancement -- the team simply has no baseline to compare
|
|
392
|
-
// against. madeProgress treats a null prior as `true` so a team's first cycle
|
|
393
|
-
// is not penalised toward the multi-cycle no-progress counter; but the
|
|
394
|
-
// structural dead-end guard below must NOT inherit that optimism. In the real
|
|
395
|
-
// thrash the daemon terminates and respawns the team every poll, so the
|
|
396
|
-
// completion handler ALWAYS sees priorSnapshot === null -> madeProgress always
|
|
397
|
-
// true -> the dead-end guard (when gated on !madeProgress) was unreachable and
|
|
398
|
-
// the focus churned forever. Gate the dead-end guard on !advancedThisCycle
|
|
399
|
-
// instead: false for a null prior (eligible -> can escalate), and false for an
|
|
400
|
-
// unchanged snapshot (genuinely stuck), but true when the team actually moved
|
|
401
|
-
// state this cycle (a live, progressing team is not wedged).
|
|
402
|
-
// verify_failed-pending-requeue is deliberately NOT included here (see the
|
|
403
|
-
// CRITICAL SCOPING note above): a NULL-stage verify_failed reaches the
|
|
404
|
-
// !actionable dead-end guard and must remain eligible to escalate.
|
|
405
|
-
const advancedThisCycle = priorSnapshot !== null && !snapshotsEqual(priorSnapshot, snapshot);
|
|
406
|
-
// ── Decide: handoff vs terminate ────────────────────────────────────────
|
|
407
|
-
// The decision tree is pure and isolated in completion-decision.ts
|
|
408
|
-
// (decideCompletionAction); this handler computes the facts and executes
|
|
409
|
-
// the chosen action's side effects.
|
|
410
|
-
//
|
|
411
|
-
// Non-advanceability lookup, gated by shouldCheckNonAdvanceable: scoped to
|
|
412
|
-
// agent-actionable deliveries (queued / coding / verify_failed). Those are
|
|
413
|
-
// the states the team is *expected* to move; when one carries no open
|
|
414
|
-
// issues AND cannot be advanced by the daemon either, it is a true dead
|
|
415
|
-
// end -> the thrash loop. Daemon/review-managed states (in_review, paused)
|
|
416
|
-
// are deliberately excluded: another subsystem owns advancing them, so a
|
|
417
|
-
// NULL/manual stage there is not a thrash dead end and must keep
|
|
418
|
-
// terminating cleanly. Gated on !advancedThisCycle (NOT !madeProgress):
|
|
419
|
-
// the daemon respawns the team every poll, so a wedged focus reaches here
|
|
420
|
-
// with a null prior snapshot EVERY cycle. !madeProgress was therefore never
|
|
421
|
-
// true on the live thrash path and the guard never fired -- the focus
|
|
422
|
-
// churned a fresh team indefinitely.
|
|
423
|
-
let stuck = null;
|
|
424
|
-
if (shouldCheckNonAdvanceable({ teamHasActionableWork, advancedThisCycle })) {
|
|
425
|
-
const candidates = postNonTerminal.filter(d => isStatusAgentActionable(d.executionStatus ?? ''));
|
|
426
|
-
stuck = await findNonAdvanceableDelivery(candidates, deps.fetchEffectiveWorkflow);
|
|
427
|
-
}
|
|
428
|
-
// The no-progress counter value AFTER this cycle's reset-or-increment.
|
|
429
|
-
// Only committed to teamState on the actionable-work paths below -- the
|
|
430
|
-
// no-actionable-work terminations deliberately do not touch the counter
|
|
431
|
-
// (matching the pre-decision-extraction behavior).
|
|
432
|
-
const updatedNoProgressCycles = madeProgress ? 0 : (teamState.noProgressCycles ?? 0) + 1;
|
|
433
|
-
// Measurable progress this cycle (any axis moved, including a fresh commit on
|
|
434
|
-
// the focus branch) means the prior dirty episode -- if any -- is no longer
|
|
435
|
-
// stalled, so the commit-nudge budget resets alongside the no-progress
|
|
436
|
-
// counter. Committed to teamState on the same branches that commit the
|
|
437
|
-
// no-progress counter (the actionable-work paths), keeping the two in lockstep.
|
|
438
|
-
if (madeProgress) {
|
|
439
|
-
teamState.commitNudgeAttempts = 0;
|
|
440
|
-
}
|
|
108
|
+
// 4. Wake-cycle self-heal: clear verify_failed-with-all-Done via the
|
|
109
|
+
// daemon's transparent advance before any progress accounting.
|
|
110
|
+
const heal = await runWakeCycleSelfHeal(ctx, deps, currentDeliveries, currentIssues);
|
|
111
|
+
if (heal.terminated)
|
|
112
|
+
return;
|
|
113
|
+
// 5. Compute the factual picture once, then decide (pure).
|
|
114
|
+
const facts = await computeCompletionFacts(ctx, deps, currentDeliveries, heal.effectiveDeliveries, currentIssues);
|
|
441
115
|
const action = decideCompletionAction({
|
|
442
|
-
teamHasActionableWork,
|
|
443
|
-
advancedThisCycle,
|
|
444
|
-
hasNonAdvanceableStuck: stuck !== null,
|
|
445
|
-
noProgressCycles: updatedNoProgressCycles,
|
|
116
|
+
teamHasActionableWork: facts.teamHasActionableWork,
|
|
117
|
+
advancedThisCycle: facts.advancedThisCycle,
|
|
118
|
+
hasNonAdvanceableStuck: facts.stuck !== null,
|
|
119
|
+
noProgressCycles: facts.updatedNoProgressCycles,
|
|
446
120
|
noProgressLimit: SCOPE_COVERAGE_NO_PROGRESS_LIMIT,
|
|
447
|
-
hasActionableOpenIssues: postHasActionableOpen,
|
|
121
|
+
hasActionableOpenIssues: facts.postHasActionableOpen,
|
|
448
122
|
});
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
(priorSnapshot === null || !madeProgress);
|
|
452
|
-
const deliveryAdvancement = shouldRecordDeliveryAdvancementCycle
|
|
453
|
-
? recordDeliveryAdvancementCycle(focusId, effectiveDeliveries,
|
|
454
|
-
// Reuse the SAME issue + head-sha values event.ts already computed for the
|
|
455
|
-
// madeProgress snapshot above -- passed through, never re-derived -- so the
|
|
456
|
-
// guard's cross-respawn progress axes stay consistent with madeProgress
|
|
457
|
-
// without a second issue fetch or git read (AC#5). These broaden the guard
|
|
458
|
-
// beyond execution_status so a done delivery pinned at `coding` alongside a
|
|
459
|
-
// freshly-added queued sibling no longer reads as a stall while real issue
|
|
460
|
-
// transitions and commits land (escalation a4a4d0ac).
|
|
461
|
-
currentIssues, focusHeadSha, DELIVERY_ADVANCEMENT_NO_PROGRESS_LIMIT)
|
|
462
|
-
: null;
|
|
463
|
-
if (deliveryAdvancement?.thresholdReached) {
|
|
464
|
-
let alreadyEscalated = false;
|
|
465
|
-
try {
|
|
466
|
-
alreadyEscalated = await deps.hasOpenTeamFailureEscalation(focusId);
|
|
467
|
-
}
|
|
468
|
-
catch (err) {
|
|
469
|
-
console.warn(`[focus-executor] Open-escalation check failed for "${focusName}", proceeding to escalate delivery-advancement stall: ${err.message}`);
|
|
470
|
-
}
|
|
471
|
-
if (!alreadyEscalated) {
|
|
472
|
-
await escalateDeliveryAdvancementStalled({
|
|
473
|
-
organizationId: teamState.organizationId,
|
|
474
|
-
sessionId: teamState.leadSessionId ?? '',
|
|
475
|
-
focusId,
|
|
476
|
-
focusName,
|
|
477
|
-
agentRoleId: teamState.roleId,
|
|
478
|
-
cycles: deliveryAdvancement.noAdvancementCycles,
|
|
479
|
-
deliveries: effectiveDeliveries,
|
|
480
|
-
}, deps);
|
|
481
|
-
}
|
|
482
|
-
else {
|
|
483
|
-
console.log(`[focus-executor] Delivery-advancement stall for "${focusName}" but an open ` +
|
|
484
|
-
`team_failure escalation already exists -- terminating without filing a duplicate`);
|
|
485
|
-
}
|
|
486
|
-
teamState.shutdownReason = 'work_complete';
|
|
487
|
-
deps.terminateTeam(focusId);
|
|
123
|
+
// 6. Cross-respawn delivery-advancement watchdog (may escalate + terminate).
|
|
124
|
+
if (await checkDeliveryAdvancementStall(ctx, deps, facts, action, DELIVERY_ADVANCEMENT_NO_PROGRESS_LIMIT))
|
|
488
125
|
return;
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
case 'escalate_non_advanceable_and_terminate': {
|
|
492
|
-
// No team-actionable work AND the team did not advance state this cycle
|
|
493
|
-
// AND at least one remaining non-terminal delivery is *non-advanceable*
|
|
494
|
-
// (NULL stage, or no automatic transition out of its current stage):
|
|
495
|
-
// nothing -- not the team, not the daemon -- will ever move it.
|
|
496
|
-
// Terminating cleanly would just feed the spawn -> idle-exit -> respawn
|
|
497
|
-
// thrash loop. Per the asymmetry principle the daemon must escalate,
|
|
498
|
-
// never silently churn. File EXACTLY ONE escalation (idempotent: skip if
|
|
499
|
-
// one is already open) and terminate. The escalation is filed as
|
|
500
|
-
// escalation_kind 'team_failure', which the existing respawn guard
|
|
501
|
-
// already suppresses, so the focus is not immediately respawned.
|
|
502
|
-
let alreadyEscalated = false;
|
|
503
|
-
try {
|
|
504
|
-
alreadyEscalated = await deps.hasOpenTeamFailureEscalation(focusId);
|
|
505
|
-
}
|
|
506
|
-
catch (err) {
|
|
507
|
-
console.warn(`[focus-executor] Open-escalation check failed for "${focusName}", proceeding to escalate: ${err.message}`);
|
|
508
|
-
}
|
|
509
|
-
if (!alreadyEscalated) {
|
|
510
|
-
console.warn(`[focus-executor] Non-advanceable delivery for "${focusName}" -- ` +
|
|
511
|
-
`"${stuck.name}" [${stuck.executionStatus ?? 'draft'}] cannot be advanced ` +
|
|
512
|
-
`(stage=${stuck.currentWorkflowStageId ?? 'NULL'}); escalating and terminating ` +
|
|
513
|
-
`instead of respawning`);
|
|
514
|
-
await escalateNonAdvanceable({
|
|
515
|
-
organizationId: teamState.organizationId,
|
|
516
|
-
sessionId: teamState.leadSessionId ?? '',
|
|
517
|
-
focusId,
|
|
518
|
-
focusName,
|
|
519
|
-
agentRoleId: teamState.roleId,
|
|
520
|
-
stuck: stuck,
|
|
521
|
-
remaining: postNonTerminal,
|
|
522
|
-
}, deps);
|
|
523
|
-
}
|
|
524
|
-
else {
|
|
525
|
-
console.log(`[focus-executor] Non-advanceable delivery for "${focusName}" but an open ` +
|
|
526
|
-
`escalation already exists -- terminating without filing a duplicate`);
|
|
527
|
-
}
|
|
528
|
-
teamState.lastProgressSnapshot = snapshot;
|
|
529
|
-
teamState.shutdownReason = 'work_complete';
|
|
530
|
-
deps.terminateTeam(focusId);
|
|
531
|
-
return;
|
|
532
|
-
}
|
|
533
|
-
case 'terminate_no_actionable_work': {
|
|
534
|
-
// Every remaining non-terminal delivery is in a daemon/review-managed
|
|
535
|
-
// state the coding team cannot advance (in_review, paused, or a
|
|
536
|
-
// verify_failed the self-heal could not yet clear). The team is NOT
|
|
537
|
-
// stuck on work it can do -- it has nothing to do. Terminate cleanly
|
|
538
|
-
// and let the daemon (on-exit advance), the listener auto-advance, and
|
|
539
|
-
// the review routing move these deliveries.
|
|
540
|
-
console.log(`[focus-executor] No team-actionable work remains for "${focusName}" -- ` +
|
|
541
|
-
`${postNonTerminal.length} non-terminal delivery(ies) in daemon-managed states ` +
|
|
542
|
-
`(in_review / paused / verify-pending); terminating cleanly without escalation`);
|
|
543
|
-
teamState.shutdownReason = 'work_complete';
|
|
544
|
-
deps.terminateTeam(focusId);
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
case 'escalate_no_progress_and_terminate': {
|
|
548
|
-
// ── Git-aware re-wake-before-terminate ────────────────────────────────
|
|
549
|
-
// This is the path that was KILLING PRODUCERS: a coding team that wrote
|
|
550
|
-
// all its code but did not commit it sees no delivery/issue STATE move, so
|
|
551
|
-
// it crosses the no-progress threshold and -- without this guard -- is
|
|
552
|
-
// escalated + terminated. Once it is dead the commit-gate has no live team
|
|
553
|
-
// to re-wake, so the uncommitted work is stranded until a human commits.
|
|
554
|
-
//
|
|
555
|
-
// Before escalating, check whether the team actually PRODUCED work: a
|
|
556
|
-
// dirty focus worktree (uncommitted changes) means there is real work to
|
|
557
|
-
// preserve. If so AND a live lead is still reachable AND the commit-nudge
|
|
558
|
-
// budget is not exhausted, nudge the lead to commit instead of killing it.
|
|
559
|
-
// The team stays alive, commits its own work, and the delivery advances
|
|
560
|
-
// out of coding through the normal commit-gate -- no manual commit, no
|
|
561
|
-
// escalation. A genuinely idle team (clean tree, OR no live lead, OR
|
|
562
|
-
// budget exhausted) falls through to the unchanged escalate+terminate
|
|
563
|
-
// behavior below, so a team that will never commit still escalates.
|
|
564
|
-
const worktreeIsDirty = focusWorktreePath !== null &&
|
|
565
|
-
(deps.worktreeHasUncommittedChanges ?? defaultWorktreeHasUncommittedChanges)(focusWorktreePath);
|
|
566
|
-
const liveLead = teamState.leadStdin;
|
|
567
|
-
// Budget mirrors the advance-block auto-remediation budget so the two
|
|
568
|
-
// re-wake paths (this one and delivery-advance-block's) bound retries the
|
|
569
|
-
// same way; resolveAutoRemediationBudget reads TELORA_AUTO_REMEDIATION_BUDGET.
|
|
570
|
-
const commitNudgeBudget = resolveAutoRemediationBudget();
|
|
571
|
-
const nudgesSpent = teamState.commitNudgeAttempts ?? 0;
|
|
572
|
-
if (worktreeIsDirty && liveLead && nudgesSpent < commitNudgeBudget) {
|
|
573
|
-
// Keep the producing team alive: nudge it to commit, record the
|
|
574
|
-
// snapshot + bump the per-episode nudge counter, reset the completion
|
|
575
|
-
// detector so the next 'complete' event is a fresh cycle, and RETURN
|
|
576
|
-
// WITHOUT terminating or escalating.
|
|
577
|
-
teamState.commitNudgeAttempts = nudgesSpent + 1;
|
|
578
|
-
teamState.lastProgressSnapshot = snapshot;
|
|
579
|
-
console.warn(`[focus-executor] No-progress threshold reached for "${focusName}", but the ` +
|
|
580
|
-
`focus worktree has uncommitted changes and a live lead is reachable -- ` +
|
|
581
|
-
`re-waking the team to commit instead of terminating ` +
|
|
582
|
-
`(commit-nudge attempt ${teamState.commitNudgeAttempts}/${commitNudgeBudget}).`);
|
|
583
|
-
sendMessage(liveLead, `Your focus worktree has uncommitted changes, so the daemon cannot advance ` +
|
|
584
|
-
`the delivery and will not terminate the team yet. Please commit your work in ` +
|
|
585
|
-
`${focusWorktreePath}, and move every finished issue to In Review (its delivery ` +
|
|
586
|
-
`advances out of coding once its work issues are Done/Verified/In Review). The ` +
|
|
587
|
-
`daemon advances automatically once the worktree is clean. (commit-nudge attempt ` +
|
|
588
|
-
`${teamState.commitNudgeAttempts}/${commitNudgeBudget})`);
|
|
589
|
-
completionDetector.reset();
|
|
590
|
-
return;
|
|
591
|
-
}
|
|
592
|
-
// ── Reconcile captured work into the verify gate ──────────────────────
|
|
593
|
-
// The commit-nudge re-wake did not fire (clean tree, OR no live lead, OR
|
|
594
|
-
// budget exhausted). Before escalating, rescue any committed work: the
|
|
595
|
-
// reconciler safety-commits a dirty worktree so the team's bytes are
|
|
596
|
-
// captured, and if the focus branch now carries committed work, it flips
|
|
597
|
-
// each finished coding delivery's In-Progress issues -> In Review and
|
|
598
|
-
// advances coding -> verify so the work re-enters the review gate instead
|
|
599
|
-
// of dead-ending in coding (the recurring stall this focus fixes). The
|
|
600
|
-
// no-progress team_failure escalation below fires ONLY when nothing was
|
|
601
|
-
// reconciled -- i.e. genuinely nothing was produced.
|
|
602
|
-
const reconcile = deps.reconcileTerminatedTeamWork ?? defaultReconcileTerminatedTeamWork;
|
|
603
|
-
let reconciled = { reconciledCount: 0, workCaptured: false };
|
|
604
|
-
try {
|
|
605
|
-
reconciled = await reconcile({
|
|
606
|
-
config: ctx.config,
|
|
607
|
-
teamState,
|
|
608
|
-
deliveries: effectiveDeliveries,
|
|
609
|
-
issues: currentIssues,
|
|
610
|
-
worktreePath: focusWorktreePath,
|
|
611
|
-
sessionId: teamState.leadSessionId ?? '',
|
|
612
|
-
});
|
|
613
|
-
}
|
|
614
|
-
catch (err) {
|
|
615
|
-
console.warn(`[focus-executor] Terminated-work reconciliation failed for "${focusName}": ${err.message}`);
|
|
616
|
-
}
|
|
617
|
-
if (reconciled.reconciledCount > 0) {
|
|
618
|
-
// Committed work re-entered the gate -- terminate cleanly, NO team_failure.
|
|
619
|
-
teamState.commitNudgeAttempts = 0;
|
|
620
|
-
teamState.noProgressCycles = updatedNoProgressCycles;
|
|
621
|
-
teamState.lastProgressSnapshot = snapshot;
|
|
622
|
-
console.log(`[focus-executor] Terminated team for "${focusName}" had committed work -- ` +
|
|
623
|
-
`reconciled ${reconciled.reconciledCount} coding delivery(ies) into verify; ` +
|
|
624
|
-
`terminating WITHOUT a team_failure escalation.`);
|
|
625
|
-
teamState.shutdownReason = 'work_complete';
|
|
626
|
-
deps.terminateTeam(focusId);
|
|
627
|
-
return;
|
|
628
|
-
}
|
|
629
|
-
// Crossed the no-progress threshold: escalate and shut the team down so
|
|
630
|
-
// a human can intervene. Reached only when the team HAS actionable work
|
|
631
|
-
// it owns -- a planning delivery to scope, or a coding delivery whose
|
|
632
|
-
// issues stay open -- yet has not moved it for N consecutive cycles. The
|
|
633
|
-
// no-actionable-work cases (verify_failed-with-all-Done, in_review,
|
|
634
|
-
// paused) terminate cleanly above and never reach here, so this
|
|
635
|
-
// escalation now means a genuinely stuck team, not a daemon-managed
|
|
636
|
-
// transition. The worktree is clean here (or no lead / budget spent), so
|
|
637
|
-
// the dirty episode -- if there was one -- is over: reset the nudge counter.
|
|
638
|
-
teamState.commitNudgeAttempts = 0;
|
|
639
|
-
teamState.noProgressCycles = updatedNoProgressCycles;
|
|
640
|
-
teamState.lastProgressSnapshot = snapshot;
|
|
641
|
-
console.warn(`[focus-executor] Scope-coverage guard tripped for "${focusName}" -- ` +
|
|
642
|
-
`${teamState.noProgressCycles} consecutive cycles with no progress on ${postNonTerminal.length} ` +
|
|
643
|
-
`non-terminal delivery(ies); escalating and terminating`);
|
|
644
|
-
await escalateNoProgress({
|
|
645
|
-
organizationId: teamState.organizationId,
|
|
646
|
-
sessionId: teamState.leadSessionId ?? '',
|
|
647
|
-
focusId,
|
|
648
|
-
focusName,
|
|
649
|
-
agentRoleId: teamState.roleId,
|
|
650
|
-
cycles: teamState.noProgressCycles,
|
|
651
|
-
remaining: postNonTerminal,
|
|
652
|
-
}, deps);
|
|
653
|
-
teamState.shutdownReason = 'work_complete';
|
|
654
|
-
deps.terminateTeam(focusId);
|
|
655
|
-
return;
|
|
656
|
-
}
|
|
657
|
-
case 'handoff_new_work':
|
|
658
|
-
case 'reengage': {
|
|
659
|
-
teamState.noProgressCycles = updatedNoProgressCycles;
|
|
660
|
-
teamState.lastProgressSnapshot = snapshot;
|
|
661
|
-
// Actionable deliveries with open issues -> wake message with the new
|
|
662
|
-
// work. Otherwise (planning deliveries remain) -> re-engagement nudge to
|
|
663
|
-
// scope them.
|
|
664
|
-
let message;
|
|
665
|
-
if (action.kind === 'handoff_new_work') {
|
|
666
|
-
console.log(`[focus-executor] Mid-focus handoff for "${focusName}" -- sending new work`);
|
|
667
|
-
message = buildWakeMessage(effectiveDeliveries, currentIssues, teamState.knownDeliveryIds);
|
|
668
|
-
}
|
|
669
|
-
else {
|
|
670
|
-
console.log(`[focus-executor] Re-engaging "${focusName}" -- ${postNonTerminal.length} non-terminal ` +
|
|
671
|
-
`delivery(ies) remain (cycle ${teamState.noProgressCycles}/${SCOPE_COVERAGE_NO_PROGRESS_LIMIT})`);
|
|
672
|
-
message = buildReEngagementMessage(postNonTerminal, currentIssues);
|
|
673
|
-
}
|
|
674
|
-
// Update tracking state
|
|
675
|
-
for (const d of currentDeliveries) {
|
|
676
|
-
teamState.knownDeliveryIds.add(d.id);
|
|
677
|
-
teamState.deliveryStageIds.set(d.id, d.currentWorkflowStageId);
|
|
678
|
-
}
|
|
679
|
-
completionDetector.reset();
|
|
680
|
-
sendMessage(proc.stdin, message);
|
|
681
|
-
return;
|
|
682
|
-
}
|
|
683
|
-
}
|
|
126
|
+
// 7. Execute the chosen action's side effects.
|
|
127
|
+
await executeCompletionAction(ctx, deps, facts, action, SCOPE_COVERAGE_NO_PROGRESS_LIMIT);
|
|
684
128
|
}
|
|
685
129
|
// ── Progress snapshot helpers ───────────────────────────────────────────
|
|
686
130
|
// Extracted to ./progress-snapshot.ts (keeps this file under the structural
|