instar 1.3.779 → 1.3.781
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/dashboard/index.html +161 -0
- package/dist/core/IntelligenceRouter.d.ts +13 -2
- package/dist/core/IntelligenceRouter.d.ts.map +1 -1
- package/dist/core/IntelligenceRouter.js +90 -2
- package/dist/core/IntelligenceRouter.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts +21 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +59 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/routingPriceAuthority.d.ts +0 -0
- package/dist/core/routingPriceAuthority.d.ts.map +1 -0
- package/dist/core/routingPriceAuthority.js +0 -0
- package/dist/core/routingPriceAuthority.js.map +1 -0
- package/dist/core/routingSpendView.d.ts +172 -0
- package/dist/core/routingSpendView.d.ts.map +1 -0
- package/dist/core/routingSpendView.js +215 -0
- package/dist/core/routingSpendView.js.map +1 -0
- package/dist/core/types.d.ts +25 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/data/llmBenchCoverage.d.ts +20 -0
- package/dist/data/llmBenchCoverage.d.ts.map +1 -1
- package/dist/data/llmBenchCoverage.js +38 -0
- package/dist/data/llmBenchCoverage.js.map +1 -1
- package/dist/monitoring/FeatureMetricsLedger.d.ts +74 -2
- package/dist/monitoring/FeatureMetricsLedger.d.ts.map +1 -1
- package/dist/monitoring/FeatureMetricsLedger.js +233 -8
- package/dist/monitoring/FeatureMetricsLedger.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +2 -1
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +1 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +35 -2
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +12 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts +3 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +46 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/scripts/lint-nature-chains.mjs +210 -8
- package/scripts/routing-price-refresh.mjs +216 -0
- package/scripts/routing-prices.manifest.json +64 -0
- package/src/data/builtin-manifest.json +64 -64
- package/src/data/llmBenchCoverage.ts +41 -0
- package/src/scaffold/templates/jobs/instar/routing-price-refresh.md +30 -0
- package/src/scaffold/templates.ts +2 -1
- package/upgrades/1.3.780.md +28 -0
- package/upgrades/1.3.781.md +25 -0
- package/upgrades/side-effects/nature-axis-routing-fd42-rrule-lints.md +63 -0
- package/upgrades/side-effects/routing-spend-increment-a.md +84 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Builds FD4.2 of the (still-dark) nature-axis router (spec: docs/specs/nature-axis-routing.md §296-314): the R-rule structural-exclusion guards R3–R8 that keep a bench-condemned model placement out of the router's authored chains and per-component maps.
|
|
9
|
+
|
|
10
|
+
- **R3/R4/R5/R7 position bans** (src/core/IntelligenceRouter.ts + scripts/lint-nature-chains.mjs): a pure predicate (validateChainPositionRRule) and matching build-lint reject a chain that places qwen-tier in a strict-format position (R3), the gemini-cli door in a JUDGE safety-gate position (R4), gpt-oss-20b / llama-4-scout in a JUDGE position (R5), or a DeepSeek door/model in a JUDGE position (R7). Wired into both the config-load merge and the resolve-time chain check alongside the existing FD4 harness-door ban, so a violating operator override is rejected → built-in default.
|
|
11
|
+
- **R6/R8 component-map pins** (src/data/llmBenchCoverage.ts + the build lint): doc-tree/cartographer components (NATURE_ROUTING_CLAUDE_BANNED_COMPONENTS) may never route to any claude-code door (R6); the input-classifier components (NATURE_ROUTING_INPUT_CLASSIFIER_COMPONENTS) must stay injection-exposed and pinned off the Flash-Lite door (R8, using the just-merged FD5b injection-exposure map). These guard per-component maps a config override can never touch, so they are build-lint-only.
|
|
12
|
+
- A new 18-test unit file covers each of R3–R8 (a compliant chain passes, a crafted violation fails with the right reason), the lint↔validator drift guard, fail-closed behavior, and config-load rejection.
|
|
13
|
+
|
|
14
|
+
NO runtime selection change: the shipped chains already obey every R-rule, so the rejection branches are never taken; the metered doors the rules touch (Flash-Lite, Groq) are already skipped in this increment. Dev-gated / dark: the whole nature block runs only when sessions.natureRouting is enabled; unset/off is byte-identical to before (asserted). This is the guard-lints increment only — NOT the metered-door Increment B, and NOT the go-live flip.
|
|
15
|
+
|
|
16
|
+
## What to Tell Your User
|
|
17
|
+
|
|
18
|
+
This is internal plumbing for how I choose which model runs my own background checks — nothing to turn on, and nothing about our conversations changes. In plain terms: a benchmark found a handful of "never use this model for that job" rules — some cheaper models get fooled by hidden instructions when they act as a safety judge, or mangle the strict output a quick check needs. I turned those rules into build-time guards, so a future change to my settings can never quietly re-introduce one of those bad pairings. The routing feature is still off by default, so today this is invisible — it just seals those unsafe choices shut in advance.
|
|
19
|
+
|
|
20
|
+
## Summary of New Capabilities
|
|
21
|
+
|
|
22
|
+
- **R-rule exclusion guards**: build-enforced rules that keep bench-condemned model-for-job pairings out of my internal routing — a fool-able judge model off a safety-gate, an over-thinking model off a strict-format check, a doc-tree writer off the Claude door, and my message classifiers off the cheapest easily-tricked door.
|
|
23
|
+
- **Config-safe by construction**: an operator routing override that would re-introduce a banned pairing is rejected and falls back to the safe default, so the exclusions survive any future edit.
|
|
24
|
+
|
|
25
|
+
## Evidence
|
|
26
|
+
|
|
27
|
+
- tests/unit/nature-routing-rrule-lints.test.ts (18 tests) — green: the real authored chains + maps pass all R-rules via both the build lint and the TS validator; a crafted violation of each of R3–R8 fails with the correct reason; the lint↔validator drift guard for R3/R4/R5/R7; R6/R8 fail-closed on empty sets; config-load rejection of a violating override; and the combined validator flagging both FD4 and R-rule violations on one chain.
|
|
28
|
+
- npm run lint clean (the extended nature-chains lint passes on the real chains); npx tsc --noEmit clean; the pre-existing byte-identical-when-off block, A1's clamp assertion, the FD5b injection-exposure ratchet, and the FD4 harness-door ratchet all untouched and green.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: minor -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Added **Increment A** of the Routing Control Room — a READ-ONLY spend/caps view (docs/specs/routing-control-room-spend-alerts.md). It turns the immutable per-call token record into dollars by joining a reviewed price manifest ON READ, and lists every metered paid-door key with its published caps and honest not-live/$0 state. It gates nothing and books nothing — the money ledger, the fail-closed cap gate, and the PIN cap controls are Increment B (not built here); alerts are Increment C.
|
|
9
|
+
|
|
10
|
+
- New price authority `src/core/routingPriceAuthority.ts` (Layer 1/1b): loads the git-tracked canonical price manifest (`scripts/routing-prices.manifest.json`) into a regenerable in-memory index, does the as-of price join (a correction supersedes), fails closed on invalid/negative/cached>input prices, computes freshness/stale, and applies reporting-only subsidy/credit/overlay. Never gate-eligible.
|
|
11
|
+
- New composer `src/core/routingSpendView.ts` (Surface 1): priced per-door/model rows + totals, honest `$0 (subscription — not per-token billed)`, loud `unpricedTokens` for a metered door with no matching price, every metered key's caps at $0-committed/not-live, and the Layer-1c cost-basis read contract (`costBasis`/`providerReportedUsd`/`providerDriftPct`, internal-derived + null in Increment A).
|
|
12
|
+
- `FeatureMetricsLedger` gains a nullable `door` column (Layer 0), a maintained `spend_token_rollup` daily aggregate (Layer 2, upsert-on-insert + boot reconcile from raw truth, maintained only where the view is live), a decoupled 400-day rollup retention, and a batched (bounded) retention prune.
|
|
13
|
+
- New Bearer-auth read routes `GET /routing-spend/summary?grain=hour|day|month|total` + `GET /routing-spend/caps`, dev-gated (live on a development agent, dark on the fleet; 503 when off). New read-only **Spend** dashboard tab.
|
|
14
|
+
- OFF-by-default `routing-price-refresh` job + its deterministic prober (`scripts/routing-price-refresh.mjs`) that free-probes public model-list prices and writes ONLY the machine-local observed cache — structurally never the canonical manifest.
|
|
15
|
+
- Migration parity: `migrateConfigRoutingSpendDark` seeds the dark `routingSpend` config block; `ROUTING_SPEND_CLAUDEMD_SECTION` teaches existing agents the surfaces. Tests across all three tiers (unit/integration/e2e).
|
|
16
|
+
|
|
17
|
+
Read-only, dark-on-fleet, reversible: no money controls, no PIN, no Increment-B reach. The converged spec docs (spec + ELI16 + convergence report) are brought to main status-as-is.
|
|
18
|
+
|
|
19
|
+
## What to Tell Your User
|
|
20
|
+
|
|
21
|
+
- **See what your AI routing costs**: "There's a new **Spend** page in your dashboard. It shows what the internal AI calls cost — the token counts are priced against a reviewed price list, so the dollar figures reflow automatically if a price is corrected — and it lists each paid-provider budget cap and its status. Right now no paid provider is switched on, so paid spend shows $0 and your subscription/CLI models correctly show '$0 — not per-token billed'. It's read-only: it shows the numbers, it doesn't change any spending, and there are no money controls yet (those come in a later step)."
|
|
22
|
+
|
|
23
|
+
## Summary of New Capabilities
|
|
24
|
+
|
|
25
|
+
A new read-only spend surface for the internal AI routing — two API routes (`GET /routing-spend/summary`, `GET /routing-spend/caps`) and a dashboard **Spend** tab. It lets an operator see what the internal AI calls cost (immutable token counts priced against a reviewed price list on read) and where each paid-door budget cap sits, without changing any spending behavior. On a development agent it is live; on the fleet it is dark until flipped on.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Side-Effects Review — FD4.2: R-rule structural-exclusion lints (R3–R8)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `nature-axis-routing-fd42-rrule-lints`
|
|
4
|
+
**Date:** `2026-07-05`
|
|
5
|
+
**Author:** `echo (build hand, topic 29723 routing follow-through)`
|
|
6
|
+
**Second-pass reviewer:** `not required (Tier 1 — deterministic static structural exclusions over authored config data + pure candidate-eligibility predicates, dark/dev-gated, byte-identical no-op over the shipped chains; same surface + precedent as the FD4-lints #1388 and FD5b #1393)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Builds FD4.2 of `docs/specs/nature-axis-routing.md` (§296-314): the R-rule structural exclusions R3–R8 over the (still-dark) nature router's authored chains and per-component maps. Files touched:
|
|
11
|
+
|
|
12
|
+
- `src/data/llmBenchCoverage.ts` — two new component sets: `NATURE_ROUTING_CLAUDE_BANNED_COMPONENTS` (R6, doc-tree/cartographer) and `NATURE_ROUTING_INPUT_CLASSIFIER_COMPONENTS` (R8, the input-classifier pins).
|
|
13
|
+
- `src/core/IntelligenceRouter.ts` — the pure R-rule POSITION-ban predicate `validateChainPositionRRule` (R3/R4/R5/R7) + `validateNatureRoutingChainRRules` + a combined `validateNatureRoutingChainAll`; the `NatureChainBanRule` union extended with the four R-rule tags; the combined validator wired into both `resolveRoute`'s chain-rejection and `mergeNatureRoutingChains`'s config-load rejection alongside the existing FD4 ban.
|
|
14
|
+
- `scripts/lint-nature-chains.mjs` — the build lint extended with R3–R8: `rruleViolationForPosition` (mirrors the TS predicate), source-parsing helpers (`extractStringSet`/`extractNatureMap`/`extractExposureMap`), `r6Violations`/`r8Violations`, and `runNatureRuleLints`; `main()` now fails the build on any FD4 OR R-rule violation.
|
|
15
|
+
- Tests — NEW `tests/unit/nature-routing-rrule-lints.test.ts` (18 tests: real chains pass all R-rules via both lint + TS validator; a crafted violation of each of R3–R8 fails with the right reason; the lint↔validator drift guard for R3/R4/R5/R7; R6/R8 fail-closed on empty sets; config-load rejection of a violating override; the combined validator flagging both FD4 and R-rule violations). The pre-existing byte-identical block (`nature-routing-resolver.test.ts:261-296`), A1's clamp assertion, the FD5b ratchet, and the FD4 harness-door ratchet are UNTOUCHED and green.
|
|
16
|
+
|
|
17
|
+
## Decision-point inventory
|
|
18
|
+
|
|
19
|
+
- `FD4.2 R-rule position bans (R3/R4/R5/R7)` — add — pure candidate-eligibility predicates over authored chains; a violating chain is rejected → built-in default (same mechanism as the FD4 ban). NO-OP over the shipped chains (already clean).
|
|
20
|
+
- `FD4.2 R6/R8 component-map pins` — add — build-lint-only structural assertions over per-component maps that a config override can never touch.
|
|
21
|
+
- `resolveRoute chain-rejection` — modify — now runs FD4 + R-rule violations via `validateNatureRoutingChainAll`. Byte-identical for a clean chain (rejection branch untaken).
|
|
22
|
+
- `mergeNatureRoutingChains config-load` — modify — rejects an operator override that violates FD4 OR an R-rule → built-in default + notice.
|
|
23
|
+
- `sessions.natureRouting enable/dryRun gate` — pass-through — the whole nature block still runs ONLY when enabled.
|
|
24
|
+
|
|
25
|
+
## 1. Over-block
|
|
26
|
+
|
|
27
|
+
**What legitimate inputs does this reject that it shouldn't?** Over the shipped chains: none. Every default position passes all R-rules (asserted: "the REAL authored chain map + maps pass ALL R-rule lints"), so no route is removed and no chain is rejected. The rules only reject a chain that PLACES a bench-condemned model in an unsafe slot — a placement no legitimate config authors. R3's model match is `qwen`, R5's is `gpt-oss-20b|llama-4-scout`, R7's is `deepseek` — narrow, bench-cited denylist terms that appear in no shipped or plausible-safe chain; R4 bans only the `gemini-cli` DOOR from the JUDGE chain. False-positive surface is effectively nil.
|
|
28
|
+
|
|
29
|
+
## 2. Under-block
|
|
30
|
+
|
|
31
|
+
**What failure modes does this still miss?** The R-rule set is exactly the spec's R3–R8 — it does not attempt to catch every conceivable bad placement, only the bench-condemned classes. R6 is a structural pin that is vacuously satisfied today (the cartographer component has no nature/chain yet), so it guards against a FUTURE edit rather than fixing a present defect — that is by design (the spec's "makes their exclusion structural so a future edit can't reintroduce them"). R8's flash-lite guarantee is realized by asserting flash-lite stays behind the metered gate (unreachable in Increment A) rather than a per-component resolve-time skip — the per-component walk-skip would be a SELECTION change, which this increment deliberately does not make (out of scope; the metered-skip already delivers the same runtime guarantee in Increment A). The FD7 prompt-anchor semantic-drift lint remains a separate deferred increment.
|
|
32
|
+
|
|
33
|
+
## 3. Level-of-abstraction fit
|
|
34
|
+
|
|
35
|
+
Correct altitude. The R-rule predicates live beside the FD4 ban predicate in `IntelligenceRouter.ts` and ride the identical shape (`NatureChainViolation`, per-position → per-chain → all-chains) and the identical config-load/resolve wiring. The build lint extends the existing `lint-nature-chains.mjs` (the spec's named home for the compile-time place). The two component sets sit beside `NATURE_ROUTING_CRITICAL_GATES` in the same data module. No new engine, config surface, or route.
|
|
36
|
+
|
|
37
|
+
## 4. Signal vs authority compliance
|
|
38
|
+
|
|
39
|
+
Compliant. The R-rule maps/predicates are deterministic build-time DATA + pure predicates (signals), enforced by a lint ratchet — not brittle runtime checks with blocking authority. The resolve/config-load rejection DOES exercise authority (it discards a violating chain → safe default), but only ever in the SAFE direction, on a MODEL-ROUTING decision — it never blocks a user message, never reads or credits a principal, never grants anything. `docs/signal-vs-authority.md` satisfied.
|
|
40
|
+
|
|
41
|
+
## 5. Interactions
|
|
42
|
+
|
|
43
|
+
Interacts with the FD4 ban (orthogonal and composable: `validateNatureRoutingChainAll` = FD4 violations ∪ R-rule violations; a drift-guard/combined test proves both surface independently on one chain). Placed alongside — never shadowing — FD4; the two target different unsafe placements (FD4 = the claude-code harness door; R3/R4/R5/R7 = qwen/gemini-cli/weak-gate/deepseek by chain). The resolver's existing FD5b injection gate and A1 clamp are untouched (their tests stay green). The build lint's new source-parsing reads the same `llmBenchCoverage.ts` the FD4 lint already parses.
|
|
44
|
+
|
|
45
|
+
## 6. External surfaces
|
|
46
|
+
|
|
47
|
+
No external surface. No new HTTP route, CLI command, MCP tool, Telegram/Slack path, or network egress. The lint is a `npm run lint` step (already wired for FD4). No secrets, tokens, or file paths surface anywhere.
|
|
48
|
+
|
|
49
|
+
## 6b. Operator-surface quality
|
|
50
|
+
|
|
51
|
+
No operator-facing surface added or changed. The rejection "notice" path is the pre-existing FD4 `onInvalidChain`/`onReject` callback (internal); nothing new is emitted to a user.
|
|
52
|
+
|
|
53
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
54
|
+
|
|
55
|
+
Machine-local by design, and correctly so. The R-rule predicates and maps are static source data compiled into every machine identically; the validators are pure functions over that data with no persisted or replicated state. Each machine resolves the identical verdict independently — there is no cross-machine state to strand, replicate, or coalesce. No lease, ledger, or notice interaction.
|
|
56
|
+
|
|
57
|
+
## 8. Rollback cost
|
|
58
|
+
|
|
59
|
+
Low and clean. Revert the PR: the two component sets, the R-rule predicates, and the two wiring call sites (reverting `validateNatureRoutingChainAll` back to `validateNatureRoutingChain`) drop out; the lint returns to FD4-only. No migration, no persisted state, no config schema change, no data-format change. Because the R-rules are a no-op over the shipped chains and only run when `natureRouting.enabled`, a rollback is invisible to every fleet agent (feature dark) and to any dev agent in dryRun.
|
|
60
|
+
|
|
61
|
+
## Conclusion
|
|
62
|
+
|
|
63
|
+
FD4.2 ships the R3–R8 structural exclusions as a build lint (extended `lint-nature-chains.mjs`, wired into `npm run lint`) plus resolve-time/config-load rejection for the position bans (R3/R4/R5/R7) mirroring the FD4 ban, and build-lint pins for the component-scoped rules (R6/R8). It changes no runtime selection (the shipped chains are clean; the metered doors it touches are already skipped in Increment A), is byte-identical when the feature is off, and is dark/dev-gated. R8 consumes the just-merged FD5b injection-exposure map. The FD7 prompt-anchor semantic-drift lint and the metered-door Increment B are the tracked next increments, not built here; the spec is NOT marked approved by this change.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Side-Effects Review — Routing Control Room spend/caps VIEW (Increment A)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `routing-spend-increment-a`
|
|
4
|
+
**Date:** `2026-07-05`
|
|
5
|
+
**Author:** `echo`
|
|
6
|
+
**Second-pass reviewer:** `not required` (read-only observability — no block/allow, session-lifecycle, coherence-gate, or sentinel/guard/gate/watchdog surface)
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Ships **Increment A** of the routing-control-room-spend spec: a READ-ONLY spend/caps view. It turns the immutable per-call token record (`feature_metrics`) into dollars by joining a reviewed price manifest ON READ, and lists every metered paid-door key with its published caps and honest not-live/$0 state. It **gates nothing and books nothing** — the authoritative money ledger, the O(1) fail-closed gate, and the PIN cap controls are Increment B (deliberately NOT built here); alerts are Increment C; multi-machine slicing is Increment D. Files: new `src/core/routingPriceAuthority.ts` (Layer 1/1b price + subsidy/credit read composition), `src/core/routingSpendView.ts` (Surface 1 composer), `scripts/routing-prices.manifest.json` (canonical reviewed price manifest), `scripts/routing-price-refresh.mjs` + `src/scaffold/templates/jobs/instar/routing-price-refresh.md` (the OFF-by-default observed-cache refresh job); extended `src/monitoring/FeatureMetricsLedger.ts` (Layer 0 `door` column, Layer 2 `spend_token_rollup` upsert-on-insert + boot reconcile + batched prune); new Bearer-auth dev-gated read routes `GET /routing-spend/summary` + `/routing-spend/caps` in `src/server/routes.ts`; wired in `src/server/AgentServer.ts`; config type + `migrateConfigRoutingSpendDark` + `ROUTING_SPEND_CLAUDEMD_SECTION` (Agent Awareness); a read-only dashboard **Spend** tab; `CapabilityIndex` describe entry. Also brings the converged spec docs (spec + `.eli16` + convergence report) to main status-as-is.
|
|
11
|
+
|
|
12
|
+
## Decision-point inventory
|
|
13
|
+
|
|
14
|
+
- `GET /routing-spend/summary` / `GET /routing-spend/caps` dev-gate — **add** — `resolveDevAgentGate(routingSpend.enabled, config)` → 503 when dark (fleet), live on a dev agent. A read gate only; it decides visibility, never blocks any behavior.
|
|
15
|
+
- Money gate / cap enforcement — **NOT touched** — the O(1) fail-closed money gate is Increment B and is absent here. No `costBasis`, `provider_cost_report`, subsidy, or credit ever feeds a gate (there is no gate to feed).
|
|
16
|
+
- Rollup maintenance flag (`maintainSpendRollup`) — **add** — a construction-time boolean gating whether the daily aggregate is written; not a runtime decision point, bounds fleet blast radius.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 1. Over-block
|
|
21
|
+
|
|
22
|
+
No block/allow surface — over-block not applicable. The only gate is the read-route dev-gate (503 when dark), which is the intended maturation behavior, not an over-block of any input.
|
|
23
|
+
|
|
24
|
+
## 2. Under-block
|
|
25
|
+
|
|
26
|
+
No block/allow surface — under-block not applicable.
|
|
27
|
+
|
|
28
|
+
## 3. Level-of-abstraction fit
|
|
29
|
+
|
|
30
|
+
Correct layer. This is a **reporting/observability composition** layered strictly above the immutable token ledger — the deliberate twin of the existing `natureRoutingMap.ts` (Surface 3) and `/metrics/features` read surfaces. It reuses `FeatureMetricsLedger` (Layer 0/2 token truth), `NATURE_ROUTING_DEFAULT_CHAINS` (which doors are metered), and the `resolveDevAgentGate` funnel — it re-implements none of them. The one deliberate deviation from the spec's letter: the "machine-local read index" for the price manifest is an **in-memory Map rebuilt on manifest mtime change** rather than a SQLite materialized view. For a manifest of a handful of points this is the faithful, non-authoritative, reload-on-change realisation of the spec's intent ("regenerable materialized view, rebuilt on boot AND when the manifest mtime/hash changes"); a separate SQLite substrate at this scale would add ABI/complexity risk for no benefit. The load-bearing invariants (canonical manifest is the reporting authority; the observed cache is kept in a SEPARATE index and is never the canonical source; validation fails closed; day-alignment) are all preserved, and the price-authority API separates the canonical from the observed basis so Increment B's gate can bind to canonical-only.
|
|
31
|
+
|
|
32
|
+
## 4. Signal vs authority compliance
|
|
33
|
+
|
|
34
|
+
**Required reference:** docs/signal-vs-authority.md
|
|
35
|
+
|
|
36
|
+
- [x] **No — this change has no block/allow surface.** It is a pure signal/reporting surface. Every dollar figure is a display value derived on read; nothing it computes gates, blocks, delays, or rewrites any flow. The money authority (the thing with blocking power) is Increment B and is not built here. `FeatureMetricsLedger.record()`'s existing never-throw guarantee is preserved: the new rollup upsert is isolated in its own try/catch after the primary insert and can never affect it.
|
|
37
|
+
|
|
38
|
+
## 5. Interactions
|
|
39
|
+
|
|
40
|
+
- **Shadowing:** none. The new routes are a new prefix (`/routing-spend`), no existing route matches it. The `door` column and `spend_token_rollup` table are additive; `feature_metrics` reads (`/metrics/features`) are untouched.
|
|
41
|
+
- **Double-fire:** none. The rollup upsert runs once per `feature_metrics` insert (llm-kind only), fire-and-forget. The retention prune tick now also prunes the rollup table — a distinct table, no overlap with the raw prune.
|
|
42
|
+
- **Races:** the rollup upsert shares the `feature_metrics` DB with the primary insert but is a separate statement wrapped in its own try/catch; a boot reconcile recomputes the last 30 days from raw truth so a crash-dropped upsert self-heals. The `pruneOlderThan` change from an unbounded `DELETE` to a batched one is a strict improvement (shorter locks), same net effect.
|
|
43
|
+
- **Feedback loops:** none. Reporting reads never write back into token truth.
|
|
44
|
+
|
|
45
|
+
## 6. External surfaces
|
|
46
|
+
|
|
47
|
+
- **Other agents / users:** the read routes are dev-gated (503 on the fleet), so no fleet agent exposes them until flipped. New agents get the `routingSpend` config block + CLAUDE.md awareness section via init; existing agents via `migrateConfigRoutingSpendDark` + `migrateClaudeMd` (Migration Parity honored).
|
|
48
|
+
- **External systems:** none at runtime. The OFF-by-default `routing-price-refresh` job, IF an operator enables it, makes a bounded public HTTP GET to OpenRouter's no-auth `/models` endpoint and writes only the machine-local observed cache — it structurally never touches the canonical manifest (asserted by a unit test) and metered/web-verify probes refuse without a positive budget.
|
|
49
|
+
- **Persistent state:** additive + regenerable — a nullable `door` column and a `spend_token_rollup` table on the existing feature-metrics SQLite DB (rollup maintained only when the dev-gate is live), plus optional machine-local `.instar/routing-prices.{observed,overlay}.json` / `routing-credits.json` (read-only in A, absent by default). No money state, no PIN store (that is Increment B).
|
|
50
|
+
- **Operator surface:** the only operator-facing surface is the **read-only Spend dashboard tab** (see 6b). No PIN/approval/grant/secret action is added — the money controls that would need a phone-completable form are Increment B, out of scope.
|
|
51
|
+
|
|
52
|
+
## 6b. Operator-surface quality (Operator-Surface Quality standard)
|
|
53
|
+
|
|
54
|
+
The change adds a read-only dashboard **Spend** tab (`dashboard/index.html`). It has no operator actions (no grant/revoke/adjust/go-live — those are Increment B).
|
|
55
|
+
|
|
56
|
+
1. **Leads with the primary action?** Yes — the tab opens directly onto the headline spend totals + the by-door/model and caps tables; the only control is a grain selector + Refresh. There is no action to lead with (read-only), and the answer the operator came for (what am I spending / where do the caps sit) is the first content.
|
|
57
|
+
2. **Zero raw internals as primary content?** Yes — headline is plain-language stat tiles (Gross / Net / Committed / Tokens); dollar figures and human labels are primary; `keyRef`/`door`/`priceBasis` appear only as de-emphasized support columns, not headline content.
|
|
58
|
+
3. **Destructive actions de-emphasized?** N/A — there are no destructive (or any) actions on this read-only tab.
|
|
59
|
+
4. **Plain language + phone width?** Labels read plainly ("Paid-door caps", "not live", "$0 (subscription — not per-token billed)"); tables live inside `overflow-x:auto` containers so wide content scrolls within its own box rather than breaking the page; the 503/dark state renders a friendly "not enabled on this agent" message, not an error.
|
|
60
|
+
|
|
61
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
62
|
+
|
|
63
|
+
**proxied-on-read.** Token ground truth (`feature_metrics` + `spend_token_rollup`) is recorded per-machine (each machine records its own internal LLM calls), exactly like the existing `FeatureMetricsLedger`/`TokenLedger` posture. The operator-facing spend NUMBER is unified by a future `?scope=pool` fan-out (the `GET /guards?scope=pool` / `GET /subscription-pool?scope=pool` model), which Increment A does not yet implement — the routes serve the local machine's rollup honestly (the `adjustmentsSource`/`reportingBasis` labels make the source explicit). The canonical price manifest is `unified` (git-tracked, identical everywhere); the observed cache / overlay / credits are machine-local BY DESIGN (they are reporting-only operator observations that never reach a gate, so replicating them would put deal terms on every disk for zero enforcement benefit — declared in the spec's Multi-machine posture). No user-facing notices are emitted (no one-voice concern). No durable state strands on topic transfer (the rollup is per-machine spend history, not topic-scoped). No URLs are generated. A single-machine agent is a clean no-op; a fresh agent with no manifest degrades to honest `$0`/no-provider-data.
|
|
64
|
+
|
|
65
|
+
## 8. Rollback cost
|
|
66
|
+
|
|
67
|
+
Pure, reversible: revert the code change and ship a patch. Persistent state is additive/regenerable — the `door` column and `spend_token_rollup` table can be left in place harmlessly (nothing else reads them), or dropped with no data loss (the rollup is a regenerable fold of `feature_metrics`). No money state, no PIN store, no user-visible regression during the rollback window (the feature is dev-gated dark on the fleet, so fleet users never saw it). The spec docs land as documentation only.
|
|
68
|
+
|
|
69
|
+
## Conclusion
|
|
70
|
+
|
|
71
|
+
This review produced no design changes — the change is a read-only reporting surface with no blocking authority, mirroring two existing sibling surfaces. One honest deviation is documented (in-memory price index vs a SQLite materialized view, faithful to the spec's regenerable-read-index intent at this manifest scale). One scope boundary is recorded below. The change is clear to ship as Increment A: dark/reversible, no money controls, no Increment-B reach.
|
|
72
|
+
|
|
73
|
+
**Scope boundary (Layer 1c / Amendment 1).** After this task's named source SHA (34ee47730, `status: converged`), the spec received Amendment 1 (Layer 1c — a `provider_cost_report` store + capture seam + reconciliation route) and Amendment 2 (alerts to one topic), reverting the spec to `status: revising` (not re-converged). Amendment 2 is Increment C (out of scope). Amendment 1's store/capture-seam/reconciliation-route are entirely **metered-dispatch-dependent and empty in Increment A** (no metered call path exists to capture from). This PR honors Amendment 1's Surface-1 **read contract** additively — the `costBasis` / `providerReportedUsd` / `providerDriftPct` row fields ship with honest Increment-A values (`internal-derived` / `subscription-zero` / `unpriced`; provider fields `null`) plus a `providerGroundingNote` — but does NOT build the empty provider-cost store or `/routing-spend/reconciliation` route, because they are an un-reconverged amendment whose data source (real metered dispatch) is out of scope. That store + route + capture seam land when Amendment 1 re-converges and the metered dispatch seam exists to feed them. <!-- tracked: CMT-1901 routing-control-room spend, spec docs/specs/routing-control-room-spend-alerts.md Layer 1c -->
|
|
74
|
+
|
|
75
|
+
## Evidence pointers
|
|
76
|
+
|
|
77
|
+
- Unit: `tests/unit/routing-price-authority.test.ts` (14), `tests/unit/routing-spend-view.test.ts` (6), `tests/unit/feature-metrics-spend-rollup.test.ts` (9), `tests/unit/routing-price-refresh-prober.test.ts` (8), `tests/unit/migrate-routing-spend-dark.test.ts` (4)
|
|
78
|
+
- Integration: `tests/integration/routing-spend-routes.test.ts` (4 — 200 alive / 503 dark / caps not-live / missing-deps 503)
|
|
79
|
+
- E2E: `tests/e2e/routing-spend-lifecycle.test.ts` (4 — feature-alive 200-not-503 on the production init path, Bearer-gated, read-only POST→404)
|
|
80
|
+
- Sibling regression: `/metrics/features` unit+integration+e2e, `nature-routing-map`, `feature-delivery-completeness` (CLAUDE.md parity), `CapabilityIndex`, `no-empty-catch-blocks`, `lint-dev-agent-dark-gate` — all green.
|
|
81
|
+
|
|
82
|
+
## Class-Closure Declaration (display-only mirror)
|
|
83
|
+
|
|
84
|
+
No agent-authored-artifact defect — not applicable. This is a new read-only feature, not a fix to an LLM prompt/hook/config/skill/standard, and it adds no self-triggered controller (no loop/monitor/sentinel/reaper/scheduler/recovery path — the OFF-by-default refresh job is operator-scheduled, not self-triggered).
|