[eric] router: the history pruner announces itself at boot and the backend warns when it is missing, so it can never disable itself in silence

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PNA9xhjw5SkfuB2tJ5KwCj
This commit is contained in:
ciregenz
2026-09-01 15:19:21 -07:00
co-authored by Claude Fable 5.1
parent b683d88c34
commit 81811d3c3e
5 changed files with 182 additions and 8 deletions
+15 -8
View File
@@ -100,19 +100,26 @@ const _http = require('http');
const TARGET_HOSTS = new Set(['api.openai.com', 'api.anthropic.com']);
// ENG-418: continuous history pruning rides the same interceptor. Loaded lazily and fail-open so
// a missing or broken module costs the prune, never the request.
// ENG-418: continuous history pruning rides the same interceptor. Fail-open: a missing or broken
// module costs the prune, never the request. Loaded EAGERLY so the verdict line lands in the router's
// start log at boot, where the backend reads it; a lazy load only spoke on the first request, which
// left a dead pruner indistinguishable from a quiet one until a chat had already paid for it.
let _prune = null;
function historyPrune(bodyStr) {
function loadHistoryPrune() {
try {
if (_prune === null) {
_prune = require(require('path').join(__dirname, '9router_history_prune.js'));
try { process.stderr.write('[history-prune] installed\n'); } catch (_) {}
}
return _prune.maybePrune(bodyStr);
_prune = require(require('path').join(__dirname, '9router_history_prune.js'));
try { process.stderr.write('[history-prune] installed\n'); } catch (_) {}
} catch (_) {
_prune = { maybePrune: (b) => b };
try { process.stderr.write('[history-prune] FAILED to load; requests pass through unpruned\n'); } catch (_) {}
}
}
loadHistoryPrune();
function historyPrune(bodyStr) {
try {
return _prune.maybePrune(bodyStr);
} catch (_) {
try { process.stderr.write('[history-prune] request passed through unpruned (transform threw)\n'); } catch (_) {}
return bodyStr;
}
}
@@ -0,0 +1,57 @@
"""The router's history pruner must never disable itself in silence.
`9router_gpt5_patch.js` loads the pruner eagerly and writes one verdict line to stderr at boot. In a
packaged build that stderr lands only in the router start log, which nothing read, so a pruner that
failed to load looked identical to one that was working. This reads the verdict after a successful
start and says, loudly, what every chat on the machine just lost.
"""
import logging
import os
from typing import Literal
from typeguard import typechecked
logger = logging.getLogger(__name__)
HistoryPruneState = Literal["installed", "failed", "unknown"]
P_INSTALLED_LINE = "[history-prune] installed"
P_FAILED_LINE = "[history-prune] FAILED to load"
@typechecked
def history_prune_state(log_path: str) -> HistoryPruneState:
try:
with open(log_path, "rb") as f:
text = f.read(200_000).decode("utf-8", errors="replace")
except OSError:
return "unknown"
if P_FAILED_LINE in text:
return "failed"
if P_INSTALLED_LINE in text:
return "installed"
return "unknown"
@typechecked
def report_history_prune_state(log_path: str, packaged: bool) -> HistoryPruneState:
"""Packaged builds capture the router's stderr, so silence there means the patch never loaded (the
`--require` flag is dropped when the patch file is missing). Dev sends stderr to DEVNULL, so
silence there is just silence."""
state = history_prune_state(log_path)
if state == "installed":
logger.info("9Router history pruner installed")
return state
if not packaged:
logger.debug("9Router history pruner state unknown (stderr not captured in dev)")
return state
logger.warning(
"9Router history pruner %s: every chat on this machine now resends its FULL tool history "
"on every step, so long chats will hit the context wall (the autocompact-thrash class). "
"Router start log: %s",
"FAILED to load" if state == "failed" else "never announced itself (patch not loaded?)",
log_path,
)
try:
from backend.apps.service.client import submit_diagnostic
submit_diagnostic({"kind": "router", "subkind": f"history_prune_{state}", "log_path": log_path})
except Exception:
logger.debug("submit_diagnostic history_prune_state failed", exc_info=True)
return state
+2
View File
@@ -26,6 +26,7 @@ import time
from typing import Any, Dict, List, Optional
import httpx
from backend.apps.nine_router.history_prune_state import report_history_prune_state
logger = logging.getLogger(__name__)
@@ -667,6 +668,7 @@ async def p_ensure_running_impl():
await asyncio.sleep(0.5)
if is_running():
logger.info("9Router started successfully")
report_history_prune_state(p_cap_path, p_is_packaged)
return
# Verify-at-boot: it never answered. Report with the captured tail + the exit code (non-None = it crashed; None = wedged or just slow).
p_report_start_failure(
+74
View File
@@ -0,0 +1,74 @@
"""The router's pruner may never disable itself in silence (CLAUDE.md, row 6)."""
import logging
import re
from backend.apps.nine_router import history_prune_state as hps
def p_log(tmp_path, text: str) -> str:
p = tmp_path / "start.log"
p.write_bytes(text.encode())
return str(p)
def p_capture(monkeypatch):
records: list = []
handler = logging.Handler()
handler.emit = records.append
hps.logger.addHandler(handler)
hps.logger.setLevel(logging.DEBUG)
monkeypatch.setattr(hps, "logger", hps.logger)
return records, handler
def test_reads_all_three_verdicts(tmp_path) -> None:
assert hps.history_prune_state(p_log(tmp_path, "boot\n[history-prune] installed\n")) == "installed"
assert hps.history_prune_state(p_log(tmp_path, "[history-prune] FAILED to load; requests pass through unpruned\n")) == "failed"
assert hps.history_prune_state(p_log(tmp_path, "boot, nothing about pruning\n")) == "unknown"
assert hps.history_prune_state(str(tmp_path / "missing.log")) == "unknown"
def test_a_failed_pruner_in_a_packaged_build_warns_and_names_what_stopped(tmp_path, monkeypatch) -> None:
records, handler = p_capture(monkeypatch)
try:
state = hps.report_history_prune_state(p_log(tmp_path, "[history-prune] FAILED to load; requests pass through unpruned\n"), packaged=True)
finally:
hps.logger.removeHandler(handler)
assert state == "failed"
warns = [r for r in records if r.levelno >= logging.WARNING]
assert warns, "the fallback must be LOUD"
msg = warns[0].getMessage()
assert "FULL tool history" in msg and "context wall" in msg
def test_silence_in_a_packaged_build_is_also_a_warning(tmp_path, monkeypatch) -> None:
records, handler = p_capture(monkeypatch)
try:
state = hps.report_history_prune_state(p_log(tmp_path, "ready\n"), packaged=True)
finally:
hps.logger.removeHandler(handler)
assert state == "unknown"
assert [r for r in records if r.levelno >= logging.WARNING], "a patch that never announced itself did not load"
def test_installed_and_dev_silence_are_quiet(tmp_path, monkeypatch) -> None:
records, handler = p_capture(monkeypatch)
try:
hps.report_history_prune_state(p_log(tmp_path, "[history-prune] installed\n"), packaged=True)
hps.report_history_prune_state(p_log(tmp_path, "nothing\n"), packaged=False)
finally:
hps.logger.removeHandler(handler)
assert not [r for r in records if r.levelno >= logging.WARNING], "dev captures no stderr, so silence there is not a verdict"
def test_the_report_is_wired_onto_the_start_success_path() -> None:
src = open("backend/apps/nine_router/process.py").read()
ok = src.index('logger.info("9Router started successfully")')
call = src.index("report_history_prune_state(p_cap_path, p_is_packaged)")
ret = src.index("return", call)
assert ok < call < ret, "the verdict is read right after a successful start, before the function returns"
def test_the_patch_loads_the_pruner_eagerly_not_lazily() -> None:
src = open("backend/apps/agents/9router_gpt5_patch.js").read()
assert re.search(r"^loadHistoryPrune\(\);", src, re.M), "a lazy load only speaks on the first request; the boot log would stay empty"
assert src.index("loadHistoryPrune();") < src.index("function historyPrune(")
+34
View File
@@ -225,3 +225,37 @@ test('old SMALL results age too, newest stay sacred (the 32% floor from the real
assert.match(res[i].content[0].text, /nothing to commit/, 'a recent small IS the answer and must survive');
}
});
const { spawnSync } = require('node:child_process');
const fs = require('node:fs');
const os = require('node:os');
const pathMod = require('node:path');
const PATCH_SRC = pathMod.join(__dirname, '..', 'backend', 'apps', 'agents', '9router_gpt5_patch.js');
function bootPatchWith(pruneModuleSource) {
const dir = fs.mkdtempSync(pathMod.join(os.tmpdir(), 'osw-patch-'));
fs.copyFileSync(PATCH_SRC, pathMod.join(dir, '9router_gpt5_patch.js'));
if (pruneModuleSource !== null) fs.writeFileSync(pathMod.join(dir, '9router_history_prune.js'), pruneModuleSource);
const r = spawnSync(process.execPath, ['--require', pathMod.join(dir, '9router_gpt5_patch.js'), '-e', 'process.stdout.write("booted")'], { encoding: 'utf8' });
fs.rmSync(dir, { recursive: true, force: true });
return r;
}
test('the pruner announces itself at LOAD, before any request (liveness, not just correctness)', () => {
const real = fs.readFileSync(pathMod.join(__dirname, '..', 'backend', 'apps', 'agents', '9router_history_prune.js'), 'utf8');
const r = bootPatchWith(real);
assert.strictEqual(r.stdout, 'booted');
assert.match(r.stderr, /\[history-prune\] installed/, 'the installed line must land at boot, with zero requests sent');
});
test('a broken pruner module says so at boot and the router still boots (fail-open, loudly)', () => {
const r = bootPatchWith('throw new Error("simulated broken pruner");');
assert.strictEqual(r.stdout, 'booted', 'a dead pruner must never take the router down with it');
assert.match(r.stderr, /\[history-prune\] FAILED to load; requests pass through unpruned/);
});
test('a MISSING pruner module is the same story as a broken one', () => {
const r = bootPatchWith(null);
assert.strictEqual(r.stdout, 'booted');
assert.match(r.stderr, /FAILED to load/);
});