@telora/daemon 0.21.98 → 0.21.134
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 +6 -3
- package/dist/audit/audit-hooks.d.ts +106 -0
- package/dist/audit/audit-hooks.d.ts.map +1 -1
- package/dist/audit/audit-hooks.js +149 -17
- package/dist/audit/audit-hooks.js.map +1 -1
- package/dist/ci/ci-sensor-loop.d.ts +6 -0
- package/dist/ci/ci-sensor-loop.d.ts.map +1 -1
- package/dist/ci/ci-sensor-loop.js +50 -3
- package/dist/ci/ci-sensor-loop.js.map +1 -1
- package/dist/completion/coverage-drift-state.d.ts +29 -2
- package/dist/completion/coverage-drift-state.d.ts.map +1 -1
- package/dist/completion/coverage-drift-state.js +42 -2
- package/dist/completion/coverage-drift-state.js.map +1 -1
- package/dist/completion/coverage-stabilized.d.ts +58 -0
- package/dist/completion/coverage-stabilized.d.ts.map +1 -0
- package/dist/completion/coverage-stabilized.js +84 -0
- package/dist/completion/coverage-stabilized.js.map +1 -0
- package/dist/completion/delivery-advancement-guard.d.ts +21 -0
- package/dist/completion/delivery-advancement-guard.d.ts.map +1 -1
- package/dist/completion/delivery-advancement-guard.js +31 -3
- package/dist/completion/delivery-advancement-guard.js.map +1 -1
- package/dist/completion/event-escalations.d.ts +15 -0
- package/dist/completion/event-escalations.d.ts.map +1 -1
- package/dist/completion/event-escalations.js +42 -4
- package/dist/completion/event-escalations.js.map +1 -1
- package/dist/completion/event-phases.d.ts.map +1 -1
- package/dist/completion/event-phases.js +5 -2
- package/dist/completion/event-phases.js.map +1 -1
- package/dist/completion/machine-remediable-demotion.d.ts +74 -0
- package/dist/completion/machine-remediable-demotion.d.ts.map +1 -0
- package/dist/completion/machine-remediable-demotion.js +147 -0
- package/dist/completion/machine-remediable-demotion.js.map +1 -0
- package/dist/completion/review-exit-phase.d.ts.map +1 -1
- package/dist/completion/review-exit-phase.js +10 -6
- package/dist/completion/review-exit-phase.js.map +1 -1
- package/dist/completion/settle-injection-sweep.d.ts +55 -0
- package/dist/completion/settle-injection-sweep.d.ts.map +1 -0
- package/dist/completion/settle-injection-sweep.js +88 -0
- package/dist/completion/settle-injection-sweep.js.map +1 -0
- package/dist/completion/state-cascade.d.ts.map +1 -1
- package/dist/completion/state-cascade.js +41 -0
- package/dist/completion/state-cascade.js.map +1 -1
- package/dist/completion/status-advance-phase.d.ts.map +1 -1
- package/dist/completion/status-advance-phase.js +18 -1
- package/dist/completion/status-advance-phase.js.map +1 -1
- package/dist/completion/team-lifecycle.d.ts +42 -0
- package/dist/completion/team-lifecycle.d.ts.map +1 -1
- package/dist/completion/team-lifecycle.js +71 -0
- package/dist/completion/team-lifecycle.js.map +1 -1
- package/dist/delivery/delivery-lifecycle.d.ts +19 -33
- package/dist/delivery/delivery-lifecycle.d.ts.map +1 -1
- package/dist/delivery/delivery-lifecycle.js +19 -147
- package/dist/delivery/delivery-lifecycle.js.map +1 -1
- package/dist/delivery/delivery-merge-ops.d.ts +46 -0
- package/dist/delivery/delivery-merge-ops.d.ts.map +1 -0
- package/dist/delivery/delivery-merge-ops.js +154 -0
- package/dist/delivery/delivery-merge-ops.js.map +1 -0
- package/dist/delivery/delivery-reconciler-apply.d.ts +28 -0
- package/dist/delivery/delivery-reconciler-apply.d.ts.map +1 -1
- package/dist/delivery/delivery-reconciler-apply.js +30 -0
- package/dist/delivery/delivery-reconciler-apply.js.map +1 -1
- package/dist/delivery/delivery-reconciler.d.ts +7 -2
- package/dist/delivery/delivery-reconciler.d.ts.map +1 -1
- package/dist/delivery/delivery-reconciler.js +34 -5
- package/dist/delivery/delivery-reconciler.js.map +1 -1
- package/dist/delivery/external-hold-surface.d.ts +32 -0
- package/dist/delivery/external-hold-surface.d.ts.map +1 -0
- package/dist/delivery/external-hold-surface.js +67 -0
- package/dist/delivery/external-hold-surface.js.map +1 -0
- package/dist/delivery/external-hold.d.ts +21 -0
- package/dist/delivery/external-hold.d.ts.map +1 -1
- package/dist/delivery/external-hold.js +40 -5
- package/dist/delivery/external-hold.js.map +1 -1
- package/dist/delivery/paused-revival.d.ts +140 -0
- package/dist/delivery/paused-revival.d.ts.map +1 -0
- package/dist/delivery/paused-revival.js +229 -0
- package/dist/delivery/paused-revival.js.map +1 -0
- package/dist/delivery/wait-reachability.d.ts.map +1 -1
- package/dist/delivery/wait-reachability.js +11 -0
- package/dist/delivery/wait-reachability.js.map +1 -1
- package/dist/directive/close-loop-dispatcher.d.ts +16 -263
- package/dist/directive/close-loop-dispatcher.d.ts.map +1 -1
- package/dist/directive/close-loop-dispatcher.js +21 -259
- package/dist/directive/close-loop-dispatcher.js.map +1 -1
- package/dist/directive/directive-dispatch.d.ts +19 -0
- package/dist/directive/directive-dispatch.d.ts.map +1 -1
- package/dist/directive/directive-dispatch.js +18 -0
- package/dist/directive/directive-dispatch.js.map +1 -1
- package/dist/directive/directive-executor.d.ts +1 -3
- package/dist/directive/directive-executor.d.ts.map +1 -1
- package/dist/directive/directive-executor.js +23 -21
- package/dist/directive/directive-executor.js.map +1 -1
- package/dist/directive/phase-sync.d.ts +28 -15
- package/dist/directive/phase-sync.d.ts.map +1 -1
- package/dist/directive/phase-sync.js +28 -36
- package/dist/directive/phase-sync.js.map +1 -1
- package/dist/directive/resolver-dispatch.d.ts +19 -92
- package/dist/directive/resolver-dispatch.d.ts.map +1 -1
- package/dist/directive/resolver-dispatch.js +19 -65
- package/dist/directive/resolver-dispatch.js.map +1 -1
- package/dist/focus/focus-engine.d.ts.map +1 -1
- package/dist/focus/focus-engine.js +26 -0
- package/dist/focus/focus-engine.js.map +1 -1
- package/dist/focus/focus-executor.d.ts.map +1 -1
- package/dist/focus/focus-executor.js +290 -256
- package/dist/focus/focus-executor.js.map +1 -1
- package/dist/focus/focus-loop-registry.d.ts.map +1 -1
- package/dist/focus/focus-loop-registry.js +43 -1
- package/dist/focus/focus-loop-registry.js.map +1 -1
- package/dist/focus/focus-respawn-guard.d.ts.map +1 -1
- package/dist/focus/focus-respawn-guard.js +11 -2
- package/dist/focus/focus-respawn-guard.js.map +1 -1
- package/dist/focus/focus-team-state.d.ts +11 -0
- package/dist/focus/focus-team-state.d.ts.map +1 -1
- package/dist/focus/focus-team-state.js +17 -0
- package/dist/focus/focus-team-state.js.map +1 -1
- package/dist/focus/listener-auto-advance.d.ts +13 -1
- package/dist/focus/listener-auto-advance.d.ts.map +1 -1
- package/dist/focus/listener-auto-advance.js +103 -22
- package/dist/focus/listener-auto-advance.js.map +1 -1
- package/dist/focus/listener.d.ts.map +1 -1
- package/dist/focus/listener.js +125 -75
- package/dist/focus/listener.js.map +1 -1
- package/dist/focus/tick-deadline.d.ts +55 -0
- package/dist/focus/tick-deadline.d.ts.map +1 -0
- package/dist/focus/tick-deadline.js +91 -0
- package/dist/focus/tick-deadline.js.map +1 -0
- package/dist/focus-stage-lifecycle/index.d.ts +18 -25
- package/dist/focus-stage-lifecycle/index.d.ts.map +1 -1
- package/dist/focus-stage-lifecycle/index.js +26 -38
- package/dist/focus-stage-lifecycle/index.js.map +1 -1
- package/dist/focus-stage-lifecycle/wedge-cause-arbiter.d.ts +52 -0
- package/dist/focus-stage-lifecycle/wedge-cause-arbiter.d.ts.map +1 -0
- package/dist/focus-stage-lifecycle/wedge-cause-arbiter.js +94 -0
- package/dist/focus-stage-lifecycle/wedge-cause-arbiter.js.map +1 -0
- package/dist/focus-stage-lifecycle/wedge-watchdog.d.ts +18 -1
- package/dist/focus-stage-lifecycle/wedge-watchdog.d.ts.map +1 -1
- package/dist/focus-stage-lifecycle/wedge-watchdog.js +57 -7
- package/dist/focus-stage-lifecycle/wedge-watchdog.js.map +1 -1
- package/dist/guard/condition-evaluators.d.ts +9 -0
- package/dist/guard/condition-evaluators.d.ts.map +1 -1
- package/dist/guard/condition-evaluators.js +15 -0
- package/dist/guard/condition-evaluators.js.map +1 -1
- package/dist/guard/guard-evaluator.d.ts +17 -1
- package/dist/guard/guard-evaluator.d.ts.map +1 -1
- package/dist/guard/guard-evaluator.js +61 -2
- package/dist/guard/guard-evaluator.js.map +1 -1
- package/dist/guard/trigger-executor.d.ts +30 -18
- package/dist/guard/trigger-executor.d.ts.map +1 -1
- package/dist/guard/trigger-executor.js +55 -31
- package/dist/guard/trigger-executor.js.map +1 -1
- package/dist/prompt/team-prompt-base.d.ts +1 -1
- package/dist/prompt/team-prompt-base.d.ts.map +1 -1
- package/dist/prompt/team-prompt-base.js +1 -1
- package/dist/prompt/team-prompt-base.js.map +1 -1
- package/dist/prompt-sections/persona-sections.d.ts +0 -32
- package/dist/prompt-sections/persona-sections.d.ts.map +1 -1
- package/dist/prompt-sections/persona-sections.js +19 -88
- package/dist/prompt-sections/persona-sections.js.map +1 -1
- package/dist/queries/deliveries.d.ts +23 -0
- package/dist/queries/deliveries.d.ts.map +1 -1
- package/dist/queries/deliveries.js +6 -0
- package/dist/queries/deliveries.js.map +1 -1
- package/dist/queries/focuses.d.ts +36 -0
- package/dist/queries/focuses.d.ts.map +1 -1
- package/dist/queries/focuses.js +45 -0
- package/dist/queries/focuses.js.map +1 -1
- package/dist/queries/guards.d.ts +10 -0
- package/dist/queries/guards.d.ts.map +1 -1
- package/dist/queries/guards.js +14 -1
- package/dist/queries/guards.js.map +1 -1
- package/dist/queries/issues.d.ts +65 -1
- package/dist/queries/issues.d.ts.map +1 -1
- package/dist/queries/issues.js +135 -2
- package/dist/queries/issues.js.map +1 -1
- package/dist/queries/schemas.d.ts +51 -0
- package/dist/queries/schemas.d.ts.map +1 -1
- package/dist/queries/schemas.js +34 -0
- package/dist/queries/schemas.js.map +1 -1
- package/dist/queries/verification.d.ts +5 -6
- package/dist/queries/verification.d.ts.map +1 -1
- package/dist/queries/verification.js +5 -6
- package/dist/queries/verification.js.map +1 -1
- package/dist/resolvers/loop-persona.d.ts +1 -11
- package/dist/resolvers/loop-persona.d.ts.map +1 -1
- package/dist/resolvers/loop-persona.js +2 -16
- package/dist/resolvers/loop-persona.js.map +1 -1
- package/dist/shell/unified-shell-provisioning.d.ts +40 -0
- package/dist/shell/unified-shell-provisioning.d.ts.map +1 -1
- package/dist/shell/unified-shell-provisioning.js +72 -0
- package/dist/shell/unified-shell-provisioning.js.map +1 -1
- package/dist/shell/unified-shell.d.ts.map +1 -1
- package/dist/shell/unified-shell.js +18 -1
- package/dist/shell/unified-shell.js.map +1 -1
- package/dist/spawn/helpers.d.ts.map +1 -1
- package/dist/spawn/helpers.js +6 -1
- package/dist/spawn/helpers.js.map +1 -1
- package/dist/spawn/process-spawn.d.ts.map +1 -1
- package/dist/spawn/process-spawn.js +7 -0
- package/dist/spawn/process-spawn.js.map +1 -1
- package/dist/spawn/spawn-cooldown.d.ts +41 -7
- package/dist/spawn/spawn-cooldown.d.ts.map +1 -1
- package/dist/spawn/spawn-cooldown.js +47 -0
- package/dist/spawn/spawn-cooldown.js.map +1 -1
- package/dist/spawn/spawn-decision.d.ts.map +1 -1
- package/dist/spawn/spawn-decision.js +33 -1
- package/dist/spawn/spawn-decision.js.map +1 -1
- package/dist/verify/stall-detectors.d.ts +20 -8
- package/dist/verify/stall-detectors.d.ts.map +1 -1
- package/dist/verify/stall-detectors.js +27 -46
- package/dist/verify/stall-detectors.js.map +1 -1
- package/dist/workflow/gate-failure-policy.d.ts +48 -0
- package/dist/workflow/gate-failure-policy.d.ts.map +1 -0
- package/dist/workflow/gate-failure-policy.js +57 -0
- package/dist/workflow/gate-failure-policy.js.map +1 -0
- package/dist/workflow/pipeline-config.d.ts +2 -2
- package/dist/workflow/pipeline-config.js +2 -2
- package/package.json +1 -1
- package/dist/directive/close-loop-stage.d.ts +0 -50
- package/dist/directive/close-loop-stage.d.ts.map +0 -1
- package/dist/directive/close-loop-stage.js +0 -210
- package/dist/directive/close-loop-stage.js.map +0 -1
- package/dist/focus-stage-lifecycle/review-convergence.d.ts +0 -133
- package/dist/focus-stage-lifecycle/review-convergence.d.ts.map +0 -1
- package/dist/focus-stage-lifecycle/review-convergence.js +0 -237
- package/dist/focus-stage-lifecycle/review-convergence.js.map +0 -1
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Close-loop stage dispatcher.
|
|
3
|
-
*
|
|
4
|
-
* Drives the `close_loop` lifecycle stage for a focus: classify anchored
|
|
5
|
-
* injections by terminal state, then short-circuit to done, spawn the
|
|
6
|
-
* one-shot bookkeeper team, advance with an injection sweep, or hold.
|
|
7
|
-
* The pure decision logic lives in close-loop-dispatcher.ts; this module
|
|
8
|
-
* is the impure dispatch around it. Extracted from directive-executor.ts;
|
|
9
|
-
* the executor re-exports the public surface so existing importers are
|
|
10
|
-
* unaffected.
|
|
11
|
-
*/
|
|
12
|
-
import type { DaemonConfig, StageDirective } from '../types/index.js';
|
|
13
|
-
import { type CloseLoopDecision } from './close-loop-dispatcher.js';
|
|
14
|
-
/** Test helper: clear the bookkeeper-spawned set. */
|
|
15
|
-
export declare function __resetCloseLoopStateForTesting(): void;
|
|
16
|
-
/**
|
|
17
|
-
* Inspect open escalations and return the set of injection ids that have
|
|
18
|
-
* an active `injection_unverified` escalation (status pending or
|
|
19
|
-
* in_review). Used by the close_loop dispatcher to classify "escalated"
|
|
20
|
-
* injections as terminal.
|
|
21
|
-
*
|
|
22
|
-
* The `metadata.injection_id` field is set by the bookkeeper persona when
|
|
23
|
-
* filing the escalation -- see the close_loop directive prompt.
|
|
24
|
-
*/
|
|
25
|
-
export declare function getOpenInjectionUnverifiedEscalationIds(): Promise<Set<string>>;
|
|
26
|
-
/**
|
|
27
|
-
* Dispatch the close_loop stage for one focus.
|
|
28
|
-
*
|
|
29
|
-
* Reads the focus's anchored deliveries + injection statuses + open
|
|
30
|
-
* escalations, then runs the pure `decideCloseLoop` to determine whether
|
|
31
|
-
* to short-circuit, spawn a bookkeeper, advance to done, or hold.
|
|
32
|
-
*
|
|
33
|
-
* Returns the decision so the caller (or a test) can log/assert.
|
|
34
|
-
*
|
|
35
|
-
* Idempotent across poll cycles -- the `closeLoopBookkeeperSpawned` set
|
|
36
|
-
* ensures the bookkeeper is spawned at most once per focus visit to
|
|
37
|
-
* close_loop. Tests reset this via `__resetCloseLoopStateForTesting`.
|
|
38
|
-
*/
|
|
39
|
-
export declare function dispatchCloseLoopStage(config: DaemonConfig, focus: {
|
|
40
|
-
focus_id: string;
|
|
41
|
-
focus_name: string;
|
|
42
|
-
}): Promise<CloseLoopDecision>;
|
|
43
|
-
/**
|
|
44
|
-
* Pure helper exported for tests: build the close_loop directive and verify
|
|
45
|
-
* it carries the expected assembly + model. The buildCloseLoopDirective
|
|
46
|
-
* function in close-loop-dispatcher.ts already does this; this is a
|
|
47
|
-
* dispatcher-side smoke export so the tests can grep through it.
|
|
48
|
-
*/
|
|
49
|
-
export declare function getCloseLoopDirectiveForTesting(): StageDirective;
|
|
50
|
-
//# sourceMappingURL=close-loop-stage.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"close-loop-stage.d.ts","sourceRoot":"","sources":["../../src/directive/close-loop-stage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGtE,OAAO,EAML,KAAK,iBAAiB,EAEvB,MAAM,4BAA4B,CAAC;AA2BpC,qDAAqD;AACrD,wBAAgB,+BAA+B,IAAI,IAAI,CAEtD;AAED;;;;;;;;GAQG;AACH,wBAAsB,uCAAuC,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAWpF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC9C,OAAO,CAAC,iBAAiB,CAAC,CAsK5B;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,IAAI,cAAc,CAEhE"}
|
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Close-loop stage dispatcher.
|
|
3
|
-
*
|
|
4
|
-
* Drives the `close_loop` lifecycle stage for a focus: classify anchored
|
|
5
|
-
* injections by terminal state, then short-circuit to done, spawn the
|
|
6
|
-
* one-shot bookkeeper team, advance with an injection sweep, or hold.
|
|
7
|
-
* The pure decision logic lives in close-loop-dispatcher.ts; this module
|
|
8
|
-
* is the impure dispatch around it. Extracted from directive-executor.ts;
|
|
9
|
-
* the executor re-exports the public surface so existing importers are
|
|
10
|
-
* unaffected.
|
|
11
|
-
*/
|
|
12
|
-
import { getActiveTeams } from '../focus/focus-team-state.js';
|
|
13
|
-
import { ESCALATION_KINDS } from '@telora/daemon-core';
|
|
14
|
-
import { CLOSE_LOOP_MODEL, advanceFocusToDoneWithSweep, buildCloseLoopDirective, decideCloseLoop, getAnchoringDeliveries, } from './close-loop-dispatcher.js';
|
|
15
|
-
import { buildWritebackVerify } from '../queries/verification.js';
|
|
16
|
-
import { getFocusDeliveries, updateFocusStage } from '../queries/focuses.js';
|
|
17
|
-
import { listPendingEscalations } from '../queries/issues.js';
|
|
18
|
-
import { callApi } from '../queries/shared.js';
|
|
19
|
-
import { terminateTeam, waitForTeamExit } from '../completion/index.js';
|
|
20
|
-
import { pendingSpawnDirectives, computeDirectiveHash } from './directive-queue.js';
|
|
21
|
-
import { assembleDirectiveContent } from './directive-assembly.js';
|
|
22
|
-
import { createLogger } from '../shell/log.js';
|
|
23
|
-
const log = createLogger({ module: 'close-loop-dispatcher' });
|
|
24
|
-
/**
|
|
25
|
-
* Per-focus tracker for whether the close_loop bookkeeper has been spawned
|
|
26
|
-
* at least once. Used to avoid respawning the bookkeeper on every poll
|
|
27
|
-
* cycle while non-terminal injections are still being worked.
|
|
28
|
-
*
|
|
29
|
-
* Cleared when the focus leaves close_loop (advances to done or is
|
|
30
|
-
* manually reset). The set is in-process state -- on daemon restart it is
|
|
31
|
-
* empty, which is fine because the dispatcher's decideCloseLoop function
|
|
32
|
-
* will see the bookkeeper has not run, respawn it, and the new bookkeeper
|
|
33
|
-
* will sweep idempotently (already-verified injections stay verified;
|
|
34
|
-
* already-escalated ones still surface their open escalation).
|
|
35
|
-
*/
|
|
36
|
-
const closeLoopBookkeeperSpawned = new Set();
|
|
37
|
-
/** Test helper: clear the bookkeeper-spawned set. */
|
|
38
|
-
export function __resetCloseLoopStateForTesting() {
|
|
39
|
-
closeLoopBookkeeperSpawned.clear();
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Inspect open escalations and return the set of injection ids that have
|
|
43
|
-
* an active `injection_unverified` escalation (status pending or
|
|
44
|
-
* in_review). Used by the close_loop dispatcher to classify "escalated"
|
|
45
|
-
* injections as terminal.
|
|
46
|
-
*
|
|
47
|
-
* The `metadata.injection_id` field is set by the bookkeeper persona when
|
|
48
|
-
* filing the escalation -- see the close_loop directive prompt.
|
|
49
|
-
*/
|
|
50
|
-
export async function getOpenInjectionUnverifiedEscalationIds() {
|
|
51
|
-
const escalations = await listPendingEscalations();
|
|
52
|
-
const ids = new Set();
|
|
53
|
-
for (const e of escalations) {
|
|
54
|
-
if (e.escalationKind !== ESCALATION_KINDS.INJECTION_UNVERIFIED)
|
|
55
|
-
continue;
|
|
56
|
-
const injectionId = e.metadata?.injection_id;
|
|
57
|
-
if (typeof injectionId === 'string' && injectionId.length > 0) {
|
|
58
|
-
ids.add(injectionId);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return ids;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Dispatch the close_loop stage for one focus.
|
|
65
|
-
*
|
|
66
|
-
* Reads the focus's anchored deliveries + injection statuses + open
|
|
67
|
-
* escalations, then runs the pure `decideCloseLoop` to determine whether
|
|
68
|
-
* to short-circuit, spawn a bookkeeper, advance to done, or hold.
|
|
69
|
-
*
|
|
70
|
-
* Returns the decision so the caller (or a test) can log/assert.
|
|
71
|
-
*
|
|
72
|
-
* Idempotent across poll cycles -- the `closeLoopBookkeeperSpawned` set
|
|
73
|
-
* ensures the bookkeeper is spawned at most once per focus visit to
|
|
74
|
-
* close_loop. Tests reset this via `__resetCloseLoopStateForTesting`.
|
|
75
|
-
*/
|
|
76
|
-
export async function dispatchCloseLoopStage(config, focus) {
|
|
77
|
-
const deliveries = await getFocusDeliveries(focus.focus_id);
|
|
78
|
-
const anchored = getAnchoringDeliveries(deliveries);
|
|
79
|
-
// Build the per-injection terminal-state snapshot.
|
|
80
|
-
//
|
|
81
|
-
// `statusById` is hoisted to function scope so the close_loop sweep (in
|
|
82
|
-
// the advance_to_done branch below) can reuse the snapshot without a
|
|
83
|
-
// second round-trip to reality_tree_focus_injections.
|
|
84
|
-
const states = [];
|
|
85
|
-
const statusById = new Map();
|
|
86
|
-
// injectionId -> reality_tree id, captured from the focus-injections snapshot
|
|
87
|
-
// so the writeback step can resolve each injection's targeted UDE node ids
|
|
88
|
-
// (via reality_tree_edge_list) without a second injection round-trip.
|
|
89
|
-
const treeIdByInjection = new Map();
|
|
90
|
-
if (anchored.length > 0) {
|
|
91
|
-
let openEscalationIds;
|
|
92
|
-
try {
|
|
93
|
-
openEscalationIds = await getOpenInjectionUnverifiedEscalationIds();
|
|
94
|
-
}
|
|
95
|
-
catch (err) {
|
|
96
|
-
log.warn(`[close-loop-dispatcher] Failed to fetch open injection_unverified escalations ` +
|
|
97
|
-
`for focus ${focus.focus_id.slice(0, 8)}: ${err.message}`);
|
|
98
|
-
openEscalationIds = new Set();
|
|
99
|
-
}
|
|
100
|
-
let snapshots = [];
|
|
101
|
-
try {
|
|
102
|
-
const result = await callApi('reality_tree_focus_injections', { focusId: focus.focus_id });
|
|
103
|
-
snapshots = result.items ?? [];
|
|
104
|
-
}
|
|
105
|
-
catch (err) {
|
|
106
|
-
log.warn(`[close-loop-dispatcher] Failed to fetch focus injections for ${focus.focus_id.slice(0, 8)}: ` +
|
|
107
|
-
`${err.message}`);
|
|
108
|
-
}
|
|
109
|
-
for (const item of snapshots) {
|
|
110
|
-
statusById.set(item.injection.id, item.injection.injectionStatus);
|
|
111
|
-
const treeId = item.injection.realityTreeId;
|
|
112
|
-
if (typeof treeId === 'string' && treeId.length > 0) {
|
|
113
|
-
treeIdByInjection.set(item.injection.id, treeId);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
for (const delivery of anchored) {
|
|
117
|
-
const injectionId = delivery.injectionId;
|
|
118
|
-
states.push({
|
|
119
|
-
injectionId,
|
|
120
|
-
injectionStatus: statusById.get(injectionId) ?? null,
|
|
121
|
-
hasOpenUnverifiedEscalation: openEscalationIds.has(injectionId),
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
const bookkeeperHasRun = closeLoopBookkeeperSpawned.has(focus.focus_id);
|
|
126
|
-
const decision = decideCloseLoop(deliveries, states, bookkeeperHasRun);
|
|
127
|
-
switch (decision.kind) {
|
|
128
|
-
case 'short_circuit_to_done': {
|
|
129
|
-
try {
|
|
130
|
-
await updateFocusStage(focus.focus_id, 'done');
|
|
131
|
-
log.info(`[close-loop-dispatcher] Focus "${focus.focus_name}" has no anchoring injections -- ` +
|
|
132
|
-
`short-circuiting close_loop -> done`);
|
|
133
|
-
}
|
|
134
|
-
catch (err) {
|
|
135
|
-
log.warn(`[close-loop-dispatcher] Failed to short-circuit focus ${focus.focus_id.slice(0, 8)} to done: ` +
|
|
136
|
-
`${err.message}`);
|
|
137
|
-
}
|
|
138
|
-
closeLoopBookkeeperSpawned.delete(focus.focus_id);
|
|
139
|
-
return decision;
|
|
140
|
-
}
|
|
141
|
-
case 'advance_to_done': {
|
|
142
|
-
// Focus-level injection sweep + stage advance. The helper realizes
|
|
143
|
-
// anchored injections whose anchoring deliveries closed successfully
|
|
144
|
-
// but whose status hasn't yet flipped to 'verified' (e.g. deliveries
|
|
145
|
-
// that exited via review instead of the per-delivery verify gate),
|
|
146
|
-
// then advances close_loop -> done. On sweep failure, the helper
|
|
147
|
-
// returns early WITHOUT calling updateFocusStage -- the focus stays
|
|
148
|
-
// at close_loop and the next poll cycle retries.
|
|
149
|
-
const outcome = await advanceFocusToDoneWithSweep(focus, anchored, statusById, { verify: buildWritebackVerify(treeIdByInjection), updateFocusStage });
|
|
150
|
-
// Clear the bookkeeper-spawned flag only when the focus has
|
|
151
|
-
// terminally exited close_loop -- either advanced, or attempted to
|
|
152
|
-
// advance and the stage-update itself failed. A sweep failure leaves
|
|
153
|
-
// the flag in place so the same bookkeeper context is reused on the
|
|
154
|
-
// next poll cycle's retry.
|
|
155
|
-
if (outcome.advanced || outcome.advanceError) {
|
|
156
|
-
closeLoopBookkeeperSpawned.delete(focus.focus_id);
|
|
157
|
-
}
|
|
158
|
-
return decision;
|
|
159
|
-
}
|
|
160
|
-
case 'spawn_bookkeeper': {
|
|
161
|
-
const team = getActiveTeams().get(focus.focus_id);
|
|
162
|
-
const worktreePath = team?.worktreePath ?? null;
|
|
163
|
-
try {
|
|
164
|
-
const message = await assembleDirectiveContent(config, focus.focus_id, decision.directive, worktreePath);
|
|
165
|
-
const contentHash = computeDirectiveHash(decision.directive);
|
|
166
|
-
pendingSpawnDirectives.set(focus.focus_id, {
|
|
167
|
-
message,
|
|
168
|
-
model: decision.directive.model ?? CLOSE_LOOP_MODEL,
|
|
169
|
-
// The close_loop bookkeeper is a fixed-purpose one-shot session pinned
|
|
170
|
-
// to CLOSE_LOOP_MODEL; the focus selector must NOT override it. Null
|
|
171
|
-
// override leaves the model slot (CLOSE_LOOP_MODEL) winning.
|
|
172
|
-
focusOverrideModel: null,
|
|
173
|
-
sessionType: 'coding',
|
|
174
|
-
// The close_loop bookkeeper is a distinct one-shot session: its own
|
|
175
|
-
// lineage slot, always fresh -- it must not resume (or clobber) the
|
|
176
|
-
// coder's context (INJ-B).
|
|
177
|
-
lineage: 'close_loop',
|
|
178
|
-
continuity: 'fresh',
|
|
179
|
-
contentHash,
|
|
180
|
-
});
|
|
181
|
-
if (team) {
|
|
182
|
-
terminateTeam(focus.focus_id, 'handoff');
|
|
183
|
-
await waitForTeamExit(focus.focus_id, 30000);
|
|
184
|
-
}
|
|
185
|
-
closeLoopBookkeeperSpawned.add(focus.focus_id);
|
|
186
|
-
log.info(`[close-loop-dispatcher] Spawned bookkeeper team for focus "${focus.focus_name}" ` +
|
|
187
|
-
`(${anchored.length} anchored injection(s), model: ${CLOSE_LOOP_MODEL})`);
|
|
188
|
-
}
|
|
189
|
-
catch (err) {
|
|
190
|
-
log.warn(`[close-loop-dispatcher] Failed to spawn bookkeeper for focus ${focus.focus_id.slice(0, 8)}: ` +
|
|
191
|
-
`${err.message}`);
|
|
192
|
-
}
|
|
193
|
-
return decision;
|
|
194
|
-
}
|
|
195
|
-
case 'hold': {
|
|
196
|
-
log.info(`[close-loop-dispatcher] Focus "${focus.focus_name}" held at close_loop: ${decision.reason}`);
|
|
197
|
-
return decision;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Pure helper exported for tests: build the close_loop directive and verify
|
|
203
|
-
* it carries the expected assembly + model. The buildCloseLoopDirective
|
|
204
|
-
* function in close-loop-dispatcher.ts already does this; this is a
|
|
205
|
-
* dispatcher-side smoke export so the tests can grep through it.
|
|
206
|
-
*/
|
|
207
|
-
export function getCloseLoopDirectiveForTesting() {
|
|
208
|
-
return buildCloseLoopDirective();
|
|
209
|
-
}
|
|
210
|
-
//# sourceMappingURL=close-loop-stage.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"close-loop-stage.js","sourceRoot":"","sources":["../../src/directive/close-loop-stage.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Focus review-convergence transitions (extracted from focus-stage-lifecycle).
|
|
3
|
-
*
|
|
4
|
-
* Owns the two review-axis daemon-side transitions:
|
|
5
|
-
* verify -> review (Issue 4: auto-enter review on verify-stage entry)
|
|
6
|
-
* review -> close_loop (Issue 2 + 3: state-based convergence / nonconvergent
|
|
7
|
-
* escalation)
|
|
8
|
-
*
|
|
9
|
-
* Plus the pure helpers (`hasReviewConverged`, `getReviewIterationCap`,
|
|
10
|
-
* `hasInFlightRemediations`) these transitions rely on.
|
|
11
|
-
*
|
|
12
|
-
* @module focus-stage-lifecycle/review-convergence
|
|
13
|
-
*/
|
|
14
|
-
import type { PipelineConfig } from '../types/index.js';
|
|
15
|
-
import { getLatestCompletedFocusReview as defaultGetLatestCompletedFocusReview, getInFlightRemediationCount as defaultGetInFlightRemediationCount, getFocusReviewIterationCount as defaultGetFocusReviewIterationCount, updateFocusStage as defaultUpdateFocusStage } from '../queries/focuses.js';
|
|
16
|
-
import { createEscalation as defaultCreateEscalation } from '../queries/issues.js';
|
|
17
|
-
import { emitLoopTrigger } from '../loops/index.js';
|
|
18
|
-
/**
|
|
19
|
-
* Default cap on review iterations before the daemon files a
|
|
20
|
-
* `review_nonconvergent` escalation and pauses the focus.
|
|
21
|
-
*
|
|
22
|
-
* Configurable per focus via `pipeline_config.review.maxIterations`.
|
|
23
|
-
*/
|
|
24
|
-
export declare const DEFAULT_REVIEW_MAX_ITERATIONS = 3;
|
|
25
|
-
/**
|
|
26
|
-
* Decide whether a focus has converged out of review. Convergence is
|
|
27
|
-
* deterministic and state-based:
|
|
28
|
-
*
|
|
29
|
-
* - If there are no in-flight remediation deliveries created during/after
|
|
30
|
-
* the latest review pass, the focus has converged.
|
|
31
|
-
*
|
|
32
|
-
* Pure function: takes the in-flight count and returns the decision.
|
|
33
|
-
* Exported so tests can drive it with synthetic counts.
|
|
34
|
-
*/
|
|
35
|
-
export declare function hasReviewConverged(inFlightRemediationCount: number): boolean;
|
|
36
|
-
/**
|
|
37
|
-
* Compute the configured review iteration cap for a focus. Pulls
|
|
38
|
-
* `pipeline_config.review.maxIterations` and falls back to
|
|
39
|
-
* `DEFAULT_REVIEW_MAX_ITERATIONS` when unset or non-positive.
|
|
40
|
-
*
|
|
41
|
-
* Exported for unit testing.
|
|
42
|
-
*/
|
|
43
|
-
export declare function getReviewIterationCap(pipelineConfig: PipelineConfig | null | undefined): number;
|
|
44
|
-
/**
|
|
45
|
-
* Returns true when the focus has any in-flight remediation deliveries
|
|
46
|
-
* created during/after the latest review pass.
|
|
47
|
-
*
|
|
48
|
-
* "Latest review pass" is the `started_at` of the most recent focus_reviews
|
|
49
|
-
* row; when no review has run yet, this returns false (no remediation
|
|
50
|
-
* deliveries can exist before a review pass).
|
|
51
|
-
*
|
|
52
|
-
* In-flight means execution_status is NOT one of done/cancelled/verify.
|
|
53
|
-
*
|
|
54
|
-
* Exported for unit testing the convergence query independently of the
|
|
55
|
-
* surrounding transition logic.
|
|
56
|
-
*/
|
|
57
|
-
export declare function hasInFlightRemediations(focusId: string, latestReviewPassTimestamp: string | null, deps?: {
|
|
58
|
-
getInFlightRemediationCount: typeof defaultGetInFlightRemediationCount;
|
|
59
|
-
}): Promise<boolean>;
|
|
60
|
-
/**
|
|
61
|
-
* Dependencies for `advanceVerifyToReviewIfEnabled`. Defaults reach for the
|
|
62
|
-
* real query module; tests can pass stub implementations.
|
|
63
|
-
*/
|
|
64
|
-
export interface VerifyToReviewDeps {
|
|
65
|
-
updateFocusStage: typeof defaultUpdateFocusStage;
|
|
66
|
-
emitLoopTrigger: typeof emitLoopTrigger;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* When a focus's stage is `verify` and auto-review is enabled, advance it to
|
|
70
|
-
* `review`. Auto-review is default-on (`isReviewEnabled`): only an explicit
|
|
71
|
-
* `pipeline_config.review.enabled === false` keeps the focus at `verify` until
|
|
72
|
-
* a human signals review. A focus with no stored review key auto-advances.
|
|
73
|
-
*
|
|
74
|
-
* Returns true when an advance was performed.
|
|
75
|
-
*/
|
|
76
|
-
export declare function advanceVerifyToReviewIfEnabled(focus: {
|
|
77
|
-
focusId: string;
|
|
78
|
-
focusName: string;
|
|
79
|
-
stage: string;
|
|
80
|
-
pipelineConfig: PipelineConfig | null;
|
|
81
|
-
}, deps?: VerifyToReviewDeps): Promise<boolean>;
|
|
82
|
-
/**
|
|
83
|
-
* Dependencies for `advanceReviewToCloseLoopIfConverged`. Defaults reach for
|
|
84
|
-
* the real query modules; tests can pass stub implementations.
|
|
85
|
-
*/
|
|
86
|
-
export interface ReviewConvergeDeps {
|
|
87
|
-
getLatestCompletedFocusReview: typeof defaultGetLatestCompletedFocusReview;
|
|
88
|
-
getInFlightRemediationCount: typeof defaultGetInFlightRemediationCount;
|
|
89
|
-
getFocusReviewIterationCount: typeof defaultGetFocusReviewIterationCount;
|
|
90
|
-
updateFocusStage: typeof defaultUpdateFocusStage;
|
|
91
|
-
createEscalation: typeof defaultCreateEscalation;
|
|
92
|
-
emitLoopTrigger: typeof emitLoopTrigger;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Outcome of an attempt to advance a focus past the review stage.
|
|
96
|
-
*/
|
|
97
|
-
export type ReviewTransitionOutcome = {
|
|
98
|
-
kind: 'no_action';
|
|
99
|
-
reason: string;
|
|
100
|
-
} | {
|
|
101
|
-
kind: 'converged';
|
|
102
|
-
from: 'review';
|
|
103
|
-
to: 'close_loop';
|
|
104
|
-
} | {
|
|
105
|
-
kind: 'nonconvergent';
|
|
106
|
-
iterations: number;
|
|
107
|
-
cap: number;
|
|
108
|
-
};
|
|
109
|
-
/**
|
|
110
|
-
* Evaluate the review -> close_loop transition for a focus and apply the
|
|
111
|
-
* appropriate action. Returns a structured outcome so callers can log /
|
|
112
|
-
* test the decision path.
|
|
113
|
-
*
|
|
114
|
-
* Logic:
|
|
115
|
-
* 1. If stage is not `review`, do nothing.
|
|
116
|
-
* 2. If the review iteration count is >= the cap, file a
|
|
117
|
-
* `review_nonconvergent` escalation (idempotent best-effort) and
|
|
118
|
-
* LEAVE the focus stage at `review`. The focus is paused until a
|
|
119
|
-
* human intervenes.
|
|
120
|
-
* 3. Otherwise, check in-flight remediations against the latest review
|
|
121
|
-
* pass. If zero, advance stage `review -> close_loop`.
|
|
122
|
-
* 4. Otherwise, do nothing -- remediations are still in flight.
|
|
123
|
-
*
|
|
124
|
-
* Exported with injectable deps for unit testing.
|
|
125
|
-
*/
|
|
126
|
-
export declare function advanceReviewToCloseLoopIfConverged(focus: {
|
|
127
|
-
focusId: string;
|
|
128
|
-
focusName: string;
|
|
129
|
-
organizationId: string;
|
|
130
|
-
stage: string;
|
|
131
|
-
pipelineConfig: PipelineConfig | null;
|
|
132
|
-
}, deps?: ReviewConvergeDeps): Promise<ReviewTransitionOutcome>;
|
|
133
|
-
//# sourceMappingURL=review-convergence.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"review-convergence.d.ts","sourceRoot":"","sources":["../../src/focus-stage-lifecycle/review-convergence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EACL,6BAA6B,IAAI,oCAAoC,EACrE,2BAA2B,IAAI,kCAAkC,EACjE,4BAA4B,IAAI,mCAAmC,EACnE,gBAAgB,IAAI,uBAAuB,EAC5C,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,gBAAgB,IAAI,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAOnF,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AASpD;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,IAAI,CAAC;AAI/C;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,wBAAwB,EAAE,MAAM,GAAG,OAAO,CAE5E;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,cAAc,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,GAChD,MAAM,CAMR;AAID;;;;;;;;;;;;GAYG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,yBAAyB,EAAE,MAAM,GAAG,IAAI,EACxC,IAAI,GAAE;IACJ,2BAA2B,EAAE,OAAO,kCAAkC,CAAC;CACF,GACtE,OAAO,CAAC,OAAO,CAAC,CASlB;AAID;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,gBAAgB,EAAE,OAAO,uBAAuB,CAAC;IACjD,eAAe,EAAE,OAAO,eAAe,CAAC;CACzC;AAOD;;;;;;;GAOG;AACH,wBAAsB,8BAA8B,CAClD,KAAK,EAAE;IACL,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,cAAc,GAAG,IAAI,CAAC;CACvC,EACD,IAAI,GAAE,kBAA8C,GACnD,OAAO,CAAC,OAAO,CAAC,CAyBlB;AAID;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,6BAA6B,EAAE,OAAO,oCAAoC,CAAC;IAC3E,2BAA2B,EAAE,OAAO,kCAAkC,CAAC;IACvE,4BAA4B,EAAE,OAAO,mCAAmC,CAAC;IACzE,gBAAgB,EAAE,OAAO,uBAAuB,CAAC;IACjD,gBAAgB,EAAE,OAAO,uBAAuB,CAAC;IACjD,eAAe,EAAE,OAAO,eAAe,CAAC;CACzC;AAWD;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAC/B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,YAAY,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/D;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,mCAAmC,CACvD,KAAK,EAAE;IACL,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,cAAc,GAAG,IAAI,CAAC;CACvC,EACD,IAAI,GAAE,kBAA8C,GACnD,OAAO,CAAC,uBAAuB,CAAC,CAqHlC"}
|
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Focus review-convergence transitions (extracted from focus-stage-lifecycle).
|
|
3
|
-
*
|
|
4
|
-
* Owns the two review-axis daemon-side transitions:
|
|
5
|
-
* verify -> review (Issue 4: auto-enter review on verify-stage entry)
|
|
6
|
-
* review -> close_loop (Issue 2 + 3: state-based convergence / nonconvergent
|
|
7
|
-
* escalation)
|
|
8
|
-
*
|
|
9
|
-
* Plus the pure helpers (`hasReviewConverged`, `getReviewIterationCap`,
|
|
10
|
-
* `hasInFlightRemediations`) these transitions rely on.
|
|
11
|
-
*
|
|
12
|
-
* @module focus-stage-lifecycle/review-convergence
|
|
13
|
-
*/
|
|
14
|
-
import { getLatestCompletedFocusReview as defaultGetLatestCompletedFocusReview, getInFlightRemediationCount as defaultGetInFlightRemediationCount, getFocusReviewIterationCount as defaultGetFocusReviewIterationCount, updateFocusStage as defaultUpdateFocusStage, } from '../queries/focuses.js';
|
|
15
|
-
import { createEscalation as defaultCreateEscalation } from '../queries/issues.js';
|
|
16
|
-
import { ESCALATION_REASONS, ESCALATION_KINDS, ESCALATION_DISPOSITIONS, FOCUS_STAGE, } from '@telora/daemon-core';
|
|
17
|
-
import { emitLoopTrigger } from '../loops/index.js';
|
|
18
|
-
import { isReviewEnabled } from '../workflow/pipeline-config.js';
|
|
19
|
-
import { createLogger } from '../shell/log.js';
|
|
20
|
-
const log = createLogger({ module: 'focus-stage-lifecycle' });
|
|
21
|
-
// ── Constants ─────────────────────────────────────────────────────
|
|
22
|
-
/**
|
|
23
|
-
* Default cap on review iterations before the daemon files a
|
|
24
|
-
* `review_nonconvergent` escalation and pauses the focus.
|
|
25
|
-
*
|
|
26
|
-
* Configurable per focus via `pipeline_config.review.maxIterations`.
|
|
27
|
-
*/
|
|
28
|
-
export const DEFAULT_REVIEW_MAX_ITERATIONS = 3;
|
|
29
|
-
// ── Pure helpers (Issue 2 + Issue 3) ──────────────────────────────
|
|
30
|
-
/**
|
|
31
|
-
* Decide whether a focus has converged out of review. Convergence is
|
|
32
|
-
* deterministic and state-based:
|
|
33
|
-
*
|
|
34
|
-
* - If there are no in-flight remediation deliveries created during/after
|
|
35
|
-
* the latest review pass, the focus has converged.
|
|
36
|
-
*
|
|
37
|
-
* Pure function: takes the in-flight count and returns the decision.
|
|
38
|
-
* Exported so tests can drive it with synthetic counts.
|
|
39
|
-
*/
|
|
40
|
-
export function hasReviewConverged(inFlightRemediationCount) {
|
|
41
|
-
return inFlightRemediationCount <= 0;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Compute the configured review iteration cap for a focus. Pulls
|
|
45
|
-
* `pipeline_config.review.maxIterations` and falls back to
|
|
46
|
-
* `DEFAULT_REVIEW_MAX_ITERATIONS` when unset or non-positive.
|
|
47
|
-
*
|
|
48
|
-
* Exported for unit testing.
|
|
49
|
-
*/
|
|
50
|
-
export function getReviewIterationCap(pipelineConfig) {
|
|
51
|
-
const raw = pipelineConfig?.review?.maxIterations;
|
|
52
|
-
if (typeof raw !== 'number' || !Number.isFinite(raw) || raw <= 0) {
|
|
53
|
-
return DEFAULT_REVIEW_MAX_ITERATIONS;
|
|
54
|
-
}
|
|
55
|
-
return Math.floor(raw);
|
|
56
|
-
}
|
|
57
|
-
// ── Convergence check (Issue 2) ───────────────────────────────────
|
|
58
|
-
/**
|
|
59
|
-
* Returns true when the focus has any in-flight remediation deliveries
|
|
60
|
-
* created during/after the latest review pass.
|
|
61
|
-
*
|
|
62
|
-
* "Latest review pass" is the `started_at` of the most recent focus_reviews
|
|
63
|
-
* row; when no review has run yet, this returns false (no remediation
|
|
64
|
-
* deliveries can exist before a review pass).
|
|
65
|
-
*
|
|
66
|
-
* In-flight means execution_status is NOT one of done/cancelled/verify.
|
|
67
|
-
*
|
|
68
|
-
* Exported for unit testing the convergence query independently of the
|
|
69
|
-
* surrounding transition logic.
|
|
70
|
-
*/
|
|
71
|
-
export async function hasInFlightRemediations(focusId, latestReviewPassTimestamp, deps = { getInFlightRemediationCount: defaultGetInFlightRemediationCount }) {
|
|
72
|
-
if (!latestReviewPassTimestamp) {
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
const count = await deps.getInFlightRemediationCount(focusId, latestReviewPassTimestamp);
|
|
76
|
-
return count > 0;
|
|
77
|
-
}
|
|
78
|
-
const defaultVerifyToReviewDeps = {
|
|
79
|
-
updateFocusStage: defaultUpdateFocusStage,
|
|
80
|
-
emitLoopTrigger,
|
|
81
|
-
};
|
|
82
|
-
/**
|
|
83
|
-
* When a focus's stage is `verify` and auto-review is enabled, advance it to
|
|
84
|
-
* `review`. Auto-review is default-on (`isReviewEnabled`): only an explicit
|
|
85
|
-
* `pipeline_config.review.enabled === false` keeps the focus at `verify` until
|
|
86
|
-
* a human signals review. A focus with no stored review key auto-advances.
|
|
87
|
-
*
|
|
88
|
-
* Returns true when an advance was performed.
|
|
89
|
-
*/
|
|
90
|
-
export async function advanceVerifyToReviewIfEnabled(focus, deps = defaultVerifyToReviewDeps) {
|
|
91
|
-
if (focus.stage !== FOCUS_STAGE.VERIFY)
|
|
92
|
-
return false;
|
|
93
|
-
// Default-on: only an explicit review.enabled === false keeps the focus at
|
|
94
|
-
// verify. Missing / undefined review reads as enabled (mirrors isCiEnabled).
|
|
95
|
-
if (!isReviewEnabled(focus.pipelineConfig))
|
|
96
|
-
return false;
|
|
97
|
-
try {
|
|
98
|
-
await deps.updateFocusStage(focus.focusId, 'review');
|
|
99
|
-
log.info(`[focus-stage-lifecycle] Focus "${focus.focusName}" stage verify -> review ` +
|
|
100
|
-
`(auto: pipeline_config.review.enabled)`);
|
|
101
|
-
deps.emitLoopTrigger({
|
|
102
|
-
type: 'focus_status_changed',
|
|
103
|
-
focusId: focus.focusId,
|
|
104
|
-
detail: 'stage:verify->review',
|
|
105
|
-
});
|
|
106
|
-
return true;
|
|
107
|
-
}
|
|
108
|
-
catch (err) {
|
|
109
|
-
log.warn(`[focus-stage-lifecycle] Failed to advance "${focus.focusName}" ` +
|
|
110
|
-
`verify -> review: ${err.message}`);
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
const defaultReviewConvergeDeps = {
|
|
115
|
-
getLatestCompletedFocusReview: defaultGetLatestCompletedFocusReview,
|
|
116
|
-
getInFlightRemediationCount: defaultGetInFlightRemediationCount,
|
|
117
|
-
getFocusReviewIterationCount: defaultGetFocusReviewIterationCount,
|
|
118
|
-
updateFocusStage: defaultUpdateFocusStage,
|
|
119
|
-
createEscalation: defaultCreateEscalation,
|
|
120
|
-
emitLoopTrigger,
|
|
121
|
-
};
|
|
122
|
-
/**
|
|
123
|
-
* Evaluate the review -> close_loop transition for a focus and apply the
|
|
124
|
-
* appropriate action. Returns a structured outcome so callers can log /
|
|
125
|
-
* test the decision path.
|
|
126
|
-
*
|
|
127
|
-
* Logic:
|
|
128
|
-
* 1. If stage is not `review`, do nothing.
|
|
129
|
-
* 2. If the review iteration count is >= the cap, file a
|
|
130
|
-
* `review_nonconvergent` escalation (idempotent best-effort) and
|
|
131
|
-
* LEAVE the focus stage at `review`. The focus is paused until a
|
|
132
|
-
* human intervenes.
|
|
133
|
-
* 3. Otherwise, check in-flight remediations against the latest review
|
|
134
|
-
* pass. If zero, advance stage `review -> close_loop`.
|
|
135
|
-
* 4. Otherwise, do nothing -- remediations are still in flight.
|
|
136
|
-
*
|
|
137
|
-
* Exported with injectable deps for unit testing.
|
|
138
|
-
*/
|
|
139
|
-
export async function advanceReviewToCloseLoopIfConverged(focus, deps = defaultReviewConvergeDeps) {
|
|
140
|
-
if (focus.stage !== FOCUS_STAGE.REVIEW) {
|
|
141
|
-
return { kind: 'no_action', reason: 'stage_not_review' };
|
|
142
|
-
}
|
|
143
|
-
const cap = getReviewIterationCap(focus.pipelineConfig);
|
|
144
|
-
let iterations = 0;
|
|
145
|
-
try {
|
|
146
|
-
iterations = await deps.getFocusReviewIterationCount(focus.focusId);
|
|
147
|
-
}
|
|
148
|
-
catch (err) {
|
|
149
|
-
log.warn(`[focus-stage-lifecycle] Failed to read review iteration count for ` +
|
|
150
|
-
`"${focus.focusName}": ${err.message}`);
|
|
151
|
-
// On read failure, fall through to the convergence check rather than
|
|
152
|
-
// false-escalating; if iterations were actually high the next tick
|
|
153
|
-
// will catch it.
|
|
154
|
-
}
|
|
155
|
-
if (iterations >= cap) {
|
|
156
|
-
// N-iteration cap exceeded -- file an escalation and leave the focus
|
|
157
|
-
// at review. The escalation is best-effort; downstream dedup keeps it
|
|
158
|
-
// from spamming via the agent_escalations idx_kind_status partial index
|
|
159
|
-
// (status in ('pending', 'in_review')).
|
|
160
|
-
try {
|
|
161
|
-
await deps.createEscalation({
|
|
162
|
-
organizationId: focus.organizationId,
|
|
163
|
-
sessionId: '',
|
|
164
|
-
issueId: null,
|
|
165
|
-
reasonType: ESCALATION_REASONS.DECISION_NEEDED,
|
|
166
|
-
// The intent owner must decide: descope, extend the cap, or close.
|
|
167
|
-
disposition: ESCALATION_DISPOSITIONS.NEEDS_INTENT,
|
|
168
|
-
anchor: { focusId: focus.focusId },
|
|
169
|
-
description: `Focus "${focus.focusName}" has not converged after ${iterations} ` +
|
|
170
|
-
`review iteration(s) (cap: ${cap}). The review self-loop has been ` +
|
|
171
|
-
`paused; a human must decide whether to descope remaining gaps, ` +
|
|
172
|
-
`extend the cap via pipeline_config.review.maxIterations, or close ` +
|
|
173
|
-
`the focus.`,
|
|
174
|
-
escalationKind: ESCALATION_KINDS.REVIEW_NONCONVERGENT,
|
|
175
|
-
// Evidence only -- focus provenance lives on the columns via the anchor.
|
|
176
|
-
metadata: {
|
|
177
|
-
iterations,
|
|
178
|
-
cap,
|
|
179
|
-
},
|
|
180
|
-
});
|
|
181
|
-
log.warn(`[focus-stage-lifecycle] review_nonconvergent escalation filed for ` +
|
|
182
|
-
`"${focus.focusName}" (iterations=${iterations}, cap=${cap}); focus ` +
|
|
183
|
-
`remains at stage=review.`);
|
|
184
|
-
}
|
|
185
|
-
catch (err) {
|
|
186
|
-
log.warn(`[focus-stage-lifecycle] Failed to file review_nonconvergent ` +
|
|
187
|
-
`escalation for "${focus.focusName}": ${err.message}`);
|
|
188
|
-
}
|
|
189
|
-
return { kind: 'nonconvergent', iterations, cap };
|
|
190
|
-
}
|
|
191
|
-
// Look up the latest completed review's started_at. If no review has
|
|
192
|
-
// completed yet, there are no remediations created against it; treat as
|
|
193
|
-
// "not yet converged" rather than "converged on zero" -- the agent must
|
|
194
|
-
// produce a review before we can claim convergence.
|
|
195
|
-
let latestReviewStartedAt = null;
|
|
196
|
-
try {
|
|
197
|
-
const latest = await deps.getLatestCompletedFocusReview(focus.focusId);
|
|
198
|
-
latestReviewStartedAt = latest?.startedAt ?? null;
|
|
199
|
-
}
|
|
200
|
-
catch (err) {
|
|
201
|
-
log.warn(`[focus-stage-lifecycle] Failed to read latest focus_review for ` +
|
|
202
|
-
`"${focus.focusName}": ${err.message}`);
|
|
203
|
-
return { kind: 'no_action', reason: 'review_read_failed' };
|
|
204
|
-
}
|
|
205
|
-
if (!latestReviewStartedAt) {
|
|
206
|
-
return { kind: 'no_action', reason: 'no_completed_review_yet' };
|
|
207
|
-
}
|
|
208
|
-
let inFlightCount = 0;
|
|
209
|
-
try {
|
|
210
|
-
inFlightCount = await deps.getInFlightRemediationCount(focus.focusId, latestReviewStartedAt);
|
|
211
|
-
}
|
|
212
|
-
catch (err) {
|
|
213
|
-
log.warn(`[focus-stage-lifecycle] Failed to count in-flight remediations for ` +
|
|
214
|
-
`"${focus.focusName}": ${err.message}`);
|
|
215
|
-
return { kind: 'no_action', reason: 'remediation_query_failed' };
|
|
216
|
-
}
|
|
217
|
-
if (!hasReviewConverged(inFlightCount)) {
|
|
218
|
-
return { kind: 'no_action', reason: 'remediations_in_flight' };
|
|
219
|
-
}
|
|
220
|
-
try {
|
|
221
|
-
await deps.updateFocusStage(focus.focusId, 'close_loop');
|
|
222
|
-
log.info(`[focus-stage-lifecycle] Focus "${focus.focusName}" stage review -> ` +
|
|
223
|
-
`close_loop (converged after ${iterations} iteration(s))`);
|
|
224
|
-
deps.emitLoopTrigger({
|
|
225
|
-
type: 'focus_status_changed',
|
|
226
|
-
focusId: focus.focusId,
|
|
227
|
-
detail: 'stage:review->close_loop',
|
|
228
|
-
});
|
|
229
|
-
return { kind: 'converged', from: 'review', to: 'close_loop' };
|
|
230
|
-
}
|
|
231
|
-
catch (err) {
|
|
232
|
-
log.warn(`[focus-stage-lifecycle] Failed to advance "${focus.focusName}" ` +
|
|
233
|
-
`review -> close_loop: ${err.message}`);
|
|
234
|
-
return { kind: 'no_action', reason: 'stage_write_failed' };
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
//# sourceMappingURL=review-convergence.js.map
|