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.
- package/README.md +36 -18
- package/config/benchmark-profiles.json +2 -2
- package/config/p-success-weights.json +12 -18
- package/config/routing-calibration.json +2806 -0
- package/dist/domain/pinning/session-pinner.js +1 -1
- package/dist/domain/pinning/session-pinner.js.map +1 -1
- package/dist/domain/pipeline/context-fit-stage.d.ts +14 -0
- package/dist/domain/pipeline/context-fit-stage.d.ts.map +1 -0
- package/dist/domain/pipeline/context-fit-stage.js +25 -0
- package/dist/domain/pipeline/context-fit-stage.js.map +1 -0
- package/dist/domain/pipeline/context-overflow-fallback-stage.d.ts +28 -0
- package/dist/domain/pipeline/context-overflow-fallback-stage.d.ts.map +1 -0
- package/dist/domain/pipeline/context-overflow-fallback-stage.js +77 -0
- package/dist/domain/pipeline/context-overflow-fallback-stage.js.map +1 -0
- package/dist/domain/pipeline/hardware-probe-stage.d.ts +12 -0
- package/dist/domain/pipeline/hardware-probe-stage.d.ts.map +1 -0
- package/dist/domain/pipeline/hardware-probe-stage.js +27 -0
- package/dist/domain/pipeline/hardware-probe-stage.js.map +1 -0
- package/dist/domain/pipeline/hydra-match-stage.d.ts +19 -0
- package/dist/domain/pipeline/hydra-match-stage.d.ts.map +1 -0
- package/dist/domain/pipeline/hydra-match-stage.js +178 -0
- package/dist/domain/pipeline/hydra-match-stage.js.map +1 -0
- package/dist/domain/pipeline/local-zero-stage.d.ts +23 -0
- package/dist/domain/pipeline/local-zero-stage.d.ts.map +1 -0
- package/dist/domain/pipeline/local-zero-stage.js +167 -0
- package/dist/domain/pipeline/local-zero-stage.js.map +1 -0
- package/dist/domain/pipeline/low-intensity-stage.d.ts +20 -0
- package/dist/domain/pipeline/low-intensity-stage.d.ts.map +1 -0
- package/dist/domain/pipeline/low-intensity-stage.js +221 -0
- package/dist/domain/pipeline/low-intensity-stage.js.map +1 -0
- package/dist/domain/pipeline/pipeline-stage.d.ts +116 -0
- package/dist/domain/pipeline/pipeline-stage.d.ts.map +1 -0
- package/dist/domain/pipeline/pipeline-stage.js +49 -0
- package/dist/domain/pipeline/pipeline-stage.js.map +1 -0
- package/dist/domain/pipeline/router-pipeline.d.ts +77 -185
- package/dist/domain/pipeline/router-pipeline.d.ts.map +1 -1
- package/dist/domain/pipeline/router-pipeline.js +160 -1232
- package/dist/domain/pipeline/router-pipeline.js.map +1 -1
- package/dist/domain/pipeline/safe-default-stage.d.ts +26 -0
- package/dist/domain/pipeline/safe-default-stage.d.ts.map +1 -0
- package/dist/domain/pipeline/safe-default-stage.js +75 -0
- package/dist/domain/pipeline/safe-default-stage.js.map +1 -0
- package/dist/domain/pipeline/session-pin-stage.d.ts +26 -0
- package/dist/domain/pipeline/session-pin-stage.d.ts.map +1 -0
- package/dist/domain/pipeline/session-pin-stage.js +195 -0
- package/dist/domain/pipeline/session-pin-stage.js.map +1 -0
- package/dist/domain/pipeline/stage-helpers.d.ts +58 -0
- package/dist/domain/pipeline/stage-helpers.d.ts.map +1 -0
- package/dist/domain/pipeline/stage-helpers.js +156 -0
- package/dist/domain/pipeline/stage-helpers.js.map +1 -0
- package/dist/domain/pipeline/triage-stage.d.ts +22 -0
- package/dist/domain/pipeline/triage-stage.d.ts.map +1 -0
- package/dist/domain/pipeline/triage-stage.js +83 -0
- package/dist/domain/pipeline/triage-stage.js.map +1 -0
- package/dist/domain/pipeline/turn-envelope-stage.d.ts +15 -0
- package/dist/domain/pipeline/turn-envelope-stage.d.ts.map +1 -0
- package/dist/domain/pipeline/turn-envelope-stage.js +201 -0
- package/dist/domain/pipeline/turn-envelope-stage.js.map +1 -0
- package/dist/domain/ports/hardware-probe-port.d.ts +100 -0
- package/dist/domain/ports/hardware-probe-port.d.ts.map +1 -0
- package/dist/domain/ports/hardware-probe-port.js +56 -0
- package/dist/domain/ports/hardware-probe-port.js.map +1 -0
- package/dist/domain/ports/local-runtime-port.d.ts +75 -0
- package/dist/domain/ports/local-runtime-port.d.ts.map +1 -0
- package/dist/domain/ports/local-runtime-port.js +115 -0
- package/dist/domain/ports/local-runtime-port.js.map +1 -0
- package/dist/domain/ports/telemetry-emitter-port.d.ts +121 -0
- package/dist/domain/ports/telemetry-emitter-port.d.ts.map +1 -0
- package/dist/domain/ports/telemetry-emitter-port.js +375 -0
- package/dist/domain/ports/telemetry-emitter-port.js.map +1 -0
- package/dist/domain/pricing/price-resolution.d.ts +38 -0
- package/dist/domain/pricing/price-resolution.d.ts.map +1 -0
- package/dist/domain/pricing/price-resolution.js +62 -0
- package/dist/domain/pricing/price-resolution.js.map +1 -0
- package/dist/domain/routing/expected-cost.js +1 -1
- package/dist/domain/routing/expected-cost.js.map +1 -1
- package/dist/domain/routing/p-success-classifier.d.ts +2 -2
- package/dist/domain/routing/p-success-classifier.d.ts.map +1 -1
- package/dist/domain/routing/p-success-classifier.js +20 -16
- package/dist/domain/routing/p-success-classifier.js.map +1 -1
- package/dist/index.d.ts +22 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -6
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/gateway/gateway-dispatch.d.ts.map +1 -1
- package/dist/infrastructure/gateway/gateway-dispatch.js +12 -1
- package/dist/infrastructure/gateway/gateway-dispatch.js.map +1 -1
- package/dist/infrastructure/hardware/hardware-probe.d.ts +8 -21
- package/dist/infrastructure/hardware/hardware-probe.d.ts.map +1 -1
- package/dist/infrastructure/hardware/hardware-probe.js +7 -33
- package/dist/infrastructure/hardware/hardware-probe.js.map +1 -1
- package/dist/infrastructure/hardware/throughput-meter.d.ts +7 -47
- package/dist/infrastructure/hardware/throughput-meter.d.ts.map +1 -1
- package/dist/infrastructure/hardware/throughput-meter.js +5 -0
- package/dist/infrastructure/hardware/throughput-meter.js.map +1 -1
- package/dist/infrastructure/local/local-zero-tier.d.ts +14 -36
- package/dist/infrastructure/local/local-zero-tier.d.ts.map +1 -1
- package/dist/infrastructure/local/local-zero-tier.js +16 -79
- package/dist/infrastructure/local/local-zero-tier.js.map +1 -1
- package/dist/infrastructure/persistence/memory-store.d.ts.map +1 -1
- package/dist/infrastructure/persistence/memory-store.js +9 -3
- package/dist/infrastructure/persistence/memory-store.js.map +1 -1
- package/dist/infrastructure/persistence/sqlite-store.d.ts.map +1 -1
- package/dist/infrastructure/persistence/sqlite-store.js +12 -2
- package/dist/infrastructure/persistence/sqlite-store.js.map +1 -1
- package/dist/infrastructure/pricing/price-broker.d.ts +8 -27
- package/dist/infrastructure/pricing/price-broker.d.ts.map +1 -1
- package/dist/infrastructure/pricing/price-broker.js +7 -51
- package/dist/infrastructure/pricing/price-broker.js.map +1 -1
- package/dist/infrastructure/telemetry/pin-economics-telemetry.d.ts +62 -0
- package/dist/infrastructure/telemetry/pin-economics-telemetry.d.ts.map +1 -0
- package/dist/infrastructure/telemetry/pin-economics-telemetry.js +214 -0
- package/dist/infrastructure/telemetry/pin-economics-telemetry.js.map +1 -0
- package/dist/infrastructure/telemetry/planning-delegate-telemetry.d.ts +21 -0
- package/dist/infrastructure/telemetry/planning-delegate-telemetry.d.ts.map +1 -0
- package/dist/infrastructure/telemetry/planning-delegate-telemetry.js +67 -0
- package/dist/infrastructure/telemetry/planning-delegate-telemetry.js.map +1 -0
- package/dist/infrastructure/telemetry/routing-decision-log.d.ts +50 -0
- package/dist/infrastructure/telemetry/routing-decision-log.d.ts.map +1 -0
- package/dist/infrastructure/telemetry/routing-decision-log.js +163 -0
- package/dist/infrastructure/telemetry/routing-decision-log.js.map +1 -0
- package/dist/infrastructure/telemetry/routing-telemetry.d.ts +28 -211
- package/dist/infrastructure/telemetry/routing-telemetry.d.ts.map +1 -1
- package/dist/infrastructure/telemetry/routing-telemetry.js +32 -936
- package/dist/infrastructure/telemetry/routing-telemetry.js.map +1 -1
- package/dist/infrastructure/telemetry/telemetry-scalar-fields.d.ts +83 -0
- package/dist/infrastructure/telemetry/telemetry-scalar-fields.d.ts.map +1 -0
- package/dist/infrastructure/telemetry/telemetry-scalar-fields.js +184 -0
- package/dist/infrastructure/telemetry/telemetry-scalar-fields.js.map +1 -0
- package/package.json +11 -9
- package/skills/router-release-operator/SKILL.md +21 -9
- package/skills/router-release-operator/references/dependency-freshness.md +67 -0
- package/skills/router-release-operator/references/issue-intake-checklist.md +14 -0
- package/skills/router-release-operator/references/release-manifest-template.md +16 -0
- package/skills/router-release-operator/references/release-profiles.md +11 -5
- package/src/domain/pinning/session-pinner.ts +1 -1
- package/src/domain/pipeline/context-fit-stage.ts +32 -0
- package/src/domain/pipeline/context-overflow-fallback-stage.ts +112 -0
- package/src/domain/pipeline/hardware-probe-stage.ts +31 -0
- package/src/domain/pipeline/hydra-match-stage.ts +242 -0
- package/src/domain/pipeline/local-zero-stage.ts +228 -0
- package/src/domain/pipeline/low-intensity-stage.ts +312 -0
- package/src/domain/pipeline/pipeline-stage.ts +182 -0
- package/src/domain/pipeline/router-pipeline.ts +227 -1613
- package/src/domain/pipeline/safe-default-stage.ts +87 -0
- package/src/domain/pipeline/session-pin-stage.ts +259 -0
- package/src/domain/pipeline/stage-helpers.ts +230 -0
- package/src/domain/pipeline/triage-stage.ts +98 -0
- package/src/domain/pipeline/turn-envelope-stage.ts +300 -0
- package/src/domain/ports/hardware-probe-port.ts +158 -0
- package/src/domain/ports/local-runtime-port.ts +198 -0
- package/src/domain/ports/telemetry-emitter-port.ts +622 -0
- package/src/domain/pricing/price-resolution.ts +84 -0
- package/src/domain/routing/expected-cost.ts +1 -1
- package/src/domain/routing/p-success-classifier.ts +22 -18
- package/src/index.ts +37 -6
- package/src/infrastructure/gateway/gateway-dispatch.ts +12 -1
- package/src/infrastructure/hardware/hardware-probe.ts +26 -68
- package/src/infrastructure/hardware/throughput-meter.ts +19 -50
- package/src/infrastructure/local/local-zero-tier.ts +38 -136
- package/src/infrastructure/persistence/memory-store.ts +10 -2
- package/src/infrastructure/persistence/sqlite-store.ts +13 -2
- package/src/infrastructure/pricing/price-broker.ts +15 -72
- package/src/infrastructure/telemetry/pin-economics-telemetry.ts +353 -0
- package/src/infrastructure/telemetry/planning-delegate-telemetry.ts +101 -0
- package/src/infrastructure/telemetry/routing-decision-log.ts +262 -0
- package/src/infrastructure/telemetry/routing-telemetry.ts +134 -1443
- 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
|
-
>
|
|
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
|
|
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.
|
|
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)).
|
|
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,
|
|
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
|
|
293
|
-
- **npm embedders:** you get the **routing core** (12-stage pipeline, fleet mapping,
|
|
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
|
|
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
|
|
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
|
-
#
|
|
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
|
|
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-
|
|
11
|
-
"catalog_freeze_date": "2026-09-
|
|
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":
|
|
16
|
+
"intercept": 0,
|
|
17
17
|
"coefficients": [
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
0,
|
|
26
|
-
0
|
|
27
|
-
|
|
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":
|
|
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
|
}
|