instar 1.3.336 → 1.3.338
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 +43 -7
- package/dist/commands/server.js.map +1 -1
- package/dist/core/OwnerSuspectBreaker.d.ts +79 -0
- package/dist/core/OwnerSuspectBreaker.d.ts.map +1 -0
- package/dist/core/OwnerSuspectBreaker.js +106 -0
- package/dist/core/OwnerSuspectBreaker.js.map +1 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +25 -5
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/SessionRouter.d.ts +7 -0
- package/dist/core/SessionRouter.d.ts.map +1 -1
- package/dist/core/SessionRouter.js +14 -2
- package/dist/core/SessionRouter.js.map +1 -1
- package/dist/monitoring/ContextWedgeSentinel.d.ts +46 -17
- package/dist/monitoring/ContextWedgeSentinel.d.ts.map +1 -1
- package/dist/monitoring/ContextWedgeSentinel.js +92 -30
- package/dist/monitoring/ContextWedgeSentinel.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +14 -3
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +63 -63
- package/upgrades/1.3.338.md +97 -0
- package/upgrades/aup-wedge-fresh-api.eli16.md +45 -0
- package/upgrades/side-effects/aup-wedge-fresh-api.md +83 -0
- package/upgrades/side-effects/owner-suspect-breaker.md +52 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# ELI16 — AUP-Rejection Wedge Signature + Fresh-Respawn API
|
|
2
|
+
|
|
3
|
+
## What happened
|
|
4
|
+
|
|
5
|
+
Last night one of my sessions died in a new way. Justin and the session had
|
|
6
|
+
been designing security test scenarios (deliberately-sneaky "try to trick the
|
|
7
|
+
agent" prompts, for the EXO 3.0 test harness). Enough of that content piled up
|
|
8
|
+
in the conversation that the AI provider's safety filter started refusing the
|
|
9
|
+
ENTIRE conversation — every single reply came back "this request appears to
|
|
10
|
+
violate our Usage Policy." Since each reply re-sends the whole conversation,
|
|
11
|
+
every reply failed, forever. From Justin's side it looked like me ignoring him
|
|
12
|
+
for an hour: "✓ Delivered" receipts, no answers.
|
|
13
|
+
|
|
14
|
+
The watchdog that's supposed to catch dead-but-still-printing sessions (the
|
|
15
|
+
ContextWedgeSentinel) only knew ONE death signature — the "thinking block"
|
|
16
|
+
error from May. This new one sailed right past it. And the only repair lever —
|
|
17
|
+
"restart fresh, do NOT reload the poisoned conversation" — existed solely
|
|
18
|
+
inside the watchdog's own wiring, so fixing it by hand meant editing a state
|
|
19
|
+
file directly.
|
|
20
|
+
|
|
21
|
+
## What this change does
|
|
22
|
+
|
|
23
|
+
1. **Teaches the watchdog the second signature.** It now recognizes the
|
|
24
|
+
policy-rejection loop too, and the audit log says WHICH kind of wedge it
|
|
25
|
+
caught. Safety detail: one single policy rejection is NOT treated as a
|
|
26
|
+
wedge — sometimes one message just gets refused and the next works fine,
|
|
27
|
+
and killing the conversation for that would destroy real state. The loop
|
|
28
|
+
version always repeats, so we require seeing it more than once.
|
|
29
|
+
|
|
30
|
+
2. **Adds the repair lever to the API.** `POST /sessions/refresh` now accepts
|
|
31
|
+
`"fresh": true` — restart this session WITHOUT reloading its conversation.
|
|
32
|
+
That's the right move whenever a transcript itself is poisoned.
|
|
33
|
+
|
|
34
|
+
3. **Tells every deployed agent about it.** The CLAUDE.md migration adds the
|
|
35
|
+
new signature, the API lever, and the prevention rule: keep literal attack
|
|
36
|
+
payloads in files on disk and reference them by path — never paste them
|
|
37
|
+
into the conversation itself.
|
|
38
|
+
|
|
39
|
+
## Why it's safe
|
|
40
|
+
|
|
41
|
+
Detection alone never kills anything (auto-recovery stays opt-in, unchanged).
|
|
42
|
+
The one-off-vs-loop distinction is tested on both sides with the real pane
|
|
43
|
+
text from the incident. The API param is validated, rate-guarded by the same
|
|
44
|
+
limiter every refresh uses, and reuses the exact internal mode the sentinel
|
|
45
|
+
already exercised in production twice this week.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Side-Effects Review — AUP-Rejection Wedge Signature + Fresh-Respawn API
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `aup-wedge-fresh-api`
|
|
4
|
+
**Date:** `2026-06-06`
|
|
5
|
+
**Author:** `Echo (instar-dev agent, session-robustness topic per Justin's "work on more robust ways to handle these scenarios")`
|
|
6
|
+
**Second-pass reviewer:** `self-adversarial pass over the one-off-vs-loop boundary (the only place this change could destroy user state)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
ContextWedgeSentinel learns a second wedge-signature family — the AUP-rejection
|
|
11
|
+
loop (2026-06-05 EXO incident) — via `classifyWedgeTail()`, which returns the
|
|
12
|
+
matching family for the live tail and tags every event/escalation with `kind`.
|
|
13
|
+
The AUP family requires the signature on >1 line of the capture (the loop
|
|
14
|
+
repeats every turn; a benign one-off rejection appears once). `POST
|
|
15
|
+
/sessions/refresh` forwards a validated `fresh` boolean to
|
|
16
|
+
`SessionRefresh.refreshSession` — the previously-internal-only fresh-mode.
|
|
17
|
+
CLAUDE.md migrator patch teaches deployed agents both additions. Files:
|
|
18
|
+
`ContextWedgeSentinel.ts`, `server.ts` (wiring detail strings), `routes.ts`,
|
|
19
|
+
`PostUpdateMigrator.ts`, spec doc, three test files.
|
|
20
|
+
|
|
21
|
+
## Decision-point inventory
|
|
22
|
+
|
|
23
|
+
- AUP tail classification — **add (detector)** — new signal only; same confirm
|
|
24
|
+
window + same opt-in recovery policy as the existing family.
|
|
25
|
+
- One-line AUP occurrence — **deliberate non-detection** — a benign one-off
|
|
26
|
+
rejection must never cost a session its conversation; the wedge always
|
|
27
|
+
repeats, so the second occurrence arrives within one failed turn.
|
|
28
|
+
- `fresh` route param — **add (lever, validated)** — exposes an existing
|
|
29
|
+
internal mode; same spawnLimiter + SessionRefresh rate-guard as every
|
|
30
|
+
refresh.
|
|
31
|
+
|
|
32
|
+
## 1. Over-block
|
|
33
|
+
|
|
34
|
+
None added. Detection alone never kills anything (autoRecovery ships dark,
|
|
35
|
+
unchanged). The worst over-detection case — a session DISCUSSING the AUP error
|
|
36
|
+
with two quoted copies in its tail — is covered by the same two defenses as the
|
|
37
|
+
thinking-block family (tail gate + 45s no-progress confirm window): a working
|
|
38
|
+
session scrolls the signature out before confirm.
|
|
39
|
+
|
|
40
|
+
## 2. Under-block
|
|
41
|
+
|
|
42
|
+
(a) A genuinely-wedged session whose FIRST rejection is the only one on screen
|
|
43
|
+
is not detected until the next inbound message produces the second copy —
|
|
44
|
+
bounded by one message turn; accepted to protect one-off conversations.
|
|
45
|
+
(b) A wedge whose error text the provider rewords escapes both families —
|
|
46
|
+
inherent to signature-based detection; the turn-receipt structural close is
|
|
47
|
+
tracked separately (CMT-1115 follow-up design).
|
|
48
|
+
|
|
49
|
+
## 3. Level-of-abstraction fit
|
|
50
|
+
|
|
51
|
+
The family lives inside the existing sentinel (same lifecycle, confirm window,
|
|
52
|
+
recovery, veto integration) rather than a 5th sentinel — the failure shape is
|
|
53
|
+
identical; only the signature differs. `kind` is data on existing events, not
|
|
54
|
+
a new event vocabulary. The route param reuses `RefreshOptions.fresh` which
|
|
55
|
+
SessionRefresh already owned; the route stays a thin validated entry point.
|
|
56
|
+
|
|
57
|
+
## 4. Signal vs authority compliance
|
|
58
|
+
|
|
59
|
+
**Required reference:** `docs/signal-vs-authority.md`
|
|
60
|
+
|
|
61
|
+
- [x] No new authority. The detector is a signal; recovery policy (detect-only
|
|
62
|
+
/ dry-run / live) is unchanged and opt-in. The `fresh` param is operator/agent
|
|
63
|
+
initiative through an already-rate-guarded lever; the route grants no new
|
|
64
|
+
capability that the sentinel wiring didn't already exercise.
|
|
65
|
+
|
|
66
|
+
## 5. Interactions
|
|
67
|
+
|
|
68
|
+
- **SessionReaper veto:** `isRecoveryActive` covers AUP wedges identically
|
|
69
|
+
(state machine is shared) — the reaper can't kill a session mid-recovery.
|
|
70
|
+
- **Sentinel audit:** `kind` lands in `logs/sentinel-events.jsonl` detail
|
|
71
|
+
strings; existing consumers parse free-text detail, no schema break.
|
|
72
|
+
- **`fresh` + followUpPrompt:** unchanged interaction — fresh clears the resume
|
|
73
|
+
entry after the kill persists it; respawner then spawns clean (SessionRefresh
|
|
74
|
+
ordering, already tested).
|
|
75
|
+
- **Back-compat:** `signatureIsTail()` boolean wrapper preserved — all existing
|
|
76
|
+
callers/tests pass unmodified (81 green pre-existing + new).
|
|
77
|
+
|
|
78
|
+
## 6. External surfaces / 7. Rollback
|
|
79
|
+
|
|
80
|
+
One new accepted body field on an existing authed route (400 on non-boolean);
|
|
81
|
+
response gains `fresh` echo. No schema/config/persistent state; migrator patch
|
|
82
|
+
is append-only + idempotent (marker: 'AUP-rejection wedge'). Rollback = revert;
|
|
83
|
+
the second family goes blind again and fresh-respawn returns to internal-only.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Side-Effects Review — Owner-Suspect Breaker
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `owner-suspect-breaker`
|
|
4
|
+
**Date:** `2026-06-06`
|
|
5
|
+
**Author:** `Echo (instar-dev agent, autonomous session per Justin's direction)`
|
|
6
|
+
**Second-pass reviewer:** `adversarial reviewer subagent — OBJECT on probe 1 (forever-suspect under load), CONFIRMED BY REPRODUCTION and fixed in-review (absolute per-episode TTL + regression test); probes 2–5 CONCUR`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Wires the SessionRouter's previously-inert `markOwnerSuspect` hook into a real per-peer circuit: `OwnerSuspectBreaker` (pure core; absolute-TTL half-open windows, default 30s; per-peer `FailureEpisodeLatch` for first-log/one-signal/recovery accounting; state deleted on success). Wiring composes `!isSuspect` into `isMachineAlive`, filters suspect machines from placement candidates (all-suspect → unfiltered fallback), and the new `onOwnerResponsive` router dep closes windows on any delivery ack. Plus the router `chains`-map leak fix (entries deleted when their tail settles while current). Files: `OwnerSuspectBreaker.ts` (new), `SessionRouter.ts` (two small additions), `server.ts` wiring block, tests.
|
|
11
|
+
|
|
12
|
+
## Decision-point inventory
|
|
13
|
+
|
|
14
|
+
- `isMachineAlive` composition — **modify** — a suspect peer reads as not-alive for ROUTING, sending its sessions down the EXISTING failover re-place path. The decision of where they land is unchanged (placement); the decision of when a peer is suspect is new (retry exhaustion, the signal the router already emitted).
|
|
15
|
+
- Placement candidate filter — **modify** — suspect machines excluded unless that empties the set.
|
|
16
|
+
- `OwnerSuspectBreaker` — **add** — per-peer state machine consuming the router's existing exhaustion signal.
|
|
17
|
+
- `chains` cleanup + `onOwnerResponsive` — **add (hygiene/signal)**.
|
|
18
|
+
|
|
19
|
+
## 1. Over-block
|
|
20
|
+
|
|
21
|
+
The reviewer's probe-1 OBJECT was exactly an over-block: with window-extension semantics, a steady <TTL message stream re-marked a suspect peer on every dispatch (no delivery is attempted while suspect → `recordSuccess` unreachable → TTL the only exit → extended forever). Reproduced: a peer healthy from t=40s stayed suspect at t=10min with 31 forced failovers. **Fixed in-review**: `markSuspect` no longer extends an open window (absolute per-episode TTL) — half-open is reached on schedule regardless of traffic, regression-tested. Residual over-block: a genuinely-healthy peer that failed one message's retries pays one 30s window — bounded, and any successful delivery (e.g. a different session's forward in the half-open probe) clears it instantly.
|
|
22
|
+
|
|
23
|
+
## 2. Under-block
|
|
24
|
+
|
|
25
|
+
(a) Suspect-window message POLICY is deliberately out of scope: messages still take the existing re-place path (the only message-preserving path while `queueMessage` is a production no-op). The queue-vs-replace stability trade — and the durable-queue investment it requires — is an operator decision; lettered options go to Justin <!-- tracked: CMT-1109 -->. (b) The no-op `queueMessage` also affects the `placing/transferring` branch (pre-existing; surfaced in the options message). (c) `spawnOnMachine` remains un-breakered (reviewer probe 3: deliberate — the all-suspect fallback depends on it attempting, and it throws-to-caller on failure).
|
|
26
|
+
|
|
27
|
+
## 3. Level-of-abstraction fit / 4. Signal vs authority
|
|
28
|
+
|
|
29
|
+
The breaker consumes the router's OWN exhaustion signal and feeds the router's OWN aliveness dep — no new decision-maker, no second routing brain; placement, CAS, and ownership semantics untouched. Per `docs/signal-vs-authority.md`: the suspect window is a routing-availability signal with bounded lifetime; the failover authority it triggers is the pre-existing path. Composition lives in the wiring (the router stays pure/dep-shaped).
|
|
30
|
+
|
|
31
|
+
## 5. Interactions
|
|
32
|
+
|
|
33
|
+
- **Swap count ("fewer swaps" directive):** unchanged — a session moves at most once per peer-down episode; the breaker removes the OTHER sessions' retry tax, not adds moves (reviewer probe 1 trace, post-fix).
|
|
34
|
+
- **Pins:** a HARD-pinned session does not migrate during a suspect window (placement returns `hard-pin-unavailable`; resolves in place after the TTL — reviewer probe 2).
|
|
35
|
+
- **All-suspect fallback:** placement proceeds unfiltered; `spawnOnMachine` is not `isMachineAlive`-gated so it attempts and throws-to-caller — no wedge (probe 3).
|
|
36
|
+
- **Stale-ownership acks** close the window — correct: the breaker measures transport health to the peer, which any ack proves (probe 4).
|
|
37
|
+
- **Chains cleanup race:** identity-check guard preserves per-session serialization under concurrent re-route; traced clean (probe 5).
|
|
38
|
+
- **Sustained-suspicion signal:** one DegradationReporter record per 10min episode per peer (`SessionPool.ownerDelivery`); reporter's internal cooldown bounds user-facing volume.
|
|
39
|
+
|
|
40
|
+
## 6. External surfaces / 7. Rollback
|
|
41
|
+
|
|
42
|
+
Logs + degradation records only; no API/schema/config/persistent state; no migration. Rollback = revert (the wiring block removal restores the inert-hook status quo; the leak and retry-tax return).
|
|
43
|
+
|
|
44
|
+
## Conclusion
|
|
45
|
+
|
|
46
|
+
The audit's vaguest lead ("load-shedding") resolved into the sharpest finding of the night: a fully-designed breaker hook shipped dead at the wiring layer. The fix is composition, not invention — and the adversarial pass caught a genuine inversion (the busier a recovered peer, the longer it stayed exiled) before it ever ran. Policy beyond the existing semantics goes to the operator, as it should.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Phase 5 — Second-pass review (routing authority → required)
|
|
51
|
+
|
|
52
|
+
The adversarial reviewer ran six probes at line level with live reproduction: (1) forever-suspect under steady traffic — OBJECT, reproduced (peer healthy at t=40s still suspect at t=10min, 31 forced failovers), fixed in-review (absolute per-episode TTL in `markSuspect`) + regression test; (2) swap-count + pin behavior — no added moves; pinned sessions hold through windows; (3) all-suspect fallback — cannot wedge (`spawnOnMachine` un-gated, throws-to-caller); (4) stale-ack window-clearing — semantically correct for a transport-health breaker; (5) chains-cleanup serialization — identity guard traced clean; (6) ran breaker/router unit + dispatch integration + session-pool e2e suites and tsc — all green. **Verdict: CONCUR with the applied fix.**
|