pi-smart-router 0.9.0 → 0.9.2

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 (2) hide show
  1. package/README.md +79 -3
  2. package/package.json +8 -1
package/README.md CHANGED
@@ -568,8 +568,57 @@ At runtime, `ClusterMatcher` prefers `routing_centroids` from the calibration bu
568
568
 
569
569
  Below these thresholds the train path returns bootstrap centroids unchanged. The verify script reports `oats_refinement` metadata when refinement ran.
570
570
 
571
+ These guards match `DEFAULT_OATS_MIN_POSITIVE_SAMPLES` / `DEFAULT_OATS_MIN_NEGATIVE_SAMPLES` in `scripts/lib/oats-centroid-refinement.ts` and `MINIMUM_TRAINING_SAMPLES.routing_centroids` (10) in `scripts/calibration-aggregate.ts`. The same global floors appear under `minimum_training_samples` in [`config/routing-calibration.json.example`](config/routing-calibration.json.example) (`hydra_projection` 100, `triage_thresholds` 50, `p_success_weights` / `isotonic_calibrator` 30, `routing_centroids` 10).
572
+
571
573
  See [routing-roadmap.md](docs/routing-roadmap.md) §2 P2 OATS and GitHub [#77](https://github.com/beettlle/pi-smart-router/issues/77).
572
574
 
575
+ ### Privacy-safe label packs + calibration dry-run (SP-189–SP-191 / #102)
576
+
577
+ Offline **label packs** are feature-vector + binary-outcome JSONL (never prompt/message text). Schema: `scripts/lib/label-pack-schema.ts`. Provenance, pins, and field maps: [`tests/eval/corpus/label-packs/PROVENANCE.md`](tests/eval/corpus/label-packs/PROVENANCE.md).
578
+
579
+ **Regenerate packs (offline / no network for CI fixtures):**
580
+
581
+ ```bash
582
+ # SWE-Gym verifier-style → pack
583
+ npm run routing:ingest-swe-gym -- \
584
+ --input tests/eval/corpus/label-packs/swe-gym/ci-fixture.jsonl \
585
+ --output /tmp/swe-gym-pack.jsonl
586
+
587
+ # FC-RewardBench preference / flat → pack
588
+ npm run routing:ingest-fc-rewardbench -- \
589
+ --input tests/eval/corpus/label-packs/fc-rewardbench/ci-fixture.jsonl \
590
+ --output /tmp/fc-rewardbench-pack.jsonl
591
+
592
+ # Optional weak TwinRouterBench tier proxy (exclude from holdout ECE)
593
+ npm run routing:ingest-twinrouterbench-weak -- \
594
+ --input tests/eval/corpus/label-packs/twinrouterbench-weak/ci-fixture.jsonl \
595
+ --output /tmp/trb-weak-pack.jsonl
596
+ ```
597
+
598
+ **Calibration dry-run (holdout ECE):**
599
+
600
+ ```bash
601
+ # CI fixtures (ingests packs in-memory; sample-starved → report-only)
602
+ npm run routing:calibration-dry-run
603
+
604
+ # Operator packs (schema-valid JSONL)
605
+ npm run routing:calibration-dry-run -- --packs /tmp/swe-gym-pack.jsonl /tmp/fc-rewardbench-pack.jsonl
606
+
607
+ # Soft ECE advisory fail (threshold 0.25 calibrated ECE; not a release-gate absolute)
608
+ npm run routing:calibration-dry-run -- --packs /tmp/swe-gym-pack.jsonl --enforce-soft-ece
609
+ ```
610
+
611
+ Dry-run behavior:
612
+
613
+ | Condition | Result |
614
+ |-----------|--------|
615
+ | < 30 ECE-eligible rows | `SAMPLE_STARVED` report-only (exit 0); no soft pass/fail |
616
+ | ≥ 30 ECE-eligible rows | Fit logistic + isotonic; report `holdout_ece_raw` / `holdout_ece_calibrated` |
617
+ | Rows with `exclude_from_holdout_ece` | Counted separately; **never** enter holdout ECE metrics (weak TwinRouterBench) |
618
+ | Soft threshold | Advisory `0.25` calibrated ECE — **does not** change `config/release-gates.json` |
619
+
620
+ **#96 / `modernbert_k4` advisory:** when deciding whether to enable ModernBERT K=4 heads, use **pack holdout ECE / Top-1 error on verifier-grade packs** (SWE-Gym + FC-RewardBench), not fixture-only QR. Weak TwinRouterBench rows are warm-start only. This task does **not** flip `modernbert_k4` defaults.
621
+
573
622
  ### Operator tuning (frugality slider)
574
623
 
575
624
  The multi-objective scoring weights control the cost-vs-quality tradeoff:
@@ -793,11 +842,18 @@ Contributors must run `npm run build` before publishing or consuming the library
793
842
  | `npm run routing:train-calibration` | Train routing calibration artifact bundle |
794
843
  | `npm run routing:train-p-success` | Train standalone `config/p-success-weights.json` (synthetic fixture by default) |
795
844
  | `npm run routing:verify-calibration` | Verify calibration bundle against benchmark prompts |
845
+ | `npm run routing:calibration-dry-run` | Pack-fed isotonic dry-run: holdout ECE on label packs (CI fixtures by default) |
846
+ | `npm run routing:ingest-swe-gym` | Convert SWE-Gym verifier-style JSONL → privacy-safe label pack |
847
+ | `npm run routing:ingest-fc-rewardbench` | Convert FC-RewardBench JSONL → privacy-safe label pack |
848
+ | `npm run routing:ingest-twinrouterbench-weak` | Convert TwinRouterBench weak tier labels → pack (exclude from ECE) |
796
849
  | `npm run routing:ingest-benchmarks` | Regenerate `config/benchmark-profiles.json` from leaderboard fixtures |
797
850
  | `npm run routing:verify-benchmark-profiles` | CI smoke: assert checked-in profiles match fixture ingest |
798
851
  | `npm run routing:eval-replay` | Counterfactual replay on eval trace fixtures |
799
852
  | `npm run routing:eval-harness` | Three-track eval harness (capability, cost, continuity) on fixture traces |
800
853
  | `npm run routing:eval-harness:smoke` | Harness summary JSON only (CI smoke; no network) |
854
+ | `npm run routing:eval-harness:corpus-smoke` | Harness summary on TwinRouterBench CI corpus subset (`tests/eval/corpus/twinrouterbench`) |
855
+ | `npm run routing:assert-release-gates:corpus-report` | Soft-feed: assert corpus vs absolute gates with `--report-only` (exit 0; does not gate releases) |
856
+ | `npm run routing:ingest-twinrouterbench` | Convert TwinRouterBench `question_bank.jsonl` → CI subset / full corpus JSON |
801
857
  | `npm run benchmark:encoder` | Compare MiniLM vs Granite encoder latency on held-out agent turns |
802
858
 
803
859
  ### Offline eval harness (agent-native routing)
@@ -812,9 +868,12 @@ Run locally:
812
868
  # Full metrics JSON (per-fixture + aggregate track summaries)
813
869
  npm run routing:eval-harness
814
870
 
815
- # CI-style summary only
871
+ # CI-style summary only (default fixtures under tests/eval/fixtures)
816
872
  npm run routing:eval-harness:smoke
817
873
 
874
+ # TwinRouterBench CI corpus subset (≤50 code/tool records; offline)
875
+ npm run routing:eval-harness:corpus-smoke
876
+
818
877
  # Custom fixture directory (includes TwinRouterBench static track subdirs)
819
878
  npm run routing:eval-harness -- --fixtures tests/eval/fixtures
820
879
 
@@ -822,11 +881,26 @@ npm run routing:eval-harness -- --fixtures tests/eval/fixtures
822
881
  npm run routing:eval-replay
823
882
  ```
824
883
 
825
- **CI smoke:** `.github/workflows/eval-harness-smoke.yml` runs on PRs that touch eval scripts, fixtures, or the workflow. It executes `routing:eval-harness:smoke` and eval unit tests — fast, offline, no provider network calls.
884
+ **CI smoke:** `.github/workflows/eval-harness-smoke.yml` runs on PRs that touch eval scripts, fixtures, or the workflow. It executes `routing:eval-harness:smoke`, `routing:eval-harness:corpus-smoke`, and eval unit tests — fast, offline, no provider network calls. Job timeout stays at 10 minutes.
826
885
 
827
886
  **TwinRouterBench static track:** import step-level router-visible prefixes with execution-verified target tiers (`track: "static"`). The adapter in `scripts/eval/twinrouterbench-adapter.ts` converts static track records into native eval fixtures for the three-track harness. See `docs/gemini-research.md` §9 for methodology context.
828
887
 
829
- ### Benchmark profile refresh
888
+ #### TwinRouterBench CI corpus (SP-186 / SP-187 / SP-188)
889
+
890
+ | Item | Location / command |
891
+ |------|--------------------|
892
+ | **Pinned upstream** | CommonstackAI/TwinRouterBench `@430acecac71141de77afd8e5e13690d236d58e93` (Apache-2.0) |
893
+ | **CI subset** | `tests/eval/corpus/twinrouterbench/ci-subset.json` (≤50 code/tool records) |
894
+ | **Provenance** | `tests/eval/corpus/twinrouterbench/PROVENANCE.md` |
895
+ | **Regenerate** | `npm run routing:ingest-twinrouterbench -- --input <question_bank.jsonl> --output tests/eval/corpus/twinrouterbench/ci-subset.json --limit 50 --prefer-code-tool` |
896
+ | **Harness smoke** | `npm run routing:eval-harness:corpus-smoke` |
897
+ | **Gate soft-feed** | `npm run routing:assert-release-gates:corpus-report` |
898
+
899
+ **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.
900
+
901
+ **Deferred:** RouterBench classic (outcome-matrix) smoke is out of scope for SP-188; prefer TwinRouterBench static track + dogfood for #95.
902
+
903
+ Sample fixtures under `tests/eval/fixtures/twinrouterbench/` remain the small adapter unit-test inputs and are unchanged by corpus ingest.
830
904
 
831
905
  Capability scores in `config/benchmark-profiles.json` are grounded from public leaderboard snapshots under `tests/fixtures/benchmark-leaderboards/` (and optional **recorded** live snapshots under `tests/fixtures/benchmark-leaderboards/recorded/`). Each artifact records provenance (`source_urls`, `scrape_date`, `catalog_freeze_date`) in its header.
832
906
 
@@ -908,6 +982,8 @@ Tag-triggered publish via GitHub Actions (requires `NPMSECRET` repository secret
908
982
  2. `routing:verify-benchmark-profiles` — checked-in capability profiles match fixture ingest
909
983
  3. `assert-release-gates --fixtures tests/eval/fixtures --baseline-version 0.6.0` — eval harness aggregate metrics vs `config/release-gates.json` and semver baseline regression vs `tests/eval/baselines/v0.6.0.json`
910
984
 
985
+ The TwinRouterBench CI corpus is **not** part of Tier 0: use `routing:eval-harness:corpus-smoke` / `routing:assert-release-gates:corpus-report` for offline public-track soft-feed ([#95](https://github.com/beettlle/pi-smart-router/issues/95)). Absolute thresholds in `config/release-gates.json` stay fixture-backed until operators approve a change.
986
+
911
987
  `release:check` runs the full pre-release path: **live benchmark profile refresh** (fixture fallback; dirty-tree fail), then `verify:ci`, consumer pack verify, then Tier 0 functional smoke.
912
988
 
913
989
  **Baseline re-capture (post-tag):** after shipping a new semver (e.g. v0.7.0), freeze harness metrics for the next regression reference:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-smart-router",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "description": "Auto-model router middleware for the pi.dev coding agent",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -67,9 +67,16 @@
67
67
  "routing:ingest-benchmarks": "tsx scripts/ingest-benchmark-profiles.ts",
68
68
  "routing:verify-benchmark-profiles": "vitest run tests/unit/ingest-benchmark-profiles.test.ts -t \"checked-in artifact matches fixture ingest\"",
69
69
  "routing:verify-calibration": "bash -c 'if [[ \" $* \" == *\" --skip-embed \"* ]]; then vitest run tests/unit/train-routing-calibration.test.ts -t \"verifyRoutingCalibration passes|evaluates benchmark triage gates\"; else npm run build && tsx scripts/verify-routing-calibration.ts \"$@\"; fi' --",
70
+ "routing:calibration-dry-run": "tsx scripts/verify-routing-calibration.ts --dry-run-packs",
70
71
  "routing:eval-replay": "tsx scripts/eval/counterfactual-replay.ts",
71
72
  "routing:eval-harness": "tsx scripts/eval/run-harness.ts",
72
73
  "routing:eval-harness:smoke": "tsx scripts/eval/run-harness.ts --summary-only",
74
+ "routing:eval-harness:corpus-smoke": "tsx scripts/eval/run-harness.ts --fixtures tests/eval/corpus/twinrouterbench --summary-only",
75
+ "routing:assert-release-gates:corpus-report": "tsx scripts/eval/assert-release-gates.ts --fixtures tests/eval/corpus/twinrouterbench --report-only",
76
+ "routing:ingest-twinrouterbench": "tsx scripts/eval/ingest-twinrouterbench-corpus.ts",
77
+ "routing:ingest-swe-gym": "tsx scripts/ingest-swe-gym-labels.ts",
78
+ "routing:ingest-fc-rewardbench": "tsx scripts/ingest-fc-rewardbench-labels.ts",
79
+ "routing:ingest-twinrouterbench-weak": "tsx scripts/ingest-twinrouterbench-weak-labels.ts",
73
80
  "routing:capture-baseline": "tsx scripts/eval/capture-baseline.ts",
74
81
  "routing:test-projection": "npm run build && node --experimental-strip-types scripts/test-hydra-projection.ts",
75
82
  "benchmark:encoder": "tsx scripts/benchmark-encoder-latency.ts",