instar 1.3.729 → 1.3.730

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 (65) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +174 -1
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/core/ModelSwapService.d.ts +13 -0
  5. package/dist/core/ModelSwapService.d.ts.map +1 -1
  6. package/dist/core/ModelSwapService.js +11 -0
  7. package/dist/core/ModelSwapService.js.map +1 -1
  8. package/dist/core/ModelTierEscalation.d.ts +10 -0
  9. package/dist/core/ModelTierEscalation.d.ts.map +1 -1
  10. package/dist/core/ModelTierEscalation.js +4 -0
  11. package/dist/core/ModelTierEscalation.js.map +1 -1
  12. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  13. package/dist/core/PostUpdateMigrator.js +18 -0
  14. package/dist/core/PostUpdateMigrator.js.map +1 -1
  15. package/dist/core/ProactiveSwapMonitor.d.ts +74 -20
  16. package/dist/core/ProactiveSwapMonitor.d.ts.map +1 -1
  17. package/dist/core/ProactiveSwapMonitor.js +349 -28
  18. package/dist/core/ProactiveSwapMonitor.js.map +1 -1
  19. package/dist/core/QuotaAwareScheduler.d.ts +70 -3
  20. package/dist/core/QuotaAwareScheduler.d.ts.map +1 -1
  21. package/dist/core/QuotaAwareScheduler.js +100 -16
  22. package/dist/core/QuotaAwareScheduler.js.map +1 -1
  23. package/dist/core/SessionManager.d.ts +28 -0
  24. package/dist/core/SessionManager.d.ts.map +1 -1
  25. package/dist/core/SessionManager.js +102 -0
  26. package/dist/core/SessionManager.js.map +1 -1
  27. package/dist/core/SessionRefresh.d.ts +116 -1
  28. package/dist/core/SessionRefresh.d.ts.map +1 -1
  29. package/dist/core/SessionRefresh.js +280 -17
  30. package/dist/core/SessionRefresh.js.map +1 -1
  31. package/dist/core/SwapAntiThrash.d.ts +329 -0
  32. package/dist/core/SwapAntiThrash.d.ts.map +1 -0
  33. package/dist/core/SwapAntiThrash.js +1025 -0
  34. package/dist/core/SwapAntiThrash.js.map +1 -0
  35. package/dist/core/SwapLedger.d.ts +168 -0
  36. package/dist/core/SwapLedger.d.ts.map +1 -0
  37. package/dist/core/SwapLedger.js +275 -0
  38. package/dist/core/SwapLedger.js.map +1 -0
  39. package/dist/core/SwapWorkGate.d.ts +100 -0
  40. package/dist/core/SwapWorkGate.d.ts.map +1 -0
  41. package/dist/core/SwapWorkGate.js +175 -0
  42. package/dist/core/SwapWorkGate.js.map +1 -0
  43. package/dist/core/devGatedFeatures.d.ts.map +1 -1
  44. package/dist/core/devGatedFeatures.js +6 -0
  45. package/dist/core/devGatedFeatures.js.map +1 -1
  46. package/dist/core/types.d.ts +63 -1
  47. package/dist/core/types.d.ts.map +1 -1
  48. package/dist/core/types.js.map +1 -1
  49. package/dist/monitoring/guardManifest.d.ts.map +1 -1
  50. package/dist/monitoring/guardManifest.js +40 -1
  51. package/dist/monitoring/guardManifest.js.map +1 -1
  52. package/dist/scaffold/templates.d.ts.map +1 -1
  53. package/dist/scaffold/templates.js +1 -0
  54. package/dist/scaffold/templates.js.map +1 -1
  55. package/dist/server/AgentServer.d.ts.map +1 -1
  56. package/dist/server/AgentServer.js +19 -0
  57. package/dist/server/AgentServer.js.map +1 -1
  58. package/dist/server/routes.d.ts.map +1 -1
  59. package/dist/server/routes.js +36 -3
  60. package/dist/server/routes.js.map +1 -1
  61. package/package.json +1 -1
  62. package/src/data/builtin-manifest.json +65 -65
  63. package/src/scaffold/templates.ts +1 -0
  64. package/upgrades/1.3.730.md +112 -0
  65. package/upgrades/side-effects/swap-continuity-antithrash.md +118 -0
@@ -0,0 +1,112 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: minor -->
5
+
6
+ ## What Changed
7
+
8
+ The proactive subscription-account swap could thrash: on 2026-07-02 one dev
9
+ agent executed 36 proactive swaps in 8 waves, round-tripping the same sessions
10
+ between the same hot accounts and repeatedly killing six parallel build
11
+ subagents mid-task — the continuity mechanism became the primary source of
12
+ work destruction. Per `docs/specs/swap-continuity-antithrash.md` (converged
13
+ round 6, approved), two composable pieces now bind at the swap chokepoints:
14
+
15
+ **Piece 1 — anti-thrash brakes** at the proactive DECISION chokepoint
16
+ (`ProactiveSwapMonitor`), engine `SwapAntiThrashEngine`:
17
+
18
+ - **All-hot brake**: a proactive swap is REFUSED unless at least one eligible
19
+ target measures under the target ceiling (threshold − headroom, default
20
+ 65%). When every account is hot, staying put wins — one reactive rescue
21
+ strictly dominates N pointless kills.
22
+ - **Per-session dwell** (default 45 min): a just-swapped session is not moved
23
+ again, restart-safe via the new durable decision ledger
24
+ (`state/swap-ledger.jsonl`, single writer `SwapLedger`, O(1) segment
25
+ rotation).
26
+ - **Target-materially-better**: a validity gate (quota reading PRESENT and
27
+ FRESH — an unmeasured account is never "0% cool", it counts toward all-hot)
28
+ plus absolute ceiling plus a 15-point improvement floor, in a normative
29
+ filter→score→verify order; the checked target IS the executed target
30
+ (`onQuotaPressure` gains an explicit-target funnel contract that
31
+ revalidates, never re-selects — closing the double-threshold seam).
32
+ - **Two-tier thrash breaker** (T1 inversion / T2 rotation frequency) with
33
+ episode continuation, restart-proof derivation from the ledger, and ONE
34
+ deduped attention item per episode; execution failures write `failed` rows
35
+ with per-session backoff + streak escalation (the previously-silent
36
+ discarded reactive result promise is now observed).
37
+ - Untagged sessions leave the proactive candidate set entirely (a background
38
+ optimizer must never mutate the default-slot binding). The REACTIVE
39
+ continuity guarantee is untouched (I6) — a genuinely walled session still
40
+ swaps, breaker/dwell notwithstanding.
41
+
42
+ **Piece 2 — the in-flight work gate** (`SwapWorkGate`) at the
43
+ `SessionRefresh.refreshSession` funnel, the primitive every session-killing
44
+ mutation flows through:
45
+
46
+ - A tri-state work probe (`SessionManager.checkSessionWorkState` — pane
47
+ footer + child-process legs, async + coalesced, one shared `ps` snapshot
48
+ per ~2s TTL) plus the subagent leg (`SubagentTracker`); uncertainty
49
+ resolves BUSY for optimization callers (I7; an id-less session is exiled,
50
+ never blindly killed).
51
+ - Caller classes: `proactive-swap` DEFERS (retried each tick through the full
52
+ brake pipeline, dropped at a 30-min ceiling — the wall wins);
53
+ `reactive-swap` gets a bounded grace (default 120 s, proceeds at the first
54
+ not-busy observation) then proceeds WITH the F3 mitigations (enumerated
55
+ killed subagents + the last unanswered inbound re-injected as a quoted-data
56
+ envelope); `interactive-refresh` (the default for every unlisted caller)
57
+ gets a structured refusal; `recovery` is exempt.
58
+ - `POST /sessions/refresh` answers busy PRE-202 with HTTP 409
59
+ `session-busy` (counts and ages only) and gains optional `force: true` —
60
+ force overrides ONLY the work gate, never the rate guard, and is recorded
61
+ bearer-level in the ledger.
62
+ - The model-swap idle check gains a SUBAGENT leg behind its own micro-flag
63
+ (`models.tierEscalation.subagentIdleLeg`, concrete default false — dark;
64
+ graduates on its own rung).
65
+
66
+ Observability: `GET /subscription-pool/proactive-swap` grows additive
67
+ `brakes`/`deferrals` blocks (refusal counters, breaker state, ledger
68
+ writability, hydration honesty); both pieces register in the guard manifest.
69
+
70
+ Rollout: antiThrash resolves `enabled:true, dryRun:true` when absent (dry-run
71
+ ledger rows only — decision behavior stays byte-identical to v1.3.722 until a
72
+ deliberate `dryRun:false`); `swapContinuity` ships with the enabled key
73
+ OMITTED (dev-agent gate: live-in-dry-run on a development agent, dark on the
74
+ fleet) and is registered in `DEV_GATED_FEATURES`. Rollback:
75
+ `antiThrash.dryRun:true` restores legacy decisions; removing the dev agent's
76
+ swapContinuity enablement un-wires the gate entirely.
77
+
78
+ ## What to Tell Your User
79
+
80
+ <!-- audience: agent-only, maturity: experimental -->
81
+ - **My account-switching can no longer destroy my own work (experimental,
82
+ dark)**: when I juggle several subscription accounts, I used to sometimes
83
+ bounce conversations back and forth between two nearly-full accounts —
84
+ every bounce restarted the session and killed helpers working in the
85
+ background. Now the switcher has brakes: if every account is nearly full I
86
+ stay put, a just-moved conversation rests before it can move again, and a
87
+ move only happens when the destination is genuinely much cooler. Separately,
88
+ a switch will wait for in-flight work to finish instead of cutting it down
89
+ mid-task — and on the rare forced switch, the resumed conversation is told
90
+ exactly which helpers were interrupted and which message still needs an
91
+ answer. Nothing changes on your setup yet — the brakes observe first, and
92
+ the work protection ships dark until it has soaked.
93
+
94
+ ## Summary of New Capabilities
95
+
96
+ | Capability | How to Use |
97
+ |-----------|-----------|
98
+ | Anti-thrash brakes on the proactive account swap (dry-run soak by default) | `subscriptionPool.proactiveSwap.antiThrash` in `.instar/config.json` (absent block = enabled + dry-run); live only at a deliberate `dryRun: false` |
99
+ | Durable swap-decision ledger | `state/swap-ledger.jsonl` (single writer; restart-safe dwell/breaker state) |
100
+ | Brake/deferral observability | `GET /subscription-pool/proactive-swap` → additive `brakes` / `deferrals` blocks |
101
+ | In-flight work gate on session-killing mutations (dev-gated dark, dry-run first) | `subscriptionPool.swapContinuity` (enabled omitted → live on dev agents, dark on fleet); restart-required |
102
+ | Pre-202 busy refusal + force on refresh | `POST /sessions/refresh` → 409 `session-busy` when busy; re-issue with `force: true` to override the gate (never the rate guard) |
103
+ | Model-swap subagent idle leg (dark micro-flag) | `models.tierEscalation.subagentIdleLeg: true` (default false; own rollout rung) |
104
+
105
+ ## Evidence
106
+
107
+ - Spec: `docs/specs/swap-continuity-antithrash.md` (converged round 6 — 0
108
+ CRITICAL / 0 MAJOR; approved under standing Session-A preapproval, topic
109
+ 29836). Plain-English companion: `docs/specs/swap-continuity-antithrash.eli16.md`.
110
+ - Incident data: `logs/server.log` (echo dev agent, v1.3.722, 2026-07-02 —
111
+ 36 executed proactive swaps / 72 account-swap lines / 8 waves).
112
+ - Side-effects review: `upgrades/side-effects/swap-continuity-antithrash.md`.
@@ -0,0 +1,118 @@
1
+ # Side-Effects Review — swap-continuity anti-thrash brakes + in-flight work deferral
2
+
3
+ Spec: `docs/specs/swap-continuity-antithrash.md` (converged round 6, approved).
4
+ Roadmap: Session A item 4.4 + operator-priority thrash brake (2026-07-02 incident).
5
+
6
+ ## What changes at runtime
7
+
8
+ 1. **New durable state file**: `state/swap-ledger.jsonl` (single writer:
9
+ `SwapLedger`; O(1) segment rotation, 10 MB × keep 2). Absence = cold start
10
+ (dwell un-primed for the first 45 min — the §9 accepted one-time gap).
11
+ 2. **Proactive swap decisions** (only when `subscriptionPool.proactiveSwap.enabled`
12
+ is already opted into — fleet-dark today):
13
+ - antiThrash resolves `enabled:true, dryRun:true` when the block is absent →
14
+ the ONLY immediate effect on an opted-in install is dry-run ledger rows +
15
+ status counters (rung-1 honesty, §10). Decision behavior stays
16
+ byte-identical to v1.3.722 until a deliberate `dryRun:false`.
17
+ - Live (`dryRun:false`): untagged sessions leave the proactive candidate set
18
+ (I10); refusals (all-hot / dwell / no-material-target / target-unmeasured /
19
+ reversal / thrash-breaker / ledger-lost) bind; the executed target is the
20
+ brake-checked target (I1) with execute-time revalidation.
21
+ 3. **Reactive path**: decision behavior byte-identical (I6). Additive
22
+ observation only — reactive swaps write ledger rows; execution failures
23
+ (previously a silently-discarded promise) write `failed` rows and can raise
24
+ ONE deduped attention item per streak/hop-cascade/rate-cap-strand.
25
+ 4. **Session-killing mutations** (`SessionRefresh.refreshSession`): a work gate
26
+ consults in-flight state when `swapContinuity` resolves live (dev-agent gate
27
+ — key OMITTED from shipped config, dark on fleet). Caller classes:
28
+ proactive-swap defers; reactive-swap gets a bounded grace (≤120 s) then
29
+ proceeds WITH mitigations; interactive-refresh (the default for every
30
+ unlisted caller) gets a structured `session-busy` refusal + `force`;
31
+ recovery is exempt. Forced kills carry the F3 mitigation payload
32
+ (enumerated killed subagents + re-injected unanswered inbound, quoted-data
33
+ envelope).
34
+ 5. **HTTP surface**: `/subscription-pool/proactive-swap` gains additive
35
+ `brakes`/`deferrals` blocks; `/sessions/refresh` gains a pre-202 409
36
+ `session-busy` refusal + optional `force:true` (bearer-level, recorded as
37
+ such). No route renamed; nothing 503s that didn't.
38
+ 6. **Attention items**: one per thrash episode / failure streak /
39
+ measurement-blind episode / ledger-loss episode — episode-deduped (P17),
40
+ restart-proof via episodeId derivation from the ledger.
41
+
42
+ ## Failure directions (the load-bearing ones)
43
+
44
+ - Ledger UNWRITABLE ⇒ proactive optimization PAUSES (`ledger-lost`,
45
+ counter-only), level-triggered resume + one `outage-summary` row; reactive
46
+ untouched (I12).
47
+ - Quota reading ABSENT/STALE ⇒ NOT under the ceiling; counts toward all-hot;
48
+ never treated as 0% (bound 0, R4-M1). Whole-pool blindness pauses the
49
+ optimizer AND says so (I13).
50
+ - Work-gate uncertainty ⇒ BUSY for optimization callers ('absent' behaves like
51
+ 'indeterminate', R5-M1), busy-for-grace for reactive (never strands — the
52
+ grace deadline always proceeds).
53
+
54
+ ## Rollback levers
55
+
56
+ - `antiThrash.dryRun:true` restores v1.3.722 decision behavior (keeps
57
+ observability). `antiThrash.enabled:false` disables the engine's binding
58
+ entirely (legacy path incl. legacy 10-min cooldown).
59
+ - Removing the dev agent's `swapContinuity` enablement un-wires the gate
60
+ (SessionRefresh reverts to unconditional kill). `subagentIdleLeg` stays a
61
+ concrete `false` default on the model-swap block (rung 3a flip only).
62
+ - Deleting `state/swap-ledger.jsonl` = cold start (bounded, logged, §9).
63
+
64
+ ## Blast radius
65
+
66
+ Machine-local by design (§8): per-machine ledger, per-machine breaker, no
67
+ cross-machine state. No egress, no spend, no credential surface. The only
68
+ processes touched are the agent's own tmux sessions, and only through the
69
+ pre-existing SessionRefresh funnel with strictly ADDED protection.
70
+
71
+ ## Wiring completion (second commit of this build)
72
+
73
+ The core modules landed first (2866f1073); the wiring commit binds them:
74
+ server.ts spine (unconditional ledger+engine hydration, live-knob getters,
75
+ scheduler/monitor/SessionRefresh hooks), the SessionManager tri-state work
76
+ probe (shared ps snapshot), the /sessions/refresh pre-202 409 + force,
77
+ the AgentServer ModelSwapService subagent-leg probe (Q5 dark micro-flag),
78
+ DEV_GATED_FEATURES + guard-manifest registration, CLAUDE.md template +
79
+ PostUpdateMigrator awareness, and the release-note fragment. No new runtime
80
+ side effects beyond those reviewed above — this section records that the
81
+ dark/dry-run posture described above is now actually reachable end-to-end.
82
+
83
+ ## CI-green follow-up (third commit of this build)
84
+
85
+ Scope: zero-runtime-behavior commit that turns the two red unit shards green.
86
+ (1) Two pre-existing tests asserted the PRE-brake `/sessions/refresh` call
87
+ shape (`refreshSession({ sessionName, followUpPrompt, reason, fresh })` and
88
+ the exact-args spy without `force`); both now assert the documented §4.5
89
+ shape (fresh forwarded, `force` pinned to a strict boolean, default false).
90
+ (2) The no-silent-fallbacks ratchet counted 503 vs baseline 491: a
91
+ base-vs-HEAD set-diff of the exact test heuristic showed the PR added 13
92
+ genuinely-new flagged catches (every other +entry is a symmetric line-shift
93
+ artifact — 59/59 commands/server.ts, 31/31 AgentServer.ts, 19/19 routes.ts,
94
+ 7/7 SessionManager.ts, 5/5 PostUpdateMigrator.ts). All 13 are the feature's
95
+ DESIGNED fail-safes, each failing toward the safe direction: SwapWorkGate
96
+ probe legs → indeterminate reads BUSY (I7, defer — never a wrong kill);
97
+ SessionRefresh work-gate knob/probe/mitigation arms → gate-dark or refusal
98
+ (the gate is ADDITIVE, §4.5 — a broken gate must never block a refresh, and
99
+ mitigations never gate the respawn); ProactiveSwapMonitor safe-knob reads →
100
+ feature-dark (legacy behavior holds); SwapLedger append/hydrate → loud warn
101
+ + I12 optimization pause + counted loss classes (R4-m1), partial hydration
102
+ surfaced via coveredWindow/corrupt. Each now carries an in-brace
103
+ `@silent-fallback-ok` justification; the heuristic count returns to 490
104
+ (≤ 491 baseline) — no baseline bump.
105
+
106
+ Side-effects review of THIS commit: over-block — none (comments + test
107
+ expectations only; no input is newly rejected). Under-block — none (no
108
+ detector logic changed). Abstraction fit — the tags are the ratchet's
109
+ designed exemption mechanism for reviewed fail-safes. Signal-vs-authority —
110
+ no authority added; the tagged catches remain signal-preserving fail-safes
111
+ (refusals/warns/counters are the loud surfaces). Interactions — none; the
112
+ tags are inert at runtime. External surfaces — none. Multi-machine — n/a
113
+ (no behavior change; feature posture unchanged, machine-local by design §8).
114
+ Rollback — revert the commit; nothing durable depends on it. Second-pass —
115
+ not required: although the touched files include gate/monitor modules, the
116
+ source diff is comment-only (verified: `git diff` shows no executable-line
117
+ changes in src/ beyond comment insertion) and the test diff asserts the
118
+ already-reviewed §4.5 behavior.