instar 1.3.489 → 1.3.491

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.
Files changed (175) hide show
  1. package/dashboard/index.html +30 -0
  2. package/dist/commands/server.d.ts.map +1 -1
  3. package/dist/commands/server.js +1124 -28
  4. package/dist/commands/server.js.map +1 -1
  5. package/dist/config/ConfigDefaults.d.ts.map +1 -1
  6. package/dist/config/ConfigDefaults.js +23 -0
  7. package/dist/config/ConfigDefaults.js.map +1 -1
  8. package/dist/core/CodexResumeMap.d.ts +95 -0
  9. package/dist/core/CodexResumeMap.d.ts.map +1 -0
  10. package/dist/core/CodexResumeMap.js +283 -0
  11. package/dist/core/CodexResumeMap.js.map +1 -0
  12. package/dist/core/GuardPostureStore.d.ts +38 -0
  13. package/dist/core/GuardPostureStore.d.ts.map +1 -0
  14. package/dist/core/GuardPostureStore.js +87 -0
  15. package/dist/core/GuardPostureStore.js.map +1 -0
  16. package/dist/core/MachinePoolRegistry.d.ts +16 -0
  17. package/dist/core/MachinePoolRegistry.d.ts.map +1 -1
  18. package/dist/core/MachinePoolRegistry.js +24 -1
  19. package/dist/core/MachinePoolRegistry.js.map +1 -1
  20. package/dist/core/MeshRpc.d.ts +3 -0
  21. package/dist/core/MeshRpc.d.ts.map +1 -1
  22. package/dist/core/MeshRpc.js +5 -0
  23. package/dist/core/MeshRpc.js.map +1 -1
  24. package/dist/core/ModelSwapService.d.ts +26 -11
  25. package/dist/core/ModelSwapService.d.ts.map +1 -1
  26. package/dist/core/ModelSwapService.js +59 -21
  27. package/dist/core/ModelSwapService.js.map +1 -1
  28. package/dist/core/PeerPresencePuller.d.ts +9 -0
  29. package/dist/core/PeerPresencePuller.d.ts.map +1 -1
  30. package/dist/core/PeerPresencePuller.js +1 -1
  31. package/dist/core/PeerPresencePuller.js.map +1 -1
  32. package/dist/core/PostUpdateMigrator.d.ts +16 -0
  33. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  34. package/dist/core/PostUpdateMigrator.js +92 -4
  35. package/dist/core/PostUpdateMigrator.js.map +1 -1
  36. package/dist/core/SessionManager.d.ts +4 -0
  37. package/dist/core/SessionManager.d.ts.map +1 -1
  38. package/dist/core/SessionManager.js +3 -0
  39. package/dist/core/SessionManager.js.map +1 -1
  40. package/dist/core/SessionRefresh.d.ts +36 -7
  41. package/dist/core/SessionRefresh.d.ts.map +1 -1
  42. package/dist/core/SessionRefresh.js +90 -29
  43. package/dist/core/SessionRefresh.js.map +1 -1
  44. package/dist/core/TopicProfileOrchestrator.d.ts +480 -0
  45. package/dist/core/TopicProfileOrchestrator.d.ts.map +1 -0
  46. package/dist/core/TopicProfileOrchestrator.js +1404 -0
  47. package/dist/core/TopicProfileOrchestrator.js.map +1 -0
  48. package/dist/core/TopicProfileResolver.d.ts +104 -0
  49. package/dist/core/TopicProfileResolver.d.ts.map +1 -0
  50. package/dist/core/TopicProfileResolver.js +231 -0
  51. package/dist/core/TopicProfileResolver.js.map +1 -0
  52. package/dist/core/TopicProfileStore.d.ts +308 -0
  53. package/dist/core/TopicProfileStore.d.ts.map +1 -0
  54. package/dist/core/TopicProfileStore.js +781 -0
  55. package/dist/core/TopicProfileStore.js.map +1 -0
  56. package/dist/core/TopicProfileTransferCarrier.d.ts +227 -0
  57. package/dist/core/TopicProfileTransferCarrier.d.ts.map +1 -0
  58. package/dist/core/TopicProfileTransferCarrier.js +533 -0
  59. package/dist/core/TopicProfileTransferCarrier.js.map +1 -0
  60. package/dist/core/TopicResumeMap.d.ts +67 -1
  61. package/dist/core/TopicResumeMap.d.ts.map +1 -1
  62. package/dist/core/TopicResumeMap.js +114 -1
  63. package/dist/core/TopicResumeMap.js.map +1 -1
  64. package/dist/core/classifyProfileChange.d.ts +83 -0
  65. package/dist/core/classifyProfileChange.d.ts.map +1 -0
  66. package/dist/core/classifyProfileChange.js +274 -0
  67. package/dist/core/classifyProfileChange.js.map +1 -0
  68. package/dist/core/devGatedFeatures.d.ts.map +1 -1
  69. package/dist/core/devGatedFeatures.js +6 -0
  70. package/dist/core/devGatedFeatures.js.map +1 -1
  71. package/dist/core/frameworkSessionLaunch.d.ts +26 -0
  72. package/dist/core/frameworkSessionLaunch.d.ts.map +1 -1
  73. package/dist/core/frameworkSessionLaunch.js +58 -0
  74. package/dist/core/frameworkSessionLaunch.js.map +1 -1
  75. package/dist/core/slackRefreshBinding.d.ts +83 -0
  76. package/dist/core/slackRefreshBinding.d.ts.map +1 -0
  77. package/dist/core/slackRefreshBinding.js +54 -0
  78. package/dist/core/slackRefreshBinding.js.map +1 -0
  79. package/dist/core/topicProfileIngress.d.ts +128 -0
  80. package/dist/core/topicProfileIngress.d.ts.map +1 -0
  81. package/dist/core/topicProfileIngress.js +249 -0
  82. package/dist/core/topicProfileIngress.js.map +1 -0
  83. package/dist/core/topicProfileValidation.d.ts +109 -0
  84. package/dist/core/topicProfileValidation.d.ts.map +1 -0
  85. package/dist/core/topicProfileValidation.js +247 -0
  86. package/dist/core/topicProfileValidation.js.map +1 -0
  87. package/dist/core/topicProfileWriteSurface.d.ts +222 -0
  88. package/dist/core/topicProfileWriteSurface.d.ts.map +1 -0
  89. package/dist/core/topicProfileWriteSurface.js +631 -0
  90. package/dist/core/topicProfileWriteSurface.js.map +1 -0
  91. package/dist/core/types.d.ts +66 -0
  92. package/dist/core/types.d.ts.map +1 -1
  93. package/dist/core/types.js.map +1 -1
  94. package/dist/lifeline/TelegramLifeline.d.ts.map +1 -1
  95. package/dist/lifeline/TelegramLifeline.js +6 -0
  96. package/dist/lifeline/TelegramLifeline.js.map +1 -1
  97. package/dist/messaging/TelegramAdapter.d.ts +10 -1
  98. package/dist/messaging/TelegramAdapter.d.ts.map +1 -1
  99. package/dist/messaging/TelegramAdapter.js +41 -1
  100. package/dist/messaging/TelegramAdapter.js.map +1 -1
  101. package/dist/monitoring/ActiveWorkSilenceSentinel.d.ts +8 -0
  102. package/dist/monitoring/ActiveWorkSilenceSentinel.d.ts.map +1 -1
  103. package/dist/monitoring/ActiveWorkSilenceSentinel.js +8 -0
  104. package/dist/monitoring/ActiveWorkSilenceSentinel.js.map +1 -1
  105. package/dist/monitoring/ContextWedgeSentinel.d.ts +8 -0
  106. package/dist/monitoring/ContextWedgeSentinel.d.ts.map +1 -1
  107. package/dist/monitoring/ContextWedgeSentinel.js +8 -0
  108. package/dist/monitoring/ContextWedgeSentinel.js.map +1 -1
  109. package/dist/monitoring/GuardPostureTripwire.d.ts +3 -27
  110. package/dist/monitoring/GuardPostureTripwire.d.ts.map +1 -1
  111. package/dist/monitoring/GuardPostureTripwire.js +8 -76
  112. package/dist/monitoring/GuardPostureTripwire.js.map +1 -1
  113. package/dist/monitoring/GuardRegistry.d.ts +48 -0
  114. package/dist/monitoring/GuardRegistry.d.ts.map +1 -0
  115. package/dist/monitoring/GuardRegistry.js +49 -0
  116. package/dist/monitoring/GuardRegistry.js.map +1 -0
  117. package/dist/monitoring/SessionReaper.d.ts +7 -0
  118. package/dist/monitoring/SessionReaper.d.ts.map +1 -1
  119. package/dist/monitoring/SessionReaper.js +9 -0
  120. package/dist/monitoring/SessionReaper.js.map +1 -1
  121. package/dist/monitoring/SessionWatchdog.d.ts +8 -0
  122. package/dist/monitoring/SessionWatchdog.d.ts.map +1 -1
  123. package/dist/monitoring/SessionWatchdog.js +8 -0
  124. package/dist/monitoring/SessionWatchdog.js.map +1 -1
  125. package/dist/monitoring/SocketDisconnectSentinel.d.ts +8 -0
  126. package/dist/monitoring/SocketDisconnectSentinel.d.ts.map +1 -1
  127. package/dist/monitoring/SocketDisconnectSentinel.js +8 -0
  128. package/dist/monitoring/SocketDisconnectSentinel.js.map +1 -1
  129. package/dist/monitoring/guardManifest.d.ts +67 -0
  130. package/dist/monitoring/guardManifest.d.ts.map +1 -0
  131. package/dist/monitoring/guardManifest.js +536 -0
  132. package/dist/monitoring/guardManifest.js.map +1 -0
  133. package/dist/monitoring/guardPosture.d.ts +77 -0
  134. package/dist/monitoring/guardPosture.d.ts.map +1 -0
  135. package/dist/monitoring/guardPosture.js +198 -0
  136. package/dist/monitoring/guardPosture.js.map +1 -0
  137. package/dist/monitoring/guardPostureView.d.ts +100 -0
  138. package/dist/monitoring/guardPostureView.d.ts.map +1 -0
  139. package/dist/monitoring/guardPostureView.js +294 -0
  140. package/dist/monitoring/guardPostureView.js.map +1 -0
  141. package/dist/monitoring/probes/GuardPostureProbe.d.ts +82 -0
  142. package/dist/monitoring/probes/GuardPostureProbe.d.ts.map +1 -0
  143. package/dist/monitoring/probes/GuardPostureProbe.js +384 -0
  144. package/dist/monitoring/probes/GuardPostureProbe.js.map +1 -0
  145. package/dist/scaffold/templates.d.ts.map +1 -1
  146. package/dist/scaffold/templates.js +18 -0
  147. package/dist/scaffold/templates.js.map +1 -1
  148. package/dist/scheduler/JobScheduler.d.ts +10 -0
  149. package/dist/scheduler/JobScheduler.d.ts.map +1 -1
  150. package/dist/scheduler/JobScheduler.js +12 -0
  151. package/dist/scheduler/JobScheduler.js.map +1 -1
  152. package/dist/server/AgentServer.d.ts +40 -0
  153. package/dist/server/AgentServer.d.ts.map +1 -1
  154. package/dist/server/AgentServer.js +54 -3
  155. package/dist/server/AgentServer.js.map +1 -1
  156. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  157. package/dist/server/CapabilityIndex.js +12 -0
  158. package/dist/server/CapabilityIndex.js.map +1 -1
  159. package/dist/server/peerUrlGuard.d.ts +35 -0
  160. package/dist/server/peerUrlGuard.d.ts.map +1 -0
  161. package/dist/server/peerUrlGuard.js +93 -0
  162. package/dist/server/peerUrlGuard.js.map +1 -0
  163. package/dist/server/routes.d.ts +31 -0
  164. package/dist/server/routes.d.ts.map +1 -1
  165. package/dist/server/routes.js +407 -1
  166. package/dist/server/routes.js.map +1 -1
  167. package/package.json +3 -2
  168. package/scripts/lint-guard-manifest.js +264 -0
  169. package/src/data/builtin-manifest.json +67 -67
  170. package/src/data/state-coherence-registry.json +54 -18
  171. package/src/scaffold/templates.ts +18 -0
  172. package/upgrades/1.3.490.md +34 -0
  173. package/upgrades/1.3.491.md +37 -0
  174. package/upgrades/side-effects/guard-posture-endpoint.md +101 -0
  175. package/upgrades/side-effects/topic-profile.md +142 -0
@@ -0,0 +1,533 @@
1
+ /**
2
+ * TopicProfileTransferCarrier — the §5.3 transfer-follow carrier
3
+ * (TOPIC-PROFILE-SPEC §5.3, "pull-at-ACQUIRE — a named prerequisite sub-task").
4
+ *
5
+ * A Topic Profile is STICKY OPERATOR INTENT and must follow the topic across a
6
+ * session-pool move. Verified on target: `POST /pool/transfer` carries no
7
+ * payload and has no receive-side hook, and topics also change machines
8
+ * through paths that never run the planner (hard failover, quota-aware
9
+ * placement, lease movement). So the carriage hook lives at the
10
+ * **ownership-ACQUIRE chokepoint**: when THIS machine acquires a topic, it
11
+ * pulls the topic's profile entry from the previous owner over the existing
12
+ * Ed25519 machine-auth channel (the `topic-profile-pull` MeshRpc verb) —
13
+ * covering planner transfers, failovers, and placement moves with ONE
14
+ * mechanism.
15
+ *
16
+ * Spec discipline implemented here:
17
+ * - **Batch + latency bounds** (§5.3 round-5 scalability): acquires arriving
18
+ * within the batching window coalesce into ONE pull per previous-owner
19
+ * peer carrying all topics acquired from it; the durable retry is keyed
20
+ * per (peer, batch) with backoff — never N independent per-topic retries.
21
+ * Defaults-with-disclosure notices aggregate into one summary per landing.
22
+ * - **Fully async**: `onTopicAcquired` is fire-and-forget — the spawn NEVER
23
+ * waits (the strong form of the §5.3 ~1-2s budget); reconcile-on-pull-
24
+ * landing handles late arrival.
25
+ * - **Unreachable previous owner**: the pull is filed DURABLY
26
+ * (`state/topic-profile-pending-pulls.json`) before the first attempt, so
27
+ * a crash/restart never loses it; while it is pending, resolution keeps
28
+ * using the LOCAL entry (`hasPendingPull` is the staleness signal for the
29
+ * resolution layer's disclosure).
30
+ * - **Late pull never clobbers a fresher local operator write** (§5.3
31
+ * rounds 5-8, event-ordered and clock-free): a local operator (or
32
+ * token-trust HTTP) write DURABLY AMENDS the pending-pull record itself —
33
+ * removes that topic from the (peer, batch) retry entry — via
34
+ * `onLocalWriteDurable`, which the write surface calls ONLY after the
35
+ * triggering mutate's flush has durably landed (`store.mutate` resolves
36
+ * only then, so "after await mutate" IS the contract; a flush-refused
37
+ * write throws and cancels nothing). The amendment survives restarts
38
+ * exactly as well as the pull it cancels. The apply path re-reads the
39
+ * durable record at landing time, so a write racing an in-flight pull
40
+ * cancels the REPLACE too. System-attributed writes NEVER cancel.
41
+ * - **updatedAt backstop** (clock-skew-aware, for pulls landing with no
42
+ * recorded local write event): local newer wins, ties favor local, and
43
+ * the pulled entry's updatedAt is clamped to ≤ the pull-receipt time for
44
+ * the comparison (a future-dated pulled entry is treated as older,
45
+ * audited). System-attributed local writes never supersede via the
46
+ * backstop (§5.3 round-6 — the breaker must not shed a transferred pin
47
+ * through a side door).
48
+ * - **Newly-pending supersedes older pending** for the same topic (rapid
49
+ * A→B→C→B re-transfers can't leave two pulls racing).
50
+ * - **protocol-unsupported ≠ unreachable**: a peer answering `no-handler`
51
+ * (its instar predates the verb) PARKS the record until
52
+ * `peerSupportsPull` reports support — no backoff spin against a
53
+ * permanent 404.
54
+ * - **Absent on the previous owner clears nothing** — the local entry stays
55
+ * (a failover-then-transfer-back must not wipe a still-valid pin).
56
+ * - **Receiving-machine revalidation is MANDATORY**: every landing goes
57
+ * through `store.replaceEntry(..., { revalidate })` (§10.2 closed-enum +
58
+ * framework-compat clamp; dropped fields disclosed). Provenance travels
59
+ * verbatim but the audit tags the landing `origin:'transfer:<machineId>'`
60
+ * — peer-asserted, never a locally-verified principal.
61
+ *
62
+ * The serve side (`createTopicProfilePullHandler`) is the pool-layer handler
63
+ * the dispatcher registers for the `topic-profile-pull` verb: it serves this
64
+ * machine's OWN entries (current + dry-run shadow — `previous` stays local,
65
+ * undo means "back to what this machine had").
66
+ */
67
+ import fs from 'node:fs';
68
+ import path from 'node:path';
69
+ /** Upper bound on topics served per pull (a mass failover stays one request). */
70
+ export const MAX_TOPICS_PER_PULL = 500;
71
+ /**
72
+ * The serve-side handler for the `topic-profile-pull` MeshRpc verb — pool
73
+ * layer registers it on the dispatcher. Stateless; reads the authoritative
74
+ * in-memory cache (O(topics)). Absent entries answer `present:false` so the
75
+ * receiver can distinguish "no entry" (clear nothing) from a null profile.
76
+ */
77
+ export function createTopicProfilePullHandler(deps) {
78
+ const cap = deps.maxTopicsPerPull ?? MAX_TOPICS_PER_PULL;
79
+ return (cmd) => {
80
+ if (!Array.isArray(cmd.topics) || cmd.topics.some((t) => typeof t !== 'string')) {
81
+ return { ok: false, reason: 'malformed-topics' };
82
+ }
83
+ if (cmd.topics.length > cap) {
84
+ return { ok: false, reason: 'too-many-topics' };
85
+ }
86
+ const entries = cmd.topics.map((topicKey) => {
87
+ const entry = deps.store.get(topicKey);
88
+ if (!entry || (entry.current === null && entry.intendedProfile === null)) {
89
+ return { topicKey, present: false, current: null, intendedProfile: null };
90
+ }
91
+ return {
92
+ topicKey,
93
+ present: true,
94
+ current: entry.current ? { ...entry.current } : null,
95
+ intendedProfile: entry.intendedProfile ? { ...entry.intendedProfile } : null,
96
+ };
97
+ });
98
+ return { ok: true, entries };
99
+ };
100
+ }
101
+ const DEFAULT_BATCH_WINDOW_MS = 250;
102
+ const DEFAULT_RETRY_BACKOFF_START_MS = 30_000;
103
+ const DEFAULT_RETRY_BACKOFF_MAX_MS = 30 * 60_000;
104
+ const DEFAULT_PENDING_TTL_MS = 7 * 24 * 60 * 60_000;
105
+ export class TopicProfileTransferCarrier {
106
+ d;
107
+ ledgerFile;
108
+ /** Staged acquires per previous-owner peer, awaiting the batch window. */
109
+ staged = new Map();
110
+ batchTimers = new Map();
111
+ /** Single-flight per peer (attempts never overlap per peer). */
112
+ inFlight = new Map();
113
+ episodeSeq = 0;
114
+ /** Durable ledger cache (loaded lazily, kept in sync with every write). */
115
+ pulls = null;
116
+ constructor(deps) {
117
+ this.d = deps;
118
+ this.ledgerFile = path.join(deps.stateDir, 'state', 'topic-profile-pending-pulls.json');
119
+ }
120
+ now() {
121
+ return this.d.now?.() ?? new Date();
122
+ }
123
+ // ── the ACQUIRE hook (fire-and-forget, never blocks a spawn) ──────────────
124
+ /**
125
+ * THIS machine acquired ownership of a topic. Stages a pull from the
126
+ * previous owner; acquires within the batch window coalesce into ONE pull
127
+ * per peer (§5.3 batch bound). Fire-and-forget: returns immediately.
128
+ */
129
+ onTopicAcquired(topicKey, prevOwnerMachineId) {
130
+ const key = String(topicKey);
131
+ let prev = prevOwnerMachineId;
132
+ if (prev === undefined) {
133
+ try {
134
+ prev = this.d.prevOwnerOf?.(key) ?? null;
135
+ }
136
+ catch (e) {
137
+ // @silent-fallback-ok: audited (kind:'prev-owner-lookup-failed') —
138
+ // the ACQUIRE hook is fire-and-forget and must never block a spawn
139
+ // (§5.3); with no resolvable previous owner there is nothing to pull
140
+ // from, and the local entry (if any) remains authoritative.
141
+ this.d.audit?.({ kind: 'prev-owner-lookup-failed', topic: key, error: e instanceof Error ? e.message : String(e) });
142
+ prev = null;
143
+ }
144
+ }
145
+ if (!prev || prev === this.d.selfMachineId)
146
+ return; // nothing to pull / self-move
147
+ let set = this.staged.get(prev);
148
+ if (!set) {
149
+ set = new Set();
150
+ this.staged.set(prev, set);
151
+ }
152
+ set.add(key);
153
+ if (!this.batchTimers.has(prev)) {
154
+ const peer = prev;
155
+ const t = setTimeout(() => {
156
+ this.batchTimers.delete(peer);
157
+ void this.flushStagedPeer(peer).catch((e) => {
158
+ this.d.logger?.(`topic-profile pull flush failed for ${peer}: ${e instanceof Error ? e.message : String(e)}`);
159
+ });
160
+ }, this.d.batchWindowMs ?? DEFAULT_BATCH_WINDOW_MS);
161
+ t.unref?.();
162
+ this.batchTimers.set(peer, t);
163
+ }
164
+ }
165
+ /** Force-drain every staged batch now (tests + shutdown). */
166
+ async flushStaged() {
167
+ const peers = [...this.staged.keys()];
168
+ for (const peer of peers) {
169
+ const t = this.batchTimers.get(peer);
170
+ if (t) {
171
+ clearTimeout(t);
172
+ this.batchTimers.delete(peer);
173
+ }
174
+ await this.flushStagedPeer(peer);
175
+ }
176
+ }
177
+ async flushStagedPeer(peer) {
178
+ const set = this.staged.get(peer);
179
+ this.staged.delete(peer);
180
+ if (!set || set.size === 0)
181
+ return;
182
+ const topics = [...set];
183
+ // File the durable record FIRST (a crash between filing and the attempt
184
+ // re-fires on the next tick), superseding any older pending pull for
185
+ // these topics (§5.3 — two pulls must never race for one topic).
186
+ const episodeId = `${peer}:${this.now().getTime()}:${++this.episodeSeq}`;
187
+ this.amendLedger((pulls) => {
188
+ for (const rec of pulls) {
189
+ const before = rec.topics.length;
190
+ rec.topics = rec.topics.filter((t) => !set.has(t));
191
+ if (rec.topics.length !== before) {
192
+ this.d.audit?.({
193
+ kind: 'pull-superseded-by-newer-pull',
194
+ oldEpisode: rec.episodeId,
195
+ newEpisode: episodeId,
196
+ removed: before - rec.topics.length,
197
+ });
198
+ }
199
+ }
200
+ const next = pulls.filter((r) => r.topics.length > 0);
201
+ next.push({
202
+ episodeId,
203
+ peer,
204
+ topics,
205
+ attempts: 0,
206
+ nextRetryAt: 0,
207
+ parkedForProtocol: false,
208
+ createdAt: this.now().toISOString(),
209
+ });
210
+ return next;
211
+ });
212
+ this.d.audit?.({ kind: 'pull-filed', episodeId, peer, topicCount: topics.length });
213
+ await this.attemptPeer(peer);
214
+ }
215
+ // ── the §5.3 cancel marker (local write supersedes the pending REPLACE) ───
216
+ /**
217
+ * A LOCAL profile write for `topicKey` has DURABLY landed (the caller
218
+ * invokes this only after `store.mutate(...)` resolved — `mutate` resolves
219
+ * only after its flush is durable, so a flush-refused write throws first
220
+ * and cancels nothing). Operator and token-trust HTTP writes durably amend
221
+ * every pending-pull record to drop the topic — the pending REPLACE is
222
+ * CANCELLED, event-ordered and clock-free. System-attributed writes never
223
+ * cancel (§5.3 round-6 — the breaker can't shed a transferred pin).
224
+ */
225
+ onLocalWriteDurable(topicKey, origin) {
226
+ if (origin === 'system')
227
+ return;
228
+ const key = String(topicKey);
229
+ // Staged (not yet filed) acquires for this topic are dropped too.
230
+ for (const [peer, set] of this.staged) {
231
+ if (set.delete(key) && set.size === 0)
232
+ this.staged.delete(peer);
233
+ }
234
+ const cancelledFrom = [];
235
+ this.amendLedger((pulls) => {
236
+ for (const rec of pulls) {
237
+ if (rec.topics.includes(key)) {
238
+ rec.topics = rec.topics.filter((t) => t !== key);
239
+ cancelledFrom.push(rec.episodeId);
240
+ }
241
+ }
242
+ return pulls.filter((r) => r.topics.length > 0);
243
+ });
244
+ for (const episodeId of cancelledFrom) {
245
+ this.d.audit?.({ kind: 'pull-superseded-by-local-write', topic: key, origin, episodeId });
246
+ }
247
+ }
248
+ // ── retry drain (tick / peer-online) ──────────────────────────────────────
249
+ /** Is a pull pending for this topic? (Resolution's staleness signal, §5.3.) */
250
+ hasPendingPull(topicKey) {
251
+ const key = String(topicKey);
252
+ if ([...this.staged.values()].some((s) => s.has(key)))
253
+ return true;
254
+ return this.loadLedger().some((r) => r.topics.includes(key));
255
+ }
256
+ /** Observability: the durable pending records (copies). */
257
+ pending() {
258
+ return this.loadLedger().map((r) => ({ ...r, topics: [...r.topics] }));
259
+ }
260
+ /**
261
+ * Slow tick (the server calls this every ~60s): TTL-sweep, then attempt
262
+ * every due record (backoff respected; parked records re-checked against
263
+ * the protocol handshake).
264
+ */
265
+ async tick() {
266
+ const nowMs = this.now().getTime();
267
+ const ttl = this.d.pendingTtlMs ?? DEFAULT_PENDING_TTL_MS;
268
+ const expired = [];
269
+ this.amendLedger((pulls) => pulls.filter((r) => {
270
+ const created = Date.parse(r.createdAt);
271
+ if (Number.isFinite(created) && nowMs - created > ttl) {
272
+ expired.push(r);
273
+ return false;
274
+ }
275
+ return true;
276
+ }));
277
+ for (const r of expired) {
278
+ this.d.audit?.({ kind: 'pull-expired', episodeId: r.episodeId, peer: r.peer, topicCount: r.topics.length });
279
+ }
280
+ const duePeers = new Set(this.loadLedger()
281
+ .filter((r) => (r.parkedForProtocol ? this.d.peerSupportsPull?.(r.peer) === true : r.nextRetryAt <= nowMs))
282
+ .map((r) => r.peer));
283
+ for (const peer of duePeers)
284
+ await this.attemptPeer(peer);
285
+ }
286
+ /** A peer came back online — drain its pending pulls now (backoff bypassed). */
287
+ async onPeerOnline(machineId) {
288
+ const has = this.loadLedger().some((r) => r.peer === machineId);
289
+ if (!has)
290
+ return;
291
+ this.amendLedger((pulls) => {
292
+ for (const r of pulls) {
293
+ if (r.peer === machineId && !r.parkedForProtocol)
294
+ r.nextRetryAt = 0;
295
+ }
296
+ return pulls;
297
+ });
298
+ await this.attemptPeer(machineId);
299
+ }
300
+ // ── attempt + apply ────────────────────────────────────────────────────────
301
+ attemptPeer(peer) {
302
+ const existing = this.inFlight.get(peer);
303
+ if (existing)
304
+ return existing; // single-flight per peer
305
+ const run = this.attemptPeerInner(peer).finally(() => {
306
+ this.inFlight.delete(peer);
307
+ });
308
+ this.inFlight.set(peer, run);
309
+ return run;
310
+ }
311
+ async attemptPeerInner(peer) {
312
+ const nowMs = this.now().getTime();
313
+ const records = this.loadLedger().filter((r) => r.peer === peer);
314
+ for (const rec of records) {
315
+ if (rec.parkedForProtocol) {
316
+ if (this.d.peerSupportsPull?.(peer) !== true)
317
+ continue; // still parked
318
+ this.amendLedger((pulls) => {
319
+ const live = pulls.find((p) => p.episodeId === rec.episodeId);
320
+ if (live)
321
+ live.parkedForProtocol = false;
322
+ return pulls;
323
+ });
324
+ }
325
+ else if (rec.nextRetryAt > nowMs) {
326
+ continue; // not due
327
+ }
328
+ // Pre-attempt handshake check: a known-unsupported peer parks without
329
+ // burning a transport round-trip (§5.3 rolling-update skew).
330
+ if (this.d.peerSupportsPull?.(peer) === false) {
331
+ this.park(rec.episodeId, peer);
332
+ continue;
333
+ }
334
+ let outcome;
335
+ try {
336
+ // Re-read the live record — a local write may have amended it while
337
+ // we were iterating.
338
+ const live = this.loadLedger().find((p) => p.episodeId === rec.episodeId);
339
+ if (!live || live.topics.length === 0)
340
+ continue;
341
+ outcome = await this.d.sendPull(peer, [...live.topics]);
342
+ }
343
+ catch (e) {
344
+ outcome = { kind: 'unreachable', detail: e instanceof Error ? e.message : String(e) };
345
+ }
346
+ if (outcome.kind === 'protocol-unsupported') {
347
+ this.park(rec.episodeId, peer);
348
+ continue;
349
+ }
350
+ if (outcome.kind === 'unreachable') {
351
+ const startMs = this.d.retryBackoffStartMs ?? DEFAULT_RETRY_BACKOFF_START_MS;
352
+ const maxMs = this.d.retryBackoffMaxMs ?? DEFAULT_RETRY_BACKOFF_MAX_MS;
353
+ let attempts = 0;
354
+ this.amendLedger((pulls) => {
355
+ const live = pulls.find((p) => p.episodeId === rec.episodeId);
356
+ if (live) {
357
+ live.attempts += 1;
358
+ attempts = live.attempts;
359
+ live.nextRetryAt = this.now().getTime() + Math.min(maxMs, startMs * 2 ** (live.attempts - 1));
360
+ }
361
+ return pulls;
362
+ });
363
+ this.d.audit?.({ kind: 'pull-unreachable', episodeId: rec.episodeId, peer, attempts, detail: outcome.detail });
364
+ continue;
365
+ }
366
+ await this.applyLanding(rec.episodeId, peer, outcome.entries);
367
+ }
368
+ }
369
+ park(episodeId, peer) {
370
+ this.amendLedger((pulls) => {
371
+ const live = pulls.find((p) => p.episodeId === episodeId);
372
+ if (live)
373
+ live.parkedForProtocol = true;
374
+ return pulls;
375
+ });
376
+ this.d.audit?.({ kind: 'pull-parked-protocol-unsupported', episodeId, peer });
377
+ }
378
+ /**
379
+ * A pull landed. Per topic: the durable record is re-read (a local write
380
+ * that amended it mid-flight cancels that topic's REPLACE), ownership is
381
+ * re-checked (actuation authority), the updatedAt backstop runs, then the
382
+ * §10.2-revalidated wholesale REPLACE applies. Disclosures aggregate into
383
+ * ONE summary per landing.
384
+ */
385
+ async applyLanding(episodeId, peer, entries) {
386
+ const live = this.loadLedger().find((p) => p.episodeId === episodeId);
387
+ const liveTopics = new Set(live?.topics ?? []);
388
+ const receiptIso = this.now().toISOString();
389
+ let applied = 0;
390
+ const disclosures = [];
391
+ for (const entry of entries) {
392
+ const key = entry.topicKey;
393
+ if (!liveTopics.has(key)) {
394
+ // Cancelled by a local write (the durable amendment) or superseded by
395
+ // a newer pull while this one was in flight — the REPLACE is dropped.
396
+ this.d.audit?.({ kind: 'pull-landing-cancelled', topic: key, episodeId, peer });
397
+ continue;
398
+ }
399
+ // Ownership recheck — we may no longer own the topic (re-transferred
400
+ // while the pull was pending); the current owner's own pull covers truth.
401
+ let owner = null;
402
+ try {
403
+ owner = this.d.ownerOf(key).owner;
404
+ }
405
+ catch {
406
+ // @silent-fallback-ok: the ownership RE-check is a best-effort guard
407
+ // against landing onto a re-transferred topic; a reader error means
408
+ // owner-UNKNOWN, which must not be treated as not-owner (that would
409
+ // drop a valid pull) — every landing outcome below is audited.
410
+ owner = null;
411
+ }
412
+ if (owner !== null && owner !== this.d.selfMachineId) {
413
+ this.d.audit?.({ kind: 'pull-skipped-not-owner', topic: key, episodeId, owner });
414
+ liveTopics.delete(key); // resolved — deliberately skipped, never retried
415
+ continue;
416
+ }
417
+ if (!entry.present) {
418
+ // Absent on the previous owner clears NOTHING — the local entry stays
419
+ // (a failover-then-transfer-back must not wipe a still-valid pin).
420
+ this.d.audit?.({ kind: 'pull-absent-on-previous-owner', topic: key, episodeId, peer });
421
+ liveTopics.delete(key); // resolved
422
+ continue;
423
+ }
424
+ // updatedAt backstop (clock-free cancel is primary; this catches pulls
425
+ // landing with no recorded local write event): local newer wins, ties
426
+ // favor local — and a pulled updatedAt clamped above the pull-receipt
427
+ // time (future-dated: forged or skew-ahead) is treated as OLDER than
428
+ // any local entry, audited (§5.3 round-6 — a peer-asserted timestamp
429
+ // must never outrank a genuinely fresher local pin). System-attributed
430
+ // local writes never supersede via the backstop.
431
+ const localCurrent = this.d.store.resolve(key);
432
+ const arrivingUpdatedAt = typeof entry.current?.updatedAt === 'string' ? entry.current.updatedAt : null;
433
+ if (localCurrent && arrivingUpdatedAt && !localCurrent.updatedBy.startsWith('system:')) {
434
+ const futureDated = arrivingUpdatedAt > receiptIso;
435
+ if (futureDated) {
436
+ this.d.audit?.({ kind: 'pull-updatedat-clamped', topic: key, episodeId, peer });
437
+ }
438
+ if (futureDated || localCurrent.updatedAt >= arrivingUpdatedAt) {
439
+ this.d.audit?.({
440
+ kind: 'pull-superseded-by-local-write',
441
+ topic: key,
442
+ origin: 'updatedAt-backstop',
443
+ episodeId,
444
+ });
445
+ liveTopics.delete(key); // resolved — local wins
446
+ continue;
447
+ }
448
+ }
449
+ // §10.2 MANDATORY receiving-machine revalidation, then wholesale REPLACE.
450
+ try {
451
+ const result = await this.d.store.replaceEntry(key, { current: entry.current, intendedProfile: entry.intendedProfile }, { revalidate: { fallbackFramework: this.d.effectiveFramework() } });
452
+ applied += 1;
453
+ this.d.audit?.({
454
+ kind: 'pull-applied',
455
+ topic: key,
456
+ episodeId,
457
+ origin: `transfer:${peer}`,
458
+ delta: result.delta,
459
+ droppedFieldCount: result.droppedFields.length,
460
+ });
461
+ for (const dropped of result.droppedFields) {
462
+ disclosures.push(`topic ${key}: the transferred ${dropped.field} didn't validate on this machine (${dropped.reason}) — using the default`);
463
+ }
464
+ }
465
+ catch (e) {
466
+ // FlushRefusedError etc. — the topic STAYS in the record (it remains
467
+ // in liveTopics); the next tick retries the pull (REPLACE is
468
+ // idempotent). Back the episode off so a wedged disk doesn't spin.
469
+ this.d.logger?.(`topic-profile REPLACE failed for ${key}: ${e instanceof Error ? e.message : String(e)}`);
470
+ this.amendLedger((pulls) => {
471
+ const liveRec = pulls.find((p) => p.episodeId === episodeId);
472
+ if (liveRec) {
473
+ const startMs = this.d.retryBackoffStartMs ?? DEFAULT_RETRY_BACKOFF_START_MS;
474
+ liveRec.nextRetryAt = this.now().getTime() + startMs;
475
+ }
476
+ return pulls;
477
+ });
478
+ continue;
479
+ }
480
+ liveTopics.delete(key); // resolved — applied
481
+ }
482
+ // Resolve the episode: topics remaining in liveTopics are ONLY the
483
+ // flush-failed ones (everything applied/skipped was deleted above) —
484
+ // they stay in the record for retry; the rest leave it.
485
+ this.amendLedger((pulls) => {
486
+ const liveRec = pulls.find((p) => p.episodeId === episodeId);
487
+ if (liveRec)
488
+ liveRec.topics = liveRec.topics.filter((t) => liveTopics.has(t));
489
+ return pulls.filter((r) => r.topics.length > 0);
490
+ });
491
+ if (applied > 0 || disclosures.length > 0) {
492
+ const lines = [`${applied} topic profile${applied === 1 ? '' : 's'} arrived from ${peer}.`, ...disclosures];
493
+ this.d.notify?.(lines.join('\n'));
494
+ }
495
+ }
496
+ // ── durable ledger (atomic tmp+rename, restart-proof) ─────────────────────
497
+ loadLedger() {
498
+ if (this.pulls)
499
+ return this.pulls;
500
+ try {
501
+ const parsed = JSON.parse(fs.readFileSync(this.ledgerFile, 'utf-8'));
502
+ this.pulls = Array.isArray(parsed?.pulls)
503
+ ? parsed.pulls.filter((r) => !!r &&
504
+ typeof r === 'object' &&
505
+ typeof r.episodeId === 'string' &&
506
+ typeof r.peer === 'string' &&
507
+ Array.isArray(r.topics) &&
508
+ r.topics.every((t) => typeof t === 'string'))
509
+ : [];
510
+ }
511
+ catch {
512
+ /* @silent-fallback-ok: an absent/corrupt pending-pull ledger only loses queued retries for moves that already disclosed "pins reconcile when reachable" — the reflex (next acquire or onPeerOnline) re-files; never a boot failure (TOPIC-PROFILE-SPEC §5.3) */
513
+ this.pulls = [];
514
+ }
515
+ return this.pulls;
516
+ }
517
+ /** Apply a mutation to the ledger and persist it durably (tmp+rename). */
518
+ amendLedger(fn) {
519
+ const next = fn(this.loadLedger());
520
+ this.pulls = next;
521
+ try {
522
+ fs.mkdirSync(path.dirname(this.ledgerFile), { recursive: true });
523
+ const tmp = `${this.ledgerFile}.${process.pid}.tmp`;
524
+ fs.writeFileSync(tmp, JSON.stringify({ version: 1, pulls: next }, null, 2));
525
+ fs.renameSync(tmp, this.ledgerFile);
526
+ }
527
+ catch (e) {
528
+ /* @silent-fallback-ok: a failed ledger persist keeps the in-memory amendment authoritative for this process; the durable copy self-heals at the next successful amend — logged, never thrown into the acquire/write path (TOPIC-PROFILE-SPEC §5.3) */
529
+ this.d.logger?.(`pending-pull ledger persist failed: ${e instanceof Error ? e.message : String(e)}`);
530
+ }
531
+ }
532
+ }
533
+ //# sourceMappingURL=TopicProfileTransferCarrier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TopicProfileTransferCarrier.js","sourceRoot":"","sources":["../../src/core/TopicProfileTransferCarrier.ts"],"names":[],"mappings":"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"}
@@ -9,10 +9,47 @@
9
9
  * Storage: {stateDir}/topic-resume-map.json
10
10
  * Entries auto-prune after 24 hours.
11
11
  */
12
+ /** §8 provenance tag — gates the none-loss claim (TOPIC-PROFILE-SPEC §7/§8). */
13
+ export type ResumeProvenance = 'hook' | 'mtime-fallback';
14
+ export interface ResumeEntry {
15
+ uuid: string;
16
+ savedAt: string;
17
+ sessionName: string;
18
+ /**
19
+ * §8 framework tag. Untagged legacy entries are grandfathered as
20
+ * 'claude-code' (provably safe — this map has only ever captured Claude
21
+ * JSONL UUIDs) and tagged lazily on the next write.
22
+ */
23
+ framework?: string;
24
+ /** Untagged legacy entries grandfather as 'hook' (same provable basis). */
25
+ provenance?: ResumeProvenance;
26
+ /**
27
+ * §8 — "remove" means PARK, not delete: parked entries are ignored by
28
+ * resolution (get() returns null) but recoverable by the §10.4 breaker
29
+ * revert / §10.3 undo via unpark(). Holds the parking reason.
30
+ */
31
+ parked?: string | null;
32
+ }
33
+ /**
34
+ * §8 resume-writer gate (TOPIC-PROFILE-SPEC round-3 adversarial): EVERY
35
+ * writer — the beforeSessionKill listener, the 60s heartbeat
36
+ * (refreshResumeMappings), the 8s post-spawn proactive save, and the
37
+ * shutdown/refresh-route saves — funnels through save()/refreshResumeMappings,
38
+ * so a single gate at this chokepoint covers all four structurally. The gate
39
+ * refuses a write for a topic whose resolved profile framework is not
40
+ * claude-code, or whose topic is under an active mid-framework-switch
41
+ * suppression marker. Without it, the heartbeat would re-poison the map
42
+ * within a minute of a framework switch.
43
+ */
44
+ export type ResumeWriteGate = (topicId: number) => {
45
+ allowed: boolean;
46
+ reason?: string;
47
+ };
12
48
  export declare class TopicResumeMap {
13
49
  private filePath;
14
50
  private projectDir;
15
51
  private tmuxPath;
52
+ private writeGate;
16
53
  constructor(stateDir: string, projectDir: string, tmuxPath?: string);
17
54
  /**
18
55
  * Compute the Claude Code project directory name for this project.
@@ -40,15 +77,44 @@ export declare class TopicResumeMap {
40
77
  * the most recent JSONL file regardless of which session it belongs to.
41
78
  */
42
79
  findUuidForSession(tmuxSession: string, claudeSessionId?: string): string | null;
80
+ /**
81
+ * Install the §8 profile write-gate (TOPIC-PROFILE-SPEC). All writers are
82
+ * gated at this single chokepoint — the heartbeat, the kill listener, the
83
+ * post-spawn save and the shutdown saves all land in save()/
84
+ * refreshResumeMappings. No gate installed ⇒ today's behavior (ungated).
85
+ */
86
+ setWriteGate(gate: ResumeWriteGate | null): void;
87
+ private gateAllows;
43
88
  /**
44
89
  * Persist a resume mapping before killing an idle session.
90
+ * Entries are framework-tagged ('claude-code' — this map only ever holds
91
+ * Claude JSONL UUIDs) and provenance-tagged (§8). A refused (gated) write
92
+ * is a silent no-op — the gate's caller owns disclosure.
45
93
  */
46
- save(topicId: number, uuid: string, sessionName: string): void;
94
+ save(topicId: number, uuid: string, sessionName: string, provenance?: ResumeProvenance): void;
47
95
  /**
48
96
  * Look up a resume UUID for a topic. Returns null if not found,
49
97
  * expired, or the JSONL file no longer exists.
50
98
  */
51
99
  get(topicId: number): string | null;
100
+ /**
101
+ * §8 last-line guard: the spawn path REFUSES a resume id whose framework
102
+ * tag mismatches the resolved framework, falling to CONTINUATION with
103
+ * disclosure. Untagged legacy entries grandfather as 'claude-code'.
104
+ */
105
+ getForFramework(topicId: number, resolvedFramework: string): string | null;
106
+ /**
107
+ * §8 provenance read — the none-loss rows require HOOK provenance; an
108
+ * mtime-fallback-only entry classifies as CONTINUATION-class loss.
109
+ * Untagged legacy entries grandfather as 'hook' (provably safe).
110
+ */
111
+ getProvenance(topicId: number): ResumeProvenance | null;
112
+ /** Raw entry (parked included) for the §8 park/un-park machinery. */
113
+ getEntryRaw(topicId: number): ResumeEntry | null;
114
+ /** §8 — "remove" means PARK, not delete (deletion destroys the cheap recovery). */
115
+ park(topicId: number, reason: string): void;
116
+ /** §10.4 revert / §10.3 undo — un-park the entry. Returns true when un-parked. */
117
+ unpark(topicId: number): boolean;
52
118
  /**
53
119
  * Remove an entry after successful resume (prevents stale reuse).
54
120
  */