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.
Files changed (36) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +35 -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 +17 -0
  6. package/dist/config/ConfigDefaults.js.map +1 -1
  7. package/dist/core/PostUpdateMigrator.d.ts +10 -0
  8. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  9. package/dist/core/PostUpdateMigrator.js +60 -0
  10. package/dist/core/PostUpdateMigrator.js.map +1 -1
  11. package/dist/core/devGatedFeatures.d.ts.map +1 -1
  12. package/dist/core/devGatedFeatures.js +6 -0
  13. package/dist/core/devGatedFeatures.js.map +1 -1
  14. package/dist/core/types.d.ts +24 -0
  15. package/dist/core/types.d.ts.map +1 -1
  16. package/dist/core/types.js.map +1 -1
  17. package/dist/scaffold/templates.d.ts.map +1 -1
  18. package/dist/scaffold/templates.js +4 -0
  19. package/dist/scaffold/templates.js.map +1 -1
  20. package/dist/server/AgentServer.d.ts +3 -0
  21. package/dist/server/AgentServer.d.ts.map +1 -1
  22. package/dist/server/AgentServer.js +3 -0
  23. package/dist/server/AgentServer.js.map +1 -1
  24. package/dist/server/PoolPollCache.d.ts +136 -0
  25. package/dist/server/PoolPollCache.d.ts.map +1 -0
  26. package/dist/server/PoolPollCache.js +163 -0
  27. package/dist/server/PoolPollCache.js.map +1 -0
  28. package/dist/server/routes.d.ts +9 -0
  29. package/dist/server/routes.d.ts.map +1 -1
  30. package/dist/server/routes.js +51 -8
  31. package/dist/server/routes.js.map +1 -1
  32. package/package.json +1 -1
  33. package/src/data/builtin-manifest.json +64 -64
  34. package/src/scaffold/templates.ts +4 -0
  35. package/upgrades/1.3.553.md +63 -0
  36. 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).