instar 1.3.676 → 1.3.677

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.
@@ -0,0 +1,65 @@
1
+ /**
2
+ * SpawningTopicsRegistry — F7 Piece 1
3
+ * (docs/specs/verify-after-reachability.md §Piece 1).
4
+ *
5
+ * Replaces the closure-local `Set<number>` that `onTopicMessage` used to guard against
6
+ * double-spawning a topic. The Set was cleared ONLY in the spawn promise's `.finally`,
7
+ * so a HUNG spawn left the flag set forever → every subsequent inbound for that topic
8
+ * was silently skipped (the single-machine black-hole F7 surfaces). It carried no
9
+ * timestamp and no token.
10
+ *
11
+ * This component is the SAFE minimum (round-2 review proved any external auto-CLEAR of
12
+ * the flag relocates the double-spawn race, because the spawn body is non-cancellable):
13
+ * - `add(topic)` returns a unique TOKEN and stamps `startedAtMs`.
14
+ * - `clear(topic, token)` is TOKEN-GUARDED: it deletes ONLY if the live entry's token
15
+ * still matches — so a late `.finally` from a superseded spawn cannot delete a newer
16
+ * entry (the ABA fix). The `.finally` on a spawn's own settle remains the SOLE
17
+ * clearer; NO timeout and NO sweep clear the flag here.
18
+ * - `stuckSinceMs(topic, now)` lets the TopicReachabilityVerifier SURFACE a spawn that
19
+ * has been in flight past a threshold (it is never cleared — surfaced, not raced).
20
+ *
21
+ * The mechanical auto-recovery of a hung spawn (cancellable spawn) is a tracked
22
+ * follow-up — see the spec.
23
+ */
24
+ export interface SpawningEntry {
25
+ /** Unique per-add token (NOT the topic — the ABA guard). */
26
+ token: string;
27
+ /** ms epoch when this spawn entered the registry. */
28
+ startedAtMs: number;
29
+ }
30
+ export declare class SpawningTopicsRegistry {
31
+ private readonly map;
32
+ private readonly now;
33
+ private seq;
34
+ constructor(deps?: {
35
+ now?: () => number;
36
+ });
37
+ /**
38
+ * Mark `topic` as spawning. Returns a unique token the caller passes back to
39
+ * `clear`. If an entry already exists (a retry that already landed), it is REPLACED
40
+ * with a fresh token+timestamp — the new spawn supersedes the old, and the old
41
+ * spawn's later `clear(old token)` becomes a no-op (ABA-safe).
42
+ */
43
+ add(topic: number): string;
44
+ /**
45
+ * Token-guarded clear. Deletes the entry ONLY if its live token equals `token`.
46
+ * A late `.finally` from a superseded spawn (whose token no longer matches) is a
47
+ * no-op, so it can never delete a newer spawn's entry.
48
+ */
49
+ clear(topic: number, token: string): void;
50
+ /** Is `topic` currently marked spawning? (The hot-path double-spawn guard read.) */
51
+ has(topic: number): boolean;
52
+ /**
53
+ * If `topic` has been spawning since longer than now-startedAtMs, return that age in
54
+ * ms; else undefined. The verifier uses this to detect a wedged spawn. NEVER clears.
55
+ */
56
+ stuckSinceMs(topic: number, nowMs?: number): number | undefined;
57
+ /** Snapshot of currently-spawning topics (for the verifier / status). */
58
+ entries(): Array<{
59
+ topic: number;
60
+ startedAtMs: number;
61
+ }>;
62
+ /** Count of in-flight spawns (naturally tiny — one per concurrent spawn). */
63
+ size(): number;
64
+ }
65
+ //# sourceMappingURL=SpawningTopicsRegistry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpawningTopicsRegistry.d.ts","sourceRoot":"","sources":["../../src/core/SpawningTopicsRegistry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,WAAW,aAAa;IAC5B,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoC;IACxD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,GAAG,CAAK;gBAEJ,IAAI,GAAE;QAAE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;KAAO;IAI7C;;;;;OAKG;IACH,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAM1B;;;;OAIG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAKzC,oFAAoF;IACpF,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAI3B;;;OAGG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAmB,GAAG,MAAM,GAAG,SAAS;IAM3E,yEAAyE;IACzE,OAAO,IAAI,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAIxD,6EAA6E;IAC7E,IAAI,IAAI,MAAM;CAGf"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * SpawningTopicsRegistry — F7 Piece 1
3
+ * (docs/specs/verify-after-reachability.md §Piece 1).
4
+ *
5
+ * Replaces the closure-local `Set<number>` that `onTopicMessage` used to guard against
6
+ * double-spawning a topic. The Set was cleared ONLY in the spawn promise's `.finally`,
7
+ * so a HUNG spawn left the flag set forever → every subsequent inbound for that topic
8
+ * was silently skipped (the single-machine black-hole F7 surfaces). It carried no
9
+ * timestamp and no token.
10
+ *
11
+ * This component is the SAFE minimum (round-2 review proved any external auto-CLEAR of
12
+ * the flag relocates the double-spawn race, because the spawn body is non-cancellable):
13
+ * - `add(topic)` returns a unique TOKEN and stamps `startedAtMs`.
14
+ * - `clear(topic, token)` is TOKEN-GUARDED: it deletes ONLY if the live entry's token
15
+ * still matches — so a late `.finally` from a superseded spawn cannot delete a newer
16
+ * entry (the ABA fix). The `.finally` on a spawn's own settle remains the SOLE
17
+ * clearer; NO timeout and NO sweep clear the flag here.
18
+ * - `stuckSinceMs(topic, now)` lets the TopicReachabilityVerifier SURFACE a spawn that
19
+ * has been in flight past a threshold (it is never cleared — surfaced, not raced).
20
+ *
21
+ * The mechanical auto-recovery of a hung spawn (cancellable spawn) is a tracked
22
+ * follow-up — see the spec.
23
+ */
24
+ export class SpawningTopicsRegistry {
25
+ map = new Map();
26
+ now;
27
+ seq = 0;
28
+ constructor(deps = {}) {
29
+ this.now = deps.now ?? (() => Date.now());
30
+ }
31
+ /**
32
+ * Mark `topic` as spawning. Returns a unique token the caller passes back to
33
+ * `clear`. If an entry already exists (a retry that already landed), it is REPLACED
34
+ * with a fresh token+timestamp — the new spawn supersedes the old, and the old
35
+ * spawn's later `clear(old token)` becomes a no-op (ABA-safe).
36
+ */
37
+ add(topic) {
38
+ const token = `spawn:${topic}:${this.now().toString(36)}:${(this.seq++).toString(36)}`;
39
+ this.map.set(topic, { token, startedAtMs: this.now() });
40
+ return token;
41
+ }
42
+ /**
43
+ * Token-guarded clear. Deletes the entry ONLY if its live token equals `token`.
44
+ * A late `.finally` from a superseded spawn (whose token no longer matches) is a
45
+ * no-op, so it can never delete a newer spawn's entry.
46
+ */
47
+ clear(topic, token) {
48
+ const e = this.map.get(topic);
49
+ if (e && e.token === token)
50
+ this.map.delete(topic);
51
+ }
52
+ /** Is `topic` currently marked spawning? (The hot-path double-spawn guard read.) */
53
+ has(topic) {
54
+ return this.map.has(topic);
55
+ }
56
+ /**
57
+ * If `topic` has been spawning since longer than now-startedAtMs, return that age in
58
+ * ms; else undefined. The verifier uses this to detect a wedged spawn. NEVER clears.
59
+ */
60
+ stuckSinceMs(topic, nowMs = this.now()) {
61
+ const e = this.map.get(topic);
62
+ if (!e)
63
+ return undefined;
64
+ return Math.max(0, nowMs - e.startedAtMs);
65
+ }
66
+ /** Snapshot of currently-spawning topics (for the verifier / status). */
67
+ entries() {
68
+ return [...this.map.entries()].map(([topic, e]) => ({ topic, startedAtMs: e.startedAtMs }));
69
+ }
70
+ /** Count of in-flight spawns (naturally tiny — one per concurrent spawn). */
71
+ size() {
72
+ return this.map.size;
73
+ }
74
+ }
75
+ //# sourceMappingURL=SpawningTopicsRegistry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpawningTopicsRegistry.js","sourceRoot":"","sources":["../../src/core/SpawningTopicsRegistry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AASH,MAAM,OAAO,sBAAsB;IAChB,GAAG,GAAG,IAAI,GAAG,EAAyB,CAAC;IACvC,GAAG,CAAe;IAC3B,GAAG,GAAG,CAAC,CAAC;IAEhB,YAAY,OAA+B,EAAE;QAC3C,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;OAKG;IACH,GAAG,CAAC,KAAa;QACf,MAAM,KAAK,GAAG,SAAS,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;QACvF,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACxD,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,KAAa,EAAE,KAAa;QAChC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,KAAK;YAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACrD,CAAC;IAED,oFAAoF;IACpF,GAAG,CAAC,KAAa;QACf,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC;IAED;;;OAGG;IACH,YAAY,CAAC,KAAa,EAAE,QAAgB,IAAI,CAAC,GAAG,EAAE;QACpD,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,CAAC,CAAC;YAAE,OAAO,SAAS,CAAC;QACzB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC;IAC5C,CAAC;IAED,yEAAyE;IACzE,OAAO;QACL,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IAC9F,CAAC;IAED,6EAA6E;IAC7E,IAAI;QACF,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;IACvB,CAAC;CACF"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * TopicReachabilityVerifier — F7 Piece 2 (PURE SIGNAL)
3
+ * (docs/specs/verify-after-reachability.md §Piece 2).
4
+ *
5
+ * After a destructive session/routing mutation (a `sessionReaped(terminal)` or an
6
+ * ownership release/transfer), it verifies the affected topic is still inbound-reachable
7
+ * and SURFACES a genuine orphan as ONE NORMAL-priority attention item. It mutates
8
+ * NOTHING — no clear, spawn, kill, transfer, or re-place. It is a smoke alarm.
9
+ *
10
+ * Honesty guard: the dominant single-machine path already self-heals (the next inbound
11
+ * auto-spawns), so a topic that simply has no session now but WILL spawn on the next
12
+ * message is REACHABLE, not orphaned — the verifier must not scream on every idle kill.
13
+ * Only the specific defeats of the self-heal (stuck-spawn, at-capacity, released-no-
14
+ * placement, stalled inbound-queue) are orphans.
15
+ *
16
+ * This module is the DECISION CORE (deterministic, tick-driven, injected deps). The
17
+ * server wires the triggers (events) + the live `probe` (reads session/placement state)
18
+ * + the attention sink; this class owns grace/coalescing/pressure/suppression/dedup.
19
+ */
20
+ export type OrphanReason = 'stuck-spawn' | 'at-capacity' | 'released-no-placement' | 'inbound-queue-stalled' | 'partition-suspected';
21
+ export type Reachability = {
22
+ reachable: true;
23
+ } | {
24
+ reachable: false;
25
+ reason: OrphanReason;
26
+ };
27
+ export interface AttentionSurface {
28
+ /** A single attention item (NORMAL priority, stable sourceContext, deduped by key). */
29
+ key: string;
30
+ topics: number[];
31
+ reason: string;
32
+ rolledUp: boolean;
33
+ }
34
+ export interface VerifierDeps {
35
+ /** Live reachability classification for a topic (reads local session/placement state). */
36
+ probe: (topic: number) => Reachability;
37
+ /** Raise one NORMAL attention item. */
38
+ surface: (item: AttentionSurface) => void;
39
+ /** True ⇒ skip per-topic verify churn (mass-reap is the pressure; don't amplify). */
40
+ pressureCritical: () => boolean;
41
+ /** True ⇒ an operator emergency-stop / halt is active; suppress surfacing. */
42
+ emergencyStopActive: () => boolean;
43
+ now: () => number;
44
+ /** Verify delay after a mutation (default 30s; > normal respawn so a healthy bounce isn't flagged). */
45
+ graceMs?: number;
46
+ /** Orphan count in a flush past which a single rolled-up item is emitted (default 10). */
47
+ burstThreshold?: number;
48
+ /** Per-topic minimum re-surface interval, exponential floor (default 1h). */
49
+ resurfaceFloorMs?: number;
50
+ /** Hard cap on pending verifies (overflow counted). */
51
+ maxPendingVerifies?: number;
52
+ }
53
+ export declare class TopicReachabilityVerifier {
54
+ private readonly d;
55
+ /** topic → earliest time the verify is due (now + grace at record time). Coalesced. */
56
+ private readonly pending;
57
+ /** Topics whose verify was SKIPPED under pressure / SUPPRESSED under halt — re-swept on clear. */
58
+ private readonly deferredWindow;
59
+ private readonly dedup;
60
+ private _overflow;
61
+ private _orphansSurfaced;
62
+ private _verifiedReachable;
63
+ private _lastTickAt;
64
+ constructor(deps: VerifierDeps);
65
+ /** A destructive mutation hit `topic` — schedule a coalesced post-grace verify. */
66
+ recordMutation(topic: number): void;
67
+ /**
68
+ * Process verifies whose grace has elapsed. Pure-signal: classifies + surfaces.
69
+ * Returns a report (for the status route + tests).
70
+ */
71
+ tick(): {
72
+ surfaced: number;
73
+ reachable: number;
74
+ skipped: number;
75
+ overflow: number;
76
+ pending: number;
77
+ };
78
+ private surfaceOrphans;
79
+ /** Backoff gate: re-surface only past an exponentially-widening floor, regardless of re-arm. */
80
+ private shouldSurface;
81
+ private bumpDedup;
82
+ private markReachable;
83
+ status(): {
84
+ pending: number;
85
+ deferred: number;
86
+ orphansSurfaced: number;
87
+ verifiedReachable: number;
88
+ overflow: number;
89
+ lastTickAt: number;
90
+ };
91
+ }
92
+ //# sourceMappingURL=TopicReachabilityVerifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TopicReachabilityVerifier.d.ts","sourceRoot":"","sources":["../../src/monitoring/TopicReachabilityVerifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,MAAM,YAAY,GACpB,aAAa,GACb,aAAa,GACb,uBAAuB,GACvB,uBAAuB,GACvB,qBAAqB,CAAC;AAE1B,MAAM,MAAM,YAAY,GAAG;IAAE,SAAS,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,SAAS,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE,CAAC;AAE5F,MAAM,WAAW,gBAAgB;IAC/B,uFAAuF;IACvF,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,0FAA0F;IAC1F,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,YAAY,CAAC;IACvC,uCAAuC;IACvC,OAAO,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC1C,qFAAqF;IACrF,gBAAgB,EAAE,MAAM,OAAO,CAAC;IAChC,8EAA8E;IAC9E,mBAAmB,EAAE,MAAM,OAAO,CAAC;IACnC,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,uGAAuG;IACvG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0FAA0F;IAC1F,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,uDAAuD;IACvD,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAgBD,qBAAa,yBAAyB;IACpC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAC6E;IAC/F,uFAAuF;IACvF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,kGAAkG;IAClG,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;IACvD,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,WAAW,CAAK;gBAEZ,IAAI,EAAE,YAAY;IAU9B,mFAAmF;IACnF,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAYnC;;;OAGG;IACH,IAAI,IAAI;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;IAgDnG,OAAO,CAAC,cAAc;IAqCtB,gGAAgG;IAChG,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,SAAS;IAQjB,OAAO,CAAC,aAAa;IAQrB,MAAM,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE;CAU1I"}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * TopicReachabilityVerifier — F7 Piece 2 (PURE SIGNAL)
3
+ * (docs/specs/verify-after-reachability.md §Piece 2).
4
+ *
5
+ * After a destructive session/routing mutation (a `sessionReaped(terminal)` or an
6
+ * ownership release/transfer), it verifies the affected topic is still inbound-reachable
7
+ * and SURFACES a genuine orphan as ONE NORMAL-priority attention item. It mutates
8
+ * NOTHING — no clear, spawn, kill, transfer, or re-place. It is a smoke alarm.
9
+ *
10
+ * Honesty guard: the dominant single-machine path already self-heals (the next inbound
11
+ * auto-spawns), so a topic that simply has no session now but WILL spawn on the next
12
+ * message is REACHABLE, not orphaned — the verifier must not scream on every idle kill.
13
+ * Only the specific defeats of the self-heal (stuck-spawn, at-capacity, released-no-
14
+ * placement, stalled inbound-queue) are orphans.
15
+ *
16
+ * This module is the DECISION CORE (deterministic, tick-driven, injected deps). The
17
+ * server wires the triggers (events) + the live `probe` (reads session/placement state)
18
+ * + the attention sink; this class owns grace/coalescing/pressure/suppression/dedup.
19
+ */
20
+ const DEFAULT_GRACE_MS = 30_000;
21
+ const DEFAULT_BURST = 10;
22
+ const DEFAULT_RESURFACE_FLOOR_MS = 3_600_000;
23
+ const DEFAULT_MAX_PENDING = 500;
24
+ export class TopicReachabilityVerifier {
25
+ d;
26
+ /** topic → earliest time the verify is due (now + grace at record time). Coalesced. */
27
+ pending = new Map();
28
+ /** Topics whose verify was SKIPPED under pressure / SUPPRESSED under halt — re-swept on clear. */
29
+ deferredWindow = new Set();
30
+ dedup = new Map();
31
+ _overflow = 0;
32
+ _orphansSurfaced = 0;
33
+ _verifiedReachable = 0;
34
+ _lastTickAt = 0;
35
+ constructor(deps) {
36
+ this.d = {
37
+ ...deps,
38
+ graceMs: deps.graceMs ?? DEFAULT_GRACE_MS,
39
+ burstThreshold: deps.burstThreshold ?? DEFAULT_BURST,
40
+ resurfaceFloorMs: deps.resurfaceFloorMs ?? DEFAULT_RESURFACE_FLOOR_MS,
41
+ maxPendingVerifies: deps.maxPendingVerifies ?? DEFAULT_MAX_PENDING,
42
+ };
43
+ }
44
+ /** A destructive mutation hit `topic` — schedule a coalesced post-grace verify. */
45
+ recordMutation(topic) {
46
+ const due = this.d.now() + this.d.graceMs;
47
+ if (!this.pending.has(topic)) {
48
+ if (this.pending.size >= this.d.maxPendingVerifies) {
49
+ this._overflow++;
50
+ return;
51
+ }
52
+ this.pending.set(topic, due);
53
+ }
54
+ // coalesced: an existing pending verify keeps its (earlier) due time.
55
+ }
56
+ /**
57
+ * Process verifies whose grace has elapsed. Pure-signal: classifies + surfaces.
58
+ * Returns a report (for the status route + tests).
59
+ */
60
+ tick() {
61
+ const now = this.d.now();
62
+ this._lastTickAt = now;
63
+ const due = [];
64
+ for (const [topic, dueAt] of this.pending) {
65
+ if (dueAt <= now)
66
+ due.push(topic);
67
+ }
68
+ const halt = this.d.emergencyStopActive();
69
+ const pressure = this.d.pressureCritical();
70
+ let skipped = 0;
71
+ const orphans = [];
72
+ for (const topic of due) {
73
+ this.pending.delete(topic);
74
+ // Under halt or critical pressure we do NOT churn per-topic — defer to re-sweep.
75
+ if (halt || pressure) {
76
+ this.deferredWindow.add(topic);
77
+ skipped++;
78
+ continue;
79
+ }
80
+ const r = this.d.probe(topic);
81
+ if (r.reachable) {
82
+ this._verifiedReachable++;
83
+ this.markReachable(topic); // re-arm dedup
84
+ }
85
+ else {
86
+ orphans.push({ topic, reason: r.reason });
87
+ }
88
+ }
89
+ // A window just cleared (no halt + no pressure) → re-sweep deferred topics once.
90
+ if (!halt && !pressure && this.deferredWindow.size > 0) {
91
+ for (const topic of [...this.deferredWindow]) {
92
+ this.deferredWindow.delete(topic);
93
+ const r = this.d.probe(topic);
94
+ if (r.reachable) {
95
+ this._verifiedReachable++;
96
+ this.markReachable(topic);
97
+ }
98
+ else {
99
+ orphans.push({ topic, reason: r.reason });
100
+ }
101
+ }
102
+ }
103
+ const surfaced = this.surfaceOrphans(orphans, now, pressure);
104
+ return { surfaced, reachable: this._verifiedReachable, skipped, overflow: this._overflow, pending: this.pending.size };
105
+ }
106
+ surfaceOrphans(orphans, now, pressure) {
107
+ if (orphans.length === 0)
108
+ return 0;
109
+ // Burst roll-up: a mass-orphan (or partition) → ONE rolled-up item, never N.
110
+ if (orphans.length >= this.d.burstThreshold || pressure) {
111
+ const topics = orphans.map((o) => o.topic);
112
+ this.d.surface({
113
+ key: 'topic-reachability:burst',
114
+ topics,
115
+ reason: `${topics.length} topics may be unreachable`,
116
+ rolledUp: true,
117
+ });
118
+ for (const o of orphans)
119
+ this.bumpDedup(o.topic, now);
120
+ this._orphansSurfaced += 1;
121
+ return 1;
122
+ }
123
+ // Per-topic with backoff (a single flapper cannot mint per cycle).
124
+ let count = 0;
125
+ for (const o of orphans) {
126
+ if (this.shouldSurface(o.topic, now)) {
127
+ this.d.surface({
128
+ key: `topic-reachability:${o.topic}`,
129
+ topics: [o.topic],
130
+ reason: `topic ${o.topic} may be unreachable (${o.reason})`,
131
+ rolledUp: false,
132
+ });
133
+ this.bumpDedup(o.topic, now);
134
+ this._orphansSurfaced++;
135
+ count++;
136
+ }
137
+ }
138
+ return count;
139
+ }
140
+ /** Backoff gate: re-surface only past an exponentially-widening floor, regardless of re-arm. */
141
+ shouldSurface(topic, now) {
142
+ const s = this.dedup.get(topic);
143
+ if (!s || s.armed)
144
+ return true; // first time, or re-armed by a verified-reachable obs
145
+ const wait = this.d.resurfaceFloorMs * Math.pow(2, Math.max(0, s.surfaceCount - 1));
146
+ return now - s.lastSurfacedMs >= wait;
147
+ }
148
+ bumpDedup(topic, now) {
149
+ const s = this.dedup.get(topic) ?? { lastSurfacedMs: 0, surfaceCount: 0, armed: true };
150
+ s.lastSurfacedMs = now;
151
+ s.surfaceCount = s.armed ? 1 : s.surfaceCount + 1;
152
+ s.armed = false;
153
+ this.dedup.set(topic, s);
154
+ }
155
+ markReachable(topic) {
156
+ const s = this.dedup.get(topic);
157
+ if (s) {
158
+ s.armed = true; // a genuine re-orphan after heal may surface again (not suppressed forever)
159
+ s.surfaceCount = 0;
160
+ }
161
+ }
162
+ status() {
163
+ return {
164
+ pending: this.pending.size,
165
+ deferred: this.deferredWindow.size,
166
+ orphansSurfaced: this._orphansSurfaced,
167
+ verifiedReachable: this._verifiedReachable,
168
+ overflow: this._overflow,
169
+ lastTickAt: this._lastTickAt,
170
+ };
171
+ }
172
+ }
173
+ //# sourceMappingURL=TopicReachabilityVerifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TopicReachabilityVerifier.js","sourceRoot":"","sources":["../../src/monitoring/TopicReachabilityVerifier.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.3.676",
3
+ "version": "1.3.677",
4
4
  "description": "Coherence infrastructure for self-evolving AI agents — on the Claude Code or Codex subscription you already have.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-06-26T13:09:14.108Z",
5
- "instarVersion": "1.3.676",
4
+ "generatedAt": "2026-06-26T13:12:48.410Z",
5
+ "instarVersion": "1.3.677",
6
6
  "entryCount": 202,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -15,6 +15,16 @@ background chatter. The total cap is NEVER raised — only *who gets which slot*
15
15
  Ships dark on the fleet / live on a development agent (dev-agent gate); byte-identical
16
16
  to today when off.
17
17
 
18
+ Lands postmortem fix F7 (Blast-Radius / Verify-After), dark on the fleet / live on a dev
19
+ agent. Two parts: (1) the live inbound spawn-guard is now a token-tagged
20
+ `SpawningTopicsRegistry` — a hung session start no longer silently wedges a topic forever
21
+ (ABA-safe; the `.finally` stays the sole clearer, no risky auto-clear). (2) a pure-signal
22
+ `TopicReachabilityVerifier`: after a session is killed/reaped, it checks (after a grace
23
+ window) that the conversation can still receive your next message, and if a genuine
24
+ orphan (e.g. a wedged start-up) it surfaces ONE calm NORMAL heads-up. It mutates nothing —
25
+ never kills, spawns, or clears. The probe is conservative + fail-safe (uncertain ⇒
26
+ treated as reachable), so it never cries wolf on a normal idle kill.
27
+
18
28
  ## What to Tell Your User
19
29
 
20
30
  When the machine is busy, your reply's safety check now jumps to a reserved slot instead
@@ -22,6 +32,13 @@ of waiting behind background work — fewer slow/held replies under load. The cr
22
32
  protection is exactly as strong as before. Most setups see no change (it's off on the
23
33
  fleet for now).
24
34
 
35
+ If I ever shut down a conversation's session and it genuinely can't be reached again
36
+ (e.g. a start-up that hung), you now get one calm "this conversation may be unreachable"
37
+ heads-up instead of your messages silently vanishing. Most of the time you see nothing,
38
+ because most kills self-heal on your next message. It only watches — it never tries to
39
+ auto-fix the stuck state (that proved too risky); the mechanical repair stays a tracked
40
+ follow-up. Off on the fleet for now.
41
+
25
42
  ## Summary of New Capabilities
26
43
 
27
44
  - `attribution.lane:'interactive'` requests reserved headroom — honored ONLY for an
@@ -32,6 +49,13 @@ fleet for now).
32
49
  - `/spawn-limiter` reports per-lane live counts + the reservation config.
33
50
  - Off (the fleet default) ⇒ byte-identical to the all-or-nothing cap (no `lane` written).
34
51
 
52
+ - A hung session start-up can no longer permanently jam a conversation's inbound path
53
+ (token-tagged spawn guard; ABA-safe).
54
+ - After a destructive session/routing op, a dev agent verifies the topic is still
55
+ reachable and surfaces a genuine orphan as ONE NORMAL attention item (deduped,
56
+ flap-backoff-capped, burst-rolled-up, pressure/emergency-stop aware with a re-sweep).
57
+ - Visible in `/guards` (registered). Dark on the fleet (dev-agent gate). Mutates nothing.
58
+
35
59
  ## Evidence
36
60
 
37
61
  - `hostSpawnSemaphore-priority.test.ts` (10): symmetric reserve, OOM floor unconditional,
@@ -43,3 +67,12 @@ fleet for now).
43
67
  fork-bomb burst-invariant test still green). `tsc --noEmit` clean.
44
68
  - Side-effects: `upgrades/side-effects/spawn-cap-interactive-priority.md`.
45
69
  - Spec (converged + approved): `docs/specs/spawn-cap-interactive-priority.md`.
70
+
71
+ - `spawningTopicsRegistry.test.ts` (5): ABA token-guard; `.finally` sole clearer (no
72
+ timeout/sweep); stuck-age seam.
73
+ - `topicReachabilityVerifier.test.ts` (8): grace; reachable-honesty (no false orphan on a
74
+ topic that self-heals); orphan→one NORMAL item; pressure/halt skip + re-sweep; flap
75
+ backoff; burst roll-up; coalescing.
76
+ - `tsc --noEmit` clean.
77
+ - Side-effects: `upgrades/side-effects/verify-after-reachability.md`.
78
+ - Spec (converged + approved): `docs/specs/verify-after-reachability.md`.
@@ -0,0 +1,65 @@
1
+ # Side-Effects Review — Verify-After Topic Reachability (F7 — core components)
2
+
3
+ **Version / slug:** `verify-after-reachability`
4
+ **Date:** `2026-06-26`
5
+ **Author:** `Echo (instar-dev agent)`
6
+ **Tier:** 2 (converged + approved spec: `docs/specs/verify-after-reachability.md`)
7
+ **Scope:** the two pure components (first commit) PLUS the server wiring (this commit):
8
+ Piece 1 (`spawningTopics` closure→`SpawningTopicsRegistry` refactor of the live inbound
9
+ path, token-guarded at all 4 callsites) and Piece 2 (the verifier constructed dev-gated,
10
+ triggered by `sessionReaped`, a conservative fail-safe probe, a 15s tick, NORMAL
11
+ attention surfacing, and `guardRegistry` registration). The probe is deliberately
12
+ CONSERVATIVE: a live session ⇒ reachable; a topic stuck-spawning past `stuckSpawnMs` ⇒
13
+ orphan; ANYTHING ELSE ⇒ reachable (the next inbound self-heals) — so it never
14
+ false-orphans an idle kill. The multi-machine released-no-placement + at-capacity orphan
15
+ cases (placement reads) and a dedicated `/topic-reachability` route are a tracked
16
+ follow-up; their absence means LESS coverage, never a false orphan (the probe fails safe
17
+ to reachable). <!-- tracked: topic-28744 F7-followup multimachine-probe-and-route -->
18
+
19
+ ## What this commit adds
20
+
21
+ - `SpawningTopicsRegistry` (src/core) — token-tagged replacement for the closure-local
22
+ `spawningTopics` Set: `add` returns a token, `clear` is token-guarded (the ABA fix so a
23
+ late `.finally` from a superseded spawn cannot delete a newer entry), `stuckSinceMs`
24
+ exposes in-flight age for the verifier. NO timeout, NO sweep — the `.finally` remains
25
+ the sole clearer (round-2 proved any external clear relocates the double-spawn race).
26
+ - `TopicReachabilityVerifier` (src/monitoring) — the PURE-SIGNAL decision core: grace +
27
+ per-topic coalescing, the reachable-honesty guard (a topic that will self-heal on next
28
+ inbound is REACHABLE, not orphaned), NORMAL-priority surfacing with per-topic
29
+ exponential backoff (flap cap), burst roll-up, pressure-skip + emergency-stop-suppress
30
+ WITH a re-sweep on clear (no never-surfaced orphan). It MUTATES NOTHING.
31
+
32
+ Neither component is wired into the running server yet (no runtime surface), so this
33
+ commit is inert at runtime — pure library code under test.
34
+
35
+ ## The 8 questions (for the components as committed)
36
+
37
+ 1. **Over-block** — N/A. The verifier blocks nothing; it surfaces a signal. The registry
38
+ only guards double-spawn (existing behavior), now ABA-safe.
39
+ 2. **Under-block** — N/A.
40
+ 3. **Level-of-abstraction fit** — Correct. Pure decision logic separated from the server
41
+ wiring + live-state probe (the spec's design).
42
+ 4. **Signal vs authority** — The verifier is a pure signal (zero mutation). The registry's
43
+ only authority is the EXISTING token-guarded clear (no new clearer). Complies.
44
+ 5. **Interactions** — None at runtime (unwired). The registry, once wired, replaces the
45
+ closure Set; the token guard makes its clear idempotent/ABA-safe.
46
+ 6. **External surfaces** — None yet (unwired). The future wiring adds a NORMAL attention
47
+ item + a `/topic-reachability` status route + a `/guards` entry.
48
+ 7. **Multi-machine** — The verifier's released-no-placement detection (future probe) is
49
+ machine-local (reads the local placement snapshot, freshness-gated); machine-local BY
50
+ DESIGN. The components themselves hold no cross-machine state.
51
+ 8. **Rollback cost** — Trivial. Delete two new files + their tests; nothing references
52
+ them at runtime yet.
53
+
54
+ ## Tests
55
+
56
+ - `spawningTopicsRegistry.test.ts` (5): the ABA token-guard, `.finally` is the sole
57
+ clearer (no timeout/sweep), `stuckSinceMs`, entries snapshot.
58
+ - `topicReachabilityVerifier.test.ts` (8): grace; reachable-honesty (no false orphan);
59
+ orphan→one NORMAL item; pressure-skip+re-sweep; halt-suppress+re-sweep; flap backoff;
60
+ burst roll-up; coalescing.
61
+ - `tsc --noEmit` clean.
62
+
63
+ ## Rollback
64
+
65
+ Delete the two component files + tests. No runtime reference.