@telora/daemon 0.18.33 → 0.18.40
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/completion/event-escalations.d.ts.map +1 -1
- package/dist/completion/event-escalations.js +25 -17
- package/dist/completion/event-escalations.js.map +1 -1
- package/dist/completion/event.d.ts +3 -12
- package/dist/completion/event.d.ts.map +1 -1
- package/dist/completion/event.js +50 -31
- package/dist/completion/event.js.map +1 -1
- package/dist/completion/progress-snapshot.d.ts +35 -0
- package/dist/completion/progress-snapshot.d.ts.map +1 -0
- package/dist/completion/progress-snapshot.js +105 -0
- package/dist/completion/progress-snapshot.js.map +1 -0
- package/dist/listener.d.ts.map +1 -1
- package/dist/listener.js +9 -0
- package/dist/listener.js.map +1 -1
- package/dist/nexus-incoming.d.ts +8 -7
- package/dist/nexus-incoming.d.ts.map +1 -1
- package/dist/nexus-incoming.js +10 -12
- package/dist/nexus-incoming.js.map +1 -1
- package/dist/nexus-intake-spawn.d.ts +57 -0
- package/dist/nexus-intake-spawn.d.ts.map +1 -0
- package/dist/nexus-intake-spawn.js +158 -0
- package/dist/nexus-intake-spawn.js.map +1 -0
- package/dist/nexus-intake.d.ts +94 -0
- package/dist/nexus-intake.d.ts.map +1 -0
- package/dist/nexus-intake.js +160 -0
- package/dist/nexus-intake.js.map +1 -0
- package/dist/queries/focuses.d.ts.map +1 -1
- package/dist/queries/focuses.js +1 -0
- package/dist/queries/focuses.js.map +1 -1
- package/dist/queries/issues.d.ts +12 -0
- package/dist/queries/issues.d.ts.map +1 -1
- package/dist/queries/issues.js +27 -0
- package/dist/queries/issues.js.map +1 -1
- package/dist/queries/schemas.d.ts +2 -0
- package/dist/queries/schemas.d.ts.map +1 -1
- package/dist/queries/schemas.js +1 -0
- package/dist/queries/schemas.js.map +1 -1
- package/dist/spawn-sandbox.d.ts +12 -8
- package/dist/spawn-sandbox.d.ts.map +1 -1
- package/dist/spawn-sandbox.js +25 -17
- package/dist/spawn-sandbox.js.map +1 -1
- package/dist/stall-detector-config.d.ts +24 -0
- package/dist/stall-detector-config.d.ts.map +1 -0
- package/dist/stall-detector-config.js +44 -0
- package/dist/stall-detector-config.js.map +1 -0
- package/dist/stall-detectors.d.ts +108 -0
- package/dist/stall-detectors.d.ts.map +1 -0
- package/dist/stall-detectors.js +225 -0
- package/dist/stall-detectors.js.map +1 -0
- package/dist/state-cascade.d.ts.map +1 -1
- package/dist/state-cascade.js +21 -0
- package/dist/state-cascade.js.map +1 -1
- package/dist/types/focus.d.ts +21 -0
- package/dist/types/focus.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/spawn-sandbox.js
CHANGED
|
@@ -40,6 +40,17 @@ export class SandboxUnavailableError extends Error {
|
|
|
40
40
|
}
|
|
41
41
|
/** Default bubblewrap executable name (overridable via deps for tests). */
|
|
42
42
|
export const BWRAP_COMMAND = 'bwrap';
|
|
43
|
+
/**
|
|
44
|
+
* One-shot guard for the `sandbox.off_bwrap_available` hint. resolveSandboxMode
|
|
45
|
+
* runs inside wrapSpawnCommand on EVERY spawn, but the bwrap recommendation is a
|
|
46
|
+
* single calm hint, not a per-spawn alarm -- so the health event is emitted at
|
|
47
|
+
* most once per process. Reset via resetBwrapHintForTests() in tests.
|
|
48
|
+
*/
|
|
49
|
+
let bwrapHintEmitted = false;
|
|
50
|
+
/** Test-only: reset the one-shot bwrap-hint guard so emission can be re-exercised. */
|
|
51
|
+
export function resetBwrapHintForTests() {
|
|
52
|
+
bwrapHintEmitted = false;
|
|
53
|
+
}
|
|
43
54
|
/** Return true if an executable name/path exists on the host PATH. */
|
|
44
55
|
export function commandOnPath(command, env = process.env) {
|
|
45
56
|
if (isAbsolute(command) || command.includes('/'))
|
|
@@ -59,8 +70,10 @@ export function isBwrapAvailable(deps = {}) {
|
|
|
59
70
|
* - explicit config.sandbox.mode wins (and is honored even if unavailable --
|
|
60
71
|
* buildSandboxCommand then fail-closes).
|
|
61
72
|
* - unset + darwin => 'off' (dev; bwrap is Linux-only).
|
|
62
|
-
* - unset + linux => 'off' (safe default), with a
|
|
63
|
-
* when bwrap is available.
|
|
73
|
+
* - unset + linux => 'off' (safe default), with a ONE-SHOT `info` health event
|
|
74
|
+
* emitted (once per process) when bwrap is available. `info` keeps health
|
|
75
|
+
* "healthy" (only unresolved warn/error degrade it) while staying visible on
|
|
76
|
+
* the card. Operators opt in explicitly after per-host validation
|
|
64
77
|
* -- see docs/runbook-daemon-service-user.md.
|
|
65
78
|
*/
|
|
66
79
|
export function resolveSandboxMode(sandbox, deps = {}) {
|
|
@@ -77,9 +90,11 @@ export function resolveSandboxMode(sandbox, deps = {}) {
|
|
|
77
90
|
if (deps.logRecommendation) {
|
|
78
91
|
deps.logRecommendation(message);
|
|
79
92
|
}
|
|
80
|
-
else {
|
|
93
|
+
else if (!bwrapHintEmitted) {
|
|
94
|
+
// One-shot: emit a single calm info hint per process, not per spawn.
|
|
95
|
+
bwrapHintEmitted = true;
|
|
81
96
|
healthEvents.emit({
|
|
82
|
-
severity: '
|
|
97
|
+
severity: 'info',
|
|
83
98
|
source: 'spawn-sandbox',
|
|
84
99
|
code: 'sandbox.off_bwrap_available',
|
|
85
100
|
message,
|
|
@@ -91,9 +106,10 @@ export function resolveSandboxMode(sandbox, deps = {}) {
|
|
|
91
106
|
/**
|
|
92
107
|
* Build the (possibly sandbox-wrapped) command + args for a spawn.
|
|
93
108
|
*
|
|
94
|
-
* mode 'off' -> returns the inner command unchanged
|
|
95
|
-
*
|
|
96
|
-
*
|
|
109
|
+
* mode 'off' -> returns the inner command unchanged. The unconfined default is
|
|
110
|
+
* the normal posture (not an operational fault), so it emits NO
|
|
111
|
+
* health event -- a host where confinement cannot be enabled stays
|
|
112
|
+
* silent and healthy.
|
|
97
113
|
* mode 'bwrap' -> if bwrap is unavailable, THROWS SandboxUnavailableError
|
|
98
114
|
* (fail-closed); otherwise returns a bwrap invocation that binds
|
|
99
115
|
* the worktree read-write, the readOnlyPaths read-only, system
|
|
@@ -103,7 +119,6 @@ export function buildSandboxCommand(opts) {
|
|
|
103
119
|
const { command, args, policy, mode, bwrapAvailable } = opts;
|
|
104
120
|
const bwrap = opts.bwrapCommand ?? BWRAP_COMMAND;
|
|
105
121
|
if (mode === 'off') {
|
|
106
|
-
opts.onUnconfined?.('spawn sandbox is OFF -- the agent runs with the daemon user\'s full filesystem authority');
|
|
107
122
|
return { command, args: [...args] };
|
|
108
123
|
}
|
|
109
124
|
// mode === 'bwrap'
|
|
@@ -151,7 +166,8 @@ export function buildSandboxCommand(opts) {
|
|
|
151
166
|
/**
|
|
152
167
|
* High-level helper used by the spawn path: resolve mode from config, derive the
|
|
153
168
|
* read-only allowlist (toolchain + model-auth config), and build the wrapped
|
|
154
|
-
* command.
|
|
169
|
+
* command. resolveSandboxMode emits the one-shot `info` bwrap hint; the
|
|
170
|
+
* unconfined ('off') path emits NO health event. Refuses (throws) when an
|
|
155
171
|
* explicitly-required sandbox is unavailable.
|
|
156
172
|
*/
|
|
157
173
|
export function wrapSpawnCommand(config, spawn) {
|
|
@@ -164,14 +180,6 @@ export function wrapSpawnCommand(config, spawn) {
|
|
|
164
180
|
policy: { worktreePath: spawn.worktreePath, readOnlyPaths },
|
|
165
181
|
mode,
|
|
166
182
|
bwrapAvailable,
|
|
167
|
-
onUnconfined: (reason) => {
|
|
168
|
-
healthEvents.emit({
|
|
169
|
-
severity: 'warn',
|
|
170
|
-
source: 'spawn-sandbox',
|
|
171
|
-
code: 'sandbox.unconfined',
|
|
172
|
-
message: reason,
|
|
173
|
-
});
|
|
174
|
-
},
|
|
175
183
|
});
|
|
176
184
|
}
|
|
177
185
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spawn-sandbox.js","sourceRoot":"","sources":["../src/spawn-sandbox.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD,kFAAkF;AAClF,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAChD,YAAY,MAAc;QACxB,KAAK,CAAC,qEAAqE,MAAM,EAAE,CAAC,CAAC;QACrF,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACxC,CAAC;CACF;AAED,2EAA2E;AAC3E,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAAC;AAErC,sEAAsE;AACtE,MAAM,UAAU,aAAa,CAAC,OAAe,EAAE,MAAyB,OAAO,CAAC,GAAG;IACjF,IAAI,UAAU,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,UAAU,CAAC,OAAO,CAAC,CAAC;IAC7E,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;IAC/B,OAAO,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;AAC1F,CAAC;AAED,4CAA4C;AAC5C,MAAM,UAAU,gBAAgB,CAC9B,OAA4E,EAAE;IAE9E,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,IAAI,aAAa,CAAC;IAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,IAAI,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;AACrB,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"spawn-sandbox.js","sourceRoot":"","sources":["../src/spawn-sandbox.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single in-daemon source for the two stall-detector thresholds.
|
|
3
|
+
*
|
|
4
|
+
* The CANONICAL declarations (Zod schema, ENV_VAR_METADATA discoverability
|
|
5
|
+
* entry, default, description) live in packages/daemon-core/src/env-config.ts --
|
|
6
|
+
* that module is the source of truth across the system. They are resolved here
|
|
7
|
+
* directly from process.env rather than through loadEnvConfig() for the same
|
|
8
|
+
* reason listener-auto-advance.ts inlines its daemon-core constants: the daemon
|
|
9
|
+
* consumes the registry-PUBLISHED daemon-core, so a typed
|
|
10
|
+
* loadEnvConfig().TELORA_VERIFY_FAILED_ITERATION_CAP access cannot compile until
|
|
11
|
+
* a daemon-core publish carrying the new keys lands. Centralizing the two reads
|
|
12
|
+
* + defaults in ONE module (instead of duplicating inline defaults at each call
|
|
13
|
+
* site) keeps the daemon-side mirror single-sourced; the names + defaults below
|
|
14
|
+
* MUST stay in sync with env-config.ts (positiveInt(5) / positiveFloat(6)).
|
|
15
|
+
*/
|
|
16
|
+
/** Default for TELORA_VERIFY_FAILED_ITERATION_CAP (mirror of env-config positiveInt(5)). */
|
|
17
|
+
export declare const VERIFY_FAILED_ITERATION_CAP_DEFAULT = 5;
|
|
18
|
+
/** Default for TELORA_REVIEW_WINDOW_STALE_HOURS (mirror of env-config positiveFloat(6)). */
|
|
19
|
+
export declare const REVIEW_WINDOW_STALE_HOURS_DEFAULT = 6;
|
|
20
|
+
/** Resolve the verify_failed re-code cap (env override or default). */
|
|
21
|
+
export declare function resolveVerifyFailedCap(): number;
|
|
22
|
+
/** Resolve the review-window staleness threshold in hours (env override or default). */
|
|
23
|
+
export declare function resolveReviewWindowStaleHours(): number;
|
|
24
|
+
//# sourceMappingURL=stall-detector-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stall-detector-config.d.ts","sourceRoot":"","sources":["../src/stall-detector-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,4FAA4F;AAC5F,eAAO,MAAM,mCAAmC,IAAI,CAAC;AACrD,4FAA4F;AAC5F,eAAO,MAAM,iCAAiC,IAAI,CAAC;AAkBnD,uEAAuE;AACvE,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED,wFAAwF;AACxF,wBAAgB,6BAA6B,IAAI,MAAM,CAEtD"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single in-daemon source for the two stall-detector thresholds.
|
|
3
|
+
*
|
|
4
|
+
* The CANONICAL declarations (Zod schema, ENV_VAR_METADATA discoverability
|
|
5
|
+
* entry, default, description) live in packages/daemon-core/src/env-config.ts --
|
|
6
|
+
* that module is the source of truth across the system. They are resolved here
|
|
7
|
+
* directly from process.env rather than through loadEnvConfig() for the same
|
|
8
|
+
* reason listener-auto-advance.ts inlines its daemon-core constants: the daemon
|
|
9
|
+
* consumes the registry-PUBLISHED daemon-core, so a typed
|
|
10
|
+
* loadEnvConfig().TELORA_VERIFY_FAILED_ITERATION_CAP access cannot compile until
|
|
11
|
+
* a daemon-core publish carrying the new keys lands. Centralizing the two reads
|
|
12
|
+
* + defaults in ONE module (instead of duplicating inline defaults at each call
|
|
13
|
+
* site) keeps the daemon-side mirror single-sourced; the names + defaults below
|
|
14
|
+
* MUST stay in sync with env-config.ts (positiveInt(5) / positiveFloat(6)).
|
|
15
|
+
*/
|
|
16
|
+
/** Default for TELORA_VERIFY_FAILED_ITERATION_CAP (mirror of env-config positiveInt(5)). */
|
|
17
|
+
export const VERIFY_FAILED_ITERATION_CAP_DEFAULT = 5;
|
|
18
|
+
/** Default for TELORA_REVIEW_WINDOW_STALE_HOURS (mirror of env-config positiveFloat(6)). */
|
|
19
|
+
export const REVIEW_WINDOW_STALE_HOURS_DEFAULT = 6;
|
|
20
|
+
/** Parse a positive-int env var, falling back to the default on absent/invalid. */
|
|
21
|
+
function envPositiveInt(name, def) {
|
|
22
|
+
const raw = process.env[name];
|
|
23
|
+
if (raw === undefined || raw === '')
|
|
24
|
+
return def;
|
|
25
|
+
const n = Number(raw);
|
|
26
|
+
return Number.isFinite(n) && n > 0 ? Math.floor(n) : def;
|
|
27
|
+
}
|
|
28
|
+
/** Parse a positive-float env var, falling back to the default on absent/invalid. */
|
|
29
|
+
function envPositiveFloat(name, def) {
|
|
30
|
+
const raw = process.env[name];
|
|
31
|
+
if (raw === undefined || raw === '')
|
|
32
|
+
return def;
|
|
33
|
+
const n = Number(raw);
|
|
34
|
+
return Number.isFinite(n) && n > 0 ? n : def;
|
|
35
|
+
}
|
|
36
|
+
/** Resolve the verify_failed re-code cap (env override or default). */
|
|
37
|
+
export function resolveVerifyFailedCap() {
|
|
38
|
+
return envPositiveInt('TELORA_VERIFY_FAILED_ITERATION_CAP', VERIFY_FAILED_ITERATION_CAP_DEFAULT);
|
|
39
|
+
}
|
|
40
|
+
/** Resolve the review-window staleness threshold in hours (env override or default). */
|
|
41
|
+
export function resolveReviewWindowStaleHours() {
|
|
42
|
+
return envPositiveFloat('TELORA_REVIEW_WINDOW_STALE_HOURS', REVIEW_WINDOW_STALE_HOURS_DEFAULT);
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=stall-detector-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stall-detector-config.js","sourceRoot":"","sources":["../src/stall-detector-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,4FAA4F;AAC5F,MAAM,CAAC,MAAM,mCAAmC,GAAG,CAAC,CAAC;AACrD,4FAA4F;AAC5F,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC,CAAC;AAEnD,mFAAmF;AACnF,SAAS,cAAc,CAAC,IAAY,EAAE,GAAW;IAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,GAAG,CAAC;IAChD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACtB,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AAC3D,CAAC;AAED,qFAAqF;AACrF,SAAS,gBAAgB,CAAC,IAAY,EAAE,GAAW;IACjD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,GAAG,CAAC;IAChD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACtB,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AAC/C,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,sBAAsB;IACpC,OAAO,cAAc,CAAC,oCAAoC,EAAE,mCAAmC,CAAC,CAAC;AACnG,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,6BAA6B;IAC3C,OAAO,gBAAgB,CAAC,kCAAkC,EAAE,iCAAiC,CAAC,CAAC;AACjG,CAAC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Targeted daemon-owned stall detectors.
|
|
3
|
+
*
|
|
4
|
+
* The lifecycle-aware no-progress predicate (completion/event.ts) makes the
|
|
5
|
+
* generic watchdog PATIENT: it no longer trips on healthy review/verify/requeue
|
|
6
|
+
* latency. That patience is only safe if the genuinely-wedged daemon-owned
|
|
7
|
+
* cases it would otherwise have (noisily) surfaced are caught by dedicated
|
|
8
|
+
* detectors with their own actionable reasons. This module is that safety
|
|
9
|
+
* backfill:
|
|
10
|
+
*
|
|
11
|
+
* 1. verify_failed cycle cap -- a delivery whose verify_failed re-code cycle
|
|
12
|
+
* count (iteration_count) exceeds TELORA_VERIFY_FAILED_ITERATION_CAP is
|
|
13
|
+
* stuck in an unbounded re-code loop the verify gate will never clear. File
|
|
14
|
+
* ONE verify_gate_unclearable escalation (needs_operator) carrying the
|
|
15
|
+
* gate_state / last failing condition -- not a generic no-progress ping.
|
|
16
|
+
*
|
|
17
|
+
* 2. review-window staleness -- a focus stuck in the `reviewing` phase whose
|
|
18
|
+
* review_requested_at has sat past TELORA_REVIEW_WINDOW_STALE_HOURS with no
|
|
19
|
+
* active review team (a completed review would have cleared
|
|
20
|
+
* review_requested_at) is wedged. File ONE review_window_stalled escalation
|
|
21
|
+
* (needs_operator).
|
|
22
|
+
*
|
|
23
|
+
* BOTH detectors are PER-FOCUS and operate on state the caller already has --
|
|
24
|
+
* they do NOT fan out their own getActiveFocuses / getFocusDeliveries pass.
|
|
25
|
+
* They are driven from state-cascade.ts's runCascadeChecks loop, which already
|
|
26
|
+
* iterates active focuses, fetches each focus's deliveries, and computes the
|
|
27
|
+
* review phase: the detectors RIDE that existing review-handling seam (so there
|
|
28
|
+
* is no redundant per-poll fan-out, and the review-window detector is wired into
|
|
29
|
+
* the same review handling that owns review_requested_at). Each files AT MOST
|
|
30
|
+
* ONE open escalation per entity (per delivery / per focus), mirroring the
|
|
31
|
+
* merge-back stuck_focus_branch idempotency: the dedup queries the DB so it
|
|
32
|
+
* survives a daemon restart. Pure over injected deps so the four cases
|
|
33
|
+
* (under-cap, over-cap, fresh window, stale window) unit-test without
|
|
34
|
+
* Supabase/MCP.
|
|
35
|
+
*/
|
|
36
|
+
import type { FocusDeliveryInfo } from './types.js';
|
|
37
|
+
import { type CreateEscalationParams } from './queries/issues.js';
|
|
38
|
+
/**
|
|
39
|
+
* escalation_kind discriminators for the two stall detectors. Declared as
|
|
40
|
+
* daemon-local string constants (not imported from @telora/daemon-core's
|
|
41
|
+
* ESCALATION_KINDS) on purpose: the daemon consumes the registry-PUBLISHED
|
|
42
|
+
* daemon-core, so it cannot reference newly-added enum members until a build
|
|
43
|
+
* that depends on a daemon-core publish carrying them. They ARE now in
|
|
44
|
+
* daemon-core (ESCALATION_KINDS.VERIFY_GATE_UNCLEARABLE / .REVIEW_WINDOW_STALLED)
|
|
45
|
+
* for other consumers, but the daemon keeps the local strings so this code does
|
|
46
|
+
* not depend on publish ordering. The DB CHECK -- loosened by the
|
|
47
|
+
* agent_escalations_kind_stall_detectors migration -- is the real constraint.
|
|
48
|
+
*/
|
|
49
|
+
export declare const VERIFY_GATE_UNCLEARABLE_KIND = "verify_gate_unclearable";
|
|
50
|
+
export declare const REVIEW_WINDOW_STALLED_KIND = "review_window_stalled";
|
|
51
|
+
/** The per-focus context both detectors need to anchor an escalation. */
|
|
52
|
+
export interface FocusStallContext {
|
|
53
|
+
organizationId: string;
|
|
54
|
+
focusId: string;
|
|
55
|
+
focusName: string;
|
|
56
|
+
}
|
|
57
|
+
export interface VerifyFailedCapDeps {
|
|
58
|
+
hasOpenEscalationForDelivery: (deliveryId: string, escalationKind: string) => Promise<boolean>;
|
|
59
|
+
createEscalation: (params: CreateEscalationParams) => Promise<{
|
|
60
|
+
escalationId: string | null;
|
|
61
|
+
} | void>;
|
|
62
|
+
/** Override the cap for tests; defaults to the resolved env-config value. */
|
|
63
|
+
cap?: number;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Pure decision: does this delivery exceed the verify_failed re-code cap?
|
|
67
|
+
* A terminal delivery (done/cancelled) is never escalated -- it has stopped
|
|
68
|
+
* cycling. Exported for direct unit testing.
|
|
69
|
+
*/
|
|
70
|
+
export declare function exceedsVerifyFailedCap(delivery: Pick<FocusDeliveryInfo, 'executionStatus' | 'iterationCount'>, cap: number): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Check ONE focus's ALREADY-FETCHED deliveries for any whose iteration_count
|
|
73
|
+
* exceeds the verify_failed re-code cap, filing a verify_gate_unclearable
|
|
74
|
+
* escalation (needs_operator) for each -- at most one open per delivery.
|
|
75
|
+
* Operates on the deliveries the caller (runCascadeChecks) already loaded; it
|
|
76
|
+
* does NOT fetch.
|
|
77
|
+
*/
|
|
78
|
+
export declare function checkVerifyFailedCapForFocus(ctx: FocusStallContext, deliveries: FocusDeliveryInfo[], deps?: VerifyFailedCapDeps): Promise<void>;
|
|
79
|
+
export interface ReviewWindowDeps {
|
|
80
|
+
hasOpenFocusEscalation: (focusId: string, escalationKind: string) => Promise<boolean>;
|
|
81
|
+
createEscalation: (params: CreateEscalationParams) => Promise<{
|
|
82
|
+
escalationId: string | null;
|
|
83
|
+
} | void>;
|
|
84
|
+
/** Focus IDs that currently have an active REVIEW team. Defaults to live teams. */
|
|
85
|
+
activeReviewFocusIds: () => Set<string>;
|
|
86
|
+
/** Current epoch ms; injectable for tests. Defaults to Date.now. */
|
|
87
|
+
now: () => number;
|
|
88
|
+
/** Override the staleness threshold (hours) for tests; defaults to env-config. */
|
|
89
|
+
thresholdHours?: number;
|
|
90
|
+
}
|
|
91
|
+
/** Focus IDs that currently have an active REVIEW team (live default). */
|
|
92
|
+
export declare function activeReviewFocusIds(): Set<string>;
|
|
93
|
+
/**
|
|
94
|
+
* Pure decision: is this focus's review window stale? True when a
|
|
95
|
+
* review_requested_at exists, is older than `thresholdMs`, and the focus has no
|
|
96
|
+
* active review team. A completed review clears review_requested_at, so a
|
|
97
|
+
* still-set timestamp older than the threshold with no active review team means
|
|
98
|
+
* the review never ran (or never finished). Exported for direct unit testing.
|
|
99
|
+
*/
|
|
100
|
+
export declare function isReviewWindowStale(reviewRequestedAt: string | null, hasActiveReview: boolean, nowMs: number, thresholdMs: number): boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Check ONE focus for a stale review window and file a review_window_stalled
|
|
103
|
+
* escalation (needs_operator) -- at most one open per focus. The caller
|
|
104
|
+
* (runCascadeChecks) passes the focus's review_requested_at, so this rides the
|
|
105
|
+
* existing review-handling seam rather than fanning out its own focus scan.
|
|
106
|
+
*/
|
|
107
|
+
export declare function checkReviewWindowStaleForFocus(ctx: FocusStallContext, reviewRequestedAt: string | null, deps?: ReviewWindowDeps): Promise<void>;
|
|
108
|
+
//# sourceMappingURL=stall-detectors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stall-detectors.d.ts","sourceRoot":"","sources":["../src/stall-detectors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAMpD,OAAO,EAIL,KAAK,sBAAsB,EAC5B,MAAM,qBAAqB,CAAC;AAK7B;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,4BAA4B,CAAC;AACtE,eAAO,MAAM,0BAA0B,0BAA0B,CAAC;AAElE,yEAAyE;AACzE,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAcD,MAAM,WAAW,mBAAmB;IAClC,4BAA4B,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/F,gBAAgB,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,OAAO,CAAC;QAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACtG,6EAA6E;IAC7E,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAOD;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,IAAI,CAAC,iBAAiB,EAAE,iBAAiB,GAAG,gBAAgB,CAAC,EACvE,GAAG,EAAE,MAAM,GACV,OAAO,CAGT;AAED;;;;;;GAMG;AACH,wBAAsB,4BAA4B,CAChD,GAAG,EAAE,iBAAiB,EACtB,UAAU,EAAE,iBAAiB,EAAE,EAC/B,IAAI,GAAE,mBAAgD,GACrD,OAAO,CAAC,IAAI,CAAC,CA0Df;AAID,MAAM,WAAW,gBAAgB;IAC/B,sBAAsB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACtF,gBAAgB,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,OAAO,CAAC;QAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACtG,mFAAmF;IACnF,oBAAoB,EAAE,MAAM,GAAG,CAAC,MAAM,CAAC,CAAC;IACxC,oEAAoE;IACpE,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,kFAAkF;IAClF,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,0EAA0E;AAC1E,wBAAgB,oBAAoB,IAAI,GAAG,CAAC,MAAM,CAAC,CAMlD;AASD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,eAAe,EAAE,OAAO,EACxB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,GAClB,OAAO,CAMT;AAED;;;;;GAKG;AACH,wBAAsB,8BAA8B,CAClD,GAAG,EAAE,iBAAiB,EACtB,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,IAAI,GAAE,gBAA0C,GAC/C,OAAO,CAAC,IAAI,CAAC,CA8Df"}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Targeted daemon-owned stall detectors.
|
|
3
|
+
*
|
|
4
|
+
* The lifecycle-aware no-progress predicate (completion/event.ts) makes the
|
|
5
|
+
* generic watchdog PATIENT: it no longer trips on healthy review/verify/requeue
|
|
6
|
+
* latency. That patience is only safe if the genuinely-wedged daemon-owned
|
|
7
|
+
* cases it would otherwise have (noisily) surfaced are caught by dedicated
|
|
8
|
+
* detectors with their own actionable reasons. This module is that safety
|
|
9
|
+
* backfill:
|
|
10
|
+
*
|
|
11
|
+
* 1. verify_failed cycle cap -- a delivery whose verify_failed re-code cycle
|
|
12
|
+
* count (iteration_count) exceeds TELORA_VERIFY_FAILED_ITERATION_CAP is
|
|
13
|
+
* stuck in an unbounded re-code loop the verify gate will never clear. File
|
|
14
|
+
* ONE verify_gate_unclearable escalation (needs_operator) carrying the
|
|
15
|
+
* gate_state / last failing condition -- not a generic no-progress ping.
|
|
16
|
+
*
|
|
17
|
+
* 2. review-window staleness -- a focus stuck in the `reviewing` phase whose
|
|
18
|
+
* review_requested_at has sat past TELORA_REVIEW_WINDOW_STALE_HOURS with no
|
|
19
|
+
* active review team (a completed review would have cleared
|
|
20
|
+
* review_requested_at) is wedged. File ONE review_window_stalled escalation
|
|
21
|
+
* (needs_operator).
|
|
22
|
+
*
|
|
23
|
+
* BOTH detectors are PER-FOCUS and operate on state the caller already has --
|
|
24
|
+
* they do NOT fan out their own getActiveFocuses / getFocusDeliveries pass.
|
|
25
|
+
* They are driven from state-cascade.ts's runCascadeChecks loop, which already
|
|
26
|
+
* iterates active focuses, fetches each focus's deliveries, and computes the
|
|
27
|
+
* review phase: the detectors RIDE that existing review-handling seam (so there
|
|
28
|
+
* is no redundant per-poll fan-out, and the review-window detector is wired into
|
|
29
|
+
* the same review handling that owns review_requested_at). Each files AT MOST
|
|
30
|
+
* ONE open escalation per entity (per delivery / per focus), mirroring the
|
|
31
|
+
* merge-back stuck_focus_branch idempotency: the dedup queries the DB so it
|
|
32
|
+
* survives a daemon restart. Pure over injected deps so the four cases
|
|
33
|
+
* (under-cap, over-cap, fresh window, stale window) unit-test without
|
|
34
|
+
* Supabase/MCP.
|
|
35
|
+
*/
|
|
36
|
+
import { ESCALATION_REASONS, ESCALATION_DISPOSITIONS, withRetry, } from '@telora/daemon-core';
|
|
37
|
+
import { createEscalation as defaultCreateEscalation, hasOpenEscalationForDelivery as defaultHasOpenEscalationForDelivery, hasOpenFocusEscalation as defaultHasOpenFocusEscalation, } from './queries/issues.js';
|
|
38
|
+
import { getActiveTeams } from './focus-executor.js';
|
|
39
|
+
import { isStatusTerminal } from './stage-classifier.js';
|
|
40
|
+
import { resolveVerifyFailedCap, resolveReviewWindowStaleHours } from './stall-detector-config.js';
|
|
41
|
+
/**
|
|
42
|
+
* escalation_kind discriminators for the two stall detectors. Declared as
|
|
43
|
+
* daemon-local string constants (not imported from @telora/daemon-core's
|
|
44
|
+
* ESCALATION_KINDS) on purpose: the daemon consumes the registry-PUBLISHED
|
|
45
|
+
* daemon-core, so it cannot reference newly-added enum members until a build
|
|
46
|
+
* that depends on a daemon-core publish carrying them. They ARE now in
|
|
47
|
+
* daemon-core (ESCALATION_KINDS.VERIFY_GATE_UNCLEARABLE / .REVIEW_WINDOW_STALLED)
|
|
48
|
+
* for other consumers, but the daemon keeps the local strings so this code does
|
|
49
|
+
* not depend on publish ordering. The DB CHECK -- loosened by the
|
|
50
|
+
* agent_escalations_kind_stall_detectors migration -- is the real constraint.
|
|
51
|
+
*/
|
|
52
|
+
export const VERIFY_GATE_UNCLEARABLE_KIND = 'verify_gate_unclearable';
|
|
53
|
+
export const REVIEW_WINDOW_STALLED_KIND = 'review_window_stalled';
|
|
54
|
+
/** Render gate_state compactly for the escalation body. */
|
|
55
|
+
function formatGateState(gateState) {
|
|
56
|
+
if (gateState == null)
|
|
57
|
+
return '(none recorded)';
|
|
58
|
+
try {
|
|
59
|
+
return JSON.stringify(gateState);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return '(unserializable)';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const defaultVerifyFailedCapDeps = {
|
|
66
|
+
hasOpenEscalationForDelivery: defaultHasOpenEscalationForDelivery,
|
|
67
|
+
createEscalation: defaultCreateEscalation,
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Pure decision: does this delivery exceed the verify_failed re-code cap?
|
|
71
|
+
* A terminal delivery (done/cancelled) is never escalated -- it has stopped
|
|
72
|
+
* cycling. Exported for direct unit testing.
|
|
73
|
+
*/
|
|
74
|
+
export function exceedsVerifyFailedCap(delivery, cap) {
|
|
75
|
+
if (isStatusTerminal(delivery.executionStatus ?? ''))
|
|
76
|
+
return false;
|
|
77
|
+
return (delivery.iterationCount ?? 0) > cap;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Check ONE focus's ALREADY-FETCHED deliveries for any whose iteration_count
|
|
81
|
+
* exceeds the verify_failed re-code cap, filing a verify_gate_unclearable
|
|
82
|
+
* escalation (needs_operator) for each -- at most one open per delivery.
|
|
83
|
+
* Operates on the deliveries the caller (runCascadeChecks) already loaded; it
|
|
84
|
+
* does NOT fetch.
|
|
85
|
+
*/
|
|
86
|
+
export async function checkVerifyFailedCapForFocus(ctx, deliveries, deps = defaultVerifyFailedCapDeps) {
|
|
87
|
+
const cap = deps.cap ?? resolveVerifyFailedCap();
|
|
88
|
+
for (const delivery of deliveries) {
|
|
89
|
+
if (!exceedsVerifyFailedCap(delivery, cap))
|
|
90
|
+
continue;
|
|
91
|
+
try {
|
|
92
|
+
if (await deps.hasOpenEscalationForDelivery(delivery.id, VERIFY_GATE_UNCLEARABLE_KIND)) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
await withRetry(() => deps.createEscalation({
|
|
96
|
+
organizationId: ctx.organizationId,
|
|
97
|
+
sessionId: '',
|
|
98
|
+
issueId: null,
|
|
99
|
+
reasonType: ESCALATION_REASONS.BLOCKED_BY_EXTERNAL,
|
|
100
|
+
// Operator triages the unclearable gate condition from gate_state.
|
|
101
|
+
disposition: ESCALATION_DISPOSITIONS.NEEDS_OPERATOR,
|
|
102
|
+
anchor: { deliveryId: delivery.id, focusId: ctx.focusId },
|
|
103
|
+
escalationKind: VERIFY_GATE_UNCLEARABLE_KIND,
|
|
104
|
+
description: `Delivery "${delivery.name}" has been through ${delivery.iterationCount ?? 0} ` +
|
|
105
|
+
`verify_failed re-code cycles (cap ${cap}) without clearing the verify gate. ` +
|
|
106
|
+
`The gate keeps rejecting the work -- the loop will not converge on its own.\n\n` +
|
|
107
|
+
`**Focus:** ${ctx.focusName}\n` +
|
|
108
|
+
`**Focus ID:** ${ctx.focusId}\n` +
|
|
109
|
+
`**Delivery:** "${delivery.name}" (${delivery.id})\n` +
|
|
110
|
+
`**Status:** ${delivery.executionStatus ?? 'unknown'}\n` +
|
|
111
|
+
`**iteration_count:** ${delivery.iterationCount ?? 0}\n` +
|
|
112
|
+
`**gate_state:** ${formatGateState(delivery.gateState)}\n\n` +
|
|
113
|
+
`Inspect the gate_state / last failing condition above. See docs/runbook-verify-gate.md.`,
|
|
114
|
+
whatWasTried: `The delivery re-entered verify_failed ${delivery.iterationCount ?? 0} times; ` +
|
|
115
|
+
`each verify pass was rejected by the gate and re-queued.`,
|
|
116
|
+
helpNeeded: `Resolve the blocking gate condition (relax the gate, fix the underlying defect ` +
|
|
117
|
+
`manually, or cancel the delivery if it cannot pass).`,
|
|
118
|
+
metadata: {
|
|
119
|
+
cause: VERIFY_GATE_UNCLEARABLE_KIND,
|
|
120
|
+
deliveryName: delivery.name,
|
|
121
|
+
executionStatus: delivery.executionStatus,
|
|
122
|
+
iterationCount: delivery.iterationCount ?? 0,
|
|
123
|
+
cap,
|
|
124
|
+
gateState: delivery.gateState ?? null,
|
|
125
|
+
},
|
|
126
|
+
}), { maxAttempts: 3, label: `escalation-verify-gate-unclearable-${delivery.id}` });
|
|
127
|
+
console.log(`[stall-detectors] Filed verify_gate_unclearable escalation for delivery "${delivery.name}" ` +
|
|
128
|
+
`(iteration_count ${delivery.iterationCount ?? 0} > cap ${cap})`);
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
console.warn(`[stall-detectors] verify-failed-cap: failed to file escalation for delivery "${delivery.name}":`, err.message);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/** Focus IDs that currently have an active REVIEW team (live default). */
|
|
136
|
+
export function activeReviewFocusIds() {
|
|
137
|
+
const ids = new Set();
|
|
138
|
+
for (const [focusId, team] of getActiveTeams()) {
|
|
139
|
+
if (team.sessionType === 'review')
|
|
140
|
+
ids.add(focusId);
|
|
141
|
+
}
|
|
142
|
+
return ids;
|
|
143
|
+
}
|
|
144
|
+
const defaultReviewWindowDeps = {
|
|
145
|
+
hasOpenFocusEscalation: defaultHasOpenFocusEscalation,
|
|
146
|
+
createEscalation: defaultCreateEscalation,
|
|
147
|
+
activeReviewFocusIds,
|
|
148
|
+
now: () => Date.now(),
|
|
149
|
+
};
|
|
150
|
+
/**
|
|
151
|
+
* Pure decision: is this focus's review window stale? True when a
|
|
152
|
+
* review_requested_at exists, is older than `thresholdMs`, and the focus has no
|
|
153
|
+
* active review team. A completed review clears review_requested_at, so a
|
|
154
|
+
* still-set timestamp older than the threshold with no active review team means
|
|
155
|
+
* the review never ran (or never finished). Exported for direct unit testing.
|
|
156
|
+
*/
|
|
157
|
+
export function isReviewWindowStale(reviewRequestedAt, hasActiveReview, nowMs, thresholdMs) {
|
|
158
|
+
if (!reviewRequestedAt)
|
|
159
|
+
return false;
|
|
160
|
+
if (hasActiveReview)
|
|
161
|
+
return false;
|
|
162
|
+
const requestedMs = Date.parse(reviewRequestedAt);
|
|
163
|
+
if (Number.isNaN(requestedMs))
|
|
164
|
+
return false;
|
|
165
|
+
return nowMs - requestedMs > thresholdMs;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Check ONE focus for a stale review window and file a review_window_stalled
|
|
169
|
+
* escalation (needs_operator) -- at most one open per focus. The caller
|
|
170
|
+
* (runCascadeChecks) passes the focus's review_requested_at, so this rides the
|
|
171
|
+
* existing review-handling seam rather than fanning out its own focus scan.
|
|
172
|
+
*/
|
|
173
|
+
export async function checkReviewWindowStaleForFocus(ctx, reviewRequestedAt, deps = defaultReviewWindowDeps) {
|
|
174
|
+
const thresholdHours = deps.thresholdHours ?? resolveReviewWindowStaleHours();
|
|
175
|
+
const thresholdMs = thresholdHours * 3_600_000;
|
|
176
|
+
const nowMs = deps.now();
|
|
177
|
+
const hasActiveReview = deps.activeReviewFocusIds().has(ctx.focusId);
|
|
178
|
+
if (!isReviewWindowStale(reviewRequestedAt, hasActiveReview, nowMs, thresholdMs)) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
if (await deps.hasOpenFocusEscalation(ctx.focusId, REVIEW_WINDOW_STALLED_KIND)) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const ageHours = Math.floor((nowMs - Date.parse(reviewRequestedAt)) / 3_600_000);
|
|
186
|
+
await withRetry(() => deps.createEscalation({
|
|
187
|
+
organizationId: ctx.organizationId,
|
|
188
|
+
sessionId: '',
|
|
189
|
+
issueId: null,
|
|
190
|
+
reasonType: ESCALATION_REASONS.BLOCKED_BY_EXTERNAL,
|
|
191
|
+
// Operator triages a review that was requested but never ran/finished.
|
|
192
|
+
disposition: ESCALATION_DISPOSITIONS.NEEDS_OPERATOR,
|
|
193
|
+
anchor: { focusId: ctx.focusId },
|
|
194
|
+
escalationKind: REVIEW_WINDOW_STALLED_KIND,
|
|
195
|
+
description: `Focus "${ctx.focusName}" requested a review ~${ageHours}h ago ` +
|
|
196
|
+
`(threshold ${thresholdHours}h) but no review is running and none has completed ` +
|
|
197
|
+
`(a completed review clears review_requested_at). The focus is wedged in the ` +
|
|
198
|
+
`review window -- its verify deliveries will never route to done/verify_failed ` +
|
|
199
|
+
`on their own.\n\n` +
|
|
200
|
+
`**Focus:** ${ctx.focusName}\n` +
|
|
201
|
+
`**Focus ID:** ${ctx.focusId}\n` +
|
|
202
|
+
`**review_requested_at:** ${reviewRequestedAt}\n\n` +
|
|
203
|
+
`Either re-spawn the review (clear and re-request the review window) or clear the ` +
|
|
204
|
+
`stale request via telora_product_focus update clearReviewRequest=true.`,
|
|
205
|
+
whatWasTried: `The review window was opened (review_requested_at set ~${ageHours}h ago) but no ` +
|
|
206
|
+
`review team is active and review_requested_at was never cleared by a completion.`,
|
|
207
|
+
helpNeeded: `Re-trigger the review or clear the stale review_requested_at ` +
|
|
208
|
+
`(telora_product_focus update clearReviewRequest=true) so the verify deliveries ` +
|
|
209
|
+
`can route forward.`,
|
|
210
|
+
metadata: {
|
|
211
|
+
cause: REVIEW_WINDOW_STALLED_KIND,
|
|
212
|
+
focusName: ctx.focusName,
|
|
213
|
+
reviewRequestedAt,
|
|
214
|
+
ageHours,
|
|
215
|
+
thresholdHours,
|
|
216
|
+
},
|
|
217
|
+
}), { maxAttempts: 3, label: `escalation-review-window-stalled-${ctx.focusId}` });
|
|
218
|
+
console.log(`[stall-detectors] Filed review_window_stalled escalation for focus "${ctx.focusName}" ` +
|
|
219
|
+
`(review_requested_at ~${ageHours}h old > ${thresholdHours}h)`);
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
console.warn(`[stall-detectors] review-window: failed to file escalation for focus "${ctx.focusName}":`, err.message);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=stall-detectors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stall-detectors.js","sourceRoot":"","sources":["../src/stall-detectors.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-cascade.d.ts","sourceRoot":"","sources":["../src/state-cascade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AASpD,OAAO,EAAoB,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"state-cascade.d.ts","sourceRoot":"","sources":["../src/state-cascade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AASpD,OAAO,EAAoB,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAI9E,+DAA+D;AAC/D,MAAM,WAAW,WAAW;IAC1B,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACtE,oBAAoB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E,sBAAsB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D;AAgCD;;;GAGG;AACH,wBAAgB,iCAAiC,IAAI,IAAI,CAExD;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,mBAAmB,EAC1B,cAAc,EAAE,cAAc,GAAG,IAAI,EACrC,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,UAAU,EAAE,iBAAiB,EAAE,EAC/B,IAAI,GAAE,WAAyB,GAC9B,OAAO,CAAC,IAAI,CAAC,CA2Cf;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,mBAAmB,EAC1B,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,IAAI,GAAE,WAAyB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA2D1E"}
|
package/dist/state-cascade.js
CHANGED
|
@@ -34,6 +34,7 @@ import { configForProduct } from './config.js';
|
|
|
34
34
|
import { emitLoopTrigger } from './loop-event-bus.js';
|
|
35
35
|
import { deriveFocusPhase } from './focus-phase.js';
|
|
36
36
|
import { isReviewEnabled } from './pipeline-config.js';
|
|
37
|
+
import { checkVerifyFailedCapForFocus, checkReviewWindowStaleForFocus } from './stall-detectors.js';
|
|
37
38
|
const defaultDeps = {
|
|
38
39
|
getFocusDeliveries: _getFocusDeliveries,
|
|
39
40
|
setReviewRequestedAt: _setReviewRequestedAt,
|
|
@@ -165,6 +166,26 @@ export async function runCascadeChecks(config) {
|
|
|
165
166
|
});
|
|
166
167
|
await checkAutoReview(focus.focus_id, phase, focus.pipeline_config, focus.review_requested_at, deliveries);
|
|
167
168
|
await clearStaleReviewRequest(focus.focus_id, phase, focus.review_requested_at);
|
|
169
|
+
// Targeted daemon-owned stall detectors RIDE this same review-handling
|
|
170
|
+
// seam: they reuse the deliveries + review phase + review_requested_at
|
|
171
|
+
// already loaded above instead of fanning out a second per-poll
|
|
172
|
+
// getActiveFocuses/getFocusDeliveries pass.
|
|
173
|
+
const stallCtx = {
|
|
174
|
+
organizationId: pc.organizationId,
|
|
175
|
+
focusId: focus.focus_id,
|
|
176
|
+
focusName: focus.focus_name,
|
|
177
|
+
};
|
|
178
|
+
// (1) verify_failed re-code cap -> verify_gate_unclearable (per delivery).
|
|
179
|
+
await checkVerifyFailedCapForFocus(stallCtx, deliveries);
|
|
180
|
+
// (2) review-window staleness -> review_window_stalled (per focus).
|
|
181
|
+
// Gated on the cascade's own derived `reviewing` phase: a focus stuck
|
|
182
|
+
// in the review window is exactly phase === 'reviewing' with
|
|
183
|
+
// review_requested_at still set. (clearStaleReviewRequest above only
|
|
184
|
+
// clears on a coding/kickoff regression, so a still-'reviewing' phase
|
|
185
|
+
// means the request was not cleared this tick.)
|
|
186
|
+
if (phase === 'reviewing') {
|
|
187
|
+
await checkReviewWindowStaleForFocus(stallCtx, focus.review_requested_at);
|
|
188
|
+
}
|
|
168
189
|
}
|
|
169
190
|
catch (err) {
|
|
170
191
|
console.warn(`[state-cascade] Cascade pass failed for focus ${focus.focus_id}:`, err.message);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-cascade.js","sourceRoot":"","sources":["../src/state-cascade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAGtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EACL,kBAAkB,IAAI,mBAAmB,EACzC,oBAAoB,IAAI,qBAAqB,EAC7C,sBAAsB,IAAI,uBAAuB,GAClD,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAA4B,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"state-cascade.js","sourceRoot":"","sources":["../src/state-cascade.ts"],"names":[],"mappings":"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"}
|