instar 1.3.641 → 1.3.643
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 +160 -6
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +29 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/InFlightSyncOpMarker.d.ts +29 -0
- package/dist/core/InFlightSyncOpMarker.d.ts.map +1 -0
- package/dist/core/InFlightSyncOpMarker.js +149 -0
- package/dist/core/InFlightSyncOpMarker.js.map +1 -0
- package/dist/core/PostUpdateMigrator.d.ts +16 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +91 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/SessionManager.d.ts +146 -1
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +498 -144
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/SleepWakeDetector.d.ts +33 -0
- package/dist/core/SleepWakeDetector.d.ts.map +1 -1
- package/dist/core/SleepWakeDetector.js +53 -0
- package/dist/core/SleepWakeDetector.js.map +1 -1
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +23 -0
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/core/types.d.ts +52 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/lifeline/ServerSupervisor.d.ts +42 -0
- package/dist/lifeline/ServerSupervisor.d.ts.map +1 -1
- package/dist/lifeline/ServerSupervisor.js +79 -4
- package/dist/lifeline/ServerSupervisor.js.map +1 -1
- package/dist/lifeline/TelegramLifeline.d.ts.map +1 -1
- package/dist/lifeline/TelegramLifeline.js +8 -0
- package/dist/lifeline/TelegramLifeline.js.map +1 -1
- package/dist/monitoring/DegradedTmuxGuard.d.ts +161 -0
- package/dist/monitoring/DegradedTmuxGuard.d.ts.map +1 -0
- package/dist/monitoring/DegradedTmuxGuard.js +277 -0
- package/dist/monitoring/DegradedTmuxGuard.js.map +1 -0
- package/dist/monitoring/guardManifest.d.ts.map +1 -1
- package/dist/monitoring/guardManifest.js +18 -0
- package/dist/monitoring/guardManifest.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +13 -5
- package/dist/server/routes.js.map +1 -1
- package/package.json +2 -2
- package/scripts/lint-no-direct-destructive.js +14 -0
- package/scripts/lint-sync-subprocess-chokepoint.js +208 -0
- package/scripts/sync-subprocess-chokepoint-baseline.json +148 -0
- package/src/data/builtin-manifest.json +65 -65
- package/upgrades/1.3.642.md +46 -0
- package/upgrades/1.3.643.md +66 -0
- package/upgrades/side-effects/false-excuse-deferral-stop-guard.md +75 -0
- package/upgrades/side-effects/tmux-event-loop-resilience.md +86 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: minor -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
The instar server makes frequent calls to `tmux` (the terminal multiplexer that
|
|
9
|
+
holds every agent session). Those calls were SYNCHRONOUS — they blocked the
|
|
10
|
+
server's single event loop until tmux answered. All agents on one machine share
|
|
11
|
+
ONE tmux server, so when it got slow under load a single call could freeze the
|
|
12
|
+
whole server for ~15 seconds. Two user-visible things broke as a result: the
|
|
13
|
+
dashboard's live connection dropped (it showed "disconnected"), and the ~0-CPU
|
|
14
|
+
freeze was MISREAD as the machine going to sleep (a false sleep/wake event).
|
|
15
|
+
|
|
16
|
+
This change makes the hot path non-blocking and adds three defenses — all
|
|
17
|
+
dev-gated, meaning they run LIVE on development agents (where the bug was
|
|
18
|
+
diagnosed) and ship DARK on the fleet for a graduated rollout:
|
|
19
|
+
|
|
20
|
+
- The frequent tmux calls now run asynchronously with a hard timeout, and the
|
|
21
|
+
dashboard's and sessions' reads are served from a cache that can never hang on
|
|
22
|
+
tmux.
|
|
23
|
+
- An in-flight-operation marker lets the sleep detector tell a real freeze apart
|
|
24
|
+
from a real sleep, so a slow tmux call is no longer misreported as sleep. The
|
|
25
|
+
lifeline reads the same marker cross-process, so it won't restart a
|
|
26
|
+
busy-but-alive server.
|
|
27
|
+
- A signal-only "degraded tmux" guard watches for a persistently slow tmux server
|
|
28
|
+
and surfaces it on `/guards` — without ever killing anything.
|
|
29
|
+
|
|
30
|
+
Crucially, a tmux call that times out is treated as INDETERMINATE (keep the
|
|
31
|
+
session) — never as "the session is dead" — so a slow tmux can never cause a live
|
|
32
|
+
session to be wrongly reaped.
|
|
33
|
+
|
|
34
|
+
## What to Tell Your User
|
|
35
|
+
|
|
36
|
+
For most agents nothing changes yet — this ships disabled on the fleet and is
|
|
37
|
+
being soaked on development agents first. On a development agent the visible win
|
|
38
|
+
is that the dashboard stops randomly showing "disconnected" and the agent stops
|
|
39
|
+
occasionally misreporting itself as having gone to sleep — both were caused by a
|
|
40
|
+
busy shared terminal manager freezing the server loop.
|
|
41
|
+
|
|
42
|
+
## Summary of New Capabilities
|
|
43
|
+
|
|
44
|
+
- Async, timeout-bounded tmux on the server hot path; dashboard + session reads
|
|
45
|
+
served from a non-blocking cache that can't hang on a slow tmux.
|
|
46
|
+
- Tri-state tmux outcome (success / definitely-absent / indeterminate); a timeout
|
|
47
|
+
KEEPS the session (never a false reap under load).
|
|
48
|
+
- In-flight-sync-op marker as the block-vs-sleep discriminator — works
|
|
49
|
+
cross-process so the lifeline won't restart a busy-but-alive server.
|
|
50
|
+
- Signal-only `DegradedTmuxGuard` (bounded-accumulation ring, load-gated, never
|
|
51
|
+
kills tmux) surfacing a persistently degraded tmux server via `GET /guards`.
|
|
52
|
+
- All three behind dev-gated flags (`monitoring.tmuxResilience.*`,
|
|
53
|
+
`monitoring.degradedTmuxGuard`): live on development agents, dark on the fleet.
|
|
54
|
+
|
|
55
|
+
## Evidence
|
|
56
|
+
|
|
57
|
+
Reproduced and guarded by a 3-tier suite (307 tests): InFlightSyncOpMarker
|
|
58
|
+
round-trip + TTL self-heal; SleepWakeDetector block-vs-sleep on both sides of the
|
|
59
|
+
boundary; SessionManager async tri-state (timeout ⇒ indeterminate KEEP, the
|
|
60
|
+
no-false-reap regression guard); wake-handler + ServerSupervisor amplifier guards;
|
|
61
|
+
DegradedTmuxGuard bounded-accumulation burst-invariant (10,000 samples, ring
|
|
62
|
+
length never exceeds windowSize); an integration slow-stub (a ~15s tmux stub
|
|
63
|
+
proves `/health` and `/sessions` never hang and zero sessions are reaped); and an
|
|
64
|
+
e2e dev-gate lifecycle (the three flags resolve live-on-dev / dark-on-fleet, the
|
|
65
|
+
guard reports alive not missing). `tsc`, `lint-sync-subprocess-chokepoint`,
|
|
66
|
+
`lint-guard-manifest`, and the `no-silent-fallbacks` ratchet are all green.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Side-Effects Review — False-Excuse Deferral Stop-Guard
|
|
2
|
+
|
|
3
|
+
**Slug:** `false-excuse-deferral-stop-guard` · **Tier:** 1 (small, low-risk hook behavior addition;
|
|
4
|
+
operator-requested direct fix, no spec). Touches `PostUpdateMigrator.getStopGateRouterHook()` (a
|
|
5
|
+
hook template), so the tier signal flags `belowFloor` (a PostUpdateMigrator touch raises the floor) —
|
|
6
|
+
accepted: the change is a self-contained substring guard, fully reversible, no external surface.
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
`stop-gate-router` hook gains `falseExcuseDeferralGuard`, mirroring the existing
|
|
11
|
+
`statedContinuationGuard`: a mode-independent IIFE that, on a Stop, blocks ONCE when the final
|
|
12
|
+
assistant message contains BOTH (a) a named piece of remaining work AND (b) a self-protective
|
|
13
|
+
deferral rationalization (session-length / time-of-day / made-mistakes / don't-rush /
|
|
14
|
+
tracked-so-it-won't-slip / next-session), re-feeding a "this excuse is false — proceed" directive.
|
|
15
|
+
Same change in the deployed copy is overwritten from this source on the next update (Migration
|
|
16
|
+
Parity — always-overwrite built-in hooks), so it reaches every agent.
|
|
17
|
+
|
|
18
|
+
## 1. Over-block / false positive
|
|
19
|
+
|
|
20
|
+
The AND-of-both-signals requirement is the false-positive control: a self-protective phrase alone
|
|
21
|
+
(e.g. "it's late, but everything is done") does NOT block — `knownWork` must also match. Tested:
|
|
22
|
+
a genuine completion and a time-reference-with-no-pending-work both pass through (no block). Cost of
|
|
23
|
+
any residual false positive is bounded to ONE extra turn (the agent re-affirms and re-stops), because
|
|
24
|
+
the guard fires once.
|
|
25
|
+
|
|
26
|
+
## 2. Under-block
|
|
27
|
+
|
|
28
|
+
If the agent stops with an excuse but uses wording outside the phrase lists, it slips through (a
|
|
29
|
+
false negative) — acceptable: the guard is a high-precision catch for the documented recurring
|
|
30
|
+
phrasings, not a complete classifier. The lists cover the operator-cited forms and the agent's actual
|
|
31
|
+
observed messages. Tunable by extending the arrays.
|
|
32
|
+
|
|
33
|
+
## 4. Signal vs authority
|
|
34
|
+
|
|
35
|
+
The guard is a one-shot re-feed (block decision with a reminder), exactly like the
|
|
36
|
+
stated-continuation guard. It never silences or rewrites a message and takes no destructive action.
|
|
37
|
+
The `stop_hook_active` loop guard guarantees the agent is never trapped — a legitimate stop (real
|
|
38
|
+
external blocker / work complete / a user-only decision) re-stops cleanly on the next attempt.
|
|
39
|
+
|
|
40
|
+
## 5. Interactions
|
|
41
|
+
|
|
42
|
+
Sits directly after `statedContinuationGuard` in the same hook, before the server round-trip — so it
|
|
43
|
+
works even when the server-side stop-gate is in shadow/off mode (which is exactly when these stalls
|
|
44
|
+
slip through). No new dependency, no network call, no state. Pure substring matching. Cannot recurse
|
|
45
|
+
or grow unbounded.
|
|
46
|
+
|
|
47
|
+
## 6. External surfaces
|
|
48
|
+
|
|
49
|
+
None. No route, no egress, no spend (no LLM call — deterministic substring matching in the hook
|
|
50
|
+
subprocess).
|
|
51
|
+
|
|
52
|
+
## 7. Multi-machine posture
|
|
53
|
+
|
|
54
|
+
Machine-local: the hook runs per-session on whichever machine hosts the session. No replicated state.
|
|
55
|
+
Each agent on each machine gets the guard via the standard hook update.
|
|
56
|
+
|
|
57
|
+
## 8. Rollback cost
|
|
58
|
+
|
|
59
|
+
Trivial: delete the `falseExcuseDeferralGuard` IIFE from `getStopGateRouterHook()`; the next update
|
|
60
|
+
overwrites the deployed copy back to guard-free. No data, no migration to unwind.
|
|
61
|
+
|
|
62
|
+
## Evidence pointers
|
|
63
|
+
|
|
64
|
+
- `tests/unit/stop-gate-false-excuse-deferral.test.ts` (6): renders valid JS containing the guard;
|
|
65
|
+
blocks the real-world excuse-stop (named work + self-protective excuse); blocks a
|
|
66
|
+
session-length/next-session deferral; does NOT block a genuine completion; does NOT block a time
|
|
67
|
+
reference with no deferred work (false-positive control); does NOT re-block under `stop_hook_active`.
|
|
68
|
+
- `tests/unit/stop-gate-stated-continuation.test.ts` + `tests/unit/generated-hooks-parse.test.ts`
|
|
69
|
+
stay green (the template literal still renders valid JS). Full `tsc` clean.
|
|
70
|
+
|
|
71
|
+
## Conclusion
|
|
72
|
+
|
|
73
|
+
Delivers the operator-requested structural catch for the recurring false-excuse early-stop, as an
|
|
74
|
+
instar feature that ships to every agent. High-precision, loop-safe, reversible, no external surface.
|
|
75
|
+
Ship.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Side-Effects Review — tmux Event-Loop Resilience (Increment 1)
|
|
2
|
+
|
|
3
|
+
**Slug:** `tmux-event-loop-resilience` · **Tier:** 2 (converged + approved spec
|
|
4
|
+
`docs/specs/tmux-event-loop-resilience.md`). Touches `PostUpdateMigrator` (a
|
|
5
|
+
config-strip migration), so the tier signal flags `belowFloor` — accepted: the
|
|
6
|
+
PostUpdateMigrator touch is a dev-gate cleanup strip (removes a stale persisted
|
|
7
|
+
`enabled:false` so the dev-gate resolves live-on-dev), fully reversible, no
|
|
8
|
+
external surface.
|
|
9
|
+
|
|
10
|
+
## Summary of the change
|
|
11
|
+
|
|
12
|
+
The server's frequent synchronous `tmux` calls blocked the single event loop; a
|
|
13
|
+
slow shared tmux server froze the loop ~15s, dropping the dashboard websocket and
|
|
14
|
+
getting misread as host sleep. Increment 1: (A) an async, timeout-bounded tmux
|
|
15
|
+
hot path with cache-served dashboard/session reads; (B) an in-flight-sync-op
|
|
16
|
+
marker as the block-vs-sleep discriminator (in-process for SleepWakeDetector,
|
|
17
|
+
cross-process via a mirror file for ServerSupervisor); (C) a signal-only
|
|
18
|
+
`DegradedTmuxGuard` that surfaces a persistently slow tmux without ever killing
|
|
19
|
+
it. All three are dev-gated: LIVE on a development agent, DARK on the fleet.
|
|
20
|
+
|
|
21
|
+
## 1. Over-block / false signal
|
|
22
|
+
|
|
23
|
+
`DegradedTmuxGuard` is signal-only — it raises ONE deduped attention item and
|
|
24
|
+
never kills, restarts, or reaps anything, so a false degradation signal costs at
|
|
25
|
+
most one surfaced notice. It is load-gated (suppressed when per-core load is high
|
|
26
|
+
— that is expected slowness, not a tmux fault) and requires N-cycle corroboration
|
|
27
|
+
(a single hiccup never raises an episode), with episode dedup + age-escalation.
|
|
28
|
+
The in-flight marker can only SUPPRESS a wake / DEFER a restart when affirmatively
|
|
29
|
+
present, in-flight, and non-stale — a false positive there fails toward the SAFE
|
|
30
|
+
direction (keep the session alive / let a genuinely-dead server restart).
|
|
31
|
+
|
|
32
|
+
## 2. Under-block / missed signal
|
|
33
|
+
|
|
34
|
+
A real freeze the marker doesn't cover is still caught: SleepWakeDetector's
|
|
35
|
+
existing cpuBlockBusyRatio path (CPU-spin blocks) is unchanged, and the marker
|
|
36
|
+
adds the ~0-CPU I/O-wait case #1240 couldn't see. The marker self-heals via a
|
|
37
|
+
2×timeout TTL so a leaked depth can never permanently blind sleep detection. The
|
|
38
|
+
supervisor defer is hard-capped (`starvationRestartThreshold`) so a stuck
|
|
39
|
+
in-flight marker can never wedge the restart path forever — it gives up loudly.
|
|
40
|
+
|
|
41
|
+
## 3. Blast radius / fail-open
|
|
42
|
+
|
|
43
|
+
Every failure path fails toward SAFE and is non-silent (no-silent-fallbacks
|
|
44
|
+
ratchet enforced): a tmux call that times out maps to INDETERMINATE, never to
|
|
45
|
+
"absent" — `isSessionAliveAsync` returns `'indeterminate'` (never `false`) on a
|
|
46
|
+
timeout, so a slow tmux can NEVER cause a live session to be reaped (the
|
|
47
|
+
line-2352 regression the tri-state guards). The cross-process marker reader is
|
|
48
|
+
fail-OPEN: an absent/unparseable mirror ⇒ null ⇒ the supervisor proceeds to
|
|
49
|
+
restart a genuinely dead server. The mirror writer is best-effort: an unwritable
|
|
50
|
+
mirror never breaks a tmux call. `DegradedTmuxGuard` uses a fixed-capacity
|
|
51
|
+
modulo-write ring (Bounded Accumulation — 10,000-sample burst-invariant test
|
|
52
|
+
proves the ring length never exceeds windowSize), so it cannot grow memory under
|
|
53
|
+
a flood.
|
|
54
|
+
|
|
55
|
+
## 4. Signal vs authority
|
|
56
|
+
|
|
57
|
+
The only blocking authority introduced is the supervisor restart-DEFER, and it is
|
|
58
|
+
bounded (hard cap + TTL + fail-open) and additive to the existing
|
|
59
|
+
`deferRestartForCpuStarvation` (the `||` keeps the CPU side-effect — verified by
|
|
60
|
+
test). The wake-handler amplifier guard short-circuits a marker-covered wake and
|
|
61
|
+
bounds the cascade's tmux re-validation (async 9000+SIGKILL); it never silences a
|
|
62
|
+
genuine recovery. `DegradedTmuxGuard` holds zero authority — it observes and
|
|
63
|
+
surfaces only.
|
|
64
|
+
|
|
65
|
+
## 5. Interactions
|
|
66
|
+
|
|
67
|
+
Reuses the existing `'event-loop-block'` WakeSuppressionReason + StallEvent (no
|
|
68
|
+
new telemetry shape). The async hot-path twins coexist with the legacy sync
|
|
69
|
+
methods behind `tmuxAsyncEnabled` (off ⇒ byte-identical legacy behavior — the e2e
|
|
70
|
+
observable-equivalence test). `getCachedRunningSessions()` is the cache the
|
|
71
|
+
request routes (GET /status et al.) now read; the non-request consumers
|
|
72
|
+
(JobScheduler, WebSocketManager, AutoUpdater) intentionally keep the live read
|
|
73
|
+
(documented residual, Increment 2 territory). Per-agent socket isolation is
|
|
74
|
+
explicitly OUT of Increment 1 (Increment 2).
|
|
75
|
+
|
|
76
|
+
## 6. Rollback
|
|
77
|
+
|
|
78
|
+
Each of the three flags is independently disable-able in `.instar/config.json`
|
|
79
|
+
(`monitoring.tmuxResilience.asyncHotPath.enabled`,
|
|
80
|
+
`monitoring.tmuxResilience.inFlightMarker.enabled`,
|
|
81
|
+
`monitoring.degradedTmuxGuard.enabled`) — set to `false` to revert that layer to
|
|
82
|
+
the pre-change behavior. The async hot path falls back to the sync path; the
|
|
83
|
+
marker branch goes inert; the guard stops observing. The migration strips only a
|
|
84
|
+
default-shaped persisted `false`, never an explicit operator `true`/`false`.
|
|
85
|
+
Config defaults OMIT `enabled` so the dev-gate (resolveDevAgentGate) governs:
|
|
86
|
+
fleet = dark, dev = live.
|