instar 1.3.705 → 1.3.707

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 (42) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +16 -0
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  5. package/dist/core/PostUpdateMigrator.js +20 -0
  6. package/dist/core/PostUpdateMigrator.js.map +1 -1
  7. package/dist/core/componentCategories.d.ts.map +1 -1
  8. package/dist/core/componentCategories.js +22 -0
  9. package/dist/core/componentCategories.js.map +1 -1
  10. package/dist/core/types.d.ts +6 -0
  11. package/dist/core/types.d.ts.map +1 -1
  12. package/dist/core/types.js.map +1 -1
  13. package/dist/monitoring/guardAcceptedFallbacks.d.ts +48 -0
  14. package/dist/monitoring/guardAcceptedFallbacks.d.ts.map +1 -0
  15. package/dist/monitoring/guardAcceptedFallbacks.js +106 -0
  16. package/dist/monitoring/guardAcceptedFallbacks.js.map +1 -0
  17. package/dist/monitoring/guardManifest.d.ts +25 -0
  18. package/dist/monitoring/guardManifest.d.ts.map +1 -1
  19. package/dist/monitoring/guardManifest.js +48 -0
  20. package/dist/monitoring/guardManifest.js.map +1 -1
  21. package/dist/monitoring/guardPostureView.d.ts +23 -0
  22. package/dist/monitoring/guardPostureView.d.ts.map +1 -1
  23. package/dist/monitoring/guardPostureView.js +66 -0
  24. package/dist/monitoring/guardPostureView.js.map +1 -1
  25. package/dist/monitoring/probes/GuardPostureProbe.d.ts +13 -2
  26. package/dist/monitoring/probes/GuardPostureProbe.d.ts.map +1 -1
  27. package/dist/monitoring/probes/GuardPostureProbe.js +143 -40
  28. package/dist/monitoring/probes/GuardPostureProbe.js.map +1 -1
  29. package/dist/scaffold/templates.d.ts.map +1 -1
  30. package/dist/scaffold/templates.js +1 -0
  31. package/dist/scaffold/templates.js.map +1 -1
  32. package/dist/server/routes.d.ts.map +1 -1
  33. package/dist/server/routes.js +46 -1
  34. package/dist/server/routes.js.map +1 -1
  35. package/package.json +1 -1
  36. package/src/data/builtin-manifest.json +63 -63
  37. package/src/data/state-coherence-registry.json +18 -0
  38. package/src/scaffold/templates.ts +1 -0
  39. package/upgrades/1.3.706.md +80 -0
  40. package/upgrades/1.3.707.md +65 -0
  41. package/upgrades/side-effects/g3-dark-but-load-bearing-guards.md +143 -0
  42. package/upgrades/side-effects/llm-routing-register-uncategorized-components.md +100 -0
@@ -0,0 +1,80 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: minor -->
5
+
6
+ ## What Changed
7
+
8
+ The guard-posture inventory (`GET /guards`) can now express that a DARK feature
9
+ is load-bearing — that a critical path depends on it — instead of treating it as
10
+ an ordinary optional dark feature that sits quiet while the path it should guard
11
+ runs unguarded. This is the guard-layer arm of the ratified "A Dark Feature Guards
12
+ Nothing" standard, and it closes the gap the 2026-07-01 silent-loss postmortem
13
+ named (operator message delivery depended on dark guards, and nothing surfaced it).
14
+
15
+ A guard declared `loadBearing` in the manifest now carries a `criticalPath` label
16
+ on every row, and when it sits silently unguarded it is classified one of three
17
+ ways:
18
+ - `loadBearingGap` — LOUD. A critical path is unguarded. It alerts on its OWN
19
+ attention episode track (`guard-posture-loadbearing:ep-N`), so a long-lived gap
20
+ can never mask an acute load-shed on another guard.
21
+ - `loadBearingSoaking` — a dry-run guard graduating within a bounded, code-constant
22
+ soak window. Surfaced on `/guards` only, no alert; it LAPSES to a loud gap if it
23
+ stalls past the window.
24
+ - `loadBearingAccepted` — an owned, PIN-authenticated operator acceptance is on
25
+ record. Full suppression + a visible accepted-risk row.
26
+
27
+ New route (dashboard-PIN-gated, `owner` + `reason` both required):
28
+ `POST`/`DELETE /guards/:key/accept-fallback`. The initial curated load-bearing set
29
+ is small and conservative: the durable inbound-message queue and the stranded-topic
30
+ sentinel, both on the operator-message-delivery critical path.
31
+
32
+ The classifier stays observe-only — it never gates, blocks, or disables anything,
33
+ and `/guards` is byte-identical for a consumer that ignores the new fields. Rollback
34
+ lever for the alert: `monitoring.guardPostureProbe.alertLoadBearingGaps` (the
35
+ classification stays either way).
36
+
37
+ ## What to Tell Your User
38
+
39
+ I can now tell you when a critical safety system is turned off but something
40
+ important actually depends on it — instead of it quietly staying dark. If that
41
+ happens I will flag it clearly and give you three ways to resolve it: turn the
42
+ guard on, let it finish a short grace period while it is being rolled out, or have
43
+ you formally accept the risk (which I record with your name and reason so it is a
44
+ real decision, not a shrug). Accepting a risk needs your dashboard PIN — I cannot
45
+ do it on your behalf. If you ever ask "why is a critical guard flagged as a gap?",
46
+ that is this feature at work.
47
+
48
+ ## Summary of New Capabilities
49
+
50
+ - `GET /guards` rows gain `loadBearing`, `criticalPath`, `loadBearingGap`,
51
+ `loadBearingSoaking`, `loadBearingAccepted`, and `acceptedFallbackReason`; the
52
+ summary and the capacity heartbeat gain `loadBearingGapKeys`,
53
+ `loadBearingSoakingKeys`, and `loadBearingAcceptedKeys`.
54
+ - `POST /guards/:key/accept-fallback` (dashboard-PIN-gated; `owner` + `reason`
55
+ required) records an owned per-machine acceptance; `DELETE` revokes it and
56
+ reopens the gap.
57
+ - The GuardPostureProbe raises a load-bearing gap on its own bounded, per-episode
58
+ attention topic, separate from the acute load-shed track (no masking).
59
+ - Per-machine by design: accepting a risk on one machine never silences a peer's
60
+ gap; a peer's critical-path label is looked up from the local, fleet-uniform
61
+ manifest.
62
+
63
+ ## Evidence
64
+
65
+ Before: on an agent where a load-bearing feature ships dark (e.g. the inbound-message
66
+ queue with `enabled:false`), `GET /guards` reported it identically to any optional
67
+ dark feature — an `off:dark-default` row with no signal that a critical path depended
68
+ on it, so nothing ever surfaced the exposure (the postmortem's silent-loss shape).
69
+
70
+ After (verified over the real HTTP route + real disk in the Tier-3 e2e): the same
71
+ dark guard's `/guards` row now carries `loadBearing:true`, a `criticalPath` string,
72
+ and `loadBearingGap:true`, and the summary lists it under `loadBearingGapKeys`.
73
+ `POST /guards/:key/accept-fallback` with a valid PIN + owner + reason flips the row to
74
+ `loadBearingAccepted` (gap cleared, reason visible); `DELETE` reopens the gap, and both
75
+ survive a fresh-server "reboot" reading the same durable per-machine record. The
76
+ masking regression drives the REAL `createAttentionItem` funnel and asserts an acute
77
+ off-runtime-divergent guard surfaces while a load-bearing-gap episode has been open —
78
+ and an inert-lever test proves the funnel dedups by item id, so the separate episode
79
+ track (not a healthKey change) is what carries the fix. `npx tsc --noEmit` clean and
80
+ `npm run lint` exit 0.
@@ -0,0 +1,65 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ Thirteen background LLM call sites that were quietly running on the default
9
+ framework (Claude) now route off-Claude with their peers. The framework router
10
+ picks a provider by a component's category; these thirteen were absent from the
11
+ category map, so they resolved to `other` and fell back to Claude — burning
12
+ Anthropic quota while near-identical siblings routed to the cheaper, less
13
+ rate-limited off-Claude providers.
14
+
15
+ They were the pinned `WIRING_EXCLUSIONS` backlog explicitly deferred from the
16
+ token-audit PR ("each needs its own deliberate routing decision"). This is that
17
+ decision: each is registered by function — `InputClassifier`,
18
+ `SessionSummarySentinel`, `TelegramAdapter` (sentinel; `TelegramAdapter` also fixes
19
+ an asymmetry versus the already-registered `SlackAdapter` alert-suppression judge);
20
+ `ResumeValidator` (gate); and `Usher`, `TopicIntentExtractor`, `PreCompactionFlush`,
21
+ `TreeSynthesis`, `LLMConflictResolver`, `openConversationBrief`, `a2a-checkin`,
22
+ `correction-learning`, `mentor-stage-b` (reflector).
23
+
24
+ `WIRING_EXCLUSIONS` now holds only the five components that route via an explicit
25
+ `attribution.category` and are correctly map-unregistered. The existing wiring
26
+ ratchet remains the drift guard against future recurrence. Additive and fully
27
+ reversible; no new logic, no new decision authority.
28
+
29
+ ## What to Tell Your User
30
+
31
+ Thirteen of my small background AI helpers — the ones that classify messages,
32
+ summarize sessions, extract facts before compaction, and similar housekeeping —
33
+ were quietly running on Claude instead of the cheaper providers the rest of my
34
+ background checks use. That was burning Claude subscription quota (the scarce,
35
+ rate-limited resource) on work that never needed it. They now route off Claude
36
+ like their peers, so more of your Claude quota stays available for the
37
+ conversations and heavy work where it matters. Nothing changes in what these
38
+ helpers do — only which provider quietly runs them.
39
+
40
+ ## Summary of New Capabilities
41
+
42
+ None — no new API routes, config keys, or user-facing behavior. This is a
43
+ routing-map completion: 13 existing internal LLM call sites now resolve to their
44
+ proper category (`sentinel`/`gate`/`reflector`) so the existing
45
+ Provider-Fallback Default Policy routes them off Claude. Operators can still
46
+ override any of them per-agent via `sessions.componentFrameworks.overrides`.
47
+
48
+ ## Evidence
49
+
50
+ Before: `GET /intelligence/routing` on a live agent showed these 13 components
51
+ absent from the known-components registry, and `categoryForComponent()` in the
52
+ deployed dist resolved each to `'other'` → the agent default framework (Claude).
53
+ Verified against the deployed code during the 2026-07-01 LLM Routing Registry
54
+ audit (docs/LLM-ROUTING-REGISTRY.md), not just the source branch — each of the
55
+ 13 calls `.evaluate()` with an `attribution.component` that had no map entry and
56
+ no explicit `attribution.category`, while siblings like `SlackAdapter` (mapped)
57
+ routed off-Claude.
58
+
59
+ After: `categoryForComponent('InputClassifier')` → `'sentinel'` (and likewise
60
+ for all 13; `'TelegramAdapter'` now matches the already-mapped `SlackAdapter`).
61
+ The wiring ratchet (`tests/unit/llm-attribution-ratchet.test.ts`) proves both
62
+ directions over the real source tree: every literal `attribution.component` in
63
+ `src/` resolves to a registered category or a pinned explicit-category
64
+ exception, and every remaining exception genuinely resolves to `'other'`. The
65
+ router, integration, and e2e routing suites are green unchanged.
@@ -0,0 +1,143 @@
1
+ # Side-Effects Review — G3 Dark-but-Load-Bearing Guard Classification
2
+
3
+ **Version / slug:** `g3-dark-but-load-bearing-guards`
4
+ **Date:** `2026-07-01`
5
+ **Author:** Echo (autonomous)
6
+ **Spec:** `docs/specs/g3-dark-but-load-bearing-guards.md` (review-convergence 2026-07-02, 6 iterations, cross-model codex GPT-5.5; approved:true)
7
+ **Parent principle:** A Dark Feature Guards Nothing (STANDARDS-REGISTRY.md, ratified 2026-07-01 PR #1316)
8
+ **Second-pass reviewer:** not-required (Tier 2; observe-only classifier + one PIN-gated suppress route; no new gating/blocking decision, byte-identical `/guards` for a consumer ignoring the new fields)
9
+
10
+ ## Summary of the change
11
+
12
+ The `/guards` inventory could not express "this dark feature is load-bearing" — a
13
+ feature that ships DARK but that a CRITICAL PATH depends on was indistinguishable
14
+ from a genuinely-optional dark feature. It sat quiet while the path it should guard
15
+ ran unguarded (the 2026-07-01 silent-loss postmortem's case study: operator message
16
+ delivery depended on dark guards, and nothing surfaced it).
17
+
18
+ G3 adds an optional `loadBearing`/`criticalPath`/`soakWindowDays`/`declaredLoadBearingAt`
19
+ manifest declaration and classifies a load-bearing guard in a silent-unguarded posture
20
+ into ONE of three states — `loadBearingGap` (loud, its OWN attention channel),
21
+ `loadBearingSoaking` (graduate arm, no attention item), `loadBearingAccepted` (owned,
22
+ PIN-authenticated operator acceptance). `criticalPath` travels on EVERY anomaly of a
23
+ load-bearing guard. Pure classifier, observe-only — it never gates.
24
+
25
+ ### Files added
26
+ - `src/monitoring/guardAcceptedFallbacks.ts` — ALL disk I/O for the per-machine
27
+ operator-accept store (`state/guard-accepted-fallbacks.json`, keyed
28
+ `<machineId>:<guardKey>`). Keeps the classifier PURE: the caller reads/scopes
29
+ once and threads the map in. Missing/corrupt file ⇒ empty map (safe direction).
30
+ - Tests: `tests/unit/monitoring/guard-posture-loadbearing.test.ts`,
31
+ `tests/unit/monitoring/guard-posture-probe-loadbearing.test.ts`,
32
+ `tests/integration/guards-accept-fallback-route.test.ts`,
33
+ `tests/e2e/guards-loadbearing-lifecycle.test.ts`,
34
+ `tests/unit/PostUpdateMigrator-guardLoadBearingSection.test.ts`.
35
+
36
+ ### Files modified
37
+ - `src/monitoring/guardManifest.ts` — 4 optional fields; the two NEW manifest lints
38
+ (`validateGuardManifest`); the curated initial load-bearing set (inboundQueue +
39
+ strandedTopicSentinel, both operator-message-delivery critical-path, 30-day soak
40
+ window declared 2026-07-01).
41
+ - `src/monitoring/guardPostureView.ts` — the SIX new row fields + three-state
42
+ precedence (accepted → soaking-within-window → gap) on a threaded `now`; both
43
+ `deriveGuardRow` and `buildGuardInventory` stay PURE (accept map threaded via
44
+ `DeriveInput`/`opts`); `ROW_FIELD_ALLOWLIST` extended with all six by name; summary
45
+ + heartbeat gain the three key-lists.
46
+ - `src/monitoring/probes/GuardPostureProbe.ts` — the SEPARATE `load-bearing-gap`
47
+ episode track (own `openEpisodeId`/`episodeEmitted`/anomalies + item-id namespace
48
+ `guard-posture-loadbearing:ep-N`); acute close condition tests `currentAcute` (not
49
+ the shared set); `load-bearing-gap` class pushed in both evaluate paths; criticalPath
50
+ on all load-bearing anomalies; `alertLoadBearingGaps` sub-flag (default-on).
51
+ - `src/core/types.ts` — the 3 optional key-list fields on `GuardPostureSummary`.
52
+ - `src/server/routes.ts` — GET /guards threads the accept map + now; the PIN-gated
53
+ `POST`/`DELETE /guards/:key/accept-fallback` route (owner+reason required).
54
+ - `src/commands/server.ts` — all 3 `buildGuardInventory` call sites thread the accept
55
+ map + now (route, probe `getLocalPosture`, heartbeat `selfGuardPosture`); the probe's
56
+ `alertLoadBearingGaps` sub-flag resolved from config (default true).
57
+ - `src/scaffold/templates.ts` + `src/core/PostUpdateMigrator.ts` — Agent Awareness +
58
+ Migration Parity (content-sniffed on `loadBearingGap`).
59
+ - `src/data/state-coherence-registry.json` — the new `guard-accepted-fallbacks`
60
+ store category + a `resolution`-class retention policy.
61
+
62
+ ## Decision-point inventory
63
+
64
+ - **Added — one PIN-gated route** (`POST/DELETE /guards/:key/accept-fallback`): the
65
+ ONLY new decision surface. It SUPPRESSES a safety SIGNAL for a named operator; it
66
+ never allows/blocks an action. Gated by `checkMandatePin` (Know Your Principal — a
67
+ Bearer token cannot accept a safety risk); `owner` AND `reason` both REQUIRED (400
68
+ otherwise); a non-load-bearing key is 404.
69
+ - **Added — classifier flags**: `deriveGuardRow` sets orthogonal FLAGS; the nine
70
+ `GuardEffectiveState` values + the normative precedence table are UNCHANGED. No new
71
+ effective state, so the nine-state contract cannot regress.
72
+ - **Added — probe anomaly class** `load-bearing-gap` on its OWN episode track.
73
+ Observe-only: the probe only ever calls the attention funnel; never gates.
74
+ - No agent-to-user message-flow gate is added.
75
+
76
+ ## Roll-up across the seven review dimensions
77
+
78
+ 1. **Over-block**: none. The classifier adds flags; `/guards` is byte-identical for a
79
+ consumer ignoring the new fields. The accept route only suppresses a signal — it
80
+ loosens nothing and blocks nothing. The alert has a rollback sub-flag
81
+ (`alertLoadBearingGaps:false`) that keeps the classification.
82
+ 2. **Under-block**: none. No gate was loosened. A load-bearing dark guard is now MORE
83
+ visible, not less. Soaking only defers the alert within a bounded, code-constant
84
+ window; a typo'd `declaredLoadBearingAt` falls to the LOUD gap (safe direction).
85
+ 3. **Masking**: the round-3/round-4 fix. `load-bearing-gap` is a designed long-lived
86
+ anomaly; running it on a SEPARATE episode track (its own item-id namespace) means a
87
+ week-open gap can never hold the acute track's episode open. The regression test
88
+ drives the REAL `createAttentionItem` funnel and asserts an acute off-runtime-divergent
89
+ surfaces while the lb episode is open; the inert-lever test proves the funnel dedups
90
+ by ID, so a healthKey-only split would leave masking intact.
91
+ 4. **Multi-machine**: classification is per-machine (an accept on machine A never
92
+ silences a peer's gap — proven by test). Manifest fields are fleet-uniform constants;
93
+ a peer's criticalPath is looked up from the local manifest. The heartbeat carries the
94
+ three key-lists (Array.isArray-guarded for an un-upgraded peer). The 3rd-site fix: the
95
+ heartbeat compute threads the local accept map, so an accepted guard never ships to
96
+ peers as a false gap.
97
+ 5. **Purity / I/O layering**: `deriveGuardRow` AND `buildGuardInventory` gain no `fs`.
98
+ The caller reads the accept file ONCE per inventory build and threads the scoped map;
99
+ the new `guardAcceptedFallbacks.ts` module owns all disk I/O. One accept-file read per
100
+ inventory build, never per guard.
101
+ 6. **Bounded Notification Surface**: each track surfaces as ONE bounded, coalesced,
102
+ per-episode class-level forum topic (two class-level topics max, never per-guard).
103
+ Soaking pushes NO attention item.
104
+ 7. **Migration Parity / Agent Awareness**: the manifest ships as code (reaches existing
105
+ agents automatically); the CLAUDE.md vocabulary + accept route reach existing agents
106
+ via a content-sniffed `migrateClaudeMd` addendum; new agents get it in `generateClaudeMd`.
107
+ Migration idempotency + parity covered by a dedicated test.
108
+
109
+ ## Rollout / rollback
110
+
111
+ Classification + route + separate-track wiring ship ALWAYS-ON as pure observability
112
+ (additive, never gating). The probe ALERT on `load-bearing-gap` rides the existing
113
+ GuardPostureProbe enablement + the `monitoring.guardPostureProbe.alertLoadBearingGaps`
114
+ sub-flag (default-on; soak windows are code constants). Rollback = set the sub-flag
115
+ false; `/guards` keeps the classification. The accept store is a per-machine JSON file;
116
+ DELETE scopes to the operator record only, never the manifest soak constant.
117
+
118
+ ## Test evidence
119
+
120
+ `npx tsc --noEmit` clean; full `npm run lint` exit 0. New tests green: 21 unit
121
+ (classification/lints/allowlist/key-lists/purity), 9 probe-track unit (separate track,
122
+ masking regression against the real funnel, inert-lever guard, soaking-silent,
123
+ criticalPath, heartbeat array-guard, per-machine independence, alert rollback), 7
124
+ integration (PIN gate, owner-required, accept clears gap, DELETE-revoke survives reboot),
125
+ 3 e2e (feature-alive: six fields + accept route mounted). Existing guard/posture suites
126
+ (monitoring 260, guard integration+e2e 51, server+migrator 27, parity+discoverability 170)
127
+ all pass.
128
+
129
+ ## Post-review fixes (2026-07-02)
130
+
131
+ - Rebased onto JKHeadley/main after PR #1317 (silent-loss fix) merged. #1317's
132
+ rejected-enumeration + wiring gate on `routes.ts`/`server.ts`/`types.ts` and G3's
133
+ additive edits compose cleanly (adjacent, non-overlapping edits; tsc + `npm run lint`
134
+ clean post-rebase; the guard/posture suite re-run green).
135
+ - CI's full sharded suite caught two mechanical guard-tracking failures the loaded-box
136
+ local run (exit 144) missed. Neither weakens a guard:
137
+ 1. `no-silent-fallbacks` (492 > baseline 491): `readAcceptedFallbacks`'s
138
+ missing/corrupt-file catch in `guardAcceptedFallbacks.ts` is annotated
139
+ `@silent-fallback-ok` — an empty map is the SAFE direction (no phantom accept can
140
+ suppress a real load-bearing gap). Threshold NOT bumped; count back to 491.
141
+ 2. `feature-delivery-completeness`: the migrator's new `loadBearingGap` content-sniff
142
+ section is registered in `legacyMigratorSections` (migrator-only behavioral
143
+ awareness, also emitted inline by `generateClaudeMd` so a fresh init self-matches).
@@ -0,0 +1,100 @@
1
+ # Side-Effects Review — Register 13 uncategorized LLM components
2
+
3
+ **Version / slug:** `llm-routing-register-uncategorized-components`
4
+ **Date:** `2026-07-01`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `not required`
7
+
8
+ ## Summary of the change
9
+
10
+ Adds 13 previously-unregistered LLM call-site component names to `COMPONENT_CATEGORY`
11
+ in `src/core/componentCategories.ts`, each tagged `sentinel` / `gate` / `reflector`
12
+ by function, so the framework router resolves them to a real category instead of
13
+ `other` (which falls back to the agent default framework, Claude). Correspondingly
14
+ removes those 13 names from the `WIRING_EXCLUSIONS` pinned backlog in
15
+ `tests/unit/llm-attribution-ratchet.test.ts`, leaving only the 5 components that
16
+ route via an explicit `attribution.category` (so they correctly stay map-unregistered).
17
+ Files touched: `src/core/componentCategories.ts`, `tests/unit/llm-attribution-ratchet.test.ts`.
18
+ The change interacts with one decision point — the router's category resolution
19
+ (`categoryForComponent`) — but only feeds it more data; it adds no branching logic.
20
+
21
+ ## Decision-point inventory
22
+
23
+ - `categoryForComponent` (src/core/componentCategories.ts) — **pass-through** — unchanged
24
+ function; this change only adds rows to the map it reads. No control-flow edit.
25
+ - `IntelligenceRouter.resolveFramework` (per-component framework routing) — **pass-through** —
26
+ consumes the category; unchanged. The 13 components now resolve to their category's
27
+ configured framework instead of the default, which is the intended effect.
28
+
29
+ ---
30
+
31
+ ## 1. Over-block
32
+
33
+ **What legitimate inputs does this change reject that it shouldn't?**
34
+
35
+ No block/allow surface — over-block not applicable. This is a routing-map data change,
36
+ not a gate. It changes WHICH provider runs a call, never WHETHER the call is allowed.
37
+
38
+ ---
39
+
40
+ ## 2. Under-block
41
+
42
+ **What failure modes does this still miss?**
43
+
44
+ No block/allow surface — under-block not applicable. The change cannot make any gate
45
+ more permissive; gate *authority* is untouched. (The one `gate`-category addition,
46
+ ResumeValidator, keeps its existing behavior; only its provider changes.)
47
+
48
+ ---
49
+
50
+ ## 3. Wrong-provider / routing correctness
51
+
52
+ **Could a component now route to a provider that can't serve it correctly?**
53
+
54
+ Low risk. All 13 join categories (`sentinel`/`gate`/`reflector`) that the
55
+ Provider-Fallback Default Policy already routes off-Claude for dozens of live
56
+ components — the target providers (codex-cli → pi-cli → gemini-cli, Claude last)
57
+ are production-exercised. A rate-limited or missing framework degrades via the
58
+ existing per-framework circuit breaker + fallback chain (unchanged). No component
59
+ routes to a provider class not already in use for its category.
60
+
61
+ The one behavioral nuance: components that make *nuanced/critical* judgments
62
+ (e.g. LLMConflictResolver resolving divergent multi-machine state) now run on a
63
+ non-Claude reasoning-capable model (gpt-5.5 via codex) rather than Claude. This is
64
+ aligned with the operator directive to prefer subsidized non-Claude subscriptions,
65
+ and gpt-5.5 is reasoning-capable. A later nature-based routing pass (operator-reviewed)
66
+ will refine per-task model tiers; this change does not lock that in.
67
+
68
+ ---
69
+
70
+ ## 4. Reversibility / rollback
71
+
72
+ Fully reversible: remove the 13 rows from `COMPONENT_CATEGORY` (and restore them to
73
+ `WIRING_EXCLUSIONS`) and routing returns to the prior state. No migration, no state
74
+ schema change, no persisted data. An operator can also override any single component
75
+ per-agent via `sessions.componentFrameworks.overrides` without touching this map.
76
+
77
+ ---
78
+
79
+ ## 5. Blast radius
80
+
81
+ Fleet-wide once shipped (it's a core map), but strictly additive and category-consistent.
82
+ No new code paths execute; the router simply finds a category where it previously found
83
+ none. Existing tests cover it: the wiring ratchet (`llm-attribution-ratchet.test.ts`)
84
+ proves every live `.evaluate()` component now resolves, `intelligence-router.test.ts` and
85
+ the integration/e2e routing suites remain green. Signal-vs-authority: N/A — no detector
86
+ and no authority is added or modified; this is a data-map completion.
87
+
88
+ ---
89
+
90
+ ## 6. Test coverage
91
+
92
+ - `tests/unit/llm-attribution-ratchet.test.ts` — the drift guard; asserts every literal
93
+ `attribution.component` in `src/` resolves to a registered category or a pinned
94
+ (explicit-category) exception, AND that every pinned exception still resolves to `other`.
95
+ Both sides green after the change.
96
+ - `tests/unit/intelligence-router.test.ts`, `tests/unit/standards-coverage-component-category.test.ts`,
97
+ `tests/integration/intelligence-routing-routes.test.ts`,
98
+ `tests/integration/provider-fallback-default-routing.test.ts` — all green.
99
+ - No NEW test added: the existing ratchet already IS the structural guard for this
100
+ invariant (adding a duplicate would be noise).