instar 1.3.819 → 1.3.821
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/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +498 -3
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +69 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/BackupManager.d.ts +10 -0
- package/dist/core/BackupManager.d.ts.map +1 -1
- package/dist/core/BackupManager.js +16 -0
- package/dist/core/BackupManager.js.map +1 -1
- package/dist/core/BoundedJsonlAudit.d.ts +37 -0
- package/dist/core/BoundedJsonlAudit.d.ts.map +1 -0
- package/dist/core/BoundedJsonlAudit.js +87 -0
- package/dist/core/BoundedJsonlAudit.js.map +1 -0
- package/dist/core/JudgmentProvenanceLog.d.ts +144 -0
- package/dist/core/JudgmentProvenanceLog.d.ts.map +1 -0
- package/dist/core/JudgmentProvenanceLog.js +301 -0
- package/dist/core/JudgmentProvenanceLog.js.map +1 -0
- package/dist/core/MachineIdentity.d.ts.map +1 -1
- package/dist/core/MachineIdentity.js +2 -0
- package/dist/core/MachineIdentity.js.map +1 -1
- package/dist/core/OwnerDarkLadder.d.ts +108 -0
- package/dist/core/OwnerDarkLadder.d.ts.map +1 -0
- package/dist/core/OwnerDarkLadder.js +229 -0
- package/dist/core/OwnerDarkLadder.js.map +1 -0
- package/dist/core/PostUpdateMigrator.d.ts +10 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +106 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/SpawnAdmission.d.ts +218 -0
- package/dist/core/SpawnAdmission.d.ts.map +1 -0
- package/dist/core/SpawnAdmission.js +440 -0
- package/dist/core/SpawnAdmission.js.map +1 -0
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +24 -0
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/core/machineCoherenceManifest.d.ts.map +1 -1
- package/dist/core/machineCoherenceManifest.js +30 -0
- package/dist/core/machineCoherenceManifest.js.map +1 -1
- package/dist/core/types.d.ts +87 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/monitoring/DuplicateSessionReconciler.d.ts +225 -0
- package/dist/monitoring/DuplicateSessionReconciler.d.ts.map +1 -0
- package/dist/monitoring/DuplicateSessionReconciler.js +544 -0
- package/dist/monitoring/DuplicateSessionReconciler.js.map +1 -0
- package/dist/monitoring/FailureAnalyzer.d.ts.map +1 -1
- package/dist/monitoring/FailureAnalyzer.js +40 -1
- package/dist/monitoring/FailureAnalyzer.js.map +1 -1
- package/dist/monitoring/FailureLedger.d.ts +9 -0
- package/dist/monitoring/FailureLedger.d.ts.map +1 -1
- package/dist/monitoring/FailureLedger.js +21 -4
- package/dist/monitoring/FailureLedger.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +2 -1
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +5 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +4 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +9 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/fileRoutes.d.ts +16 -0
- package/dist/server/fileRoutes.d.ts.map +1 -1
- package/dist/server/fileRoutes.js +46 -0
- package/dist/server/fileRoutes.js.map +1 -1
- package/dist/server/routes.d.ts +7 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +99 -0
- package/dist/server/routes.js.map +1 -1
- package/dist/testing/selfActionRegistry.d.ts.map +1 -1
- package/dist/testing/selfActionRegistry.js +89 -0
- package/dist/testing/selfActionRegistry.js.map +1 -1
- package/package.json +1 -1
- package/scripts/lib/self-action-detect.mjs +2 -0
- package/skills/instar-dev/templates/side-effects-artifact.md +8 -0
- package/skills/spec-converge/SKILL.md +1 -0
- package/skills/spec-converge/scripts/write-convergence-tag.mjs +71 -0
- package/src/data/builtin-manifest.json +65 -65
- package/src/scaffold/templates.ts +2 -1
- package/upgrades/1.3.820.md +25 -0
- package/upgrades/1.3.821.md +22 -0
- package/upgrades/side-effects/ownership-gated-spawn-increment-1.md +166 -0
- package/upgrades/side-effects/two-node-harness-increment2-entry-gate.md +64 -0
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DuplicateSessionReconciler — Layer B of ownership-gated-spawn-and-judgment-
|
|
3
|
+
* within-floors (§3.2): converge the OWNERSHIP RECORD when the same
|
|
4
|
+
* conversation is live on two or more machines, so the EXISTING gated closeout
|
|
5
|
+
* (SessionReaper's topic-moved sweeper) can close the non-owner copy through
|
|
6
|
+
* its normal path. **The reconciler never kills a session** — it repairs
|
|
7
|
+
* records; the closeout's own guards (fresh re-checks, veto breaker,
|
|
8
|
+
* terminate-time re-probe) do every close.
|
|
9
|
+
*
|
|
10
|
+
* The 2026-07-10 incident's healing gap in one line: the duplicate detector
|
|
11
|
+
* was observe-only and request-driven (only ran when a dashboard polled), and
|
|
12
|
+
* both machines' cleanup separately refused the duplicates (`not-lease-holder`
|
|
13
|
+
* on one side, `open-commitment` on the other) — two individually-correct
|
|
14
|
+
* guards jointly making a duplicate immortal.
|
|
15
|
+
*
|
|
16
|
+
* Deterministic core (all ambiguity ESCALATES, never guesses):
|
|
17
|
+
* - §3.2.0 substrate gate: refuses to arm on an in-memory ownership store
|
|
18
|
+
* (fleet default) — a loud `substrate-not-ready` status, never silent.
|
|
19
|
+
* - §3.2.1 discovery on the serving-lease holder only, tick-paced, fresh
|
|
20
|
+
* direct probe before ANY record write; per-tick caps (P17).
|
|
21
|
+
* - §3.2.2 intended-owner evidence ladder: hard pin (never a quarantined
|
|
22
|
+
* one) → highest ADMISSIBLE ownershipEpoch → server-registered live
|
|
23
|
+
* autonomous run (probe-confirmed) → J2/escalate. Rule-2/rule-3
|
|
24
|
+
* contradiction, both-self-with-equal-epochs, both-live-runs, and CAS
|
|
25
|
+
* 409s always escalate. "Most recent user interaction" is deliberately
|
|
26
|
+
* NOT a rule — the non-owner duplicate often has the latest message
|
|
27
|
+
* BECAUSE of the bug.
|
|
28
|
+
* - §3.2.3 convergence: ONE fenced CAS naming the intended owner (journal
|
|
29
|
+
* replication fast-forwards every peer), then peer-echo confirmation
|
|
30
|
+
* within `echoConfirmTicks`; no echo → ONE aggregated
|
|
31
|
+
* `convergence-not-observed` escalation (P17).
|
|
32
|
+
* - §3.2.5 P19 breaker: a topic re-duplicating past the threshold stops
|
|
33
|
+
* being auto-reconciled and raises ONE item; record FLIPS and
|
|
34
|
+
* echoed-but-unhealed episodes count; transfer-traceable episodes are
|
|
35
|
+
* excluded from the clamp (but ≥3/24h raise an observability-only item).
|
|
36
|
+
* - Registry-error freeze: while the SpawnAdmission error episode is open,
|
|
37
|
+
* the reconciler pauses (same fault domain — §3.1 row e).
|
|
38
|
+
*
|
|
39
|
+
* Increment-1 posture: dev-gated + dryRun — detection, evidence ladder, and
|
|
40
|
+
* would-converge decisions run and journal; NO CAS lands, NO closeout arms
|
|
41
|
+
* until a deliberate dryRun:false (and the substrate gate holds regardless).
|
|
42
|
+
*/
|
|
43
|
+
/** §3.2.5 — receive-side type clamp for replicated breaker rows. */
|
|
44
|
+
export function clampBreakerRow(row) {
|
|
45
|
+
if (!row || typeof row !== 'object')
|
|
46
|
+
return null;
|
|
47
|
+
const r = row;
|
|
48
|
+
if (typeof r.key !== 'string' || r.key.length === 0 || r.key.length > 256)
|
|
49
|
+
return null;
|
|
50
|
+
if (typeof r.count !== 'number' || !Number.isFinite(r.count) || r.count < 0 || r.count > 1_000_000)
|
|
51
|
+
return null;
|
|
52
|
+
if (typeof r.lastAt !== 'string' || Number.isNaN(Date.parse(r.lastAt)))
|
|
53
|
+
return null;
|
|
54
|
+
return { key: r.key, count: Math.floor(r.count), lastAt: r.lastAt };
|
|
55
|
+
}
|
|
56
|
+
export class DuplicateSessionReconciler {
|
|
57
|
+
deps;
|
|
58
|
+
cfg;
|
|
59
|
+
nowFn;
|
|
60
|
+
episodes = new Map();
|
|
61
|
+
/** §3.2.5 per-topic re-duplication episode opens within the window. */
|
|
62
|
+
breakerEpisodes = new Map();
|
|
63
|
+
/** Topics whose episode is transfer-traceable this window (excluded from the clamp). */
|
|
64
|
+
transferTraceable = new Map();
|
|
65
|
+
lastTick = null;
|
|
66
|
+
substrateNotReadySince = null;
|
|
67
|
+
substratePauseItemRaised = false;
|
|
68
|
+
counters = {
|
|
69
|
+
ticks: 0,
|
|
70
|
+
candidatesSeen: 0,
|
|
71
|
+
converged: 0,
|
|
72
|
+
wouldConverge: 0,
|
|
73
|
+
escalations: 0,
|
|
74
|
+
echoConfirmed: 0,
|
|
75
|
+
echoTimeouts: 0,
|
|
76
|
+
breakerClamps: 0,
|
|
77
|
+
probeDeferrals: 0,
|
|
78
|
+
frozenTicks: 0,
|
|
79
|
+
};
|
|
80
|
+
constructor(cfg, deps) {
|
|
81
|
+
this.cfg = cfg;
|
|
82
|
+
this.deps = deps;
|
|
83
|
+
this.nowFn = deps.now ?? (() => Date.now());
|
|
84
|
+
}
|
|
85
|
+
setConfig(cfg) {
|
|
86
|
+
this.cfg = cfg;
|
|
87
|
+
}
|
|
88
|
+
/** One reconciler pass. Returns the report (also retained for status()). */
|
|
89
|
+
async tick() {
|
|
90
|
+
const report = {
|
|
91
|
+
at: new Date(this.nowFn()).toISOString(),
|
|
92
|
+
ran: false,
|
|
93
|
+
candidates: 0,
|
|
94
|
+
reconciled: 0,
|
|
95
|
+
wouldConverge: 0,
|
|
96
|
+
escalations: 0,
|
|
97
|
+
echoConfirmed: 0,
|
|
98
|
+
echoTimeouts: 0,
|
|
99
|
+
breakerClamped: 0,
|
|
100
|
+
};
|
|
101
|
+
this.counters.ticks++;
|
|
102
|
+
try {
|
|
103
|
+
if (!this.cfg.enabled)
|
|
104
|
+
return this.finish(report, 'disabled');
|
|
105
|
+
if (!this.deps.holdsLease())
|
|
106
|
+
return this.finish(report, 'not-lease-holder');
|
|
107
|
+
if (this.deps.errorEpisodeOpen()) {
|
|
108
|
+
this.counters.frozenTicks++;
|
|
109
|
+
return this.finish(report, 'registry-error-episode-open (frozen — same fault domain as the spawn error arm)');
|
|
110
|
+
}
|
|
111
|
+
const substrate = this.deps.substrateReady();
|
|
112
|
+
if (!substrate.ready) {
|
|
113
|
+
this.noteSubstrateNotReady(substrate.reason);
|
|
114
|
+
return this.finish(report, `substrate-not-ready: ${substrate.reason ?? 'in-memory ownership store'}`);
|
|
115
|
+
}
|
|
116
|
+
this.substrateNotReadySince = null;
|
|
117
|
+
this.substratePauseItemRaised = false;
|
|
118
|
+
report.ran = true;
|
|
119
|
+
// Echo confirmation countdown for episodes with a pending convergence.
|
|
120
|
+
await this.checkPendingEchoes(report);
|
|
121
|
+
const { candidates, degraded } = await this.deps.discoverCandidates();
|
|
122
|
+
if (degraded) {
|
|
123
|
+
this.deps.log(`[dup-reconciler] discovery degraded: ${degraded}`);
|
|
124
|
+
}
|
|
125
|
+
report.candidates = candidates.length;
|
|
126
|
+
this.counters.candidatesSeen += candidates.length;
|
|
127
|
+
let reconcilesThisTick = 0;
|
|
128
|
+
let writesThisTick = 0;
|
|
129
|
+
for (const cand of candidates) {
|
|
130
|
+
if (reconcilesThisTick >= this.cfg.maxReconcilesPerTick)
|
|
131
|
+
break;
|
|
132
|
+
if (writesThisTick >= this.cfg.maxConvergenceWritesPerTick)
|
|
133
|
+
break;
|
|
134
|
+
const acted = await this.reconcileOne(cand, report);
|
|
135
|
+
if (acted.consumed)
|
|
136
|
+
reconcilesThisTick++;
|
|
137
|
+
if (acted.wrote)
|
|
138
|
+
writesThisTick++;
|
|
139
|
+
}
|
|
140
|
+
return this.finish(report);
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
this.deps.log(`[dup-reconciler] tick error: ${err?.message ?? String(err)}`);
|
|
144
|
+
return this.finish(report, `tick-error: ${err?.message ?? String(err)}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
finish(report, skippedReason) {
|
|
148
|
+
if (skippedReason)
|
|
149
|
+
report.skippedReason = skippedReason;
|
|
150
|
+
this.lastTick = report;
|
|
151
|
+
return report;
|
|
152
|
+
}
|
|
153
|
+
noteSubstrateNotReady(reason) {
|
|
154
|
+
const now = this.nowFn();
|
|
155
|
+
if (this.substrateNotReadySince === null)
|
|
156
|
+
this.substrateNotReadySince = now;
|
|
157
|
+
// §3.2.0 substrate flap: a NOT-ready pause persisting >30 min raises ONE item.
|
|
158
|
+
if (!this.substratePauseItemRaised && this.cfg.enabled && now - this.substrateNotReadySince > 30 * 60_000) {
|
|
159
|
+
this.substratePauseItemRaised = true;
|
|
160
|
+
this.deps.raiseAttention({
|
|
161
|
+
id: `dup-reconciler-substrate:${this.deps.selfMachineId() ?? 'local'}`,
|
|
162
|
+
title: 'Duplicate reconciler paused — ownership substrate not ready',
|
|
163
|
+
body: `The duplicate-session reconciler has been paused for over 30 minutes (${reason ?? 'in-memory ownership store / replication not live'}). Duplicates are not being auto-converged while this holds.`,
|
|
164
|
+
priority: 'medium',
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/** Reconcile one duplicate candidate. */
|
|
169
|
+
async reconcileOne(cand, report) {
|
|
170
|
+
const now = this.nowFn();
|
|
171
|
+
const sessionKey = cand.platform === 'telegram' ? cand.platformId : cand.platformId;
|
|
172
|
+
// One authority in motion per topic (§3.2.1 item 3).
|
|
173
|
+
if (this.deps.topicHasAuthorityInMotion(sessionKey)) {
|
|
174
|
+
this.journalRow({ kind: 'deferred-authority-in-motion', key: cand.key });
|
|
175
|
+
return { consumed: false, wrote: false };
|
|
176
|
+
}
|
|
177
|
+
// §3.2.5 breaker: a clamped topic is not auto-reconciled.
|
|
178
|
+
if (this.breakerClamped(cand.key, now)) {
|
|
179
|
+
report.breakerClamped++;
|
|
180
|
+
this.counters.breakerClamps++;
|
|
181
|
+
return { consumed: false, wrote: false };
|
|
182
|
+
}
|
|
183
|
+
const ep = this.openOrGetEpisode(cand.key, now);
|
|
184
|
+
if (ep.echoPending)
|
|
185
|
+
return { consumed: false, wrote: false }; // convergence in flight
|
|
186
|
+
if (ep.convergenceAttempts >= 3) {
|
|
187
|
+
// §3.8: 3 convergence attempts per episode, then escalate once.
|
|
188
|
+
this.escalateOnce(ep, cand, 'convergence-attempts-exhausted', report);
|
|
189
|
+
return { consumed: false, wrote: false };
|
|
190
|
+
}
|
|
191
|
+
// Fresh direct probes — a cache row is never acted on (§3.2.1 item 1).
|
|
192
|
+
const probes = new Map();
|
|
193
|
+
for (const m of cand.machines) {
|
|
194
|
+
const p = await this.deps.probeLiveCopy(m.machineId, cand.key);
|
|
195
|
+
if (!p.ok) {
|
|
196
|
+
this.counters.probeDeferrals++;
|
|
197
|
+
ep.convergenceAttempts++;
|
|
198
|
+
this.journalRow({ kind: 'probe-failed-deferred', key: cand.key, machine: m.machineId, attempts: ep.convergenceAttempts });
|
|
199
|
+
return { consumed: true, wrote: false };
|
|
200
|
+
}
|
|
201
|
+
probes.set(m.machineId, p.live);
|
|
202
|
+
}
|
|
203
|
+
const liveMachines = cand.machines.filter((m) => probes.get(m.machineId));
|
|
204
|
+
if (liveMachines.length < 2) {
|
|
205
|
+
// Duplicate no longer exists — close the episode quietly.
|
|
206
|
+
this.episodes.delete(cand.key);
|
|
207
|
+
this.journalRow({ kind: 'duplicate-resolved-before-action', key: cand.key });
|
|
208
|
+
return { consumed: true, wrote: false };
|
|
209
|
+
}
|
|
210
|
+
// Intended-owner determination (§3.2.2, evidence-ordered).
|
|
211
|
+
const verdict = await this.intendedOwner(sessionKey, cand, ep);
|
|
212
|
+
this.provenanceRow(cand, verdict);
|
|
213
|
+
if (verdict.kind === 'escalate') {
|
|
214
|
+
this.escalateOnce(ep, cand, verdict.reason, report);
|
|
215
|
+
return { consumed: true, wrote: false };
|
|
216
|
+
}
|
|
217
|
+
// `target-has-live-copy` precondition (§3.2.1): the intended owner must
|
|
218
|
+
// hold a LIVE copy — the reconciler never converges onto a dead target.
|
|
219
|
+
if (!probes.get(verdict.owner)) {
|
|
220
|
+
this.escalateOnce(ep, cand, `intended-owner-${verdict.owner}-has-no-live-copy`, report);
|
|
221
|
+
return { consumed: true, wrote: false };
|
|
222
|
+
}
|
|
223
|
+
ep.convergenceAttempts++;
|
|
224
|
+
if (this.cfg.dryRun) {
|
|
225
|
+
report.wouldConverge++;
|
|
226
|
+
this.counters.wouldConverge++;
|
|
227
|
+
this.journalRow({
|
|
228
|
+
kind: 'would-converge',
|
|
229
|
+
key: cand.key,
|
|
230
|
+
owner: verdict.owner,
|
|
231
|
+
rule: verdict.rule,
|
|
232
|
+
detail: verdict.detail,
|
|
233
|
+
machines: liveMachines.map((m) => m.machineId),
|
|
234
|
+
attempts: ep.convergenceAttempts,
|
|
235
|
+
dryRun: true,
|
|
236
|
+
});
|
|
237
|
+
// Dry-run counts an episode "handled" — reset so the soak journals each
|
|
238
|
+
// tick's fresh verdict without escalating attempt exhaustion.
|
|
239
|
+
this.episodes.delete(cand.key);
|
|
240
|
+
return { consumed: true, wrote: false };
|
|
241
|
+
}
|
|
242
|
+
// ── Already-converged record (the 2026-07-10 incident's OWN shape) ──
|
|
243
|
+
// A bootleg spawn is exactly the un-gated path: it creates a duplicate
|
|
244
|
+
// SESSION without ever touching the ownership RECORD, so the record
|
|
245
|
+
// frequently already names the intended owner. The FSM refuses a claim
|
|
246
|
+
// on an active self-owned record BY DESIGN ("claiming what you already
|
|
247
|
+
// own... would burn an epoch for nothing and masks a reconciler bug"),
|
|
248
|
+
// so a CAS here would land at `claim-out-of-sequence` → escalate — the
|
|
249
|
+
// incident shape would page the operator instead of self-healing.
|
|
250
|
+
// The record needs no repair: go straight to the peer-echo window (the
|
|
251
|
+
// MISSING piece may be replication/materialization on the peer), and the
|
|
252
|
+
// echo-confirmed path arms the existing closeout as usual. No epoch burn.
|
|
253
|
+
const selfView = this.deps
|
|
254
|
+
.readOwnershipViews(sessionKey)
|
|
255
|
+
.find((v) => v.admissible && v.machineId === (this.deps.selfMachineId() ?? 'self'));
|
|
256
|
+
if (selfView && selfView.owner === verdict.owner) {
|
|
257
|
+
this.counters.converged++;
|
|
258
|
+
report.reconciled++;
|
|
259
|
+
this.journalRow({
|
|
260
|
+
kind: 'record-already-converged',
|
|
261
|
+
key: cand.key,
|
|
262
|
+
owner: verdict.owner,
|
|
263
|
+
rule: verdict.rule,
|
|
264
|
+
detail: verdict.detail,
|
|
265
|
+
attempts: ep.convergenceAttempts,
|
|
266
|
+
});
|
|
267
|
+
ep.echoPending = {
|
|
268
|
+
owner: verdict.owner,
|
|
269
|
+
machines: liveMachines.map((m) => m.machineId).filter((m) => m !== verdict.owner),
|
|
270
|
+
ticksLeft: this.cfg.echoConfirmTicks,
|
|
271
|
+
};
|
|
272
|
+
return { consumed: true, wrote: false };
|
|
273
|
+
}
|
|
274
|
+
// ── LIVE convergence ──
|
|
275
|
+
const cas = this.deps.casConverge(sessionKey, verdict.owner);
|
|
276
|
+
if (!cas.ok) {
|
|
277
|
+
// A 409/conflict escalates, never retries blindly (§3.2.2 / FD13).
|
|
278
|
+
this.escalateOnce(ep, cand, `cas-refused: ${cas.reason ?? 'conflict'}`, report);
|
|
279
|
+
return { consumed: true, wrote: true };
|
|
280
|
+
}
|
|
281
|
+
this.counters.converged++;
|
|
282
|
+
report.reconciled++;
|
|
283
|
+
this.journalRow({
|
|
284
|
+
kind: 'converged-record',
|
|
285
|
+
key: cand.key,
|
|
286
|
+
owner: verdict.owner,
|
|
287
|
+
rule: verdict.rule,
|
|
288
|
+
detail: verdict.detail,
|
|
289
|
+
attempts: ep.convergenceAttempts,
|
|
290
|
+
});
|
|
291
|
+
// Peer-echo confirmation window (§3.2.3 item 2).
|
|
292
|
+
ep.echoPending = {
|
|
293
|
+
owner: verdict.owner,
|
|
294
|
+
machines: liveMachines.map((m) => m.machineId).filter((m) => m !== verdict.owner),
|
|
295
|
+
ticksLeft: this.cfg.echoConfirmTicks,
|
|
296
|
+
};
|
|
297
|
+
return { consumed: true, wrote: true };
|
|
298
|
+
}
|
|
299
|
+
/** §3.2.2 — the evidence ladder. */
|
|
300
|
+
async intendedOwner(sessionKey, cand, ep) {
|
|
301
|
+
// Rule 3 input first (needed for the rule-2/rule-3 contradiction guard).
|
|
302
|
+
let runs = [];
|
|
303
|
+
try {
|
|
304
|
+
runs = (await this.deps.liveRunHosts(sessionKey)).filter((r) => r.confirmed);
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
// @silent-fallback-ok: unreadable run registrations → rule 3 contributes
|
|
308
|
+
// no evidence; with no other evidence the verdict ESCALATES, never guesses.
|
|
309
|
+
runs = [];
|
|
310
|
+
}
|
|
311
|
+
// Both-copies-carry-live-runs → always escalate (§3.2.2).
|
|
312
|
+
if (runs.length >= 2)
|
|
313
|
+
return { kind: 'escalate', reason: 'both-copies-carry-live-runs' };
|
|
314
|
+
// Rule 1 — hard pin (quarantined never counts).
|
|
315
|
+
const pin = this.deps.readPin(sessionKey);
|
|
316
|
+
if (pin?.pinned && pin.preferredMachine && !pin.quarantined) {
|
|
317
|
+
return { kind: 'owner', owner: pin.preferredMachine, rule: 'hard-pin', detail: 'pin-store hard pin' };
|
|
318
|
+
}
|
|
319
|
+
// Rule 2 — highest ADMISSIBLE epoch; never wall-clock recency.
|
|
320
|
+
const views = this.deps.readOwnershipViews(sessionKey).filter((v) => v.admissible && v.owner);
|
|
321
|
+
let epochWinner = null;
|
|
322
|
+
let tie = false;
|
|
323
|
+
for (const v of views) {
|
|
324
|
+
if (!epochWinner || v.epoch > epochWinner.epoch) {
|
|
325
|
+
epochWinner = v;
|
|
326
|
+
tie = false;
|
|
327
|
+
}
|
|
328
|
+
else if (v.epoch === epochWinner.epoch && v.owner !== epochWinner.owner) {
|
|
329
|
+
tie = true;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (epochWinner && !tie) {
|
|
333
|
+
// Rule-2/rule-3 contradiction guard: epoch winner vs live-run host.
|
|
334
|
+
if (runs.length === 1 && runs[0].machineId !== epochWinner.owner) {
|
|
335
|
+
return {
|
|
336
|
+
kind: 'escalate',
|
|
337
|
+
reason: `rule2-rule3-contradiction: epoch names ${epochWinner.owner}, live run on ${runs[0].machineId}`,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
return {
|
|
341
|
+
kind: 'owner',
|
|
342
|
+
owner: epochWinner.owner,
|
|
343
|
+
rule: 'highest-epoch',
|
|
344
|
+
detail: `admissible epoch ${epochWinner.epoch} (view of ${epochWinner.machineId})`,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
// Rule 3 — a single confirmed live run, with PRE-EPISODE registration
|
|
348
|
+
// (evidence minted after the bootleg spawn never corroborates — §3.4 floor).
|
|
349
|
+
if (runs.length === 1) {
|
|
350
|
+
const r = runs[0];
|
|
351
|
+
if (r.registeredAt < ep.openedAt || !ep.openBackdated) {
|
|
352
|
+
return {
|
|
353
|
+
kind: 'owner',
|
|
354
|
+
owner: r.machineId,
|
|
355
|
+
rule: 'live-run',
|
|
356
|
+
detail: `server-registered live run (registered ${new Date(r.registeredAt).toISOString()})`,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
return { kind: 'escalate', reason: 'live-run-registered-after-episode-open (post-spawn evidence)' };
|
|
360
|
+
}
|
|
361
|
+
// Rule 4 — J2 arrives in Increment 3; the deterministic default IS escalate.
|
|
362
|
+
return { kind: 'escalate', reason: tie ? 'symmetric-divergence-equal-epochs' : 'no-admissible-evidence' };
|
|
363
|
+
}
|
|
364
|
+
async checkPendingEchoes(report) {
|
|
365
|
+
const timedOut = [];
|
|
366
|
+
for (const [key, ep] of this.episodes) {
|
|
367
|
+
if (!ep.echoPending)
|
|
368
|
+
continue;
|
|
369
|
+
const pending = [];
|
|
370
|
+
for (const m of ep.echoPending.machines) {
|
|
371
|
+
let echoed = false;
|
|
372
|
+
try {
|
|
373
|
+
echoed = await this.deps.peerEchoObserved(key.includes(':') ? key.split(':').slice(1).join(':') : key, ep.echoPending.owner, m);
|
|
374
|
+
}
|
|
375
|
+
catch {
|
|
376
|
+
echoed = false;
|
|
377
|
+
}
|
|
378
|
+
if (!echoed)
|
|
379
|
+
pending.push(m);
|
|
380
|
+
}
|
|
381
|
+
if (pending.length === 0) {
|
|
382
|
+
this.counters.echoConfirmed++;
|
|
383
|
+
report.echoConfirmed++;
|
|
384
|
+
this.journalRow({ kind: 'echo-confirmed', key, owner: ep.echoPending.owner });
|
|
385
|
+
// Convergence observed everywhere → the existing closeout owns the close.
|
|
386
|
+
if (!this.cfg.dryRun)
|
|
387
|
+
this.deps.armCloseout(key.includes(':') ? key.split(':').slice(1).join(':') : key, ep.echoPending.owner);
|
|
388
|
+
this.episodes.delete(key);
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
ep.echoPending.machines = pending;
|
|
392
|
+
ep.echoPending.ticksLeft--;
|
|
393
|
+
if (ep.echoPending.ticksLeft <= 0) {
|
|
394
|
+
this.counters.echoTimeouts++;
|
|
395
|
+
report.echoTimeouts++;
|
|
396
|
+
// Echoed-but-unhealed counts toward the breaker (§3.2.3 item 2 backstop).
|
|
397
|
+
this.bumpBreaker(key, this.nowFn());
|
|
398
|
+
timedOut.push({ key, owner: ep.echoPending.owner, machines: pending });
|
|
399
|
+
ep.echoPending = null;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
// Per-tick aggregation (P17): ALL convergence-not-observed escalations in
|
|
403
|
+
// one tick fold into ONE attention item enumerating topics.
|
|
404
|
+
if (timedOut.length > 0) {
|
|
405
|
+
report.escalations++;
|
|
406
|
+
this.counters.escalations++;
|
|
407
|
+
this.deps.raiseAttention({
|
|
408
|
+
id: `dup-reconciler-echo:${new Date(this.nowFn()).toISOString().slice(0, 13)}`,
|
|
409
|
+
title: `Convergence not observed on ${timedOut.length} conversation(s)`,
|
|
410
|
+
body: 'I repaired the ownership record for these conversations but the other machine(s) never showed the repair in their own view:\n' +
|
|
411
|
+
timedOut.map((t) => `• ${t.key} → ${t.owner} (unconfirmed: ${t.machines.join(', ')})`).join('\n') +
|
|
412
|
+
'\nDetails: logs/duplicate-reconciler.jsonl.',
|
|
413
|
+
priority: 'high',
|
|
414
|
+
});
|
|
415
|
+
for (const t of timedOut) {
|
|
416
|
+
this.journalRow({ kind: 'convergence-not-observed', key: t.key, owner: t.owner, unconfirmed: t.machines });
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
openOrGetEpisode(key, now) {
|
|
421
|
+
let ep = this.episodes.get(key);
|
|
422
|
+
if (ep)
|
|
423
|
+
return ep;
|
|
424
|
+
// §3 glossary: episode-open = the contested copy's spawn-registration ts,
|
|
425
|
+
// BACKDATED at detection. The wiring supplies detection-time here; the
|
|
426
|
+
// backdating refinement rides the spawn-registration read where available.
|
|
427
|
+
ep = { key, openedAt: now, openBackdated: false, convergenceAttempts: 0, echoPending: null, escalated: false };
|
|
428
|
+
this.episodes.set(key, ep);
|
|
429
|
+
this.bumpBreaker(key, now);
|
|
430
|
+
this.journalRow({ kind: 'duplicate-episode-opened', key, openedAt: new Date(now).toISOString() });
|
|
431
|
+
return ep;
|
|
432
|
+
}
|
|
433
|
+
/** Record a topic-moved (transfer-traceable) episode — excluded from the clamp. */
|
|
434
|
+
noteTransferTraceable(key) {
|
|
435
|
+
const now = this.nowFn();
|
|
436
|
+
const arr = (this.transferTraceable.get(key) ?? []).filter((t) => now - t < this.cfg.breakerWindowMs);
|
|
437
|
+
arr.push(now);
|
|
438
|
+
this.transferTraceable.set(key, arr);
|
|
439
|
+
// ≥3 transfer-traceable episodes per topic per window → ONE observability-only item.
|
|
440
|
+
if (arr.length === 3) {
|
|
441
|
+
this.deps.raiseAttention({
|
|
442
|
+
id: `dup-reconciler-transfer-flap:${key}`,
|
|
443
|
+
title: `Conversation ${key} moved machines ${arr.length} times in 24h`,
|
|
444
|
+
body: 'An automated mover is flapping this conversation between machines (moves are exempt from the duplicate breaker, but this volume is worth a look).',
|
|
445
|
+
priority: 'medium',
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
bumpBreaker(key, now) {
|
|
450
|
+
const arr = (this.breakerEpisodes.get(key) ?? []).filter((t) => now - t < this.cfg.breakerWindowMs);
|
|
451
|
+
arr.push(now);
|
|
452
|
+
this.breakerEpisodes.set(key, arr);
|
|
453
|
+
// TTL prune of idle topics rides the filter above; drop empty entries lazily.
|
|
454
|
+
if (arr.length === this.cfg.breakerThreshold) {
|
|
455
|
+
// Breaker just tripped: ONE item; the ladder clamp is read via breakerClamped().
|
|
456
|
+
this.deps.raiseAttention({
|
|
457
|
+
id: `dup-reconciler-breaker:${key}`,
|
|
458
|
+
title: `Conversation ${key} keeps re-duplicating — auto-reconcile stopped`,
|
|
459
|
+
body: `This conversation duplicated ${arr.length} times inside the window. I've stopped auto-converging it (and clamped its owner-dark ladder to the notice floor) — something is re-creating the duplicate. Details: logs/duplicate-reconciler.jsonl.`,
|
|
460
|
+
priority: 'high',
|
|
461
|
+
});
|
|
462
|
+
this.journalRow({ kind: 'breaker-tripped', key, episodes: arr.length });
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
/** §3.2.5 — also consulted by the owner-dark ladder (clamp to rung 3). */
|
|
466
|
+
breakerClamped(key, now) {
|
|
467
|
+
const t = now ?? this.nowFn();
|
|
468
|
+
const transferTraceable = (this.transferTraceable.get(key) ?? []).filter((x) => t - x < this.cfg.breakerWindowMs);
|
|
469
|
+
const arr = (this.breakerEpisodes.get(key) ?? []).filter((x) => t - x < this.cfg.breakerWindowMs);
|
|
470
|
+
// Transfer-traceable episodes do not clamp (§3.2.5).
|
|
471
|
+
const effective = arr.length - transferTraceable.length;
|
|
472
|
+
return effective >= this.cfg.breakerThreshold;
|
|
473
|
+
}
|
|
474
|
+
escalateOnce(ep, cand, reason, report) {
|
|
475
|
+
if (ep.escalated)
|
|
476
|
+
return;
|
|
477
|
+
ep.escalated = true;
|
|
478
|
+
report.escalations++;
|
|
479
|
+
this.counters.escalations++;
|
|
480
|
+
// Fixed template + metadata (§3.4) — never free text from tails/arbiters.
|
|
481
|
+
this.deps.raiseAttention({
|
|
482
|
+
id: `dup-reconciler-escalate:${cand.key}`,
|
|
483
|
+
title: `Duplicate conversation needs your call: ${cand.key}`,
|
|
484
|
+
body: `The same conversation is live on ${cand.machines.length} machines (${cand.machines.map((m) => m.machineId).join(', ')}) ` +
|
|
485
|
+
`and I can't safely converge it automatically (reason: ${reason}). ` +
|
|
486
|
+
`Close the copy you don't want from the dashboard, or move the topic explicitly. Details: logs/duplicate-reconciler.jsonl.`,
|
|
487
|
+
priority: 'high',
|
|
488
|
+
});
|
|
489
|
+
this.journalRow({ kind: 'escalated', key: cand.key, reason });
|
|
490
|
+
}
|
|
491
|
+
journalRow(row) {
|
|
492
|
+
this.deps.journal.append({ ts: new Date(this.nowFn()).toISOString(), ...row });
|
|
493
|
+
}
|
|
494
|
+
provenanceRow(cand, verdict) {
|
|
495
|
+
if (!this.deps.provenance)
|
|
496
|
+
return;
|
|
497
|
+
try {
|
|
498
|
+
this.deps.provenance({
|
|
499
|
+
component: 'DuplicateSessionReconciler',
|
|
500
|
+
decisionPoint: 'which-duplicate-survives',
|
|
501
|
+
context: {
|
|
502
|
+
key: cand.key,
|
|
503
|
+
machines: cand.machines.map((m) => m.machineId),
|
|
504
|
+
dryRun: this.cfg.dryRun,
|
|
505
|
+
},
|
|
506
|
+
optionsPresented: ['owner-copy-survives', 'escalate-to-attention'],
|
|
507
|
+
decision: verdict.kind === 'owner' ? `owner:${verdict.owner} (${verdict.rule})` : `escalate:${verdict.reason}`,
|
|
508
|
+
reason: verdict.kind === 'owner' ? verdict.detail : verdict.reason,
|
|
509
|
+
floor: 'evidence-ladder pin→epoch→live-run→escalate (§3.2.2); J2 shadow arrives Increment 3',
|
|
510
|
+
fallbackRung: 'deterministic',
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
catch {
|
|
514
|
+
/* @silent-fallback-ok: provenance is observability. */
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
status() {
|
|
518
|
+
const now = this.nowFn();
|
|
519
|
+
return {
|
|
520
|
+
enabled: this.cfg.enabled,
|
|
521
|
+
dryRun: this.cfg.dryRun,
|
|
522
|
+
substrate: this.deps.substrateReady(),
|
|
523
|
+
lastTick: this.lastTick,
|
|
524
|
+
openEpisodes: this.episodes.size,
|
|
525
|
+
breaker: [...this.breakerEpisodes.entries()]
|
|
526
|
+
.map(([key, arr]) => ({
|
|
527
|
+
key,
|
|
528
|
+
episodes: arr.filter((t) => now - t < this.cfg.breakerWindowMs).length,
|
|
529
|
+
clamped: this.breakerClamped(key, now),
|
|
530
|
+
}))
|
|
531
|
+
.filter((b) => b.episodes > 0),
|
|
532
|
+
counters: { ...this.counters },
|
|
533
|
+
config: {
|
|
534
|
+
reconcilerTickMs: this.cfg.reconcilerTickMs,
|
|
535
|
+
maxReconcilesPerTick: this.cfg.maxReconcilesPerTick,
|
|
536
|
+
maxConvergenceWritesPerTick: this.cfg.maxConvergenceWritesPerTick,
|
|
537
|
+
echoConfirmTicks: this.cfg.echoConfirmTicks,
|
|
538
|
+
breakerThreshold: this.cfg.breakerThreshold,
|
|
539
|
+
breakerWindowMs: this.cfg.breakerWindowMs,
|
|
540
|
+
},
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
//# sourceMappingURL=DuplicateSessionReconciler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DuplicateSessionReconciler.js","sourceRoot":"","sources":["../../src/monitoring/DuplicateSessionReconciler.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FailureAnalyzer.d.ts","sourceRoot":"","sources":["../../src/monitoring/FailureAnalyzer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,aAAa,EAAkC,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEvG,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,uBAAuB,EAAE,MAAM,CAAC;IAChC,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,aAAa,EAAE,aAI3B,CAAC;
|
|
1
|
+
{"version":3,"file":"FailureAnalyzer.d.ts","sourceRoot":"","sources":["../../src/monitoring/FailureAnalyzer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,aAAa,EAAkC,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEvG,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,uBAAuB,EAAE,MAAM,CAAC;IAChC,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,aAAa,EAAE,aAI3B,CAAC;AA6BF,MAAM,WAAW,aAAa;IAC5B,kBAAkB,EAAE,aAAa,EAAE,CAAC;IACpC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,MAAM;IAAiB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAA7C,MAAM,EAAE,aAAa,EAAmB,KAAK,GAAE,aAA6B;IAEzG;;;;OAIG;IACH,OAAO,IAAI,aAAa;CAkFzB"}
|
|
@@ -18,6 +18,15 @@ const RECOMMENDATION_BY_CATEGORY = {
|
|
|
18
18
|
regression: 'Recurring regressions — a shipped feature keeps breaking; add a regression guard (a test pinning the behavior) for this path.',
|
|
19
19
|
unknown: 'Recurring uncategorized failures — improve the failure-categorization step so these become actionable.',
|
|
20
20
|
};
|
|
21
|
+
/**
|
|
22
|
+
* Judgment Within Floors (ownership-gated-spawn spec §3.6): the paired
|
|
23
|
+
* recommendation for the filer-flagged `judgmentCandidate` cluster. Template
|
|
24
|
+
* text, never free LLM prose — same contract as RECOMMENDATION_BY_CATEGORY.
|
|
25
|
+
*/
|
|
26
|
+
const JUDGMENT_CANDIDATE_RECOMMENDATION = 'Recurring failures trace to static heuristics at competing-signals decision points — ' +
|
|
27
|
+
'evaluate each as a judgment point within a deterministic floor (bounded action space, ' +
|
|
28
|
+
'conservative default, bench-laddered arbiter) per the Judgment Within Floors standard ' +
|
|
29
|
+
'in docs/STANDARDS-REGISTRY.md.';
|
|
21
30
|
export class FailureAnalyzer {
|
|
22
31
|
ledger;
|
|
23
32
|
gates;
|
|
@@ -76,9 +85,39 @@ export class FailureAnalyzer {
|
|
|
76
85
|
if (insight)
|
|
77
86
|
discovered.push(insight);
|
|
78
87
|
}
|
|
88
|
+
// Judgment-candidate cluster (§3.6): filer-flagged records cluster ACROSS
|
|
89
|
+
// categories — the shared trait is "a static heuristic at a competing-signals
|
|
90
|
+
// decision point failed", not the failure category. Same diversity gates.
|
|
91
|
+
const jcCluster = records.filter((r) => r.judgmentCandidate === true);
|
|
92
|
+
let jcConsidered = 0;
|
|
93
|
+
if (jcCluster.length > 0) {
|
|
94
|
+
jcConsidered = 1;
|
|
95
|
+
const distinctSessions = new Set(jcCluster.map((r) => r.filedBy)).size;
|
|
96
|
+
const distinctCauseCommits = new Set(jcCluster.map((r) => r.causeCommitOid ?? r.id)).size;
|
|
97
|
+
const crosses = jcCluster.length >= this.gates.minSupport &&
|
|
98
|
+
distinctSessions >= this.gates.minDistinctSessions &&
|
|
99
|
+
distinctCauseCommits >= this.gates.minDistinctCauseCommits;
|
|
100
|
+
if (crosses) {
|
|
101
|
+
const insight = this.ledger.upsertInsight({
|
|
102
|
+
identityKey: 'judgment-candidate',
|
|
103
|
+
summary: `${jcCluster.length} attributed judgment-candidate failures (static heuristics at competing-signals decision points) across ${distinctSessions} sessions / ${distinctCauseCommits} cause-commits`,
|
|
104
|
+
recommendation: JUDGMENT_CANDIDATE_RECOMMENDATION,
|
|
105
|
+
supportingFailureIds: jcCluster.map((r) => r.id),
|
|
106
|
+
distinctSessions,
|
|
107
|
+
distinctCauseCommits,
|
|
108
|
+
targetCategory: 'judgment-candidate',
|
|
109
|
+
baselineRate: jcCluster.length,
|
|
110
|
+
});
|
|
111
|
+
if (insight)
|
|
112
|
+
discovered.push(insight);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
belowThreshold++;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
79
118
|
return {
|
|
80
119
|
insightsDiscovered: discovered,
|
|
81
|
-
clustersConsidered: byCategory.size,
|
|
120
|
+
clustersConsidered: byCategory.size + jcConsidered,
|
|
82
121
|
clustersBelowThreshold: belowThreshold,
|
|
83
122
|
};
|
|
84
123
|
}
|