@telora/daemon 0.22.13 → 0.22.52
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 +5 -3
- package/dist/backends/index.d.ts.map +1 -1
- package/dist/backends/index.js +1 -0
- package/dist/backends/index.js.map +1 -1
- package/dist/ci/ci-status.d.ts.map +1 -1
- package/dist/ci/ci-status.js +1 -0
- package/dist/ci/ci-status.js.map +1 -1
- package/dist/completion/detector.d.ts +9 -0
- package/dist/completion/detector.d.ts.map +1 -1
- package/dist/completion/detector.js +18 -0
- package/dist/completion/detector.js.map +1 -1
- package/dist/completion/event-phases.d.ts +20 -0
- package/dist/completion/event-phases.d.ts.map +1 -1
- package/dist/completion/event-phases.js +46 -13
- package/dist/completion/event-phases.js.map +1 -1
- package/dist/completion/event.d.ts +69 -9
- package/dist/completion/event.d.ts.map +1 -1
- package/dist/completion/event.js +109 -22
- package/dist/completion/event.js.map +1 -1
- package/dist/completion/liveness-firing.d.ts +27 -0
- package/dist/completion/liveness-firing.d.ts.map +1 -0
- package/dist/completion/liveness-firing.js +79 -0
- package/dist/completion/liveness-firing.js.map +1 -0
- package/dist/completion/review-exit-phase.js +4 -4
- package/dist/completion/review-exit-phase.js.map +1 -1
- package/dist/completion/state-cascade.d.ts.map +1 -1
- package/dist/completion/state-cascade.js +8 -0
- package/dist/completion/state-cascade.js.map +1 -1
- package/dist/completion/stdin-turn-tracker.d.ts +19 -0
- package/dist/completion/stdin-turn-tracker.d.ts.map +1 -0
- package/dist/completion/stdin-turn-tracker.js +57 -0
- package/dist/completion/stdin-turn-tracker.js.map +1 -0
- package/dist/completion/supervision-firing-record.d.ts +242 -0
- package/dist/completion/supervision-firing-record.d.ts.map +1 -0
- package/dist/completion/supervision-firing-record.js +292 -0
- package/dist/completion/supervision-firing-record.js.map +1 -0
- package/dist/completion/team-lifecycle.d.ts.map +1 -1
- package/dist/completion/team-lifecycle.js +9 -0
- package/dist/completion/team-lifecycle.js.map +1 -1
- package/dist/directive/directive-dispatch.d.ts +7 -1
- package/dist/directive/directive-dispatch.d.ts.map +1 -1
- package/dist/directive/directive-dispatch.js +105 -39
- package/dist/directive/directive-dispatch.js.map +1 -1
- package/dist/directive/directive-executor.d.ts +1 -10
- package/dist/directive/directive-executor.d.ts.map +1 -1
- package/dist/directive/directive-executor.js +51 -33
- package/dist/directive/directive-executor.js.map +1 -1
- package/dist/directive/directive-queue.d.ts +35 -9
- package/dist/directive/directive-queue.d.ts.map +1 -1
- package/dist/directive/directive-queue.js +15 -8
- package/dist/directive/directive-queue.js.map +1 -1
- package/dist/directive/directive-verify-hold-gate.d.ts +33 -0
- package/dist/directive/directive-verify-hold-gate.d.ts.map +1 -0
- package/dist/directive/directive-verify-hold-gate.js +57 -0
- package/dist/directive/directive-verify-hold-gate.js.map +1 -0
- package/dist/directive/phase-sync.d.ts +11 -0
- package/dist/directive/phase-sync.d.ts.map +1 -1
- package/dist/directive/phase-sync.js +12 -0
- package/dist/directive/phase-sync.js.map +1 -1
- package/dist/drift/staleness-sensor.d.ts +62 -67
- package/dist/drift/staleness-sensor.d.ts.map +1 -1
- package/dist/drift/staleness-sensor.js +233 -68
- package/dist/drift/staleness-sensor.js.map +1 -1
- package/dist/drift/staleness.d.ts +8 -0
- package/dist/drift/staleness.d.ts.map +1 -1
- package/dist/drift/staleness.js +7 -1
- package/dist/drift/staleness.js.map +1 -1
- package/dist/focus/focus-engine.js +1 -1
- package/dist/focus/focus-engine.js.map +1 -1
- package/dist/focus/focus-team-state.d.ts.map +1 -1
- package/dist/focus/focus-team-state.js +2 -0
- package/dist/focus/focus-team-state.js.map +1 -1
- package/dist/focus/focus-worktree-state.d.ts.map +1 -1
- package/dist/focus/focus-worktree-state.js +5 -2
- package/dist/focus/focus-worktree-state.js.map +1 -1
- package/dist/focus/listener-spawn.d.ts +39 -0
- package/dist/focus/listener-spawn.d.ts.map +1 -1
- package/dist/focus/listener-spawn.js +140 -24
- package/dist/focus/listener-spawn.js.map +1 -1
- package/dist/focus/listener.d.ts.map +1 -1
- package/dist/focus/listener.js +53 -25
- package/dist/focus/listener.js.map +1 -1
- package/dist/focus/session-health.d.ts +25 -3
- package/dist/focus/session-health.d.ts.map +1 -1
- package/dist/focus/session-health.js +32 -1
- package/dist/focus/session-health.js.map +1 -1
- package/dist/focus/team-actionable-work.d.ts +41 -2
- package/dist/focus/team-actionable-work.d.ts.map +1 -1
- package/dist/focus/team-actionable-work.js +52 -7
- package/dist/focus/team-actionable-work.js.map +1 -1
- package/dist/merge/focus-merge.d.ts +12 -1
- package/dist/merge/focus-merge.d.ts.map +1 -1
- package/dist/merge/focus-merge.js +33 -2
- package/dist/merge/focus-merge.js.map +1 -1
- package/dist/nexus/return.d.ts +105 -1
- package/dist/nexus/return.d.ts.map +1 -1
- package/dist/nexus/return.js +237 -47
- package/dist/nexus/return.js.map +1 -1
- package/dist/queries/schemas.d.ts +12 -0
- package/dist/queries/schemas.d.ts.map +1 -1
- package/dist/queries/schemas.js +7 -0
- package/dist/queries/schemas.js.map +1 -1
- package/dist/queries/sessions.d.ts.map +1 -1
- package/dist/queries/sessions.js +1 -0
- package/dist/queries/sessions.js.map +1 -1
- package/dist/shell/heartbeat-metadata.d.ts +102 -0
- package/dist/shell/heartbeat-metadata.d.ts.map +1 -0
- package/dist/shell/heartbeat-metadata.js +172 -0
- package/dist/shell/heartbeat-metadata.js.map +1 -0
- package/dist/shell/heartbeat.d.ts +7 -22
- package/dist/shell/heartbeat.d.ts.map +1 -1
- package/dist/shell/heartbeat.js +120 -89
- package/dist/shell/heartbeat.js.map +1 -1
- package/dist/shell/unified-shell.d.ts.map +1 -1
- package/dist/shell/unified-shell.js +21 -12
- package/dist/shell/unified-shell.js.map +1 -1
- package/dist/spawn/helpers.d.ts.map +1 -1
- package/dist/spawn/helpers.js +7 -1
- package/dist/spawn/helpers.js.map +1 -1
- package/dist/spawn/process-spawn.d.ts +32 -1
- package/dist/spawn/process-spawn.d.ts.map +1 -1
- package/dist/spawn/process-spawn.js +43 -5
- package/dist/spawn/process-spawn.js.map +1 -1
- package/dist/testing/fixtures/index.js +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/session.d.ts +11 -0
- package/dist/types/session.d.ts.map +1 -1
- package/dist/update/auto-update-idle.d.ts +22 -0
- package/dist/update/auto-update-idle.d.ts.map +1 -1
- package/dist/update/auto-update-idle.js +8 -1
- package/dist/update/auto-update-idle.js.map +1 -1
- package/dist/update/auto-update.d.ts +0 -6
- package/dist/update/auto-update.d.ts.map +1 -1
- package/dist/update/auto-update.js +20 -6
- package/dist/update/auto-update.js.map +1 -1
- package/dist/update/version-check.d.ts +73 -0
- package/dist/update/version-check.d.ts.map +1 -1
- package/dist/update/version-check.js +107 -11
- package/dist/update/version-check.js.map +1 -1
- package/dist/verify/stall-detector-config.d.ts +26 -0
- package/dist/verify/stall-detector-config.d.ts.map +1 -1
- package/dist/verify/stall-detector-config.js +32 -0
- package/dist/verify/stall-detector-config.js.map +1 -1
- package/dist/verify/stall-detectors.d.ts +40 -0
- package/dist/verify/stall-detectors.d.ts.map +1 -1
- package/dist/verify/stall-detectors.js +60 -6
- package/dist/verify/stall-detectors.js.map +1 -1
- package/dist/verify/stalled-team-backstop.d.ts +43 -0
- package/dist/verify/stalled-team-backstop.d.ts.map +1 -0
- package/dist/verify/stalled-team-backstop.js +131 -0
- package/dist/verify/stalled-team-backstop.js.map +1 -0
- package/dist/worktree/verify-interlock.d.ts.map +1 -1
- package/dist/worktree/verify-interlock.js +3 -1
- package/dist/worktree/verify-interlock.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
|
|
2
|
+
// Pinned by: packages/daemon/src/completion/stdin-turn-tracker.test.ts,
|
|
3
|
+
// packages/daemon/src/spawn/spawn-compact-turn.test.ts
|
|
4
|
+
// PINNED-BY-END
|
|
5
|
+
/**
|
|
6
|
+
* Stdin-turn tracker -- orders team teardown after queued directive delivery.
|
|
7
|
+
*
|
|
8
|
+
* The completion detector fires `complete` when the lead finishes a turn (a
|
|
9
|
+
* `result` event) with no active subagents, and the completion handler tears the
|
|
10
|
+
* team down. But the daemon may have WRITTEN more stdin user messages to the team
|
|
11
|
+
* than the CLI has resulted yet: a coding-stage /compact + content injected ~0.4s
|
|
12
|
+
* after spawn sit queued BEHIND the long kickoff turn, and the kickoff's result
|
|
13
|
+
* triggers teardown before they run (2026-09-06: 5 of 10 /compact sends lost this
|
|
14
|
+
* way).
|
|
15
|
+
*
|
|
16
|
+
* This tracks stdin user messages SENT per focus. The completion detector reads
|
|
17
|
+
* it (pendingStdinTurns) and SUPPRESSES `complete` while more messages have been
|
|
18
|
+
* written than the CLI has resulted (turnCount, one result event per turn -- see
|
|
19
|
+
* CompletionDetector). So a team with queued stdin turns is not classified done
|
|
20
|
+
* until it has processed them.
|
|
21
|
+
*
|
|
22
|
+
* Conservative + bounded by construction: it only DELAYS completion (never kills
|
|
23
|
+
* early), it is INERT for a normal spawn (kickoff alone -> sent === turnCount), and
|
|
24
|
+
* a directive the CLI never results leaves the team to the existing backstops (the
|
|
25
|
+
* process-close path and the stall/liveness detectors), not wedged here.
|
|
26
|
+
*
|
|
27
|
+
* @module completion/stdin-turn-tracker
|
|
28
|
+
*/
|
|
29
|
+
/** stdin user messages the daemon has written to each focus's live team. */
|
|
30
|
+
const sentByFocus = new Map();
|
|
31
|
+
/**
|
|
32
|
+
* Record one stdin user message written to a focus's team (the spawn kickoff
|
|
33
|
+
* write, and each executeInjectDirective sendMessage). One write == one CLI turn
|
|
34
|
+
* == one eventual `result` event.
|
|
35
|
+
*/
|
|
36
|
+
export function recordStdinMessageSent(focusId) {
|
|
37
|
+
sentByFocus.set(focusId, (sentByFocus.get(focusId) ?? 0) + 1);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Whether the focus has stdin turns queued but not yet resulted: the daemon has
|
|
41
|
+
* written more user messages than the CLI has completed (`turnCount`). The
|
|
42
|
+
* completion detector suppresses `complete` while this is true so a queued
|
|
43
|
+
* directive is delivered before teardown. False when nothing was written (the map
|
|
44
|
+
* has no entry) -- so a team with no injected directive completes normally.
|
|
45
|
+
*/
|
|
46
|
+
export function pendingStdinTurns(focusId, turnCount) {
|
|
47
|
+
return (sentByFocus.get(focusId) ?? 0) > turnCount;
|
|
48
|
+
}
|
|
49
|
+
/** Clear a focus's tracking on teardown (a respawn starts a fresh count / detector). */
|
|
50
|
+
export function clearStdinTurnTracking(focusId) {
|
|
51
|
+
sentByFocus.delete(focusId);
|
|
52
|
+
}
|
|
53
|
+
/** Test-only: reset all tracking. */
|
|
54
|
+
export function __resetStdinTurnTrackingForTesting() {
|
|
55
|
+
sentByFocus.clear();
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=stdin-turn-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdin-turn-tracker.js","sourceRoot":"","sources":["../../src/completion/stdin-turn-tracker.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,wEAAwE;AACxE,yDAAyD;AACzD,gBAAgB;AAEhB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,4EAA4E;AAC5E,MAAM,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAC;AAE9C;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAAe;IACpD,WAAW,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAChE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe,EAAE,SAAiB;IAClE,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC;AACrD,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,sBAAsB,CAAC,OAAe;IACpD,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AAC9B,CAAC;AAED,qCAAqC;AACrC,MAAM,UAAU,kCAAkC;IAChD,WAAW,CAAC,KAAK,EAAE,CAAC;AACtB,CAAC"}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supervision firing records -- the falsifiability instrument.
|
|
3
|
+
*
|
|
4
|
+
* The mechanisms that can END, HOLD, or RESPAWN a team (teardown-at-first-result,
|
|
5
|
+
* the spawn-gate-vs-exit respawn, the liveness badge, stall detectors, the verify
|
|
6
|
+
* interlock) were unfalsifiable: nothing recorded whether a firing was later
|
|
7
|
+
* borne out, so a false positive (the 80-spawn storm, the 5 lost compactions on
|
|
8
|
+
* 2026-09-06) was discoverable only by grepping the log after the cost was paid.
|
|
9
|
+
*
|
|
10
|
+
* This module gives every such mechanism a small firing record with a LATER-
|
|
11
|
+
* classified verdict: a firing predicts an outcome ("this team has no more
|
|
12
|
+
* actionable work"; "this respawn has work to do"; "this team is unresponsive"),
|
|
13
|
+
* and once reality is known the firing is classified true_positive (the
|
|
14
|
+
* prediction was borne out) or false_positive (it was not). The per-mechanism
|
|
15
|
+
* false-positive RATE is then a reading (surfaced in the heartbeat + metrics),
|
|
16
|
+
* and demotion from block to observe becomes a policy driven by that reading
|
|
17
|
+
* rather than a human hand-diagnosing a stuck mode.
|
|
18
|
+
*
|
|
19
|
+
* In-memory + bounded (a per-mechanism ring buffer): this is a live falsifiability
|
|
20
|
+
* signal for the running daemon, not a durable audit log. The ratchet pattern
|
|
21
|
+
* (scripts/ratchet-*.mjs) is the precedent for demote-on-reading; this is its
|
|
22
|
+
* runtime analogue.
|
|
23
|
+
*
|
|
24
|
+
* @module completion/supervision-firing-record
|
|
25
|
+
*/
|
|
26
|
+
/** The supervision mechanisms covered first (the three implicated 2026-09-06). */
|
|
27
|
+
export declare const SUPERVISION_MECHANISM: {
|
|
28
|
+
/** The completion handler ends a team at its first `result`. Predicts: the team
|
|
29
|
+
* had no more actionable work / nothing queued still to run. */
|
|
30
|
+
readonly TEARDOWN_AT_FIRST_RESULT: "teardown_at_first_result";
|
|
31
|
+
/** The spawn gate respawns a focus its exit classifier just declared done.
|
|
32
|
+
* Predicts: the respawned focus has team-actionable work. */
|
|
33
|
+
readonly SPAWN_GATE_VS_EXIT_RESPAWN: "spawn_gate_vs_exit_respawn";
|
|
34
|
+
/** The liveness badge marks a team unresponsive. Predicts: the team is wedged
|
|
35
|
+
* (not a legitimate multi-minute park). */
|
|
36
|
+
readonly LIVENESS_BADGE: "liveness_badge";
|
|
37
|
+
/** The poll-loop stalled-team backstop reaps a live `executing` team that has
|
|
38
|
+
* emitted no agent event past the backstop window, WITHOUT waiting for the
|
|
39
|
+
* completion event (which can be suppressed / never fire / be latched -- the
|
|
40
|
+
* 2026-09-07 wedge). Predicts: the idle team is wedged, not mid-long-operation. */
|
|
41
|
+
readonly STALLED_TEAM_REAP: "stalled_team_reap";
|
|
42
|
+
};
|
|
43
|
+
export type SupervisionMechanism = (typeof SUPERVISION_MECHANISM)[keyof typeof SUPERVISION_MECHANISM];
|
|
44
|
+
/** A firing's later-classified verdict. */
|
|
45
|
+
export type FiringVerdict = 'true_positive' | 'false_positive' | 'unclassified';
|
|
46
|
+
/** One recorded firing of a supervision mechanism. */
|
|
47
|
+
export interface SupervisionFiring {
|
|
48
|
+
/** Monotonic id, unique within this daemon run. */
|
|
49
|
+
id: number;
|
|
50
|
+
/** Which mechanism fired (a SUPERVISION_MECHANISM value; a string so a new
|
|
51
|
+
* mechanism can record without a type change). */
|
|
52
|
+
mechanism: string;
|
|
53
|
+
/** The focus the firing acted on. */
|
|
54
|
+
focusId: string;
|
|
55
|
+
/** Epoch ms the mechanism fired. */
|
|
56
|
+
firedAt: number;
|
|
57
|
+
/** The outcome the mechanism PREDICTED when it fired (free text, per mechanism). */
|
|
58
|
+
predicted: string;
|
|
59
|
+
/** Verdict, classified later from the observed outcome. */
|
|
60
|
+
verdict: FiringVerdict;
|
|
61
|
+
/**
|
|
62
|
+
* Whether the mechanism ACTUALLY took its kill/hold/respawn action when it
|
|
63
|
+
* fired. True by default (most firings act). Set false at the suppression sites
|
|
64
|
+
* when the mechanism is demoted to observe-only and records without acting.
|
|
65
|
+
* An UNACTED firing had no action to falsify, so the settle sweep must never
|
|
66
|
+
* true-positive it by silence -- doing so was the self-laundering bug (a
|
|
67
|
+
* demotion lifting itself and erasing the FP history that justified it). An
|
|
68
|
+
* unacted firing is scored ONLY by an active event that observes what the
|
|
69
|
+
* unblocked system actually went on to do; until then it stays unclassified.
|
|
70
|
+
*/
|
|
71
|
+
acted: boolean;
|
|
72
|
+
/**
|
|
73
|
+
* How the verdict was reached: 'active' (an observed falsifying/confirming
|
|
74
|
+
* event) vs 'sweep' (the settle window elapsed with no falsifying event).
|
|
75
|
+
* Absent while unclassified. Lets a reading tell a verdict EARNED in action
|
|
76
|
+
* from one inferred from silence.
|
|
77
|
+
*/
|
|
78
|
+
classifiedBy?: 'active' | 'sweep';
|
|
79
|
+
/** Epoch ms the verdict was classified (absent while unclassified). */
|
|
80
|
+
classifiedAt?: number;
|
|
81
|
+
/** The outcome actually observed at classification time (absent while unclassified). */
|
|
82
|
+
observed?: string;
|
|
83
|
+
}
|
|
84
|
+
/** Aggregate reading for one mechanism over a window. */
|
|
85
|
+
export interface FiringStats {
|
|
86
|
+
mechanism: string;
|
|
87
|
+
total: number;
|
|
88
|
+
truePositives: number;
|
|
89
|
+
falsePositives: number;
|
|
90
|
+
unclassified: number;
|
|
91
|
+
/** falsePositives / (truePositives + falsePositives); null when none are
|
|
92
|
+
* classified yet (a rate over zero classified firings is undefined, never 0). */
|
|
93
|
+
falsePositiveRate: number | null;
|
|
94
|
+
/** Firings recorded while the mechanism was demoted (acted===false): observe-only
|
|
95
|
+
* counterfactuals, never true-positived by silence. */
|
|
96
|
+
unacted: number;
|
|
97
|
+
/** True positives reached by an OBSERVED event (classifiedBy==='active'). */
|
|
98
|
+
activeTruePositives: number;
|
|
99
|
+
/** True positives inferred from the settle window elapsing (classifiedBy==='sweep').
|
|
100
|
+
* A reading that separates accuracy EARNED in action from the benefit of the
|
|
101
|
+
* doubt silence gives an acted firing. */
|
|
102
|
+
sweptTruePositives: number;
|
|
103
|
+
}
|
|
104
|
+
/** Max firings retained per mechanism (oldest evicted). Bounds memory; the rate
|
|
105
|
+
* is a recency signal, not a full history. */
|
|
106
|
+
export declare const FIRING_BUFFER_CAP = 256;
|
|
107
|
+
/**
|
|
108
|
+
* Settle window: an ACTED prediction that survives this long with no falsifying
|
|
109
|
+
* event is swept to true_positive. It is also the FP horizon by construction -- an
|
|
110
|
+
* active classifier (e.g. a teardown falsified by a re-ready focus) can only fire
|
|
111
|
+
* while the firing is still unclassified, i.e. younger than this. So a teardown a
|
|
112
|
+
* focus contradicts QUICKLY (the storm) is FP; one that stands past the window is
|
|
113
|
+
* TP. One knob. 3 minutes: longer than a spawn cooldown / re-ready cycle, short
|
|
114
|
+
* enough that the rate reflects recent behavior.
|
|
115
|
+
*
|
|
116
|
+
* The sweep applies ONLY to ACTED firings: an unacted firing (recorded while the
|
|
117
|
+
* mechanism was demoted to observe-only) had no action, so silence proves nothing
|
|
118
|
+
* about it. True-positiving unacted firings by silence was the self-laundering bug
|
|
119
|
+
* -- it lifted a demotion within one settle window and erased the FP history that
|
|
120
|
+
* justified it, regardless of the mechanism's real accuracy.
|
|
121
|
+
*/
|
|
122
|
+
export declare const FIRING_SETTLE_WINDOW_MS = 180000;
|
|
123
|
+
/**
|
|
124
|
+
* Record a mechanism firing. Returns the firing id so the caller can classify it
|
|
125
|
+
* later once the predicted outcome is known. The buffer is bounded per mechanism.
|
|
126
|
+
*/
|
|
127
|
+
export declare function recordFiring(args: {
|
|
128
|
+
mechanism: string;
|
|
129
|
+
focusId: string;
|
|
130
|
+
predicted: string;
|
|
131
|
+
now?: number;
|
|
132
|
+
}): number;
|
|
133
|
+
/**
|
|
134
|
+
* Flip the MOST RECENT firing of `mechanism` for `focusId` to UNACTED -- the
|
|
135
|
+
* mechanism recorded it but, being demoted to observe-only, did NOT take its
|
|
136
|
+
* kill/hold/respawn action. An unacted firing is never true-positived by the
|
|
137
|
+
* settle sweep (silence cannot vindicate an action that never happened); it is
|
|
138
|
+
* scored only by an active event observing what the unblocked system did. Returns
|
|
139
|
+
* true if a firing was found and flipped. Idempotent: re-flipping an already
|
|
140
|
+
* unacted firing is a no-op.
|
|
141
|
+
*/
|
|
142
|
+
export declare function markLatestFiringUnacted(mechanism: string, focusId: string): boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Classify a recorded firing from the outcome it predicted: `borneOut` true means
|
|
145
|
+
* the prediction held (true_positive), false means it did not (false_positive).
|
|
146
|
+
* A no-op if the id is unknown (evicted or never recorded) or already classified.
|
|
147
|
+
*/
|
|
148
|
+
export declare function classifyFiring(id: number, args: {
|
|
149
|
+
borneOut: boolean;
|
|
150
|
+
observed: string;
|
|
151
|
+
now?: number;
|
|
152
|
+
}): void;
|
|
153
|
+
/**
|
|
154
|
+
* Aggregate the reading for one mechanism over the firings within `windowMs`
|
|
155
|
+
* (default: all retained). The false-positive rate is over CLASSIFIED firings
|
|
156
|
+
* only; null when none are classified yet.
|
|
157
|
+
*/
|
|
158
|
+
export declare function firingStats(mechanism: string, opts?: {
|
|
159
|
+
windowMs?: number;
|
|
160
|
+
now?: number;
|
|
161
|
+
}): FiringStats;
|
|
162
|
+
/**
|
|
163
|
+
* Classify the MOST RECENT still-unclassified firing of `mechanism` for `focusId`
|
|
164
|
+
* from the outcome it predicted, and report whether one was found. The active
|
|
165
|
+
* classification path: a caller that observes the falsifying (or confirming)
|
|
166
|
+
* event for a focus -- e.g. the spawn gate respawning a focus a teardown just
|
|
167
|
+
* declared work-free -- classifies that specific prior firing without threading
|
|
168
|
+
* its id through unrelated code. A no-op (returns false) when the focus has no
|
|
169
|
+
* unclassified firing of that mechanism (e.g. a first-ever spawn, no prior
|
|
170
|
+
* teardown).
|
|
171
|
+
*/
|
|
172
|
+
export declare function classifyLatestUnclassifiedFiring(mechanism: string, focusId: string, args: {
|
|
173
|
+
borneOut: boolean;
|
|
174
|
+
observed: string;
|
|
175
|
+
now?: number;
|
|
176
|
+
}): boolean;
|
|
177
|
+
/**
|
|
178
|
+
* Settle sweep: classify every still-unclassified ACTED firing older than
|
|
179
|
+
* `olderThanMs` as borne out (true_positive) -- the prediction HELD because no
|
|
180
|
+
* falsifying event arrived within the settle window (FPs are classified actively
|
|
181
|
+
* at their falsifying event, so an aged unclassified ACTED firing is one nothing
|
|
182
|
+
* contradicted). UNACTED firings are skipped: a firing whose mechanism was demoted
|
|
183
|
+
* and took no action has nothing for silence to vindicate, so sweeping it to TP
|
|
184
|
+
* would launder the demotion (lift it and erase the FP record that justified it).
|
|
185
|
+
* An unacted firing is scored only by an active event. Optionally scoped to one
|
|
186
|
+
* `mechanism`. Returns the number classified. Idempotent and cheap; call it from
|
|
187
|
+
* the poll loop.
|
|
188
|
+
*/
|
|
189
|
+
export declare function sweepAgedFirings(args: {
|
|
190
|
+
olderThanMs: number;
|
|
191
|
+
now?: number;
|
|
192
|
+
mechanism?: string;
|
|
193
|
+
observed?: string;
|
|
194
|
+
}): number;
|
|
195
|
+
/** Min classified firings before demotion can be recommended -- a single early FP
|
|
196
|
+
* must not disable a mechanism. */
|
|
197
|
+
export declare const DEMOTE_MIN_CLASSIFIED = 8;
|
|
198
|
+
/** Window the demotion policy reads the rate over (1 hour). */
|
|
199
|
+
export declare const DEMOTE_WINDOW_MS = 3600000;
|
|
200
|
+
/**
|
|
201
|
+
* Demotion policy -- the CONDITION a mechanism must meet to be demoted to
|
|
202
|
+
* observe-only. This is the pure predicate; the heartbeat surfaces it as a
|
|
203
|
+
* reading (demotionRecommended) and the action sites ENFORCE it via
|
|
204
|
+
* isMechanismDemoted (below). "Converge first" (the focus statement) was about
|
|
205
|
+
* not DELETING supervision code -- demotion to observe-only is a runtime,
|
|
206
|
+
* reversible, data-driven gate, which the AC requires be enforced, not merely
|
|
207
|
+
* reported (AC/FRT #10: "demoted to observe-only -- it records but no longer
|
|
208
|
+
* kills or holds -- by policy").
|
|
209
|
+
*
|
|
210
|
+
* A mechanism has earned demotion when, over the window, it has at least
|
|
211
|
+
* DEMOTE_MIN_CLASSIFIED classified firings AND ZERO true positives -- it has NEVER
|
|
212
|
+
* been right in a meaningful sample. The min-sample guard keeps a single early FP
|
|
213
|
+
* from disabling a mechanism. The reading is over a window and self-lifting: as a
|
|
214
|
+
* demoted mechanism's firings age (or sweep to TP because, no longer acting, it
|
|
215
|
+
* has nothing left to falsify them), the condition clears and the mechanism
|
|
216
|
+
* re-arms -- a brief circuit-break during a storm, not a permanent disable.
|
|
217
|
+
*/
|
|
218
|
+
export declare function isDemotionRecommended(mechanism: string, opts?: {
|
|
219
|
+
windowMs?: number;
|
|
220
|
+
minClassified?: number;
|
|
221
|
+
now?: number;
|
|
222
|
+
}): boolean;
|
|
223
|
+
/**
|
|
224
|
+
* Enforcement gate: whether a mechanism is currently DEMOTED and its caller must
|
|
225
|
+
* therefore skip the kill/hold/respawn (record the firing, take no action). The
|
|
226
|
+
* single seam every action site reads so the block-to-observe policy lives in one
|
|
227
|
+
* place -- and so it can later diverge from the raw reading (e.g. an operator
|
|
228
|
+
* kill-switch, or a per-mechanism min-sample) without touching every call site.
|
|
229
|
+
* Same condition as isDemotionRecommended today; a distinct name so an action
|
|
230
|
+
* site reads as enforcement ("if demoted, do not terminate") rather than as the
|
|
231
|
+
* advisory reading the heartbeat shows.
|
|
232
|
+
*/
|
|
233
|
+
export declare function isMechanismDemoted(mechanism: string, opts?: {
|
|
234
|
+
windowMs?: number;
|
|
235
|
+
minClassified?: number;
|
|
236
|
+
now?: number;
|
|
237
|
+
}): boolean;
|
|
238
|
+
/** The mechanisms that currently have at least one recorded firing. */
|
|
239
|
+
export declare function firingMechanisms(): string[];
|
|
240
|
+
/** Test-only: clear all recorded firings and reset the id counter. */
|
|
241
|
+
export declare function __resetFiringRecordsForTesting(): void;
|
|
242
|
+
//# sourceMappingURL=supervision-firing-record.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supervision-firing-record.d.ts","sourceRoot":"","sources":["../../src/completion/supervision-firing-record.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
// PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
|
|
2
|
+
// Pinned by: packages/daemon/src/completion/liveness-firing.test.ts,
|
|
3
|
+
// packages/daemon/src/completion/supervision-firing-record.test.ts,
|
|
4
|
+
// packages/daemon/src/completion/teardown-demotion-gate.test.ts,
|
|
5
|
+
// packages/daemon/src/focus/respawn-demotion-gate.test.ts,
|
|
6
|
+
// packages/daemon/src/focus/supervision-firing-correlation.test.ts,
|
|
7
|
+
// packages/daemon/src/shell/supervision-firing-rates.test.ts,
|
|
8
|
+
// packages/daemon/src/verify/stalled-team-backstop-detector.test.ts
|
|
9
|
+
// PINNED-BY-END
|
|
10
|
+
/**
|
|
11
|
+
* Supervision firing records -- the falsifiability instrument.
|
|
12
|
+
*
|
|
13
|
+
* The mechanisms that can END, HOLD, or RESPAWN a team (teardown-at-first-result,
|
|
14
|
+
* the spawn-gate-vs-exit respawn, the liveness badge, stall detectors, the verify
|
|
15
|
+
* interlock) were unfalsifiable: nothing recorded whether a firing was later
|
|
16
|
+
* borne out, so a false positive (the 80-spawn storm, the 5 lost compactions on
|
|
17
|
+
* 2026-09-06) was discoverable only by grepping the log after the cost was paid.
|
|
18
|
+
*
|
|
19
|
+
* This module gives every such mechanism a small firing record with a LATER-
|
|
20
|
+
* classified verdict: a firing predicts an outcome ("this team has no more
|
|
21
|
+
* actionable work"; "this respawn has work to do"; "this team is unresponsive"),
|
|
22
|
+
* and once reality is known the firing is classified true_positive (the
|
|
23
|
+
* prediction was borne out) or false_positive (it was not). The per-mechanism
|
|
24
|
+
* false-positive RATE is then a reading (surfaced in the heartbeat + metrics),
|
|
25
|
+
* and demotion from block to observe becomes a policy driven by that reading
|
|
26
|
+
* rather than a human hand-diagnosing a stuck mode.
|
|
27
|
+
*
|
|
28
|
+
* In-memory + bounded (a per-mechanism ring buffer): this is a live falsifiability
|
|
29
|
+
* signal for the running daemon, not a durable audit log. The ratchet pattern
|
|
30
|
+
* (scripts/ratchet-*.mjs) is the precedent for demote-on-reading; this is its
|
|
31
|
+
* runtime analogue.
|
|
32
|
+
*
|
|
33
|
+
* @module completion/supervision-firing-record
|
|
34
|
+
*/
|
|
35
|
+
/** The supervision mechanisms covered first (the three implicated 2026-09-06). */
|
|
36
|
+
export const SUPERVISION_MECHANISM = {
|
|
37
|
+
/** The completion handler ends a team at its first `result`. Predicts: the team
|
|
38
|
+
* had no more actionable work / nothing queued still to run. */
|
|
39
|
+
TEARDOWN_AT_FIRST_RESULT: 'teardown_at_first_result',
|
|
40
|
+
/** The spawn gate respawns a focus its exit classifier just declared done.
|
|
41
|
+
* Predicts: the respawned focus has team-actionable work. */
|
|
42
|
+
SPAWN_GATE_VS_EXIT_RESPAWN: 'spawn_gate_vs_exit_respawn',
|
|
43
|
+
/** The liveness badge marks a team unresponsive. Predicts: the team is wedged
|
|
44
|
+
* (not a legitimate multi-minute park). */
|
|
45
|
+
LIVENESS_BADGE: 'liveness_badge',
|
|
46
|
+
/** The poll-loop stalled-team backstop reaps a live `executing` team that has
|
|
47
|
+
* emitted no agent event past the backstop window, WITHOUT waiting for the
|
|
48
|
+
* completion event (which can be suppressed / never fire / be latched -- the
|
|
49
|
+
* 2026-09-07 wedge). Predicts: the idle team is wedged, not mid-long-operation. */
|
|
50
|
+
STALLED_TEAM_REAP: 'stalled_team_reap',
|
|
51
|
+
};
|
|
52
|
+
/** Max firings retained per mechanism (oldest evicted). Bounds memory; the rate
|
|
53
|
+
* is a recency signal, not a full history. */
|
|
54
|
+
export const FIRING_BUFFER_CAP = 256;
|
|
55
|
+
/**
|
|
56
|
+
* Settle window: an ACTED prediction that survives this long with no falsifying
|
|
57
|
+
* event is swept to true_positive. It is also the FP horizon by construction -- an
|
|
58
|
+
* active classifier (e.g. a teardown falsified by a re-ready focus) can only fire
|
|
59
|
+
* while the firing is still unclassified, i.e. younger than this. So a teardown a
|
|
60
|
+
* focus contradicts QUICKLY (the storm) is FP; one that stands past the window is
|
|
61
|
+
* TP. One knob. 3 minutes: longer than a spawn cooldown / re-ready cycle, short
|
|
62
|
+
* enough that the rate reflects recent behavior.
|
|
63
|
+
*
|
|
64
|
+
* The sweep applies ONLY to ACTED firings: an unacted firing (recorded while the
|
|
65
|
+
* mechanism was demoted to observe-only) had no action, so silence proves nothing
|
|
66
|
+
* about it. True-positiving unacted firings by silence was the self-laundering bug
|
|
67
|
+
* -- it lifted a demotion within one settle window and erased the FP history that
|
|
68
|
+
* justified it, regardless of the mechanism's real accuracy.
|
|
69
|
+
*/
|
|
70
|
+
export const FIRING_SETTLE_WINDOW_MS = 180_000;
|
|
71
|
+
const firings = new Map();
|
|
72
|
+
let nextId = 1;
|
|
73
|
+
/**
|
|
74
|
+
* Record a mechanism firing. Returns the firing id so the caller can classify it
|
|
75
|
+
* later once the predicted outcome is known. The buffer is bounded per mechanism.
|
|
76
|
+
*/
|
|
77
|
+
export function recordFiring(args) {
|
|
78
|
+
const id = nextId++;
|
|
79
|
+
const firing = {
|
|
80
|
+
id,
|
|
81
|
+
mechanism: args.mechanism,
|
|
82
|
+
focusId: args.focusId,
|
|
83
|
+
firedAt: args.now ?? Date.now(),
|
|
84
|
+
predicted: args.predicted,
|
|
85
|
+
verdict: 'unclassified',
|
|
86
|
+
// Recorded as ACTED by default: most firings come from a mechanism that took
|
|
87
|
+
// its action. A suppression site that records-without-acting (demoted to
|
|
88
|
+
// observe-only) calls markLatestFiringUnacted right after to flip this.
|
|
89
|
+
acted: true,
|
|
90
|
+
};
|
|
91
|
+
const buf = firings.get(args.mechanism) ?? [];
|
|
92
|
+
buf.push(firing);
|
|
93
|
+
if (buf.length > FIRING_BUFFER_CAP)
|
|
94
|
+
buf.shift();
|
|
95
|
+
firings.set(args.mechanism, buf);
|
|
96
|
+
return id;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Flip the MOST RECENT firing of `mechanism` for `focusId` to UNACTED -- the
|
|
100
|
+
* mechanism recorded it but, being demoted to observe-only, did NOT take its
|
|
101
|
+
* kill/hold/respawn action. An unacted firing is never true-positived by the
|
|
102
|
+
* settle sweep (silence cannot vindicate an action that never happened); it is
|
|
103
|
+
* scored only by an active event observing what the unblocked system did. Returns
|
|
104
|
+
* true if a firing was found and flipped. Idempotent: re-flipping an already
|
|
105
|
+
* unacted firing is a no-op.
|
|
106
|
+
*/
|
|
107
|
+
export function markLatestFiringUnacted(mechanism, focusId) {
|
|
108
|
+
const buf = firings.get(mechanism);
|
|
109
|
+
if (!buf)
|
|
110
|
+
return false;
|
|
111
|
+
for (let i = buf.length - 1; i >= 0; i--) {
|
|
112
|
+
const f = buf[i];
|
|
113
|
+
if (f && f.focusId === focusId) {
|
|
114
|
+
f.acted = false;
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Classify a recorded firing from the outcome it predicted: `borneOut` true means
|
|
122
|
+
* the prediction held (true_positive), false means it did not (false_positive).
|
|
123
|
+
* A no-op if the id is unknown (evicted or never recorded) or already classified.
|
|
124
|
+
*/
|
|
125
|
+
export function classifyFiring(id, args) {
|
|
126
|
+
for (const buf of firings.values()) {
|
|
127
|
+
const firing = buf.find((f) => f.id === id);
|
|
128
|
+
if (!firing)
|
|
129
|
+
continue;
|
|
130
|
+
if (firing.verdict !== 'unclassified')
|
|
131
|
+
return;
|
|
132
|
+
firing.verdict = args.borneOut ? 'true_positive' : 'false_positive';
|
|
133
|
+
firing.classifiedBy = 'active';
|
|
134
|
+
firing.classifiedAt = args.now ?? Date.now();
|
|
135
|
+
firing.observed = args.observed;
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Aggregate the reading for one mechanism over the firings within `windowMs`
|
|
141
|
+
* (default: all retained). The false-positive rate is over CLASSIFIED firings
|
|
142
|
+
* only; null when none are classified yet.
|
|
143
|
+
*/
|
|
144
|
+
export function firingStats(mechanism, opts = {}) {
|
|
145
|
+
const now = opts.now ?? Date.now();
|
|
146
|
+
const cutoff = opts.windowMs === undefined ? -Infinity : now - opts.windowMs;
|
|
147
|
+
const buf = (firings.get(mechanism) ?? []).filter((f) => f.firedAt >= cutoff);
|
|
148
|
+
let truePositives = 0;
|
|
149
|
+
let falsePositives = 0;
|
|
150
|
+
let unclassified = 0;
|
|
151
|
+
let unacted = 0;
|
|
152
|
+
let activeTruePositives = 0;
|
|
153
|
+
let sweptTruePositives = 0;
|
|
154
|
+
for (const f of buf) {
|
|
155
|
+
if (!f.acted)
|
|
156
|
+
unacted++;
|
|
157
|
+
if (f.verdict === 'true_positive') {
|
|
158
|
+
truePositives++;
|
|
159
|
+
if (f.classifiedBy === 'sweep')
|
|
160
|
+
sweptTruePositives++;
|
|
161
|
+
else
|
|
162
|
+
activeTruePositives++;
|
|
163
|
+
}
|
|
164
|
+
else if (f.verdict === 'false_positive')
|
|
165
|
+
falsePositives++;
|
|
166
|
+
else
|
|
167
|
+
unclassified++;
|
|
168
|
+
}
|
|
169
|
+
const classified = truePositives + falsePositives;
|
|
170
|
+
return {
|
|
171
|
+
mechanism,
|
|
172
|
+
total: buf.length,
|
|
173
|
+
truePositives,
|
|
174
|
+
falsePositives,
|
|
175
|
+
unclassified,
|
|
176
|
+
falsePositiveRate: classified === 0 ? null : falsePositives / classified,
|
|
177
|
+
unacted,
|
|
178
|
+
activeTruePositives,
|
|
179
|
+
sweptTruePositives,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Classify the MOST RECENT still-unclassified firing of `mechanism` for `focusId`
|
|
184
|
+
* from the outcome it predicted, and report whether one was found. The active
|
|
185
|
+
* classification path: a caller that observes the falsifying (or confirming)
|
|
186
|
+
* event for a focus -- e.g. the spawn gate respawning a focus a teardown just
|
|
187
|
+
* declared work-free -- classifies that specific prior firing without threading
|
|
188
|
+
* its id through unrelated code. A no-op (returns false) when the focus has no
|
|
189
|
+
* unclassified firing of that mechanism (e.g. a first-ever spawn, no prior
|
|
190
|
+
* teardown).
|
|
191
|
+
*/
|
|
192
|
+
export function classifyLatestUnclassifiedFiring(mechanism, focusId, args) {
|
|
193
|
+
const buf = firings.get(mechanism);
|
|
194
|
+
if (!buf)
|
|
195
|
+
return false;
|
|
196
|
+
for (let i = buf.length - 1; i >= 0; i--) {
|
|
197
|
+
const f = buf[i];
|
|
198
|
+
if (f && f.focusId === focusId && f.verdict === 'unclassified') {
|
|
199
|
+
f.verdict = args.borneOut ? 'true_positive' : 'false_positive';
|
|
200
|
+
f.classifiedBy = 'active';
|
|
201
|
+
f.classifiedAt = args.now ?? Date.now();
|
|
202
|
+
f.observed = args.observed;
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Settle sweep: classify every still-unclassified ACTED firing older than
|
|
210
|
+
* `olderThanMs` as borne out (true_positive) -- the prediction HELD because no
|
|
211
|
+
* falsifying event arrived within the settle window (FPs are classified actively
|
|
212
|
+
* at their falsifying event, so an aged unclassified ACTED firing is one nothing
|
|
213
|
+
* contradicted). UNACTED firings are skipped: a firing whose mechanism was demoted
|
|
214
|
+
* and took no action has nothing for silence to vindicate, so sweeping it to TP
|
|
215
|
+
* would launder the demotion (lift it and erase the FP record that justified it).
|
|
216
|
+
* An unacted firing is scored only by an active event. Optionally scoped to one
|
|
217
|
+
* `mechanism`. Returns the number classified. Idempotent and cheap; call it from
|
|
218
|
+
* the poll loop.
|
|
219
|
+
*/
|
|
220
|
+
export function sweepAgedFirings(args) {
|
|
221
|
+
const now = args.now ?? Date.now();
|
|
222
|
+
const cutoff = now - args.olderThanMs;
|
|
223
|
+
const bufs = args.mechanism
|
|
224
|
+
? [firings.get(args.mechanism)].filter((b) => b !== undefined)
|
|
225
|
+
: [...firings.values()];
|
|
226
|
+
let swept = 0;
|
|
227
|
+
for (const buf of bufs) {
|
|
228
|
+
for (const f of buf) {
|
|
229
|
+
// Skip UNACTED firings: no action happened, so the settle window elapsing
|
|
230
|
+
// proves nothing -- true-positiving it by silence is the self-laundering bug.
|
|
231
|
+
if (f.verdict === 'unclassified' && f.acted && f.firedAt < cutoff) {
|
|
232
|
+
f.verdict = 'true_positive';
|
|
233
|
+
f.classifiedBy = 'sweep';
|
|
234
|
+
f.classifiedAt = now;
|
|
235
|
+
f.observed = args.observed ?? 'settle window elapsed with no falsifying event';
|
|
236
|
+
swept++;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return swept;
|
|
241
|
+
}
|
|
242
|
+
/** Min classified firings before demotion can be recommended -- a single early FP
|
|
243
|
+
* must not disable a mechanism. */
|
|
244
|
+
export const DEMOTE_MIN_CLASSIFIED = 8;
|
|
245
|
+
/** Window the demotion policy reads the rate over (1 hour). */
|
|
246
|
+
export const DEMOTE_WINDOW_MS = 3_600_000;
|
|
247
|
+
/**
|
|
248
|
+
* Demotion policy -- the CONDITION a mechanism must meet to be demoted to
|
|
249
|
+
* observe-only. This is the pure predicate; the heartbeat surfaces it as a
|
|
250
|
+
* reading (demotionRecommended) and the action sites ENFORCE it via
|
|
251
|
+
* isMechanismDemoted (below). "Converge first" (the focus statement) was about
|
|
252
|
+
* not DELETING supervision code -- demotion to observe-only is a runtime,
|
|
253
|
+
* reversible, data-driven gate, which the AC requires be enforced, not merely
|
|
254
|
+
* reported (AC/FRT #10: "demoted to observe-only -- it records but no longer
|
|
255
|
+
* kills or holds -- by policy").
|
|
256
|
+
*
|
|
257
|
+
* A mechanism has earned demotion when, over the window, it has at least
|
|
258
|
+
* DEMOTE_MIN_CLASSIFIED classified firings AND ZERO true positives -- it has NEVER
|
|
259
|
+
* been right in a meaningful sample. The min-sample guard keeps a single early FP
|
|
260
|
+
* from disabling a mechanism. The reading is over a window and self-lifting: as a
|
|
261
|
+
* demoted mechanism's firings age (or sweep to TP because, no longer acting, it
|
|
262
|
+
* has nothing left to falsify them), the condition clears and the mechanism
|
|
263
|
+
* re-arms -- a brief circuit-break during a storm, not a permanent disable.
|
|
264
|
+
*/
|
|
265
|
+
export function isDemotionRecommended(mechanism, opts = {}) {
|
|
266
|
+
const s = firingStats(mechanism, { windowMs: opts.windowMs ?? DEMOTE_WINDOW_MS, now: opts.now });
|
|
267
|
+
const classified = s.truePositives + s.falsePositives;
|
|
268
|
+
return classified >= (opts.minClassified ?? DEMOTE_MIN_CLASSIFIED) && s.truePositives === 0;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Enforcement gate: whether a mechanism is currently DEMOTED and its caller must
|
|
272
|
+
* therefore skip the kill/hold/respawn (record the firing, take no action). The
|
|
273
|
+
* single seam every action site reads so the block-to-observe policy lives in one
|
|
274
|
+
* place -- and so it can later diverge from the raw reading (e.g. an operator
|
|
275
|
+
* kill-switch, or a per-mechanism min-sample) without touching every call site.
|
|
276
|
+
* Same condition as isDemotionRecommended today; a distinct name so an action
|
|
277
|
+
* site reads as enforcement ("if demoted, do not terminate") rather than as the
|
|
278
|
+
* advisory reading the heartbeat shows.
|
|
279
|
+
*/
|
|
280
|
+
export function isMechanismDemoted(mechanism, opts = {}) {
|
|
281
|
+
return isDemotionRecommended(mechanism, opts);
|
|
282
|
+
}
|
|
283
|
+
/** The mechanisms that currently have at least one recorded firing. */
|
|
284
|
+
export function firingMechanisms() {
|
|
285
|
+
return [...firings.keys()];
|
|
286
|
+
}
|
|
287
|
+
/** Test-only: clear all recorded firings and reset the id counter. */
|
|
288
|
+
export function __resetFiringRecordsForTesting() {
|
|
289
|
+
firings.clear();
|
|
290
|
+
nextId = 1;
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=supervision-firing-record.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supervision-firing-record.js","sourceRoot":"","sources":["../../src/completion/supervision-firing-record.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"team-lifecycle.d.ts","sourceRoot":"","sources":["../../src/completion/team-lifecycle.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"team-lifecycle.d.ts","sourceRoot":"","sources":["../../src/completion/team-lifecycle.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AA+B5D;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,cAAc,CAAC,EAAE,cAAc,CAAC,gBAAgB,CAAC,GAChD,OAAO,CA4ET;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,SAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CAsB1F;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAKxC;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,mFAAmF;IACnF,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,6EAA6E;IAC7E,WAAW,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC;CACtD;AAcD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,qBAAqB,GAC1B,uBAAuB,CAiDzB;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,mFAAmF;IACnF,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACnC,0EAA0E;IAC1E,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC;CACtD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,mBAAmB,GACxB,uBAAuB,CAwDzB;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,IAAI,CAAC,CAyBf;AAuCD;;;;;;;;;;;;GAYG;AACH,wBAAsB,gCAAgC,CACpD,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,IAAI,CAAC,CAwIf"}
|