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.
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +174 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/core/ModelSwapService.d.ts +13 -0
- package/dist/core/ModelSwapService.d.ts.map +1 -1
- package/dist/core/ModelSwapService.js +11 -0
- package/dist/core/ModelSwapService.js.map +1 -1
- package/dist/core/ModelTierEscalation.d.ts +10 -0
- package/dist/core/ModelTierEscalation.d.ts.map +1 -1
- package/dist/core/ModelTierEscalation.js +4 -0
- package/dist/core/ModelTierEscalation.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +18 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/ProactiveSwapMonitor.d.ts +74 -20
- package/dist/core/ProactiveSwapMonitor.d.ts.map +1 -1
- package/dist/core/ProactiveSwapMonitor.js +349 -28
- package/dist/core/ProactiveSwapMonitor.js.map +1 -1
- package/dist/core/QuotaAwareScheduler.d.ts +70 -3
- package/dist/core/QuotaAwareScheduler.d.ts.map +1 -1
- package/dist/core/QuotaAwareScheduler.js +100 -16
- package/dist/core/QuotaAwareScheduler.js.map +1 -1
- package/dist/core/SessionManager.d.ts +28 -0
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +102 -0
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/SessionRefresh.d.ts +116 -1
- package/dist/core/SessionRefresh.d.ts.map +1 -1
- package/dist/core/SessionRefresh.js +280 -17
- package/dist/core/SessionRefresh.js.map +1 -1
- package/dist/core/SwapAntiThrash.d.ts +329 -0
- package/dist/core/SwapAntiThrash.d.ts.map +1 -0
- package/dist/core/SwapAntiThrash.js +1025 -0
- package/dist/core/SwapAntiThrash.js.map +1 -0
- package/dist/core/SwapLedger.d.ts +168 -0
- package/dist/core/SwapLedger.d.ts.map +1 -0
- package/dist/core/SwapLedger.js +275 -0
- package/dist/core/SwapLedger.js.map +1 -0
- package/dist/core/SwapWorkGate.d.ts +100 -0
- package/dist/core/SwapWorkGate.d.ts.map +1 -0
- package/dist/core/SwapWorkGate.js +175 -0
- package/dist/core/SwapWorkGate.js.map +1 -0
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +6 -0
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/core/types.d.ts +63 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/monitoring/guardManifest.d.ts.map +1 -1
- package/dist/monitoring/guardManifest.js +40 -1
- package/dist/monitoring/guardManifest.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +1 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +19 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +36 -3
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +65 -65
- package/src/scaffold/templates.ts +1 -0
- package/upgrades/1.3.730.md +112 -0
- 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.
|