instar 1.3.329 → 1.3.331

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 (58) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +138 -17
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/core/AnthropicSubscriptionRouter.d.ts +68 -0
  5. package/dist/core/AnthropicSubscriptionRouter.d.ts.map +1 -0
  6. package/dist/core/AnthropicSubscriptionRouter.js +82 -0
  7. package/dist/core/AnthropicSubscriptionRouter.js.map +1 -0
  8. package/dist/core/HttpLeaseTransport.d.ts +24 -0
  9. package/dist/core/HttpLeaseTransport.d.ts.map +1 -1
  10. package/dist/core/HttpLeaseTransport.js +34 -4
  11. package/dist/core/HttpLeaseTransport.js.map +1 -1
  12. package/dist/core/InteractivePoolIntelligenceProvider.d.ts +38 -0
  13. package/dist/core/InteractivePoolIntelligenceProvider.d.ts.map +1 -0
  14. package/dist/core/InteractivePoolIntelligenceProvider.js +61 -0
  15. package/dist/core/InteractivePoolIntelligenceProvider.js.map +1 -0
  16. package/dist/core/PeerFailureLogGate.d.ts +44 -0
  17. package/dist/core/PeerFailureLogGate.d.ts.map +1 -0
  18. package/dist/core/PeerFailureLogGate.js +60 -0
  19. package/dist/core/PeerFailureLogGate.js.map +1 -0
  20. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  21. package/dist/core/PostUpdateMigrator.js +17 -0
  22. package/dist/core/PostUpdateMigrator.js.map +1 -1
  23. package/dist/core/intelligenceProviderFactory.d.ts +21 -0
  24. package/dist/core/intelligenceProviderFactory.d.ts.map +1 -1
  25. package/dist/core/intelligenceProviderFactory.js +24 -1
  26. package/dist/core/intelligenceProviderFactory.js.map +1 -1
  27. package/dist/core/types.d.ts +38 -0
  28. package/dist/core/types.d.ts.map +1 -1
  29. package/dist/core/types.js.map +1 -1
  30. package/dist/providers/adapters/anthropic-interactive-pool/config.d.ts +17 -0
  31. package/dist/providers/adapters/anthropic-interactive-pool/config.d.ts.map +1 -1
  32. package/dist/providers/adapters/anthropic-interactive-pool/config.js +1 -0
  33. package/dist/providers/adapters/anthropic-interactive-pool/config.js.map +1 -1
  34. package/dist/providers/adapters/anthropic-interactive-pool/pool.d.ts +16 -2
  35. package/dist/providers/adapters/anthropic-interactive-pool/pool.d.ts.map +1 -1
  36. package/dist/providers/adapters/anthropic-interactive-pool/pool.js +95 -5
  37. package/dist/providers/adapters/anthropic-interactive-pool/pool.js.map +1 -1
  38. package/dist/providers/bootRegistration.d.ts +83 -0
  39. package/dist/providers/bootRegistration.d.ts.map +1 -0
  40. package/dist/providers/bootRegistration.js +158 -0
  41. package/dist/providers/bootRegistration.js.map +1 -0
  42. package/dist/providers/costAwareRouting.d.ts +16 -0
  43. package/dist/providers/costAwareRouting.d.ts.map +1 -1
  44. package/dist/providers/costAwareRouting.js +37 -21
  45. package/dist/providers/costAwareRouting.js.map +1 -1
  46. package/dist/scaffold/templates.d.ts.map +1 -1
  47. package/dist/scaffold/templates.js +5 -0
  48. package/dist/scaffold/templates.js.map +1 -1
  49. package/dist/server/routes.d.ts.map +1 -1
  50. package/dist/server/routes.js +26 -1
  51. package/dist/server/routes.js.map +1 -1
  52. package/package.json +1 -1
  53. package/src/data/builtin-manifest.json +63 -63
  54. package/src/scaffold/templates.ts +5 -0
  55. package/upgrades/{1.3.329.md → 1.3.330.md} +40 -0
  56. package/upgrades/1.3.331.md +77 -0
  57. package/upgrades/side-effects/mesh-pull-brakes.md +63 -0
  58. package/upgrades/side-effects/provider-substrate-live-wiring.md +145 -0
@@ -0,0 +1,77 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ The provider-portability substrate (both Anthropic adapters + the cost-aware
9
+ routing policy, on main since 2026-05-18) shipped dark: server boot installed
10
+ the routing policy with a literal `readSdkCredit: () => null` stub and
11
+ registered ZERO adapters, while every internal LLM call (sentinels, gates,
12
+ extractors — measured ~1,000 real calls / ~27M input tokens per 24h on one
13
+ agent) hardcoded `claude -p`, the path that bills the Agent SDK credit pot
14
+ after 2026-06-15 and fails with no reroute when it drains. This PR wires the
15
+ substrate into production: (1) `registerAnthropicAdapters()` at server boot —
16
+ gated (codex-only agents register nothing), idempotent (incl. concurrent
17
+ single-flight), lazy (zero spawns at boot), with a TTL-cached real credit
18
+ reader replacing the null stub; (2) a new `intelligence.subscriptionPath.mode`
19
+ config — `off` (default; byte-for-byte today's behavior, argv pinned by test)
20
+ / `auto` (drain the prepaid SDK pot while healthy, fall back to the
21
+ subscription interactive pool when unknown/at-margin, one cross-path fallback
22
+ with DegradationReporter) / `force` (interactive pool ONLY, zero `claude -p` —
23
+ the soak + June-15 emergency lever); (3) `AnthropicSubscriptionRouter` +
24
+ `InteractivePoolIntelligenceProvider` inside the existing breaker wrap, with
25
+ the pure `decideSdkVsSubscription` decision extracted and shared so the two
26
+ routing layers cannot drift; (4) `GET /providers/registry` introspection;
27
+ (5) pool production-hardening — `model` knob (intelligence pool runs haiku),
28
+ poolSize validation, idle retirement (`maxIdleMinutes` was dead config),
29
+ on-demand growth, agent-scoped session prefix + orphan recovery at start().
30
+
31
+ ## What to Tell Your User
32
+
33
+ After June 15, Anthropic changes how background AI calls are billed: the
34
+ "headless" path I use for internal housekeeping (message screening, safety
35
+ checks, summaries) starts drawing from a prepaid credit pot instead of the
36
+ flat subscription. Before this change, when that pot ran dry all my background
37
+ thinking would simply FAIL — silently. Now I have a second lane: I can run
38
+ those same internal calls through a normal interactive Claude session (the
39
+ same kind you chat with me in), which stays on the subscription. There's a
40
+ switch with three positions — today's behavior (default), automatic (use the
41
+ prepaid pot while it's healthy, switch lanes when it runs low), and
42
+ subscription-only (the June-15 emergency lever). Nothing changes for you at
43
+ this release — the switch ships in the OFF position — but the lane now exists,
44
+ is tested, and can be flipped per-agent when the billing change lands.
45
+
46
+ ## Summary of New Capabilities
47
+
48
+ - Boot registration of both Anthropic providers (`headless` + `interactive
49
+ pool`) — lazy, gated, idempotent; routing policy now reads REAL SDK credit
50
+ state (TTL-cached) instead of a hardcoded null.
51
+ - `intelligence.subscriptionPath.mode: off | auto | force` — per-agent control
52
+ of which Anthropic lane internal LLM calls use; `off` is pinned
53
+ byte-for-byte to today's `claude -p` argv by test.
54
+ - `GET /providers/registry` — what is ACTUALLY registered (adapter ids +
55
+ capability flags only), the June-15 readiness diagnostic.
56
+ - Interactive-pool hardening: configurable model (haiku for internal calls),
57
+ poolSize validation, idle-session retirement, on-demand growth, agent-scoped
58
+ tmux prefix + orphan REPL recovery after crashes.
59
+
60
+ ## Evidence
61
+
62
+ Gap measured live on echo: ~1,000 internal `claude -p` calls / ~27M input
63
+ tokens per 24h, all unrouted (boot stub `readSdkCredit: () => null`, registry
64
+ empty — confirmed via the new `/providers/registry` on a pre-fix boot in the
65
+ e2e test). 44+ new tests across all three tiers: unit (router decision
66
+ boundaries both sides, factory argv pin, bootRegistration gating/idempotency/
67
+ laziness, pool model flag + lifecycle hardening), integration
68
+ (`providers-registry-route.test.ts` full HTTP pipeline), e2e
69
+ (`provider-substrate-live-wiring.test.ts` — production-mirroring boot:
70
+ registry populated, route 200, default-off invariance, codex-only gate,
71
+ no-spawn-at-boot). 5-agent adversarial review panel: correctness/wiring
72
+ (BLOCK→fixed: registration TOCTOU single-flight), security/cost (SHIP;
73
+ poolSize validation added), ops/scale (BLOCK→fixed: idle retirement, orphan
74
+ recovery), standards/lessons (BLOCK→fixed: Agent Awareness template +
75
+ migration parity), spec-vs-reality (SHIP — truths T1–T7 all VERIFIED).
76
+ Spec: `docs/specs/provider-substrate-live-wiring.md` (+ `.eli16.md`);
77
+ side-effects: `upgrades/side-effects/provider-substrate-live-wiring.md`.
@@ -0,0 +1,63 @@
1
+ # Side-Effects Review — Mesh Lease-Transport Brakes
2
+
3
+ **Version / slug:** `mesh-pull-brakes`
4
+ **Date:** `2026-06-05`
5
+ **Author:** `Echo (instar-dev agent)`
6
+ **Second-pass reviewer:** `independent adversarial reviewer subagent — CONCUR with one substantive finding (10s timeout default → false self-suspend risk), APPLIED before commit (30s default + leaseTtlMs-derived wiring)`
7
+
8
+ ## Summary of the change
9
+
10
+ `HttpLeaseTransport` (the fenced-lease wire) gains the two P19 brakes its deliberate fixed-cadence callers were missing: per-peer state-change failure logging via the new pure `PeerFailureLogGate` (first / every-Nth / recovery; exact bound ⌈F/N⌉+1 lines per streak; default N=360 ≈ 30min at the 5s pull cadence) replacing one-line-per-attempt (≈17k/day per down peer), with previously-silent non-ok responses now gated-logged; and `AbortSignal.timeout` on both fetches (default 30s; `server.ts` derives `min(leaseTtlMs/2, 30_000)`) closing the hung-socket wedge behind the pull loop's `leasePulling` re-entrancy guard. Files: `PeerFailureLogGate.ts` (new), `HttpLeaseTransport.ts`, one wiring line in `server.ts`, two test files.
11
+
12
+ ## Decision-point inventory
13
+
14
+ - `HttpLeaseTransport.pullPeer` / `broadcast` — **modify (bounded)** — same requests, same return semantics; failures now abort at the timeout instead of hanging forever, and logging is gated.
15
+ - `PeerFailureLogGate` — **add** — a pure log shaper; no authority of any kind.
16
+ - `server.ts` lease-transport construction — **modify (one line)** — passes the config-derived timeout.
17
+
18
+ ## 1. Over-block
19
+
20
+ The one real over-block risk was found BY the reviewer and closed BEFORE commit: a 10s timeout default sat inside the fleet's documented 5–40s receiver-stall envelope; because `LeaseCoordinator.renew()` treats an unconfirmed broadcast past `leaseTtlMs` as "no medium" and self-suspends the holder, a slow-but-alive peer would have falsely demoted a healthy awake machine on its FIRST slow renewal (renewal cadence 120s > TTL 60s leaves no retry headroom). Applied fix: 30s default (above the stall envelope's bulk; a truly hung socket never returns, so the wedge bound is intact) + derivation from `leaseTtlMs` at the construction site so operator-widened TTLs keep proportion. Residual: a peer consistently slower than 30s per request still reads as unreachable — at that point it is functionally unreachable (the TTL itself is 60s).
21
+
22
+ ## 2. Under-block
23
+
24
+ (a) Sibling transports (`HttpLiveTailTransport`, `ReplyMarkerTransport`) share the no-timeout + per-attempt-log pattern at lower blast radius (no lease authority; live-tail already has #867's per-topic backoff bounding attempt rate) — next audit targets <!-- tracked: CMT-1109 -->. (b) A peer removed from the registry mid-failure-streak leaves one stale `Map` entry in the gate (never recovers, never deleted) — bounded by historical peer×op count (reviewer: "not worth fixing, worth knowing"). (c) The gate bounds LOG volume, not attempt volume — attempt cadence is the deliberate anti-blinding design and is out of scope by intent.
25
+
26
+ ## 3. Level-of-abstraction fit
27
+
28
+ Yes. The brakes live in the transport that generates the cost; the cadence stays owned by the caller (`MultiMachineCoordinator`), exactly as the file's contract states. `PeerFailureLogGate` is the established pure-suppressor shape (`AgeKillBackoff`, `SlowRetrySentinelEscalation`) — count-based here (no clock) because the bound should be exact per attempt, not wall-time-dependent. Keying broadcast/pull separately mirrors `isReachable()`'s bidirectional reachability model.
29
+
30
+ ## 4. Signal vs authority compliance
31
+
32
+ **Required reference:** `docs/signal-vs-authority.md`
33
+
34
+ - [x] No — the gate shapes LOG output only. The timeout changes failure TIMING (hang → bounded abort), not failure handling: both callers already treated failures as advisisory data. Lease acquire/renew/suspend semantics are byte-identical; the reviewer traced the renewal path end-to-end to confirm the only semantic risk (timeout sizing) and it was resolved.
35
+
36
+ ## 5. Interactions
37
+
38
+ - **Renewal-requires-medium:** the critical interaction, analyzed in §1 (reviewer probe 1) — resolved by the 30s/config-derived sizing.
39
+ - **Anti-blinding pull loop:** a timed-out pull loses one learning tick and retries in ~5s; never feeds self-suspend. The timeout RESTORES anti-blinding in the hung-socket case (previously wedged forever).
40
+ - **Test clocks:** `AbortSignal.timeout` uses real timers; the injected `now()` drives only reachability windows — orthogonal, no interaction (reviewer probe 2; all suites green).
41
+ - **Log-format consumers:** no test or consumer pinned the old per-attempt strings (reviewer probe 5, grepped src+tests).
42
+ - **Node engines:** `AbortSignal.timeout` needs ≥17.3; package.json engines is ≥20.12 (reviewer probe 4).
43
+
44
+ ## 6. External surfaces
45
+
46
+ - **Logs only** — fewer lines in the failure steady-state, new (bounded) visibility for rejecting peers and recoveries. No API, schema, message, or notification surface. No topic creation (Bounded Notification Surface untouched).
47
+ - **Persistent state / config / migration:** none. The derivation reads the existing `leaseTtlMs`; `requestTimeoutMs`/`failureLogEveryN` are dep options with in-code defaults.
48
+
49
+ ## 7. Rollback cost
50
+
51
+ Revert the commit. No state, no config, no schema. Rollback's only observable effect: the log flood and the hung-socket wedge return.
52
+
53
+ ## Conclusion
54
+
55
+ The transport carrying the mesh's most consequential state (the lease) now satisfies P19: bounded per-attempt cost (log gate), a hard bound on a single attempt's duration (abort timeout), with the deliberately-unbounded cadence left intact and explicitly justified (anti-blinding). The review process did its job visibly: the reviewer's false-self-suspend trace changed the shipped default before commit — recorded in the spec so the sizing rationale survives.
56
+
57
+ ---
58
+
59
+ ## Phase 5 — Second-pass review (lease/coherence-critical → required)
60
+
61
+ An independent adversarial reviewer ran five probes at line level: (1) false unreachability via the renewal path — FOUND the 10s-default risk (traced `tickLease` 120s cadence → `renew()` → `broadcast` → self-suspend at TTL 60s against the fleet's 5–40s stall envelope) and proposed the exact fix that was then applied (30s default + `min(leaseTtlMs/2, 30s)` wired at `server.ts`); (2) AbortSignal real-timers vs injected test clocks — orthogonal, clean; (3) gate key independence + memory bounds — intended and bounded (one stale-key note, accepted); (4) Node engines — satisfied; (5) old-format/silence pins — none in src or tests. Ran the six lease/transport suites + tsc — all green. **Verdict: CONCUR** (with the recommended change, which is now in the diff).
62
+
63
+ **Post-CI note:** the original commit was made before this worktree had its husky shims installed (`pnpm install` not yet run → zero hooks → no decision-audit entry staged) — the decision-audit CI gate caught the bypass exactly as designed (#830). This follow-up commit runs through the full gate and carries the entry.
@@ -0,0 +1,145 @@
1
+ # Side-Effects Review — Provider-Substrate Live Wiring (June-15 readiness PR 1)
2
+
3
+ **Version / slug:** `provider-substrate-live-wiring`
4
+ **Date:** `2026-06-05`
5
+ **Author:** `echo (Claude Opus)`
6
+ **Second-pass reviewer:** `5-agent verification panel (build Phase 3, LARGE)`
7
+
8
+ ## Summary of the change
9
+
10
+ Wires the already-built provider substrate into production: registers both
11
+ Anthropic adapters with the providers registry at server boot (gated,
12
+ idempotent, lazy), plumbs a real TTL-cached SDK-credit reader into the
13
+ CostAwareRoutingPolicy (replacing the `() => null` stub), and adds an
14
+ opt-in `intelligence.subscriptionPath` mode (`off`/`auto`/`force`) that
15
+ routes the internal-intelligence funnel between `claude -p` (SDK-credit
16
+ path) and the interactive REPL pool (subscription floor) per spec 04
17
+ Rule 1. Files: `src/providers/bootRegistration.ts` (new),
18
+ `src/core/InteractivePoolIntelligenceProvider.ts` (new),
19
+ `src/core/AnthropicSubscriptionRouter.ts` (new),
20
+ `src/providers/costAwareRouting.ts` (decision extracted to shared pure fn),
21
+ `src/providers/adapters/anthropic-interactive-pool/{config,pool}.ts`
22
+ (model knob), `src/core/intelligenceProviderFactory.ts` (option),
23
+ `src/core/types.ts` (config type), `src/commands/server.ts` (boot+shutdown
24
+ wiring), `src/server/routes.ts` (GET /providers/registry).
25
+
26
+ ## Decision-point inventory
27
+
28
+ - `registerAnthropicAdapters` gates (claudeForbidden, enabledFrameworks) — add — refuse Claude adapters on codex-only agents
29
+ - `decideSdkVsSubscription` (extracted) — modify (refactor, semantics identical, existing tests pass) — SDK-pot vs subscription threshold
30
+ - `AnthropicSubscriptionRouter.evaluate` mode branch — add — off/auto/force routing of internal LLM calls
31
+ - `buildIntelligenceProvider` claude-code case — modify — wraps with router ONLY when the new option is passed; otherwise byte-identical
32
+ - shutdown pool dispose — add — kills pool tmux sessions at server stop
33
+ - `GET /providers/registry` — add — read-only introspection
34
+
35
+ ## 1. Over-block
36
+
37
+ **What legitimate inputs does this change reject that it shouldn't?**
38
+
39
+ In `force` mode, a pool outage (tmux missing, spawn failure) makes internal
40
+ LLM calls fail even though `claude -p` would have worked — by design (force
41
+ mode's contract is zero `claude -p` traffic), and loudly. Fleet default is
42
+ `off`, so nobody is exposed without an explicit flip. The codex-only gate
43
+ refuses registration on codex-only agents — correct, mirrors the existing
44
+ ClaudeCliIntelligenceProvider guard. No other block/allow surface.
45
+
46
+ ## 2. Under-block
47
+
48
+ **What should be rejected but passes?**
49
+
50
+ `auto` mode falls back across paths on ANY primary error, including
51
+ prompt-shaped errors (e.g. timeout from an oversized prompt) where the
52
+ retry will likely fail again — one wasted call, bounded (exactly one
53
+ fallback attempt, then a loud throw). The pool's `--dangerously-skip-
54
+ permissions` spawn means a prompt-injected judgment call could in principle
55
+ invoke tools inside the pool session; mitigated by the empty scratch
56
+ workdir and unchanged from the adapter's prototyped+parity-tested shape;
57
+ hardening is tracked in the spec (CMT-1105).
58
+
59
+ ## 3. Level-of-abstraction fit
60
+
61
+ Registration lives in `src/providers/` (substrate layer), the two
62
+ IntelligenceProvider implementations in `src/core/` next to their peers,
63
+ and the mode wiring at the composition root (server.ts) — matching the
64
+ existing layering (IntelligenceRouter precedent). The threshold logic was
65
+ EXTRACTED to one shared pure function rather than duplicated across the
66
+ two routing layers.
67
+
68
+ ## 4. Signal vs authority compliance
69
+
70
+ The router treats config (`mode`) as authority and credit snapshots as
71
+ signal: unknown signal degrades conservatively (subscription floor), never
72
+ blocks the call path. onRoute/onDegrade are observability taps with no
73
+ authority. No LLM judgment gates any decision here — all routing is
74
+ deterministic from config + credit state.
75
+
76
+ ## 5. Interactions
77
+
78
+ - **Circuit breaker**: router sits INSIDE the breaker wrap — a rate-limit
79
+ on the surviving path still trips the account-global breaker. Unchanged
80
+ for mode off.
81
+ - **Per-component IntelligenceRouter**: claude-code builds inherit the same
82
+ subscriptionPath option, so codex-default agents with claude-routed
83
+ components stay consistent.
84
+ - **/metrics/features**: pool-served calls still attribute calls+latency;
85
+ token columns read 0 (pool reports no per-call usage; onUsage is
86
+ deliberately not invoked with fake zeros).
87
+ - **SessionReaper/tmux tooling**: pool sessions are named `instar-pool-*`;
88
+ they are adapter-managed (maxIdle 30m, maxMessages 50, dispose at
89
+ shutdown). Reaper does not manage them (they are not instar sessions in
90
+ the session store).
91
+ - **QuotaTracker**: unaffected — file-based scheduler shedding stays as-is.
92
+
93
+ ## 6. External surfaces
94
+
95
+ `GET /providers/registry` (Bearer-authed like its router peers) returns
96
+ adapter ids + capability flag names + a policy-installed boolean. No prompt
97
+ content, no credentials; integration test asserts no key-material shapes in
98
+ the payload. The usage-meter call (`/api/oauth/usage`) is the EXISTING
99
+ read-only observability exception under spec 04 Rule 2, now TTL-cached
100
+ (60s) so routing volume cannot hammer it.
101
+
102
+ ## 7. Rollback cost
103
+
104
+ Mode is config-gated default-off: rollback = remove the config key (or set
105
+ `off`) + restart — no data, no state, no migration. Registration itself is
106
+ inert without the mode (lazy pool, no spawns). Worst-case orphan: pool tmux
107
+ sessions if the server dies UNgracefully mid-soak; bounded by poolSize
108
+ (default 2) and visible via `tmux ls` (`instar-pool-*`).
109
+
110
+ ## Conclusion
111
+
112
+ Ship. Default-off + pinned-argv invariance test means zero fleet behavior
113
+ change at merge; the June-15 lever becomes a config flip backed by 44 new
114
+ tests across all three tiers.
115
+
116
+ ## Second-pass review (if required)
117
+
118
+ Build Phase 3 runs the 5-agent verification panel (LARGE build) before
119
+ commit; findings folded back into code/spec before the convergence tag.
120
+
121
+ ## Evidence pointers
122
+
123
+ - Unit: `tests/unit/providers/bootRegistration.test.ts`,
124
+ `tests/unit/anthropic-subscription-router.test.ts`,
125
+ `tests/unit/intelligence-provider-factory-subscription-path.test.ts`,
126
+ `tests/unit/providers/adapters/anthropic-interactive-pool/pool-model-flag.test.ts`
127
+ - Integration: `tests/integration/providers-registry-route.test.ts`
128
+ - E2E: `tests/e2e/provider-substrate-live-wiring.test.ts`
129
+ - Spec: `docs/specs/provider-substrate-live-wiring.md` (+ `.eli16.md`)
130
+ - Live exposure measurement driving the work: /metrics/features 24h on echo
131
+ (~1,014 real internal calls, ~26.7M tokens-in).
132
+
133
+ ## CI-green follow-up (same PR)
134
+
135
+ - New side effect: a FAILED boot registration/policy install now emits a
136
+ DegradationReporter event (`serverBoot.anthropicProviderRegistration`) in
137
+ addition to the yellow boot log line — so a dark June-15 routing install is
138
+ visible in the degradation feed, not just scrollback. No behavior change on
139
+ the success path.
140
+ - Silent-fallback ratchet lowered 459 → 458 (that catch is now reporter-wired);
141
+ two non-degradation catches carry in-brace `@silent-fallback-ok`
142
+ justifications (raced tmux kill-session; HTTP 500 surfaced to caller).
143
+ - `/providers/registry` tracked in the feature-delivery-completeness guard
144
+ (read-surface class, like `/session/clock`; no framework shadow — the lever
145
+ only applies to claude-code internal traffic).