@telora/daemon 0.22.181 → 0.22.189
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 +9 -3
- package/dist/audit/audit-floor-policy.d.ts +188 -0
- package/dist/audit/audit-floor-policy.d.ts.map +1 -0
- package/dist/audit/audit-floor-policy.js +353 -0
- package/dist/audit/audit-floor-policy.js.map +1 -0
- package/dist/audit/audit-prompt.d.ts +4 -177
- package/dist/audit/audit-prompt.d.ts.map +1 -1
- package/dist/audit/audit-prompt.js +6 -329
- package/dist/audit/audit-prompt.js.map +1 -1
- package/dist/completion/escalations.d.ts.map +1 -1
- package/dist/completion/escalations.js +4 -1
- package/dist/completion/escalations.js.map +1 -1
- package/dist/completion/review-exit-phase.d.ts.map +1 -1
- package/dist/completion/review-exit-phase.js +11 -4
- package/dist/completion/review-exit-phase.js.map +1 -1
- package/dist/completion/standing-approval-convergence.d.ts.map +1 -1
- package/dist/completion/standing-approval-convergence.js +2 -1
- package/dist/completion/standing-approval-convergence.js.map +1 -1
- package/dist/focus/focus-executor.d.ts.map +1 -1
- package/dist/focus/focus-executor.js +2 -0
- package/dist/focus/focus-executor.js.map +1 -1
- package/dist/focus/focus-loop-registry.js +2 -2
- package/dist/focus/focus-loop-registry.js.map +1 -1
- package/dist/focus/session-health.d.ts.map +1 -1
- package/dist/focus/session-health.js +2 -1
- package/dist/focus/session-health.js.map +1 -1
- package/dist/focus-stage-lifecycle/planning-release.d.ts +52 -17
- package/dist/focus-stage-lifecycle/planning-release.d.ts.map +1 -1
- package/dist/focus-stage-lifecycle/planning-release.js +58 -70
- package/dist/focus-stage-lifecycle/planning-release.js.map +1 -1
- package/dist/focus-stage-lifecycle/ratify-bar-gate.d.ts +108 -0
- package/dist/focus-stage-lifecycle/ratify-bar-gate.d.ts.map +1 -0
- package/dist/focus-stage-lifecycle/ratify-bar-gate.js +174 -0
- package/dist/focus-stage-lifecycle/ratify-bar-gate.js.map +1 -0
- package/dist/git/merge-to-main.d.ts.map +1 -1
- package/dist/git/merge-to-main.js.map +1 -1
- package/dist/loops/llm-client.d.ts +1 -1
- package/dist/loops/llm-client.d.ts.map +1 -1
- package/dist/loops/llm-client.js +18 -105
- package/dist/loops/llm-client.js.map +1 -1
- package/dist/nexus/index.d.ts.map +1 -1
- package/dist/nexus/index.js +2 -1
- package/dist/nexus/index.js.map +1 -1
- package/dist/nexus/intake-prompt-sections.d.ts +70 -0
- package/dist/nexus/intake-prompt-sections.d.ts.map +1 -0
- package/dist/nexus/intake-prompt-sections.js +145 -0
- package/dist/nexus/intake-prompt-sections.js.map +1 -0
- package/dist/nexus/intake-spawn.d.ts +49 -13
- package/dist/nexus/intake-spawn.d.ts.map +1 -1
- package/dist/nexus/intake-spawn.js +45 -122
- package/dist/nexus/intake-spawn.js.map +1 -1
- package/dist/nexus/intake.d.ts +55 -0
- package/dist/nexus/intake.d.ts.map +1 -1
- package/dist/nexus/intake.js +62 -1
- package/dist/nexus/intake.js.map +1 -1
- package/dist/prd/arm-readiness.d.ts +93 -0
- package/dist/prd/arm-readiness.d.ts.map +1 -0
- package/dist/prd/arm-readiness.js +254 -0
- package/dist/prd/arm-readiness.js.map +1 -0
- package/dist/prd/arm-refusal.d.ts +149 -0
- package/dist/prd/arm-refusal.d.ts.map +1 -0
- package/dist/prd/arm-refusal.js +219 -0
- package/dist/prd/arm-refusal.js.map +1 -0
- package/dist/prd/controller-shared.d.ts +111 -0
- package/dist/prd/controller-shared.d.ts.map +1 -1
- package/dist/prd/controller-shared.js +3 -1
- package/dist/prd/controller-shared.js.map +1 -1
- package/dist/prd/controller.d.ts +16 -10
- package/dist/prd/controller.d.ts.map +1 -1
- package/dist/prd/controller.js +28 -35
- package/dist/prd/controller.js.map +1 -1
- package/dist/prd/frontier-arming.d.ts +151 -0
- package/dist/prd/frontier-arming.d.ts.map +1 -0
- package/dist/prd/frontier-arming.js +315 -0
- package/dist/prd/frontier-arming.js.map +1 -0
- package/dist/prd/index.d.ts +10 -1
- package/dist/prd/index.d.ts.map +1 -1
- package/dist/prd/index.js +9 -1
- package/dist/prd/index.js.map +1 -1
- package/dist/prd/scoping-prompt.d.ts +68 -0
- package/dist/prd/scoping-prompt.d.ts.map +1 -0
- package/dist/prd/scoping-prompt.js +86 -0
- package/dist/prd/scoping-prompt.js.map +1 -0
- package/dist/prompt/prompt-contradictions.d.ts +20 -1
- package/dist/prompt/prompt-contradictions.d.ts.map +1 -1
- package/dist/prompt/prompt-contradictions.js +35 -0
- package/dist/prompt/prompt-contradictions.js.map +1 -1
- package/dist/prompt/team-prompt-base.d.ts +18 -0
- package/dist/prompt/team-prompt-base.d.ts.map +1 -1
- package/dist/prompt/team-prompt-base.js +56 -11
- package/dist/prompt/team-prompt-base.js.map +1 -1
- package/dist/prompt-sections/persona-sections.d.ts +36 -13
- package/dist/prompt-sections/persona-sections.d.ts.map +1 -1
- package/dist/prompt-sections/persona-sections.js +93 -16
- package/dist/prompt-sections/persona-sections.js.map +1 -1
- package/dist/prompt-sections/scoping-sections.d.ts +19 -5
- package/dist/prompt-sections/scoping-sections.d.ts.map +1 -1
- package/dist/prompt-sections/scoping-sections.js +45 -38
- package/dist/prompt-sections/scoping-sections.js.map +1 -1
- package/dist/prompt-sections/shared.d.ts +8 -0
- package/dist/prompt-sections/shared.d.ts.map +1 -1
- package/dist/prompt-sections/shared.js.map +1 -1
- package/dist/queries/agent-resolvable-promotion.d.ts +12 -0
- package/dist/queries/agent-resolvable-promotion.d.ts.map +1 -1
- package/dist/queries/agent-resolvable-promotion.js +17 -6
- package/dist/queries/agent-resolvable-promotion.js.map +1 -1
- package/dist/queries/delivery-gate-state.d.ts +3 -0
- package/dist/queries/delivery-gate-state.d.ts.map +1 -1
- package/dist/queries/delivery-gate-state.js +1 -1
- package/dist/queries/delivery-gate-state.js.map +1 -1
- package/dist/queries/focus-criteria-standing.d.ts +170 -7
- package/dist/queries/focus-criteria-standing.d.ts.map +1 -1
- package/dist/queries/focus-criteria-standing.js +143 -8
- package/dist/queries/focus-criteria-standing.js.map +1 -1
- package/dist/queries/focus-ratify-release.d.ts +43 -0
- package/dist/queries/focus-ratify-release.d.ts.map +1 -0
- package/dist/queries/focus-ratify-release.js +45 -0
- package/dist/queries/focus-ratify-release.js.map +1 -0
- package/dist/queries/focuses.d.ts +0 -10
- package/dist/queries/focuses.d.ts.map +1 -1
- package/dist/queries/focuses.js +0 -14
- package/dist/queries/focuses.js.map +1 -1
- package/dist/queries/prd.d.ts +31 -0
- package/dist/queries/prd.d.ts.map +1 -1
- package/dist/queries/prd.js +26 -1
- package/dist/queries/prd.js.map +1 -1
- package/dist/queries/shared.js +1 -1
- package/dist/queries/verify-own-holds.d.ts +89 -0
- package/dist/queries/verify-own-holds.d.ts.map +1 -0
- package/dist/queries/verify-own-holds.js +93 -0
- package/dist/queries/verify-own-holds.js.map +1 -0
- package/dist/resolvers/focus-criteria-standing.d.ts.map +1 -1
- package/dist/resolvers/focus-criteria-standing.js +324 -25
- package/dist/resolvers/focus-criteria-standing.js.map +1 -1
- package/dist/security/security-auto-inject.d.ts +51 -0
- package/dist/security/security-auto-inject.d.ts.map +1 -1
- package/dist/security/security-auto-inject.js +101 -11
- package/dist/security/security-auto-inject.js.map +1 -1
- package/dist/security/security-scan-engine.d.ts +4 -4
- package/dist/security/security-scan-engine.d.ts.map +1 -1
- package/dist/security/security-scan-engine.js +6 -0
- package/dist/security/security-scan-engine.js.map +1 -1
- package/dist/session/session-lineage.d.ts.map +1 -1
- package/dist/session/session-lineage.js +2 -0
- package/dist/session/session-lineage.js.map +1 -1
- package/dist/shell/constants.d.ts +5 -0
- package/dist/shell/constants.d.ts.map +1 -1
- package/dist/shell/constants.js +5 -0
- package/dist/shell/constants.js.map +1 -1
- package/dist/shell/daemon-instance.d.ts +74 -64
- package/dist/shell/daemon-instance.d.ts.map +1 -1
- package/dist/shell/daemon-instance.js +16 -121
- package/dist/shell/daemon-instance.js.map +1 -1
- package/dist/shell/health-events.js +1 -1
- package/dist/shell/health-events.js.map +1 -1
- package/dist/shell/instance-liveness.d.ts +112 -0
- package/dist/shell/instance-liveness.d.ts.map +1 -0
- package/dist/shell/instance-liveness.js +205 -0
- package/dist/shell/instance-liveness.js.map +1 -0
- package/dist/shell/schema-version-gate.d.ts +22 -0
- package/dist/shell/schema-version-gate.d.ts.map +1 -1
- package/dist/shell/schema-version-gate.js +116 -6
- package/dist/shell/schema-version-gate.js.map +1 -1
- package/dist/shell/worker-pidfile.d.ts +21 -8
- package/dist/shell/worker-pidfile.d.ts.map +1 -1
- package/dist/shell/worker-pidfile.js +38 -9
- package/dist/shell/worker-pidfile.js.map +1 -1
- package/dist/spawn/lead-launch.d.ts.map +1 -1
- package/dist/spawn/lead-launch.js +2 -1
- package/dist/spawn/lead-launch.js.map +1 -1
- package/dist/spawn/one-shot-model.d.ts +145 -0
- package/dist/spawn/one-shot-model.d.ts.map +1 -0
- package/dist/spawn/one-shot-model.js +329 -0
- package/dist/spawn/one-shot-model.js.map +1 -0
- package/dist/spawn/one-shot-scoping-spawn.d.ts +51 -0
- package/dist/spawn/one-shot-scoping-spawn.d.ts.map +1 -0
- package/dist/spawn/one-shot-scoping-spawn.js +156 -0
- package/dist/spawn/one-shot-scoping-spawn.js.map +1 -0
- package/dist/spawn/process-spawn.d.ts +12 -0
- package/dist/spawn/process-spawn.d.ts.map +1 -1
- package/dist/spawn/process-spawn.js +1 -1
- package/dist/spawn/process-spawn.js.map +1 -1
- package/dist/spawn/prompt-assembly.d.ts +8 -0
- package/dist/spawn/prompt-assembly.d.ts.map +1 -1
- package/dist/spawn/prompt-assembly.js +15 -3
- package/dist/spawn/prompt-assembly.js.map +1 -1
- package/dist/spawn/review-hold-park.d.ts.map +1 -1
- package/dist/spawn/review-hold-park.js +7 -3
- package/dist/spawn/review-hold-park.js.map +1 -1
- package/dist/spawn/routing-decider-spawn.d.ts +30 -0
- package/dist/spawn/routing-decider-spawn.d.ts.map +1 -1
- package/dist/spawn/routing-decider-spawn.js +84 -2
- package/dist/spawn/routing-decider-spawn.js.map +1 -1
- package/dist/spawn/routing-decider.d.ts.map +1 -1
- package/dist/spawn/routing-decider.js +22 -0
- package/dist/spawn/routing-decider.js.map +1 -1
- package/dist/spawn/spawn-cooldown.d.ts +15 -1
- package/dist/spawn/spawn-cooldown.d.ts.map +1 -1
- package/dist/spawn/spawn-cooldown.js +67 -19
- package/dist/spawn/spawn-cooldown.js.map +1 -1
- package/dist/spawn/worktree-session.d.ts +24 -6
- package/dist/spawn/worktree-session.d.ts.map +1 -1
- package/dist/spawn/worktree-session.js +37 -14
- package/dist/spawn/worktree-session.js.map +1 -1
- package/dist/testing/arm-coverage-model.d.ts +75 -0
- package/dist/testing/arm-coverage-model.d.ts.map +1 -0
- package/dist/testing/arm-coverage-model.js +114 -0
- package/dist/testing/arm-coverage-model.js.map +1 -0
- package/dist/testing/fixtures/index.js +1 -1
- package/dist/types/index.js +1 -1
- package/dist/verify/ai-inspection-runner.d.ts.map +1 -1
- package/dist/verify/ai-inspection-runner.js +26 -7
- package/dist/verify/ai-inspection-runner.js.map +1 -1
- package/dist/verify/de-restatement.d.ts +9 -2
- package/dist/verify/de-restatement.d.ts.map +1 -1
- package/dist/verify/de-restatement.js +14 -76
- package/dist/verify/de-restatement.js.map +1 -1
- package/dist/verify/default-on-guards.d.ts +5 -2
- package/dist/verify/default-on-guards.d.ts.map +1 -1
- package/dist/verify/default-on-guards.js +5 -3
- package/dist/verify/default-on-guards.js.map +1 -1
- package/dist/verify/verification-boundary-env.d.ts +7 -2
- package/dist/verify/verification-boundary-env.d.ts.map +1 -1
- package/dist/verify/verification-boundary-env.js +9 -4
- package/dist/verify/verification-boundary-env.js.map +1 -1
- package/dist/verify/verification-deps-default.d.ts.map +1 -1
- package/dist/verify/verification-deps-default.js +6 -0
- package/dist/verify/verification-deps-default.js.map +1 -1
- package/dist/verify/verification-deps.d.ts +10 -0
- package/dist/verify/verification-deps.d.ts.map +1 -1
- package/dist/verify/verification-deterministic.d.ts +7 -0
- package/dist/verify/verification-deterministic.d.ts.map +1 -1
- package/dist/verify/verification-deterministic.js.map +1 -1
- package/dist/verify/verification-engine.d.ts +1 -20
- package/dist/verify/verification-engine.d.ts.map +1 -1
- package/dist/verify/verification-engine.js +24 -55
- package/dist/verify/verification-engine.js.map +1 -1
- package/dist/verify/verification-evidence-unavailable-feedback.d.ts +3 -1
- package/dist/verify/verification-evidence-unavailable-feedback.d.ts.map +1 -1
- package/dist/verify/verification-evidence-unavailable-feedback.js +3 -1
- package/dist/verify/verification-evidence-unavailable-feedback.js.map +1 -1
- package/dist/verify/verification-feedback.d.ts +4 -12
- package/dist/verify/verification-feedback.d.ts.map +1 -1
- package/dist/verify/verification-feedback.js +7 -12
- package/dist/verify/verification-feedback.js.map +1 -1
- package/dist/verify/verification-history-reset.d.ts.map +1 -1
- package/dist/verify/verification-history-reset.js +11 -3
- package/dist/verify/verification-history-reset.js.map +1 -1
- package/dist/verify/verification-model-unavailable-hold.d.ts +92 -0
- package/dist/verify/verification-model-unavailable-hold.d.ts.map +1 -0
- package/dist/verify/verification-model-unavailable-hold.js +207 -0
- package/dist/verify/verification-model-unavailable-hold.js.map +1 -0
- package/dist/verify/verification-strategies.d.ts +32 -0
- package/dist/verify/verification-strategies.d.ts.map +1 -0
- package/dist/verify/verification-strategies.js +47 -0
- package/dist/verify/verification-strategies.js.map +1 -0
- package/dist/verify/verify-reconciliation.d.ts.map +1 -1
- package/dist/verify/verify-reconciliation.js +2 -1
- package/dist/verify/verify-reconciliation.js.map +1 -1
- package/dist/verify/verify-restatement.d.ts.map +1 -1
- package/dist/verify/verify-restatement.js +21 -8
- package/dist/verify/verify-restatement.js.map +1 -1
- package/package.json +2 -2
|
@@ -3,128 +3,15 @@
|
|
|
3
3
|
// packages/daemon/src/shell/crash-recovery-session-scope.test.ts,
|
|
4
4
|
// packages/daemon/src/shell/daemon-instance.test.ts, packages/daemon/src/worktree/worktree-state.test.ts
|
|
5
5
|
// PINNED-BY-END
|
|
6
|
-
/**
|
|
7
|
-
* Daemon INSTANCE identity -- which running daemon owns a session row.
|
|
8
|
-
*
|
|
9
|
-
* `daemon_hostname` answers "which machine", which is enough to keep one host's
|
|
10
|
-
* crash recovery off another host's live work. It is NOT enough to separate two
|
|
11
|
-
* daemon instances on the SAME host and product: to that scope they are the
|
|
12
|
-
* same owner, so one's recovery sweep would mark the other's running sessions
|
|
13
|
-
* failed. It is also not enough to answer the question recovery actually has to
|
|
14
|
-
* answer before it writes `failed` -- has the team this row represents actually
|
|
15
|
-
* stopped? -- because a hostname says nothing about liveness.
|
|
16
|
-
*
|
|
17
|
-
* An instance id is `<hostname>:<pid>:<startIdentity>`:
|
|
18
|
-
*
|
|
19
|
-
* - `pid` is the daemon process, comparable on its own host.
|
|
20
|
-
* - `startIdentity` is the kernel's start time for that pid (/proc stat field
|
|
21
|
-
* 22 on Linux). It makes the id PID-REUSE SAFE: a new process that happens
|
|
22
|
-
* to land on a dead daemon's pid has a different start time, so it is not
|
|
23
|
-
* mistaken for the instance that owned those rows.
|
|
24
|
-
*
|
|
25
|
-
* The id is therefore the thing crash recovery can probe: "is the daemon that
|
|
26
|
-
* wrote this row still running?" A row whose owner is alive, or whose owner
|
|
27
|
-
* cannot be determined, is left alone.
|
|
28
|
-
*
|
|
29
|
-
* A dead owner is NOT on its own proof that the row's team stopped. Team leads
|
|
30
|
-
* are spawned DETACHED, so they survive a hard-killed daemon; the boot reap
|
|
31
|
-
* that ends them (`reapAndClearWorkerPidfile`, unified-shell) is explicitly
|
|
32
|
-
* best-effort -- a missing or malformed pidfile yields no entries and a failed
|
|
33
|
-
* kill is swallowed. So the destructive pass needs a SECOND proof, about the
|
|
34
|
-
* session's own process: `classifySessionProcess`. Both must agree before a row
|
|
35
|
-
* is closed.
|
|
36
|
-
*
|
|
37
|
-
* @module shell/daemon-instance
|
|
38
|
-
*/
|
|
39
|
-
import { hostname } from 'node:os';
|
|
40
6
|
import { createLogger } from './log.js';
|
|
41
7
|
import { isProcessAlive } from '../spawn/liveness.js';
|
|
42
8
|
import { readProcessStartIdentity, isProcessGroupAlive } from '../spawn/process-group-kill.js';
|
|
43
9
|
import { readLeadRecordsBySession } from './worker-pidfile.js';
|
|
10
|
+
import { classifyOwningInstance, defaultInstanceProbes, isReservationInFlight, } from './instance-liveness.js';
|
|
11
|
+
// The identity half, re-exported so none of this module's importers move. The
|
|
12
|
+
// DEFINING module is instance-liveness.ts; new code should import from there.
|
|
13
|
+
export { daemonInstanceId, resetDaemonInstanceIdCache, parseDaemonInstanceId, defaultInstanceProbes, classifyOwningInstance, isReservationInFlight, } from './instance-liveness.js';
|
|
44
14
|
const log = createLogger({ module: 'daemon-instance' });
|
|
45
|
-
/** Separator between instance-id parts. Hostnames cannot contain ':'. */
|
|
46
|
-
const SEP = ':';
|
|
47
|
-
/** Memoized so every read within one daemon run returns the same identity. */
|
|
48
|
-
let cachedId = null;
|
|
49
|
-
/**
|
|
50
|
-
* This daemon instance's id. Stamped onto every session it creates, and the
|
|
51
|
-
* identity its own crash recovery compares against.
|
|
52
|
-
*/
|
|
53
|
-
export function daemonInstanceId() {
|
|
54
|
-
if (cachedId === null) {
|
|
55
|
-
const start = readProcessStartIdentity(process.pid);
|
|
56
|
-
cachedId = [hostname(), String(process.pid), start ?? 'unknown'].join(SEP);
|
|
57
|
-
}
|
|
58
|
-
return cachedId;
|
|
59
|
-
}
|
|
60
|
-
/** Reset the memo. Test seam only -- a daemon's identity never changes in life. */
|
|
61
|
-
export function resetDaemonInstanceIdCache() {
|
|
62
|
-
cachedId = null;
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Parse an instance id, or null when it is absent or malformed. A row written
|
|
66
|
-
* by a daemon predating this column parses to null and is treated as
|
|
67
|
-
* unattributable -- never as "gone".
|
|
68
|
-
*/
|
|
69
|
-
export function parseDaemonInstanceId(id) {
|
|
70
|
-
if (typeof id !== 'string' || id.length === 0)
|
|
71
|
-
return null;
|
|
72
|
-
const parts = id.split(SEP);
|
|
73
|
-
if (parts.length !== 3)
|
|
74
|
-
return null;
|
|
75
|
-
const [host, pidText, start] = parts;
|
|
76
|
-
if (!host || !pidText)
|
|
77
|
-
return null;
|
|
78
|
-
// STRICT: Number.parseInt('200junk') is 200, which would have us probe an
|
|
79
|
-
// unintended pid on the strength of a malformed id. Require all digits.
|
|
80
|
-
if (!/^[0-9]+$/.test(pidText))
|
|
81
|
-
return null;
|
|
82
|
-
const pid = Number.parseInt(pidText, 10);
|
|
83
|
-
if (!Number.isInteger(pid) || pid <= 0)
|
|
84
|
-
return null;
|
|
85
|
-
return { hostname: host, pid, startIdentity: start === 'unknown' ? null : (start ?? null) };
|
|
86
|
-
}
|
|
87
|
-
/** Real probes against this host. */
|
|
88
|
-
export function defaultInstanceProbes() {
|
|
89
|
-
return {
|
|
90
|
-
selfId: daemonInstanceId(),
|
|
91
|
-
selfHostname: hostname(),
|
|
92
|
-
isProcessAlive,
|
|
93
|
-
readStartIdentity: readProcessStartIdentity,
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Classify the instance that owns a session row.
|
|
98
|
-
*
|
|
99
|
-
* Every branch that is not `gone` leaves the row alone, because only `gone` is
|
|
100
|
-
* positive evidence that the team the row represents has stopped. Recovery
|
|
101
|
-
* would rather leave a stale row than falsify a live one: a stale `running` row
|
|
102
|
-
* is visible and self-correcting on the next sweep, whereas a live session
|
|
103
|
-
* marked `failed` silently tells every reader -- including "is this worktree
|
|
104
|
-
* being written?" -- that a running team is not there.
|
|
105
|
-
*/
|
|
106
|
-
export function classifyOwningInstance(instanceId, deps) {
|
|
107
|
-
if (instanceId === deps.selfId)
|
|
108
|
-
return 'self';
|
|
109
|
-
const parsed = parseDaemonInstanceId(instanceId);
|
|
110
|
-
if (parsed === null)
|
|
111
|
-
return 'unknown';
|
|
112
|
-
// A pid from another machine is not a pid we can probe. The host scope should
|
|
113
|
-
// already have excluded it; if one arrives anyway, it is unknown, not gone.
|
|
114
|
-
if (parsed.hostname !== deps.selfHostname)
|
|
115
|
-
return 'unknown';
|
|
116
|
-
if (!deps.isProcessAlive(parsed.pid))
|
|
117
|
-
return 'gone';
|
|
118
|
-
// The pid is alive -- but is it the SAME process, or a reused pid? A start
|
|
119
|
-
// identity that disagrees means the original instance died and its pid was
|
|
120
|
-
// recycled, so the owner IS gone.
|
|
121
|
-
const liveStart = deps.readStartIdentity(parsed.pid);
|
|
122
|
-
if (parsed.startIdentity !== null && liveStart !== null && parsed.startIdentity !== liveStart) {
|
|
123
|
-
return 'gone';
|
|
124
|
-
}
|
|
125
|
-
// Alive, and nothing contradicts that it is the owner. Leave it.
|
|
126
|
-
return 'peer_alive';
|
|
127
|
-
}
|
|
128
15
|
/**
|
|
129
16
|
* Real probes against this host. The lead-record read is memoized PER PROBE
|
|
130
17
|
* OBJECT: a sweep classifying many rows reads the file once, and every row in
|
|
@@ -142,13 +29,21 @@ export function defaultSessionProcessProbes() {
|
|
|
142
29
|
cached = readLeadRecordsBySession();
|
|
143
30
|
return cached;
|
|
144
31
|
},
|
|
32
|
+
// Bound to the real host probes. This is the DEFAULT the mars-shaped
|
|
33
|
+
// stranded reservation is judged by, not only an injected test seam.
|
|
34
|
+
reservationInFlight: (reservedBy) => isReservationInFlight(reservedBy),
|
|
145
35
|
};
|
|
146
36
|
}
|
|
147
37
|
export function classifyRecordedProcess(record, probes) {
|
|
148
|
-
// A RESERVATION: the record exists because a spawn was about to happen
|
|
149
|
-
// names no pid
|
|
150
|
-
|
|
151
|
-
|
|
38
|
+
// A RESERVATION: the record exists because a spawn was about to happen, so it
|
|
39
|
+
// names no pid. Nothing about the PROCESS is decidable -- but whether anything
|
|
40
|
+
// ever will be is, and that is what `reservedBy` answers. A reservation whose
|
|
41
|
+
// reserving instance is still running (or cannot be attributed at all) is in
|
|
42
|
+
// flight and stays `indeterminate`; one whose instance is provably gone is
|
|
43
|
+
// stranded, will never be resolved by anyone, and reads `gone`.
|
|
44
|
+
if (record.pending) {
|
|
45
|
+
return probes.reservationInFlight(record.reservedBy) ? 'indeterminate' : 'gone';
|
|
46
|
+
}
|
|
152
47
|
const { pid } = record;
|
|
153
48
|
if (!Number.isInteger(pid) || pid <= 1)
|
|
154
49
|
return 'indeterminate';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daemon-instance.js","sourceRoot":"","sources":["../../src/shell/daemon-instance.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,2EAA2E;AAC3E,oEAAoE;AACpE,2GAA2G;AAC3G,gBAAgB;
|
|
1
|
+
{"version":3,"file":"daemon-instance.js","sourceRoot":"","sources":["../../src/shell/daemon-instance.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
|
|
2
|
-
// Pinned by:
|
|
2
|
+
// Pinned by: packages/daemon/src/shell/schema-version-gate.test.ts
|
|
3
3
|
// PINNED-BY-END
|
|
4
4
|
/**
|
|
5
5
|
* Daemon-local seam over daemon-core's healthEvents buffer.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"health-events.js","sourceRoot":"","sources":["../../src/shell/health-events.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,
|
|
1
|
+
{"version":3,"file":"health-events.js","sourceRoot":"","sources":["../../src/shell/health-events.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,mEAAmE;AACnE,gBAAgB;AAEhB;;;;;;;GAOG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/** The parts of a parsed instance id. */
|
|
2
|
+
export interface DaemonInstance {
|
|
3
|
+
hostname: string;
|
|
4
|
+
pid: number;
|
|
5
|
+
/** Kernel start time of the daemon pid; null when the platform cannot supply one. */
|
|
6
|
+
startIdentity: string | null;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* This daemon instance's id. Stamped onto every session it creates, onto every
|
|
10
|
+
* lead-record RESERVATION it makes, and the identity its own crash recovery
|
|
11
|
+
* compares against.
|
|
12
|
+
*/
|
|
13
|
+
export declare function daemonInstanceId(): string;
|
|
14
|
+
/** Reset the memo. Test seam only -- a daemon's identity never changes in life. */
|
|
15
|
+
export declare function resetDaemonInstanceIdCache(): void;
|
|
16
|
+
/**
|
|
17
|
+
* Parse an instance id, or null when it is absent or malformed. A row written
|
|
18
|
+
* by a daemon predating this column parses to null and is treated as
|
|
19
|
+
* unattributable -- never as "gone".
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseDaemonInstanceId(id: string | null | undefined): DaemonInstance | null;
|
|
22
|
+
/**
|
|
23
|
+
* What a session row's owning instance is doing now.
|
|
24
|
+
*
|
|
25
|
+
* - `self` -- this daemon owns it. Never reap: the team is ours and live.
|
|
26
|
+
* - `peer_alive` -- another daemon instance on this host is still running. Its
|
|
27
|
+
* sessions are its business; reaping them is the defect.
|
|
28
|
+
* - `gone` -- the owning instance is not running. Its teams stopped with
|
|
29
|
+
* it, so its rows are safe to close.
|
|
30
|
+
* - `unknown` -- no id, a malformed id, or an id from another host whose pid
|
|
31
|
+
* we cannot probe. FAILS SAFE: not reaped.
|
|
32
|
+
*/
|
|
33
|
+
export type InstanceLiveness = 'self' | 'peer_alive' | 'gone' | 'unknown';
|
|
34
|
+
/** Probes `classifyOwningInstance` reaches, injectable for tests. */
|
|
35
|
+
export interface InstanceProbeDeps {
|
|
36
|
+
selfId: string;
|
|
37
|
+
selfHostname: string;
|
|
38
|
+
isProcessAlive: (pid: number) => boolean;
|
|
39
|
+
readStartIdentity: (pid: number) => string | null;
|
|
40
|
+
}
|
|
41
|
+
/** Real probes against this host. */
|
|
42
|
+
export declare function defaultInstanceProbes(): InstanceProbeDeps;
|
|
43
|
+
/**
|
|
44
|
+
* Classify the instance that owns a session row.
|
|
45
|
+
*
|
|
46
|
+
* Every branch that is not `gone` leaves the row alone, because only `gone` is
|
|
47
|
+
* positive evidence that the team the row represents has stopped. Recovery
|
|
48
|
+
* would rather leave a stale row than falsify a live one: a stale `running` row
|
|
49
|
+
* is visible and self-correcting on the next sweep, whereas a live session
|
|
50
|
+
* marked `failed` silently tells every reader -- including "is this worktree
|
|
51
|
+
* being written?" -- that a running team is not there.
|
|
52
|
+
*/
|
|
53
|
+
export declare function classifyOwningInstance(instanceId: string | null | undefined, deps: InstanceProbeDeps): InstanceLiveness;
|
|
54
|
+
/**
|
|
55
|
+
* Is a lead-record RESERVATION still in flight?
|
|
56
|
+
*
|
|
57
|
+
* This is the ONE definition of the rule `reserveLeadRecord` has stated in a
|
|
58
|
+
* comment since the field was added: "a reservation is 'in flight' only while
|
|
59
|
+
* the instance that made it is still running; once that instance is provably
|
|
60
|
+
* gone nothing will ever resolve it, and it must not hold a worktree open
|
|
61
|
+
* forever." Two judgements read it -- the retention filter
|
|
62
|
+
* (`recordedLeadStillRunning`, so the reap and boot passes retire a stranded
|
|
63
|
+
* reservation) and the session verdict (`classifyRecordedProcess`, so a live
|
|
64
|
+
* daemon's sweeps stop answering `unprovable` about it forever) -- and both
|
|
65
|
+
* reach it through an injectable dep defaulting here, so there is no second
|
|
66
|
+
* copy of the rule to drift.
|
|
67
|
+
*
|
|
68
|
+
* DIRECTION OF FAILURE. Absent or empty `reservedBy` is IN FLIGHT. A reservation
|
|
69
|
+
* written by a daemon predating the field names nobody, and retiring a record on
|
|
70
|
+
* a guess is the one move this module refuses everywhere: unattributable is not
|
|
71
|
+
* gone. Of the four classifications only `gone` retires; `self`, `peer_alive`
|
|
72
|
+
* and `unknown` (a malformed id, or an id from a host whose pids we cannot
|
|
73
|
+
* probe) all read as in flight.
|
|
74
|
+
*
|
|
75
|
+
* THE OBJECTION, AND WHY `gone` IS NEVERTHELESS THE RIGHT VERDICT.
|
|
76
|
+
* `docs/DECISIONS.md` (2026-09-21) records the consideration this
|
|
77
|
+
* implementation has to answer: expiring a reservation because its reserving
|
|
78
|
+
* instance is gone converts "unprovable" into "nothing was spawned", which is
|
|
79
|
+
* the inference this module refuses everywhere else -- a lead is spawned
|
|
80
|
+
* DETACHED and can outlive the daemon that reserved it, and dropping its only
|
|
81
|
+
* record would make it unnameable by the force-stop and next-boot reaps.
|
|
82
|
+
*
|
|
83
|
+
* The answer is that a reservation is ALREADY unnameable by every kill path, so
|
|
84
|
+
* retiring it costs no reaper anything. Three independent guards see to that,
|
|
85
|
+
* and each is there on purpose:
|
|
86
|
+
*
|
|
87
|
+
* 1. It carries `PENDING_PID` (0). `reapPersistedWorkerGroups` skips every
|
|
88
|
+
* `pending` entry outright, before any liveness probe -- `kill(-0, ...)`
|
|
89
|
+
* signals the CALLER's own process group, which is the daemon.
|
|
90
|
+
* 2. `readWorkerPidfile`, the reader every pid-consuming pass goes through,
|
|
91
|
+
* FILTERS reservations out, so the untracked-lead sweep never sees one.
|
|
92
|
+
* 3. Even if it did, that sweep's `unverifiedReason` refuses to signal any
|
|
93
|
+
* entry with no `startIdentity`, and a reservation has none.
|
|
94
|
+
*
|
|
95
|
+
* So retaining the row buys no kill capability. What retention DOES buy is the
|
|
96
|
+
* claim "we do not know" -- and once the reserving instance is gone that claim
|
|
97
|
+
* can never improve: nothing will ever write the pid, resolve the reservation or
|
|
98
|
+
* release it, so the session it names stays paralysed for good (session-health
|
|
99
|
+
* skips it `unprovable_process`, abandoned-merge recovery refuses to restore its
|
|
100
|
+
* worktree, and the worktree write signal answers `provable: false`). A detached
|
|
101
|
+
* lead that really did survive the window is equally unnameable whether the row
|
|
102
|
+
* is kept or dropped, because its pid was never written. No capability lost,
|
|
103
|
+
* permanent paralysis ended: that asymmetry is what makes `gone` correct here
|
|
104
|
+
* and not elsewhere.
|
|
105
|
+
*
|
|
106
|
+
* Note what this does NOT do: it never retires a reservation of a LIVE instance.
|
|
107
|
+
* `launchLeadProcess` refuses a spawn whose record could not be reserved, so
|
|
108
|
+
* deleting a running daemon's reservation would reopen the very pre-spawn window
|
|
109
|
+
* the reservation exists to close.
|
|
110
|
+
*/
|
|
111
|
+
export declare function isReservationInFlight(reservedBy: string | undefined, deps?: InstanceProbeDeps): boolean;
|
|
112
|
+
//# sourceMappingURL=instance-liveness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instance-liveness.d.ts","sourceRoot":"","sources":["../../src/shell/instance-liveness.ts"],"names":[],"mappings":"AAgDA,yCAAyC;AACzC,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,qFAAqF;IACrF,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAQD;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAMzC;AAED,mFAAmF;AACnF,wBAAgB,0BAA0B,IAAI,IAAI,CAEjD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,cAAc,GAAG,IAAI,CAY1F;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,YAAY,GAAG,MAAM,GAAG,SAAS,CAAC;AAE1E,qEAAqE;AACrE,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACzC,iBAAiB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CACnD;AAED,qCAAqC;AACrC,wBAAgB,qBAAqB,IAAI,iBAAiB,CAOzD;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACrC,IAAI,EAAE,iBAAiB,GACtB,gBAAgB,CAsBlB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,IAAI,GAAE,iBAA2C,GAChD,OAAO,CAoBT"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
|
|
2
|
+
// Pinned by: packages/daemon/src/shell/daemon-instance.test.ts,
|
|
3
|
+
// packages/daemon/src/shell/worker-pidfile.test.ts
|
|
4
|
+
// PINNED-BY-END
|
|
5
|
+
/**
|
|
6
|
+
* Daemon instance IDENTITY and its liveness -- the leaf half of
|
|
7
|
+
* shell/daemon-instance.ts.
|
|
8
|
+
*
|
|
9
|
+
* `daemon_hostname` answers "which machine", which is enough to keep one host's
|
|
10
|
+
* crash recovery off another host's live work. It is NOT enough to separate two
|
|
11
|
+
* daemon instances on the SAME host and product: to that scope they are the
|
|
12
|
+
* same owner, so one's recovery sweep would mark the other's running sessions
|
|
13
|
+
* failed. It is also not enough to answer the question recovery actually has to
|
|
14
|
+
* answer before it writes `failed` -- has the team this row represents actually
|
|
15
|
+
* stopped? -- because a hostname says nothing about liveness.
|
|
16
|
+
*
|
|
17
|
+
* An instance id is `<hostname>:<pid>:<startIdentity>`:
|
|
18
|
+
*
|
|
19
|
+
* - `pid` is the daemon process, comparable on its own host.
|
|
20
|
+
* - `startIdentity` is the kernel's start time for that pid (/proc stat field
|
|
21
|
+
* 22 on Linux). It makes the id PID-REUSE SAFE: a new process that happens
|
|
22
|
+
* to land on a dead daemon's pid has a different start time, so it is not
|
|
23
|
+
* mistaken for the instance that owned those rows.
|
|
24
|
+
*
|
|
25
|
+
* WHY THIS IS ITS OWN MODULE. It was the first half of `daemon-instance.ts`,
|
|
26
|
+
* which also holds the SESSION-process half and therefore imports
|
|
27
|
+
* `shell/worker-pidfile.ts` (`readLeadRecordsBySession`, `WorkerPidEntry`).
|
|
28
|
+
* That import is one-directional, so `worker-pidfile.ts` could not ask the
|
|
29
|
+
* identity question back -- and asking it is exactly what a reservation's
|
|
30
|
+
* `reservedBy` field needs (see {@link isReservationInFlight}). Splitting the
|
|
31
|
+
* identity half down here inverts nothing and duplicates nothing: this module's
|
|
32
|
+
* imports stop at node:os, the logger and the two process probes, none of which
|
|
33
|
+
* reach the pidfile.
|
|
34
|
+
*
|
|
35
|
+
* @module shell/instance-liveness
|
|
36
|
+
*/
|
|
37
|
+
import { hostname } from 'node:os';
|
|
38
|
+
import { createLogger } from './log.js';
|
|
39
|
+
import { isProcessAlive } from '../spawn/liveness.js';
|
|
40
|
+
import { readProcessStartIdentity } from '../spawn/process-group-kill.js';
|
|
41
|
+
// Kept so this module owns a logger of its own rather than borrowing one across
|
|
42
|
+
// the split; the identity probes themselves are silent by design (they are read
|
|
43
|
+
// in tight sweeps and their verdicts are logged by the callers that act on them).
|
|
44
|
+
const log = createLogger({ module: 'instance-liveness' });
|
|
45
|
+
/** Separator between instance-id parts. Hostnames cannot contain ':'. */
|
|
46
|
+
const SEP = ':';
|
|
47
|
+
/** Memoized so every read within one daemon run returns the same identity. */
|
|
48
|
+
let cachedId = null;
|
|
49
|
+
/**
|
|
50
|
+
* This daemon instance's id. Stamped onto every session it creates, onto every
|
|
51
|
+
* lead-record RESERVATION it makes, and the identity its own crash recovery
|
|
52
|
+
* compares against.
|
|
53
|
+
*/
|
|
54
|
+
export function daemonInstanceId() {
|
|
55
|
+
if (cachedId === null) {
|
|
56
|
+
const start = readProcessStartIdentity(process.pid);
|
|
57
|
+
cachedId = [hostname(), String(process.pid), start ?? 'unknown'].join(SEP);
|
|
58
|
+
}
|
|
59
|
+
return cachedId;
|
|
60
|
+
}
|
|
61
|
+
/** Reset the memo. Test seam only -- a daemon's identity never changes in life. */
|
|
62
|
+
export function resetDaemonInstanceIdCache() {
|
|
63
|
+
cachedId = null;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Parse an instance id, or null when it is absent or malformed. A row written
|
|
67
|
+
* by a daemon predating this column parses to null and is treated as
|
|
68
|
+
* unattributable -- never as "gone".
|
|
69
|
+
*/
|
|
70
|
+
export function parseDaemonInstanceId(id) {
|
|
71
|
+
if (typeof id !== 'string' || id.length === 0)
|
|
72
|
+
return null;
|
|
73
|
+
const parts = id.split(SEP);
|
|
74
|
+
if (parts.length !== 3)
|
|
75
|
+
return null;
|
|
76
|
+
const [host, pidText, start] = parts;
|
|
77
|
+
if (!host || !pidText)
|
|
78
|
+
return null;
|
|
79
|
+
// STRICT: Number.parseInt('200junk') is 200, which would have us probe an
|
|
80
|
+
// unintended pid on the strength of a malformed id. Require all digits.
|
|
81
|
+
if (!/^[0-9]+$/.test(pidText))
|
|
82
|
+
return null;
|
|
83
|
+
const pid = Number.parseInt(pidText, 10);
|
|
84
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
85
|
+
return null;
|
|
86
|
+
return { hostname: host, pid, startIdentity: start === 'unknown' ? null : (start ?? null) };
|
|
87
|
+
}
|
|
88
|
+
/** Real probes against this host. */
|
|
89
|
+
export function defaultInstanceProbes() {
|
|
90
|
+
return {
|
|
91
|
+
selfId: daemonInstanceId(),
|
|
92
|
+
selfHostname: hostname(),
|
|
93
|
+
isProcessAlive,
|
|
94
|
+
readStartIdentity: readProcessStartIdentity,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Classify the instance that owns a session row.
|
|
99
|
+
*
|
|
100
|
+
* Every branch that is not `gone` leaves the row alone, because only `gone` is
|
|
101
|
+
* positive evidence that the team the row represents has stopped. Recovery
|
|
102
|
+
* would rather leave a stale row than falsify a live one: a stale `running` row
|
|
103
|
+
* is visible and self-correcting on the next sweep, whereas a live session
|
|
104
|
+
* marked `failed` silently tells every reader -- including "is this worktree
|
|
105
|
+
* being written?" -- that a running team is not there.
|
|
106
|
+
*/
|
|
107
|
+
export function classifyOwningInstance(instanceId, deps) {
|
|
108
|
+
if (instanceId === deps.selfId)
|
|
109
|
+
return 'self';
|
|
110
|
+
const parsed = parseDaemonInstanceId(instanceId);
|
|
111
|
+
if (parsed === null)
|
|
112
|
+
return 'unknown';
|
|
113
|
+
// A pid from another machine is not a pid we can probe. The host scope should
|
|
114
|
+
// already have excluded it; if one arrives anyway, it is unknown, not gone.
|
|
115
|
+
if (parsed.hostname !== deps.selfHostname)
|
|
116
|
+
return 'unknown';
|
|
117
|
+
if (!deps.isProcessAlive(parsed.pid))
|
|
118
|
+
return 'gone';
|
|
119
|
+
// The pid is alive -- but is it the SAME process, or a reused pid? A start
|
|
120
|
+
// identity that disagrees means the original instance died and its pid was
|
|
121
|
+
// recycled, so the owner IS gone.
|
|
122
|
+
const liveStart = deps.readStartIdentity(parsed.pid);
|
|
123
|
+
if (parsed.startIdentity !== null && liveStart !== null && parsed.startIdentity !== liveStart) {
|
|
124
|
+
return 'gone';
|
|
125
|
+
}
|
|
126
|
+
// Alive, and nothing contradicts that it is the owner. Leave it.
|
|
127
|
+
return 'peer_alive';
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Is a lead-record RESERVATION still in flight?
|
|
131
|
+
*
|
|
132
|
+
* This is the ONE definition of the rule `reserveLeadRecord` has stated in a
|
|
133
|
+
* comment since the field was added: "a reservation is 'in flight' only while
|
|
134
|
+
* the instance that made it is still running; once that instance is provably
|
|
135
|
+
* gone nothing will ever resolve it, and it must not hold a worktree open
|
|
136
|
+
* forever." Two judgements read it -- the retention filter
|
|
137
|
+
* (`recordedLeadStillRunning`, so the reap and boot passes retire a stranded
|
|
138
|
+
* reservation) and the session verdict (`classifyRecordedProcess`, so a live
|
|
139
|
+
* daemon's sweeps stop answering `unprovable` about it forever) -- and both
|
|
140
|
+
* reach it through an injectable dep defaulting here, so there is no second
|
|
141
|
+
* copy of the rule to drift.
|
|
142
|
+
*
|
|
143
|
+
* DIRECTION OF FAILURE. Absent or empty `reservedBy` is IN FLIGHT. A reservation
|
|
144
|
+
* written by a daemon predating the field names nobody, and retiring a record on
|
|
145
|
+
* a guess is the one move this module refuses everywhere: unattributable is not
|
|
146
|
+
* gone. Of the four classifications only `gone` retires; `self`, `peer_alive`
|
|
147
|
+
* and `unknown` (a malformed id, or an id from a host whose pids we cannot
|
|
148
|
+
* probe) all read as in flight.
|
|
149
|
+
*
|
|
150
|
+
* THE OBJECTION, AND WHY `gone` IS NEVERTHELESS THE RIGHT VERDICT.
|
|
151
|
+
* `docs/DECISIONS.md` (2026-09-21) records the consideration this
|
|
152
|
+
* implementation has to answer: expiring a reservation because its reserving
|
|
153
|
+
* instance is gone converts "unprovable" into "nothing was spawned", which is
|
|
154
|
+
* the inference this module refuses everywhere else -- a lead is spawned
|
|
155
|
+
* DETACHED and can outlive the daemon that reserved it, and dropping its only
|
|
156
|
+
* record would make it unnameable by the force-stop and next-boot reaps.
|
|
157
|
+
*
|
|
158
|
+
* The answer is that a reservation is ALREADY unnameable by every kill path, so
|
|
159
|
+
* retiring it costs no reaper anything. Three independent guards see to that,
|
|
160
|
+
* and each is there on purpose:
|
|
161
|
+
*
|
|
162
|
+
* 1. It carries `PENDING_PID` (0). `reapPersistedWorkerGroups` skips every
|
|
163
|
+
* `pending` entry outright, before any liveness probe -- `kill(-0, ...)`
|
|
164
|
+
* signals the CALLER's own process group, which is the daemon.
|
|
165
|
+
* 2. `readWorkerPidfile`, the reader every pid-consuming pass goes through,
|
|
166
|
+
* FILTERS reservations out, so the untracked-lead sweep never sees one.
|
|
167
|
+
* 3. Even if it did, that sweep's `unverifiedReason` refuses to signal any
|
|
168
|
+
* entry with no `startIdentity`, and a reservation has none.
|
|
169
|
+
*
|
|
170
|
+
* So retaining the row buys no kill capability. What retention DOES buy is the
|
|
171
|
+
* claim "we do not know" -- and once the reserving instance is gone that claim
|
|
172
|
+
* can never improve: nothing will ever write the pid, resolve the reservation or
|
|
173
|
+
* release it, so the session it names stays paralysed for good (session-health
|
|
174
|
+
* skips it `unprovable_process`, abandoned-merge recovery refuses to restore its
|
|
175
|
+
* worktree, and the worktree write signal answers `provable: false`). A detached
|
|
176
|
+
* lead that really did survive the window is equally unnameable whether the row
|
|
177
|
+
* is kept or dropped, because its pid was never written. No capability lost,
|
|
178
|
+
* permanent paralysis ended: that asymmetry is what makes `gone` correct here
|
|
179
|
+
* and not elsewhere.
|
|
180
|
+
*
|
|
181
|
+
* Note what this does NOT do: it never retires a reservation of a LIVE instance.
|
|
182
|
+
* `launchLeadProcess` refuses a spawn whose record could not be reserved, so
|
|
183
|
+
* deleting a running daemon's reservation would reopen the very pre-spawn window
|
|
184
|
+
* the reservation exists to close.
|
|
185
|
+
*/
|
|
186
|
+
export function isReservationInFlight(reservedBy, deps = defaultInstanceProbes()) {
|
|
187
|
+
if (reservedBy === undefined || reservedBy.length === 0) {
|
|
188
|
+
// Unattributable. Nothing to probe, so nothing is proved -- keep it.
|
|
189
|
+
//
|
|
190
|
+
// Deliberately EXPLICIT even though it is redundant by construction:
|
|
191
|
+
// `classifyOwningInstance` would also answer 'unknown' here (an absent or
|
|
192
|
+
// empty id parses to null). Stating it at the site the rule is about means
|
|
193
|
+
// the safe direction for the field this function exists to read does not
|
|
194
|
+
// depend on how the parser happens to treat an empty string.
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
const owner = classifyOwningInstance(reservedBy, deps);
|
|
198
|
+
if (owner === 'gone') {
|
|
199
|
+
log.info(`[instance-liveness] reservation made by ${reservedBy} is stranded -- ` +
|
|
200
|
+
'that daemon instance is provably gone, so nothing will ever resolve it');
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=instance-liveness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instance-liveness.js","sourceRoot":"","sources":["../../src/shell/instance-liveness.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,6 +9,16 @@ export interface SchemaGateResult {
|
|
|
9
9
|
applied: string[];
|
|
10
10
|
/** Expected ∖ applied after the final ledger read. Empty when ok. */
|
|
11
11
|
missing: string[];
|
|
12
|
+
/**
|
|
13
|
+
* Applied ∖ expected after the final ledger read: migrations the LIVE DB
|
|
14
|
+
* carries that this daemon build does not know about -- i.e. this daemon is
|
|
15
|
+
* OLDER than the schema it is about to arm against.
|
|
16
|
+
*
|
|
17
|
+
* Reported and recorded, never gated on: see the "IT IS A REPORT, NOT A
|
|
18
|
+
* GATE" note in the module header. A non-empty value leaves `ok`,
|
|
19
|
+
* `classification` and `attempts` untouched.
|
|
20
|
+
*/
|
|
21
|
+
unknownApplied: string[];
|
|
12
22
|
ok: boolean;
|
|
13
23
|
/**
|
|
14
24
|
* Classification of the initial missing set:
|
|
@@ -90,6 +100,16 @@ export declare function parseMigrationTimestamp(version: string): number;
|
|
|
90
100
|
* or an unparseable timestamp), which cannot be a recoverable deploy tail.
|
|
91
101
|
*/
|
|
92
102
|
export declare function classifyMissing(missing: string[], applied: string[], cfg: TransientToleranceConfig): Exclude<GapClassification, 'ok'>;
|
|
103
|
+
/**
|
|
104
|
+
* The reverse set: `applied` ∖ `expected` -- migration versions the live DB
|
|
105
|
+
* carries that this daemon build does not know about. Preserves the ledger's
|
|
106
|
+
* own order so the newest unknown versions read last, as they do in the
|
|
107
|
+
* ledger.
|
|
108
|
+
*
|
|
109
|
+
* Exported so the reverse direction is testable on its own, and so a later
|
|
110
|
+
* caller can ask the question without re-deriving the set difference.
|
|
111
|
+
*/
|
|
112
|
+
export declare function computeUnknownApplied(expected: string[], applied: string[]): string[];
|
|
93
113
|
export interface CheckSchemaVersionDeps {
|
|
94
114
|
mode?: SchemaGateMode;
|
|
95
115
|
getExpected?: () => string[];
|
|
@@ -104,5 +124,7 @@ export interface CheckSchemaVersionDeps {
|
|
|
104
124
|
log?: (msg: string) => void;
|
|
105
125
|
}
|
|
106
126
|
export declare function checkSchemaVersion(deps?: CheckSchemaVersionDeps): Promise<SchemaGateResult>;
|
|
127
|
+
/** Health-event code for "this daemon is older than the schema it arms against". */
|
|
128
|
+
export declare const DAEMON_BEHIND_DB_CODE = "schema.daemon_behind_db";
|
|
107
129
|
export declare function reportSchemaGateResult(result: SchemaGateResult): void;
|
|
108
130
|
//# sourceMappingURL=schema-version-gate.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-version-gate.d.ts","sourceRoot":"","sources":["../../src/shell/schema-version-gate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"schema-version-gate.d.ts","sourceRoot":"","sources":["../../src/shell/schema-version-gate.ts"],"names":[],"mappings":"AA4DA,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,MAAM,GAAG,KAAK,CAAC;AAE1D,sEAAsE;AACtE,MAAM,MAAM,iBAAiB,GAAG,IAAI,GAAG,WAAW,GAAG,OAAO,CAAC;AAE7D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,cAAc,CAAC;IACrB,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,uEAAuE;IACvE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,qEAAqE;IACrE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB;;;;;;;;OAQG;IACH,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ;;;;;;;OAOG;IACH,cAAc,EAAE,iBAAiB,CAAC;IAClC,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,cAAc,CAO1F;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAItD;AAMD;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACvC,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB,wEAAwE;IACxE,cAAc,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,2BAA2B,EAAE,wBAKzC,CAAC;AAMF;;;;;;;;;GASG;AACH,wBAAgB,+BAA+B,CAC7C,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,wBAAwB,CAQ1B;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAK/D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EAAE,EACjB,OAAO,EAAE,MAAM,EAAE,EACjB,GAAG,EAAE,wBAAwB,GAC5B,OAAO,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAclC;AAOD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAGrF;AAID,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,MAAM,EAAE,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACrC,iEAAiE;IACjE,kBAAkB,CAAC,EAAE,wBAAwB,CAAC;IAC9C,wDAAwD;IACxD,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,wEAAwE;IACxE,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED,wBAAsB,kBAAkB,CAAC,IAAI,GAAE,sBAA2B,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAuFrG;AAED,oFAAoF;AACpF,eAAO,MAAM,qBAAqB,4BAA4B,CAAC;AA4D/D,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CA+BrE"}
|