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.
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +17 -2
- package/dist/commands/server.js.map +1 -1
- package/dist/core/AutoUpdater.d.ts.map +1 -1
- package/dist/core/AutoUpdater.js +35 -19
- package/dist/core/AutoUpdater.js.map +1 -1
- package/dist/core/SessionRouter.d.ts +18 -0
- package/dist/core/SessionRouter.d.ts.map +1 -1
- package/dist/core/SessionRouter.js +27 -0
- package/dist/core/SessionRouter.js.map +1 -1
- package/dist/core/UpdateGate.d.ts +20 -0
- package/dist/core/UpdateGate.d.ts.map +1 -1
- package/dist/core/UpdateGate.js +52 -24
- package/dist/core/UpdateGate.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +3 -3
- package/upgrades/1.3.156.md +24 -0
- package/upgrades/1.3.157.md +54 -0
- package/upgrades/side-effects/restart-when-idle.md +51 -0
- package/upgrades/side-effects/session-pool-route-shortcircuit.md +98 -0
|
@@ -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`).
|