instar 1.3.552 → 1.3.553
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +35 -2
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +17 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +10 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +60 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +6 -0
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/core/types.d.ts +24 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +4 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +3 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +3 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/PoolPollCache.d.ts +136 -0
- package/dist/server/PoolPollCache.d.ts.map +1 -0
- package/dist/server/PoolPollCache.js +163 -0
- package/dist/server/PoolPollCache.js.map +1 -0
- package/dist/server/routes.d.ts +9 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +51 -8
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +64 -64
- package/src/scaffold/templates.ts +4 -0
- package/upgrades/1.3.553.md +63 -0
- package/upgrades/side-effects/multi-machine-seamlessness-ws44-pool-cache.md +115 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Side-Effects Review — WS4.4(f): global pool-cache unification
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `multi-machine-seamlessness-ws44-pool-cache`
|
|
4
|
+
**Date:** `2026-06-13`
|
|
5
|
+
**Author:** `Instar Agent (echo)`
|
|
6
|
+
**Spec:** `docs/specs/MULTI-MACHINE-SEAMLESSNESS-SPEC.md` §WS4.4 clause (f) (converged + approved; deferred second half of WS4.4, tracked CMT-1416)
|
|
7
|
+
**Second-pass reviewer:** not-required — pure read-side efficiency/observability; no new authority, no credential-bearing code, no destructive op, no third-party spend.
|
|
8
|
+
|
|
9
|
+
## Summary of the change
|
|
10
|
+
|
|
11
|
+
WS4.4 clause (f), the deferred second half of WS4.4: every pool-scope dashboard
|
|
12
|
+
surface (`/sessions?scope=pool`, `/jobs?scope=pool`, `/attention?scope=pool`,
|
|
13
|
+
`/guards?scope=pool`, …) used to fan out to every peer machine INDEPENDENTLY — once
|
|
14
|
+
per surface, per client, per poll interval. With a dashboard polling several tabs at
|
|
15
|
+
once, the same peer is hit N times per interval. This unifies all of them onto ONE
|
|
16
|
+
shared per-peer poll cache: one fan-out per interval feeds every pool-scope surface,
|
|
17
|
+
and under CPU load-shed the cache serves last-cached peer data tagged `stale: true`
|
|
18
|
+
instead of re-fanning (load-shed, honestly labeled).
|
|
19
|
+
|
|
20
|
+
New module (pure + dependency-injected, unit-testable):
|
|
21
|
+
- `src/server/PoolPollCache.ts` — `fetchPeer(peerMachineId, routePath, fetcher)`:
|
|
22
|
+
within-TTL cache hit (one fan-out feeds two surfaces), single-flight coalescing of
|
|
23
|
+
concurrent callers, CPU load-shed stale-serve (never from an empty cache — a first
|
|
24
|
+
read always fetches), a failed fetch is NEVER cached (transient errors don't stick),
|
|
25
|
+
per-(peer, route) keying, and a read-only `snapshot()` of live load + counters.
|
|
26
|
+
|
|
27
|
+
Wiring (all behind the dark flag):
|
|
28
|
+
- `routes.ts` — the `/jobs?scope=pool` per-peer fetch routes through `ctx.poolPollCache`
|
|
29
|
+
when wired; `null` (flag dark / single-machine) ⇒ today's direct per-peer fetch
|
|
30
|
+
byte-for-byte. New read-only `GET /pool/poll-cache` observability route (503 when
|
|
31
|
+
dark — the ships-dark contract). A stale-served peer body surfaces an honest
|
|
32
|
+
`pool.stale: true` tag on the merged response.
|
|
33
|
+
- `commands/server.ts` + `AgentServer.ts` — dev-gated construction (resolveDevAgentGate)
|
|
34
|
+
in the mesh-setup block; `seamlessnessFlags.ws44PoolCache` capability advert.
|
|
35
|
+
- `types.ts` / `ConfigDefaults.ts` / `devGatedFeatures.ts` — flag (`ws44PoolCache`,
|
|
36
|
+
DELIBERATELY OMITTED so the dev-gate decides) + `ws44PoolCacheTtlMs` tunable +
|
|
37
|
+
`MachineCapacity.ws44PoolCache` advert field.
|
|
38
|
+
- `PostUpdateMigrator.ts` + `templates.ts` — Migration Parity (config strip-default +
|
|
39
|
+
CLAUDE.md section + shadow-capability markers) + Agent Awareness.
|
|
40
|
+
|
|
41
|
+
## Decision-point inventory
|
|
42
|
+
|
|
43
|
+
- `/jobs?scope=pool` per-peer fetch through the shared cache — **add** — wired only
|
|
44
|
+
when the dark flag is on; else the existing direct fetch is unchanged.
|
|
45
|
+
- `GET /pool/poll-cache` observability route — **add** — 503 while dark (ships-dark
|
|
46
|
+
contract, like `/pool/queue`); 200 with the snapshot when wired.
|
|
47
|
+
- CPU load-shed stale-serve — **add** — over the load-per-core threshold, serve
|
|
48
|
+
last-cached tagged `stale: true` rather than re-fanning; an empty cache still fetches.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 1. Over-block
|
|
53
|
+
Flag-off (default fleet): zero new behavior — every pool-scope surface keeps its direct
|
|
54
|
+
per-peer fetch byte-for-byte (verified by the "NOT WIRED (cache null)" integration
|
|
55
|
+
test). Flag-on: the 3s default TTL matches the per-route pool caches already in use, so
|
|
56
|
+
no legitimate fresh read is starved. Load-shed only triggers at/above the load-per-core
|
|
57
|
+
threshold (default 1.5, the SessionReaper critical default) AND only when something is
|
|
58
|
+
already cached — a first read is never load-shed.
|
|
59
|
+
|
|
60
|
+
## 2. Under-block
|
|
61
|
+
Not an authority — this gates nothing. It only decides whether to reuse a recently
|
|
62
|
+
fetched peer body or re-fetch. The worst case is serving a body up to one TTL (or, under
|
|
63
|
+
load-shed, the last-cached) older than a fresh fan-out would produce — and load-shed is
|
|
64
|
+
HONESTLY tagged (`stale: true` on the body, `pool.stale: true` on the merge) so a stale
|
|
65
|
+
serve is never silent. A failed fetch is never cached, so a transient peer error cannot
|
|
66
|
+
stick for the whole TTL.
|
|
67
|
+
|
|
68
|
+
## 3. Level-of-abstraction fit
|
|
69
|
+
Right layer. `PoolPollCache` is a pure primitive (injected clock + load reader); the
|
|
70
|
+
route change is a thin branch that calls the fetcher directly when the cache is null.
|
|
71
|
+
It rides the existing pool fan-out pattern (resolvePeerUrls + per-peer timeout + failed
|
|
72
|
+
markers) rather than inventing new transport.
|
|
73
|
+
|
|
74
|
+
## 4. Signal vs authority compliance
|
|
75
|
+
Everything here is a SIGNAL, never an authority: the cache hit / load-shed staleness is
|
|
76
|
+
observability + an efficiency choice; it never gates serving anything, never mutates
|
|
77
|
+
state, and never caches private end-user content (the pool-scope surfaces are
|
|
78
|
+
operator-Bearer reads of mesh METADATA — `/view/:id` private bodies are handled by
|
|
79
|
+
WS4.4 pool-links, which explicitly never caches them).
|
|
80
|
+
|
|
81
|
+
## 5. Interactions
|
|
82
|
+
- Flag-off path leaves every pool-scope surface identical (no shadowing of the direct
|
|
83
|
+
fetch).
|
|
84
|
+
- Coexists with the per-route payload caches (`jobsPoolCache`, etc.) — those dampen two
|
|
85
|
+
rapid calls to the SAME surface; this shared cache dampens the cross-surface,
|
|
86
|
+
cross-client per-peer fan-out. The integration test proves the shared cache is
|
|
87
|
+
genuinely in the path by asserting on ITS OWN counters (`stats.fetches`,
|
|
88
|
+
`cachedKeys`), not the maskable per-route "1 peer hit".
|
|
89
|
+
- Single-machine installs have no peers ⇒ a strict no-op (the cache is never
|
|
90
|
+
constructed, surfaces never call it).
|
|
91
|
+
|
|
92
|
+
## 6. Rollback
|
|
93
|
+
Set/omit `multiMachine.seamlessness.ws44PoolCache` (dev-gated; dark on the fleet by
|
|
94
|
+
default). When off, the cache is never constructed and `/pool/poll-cache` 503s — fully
|
|
95
|
+
reverting to the direct per-peer fetch. No data migration, no persisted state to undo.
|
|
96
|
+
|
|
97
|
+
## Evidence
|
|
98
|
+
|
|
99
|
+
- `tests/unit/pool-poll-cache.test.ts` (9): both sides of every decision boundary —
|
|
100
|
+
within-TTL hit vs expired re-fetch; single-flight coalescing; load-shed over/under
|
|
101
|
+
threshold; load-shed with an EMPTY cache still fetches; a failed fetch is never
|
|
102
|
+
cached; per-(peer, route) keying; snapshot wiring + boundary flip at the threshold.
|
|
103
|
+
- `tests/integration/ws44-pool-cache-route.test.ts` (4): real wiring (real second-server
|
|
104
|
+
peer that COUNTS its `/jobs` hits) — the fan-out routes THROUGH the shared cache
|
|
105
|
+
(asserted via the cache's OWN `stats.fetches` + `cachedKeys`, not the maskable
|
|
106
|
+
per-route "1 hit"); a second poll past the per-route window is served from the shared
|
|
107
|
+
cache WITHOUT re-hitting the peer; the merged body is byte-identical wired vs unwired;
|
|
108
|
+
`GET /pool/poll-cache` 503-when-dark / 200-when-wired.
|
|
109
|
+
- `tests/e2e/pool-poll-cache-alive.test.ts` (3): the feature is ALIVE (200, not 503) on
|
|
110
|
+
the real AgentServer stack; sits behind auth (no Bearer → 401/403); the ships-dark
|
|
111
|
+
default 503s with `{ enabled: false }`.
|
|
112
|
+
- Gate checks green: `tsc --noEmit`; `docs-coverage --check`; `no-silent-fallbacks`
|
|
113
|
+
(the one new fail-closed construction-catch is tagged `@silent-fallback-ok`);
|
|
114
|
+
`feature-delivery-completeness` (new featureSection + both shadow-marker variants);
|
|
115
|
+
`lint-dev-agent-dark-gate` (line-map recomputed for the +17 ConfigDefaults shift).
|