instar 1.3.155 → 1.3.157

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,51 @@
1
+ # Side-effects — Restart-when-idle (restart-window idle bypass)
2
+
3
+ ## 1. What files/state does this touch at runtime?
4
+ `AutoUpdater.gatedRestart()` and `UpdateGate`. No new files, no config keys, no schema, no
5
+ persisted state added. The deferral record written on an *active* deferral now carries the actual
6
+ blocking session names (previously `[]`) — same record, richer field.
7
+
8
+ ## 2. Does it change any functional behavior?
9
+ - **Agents with NO restart window configured:** none. `isInRestartWindow()` returns true when no
10
+ window is set, so the window gate is never entered.
11
+ - **Agents WITH a restart window, while ACTIVE (a healthy session running):** none — still defers
12
+ to the window exactly as before.
13
+ - **Agents WITH a restart window, while IDLE (no active sessions):** an update that lands outside
14
+ the window now restarts immediately instead of waiting hours for the window. The restart is the
15
+ same idle silent-restart the in-window path already performs.
16
+
17
+ ## 3. What happens on failure / weird config?
18
+ The probe is best-effort and read-only. If no `sessionManager` is wired, the window gate treats it
19
+ as idle and falls through to the existing "no session manager → silent restart" path one block
20
+ down (consistent semantics). `getBlockingSessions` returns `[]` for zero sessions. No throw path
21
+ added.
22
+
23
+ ## 4. Migration parity — do existing agents get it?
24
+ Yes, via the normal release — it is a **code-only** change compiled into `dist`. No
25
+ `PostUpdateMigrator` pass is needed because no agent-installed file (hook/skill/config/CLAUDE.md
26
+ template) changes. Existing agents pick it up when they update to this version.
27
+
28
+ ## 5. Could it spam / flood / burn resources?
29
+ No. It adds one in-memory session classification (a list walk already done by `canRestart`) per
30
+ restart attempt. No new timers, no new I/O, no new network calls. If anything it *reduces* work by
31
+ skipping the deferral-timer scheduling on idle boxes.
32
+
33
+ ## 6. Rollback / off-switch?
34
+ Revert the PR. The behavior is always-on (no flag) — idle restarts are inherently safe (they are
35
+ what idle agents already get inside the window), so the change is strictly a latency improvement.
36
+ A user who wants strict window-only behavior even while idle would need the reverted code; this is
37
+ documented as a deliberate trade-off, not an oversight.
38
+
39
+ ## 7. Concurrency / ordering?
40
+ None new. The idle probe (`getBlockingSessions`) is pure and side-effect-free — crucially it does
41
+ NOT start or continue `UpdateGate`'s deferral clock or set warning flags (a dedicated purity unit
42
+ test guards this). The real restart guard remains the `canRestart()` call at the session gate,
43
+ which runs after the window gate; a session that becomes active between the two checks is caught
44
+ there and deferred.
45
+
46
+ ## Blast radius
47
+ Small + additive. One conditional at the window gate in `AutoUpdater.ts` + one pure method
48
+ (`getBlockingSessions`) plus a behavior-preserving extraction (`classifyRunningSessions`) in
49
+ `UpdateGate.ts`. The existing restart gate (`canRestart`) is byte-for-byte unchanged and its
50
+ regression tests (cascade-dampener) stay green. Only affects agents that have a restart window
51
+ configured AND are idle when an update lands outside it.
@@ -0,0 +1,98 @@
1
+ # Side-Effects Review — Session-pool inbound dispatch short-circuit (bug #8)
2
+
3
+ **Version / slug:** `session-pool-route-shortcircuit`
4
+ **Date:** `2026-05-31`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `not required`
7
+
8
+ ## Summary of the change
9
+
10
+ Inbound Telegram dispatch (`src/commands/server.ts`) now short-circuits local
11
+ dispatch whenever `SessionRouter.route()` placed/forwarded the session onto another
12
+ machine — via a new pure helper `isRemotelyHandled(outcome, selfMachineId)` exported
13
+ from `SessionRouter.ts`. Previously only `'forwarded'`/`'duplicate'` short-circuited,
14
+ so a remote `'spawned'`/`'owner-dead-replaced'` fell through and double-dispatched
15
+ (spawn on target + inject into the stale local session). Adds a module-level
16
+ `_meshSelfId` and a one-line routing-decision log.
17
+
18
+ ## Decision-point inventory
19
+
20
+ - **isRemotelyHandled** — the sole decision: forwarded|duplicate → remote;
21
+ spawned|owner-dead-replaced → remote only when owner!==self; everything else →
22
+ local. Both sides + the null-self boundary unit-tested.
23
+ - **call site** — `if (isRemotelyHandled(outcome, _meshSelfId)) return;` else fall
24
+ through to existing local dispatch (unchanged).
25
+
26
+ ## 1. Over-block
27
+
28
+ **What legitimate inputs does this reject?** None are dropped. A remote outcome now
29
+ returns early (correct — the message was already handled on the owner machine). A
30
+ self/local/queued/blocked outcome still falls through to local dispatch exactly as
31
+ before. The change only ADDS two remote actions to the early-return set.
32
+
33
+ ## 2. Under-block
34
+
35
+ **What does this still miss?** It does not address bug #7 (a moved session on a
36
+ tokenless standby cannot send Telegram replies). It does not change WHICH machine
37
+ `route()`/`PlacementExecutor` chooses — if the pin isn't honored or ownership isn't
38
+ released, `route()` returns a LOCAL outcome and this still (correctly) handles
39
+ locally; the new route-decision log is what will pinpoint that on the next live run.
40
+
41
+ ## 3. Level-of-abstraction fit
42
+
43
+ **Right layer?** Yes. The decision is a pure function beside the `RouteOutcome` type
44
+ it interprets (testable, reusable). The call site is the single inbound chokepoint
45
+ that already consulted the router. `_meshSelfId` mirrors the existing
46
+ `_sessionRouter`/`_sessionPoolStage` module-capture pattern.
47
+
48
+ ## 4. Signal vs authority compliance
49
+
50
+ **Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
51
+
52
+ No new blocking authority. The helper only decides local-vs-already-handled-remotely
53
+ for dispatch; it gates nothing user-facing and blocks no message (a not-provably-
54
+ remote outcome falls through to local — fail-safe toward handling, never dropping).
55
+
56
+ ## 5. Interactions
57
+
58
+ Consumes `SessionRouter.route()`'s existing `RouteOutcome`; no router behavior
59
+ change. Pairs with the `_tryNicknameRelocation` recognizer (which sets the pin +
60
+ releases ownership) — this fix makes the SUBSEQUENT routed message's remote
61
+ placement actually short-circuit the stale local session. Gated past `'dark'`, so
62
+ inert on single-machine / pool-disabled agents. Idempotent (pure decision per
63
+ message).
64
+
65
+ ## 6. External surfaces
66
+
67
+ No HTTP routes, config, or notifications. One new server-log line per routed inbound
68
+ (`[session-pool] route topic N → action=… owner=… self=…`) — log-only observability.
69
+
70
+ ## 7. Rollback cost
71
+
72
+ Low. Revert restores the `'forwarded'||'duplicate'`-only check (re-introducing the
73
+ double-dispatch on remote placement) and drops the helper + log. No schema, no
74
+ persisted state, no migration.
75
+
76
+ ## Conclusion
77
+
78
+ Minimal, additive interpretation fix with a pure unit-tested decision, no new
79
+ authority, log-only external surface, cheap revert. Closes the double-dispatch half
80
+ of bug #8 and adds the observability to pinpoint the remainder of the live-transfer
81
+ cascade.
82
+
83
+ ## Second-pass review (if required)
84
+
85
+ Not required — pure decision helper, both branches + fail-safe tested, no destructive
86
+ op, reversible, pool-gated. Live two-machine re-test is the Tier-3 gate that follows.
87
+
88
+ ## Evidence pointers
89
+
90
+ - `tests/unit/SessionRouter.test.ts` — `isRemotelyHandled` over all RouteActions +
91
+ self/remote/null-self boundaries.
92
+ - `tests/unit/session-pool-activation-wiring.test.ts` — call site uses the helper +
93
+ still fails safe to local dispatch.
94
+ - 50 session-pool tests green; `tsc --noEmit` clean.
95
+ - Found live 2026-05-31 on throwaway topic 8882: recognizer pinned to mini, but the
96
+ follow-up injected into the local laptop session (`Injecting into
97
+ echo-standby-mode-edits`) — no forward.
98
+ - Spec: `docs/specs/session-pool-route-shortcircuit.md` (+ `.eli16.md`).