instar 1.3.514 → 1.3.516

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 (54) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +172 -2
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/config/ConfigDefaults.d.ts.map +1 -1
  5. package/dist/config/ConfigDefaults.js +11 -0
  6. package/dist/config/ConfigDefaults.js.map +1 -1
  7. package/dist/core/HybridLogicalClock.d.ts +200 -0
  8. package/dist/core/HybridLogicalClock.d.ts.map +1 -0
  9. package/dist/core/HybridLogicalClock.js +288 -0
  10. package/dist/core/HybridLogicalClock.js.map +1 -0
  11. package/dist/core/MeshRpc.d.ts +6 -0
  12. package/dist/core/MeshRpc.d.ts.map +1 -1
  13. package/dist/core/MeshRpc.js +8 -0
  14. package/dist/core/MeshRpc.js.map +1 -1
  15. package/dist/core/PoolLinkAssertion.d.ts +164 -0
  16. package/dist/core/PoolLinkAssertion.d.ts.map +1 -0
  17. package/dist/core/PoolLinkAssertion.js +184 -0
  18. package/dist/core/PoolLinkAssertion.js.map +1 -0
  19. package/dist/core/PoolLinkJtiStore.d.ts +48 -0
  20. package/dist/core/PoolLinkJtiStore.d.ts.map +1 -0
  21. package/dist/core/PoolLinkJtiStore.js +149 -0
  22. package/dist/core/PoolLinkJtiStore.js.map +1 -0
  23. package/dist/core/PoolViewProxy.d.ts +168 -0
  24. package/dist/core/PoolViewProxy.d.ts.map +1 -0
  25. package/dist/core/PoolViewProxy.js +208 -0
  26. package/dist/core/PoolViewProxy.js.map +1 -0
  27. package/dist/core/PostUpdateMigrator.d.ts +19 -0
  28. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  29. package/dist/core/PostUpdateMigrator.js +84 -0
  30. package/dist/core/PostUpdateMigrator.js.map +1 -1
  31. package/dist/core/devGatedFeatures.d.ts.map +1 -1
  32. package/dist/core/devGatedFeatures.js +6 -0
  33. package/dist/core/devGatedFeatures.js.map +1 -1
  34. package/dist/core/types.d.ts +45 -0
  35. package/dist/core/types.d.ts.map +1 -1
  36. package/dist/core/types.js.map +1 -1
  37. package/dist/scaffold/templates.d.ts.map +1 -1
  38. package/dist/scaffold/templates.js +6 -0
  39. package/dist/scaffold/templates.js.map +1 -1
  40. package/dist/server/AgentServer.d.ts +3 -0
  41. package/dist/server/AgentServer.d.ts.map +1 -1
  42. package/dist/server/AgentServer.js +3 -0
  43. package/dist/server/AgentServer.js.map +1 -1
  44. package/dist/server/routes.d.ts +38 -0
  45. package/dist/server/routes.d.ts.map +1 -1
  46. package/dist/server/routes.js +109 -1
  47. package/dist/server/routes.js.map +1 -1
  48. package/package.json +1 -1
  49. package/src/data/builtin-manifest.json +64 -64
  50. package/src/scaffold/templates.ts +6 -0
  51. package/upgrades/1.3.515.md +64 -0
  52. package/upgrades/1.3.516.md +55 -0
  53. package/upgrades/side-effects/multi-machine-seamlessness-ws44-pool-links.md +146 -0
  54. package/upgrades/side-effects/replicated-store-foundation-hlc-primitive.md +98 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PoolLinkJtiStore.js","sourceRoot":"","sources":["../../src/core/PoolLinkJtiStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,168 @@
1
+ /**
2
+ * PoolViewProxy — the fronting-machine side of WS4.4 "links that survive machine
3
+ * boundaries" (MULTI-MACHINE-SEAMLESSNESS-SPEC §WS4.4 a/c/d). SECURITY-SENSITIVE
4
+ * adjacent (it carries the assertion from PoolLinkAssertion; it never substitutes
5
+ * machine creds for user creds, never logs tokens, never caches private bodies).
6
+ *
7
+ * Responsibilities (pure logic; transport injected):
8
+ * (a) HOLDER RESOLUTION — view-id ownership ≠ topic ownership. There is NO
9
+ * replicated view-id→machine index in instar today (PrivateViewer stores
10
+ * each view on the local disk of the machine that created it). So the
11
+ * holder is resolved by a FAN-OUT PROBE: ask each online peer "do you hold
12
+ * view X?"; the holder answers present, all others answer absent. The
13
+ * resolution is memoized briefly so repeated requests for the same view
14
+ * within a window don't re-fan-out (DOCUMENTED LIMIT: a view moved between
15
+ * machines within the memo window resolves to the stale holder until the
16
+ * memo expires; the proxied request then 404s and the memo is invalidated).
17
+ * (c) CONCURRENCY CAP — a semaphore bounds in-flight proxied requests so a
18
+ * burst of remote-view fetches can't exhaust the fronting machine. Over the
19
+ * cap → an honest 503 (retry), never an unbounded queue.
20
+ * (d) OFFLINE HOLDER — when the resolved holder is offline/unreachable, the
21
+ * result is an explicit `holder-offline` so the caller serves the honest
22
+ * "content temporarily unavailable — its machine is offline" page, NEVER a
23
+ * bare 404 (which would imply the view doesn't exist) and NEVER stale
24
+ * content (private bodies are never cached at the edge).
25
+ *
26
+ * The HTTP transport (fetch, peer URL list, the machine-authed mesh send) is
27
+ * injected so the resolution + cap + offline state machine is unit-testable with
28
+ * in-memory fakes.
29
+ */
30
+ export interface PoolPeer {
31
+ machineId: string;
32
+ url: string;
33
+ /** Registry liveness; false = known-offline (skip the probe, fail fast). */
34
+ online?: boolean;
35
+ }
36
+ export type HolderResolution = {
37
+ kind: 'local';
38
+ } | {
39
+ kind: 'remote';
40
+ machineId: string;
41
+ url: string;
42
+ } | {
43
+ kind: 'not-found';
44
+ } | {
45
+ kind: 'holder-offline';
46
+ machineId: string;
47
+ } | {
48
+ kind: 'no-peers';
49
+ }
50
+ /**
51
+ * §WS4.4 (f) load-shed: the fronting machine is over its CPU threshold AND has
52
+ * no fresh memoized resolution to serve, so it declines to re-fan-out right now.
53
+ * The caller surfaces an honest "busy, retry shortly" rather than spending a
54
+ * CPU-bound box on a fresh fan-out. NEVER returned when a (possibly stale)
55
+ * cached resolution exists — that path returns the cached resolution flagged
56
+ * `stale: true` instead (load-shed, honestly labeled).
57
+ */
58
+ | {
59
+ kind: 'load-shed';
60
+ };
61
+ /**
62
+ * A resolution plus its load-posture metadata. `stale: true` means the fronting
63
+ * machine was over its CPU threshold and served the LAST-CACHED resolution
64
+ * instead of re-fanning-out (§WS4.4 f). `cachedAtMs` is when that cached
65
+ * resolution was computed (so the caller can label "as of N ago"). On a fresh
66
+ * fan-out (or local/no-peers), `stale` is false and `cachedAtMs` is the
67
+ * just-computed time.
68
+ */
69
+ export interface TaggedHolderResolution {
70
+ resolution: HolderResolution;
71
+ stale: boolean;
72
+ cachedAtMs: number;
73
+ }
74
+ export interface PoolViewProxyDeps {
75
+ /** This fronting machine's id. */
76
+ selfMachineId: string;
77
+ /** Does THIS machine hold the view locally? (PrivateViewer.get != null) */
78
+ heldLocally: (viewId: string) => boolean;
79
+ /** Every registered peer (machineId + url + liveness). */
80
+ listPeers: () => PoolPeer[];
81
+ /**
82
+ * Probe one peer: does it hold `viewId`? Resolves to `'present'` (holder),
83
+ * `'absent'` (not this peer), or `'unreachable'` (probe failed/timed out).
84
+ * Machine-authed under the hood (the production probe is a mesh verb), so a
85
+ * peer's answer is from a registered same-operator machine.
86
+ */
87
+ probePeer: (peer: PoolPeer, viewId: string) => Promise<'present' | 'absent' | 'unreachable'>;
88
+ now: () => number;
89
+ /** Holder-resolution memo TTL (ms). Default 30_000. */
90
+ resolveMemoTtlMs?: number;
91
+ /** Max in-flight proxied requests. Default 16. */
92
+ maxConcurrent?: number;
93
+ /**
94
+ * §WS4.4 (f) load-shed posture: the fronting machine's CURRENT 1-min CPU load
95
+ * normalized per core (loadavg[0] / numCpus). Injected (os.loadavg in prod) so
96
+ * the load-shed branch is unit-testable. Optional — absent ⇒ never load-sheds.
97
+ */
98
+ cpuLoadPerCore?: () => number;
99
+ /**
100
+ * §WS4.4 (f): the load-per-core threshold at/above which holder resolution
101
+ * load-sheds (serves last-cached with a staleness tag instead of re-fanning).
102
+ * Default 1.5 (mirrors SessionReaper cpuCriticalLoadPerCore). 0 disables.
103
+ */
104
+ loadShedLoadPerCore?: number;
105
+ logger?: (line: string) => void;
106
+ }
107
+ export declare class PoolViewProxy {
108
+ private readonly d;
109
+ private readonly memo;
110
+ private inFlight;
111
+ constructor(d: PoolViewProxyDeps);
112
+ private memoTtl;
113
+ private maxConcurrent;
114
+ /** Current in-flight proxied request count (observability/tests). */
115
+ get inFlightCount(): number;
116
+ /** True if a new proxied request would exceed the concurrency cap. */
117
+ atCapacity(): boolean;
118
+ /**
119
+ * Invalidate a memoized resolution (e.g. after a proxied request 404s — the
120
+ * holder moved). Next resolve() re-fans-out.
121
+ */
122
+ invalidate(viewId: string): void;
123
+ /**
124
+ * Resolve which machine HOLDS `viewId`. Local-first (no probe needed), then a
125
+ * fan-out probe of online peers. Memoized briefly. Resolution outcomes:
126
+ * - local → this machine has it (serve normally, no proxy)
127
+ * - remote → a reachable peer holds it (proxy to it)
128
+ * - holder-offline→ exactly one peer is the holder but it's offline/unreachable
129
+ * - not-found → no peer (and not local) holds it
130
+ * - no-peers → there are no peers to probe (single-machine)
131
+ */
132
+ resolveHolder(viewId: string): Promise<HolderResolution>;
133
+ /** §WS4.4 (f): is the fronting machine currently over its CPU threshold? */
134
+ private overCpuThreshold;
135
+ /**
136
+ * §WS4.4 (f) load-shed-aware holder resolution. The route should call THIS
137
+ * (not the raw resolveHolder) so it can honestly label staleness:
138
+ * - Local-held views ALWAYS resolve fresh (no fan-out, no CPU cost) — they
139
+ * are never load-shed.
140
+ * - Under the CPU threshold: behaves exactly like resolveHolder (fresh fan-out,
141
+ * memoized), returned with stale:false.
142
+ * - At/over the threshold WITH a cached resolution (fresh OR expired): serve
143
+ * that cached resolution flagged stale:true + its cachedAtMs (load-shed,
144
+ * honestly labeled — NO re-fan-out).
145
+ * - At/over the threshold WITHOUT any cached resolution: { kind: 'load-shed' },
146
+ * so the caller surfaces an honest "busy, retry shortly" rather than spending
147
+ * a CPU-bound box on a fresh fan-out. NEVER serves a fabricated/stale body.
148
+ */
149
+ resolveHolderTagged(viewId: string): Promise<TaggedHolderResolution>;
150
+ /**
151
+ * Run `fn` (the actual proxied fetch) under the concurrency cap. Returns
152
+ * `{ ok: false, reason: 'at-capacity' }` immediately when over the cap (no
153
+ * queue — the caller surfaces an honest 503). On accept, the in-flight count is
154
+ * incremented for the duration of `fn` and decremented in a finally.
155
+ *
156
+ * `fn` MUST stream the holder's response straight through to the client
157
+ * WITHOUT buffering the private body to disk/cache (spec §WS4.4 b/c) — that is
158
+ * the caller's contract; this method only bounds concurrency.
159
+ */
160
+ withSlot<T>(fn: () => Promise<T>): Promise<{
161
+ ok: true;
162
+ value: T;
163
+ } | {
164
+ ok: false;
165
+ reason: 'at-capacity';
166
+ }>;
167
+ }
168
+ //# sourceMappingURL=PoolViewProxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PoolViewProxy.d.ts","sourceRoot":"","sources":["../../src/core/PoolViewProxy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,4EAA4E;IAC5E,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE;AACtB;;;;;;;GAOG;GACD;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC;AAE1B;;;;;;;GAOG;AACH,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,gBAAgB,CAAC;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,kCAAkC;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IACzC,0DAA0D;IAC1D,SAAS,EAAE,MAAM,QAAQ,EAAE,CAAC;IAC5B;;;;;OAKG;IACH,SAAS,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,GAAG,QAAQ,GAAG,aAAa,CAAC,CAAC;IAC7F,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,uDAAuD;IACvD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kDAAkD;IAClD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,MAAM,CAAC;IAC9B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAWD,qBAAa,aAAa;IAIZ,OAAO,CAAC,QAAQ,CAAC,CAAC;IAH9B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgC;IACrD,OAAO,CAAC,QAAQ,CAAK;gBAEQ,CAAC,EAAE,iBAAiB;IAEjD,OAAO,CAAC,OAAO;IAGf,OAAO,CAAC,aAAa;IAIrB,qEAAqE;IACrE,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED,sEAAsE;IACtE,UAAU,IAAI,OAAO;IAIrB;;;OAGG;IACH,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAIhC;;;;;;;;OAQG;IACG,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA2D9D,4EAA4E;IAC5E,OAAO,CAAC,gBAAgB;IAiBxB;;;;;;;;;;;;;OAaG;IACG,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC;IA4B1E;;;;;;;;;OASG;IACG,QAAQ,CAAC,CAAC,EACd,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,KAAK,EAAE,CAAC,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,aAAa,CAAA;KAAE,CAAC;CAa1E"}
@@ -0,0 +1,208 @@
1
+ /**
2
+ * PoolViewProxy — the fronting-machine side of WS4.4 "links that survive machine
3
+ * boundaries" (MULTI-MACHINE-SEAMLESSNESS-SPEC §WS4.4 a/c/d). SECURITY-SENSITIVE
4
+ * adjacent (it carries the assertion from PoolLinkAssertion; it never substitutes
5
+ * machine creds for user creds, never logs tokens, never caches private bodies).
6
+ *
7
+ * Responsibilities (pure logic; transport injected):
8
+ * (a) HOLDER RESOLUTION — view-id ownership ≠ topic ownership. There is NO
9
+ * replicated view-id→machine index in instar today (PrivateViewer stores
10
+ * each view on the local disk of the machine that created it). So the
11
+ * holder is resolved by a FAN-OUT PROBE: ask each online peer "do you hold
12
+ * view X?"; the holder answers present, all others answer absent. The
13
+ * resolution is memoized briefly so repeated requests for the same view
14
+ * within a window don't re-fan-out (DOCUMENTED LIMIT: a view moved between
15
+ * machines within the memo window resolves to the stale holder until the
16
+ * memo expires; the proxied request then 404s and the memo is invalidated).
17
+ * (c) CONCURRENCY CAP — a semaphore bounds in-flight proxied requests so a
18
+ * burst of remote-view fetches can't exhaust the fronting machine. Over the
19
+ * cap → an honest 503 (retry), never an unbounded queue.
20
+ * (d) OFFLINE HOLDER — when the resolved holder is offline/unreachable, the
21
+ * result is an explicit `holder-offline` so the caller serves the honest
22
+ * "content temporarily unavailable — its machine is offline" page, NEVER a
23
+ * bare 404 (which would imply the view doesn't exist) and NEVER stale
24
+ * content (private bodies are never cached at the edge).
25
+ *
26
+ * The HTTP transport (fetch, peer URL list, the machine-authed mesh send) is
27
+ * injected so the resolution + cap + offline state machine is unit-testable with
28
+ * in-memory fakes.
29
+ */
30
+ const DEFAULT_MEMO_TTL_MS = 30_000;
31
+ const DEFAULT_MAX_CONCURRENT = 16;
32
+ const DEFAULT_LOAD_SHED_LOAD_PER_CORE = 1.5;
33
+ export class PoolViewProxy {
34
+ d;
35
+ memo = new Map();
36
+ inFlight = 0;
37
+ constructor(d) {
38
+ this.d = d;
39
+ }
40
+ memoTtl() {
41
+ return this.d.resolveMemoTtlMs ?? DEFAULT_MEMO_TTL_MS;
42
+ }
43
+ maxConcurrent() {
44
+ return this.d.maxConcurrent ?? DEFAULT_MAX_CONCURRENT;
45
+ }
46
+ /** Current in-flight proxied request count (observability/tests). */
47
+ get inFlightCount() {
48
+ return this.inFlight;
49
+ }
50
+ /** True if a new proxied request would exceed the concurrency cap. */
51
+ atCapacity() {
52
+ return this.inFlight >= this.maxConcurrent();
53
+ }
54
+ /**
55
+ * Invalidate a memoized resolution (e.g. after a proxied request 404s — the
56
+ * holder moved). Next resolve() re-fans-out.
57
+ */
58
+ invalidate(viewId) {
59
+ this.memo.delete(viewId);
60
+ }
61
+ /**
62
+ * Resolve which machine HOLDS `viewId`. Local-first (no probe needed), then a
63
+ * fan-out probe of online peers. Memoized briefly. Resolution outcomes:
64
+ * - local → this machine has it (serve normally, no proxy)
65
+ * - remote → a reachable peer holds it (proxy to it)
66
+ * - holder-offline→ exactly one peer is the holder but it's offline/unreachable
67
+ * - not-found → no peer (and not local) holds it
68
+ * - no-peers → there are no peers to probe (single-machine)
69
+ */
70
+ async resolveHolder(viewId) {
71
+ // Local-first: a locally-held view never needs a probe.
72
+ if (this.d.heldLocally(viewId))
73
+ return { kind: 'local' };
74
+ const cached = this.memo.get(viewId);
75
+ if (cached && this.d.now() - cached.at < this.memoTtl()) {
76
+ return cached.resolution;
77
+ }
78
+ const peers = this.d.listPeers().filter((p) => p.machineId !== this.d.selfMachineId);
79
+ if (peers.length === 0) {
80
+ const r = { kind: 'no-peers' };
81
+ this.memo.set(viewId, { resolution: r, at: this.d.now() });
82
+ return r;
83
+ }
84
+ // Probe online peers; a known-offline peer is skipped at the probe (it
85
+ // cannot answer). We track whether any peer was unreachable so we can
86
+ // distinguish "nobody holds it" (not-found) from "the holder may be the one
87
+ // we couldn't reach" — but only an UNREACHABLE peer that we cannot rule out
88
+ // produces holder-offline, and only when no online peer answered present.
89
+ let holder = null;
90
+ let sawUnreachable = null;
91
+ const results = await Promise.all(peers.map(async (p) => {
92
+ if (p.online === false)
93
+ return { peer: p, ans: 'unreachable' };
94
+ try {
95
+ const ans = await this.d.probePeer(p, viewId);
96
+ return { peer: p, ans };
97
+ }
98
+ catch {
99
+ return { peer: p, ans: 'unreachable' };
100
+ }
101
+ }));
102
+ for (const { peer, ans } of results) {
103
+ if (ans === 'present') {
104
+ holder = peer;
105
+ break;
106
+ }
107
+ if (ans === 'unreachable')
108
+ sawUnreachable = peer;
109
+ }
110
+ let resolution;
111
+ if (holder) {
112
+ // The present holder might itself be marked offline in the registry yet
113
+ // answered — answering proves reachability, so it's `remote`.
114
+ resolution = { kind: 'remote', machineId: holder.machineId, url: holder.url };
115
+ }
116
+ else if (sawUnreachable) {
117
+ // No online peer claims it, but a peer was unreachable — it may be the
118
+ // offline holder. Honest "temporarily unavailable", never a bare 404.
119
+ resolution = { kind: 'holder-offline', machineId: sawUnreachable.machineId };
120
+ }
121
+ else {
122
+ resolution = { kind: 'not-found' };
123
+ }
124
+ this.memo.set(viewId, { resolution, at: this.d.now() });
125
+ return resolution;
126
+ }
127
+ /** §WS4.4 (f): is the fronting machine currently over its CPU threshold? */
128
+ overCpuThreshold() {
129
+ const sampler = this.d.cpuLoadPerCore;
130
+ if (!sampler)
131
+ return false;
132
+ const threshold = this.d.loadShedLoadPerCore ?? DEFAULT_LOAD_SHED_LOAD_PER_CORE;
133
+ if (!(threshold > 0))
134
+ return false; // 0/negative disables load-shed.
135
+ let load;
136
+ try {
137
+ load = sampler();
138
+ }
139
+ catch {
140
+ // @silent-fallback-ok: a CPU-sampler failure never load-sheds — it fails
141
+ // toward serving FRESH (the safe direction); load-shed is an optimization,
142
+ // not a correctness gate, so there is nothing to report as a degradation.
143
+ return false;
144
+ }
145
+ return Number.isFinite(load) && load >= threshold;
146
+ }
147
+ /**
148
+ * §WS4.4 (f) load-shed-aware holder resolution. The route should call THIS
149
+ * (not the raw resolveHolder) so it can honestly label staleness:
150
+ * - Local-held views ALWAYS resolve fresh (no fan-out, no CPU cost) — they
151
+ * are never load-shed.
152
+ * - Under the CPU threshold: behaves exactly like resolveHolder (fresh fan-out,
153
+ * memoized), returned with stale:false.
154
+ * - At/over the threshold WITH a cached resolution (fresh OR expired): serve
155
+ * that cached resolution flagged stale:true + its cachedAtMs (load-shed,
156
+ * honestly labeled — NO re-fan-out).
157
+ * - At/over the threshold WITHOUT any cached resolution: { kind: 'load-shed' },
158
+ * so the caller surfaces an honest "busy, retry shortly" rather than spending
159
+ * a CPU-bound box on a fresh fan-out. NEVER serves a fabricated/stale body.
160
+ */
161
+ async resolveHolderTagged(viewId) {
162
+ // Local-first: a locally-held view is free to resolve — never load-shed.
163
+ if (this.d.heldLocally(viewId)) {
164
+ return { resolution: { kind: 'local' }, stale: false, cachedAtMs: this.d.now() };
165
+ }
166
+ const cached = this.memo.get(viewId);
167
+ const memoFresh = cached && this.d.now() - cached.at < this.memoTtl();
168
+ if (this.overCpuThreshold()) {
169
+ // Load-shed: never re-fan-out. Serve any cached resolution (fresh OR
170
+ // expired) with an explicit staleness tag; if none exists, decline honestly.
171
+ if (cached) {
172
+ this.d.logger?.(`[pool-view-proxy] load-shed: serving cached holder resolution for ${viewId} (age ${this.d.now() - cached.at}ms, stale=${!memoFresh})`);
173
+ return { resolution: cached.resolution, stale: !memoFresh, cachedAtMs: cached.at };
174
+ }
175
+ this.d.logger?.(`[pool-view-proxy] load-shed: no cached resolution for ${viewId} — declining fan-out`);
176
+ return { resolution: { kind: 'load-shed' }, stale: false, cachedAtMs: this.d.now() };
177
+ }
178
+ // Below threshold: normal path (fresh fan-out when the memo is cold/expired).
179
+ const resolution = await this.resolveHolder(viewId);
180
+ const entry = this.memo.get(viewId);
181
+ return { resolution, stale: false, cachedAtMs: entry?.at ?? this.d.now() };
182
+ }
183
+ /**
184
+ * Run `fn` (the actual proxied fetch) under the concurrency cap. Returns
185
+ * `{ ok: false, reason: 'at-capacity' }` immediately when over the cap (no
186
+ * queue — the caller surfaces an honest 503). On accept, the in-flight count is
187
+ * incremented for the duration of `fn` and decremented in a finally.
188
+ *
189
+ * `fn` MUST stream the holder's response straight through to the client
190
+ * WITHOUT buffering the private body to disk/cache (spec §WS4.4 b/c) — that is
191
+ * the caller's contract; this method only bounds concurrency.
192
+ */
193
+ async withSlot(fn) {
194
+ if (this.atCapacity()) {
195
+ this.d.logger?.(`[pool-view-proxy] at capacity (${this.inFlight}/${this.maxConcurrent()}) — shedding`);
196
+ return { ok: false, reason: 'at-capacity' };
197
+ }
198
+ this.inFlight++;
199
+ try {
200
+ const value = await fn();
201
+ return { ok: true, value };
202
+ }
203
+ finally {
204
+ this.inFlight--;
205
+ }
206
+ }
207
+ }
208
+ //# sourceMappingURL=PoolViewProxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PoolViewProxy.js","sourceRoot":"","sources":["../../src/core/PoolViewProxy.ts"],"names":[],"mappings":"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"}
@@ -34,6 +34,25 @@ export interface MigratorConfig {
34
34
  hasTelegram: boolean;
35
35
  projectName: string;
36
36
  }
37
+ /**
38
+ * WS4.4 dev-gate config migration (MULTI-MACHINE-SEAMLESSNESS-SPEC §WS4.4).
39
+ * SECURITY-CRITICAL feature, DEV-GATED dark: the runtime resolves the flag via
40
+ * `resolveDevAgentGate()` (`explicit ?? !!developmentAgent`), so the config must
41
+ * OMIT it — present on a dev agent ⇒ live, absent on the fleet ⇒ dark.
42
+ *
43
+ * This migration enforces that invariant existence-checked + idempotently. It
44
+ * STRIPS a default-shaped literal `multiMachine.seamlessness.ws44PoolLinks=false`
45
+ * (the PR #1001 anti-pattern — an injected `false` force-darks even dev agents)
46
+ * so the dev-gate resolves live, exactly like the cartographer-dev-gate fix.
47
+ *
48
+ * Pure + mutating-in-place so it is unit-testable; returns true iff it changed
49
+ * `config`. Rules:
50
+ * - key absent → no-op (false). The gate already decides correctly.
51
+ * - key === false → STRIP it (true). It was a default-shaped force-dark.
52
+ * - key === true → leave it (false). An operator's explicit fleet-flip wins.
53
+ * A stripped seamlessness block left empty is removed so the file stays clean.
54
+ */
55
+ export declare function migrateConfigWs44PoolLinks(config: Record<string, unknown>): boolean;
37
56
  export declare class PostUpdateMigrator {
38
57
  private config;
39
58
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"PostUpdateMigrator.d.ts","sourceRoot":"","sources":["../../src/core/PostUpdateMigrator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PostUpdateMigrator.d.ts","sourceRoot":"","sources":["../../src/core/PostUpdateMigrator.ts"],"names":[],"mappings":"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"}
@@ -68,6 +68,42 @@ const CONTEXT_WALL_ESCALATION_NOTE = `
68
68
 
69
69
  When a session is genuinely stuck at the context wall ("Context limit reached · /compact or /clear to continue" / "conversation too long"), recovery now tries a NON-DESTRUCTIVE rung FIRST: it presses \`/compact\` for the session and verifies the wall cleared — preserving the whole conversation. Only if \`/compact\` can't clear it (the conversation is too long to even compact) does recovery fall back to the previous behavior, a fresh respawn that keeps thread history but starts a new conversation. This is gated to a genuinely idle session (a session still actively working at 100% context is left alone, never compacted out from under its work). If a user asks "why did my long session restart / did I lose the conversation?" — the answer is: I try to compact it in place first; a fresh start only happens when compaction itself fails.
70
70
  `;
71
+ /**
72
+ * WS4.4 dev-gate config migration (MULTI-MACHINE-SEAMLESSNESS-SPEC §WS4.4).
73
+ * SECURITY-CRITICAL feature, DEV-GATED dark: the runtime resolves the flag via
74
+ * `resolveDevAgentGate()` (`explicit ?? !!developmentAgent`), so the config must
75
+ * OMIT it — present on a dev agent ⇒ live, absent on the fleet ⇒ dark.
76
+ *
77
+ * This migration enforces that invariant existence-checked + idempotently. It
78
+ * STRIPS a default-shaped literal `multiMachine.seamlessness.ws44PoolLinks=false`
79
+ * (the PR #1001 anti-pattern — an injected `false` force-darks even dev agents)
80
+ * so the dev-gate resolves live, exactly like the cartographer-dev-gate fix.
81
+ *
82
+ * Pure + mutating-in-place so it is unit-testable; returns true iff it changed
83
+ * `config`. Rules:
84
+ * - key absent → no-op (false). The gate already decides correctly.
85
+ * - key === false → STRIP it (true). It was a default-shaped force-dark.
86
+ * - key === true → leave it (false). An operator's explicit fleet-flip wins.
87
+ * A stripped seamlessness block left empty is removed so the file stays clean.
88
+ */
89
+ export function migrateConfigWs44PoolLinks(config) {
90
+ const mm = config.multiMachine;
91
+ if (!mm || typeof mm !== 'object')
92
+ return false;
93
+ const seam = mm.seamlessness;
94
+ if (!seam || typeof seam !== 'object')
95
+ return false;
96
+ if (!Object.prototype.hasOwnProperty.call(seam, 'ws44PoolLinks'))
97
+ return false;
98
+ // Only a default-shaped `false` is stripped; an explicit `true` is preserved.
99
+ if (seam.ws44PoolLinks !== false)
100
+ return false;
101
+ delete seam.ws44PoolLinks;
102
+ // Tidy: drop an emptied seamlessness block so the migration leaves no cruft.
103
+ if (Object.keys(seam).length === 0)
104
+ delete mm.seamlessness;
105
+ return true;
106
+ }
71
107
  export class PostUpdateMigrator {
72
108
  config;
73
109
  /**
@@ -3630,6 +3666,27 @@ Where to look (never guess mesh state — read it):
3630
3666
  patched = true;
3631
3667
  result.upgraded.push('CLAUDE.md: added Cross-Machine Seamlessness section');
3632
3668
  }
3669
+ // WS4.4 — links that survive machine boundaries (MULTI-MACHINE-SEAMLESSNESS-SPEC
3670
+ // §WS4.4). Existing multi-machine agents need to know a private-view link keeps
3671
+ // working no matter which machine is fronting, and the security model (the
3672
+ // holder authorizes; the raw PIN never crosses; offline holder = honest
3673
+ // unavailable). Content-sniffed on a distinctive marker for idempotency.
3674
+ if (!content.includes('Links that survive machine boundaries (WS4.4')) {
3675
+ const ws44Section = `
3676
+ ### Links that survive machine boundaries (WS4.4 — pool-stable private-view links)
3677
+
3678
+ A private-view link (\`/view/:id\`) keeps working no matter WHICH of my machines is fronting the tunnel, even when the content lives on a DIFFERENT machine. The fronting machine resolves the actual HOLDER of the view (view-id ownership ≠ topic ownership — by probing peers, since each view lives on the disk of the machine that made it) and proxies to it. Ships DARK behind \`multiMachine.seamlessness.ws44PoolLinks\` (dev-agent gated); a single-machine agent is a no-op (no peers to proxy to).
3679
+
3680
+ Security model (what to tell the user if asked "is a shared link safe across my machines?"):
3681
+ - The END-USER credential is enforced end-to-end and the HOLDER makes the authorization decision — the fronting machine is a DUMB RELAY. It NEVER substitutes a machine/mesh credential for the user's, NEVER logs the token, and NEVER caches private content at the edge (\`Cache-Control: no-store\`).
3682
+ - The user's PIN/token is validated at the fronting edge, then the proxied request carries a SHORT-LIVED, audience-bound (target holder + the exact view id + HTTP method), SINGLE-USE, mesh-signed ASSERTION of that authentication — NOT the raw PIN. Each machine's PIN secret never crosses. A captured assertion cannot be replayed against another resource, another holder, or reused within its window.
3683
+ - An OFFLINE holder yields an honest "content temporarily unavailable — its machine is offline", never stale content or a bare 404.
3684
+ - **Proactive trigger:** user asks "will this link still work from my other machine / phone while the laptop is asleep?" → yes IF the holder machine is online (the content lives there); if that machine is offline the link honestly says so. Spec: \`docs/specs/MULTI-MACHINE-SEAMLESSNESS-SPEC.md\` §WS4.4.
3685
+ `;
3686
+ content += '\n' + ws44Section;
3687
+ patched = true;
3688
+ result.upgraded.push('CLAUDE.md: added WS4.4 links-that-survive-machine-boundaries section');
3689
+ }
3633
3690
  // CMT-519 — Threadline hub topic + "open this"/bind guidance. Existing agents
3634
3691
  // need to know threadline notices route parent-or-hub (never per-event topics)
3635
3692
  // and that "open this" / "tie this to X" in the hub means calling the bind
@@ -5606,6 +5663,16 @@ Create worktrees for collaborator repos with \`instar worktree create <branch>\`
5606
5663
  // the conversational triggers + read surfaces will guess instead of
5607
5664
  // reading GET /topic-profile/:topicId (the B2/B36 failure class).
5608
5665
  '**Topic Profile (per-topic model, thinking, framework pins)**',
5666
+ // WS4.4 pool-stable links (MULTI-MACHINE-SEAMLESSNESS-SPEC §WS4.4):
5667
+ // framework-agnostic — a Codex/Gemini agent fronting a multi-machine pool
5668
+ // must know a /view/:id link proxies to the holder (and the security model:
5669
+ // user PIN never crosses, single-use audience-bound signed assertion) so it
5670
+ // answers "is a shared link safe across my machines?" instead of guessing.
5671
+ // Two tail-truncated line-leading variants cover both deployed forms
5672
+ // (templates' bold block + migrateClaudeMd's H3), per the Per-Feature LLM
5673
+ // Metrics precedent; each CLAUDE.md contains exactly one, so the other no-ops.
5674
+ '**Links that survive machine boundaries (WS4.4',
5675
+ '### Links that survive machine boundaries (WS4.4',
5609
5676
  ];
5610
5677
  for (const shadowName of ['AGENTS.md', 'GEMINI.md']) {
5611
5678
  const shadowPath = path.join(this.config.projectDir, shadowName);
@@ -6326,6 +6393,23 @@ Create worktrees for collaborator repos with \`instar worktree create <branch>\`
6326
6393
  // Fallback: if ConfigDefaults import fails, log error but don't crash migration
6327
6394
  result.errors.push(`config.json defaults: ${err instanceof Error ? err.message : String(err)}`);
6328
6395
  }
6396
+ // WS4.4 — links that survive machine boundaries (MULTI-MACHINE-SEAMLESSNESS-
6397
+ // SPEC §WS4.4). DEV-GATED dark feature: the runtime resolves
6398
+ // `multiMachine.seamlessness.ws44PoolLinks` through resolveDevAgentGate()
6399
+ // (`explicit ?? !!developmentAgent`), so the flag is intentionally OMITTED
6400
+ // from config — a dev agent runs it live, the fleet stays dark. The existence-
6401
+ // check migration here therefore STRIPS a default-shaped literal `false`
6402
+ // (mirroring the cartographer-dev-gate fix) rather than injecting one: an
6403
+ // injected `false` would FORCE-DARK dev agents and defeat dogfooding (the
6404
+ // PR #1001 mechanism). When the key is absent it is a clean no-op. Idempotent;
6405
+ // never touches an operator's explicit `true`.
6406
+ if (migrateConfigWs44PoolLinks(config)) {
6407
+ patched = true;
6408
+ result.upgraded.push('config.json: stripped default-shaped multiMachine.seamlessness.ws44PoolLinks=false so the developmentAgent gate resolves it live');
6409
+ }
6410
+ else {
6411
+ result.skipped.push('config.json: multiMachine.seamlessness.ws44PoolLinks dev-gate already correct (omitted or operator-set)');
6412
+ }
6329
6413
  if (patched) {
6330
6414
  try {
6331
6415
  // Atomic write: backup, then write to tmp, then rename