pi-smart-router 0.22.0 → 1.0.0

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 (168) hide show
  1. package/README.md +36 -18
  2. package/config/benchmark-profiles.json +2 -2
  3. package/config/p-success-weights.json +12 -18
  4. package/config/routing-calibration.json +2806 -0
  5. package/dist/domain/pinning/session-pinner.js +1 -1
  6. package/dist/domain/pinning/session-pinner.js.map +1 -1
  7. package/dist/domain/pipeline/context-fit-stage.d.ts +14 -0
  8. package/dist/domain/pipeline/context-fit-stage.d.ts.map +1 -0
  9. package/dist/domain/pipeline/context-fit-stage.js +25 -0
  10. package/dist/domain/pipeline/context-fit-stage.js.map +1 -0
  11. package/dist/domain/pipeline/context-overflow-fallback-stage.d.ts +28 -0
  12. package/dist/domain/pipeline/context-overflow-fallback-stage.d.ts.map +1 -0
  13. package/dist/domain/pipeline/context-overflow-fallback-stage.js +77 -0
  14. package/dist/domain/pipeline/context-overflow-fallback-stage.js.map +1 -0
  15. package/dist/domain/pipeline/hardware-probe-stage.d.ts +12 -0
  16. package/dist/domain/pipeline/hardware-probe-stage.d.ts.map +1 -0
  17. package/dist/domain/pipeline/hardware-probe-stage.js +27 -0
  18. package/dist/domain/pipeline/hardware-probe-stage.js.map +1 -0
  19. package/dist/domain/pipeline/hydra-match-stage.d.ts +19 -0
  20. package/dist/domain/pipeline/hydra-match-stage.d.ts.map +1 -0
  21. package/dist/domain/pipeline/hydra-match-stage.js +178 -0
  22. package/dist/domain/pipeline/hydra-match-stage.js.map +1 -0
  23. package/dist/domain/pipeline/local-zero-stage.d.ts +23 -0
  24. package/dist/domain/pipeline/local-zero-stage.d.ts.map +1 -0
  25. package/dist/domain/pipeline/local-zero-stage.js +167 -0
  26. package/dist/domain/pipeline/local-zero-stage.js.map +1 -0
  27. package/dist/domain/pipeline/low-intensity-stage.d.ts +20 -0
  28. package/dist/domain/pipeline/low-intensity-stage.d.ts.map +1 -0
  29. package/dist/domain/pipeline/low-intensity-stage.js +221 -0
  30. package/dist/domain/pipeline/low-intensity-stage.js.map +1 -0
  31. package/dist/domain/pipeline/pipeline-stage.d.ts +116 -0
  32. package/dist/domain/pipeline/pipeline-stage.d.ts.map +1 -0
  33. package/dist/domain/pipeline/pipeline-stage.js +49 -0
  34. package/dist/domain/pipeline/pipeline-stage.js.map +1 -0
  35. package/dist/domain/pipeline/router-pipeline.d.ts +77 -185
  36. package/dist/domain/pipeline/router-pipeline.d.ts.map +1 -1
  37. package/dist/domain/pipeline/router-pipeline.js +160 -1232
  38. package/dist/domain/pipeline/router-pipeline.js.map +1 -1
  39. package/dist/domain/pipeline/safe-default-stage.d.ts +26 -0
  40. package/dist/domain/pipeline/safe-default-stage.d.ts.map +1 -0
  41. package/dist/domain/pipeline/safe-default-stage.js +75 -0
  42. package/dist/domain/pipeline/safe-default-stage.js.map +1 -0
  43. package/dist/domain/pipeline/session-pin-stage.d.ts +26 -0
  44. package/dist/domain/pipeline/session-pin-stage.d.ts.map +1 -0
  45. package/dist/domain/pipeline/session-pin-stage.js +195 -0
  46. package/dist/domain/pipeline/session-pin-stage.js.map +1 -0
  47. package/dist/domain/pipeline/stage-helpers.d.ts +58 -0
  48. package/dist/domain/pipeline/stage-helpers.d.ts.map +1 -0
  49. package/dist/domain/pipeline/stage-helpers.js +156 -0
  50. package/dist/domain/pipeline/stage-helpers.js.map +1 -0
  51. package/dist/domain/pipeline/triage-stage.d.ts +22 -0
  52. package/dist/domain/pipeline/triage-stage.d.ts.map +1 -0
  53. package/dist/domain/pipeline/triage-stage.js +83 -0
  54. package/dist/domain/pipeline/triage-stage.js.map +1 -0
  55. package/dist/domain/pipeline/turn-envelope-stage.d.ts +15 -0
  56. package/dist/domain/pipeline/turn-envelope-stage.d.ts.map +1 -0
  57. package/dist/domain/pipeline/turn-envelope-stage.js +201 -0
  58. package/dist/domain/pipeline/turn-envelope-stage.js.map +1 -0
  59. package/dist/domain/ports/hardware-probe-port.d.ts +100 -0
  60. package/dist/domain/ports/hardware-probe-port.d.ts.map +1 -0
  61. package/dist/domain/ports/hardware-probe-port.js +56 -0
  62. package/dist/domain/ports/hardware-probe-port.js.map +1 -0
  63. package/dist/domain/ports/local-runtime-port.d.ts +75 -0
  64. package/dist/domain/ports/local-runtime-port.d.ts.map +1 -0
  65. package/dist/domain/ports/local-runtime-port.js +115 -0
  66. package/dist/domain/ports/local-runtime-port.js.map +1 -0
  67. package/dist/domain/ports/telemetry-emitter-port.d.ts +121 -0
  68. package/dist/domain/ports/telemetry-emitter-port.d.ts.map +1 -0
  69. package/dist/domain/ports/telemetry-emitter-port.js +375 -0
  70. package/dist/domain/ports/telemetry-emitter-port.js.map +1 -0
  71. package/dist/domain/pricing/price-resolution.d.ts +38 -0
  72. package/dist/domain/pricing/price-resolution.d.ts.map +1 -0
  73. package/dist/domain/pricing/price-resolution.js +62 -0
  74. package/dist/domain/pricing/price-resolution.js.map +1 -0
  75. package/dist/domain/routing/expected-cost.js +1 -1
  76. package/dist/domain/routing/expected-cost.js.map +1 -1
  77. package/dist/domain/routing/p-success-classifier.d.ts +2 -2
  78. package/dist/domain/routing/p-success-classifier.d.ts.map +1 -1
  79. package/dist/domain/routing/p-success-classifier.js +20 -16
  80. package/dist/domain/routing/p-success-classifier.js.map +1 -1
  81. package/dist/index.d.ts +22 -6
  82. package/dist/index.d.ts.map +1 -1
  83. package/dist/index.js +19 -6
  84. package/dist/index.js.map +1 -1
  85. package/dist/infrastructure/gateway/gateway-dispatch.d.ts.map +1 -1
  86. package/dist/infrastructure/gateway/gateway-dispatch.js +12 -1
  87. package/dist/infrastructure/gateway/gateway-dispatch.js.map +1 -1
  88. package/dist/infrastructure/hardware/hardware-probe.d.ts +8 -21
  89. package/dist/infrastructure/hardware/hardware-probe.d.ts.map +1 -1
  90. package/dist/infrastructure/hardware/hardware-probe.js +7 -33
  91. package/dist/infrastructure/hardware/hardware-probe.js.map +1 -1
  92. package/dist/infrastructure/hardware/throughput-meter.d.ts +7 -47
  93. package/dist/infrastructure/hardware/throughput-meter.d.ts.map +1 -1
  94. package/dist/infrastructure/hardware/throughput-meter.js +5 -0
  95. package/dist/infrastructure/hardware/throughput-meter.js.map +1 -1
  96. package/dist/infrastructure/local/local-zero-tier.d.ts +14 -36
  97. package/dist/infrastructure/local/local-zero-tier.d.ts.map +1 -1
  98. package/dist/infrastructure/local/local-zero-tier.js +16 -79
  99. package/dist/infrastructure/local/local-zero-tier.js.map +1 -1
  100. package/dist/infrastructure/persistence/memory-store.d.ts.map +1 -1
  101. package/dist/infrastructure/persistence/memory-store.js +9 -3
  102. package/dist/infrastructure/persistence/memory-store.js.map +1 -1
  103. package/dist/infrastructure/persistence/sqlite-store.d.ts.map +1 -1
  104. package/dist/infrastructure/persistence/sqlite-store.js +12 -2
  105. package/dist/infrastructure/persistence/sqlite-store.js.map +1 -1
  106. package/dist/infrastructure/pricing/price-broker.d.ts +8 -27
  107. package/dist/infrastructure/pricing/price-broker.d.ts.map +1 -1
  108. package/dist/infrastructure/pricing/price-broker.js +7 -51
  109. package/dist/infrastructure/pricing/price-broker.js.map +1 -1
  110. package/dist/infrastructure/telemetry/pin-economics-telemetry.d.ts +62 -0
  111. package/dist/infrastructure/telemetry/pin-economics-telemetry.d.ts.map +1 -0
  112. package/dist/infrastructure/telemetry/pin-economics-telemetry.js +214 -0
  113. package/dist/infrastructure/telemetry/pin-economics-telemetry.js.map +1 -0
  114. package/dist/infrastructure/telemetry/planning-delegate-telemetry.d.ts +21 -0
  115. package/dist/infrastructure/telemetry/planning-delegate-telemetry.d.ts.map +1 -0
  116. package/dist/infrastructure/telemetry/planning-delegate-telemetry.js +67 -0
  117. package/dist/infrastructure/telemetry/planning-delegate-telemetry.js.map +1 -0
  118. package/dist/infrastructure/telemetry/routing-decision-log.d.ts +50 -0
  119. package/dist/infrastructure/telemetry/routing-decision-log.d.ts.map +1 -0
  120. package/dist/infrastructure/telemetry/routing-decision-log.js +163 -0
  121. package/dist/infrastructure/telemetry/routing-decision-log.js.map +1 -0
  122. package/dist/infrastructure/telemetry/routing-telemetry.d.ts +28 -211
  123. package/dist/infrastructure/telemetry/routing-telemetry.d.ts.map +1 -1
  124. package/dist/infrastructure/telemetry/routing-telemetry.js +32 -936
  125. package/dist/infrastructure/telemetry/routing-telemetry.js.map +1 -1
  126. package/dist/infrastructure/telemetry/telemetry-scalar-fields.d.ts +83 -0
  127. package/dist/infrastructure/telemetry/telemetry-scalar-fields.d.ts.map +1 -0
  128. package/dist/infrastructure/telemetry/telemetry-scalar-fields.js +184 -0
  129. package/dist/infrastructure/telemetry/telemetry-scalar-fields.js.map +1 -0
  130. package/package.json +11 -9
  131. package/skills/router-release-operator/SKILL.md +21 -9
  132. package/skills/router-release-operator/references/dependency-freshness.md +67 -0
  133. package/skills/router-release-operator/references/issue-intake-checklist.md +14 -0
  134. package/skills/router-release-operator/references/release-manifest-template.md +16 -0
  135. package/skills/router-release-operator/references/release-profiles.md +11 -5
  136. package/src/domain/pinning/session-pinner.ts +1 -1
  137. package/src/domain/pipeline/context-fit-stage.ts +32 -0
  138. package/src/domain/pipeline/context-overflow-fallback-stage.ts +112 -0
  139. package/src/domain/pipeline/hardware-probe-stage.ts +31 -0
  140. package/src/domain/pipeline/hydra-match-stage.ts +242 -0
  141. package/src/domain/pipeline/local-zero-stage.ts +228 -0
  142. package/src/domain/pipeline/low-intensity-stage.ts +312 -0
  143. package/src/domain/pipeline/pipeline-stage.ts +182 -0
  144. package/src/domain/pipeline/router-pipeline.ts +227 -1613
  145. package/src/domain/pipeline/safe-default-stage.ts +87 -0
  146. package/src/domain/pipeline/session-pin-stage.ts +259 -0
  147. package/src/domain/pipeline/stage-helpers.ts +230 -0
  148. package/src/domain/pipeline/triage-stage.ts +98 -0
  149. package/src/domain/pipeline/turn-envelope-stage.ts +300 -0
  150. package/src/domain/ports/hardware-probe-port.ts +158 -0
  151. package/src/domain/ports/local-runtime-port.ts +198 -0
  152. package/src/domain/ports/telemetry-emitter-port.ts +622 -0
  153. package/src/domain/pricing/price-resolution.ts +84 -0
  154. package/src/domain/routing/expected-cost.ts +1 -1
  155. package/src/domain/routing/p-success-classifier.ts +22 -18
  156. package/src/index.ts +37 -6
  157. package/src/infrastructure/gateway/gateway-dispatch.ts +12 -1
  158. package/src/infrastructure/hardware/hardware-probe.ts +26 -68
  159. package/src/infrastructure/hardware/throughput-meter.ts +19 -50
  160. package/src/infrastructure/local/local-zero-tier.ts +38 -136
  161. package/src/infrastructure/persistence/memory-store.ts +10 -2
  162. package/src/infrastructure/persistence/sqlite-store.ts +13 -2
  163. package/src/infrastructure/pricing/price-broker.ts +15 -72
  164. package/src/infrastructure/telemetry/pin-economics-telemetry.ts +353 -0
  165. package/src/infrastructure/telemetry/planning-delegate-telemetry.ts +101 -0
  166. package/src/infrastructure/telemetry/routing-decision-log.ts +262 -0
  167. package/src/infrastructure/telemetry/routing-telemetry.ts +134 -1443
  168. package/src/infrastructure/telemetry/telemetry-scalar-fields.ts +278 -0
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Auto-model router middleware for the [pi](https://pi.dev) coding agent.**
4
4
 
5
- > Current release: **v0.16.2** (mirrors `package.json`; SemVer `0.y.z`). The public API and routing behavior may change until `1.0.0`.
5
+ > **v1.0 — SemVer stable (honest defaults).** From the `1.0.0` release on, the public API and documented operator surface follow [semantic versioning](https://semver.org): breaking changes land only in a new major version, each accompanied by a migration guide — start with [docs/migration-v1.md](docs/migration-v1.md). **1.0 means stable API + honest calibration floors + gates that catch Sept-class failures** — not a claim that cheap-tier routing is behaviorally proven. [#95](https://github.com/beettlle/pi-smart-router/issues/95) / [#110](https://github.com/beettlle/pi-smart-router/issues/110) remain open for verifier-graded recalibration. The published release always mirrors `package.json` ([npm](https://www.npmjs.com/package/pi-smart-router)).
6
6
 
7
7
  pi-smart-router intercepts every LLM inference request and dynamically routes it to the optimal execution engine — balancing cost, capability, latency, and time-to-first-token (TTFT) — without requiring you to manually pick a model for each turn.
8
8
 
@@ -52,7 +52,9 @@ See [docs/PRD.md](docs/PRD.md) for full architectural justification, [docs/deep-
52
52
 
53
53
  | Dependency | Required | Notes |
54
54
  |------------|----------|-------|
55
- | [Node.js](https://nodejs.org/) >= 22 | Yes | ES module package; matches CI and `package.json` engines |
55
+ | [Node.js](https://nodejs.org/) >= 22.19.0 | Yes | ES module package; matches `package.json` `engines.node` and CI (workflows pin Node `22.19.0`) |
56
+
57
+ > **Engine floor:** `package.json` declares `engines.node >= 22.19.0`. If you (or your environment) enable `engine-strict=true` in `.npmrc`, installs on Node < 22.19.0 fail with `EBADENGINE`; on a supported Node (>= 22.19.0), `npm ci` completes with no `EBADENGINE` warnings.
56
58
  | [pi](https://pi.dev) coding agent | Yes | Extension host |
57
59
  | macOS Apple Silicon | MVP | Primary supported platform |
58
60
  | Linux (x64/arm64) | Experimental | Probe logic supported; not validated on real hardware |
@@ -94,7 +96,7 @@ Then in pi:
94
96
  npm install pi-smart-router
95
97
  ```
96
98
 
97
- Use `createRouter()` / `createRouterFromFleet()` for programmatic integration without the pi extension. See [Optional: YAML fleet (library API)](#optional-yaml-fleet-library-api).
99
+ Use `createRouter()` / `createRouterFromFleet()` for programmatic integration without the pi extension. That path is the **catalog / routing-core** composition root (not full extension wiring). See [Library vs extension](#library-vs-extension) and [Optional: YAML fleet (library API)](#optional-yaml-fleet-library-api).
98
100
 
99
101
  ### From source (contributors)
100
102
 
@@ -128,7 +130,7 @@ After auth or model list changes, restart pi or run `/reload`.
128
130
 
129
131
  ### Develop from clone
130
132
 
131
- Requires **Pi ≥ 0.80.8** (`pi.minPiVersion`). The extension loads TypeScript from this repo via pi’s loader — no `npm run build` needed for dogfooding.
133
+ Requires **Pi ≥ 0.85.1** (`pi.minPiVersion`). The extension loads TypeScript from this repo via pi’s loader — no `npm run build` needed for dogfooding.
132
134
 
133
135
  **Recommended (works from any cwd):** install the clone as a **path package** and remove any published npm copy. npm and path packages have different identities; leaving both installed can double-load and you may keep running a stale npm tarball.
134
136
 
@@ -261,7 +263,7 @@ npm run verify:ci
261
263
 
262
264
  ## Concurrency contract
263
265
 
264
- `RouterPipeline.route()` calls on a single router instance are **single-flight**: concurrent calls are serialized internally (SP-230, [#141](https://github.com/beettlle/pi-smart-router/issues/141)). The pipeline keeps per-route transient state on instance fields while stages run, so overlapping executions are queued rather than interleaved — each queued call waits at most one routing latency. This applies to `createRouter()` / `createRouterFromFleet()` handles: a shared `router.dispatch` is safe to call concurrently, and serialization does not change routing policy outcomes. For parallel routing throughput, create separate router instances.
266
+ `RouterPipeline.route()` calls on a single router instance are **single-flight**: concurrent calls are serialized internally (SP-230, [#141](https://github.com/beettlle/pi-smart-router/issues/141)). Each exclusive route owns one per-route `RoutingContext`; overlapping executions are queued rather than interleaved — each queued call waits at most one routing latency. This applies to `createRouter()` / `createRouterFromFleet()` handles: a shared `router.dispatch` is safe to call concurrently, and serialization does not change routing policy outcomes. For parallel routing throughput, create separate router instances.
265
267
 
266
268
  ## Library vs extension
267
269
 
@@ -269,8 +271,8 @@ pi-smart-router ships in two shapes, and they are **not** feature-identical ([#1
269
271
 
270
272
  | Path | What you get |
271
273
  |------|--------------|
272
- | **Pi extension** (`pi install npm:pi-smart-router`, or project-local `.pi/extensions/smart-router/` when developing from clone) | The full product — the routing pipeline **plus** the stream-level behaviors below |
273
- | **npm library** (`createRouter()` / `createRouterFromFleet()` / `GatewayDispatch`) | The routing core — the 12-stage pipeline, fleet mapping, telemetry, and gateway health/failover *selection*. Stream-level behaviors are stubbed or left to your embedder loop |
274
+ | **Pi extension** (`pi install npm:pi-smart-router`, or project-local `.pi/extensions/smart-router/` when developing from clone) — **supported product composition root** | The full product — the routing pipeline **plus** the stream-level behaviors below, with hardware probe + store-backed telemetry wired via `createDispatchOptions()` in `.pi/extensions/smart-router/fleet-bootstrap.ts` |
275
+ | **npm library** (`createRouter()` / `createRouterFromFleet()` / `GatewayDispatch`) — **catalog / embedder composition root** | The routing core — the 12-stage pipeline, fleet mapping, gateway health/failover *selection*, and constructor defaults for **`costEstimator` + `localRuntime` only**. Hardware probe stays disabled and telemetry is opt-in unless you pass those ports. Stream-level behaviors are stubbed or left to your embedder loop |
274
276
 
275
277
  ### Extension-only capabilities
276
278
 
@@ -289,8 +291,8 @@ These behaviors run in `.pi/extensions/smart-router/` and have **no equivalent i
289
291
 
290
292
  ### Recommended integration path
291
293
 
292
- - **pi users:** install the **extension** (`pi install npm:pi-smart-router`). It is the full product — everything in the table above works out of the box, including failover, delegate spawn, headroom escalation, and quota reaction.
293
- - **npm embedders:** you get the **routing core** (12-stage pipeline, fleet mapping, telemetry, gateway health tracking, failover *selection*). Plan to implement your own stream delegation, failover iteration, headroom checks, and planning-delegate spawn around the decisions the pipeline returns — or track [#149](https://github.com/beettlle/pi-smart-router/issues/149) (**extension public facade**), the migration plan for exposing the extension's stream/delegation surface as supported library API so this gap closes over time. Until #149 lands, the extension modules also import `src/**` internals directly, so deep imports into `src/` are not a stable API. See [docs/extension-package-boundary.md](docs/extension-package-boundary.md) for the facade vs internal-API boundary, the deep-import lint guard, and the extension coverage gate.
294
+ - **pi users:** install the **extension** (`pi install npm:pi-smart-router`). It is the supported composition root — everything in the table above works out of the box, including failover, delegate spawn, headroom escalation, quota reaction, hardware probe, and store-backed routing telemetry.
295
+ - **npm embedders:** you get the **routing core** (12-stage pipeline, fleet mapping, gateway health tracking, failover *selection*). Bare `createRouter()` does **not** wire hardware probe or telemetry by default — pass `GatewayDispatchOptions` on `createRouterFromFleet(fleet, options)` when you need those ports. Plan to implement your own stream delegation, failover iteration, headroom checks, and planning-delegate spawn around the decisions the pipeline returns — or track [#149](https://github.com/beettlle/pi-smart-router/issues/149) (**extension public facade**), the migration plan for exposing the extension's stream/delegation surface as supported library API so this gap closes over time. Until #149 lands, the extension modules also import `src/**` internals directly, so deep imports into `src/` are not a stable API. See [docs/extension-package-boundary.md](docs/extension-package-boundary.md) for the facade vs internal-API boundary, the deep-import lint guard, and the extension coverage gate. See also [Composition roots in the 1.0 migration guide](docs/migration-v1.md#composition-roots-library-createrouter-vs-pi-extension).
294
296
 
295
297
  ```text
296
298
  pi extension path (full product) npm library path (routing core)
@@ -323,7 +325,9 @@ To refresh after auth or settings changes, restart pi or `/reload` extensions.
323
325
 
324
326
  ## Optional: YAML fleet (library API)
325
327
 
326
- For programmatic integration **without** the pi extension, load a static fleet catalog from YAML and route via `GatewayDispatch.dispatch()`:
328
+ For programmatic integration **without** the pi extension, load a static fleet catalog from YAML and route via `GatewayDispatch.dispatch()`.
329
+
330
+ This path is **catalog-oriented**: `createRouter()` loads `models.yaml` and constructs `GatewayDispatch` with library defaults (`costEstimator` + `localRuntime`). It does **not** call the extension's `createDispatchOptions()` — so hardware probe remains disabled and no store-backed `telemetryEmitter` is attached unless you pass those options yourself. Prefer the [pi extension](#library-vs-extension) when you want the full product composition root.
327
331
 
328
332
  ```bash
329
333
  cp config/models.yaml.example ./config/models.yaml
@@ -331,13 +335,16 @@ cp config/models.yaml.example ./config/models.yaml
331
335
  ```
332
336
 
333
337
  ```typescript
334
- import { createRouter } from 'pi-smart-router';
338
+ import { createRouter, createRouterFromFleet } from 'pi-smart-router';
335
339
 
336
340
  const router = createRouter({ modelsPath: './config/models.yaml' });
337
341
  router.register(piExtensionHooks); // lifecycle only: compaction + model override
338
342
 
339
343
  const decision = await router.dispatch.dispatch(routingRequest);
340
344
  // Embedder forwards inference to decision.selected_model_id
345
+
346
+ // Optional: wire probe/telemetry yourself (same options bag as GatewayDispatch)
347
+ // createRouterFromFleet(fleet, { systemInfoProvider, hardwareConfig, telemetryEmitter, ... })
341
348
  ```
342
349
 
343
350
  ### Embedder integration paths
@@ -698,18 +705,24 @@ Manual `/smart-router feedback good|bad` is **optional**. Passive dogfood signal
698
705
 
699
706
  | Passive field / signal | Role |
700
707
  |------------------------|------|
701
- | `model_override` | Failure — operator overrode the routed model |
708
+ | `model_override` | Failure — operator overrode the routed model (behavioral) |
702
709
  | `compaction_pin_break` | Neutral/positive context — pin broke at compaction (not a cheap-tier failure by itself) |
703
- | Loop-escalation proxies (`tool_failure_chain`, pin reason `loop_escalation`) | Failure proxies for stuck tool loops |
710
+ | Loop-escalation proxies (`tool_failure_chain`, pin reason `loop_escalation`) | Failure proxies for stuck tool loops (verifier-grade) |
704
711
  | `stop_reason` / `stop_reason_invalid` / `stop_reason_length` | Execution outcome — invalid or truncated stops mark failure |
705
712
 
713
+ The train/aggregate paths derive a label per row (`deriveSuccessLabelFromExportRow` in `src/domain/routing/p-success-classifier.ts`) from the joined `outcome_signals` — **no operator annotation required**:
714
+
715
+ - **Failure** (`success: false`) if any derived failure signal is present: behavioral (`model_override`, `feedback_bad`), verifier proxies (`tool_failure_chain`, `stop_reason_invalid`, `reprompt_detected`, `high_edit_distance`), or execution (`provider_failover`, `stop_reason_length`, `infra_error`).
716
+ - **Success** (`success: true`) on explicit `feedback_good` only.
717
+ - **Unlabeled** (`success: null`, skipped by training) when no outcome signals exist, or when only neutral signals such as `compaction_pin_break` were recorded — never coerced to success.
718
+
706
719
  Optional `feedback_good` / `feedback_bad` only refine labels when the operator chooses to annotate; they are not required for a valid train path. **Do not invent labels** — incomplete exports skip or stay unlabeled rather than fabricating outcomes.
707
720
 
708
721
  **Sample floor:** collect at least **≥30** labeled **economical-tier** rows (`minimum_training_samples.p_success_weights` / `isotonic_calibrator` in [`config/routing-calibration.json.example`](config/routing-calibration.json.example)) before relying on non-neutral `P(success)` or isotonic. Below that floor the classifier returns neutral `P_success_cheap = 0.5`.
709
722
 
710
- **Provenance today vs behavioral adoption:** the checked-in `config/p-success-weights.json` remains **synthetic/fixture** (SP-175 trained on `scripts/fixtures/p-success-synthetic-train.jsonl`, not community dogfood). Treat those weights as an interim dogfood enablement until real passive-signal floors are met and artifacts are retrained/shipped ([#110](https://github.com/beettlle/pi-smart-router/issues/110) train/ship sliceSP-206). Do not claim synthetic rows are behavioral.
723
+ **Provenance today (honest defaults, v1.0.0):** checked-in `config/p-success-weights.json` and `config/routing-calibration.json` ship **neutralized** (`trained_sample_count: 0` for P(success), isotonic, and triage; HyDRA remains 0/≥100). Serve-time routing uses **structural tier hints** and `P_success_cheap = 0.5` until a verifier-graded retrain clears hard ECE gates (`holdout_ece_calibrated ≤ holdout_ece_raw`, absolute ECE ≤ 0.10, non-degenerate `y_knots`). Scripted `scripts/qa/dogfood-gather.sh` labels are **quarantined** (`scripted_intent`) and must not feed ship trains. Bootstrap routing centroids remain in the bundle. TwinRouterBench CI corpus soft-fail (`mean_over_routing_rate 0.87` vs 0.15) is a **harness adapter artifact** ([#112](https://github.com/beettlle/pi-smart-router/issues/112) / [`over-routing-analysis.md`](spine-tasks/_authoring/release-v0.11.0/over-routing-analysis.md)), not a live-pipeline measurement absolute release gates stay fixture-backed; frugality stays. [#95](https://github.com/beettlle/pi-smart-router/issues/95) / [#110](https://github.com/beettlle/pi-smart-router/issues/110) stay open for human dogfood + verifier-graded recalibration.
711
724
 
712
- **SP-206 status (v0.12.0):** **deferred / Partial (B).** Operator had no #95 dogfood exports in this window (labeled economical-tier rows = **0**, floor ≥30). No behavioral `config/p-success-weights.json` or `config/routing-calibration.json` was shipped. See [`spine-tasks/_authoring/release-v0.12.0/behavioral-calibration-partial.md`](spine-tasks/_authoring/release-v0.12.0/behavioral-calibration-partial.md). Leave [#110](https://github.com/beettlle/pi-smart-router/issues/110) open until floors are met; never invent labels.
725
+ **SP-206 / hybrid interim:** July dogfood and a Sept hybrid were explored during the 1.0 train; Sept isotonic collapsed (same-x PAV overwrite) and scripted gather labels failed #110 honesty. Code now pools same-x scores and skips null labels; shipped artifacts stay honest-untrained until post-1.0 verifier-graded trains land.
713
726
 
714
727
  **Zero-manual-label path (aggregate → train → verify):**
715
728
 
@@ -731,6 +744,8 @@ npm run routing:train-calibration -- --input path/to/aggregated.jsonl
731
744
  npm run routing:verify-calibration -- config/routing-calibration.json
732
745
  ```
733
746
 
747
+ Command cross-links: contrib export + aggregation details in [Community telemetry contribution (calibration)](#community-telemetry-contribution-calibration); standalone weights vs full bundle flags in [Operator train / reload](#operator-train--reload-no-prompt-text); OATS centroid refinement inside `routing:train-calibration` in [OATS cluster centroid refinement](#oats-cluster-centroid-refinement-offline-calibration); ECE/holdout gating in [Privacy-safe label packs + calibration dry-run](#privacy-safe-label-packs--calibration-dry-run-sp-189sp-191--102); one-line script summaries in the [Scripts](#scripts) table. The dogfood-side capture checklist lives in [`docs/qa/shadow-dogfood-protocol.md`](docs/qa/shadow-dogfood-protocol.md); the full dogfood → export → aggregate → train loop is diagrammed in [docs/migration-v1.md](docs/migration-v1.md#shadow-dogfood--calibration-behavioral-path).
748
+
734
749
  #### Operator train / reload (no prompt text)
735
750
 
736
751
  ```bash
@@ -742,7 +757,8 @@ SMART_ROUTER_DATASET=1
742
757
  # Train standalone weights (≥30 labeled rows required)
743
758
  npm run routing:train-p-success -- --input path/to/export.jsonl --output config/p-success-weights.json
744
759
 
745
- # Or regenerate the checked-in dogfood weights from the synthetic fixture (interim only):
760
+ # Retrain standalone weights from the synthetic fixture (fixture demo only — NOT behavioral;
761
+ # the checked-in weights are real dogfood, see "Provenance today" above):
746
762
  npm run routing:train-p-success
747
763
 
748
764
  # Optional: merge isotonic into an existing calibration bundle (does not rewrite hydra/centroids)
@@ -755,7 +771,7 @@ npm run routing:train-calibration -- --input path/to/aggregated.jsonl
755
771
 
756
772
  Reload is file-based: replace `config/p-success-weights.json` (and optionally `config/routing-calibration.json` for isotonic) and restart the host agent — no prompt text is ever written into training artifacts.
757
773
 
758
- **Isotonic gap:** serve-time isotonic calibration loads from `config/routing-calibration.json` (`isotonic_calibrator`). The checked-in dogfood path ships trained **logistic** weights only; isotonic is produced when you pass `--calibration-output` or run `routing:train-calibration` with ≥30 labeled samples. Until that bundle exists, the pipeline uses raw logistic `P(success)` (identity / no-op calibrator) and still exposes `p_success_raw` vs `p_success_calibrated` / `p_success_cheap` on explain and telemetry.
774
+ **Isotonic calibration (shipped since v1.0.0):** serve-time isotonic calibration loads from `config/routing-calibration.json` (`isotonic_calibrator`). The checked-in bundle ships an **honest-untrained** calibrator (`trained_sample_count: 0` identity knots), so serve-time `p_success_calibrated` / `p_success_cheap` stay at neutral `0.5` with structural tier hints until a verifier-graded retrain clears hard ECE gates (calibrated ECE ≤ raw, absolute ECE ≤ 0.10, non-degenerate `y_knots`). Operators who retrain should pass `--calibration-output` or run `routing:train-calibration` with ≥30 labeled samples (not `dogfood-gather.sh` scripted_intent). Explain and telemetry still expose `p_success_raw` vs `p_success_calibrated` / `p_success_cheap`.
759
775
 
760
776
  Library helpers (see `src/domain/routing/p-success-classifier.ts`):
761
777
 
@@ -1202,6 +1218,7 @@ npm run routing:twinrouterbench:full-track
1202
1218
  | **Gate soft-feed** | `npm run routing:assert-release-gates:corpus-report` |
1203
1219
  | **Over-routing breakdown** | `npm run routing:analyze-overrouting` · [v0.11.0 analysis](spine-tasks/_authoring/release-v0.11.0/over-routing-analysis.md) (#112 / #95) |
1204
1220
  | **Human QA protocol** | [`docs/qa/shadow-dogfood-protocol.md`](docs/qa/shadow-dogfood-protocol.md) · `npm run qa:shadow-dogfood` |
1221
+ | **Dogfood export → gates soft-feed** | `npm run qa:dogfood-soft-feed -- --export <track-b-export.json>` (dry-run, always exit 0 on gate outcome; never invents labels — #111) |
1205
1222
 
1206
1223
  **Absolute release gates stay on default fixtures.** `npm run release:functional-smoke` continues to assert `tests/eval/fixtures` against `config/release-gates.json` — do not point it at the corpus without operator review. Today the corpus subset fails `mean_over_routing_rate_max` (≈0.85 vs absolute max 0.15); that gap is intentional soft signal for the [#95](https://github.com/beettlle/pi-smart-router/issues/95) public static-track acceptance criteria alongside live dogfood traces. Use `--fixtures tests/eval/corpus/twinrouterbench` (or the corpus-report script) for #95 public-track scoring; keep absolute threshold edits out of band until operators approve. For live shadow dogfood steps and sign-off, see the [shadow dogfood protocol](docs/qa/shadow-dogfood-protocol.md).
1207
1224
 
@@ -1219,7 +1236,7 @@ First-class **local / optional nightly** path for the pinned ~970-row bank. **Do
1219
1236
 
1220
1237
  PR corpus smoke remains the vendored ≤150 subset. Absolute `config/release-gates.json` thresholds and `release:functional-smoke` stay fixture-backed.
1221
1238
 
1222
- **[#95 dual-gate protocol](https://github.com/beettlle/pi-smart-router/issues/95):** (1) live shadow dogfood (`docs/qa/shadow-dogfood-protocol.md` · `npm run qa:shadow-dogfood`) and (2) public static-track soft-feed (CI subset report, or full-track report above). Neither path edits absolute release thresholds.
1239
+ **[#95 dual-gate protocol](https://github.com/beettlle/pi-smart-router/issues/95):** (1) live shadow dogfood (`docs/qa/shadow-dogfood-protocol.md` · `npm run qa:shadow-dogfood`, plus `npm run qa:dogfood-soft-feed` to attach labeled dogfood exports to the gates as a dry-run) and (2) public static-track soft-feed (CI subset report, or full-track report above). Neither path edits absolute release thresholds.
1223
1240
 
1224
1241
  **Deferred:** RouterBench classic (outcome-matrix) smoke is out of scope for SP-188; prefer TwinRouterBench static track + dogfood for #95.
1225
1242
 
@@ -1434,6 +1451,7 @@ Confirm https://pi.dev/packages/pi-smart-router shows the new version (may lag n
1434
1451
 
1435
1452
  | Document | Purpose |
1436
1453
  |----------|---------|
1454
+ | [docs/migration-v1.md](docs/migration-v1.md) | Upgrading to 1.0: raised requirements (pi ≥ 0.85.1, Node ≥ 22.19.0), calibration artifacts, pipeline architecture notes, SemVer stability surface |
1437
1455
  | [docs/PRD.md](docs/PRD.md) | Product requirements, research lineage, pipeline specification |
1438
1456
  | [docs/constitution.md](docs/constitution.md) | Project principles and non-negotiable rules |
1439
1457
  | [specs/001-build-smart-router/spec.md](specs/001-build-smart-router/spec.md) | Detailed feature specification |
@@ -7,8 +7,8 @@
7
7
  "livecodebench": "https://livecodebench.github.io/leaderboard.html",
8
8
  "bfcl": "https://gorilla.cs.berkeley.edu/leaderboard.html"
9
9
  },
10
- "scrape_date": "2026-09-05",
11
- "catalog_freeze_date": "2026-09-05"
10
+ "scrape_date": "2026-09-10",
11
+ "catalog_freeze_date": "2026-09-10"
12
12
  },
13
13
  "aliases": {
14
14
  "anthropic/claude-opus-4": "claude-opus-4-5",
@@ -13,24 +13,18 @@
13
13
  "routing_latency_norm",
14
14
  "economical_tier"
15
15
  ],
16
- "intercept": 4.101841550841884,
16
+ "intercept": 0,
17
17
  "coefficients": [
18
- -3.9458158506645473,
19
- -3.9458158506645473,
20
- -4.6221357618419106,
21
- -4.702014247565688,
22
- -1.9442887494123497,
23
- -2.945430358303044,
24
- -2.8373347243581435,
25
- 0,
26
- 0.09441030879566878,
27
- 4.101841550841884
18
+ 0,
19
+ 0,
20
+ 0,
21
+ 0,
22
+ 0,
23
+ 0,
24
+ 0,
25
+ 0,
26
+ 0,
27
+ 0
28
28
  ],
29
- "trained_sample_count": 40,
30
- "provenance": {
31
- "source": "synthetic_fixture",
32
- "task": "SP-175",
33
- "note": "Privacy-safe feature vectors + labels only; no prompt text.",
34
- "trained_at": "2026-07-10"
35
- }
29
+ "trained_sample_count": 0
36
30
  }