Repository navigation
feat(OMN-20009): Overview shows the served run-locally share and saving per call - #359
Merged
Merged
Conversation
…tier; Overview gains quality and tier-mix panels Last run, Recent runs, Runs and the Workflow routing step read backend_id and host from delegation.decisions.v1 (OMN-20162, omnimarket#3343, migration 0054) instead of the hard-coded "Not served (OMN-20162)"; a missing backend or host is the typed "Not recorded", never a blank. The tier shows its served cost regime beside it, and Last run gains its quality score. Overview adds a quality panel bound to delegation.quality-gate.v1 and a tier-mix panel bound to delegation.model-routing.v1 by_tier. Both render served fields only; nothing is summed, divided or rounded in the browser. The direct-Postgres reader's own model-routing and quality-gate folds are removed: a second, drifted copy of omnimarket's views (no by_tier, escalations hard-coded to 0, a 0 pass rate with no runs). The projection API serves both topics; direct-Postgres mode now answers them with no rows. This keeps the OMN-19981 AC2 page test green with the panels bound. Onex-Lane: omn20225-run-backend Onex-Session: 7366ac205743487999083c1c8fcb35cb
…ier and the two Overview panels Fixtures serve OMN-20162's backend_id and host on one run and leave the other without them, plus the quality-gate and model-routing rows. Overview and Runs show the served values and a typed Not recorded; the quality and tier-mix panels show served fields only and sit below Recent runs, which still starts above the fold (OMN-19981 AC4). The scroll test now counts Recent runs' own rows, since the tier-mix panel is a second table on Overview. Screenshots regenerated. Onex-Lane: omn20225-run-backend Onex-Session: 7366ac205743487999083c1c8fcb35cb
…ert its tier and score From the independent review: the Workflow run-path binding now requests the fields WorkflowPath reads, its routing and quality-gate steps are asserted, and the e2e Not recorded check reads the unmeasured run's Backend and Host cells. Onex-Lane: omn20225-run-backend Onex-Session: 7366ac205743487999083c1c8fcb35cb
scripts/check-hardcoded-delegation-refs.sh refuses private LAN addresses in src/, and the new view tests used the lab hosts as fixture values. They now use gpu-host-a and gpu-host-b; what the tests prove is unchanged. Onex-Lane: omn20225-run-backend Onex-Session: 7366ac205743487999083c1c8fcb35cb
check-no-env-contamination.sh and hardcoded-model-config (E-LAN in TEST) refuse the lab subnet in tests/; the e2e fixture now uses gpu-host-b. Screenshots regenerated; 17 of 17 Chromium scenarios pass. Onex-Lane: omn20225-run-backend Onex-Session: 7366ac205743487999083c1c8fcb35cb
…lidator accepts artifact_sha256 verify / verify on this PR fails with extra_forbidden on artifact_sha256: the caller's workflow (1e69bec909f2) hard-pins its nested validator to d35ae63d3687, whose ModelDodReceipt forbids that field, and the merged receipts carry it. The caller moves to fad21c40911b (omnibase_core#1853), the same one-line move made on omnidash#353 (OMN-19994, with its regression test) and proposed in omnidash#350. Only this caller line changes. Onex-Lane: receipt-gate-pin-OMN-20225 Onex-Session: d99c149d-35af-4f1e-b791-28a28187bfc4
…ng per call Two Overview cards, each from one served field, with no arithmetic in the browser. Run locally shows delegation.model-routing.v1's local_call_share (local runs over all runs) as a percent, with the served local, total and not-tier-routed counts under it, so the gap to the tier-routed share stays visible. Avg saving / call shows metering-summary.v1's savings_per_measured_run_usd as served decimal text from the all-time row. The lab catalogue does not serve metering-summary.v1 yet, and the page contract test refuses a binding to an exposure it does not mark ok, so the card stays unbound and says "Not served yet: waits on metering-summary.v1". Its contract test binds it to that exposure, and nothing else, as soon as the captured catalogue serves it. A row with no saving on that exposure also no longer switches the whole page to Baseline unresolved. Absent rows, absent fields and null values each render a typed reason, never 0, 0% or $0. Stacked on omnidash#355 (OMN-20225), which removes the reader's hand-written model-routing fold this card must not read. Vitest: 21 new tests RED 21/21 before the change, GREEN after; six mutations (share from counts, null share as 0, saving from savings over runs, null saving as 0, Run locally bound to savings-overview, metering rows switching the page state) each turn them red. Full suite 1943 passed on the branch against 1922 at 850f939 and 1899 at dev 58b2f89. Chromium 1440x900: 19 of 19 local-dashboard scenarios pass, and the 2 new ones fail at 850f939. Onex-Lane: omn20009-overview Onex-Session: d99c149d-35af-4f1e-b791-28a28187bfc4
The repo pre-commit ESLint hook covers tests/, which npm run lint does not, and it refused the destructured-but-unused routing fixture. The spec now filters the served map instead; both scenarios still pass at 1440x900. Onex-Lane: omn20009-overview Onex-Session: d99c149d-35af-4f1e-b791-28a28187bfc4
Contributor
|
OCC autobind did not mint a companion for this PR: no changed-file candidate could be proven RED against the merge base, and emitting a PR-existence probe instead would be non-falsifiable evidence (OMN-15247). Hand-authored evidence is required. Considered 30 changed file(s):
This verdict is occ-autobind's, derived from this head and its merge base alone. A second producer, the occ-companion-effect backstop, consumes its own command on the lab bus and may still mint a companion under the generic binding. Whether it does depends on that consumer running, not on this diff (OMN-18876). |
2 tasks done
Onex-Lane: omn20009-served-exposures Onex-Session: e72e9157-677b-4b13-bd77-daa2be198c16
2 tasks done
#355 landed on dev as a squash while this branch still carried its unsquashed commits, so the two Overview files and two of #355's screenshots conflicted. overview.contracts.yaml and overview.page.yaml: kept this branch's Run locally and Avg saving / call cards in row 8 with Quality and Tier mix moved to row 10. Outside those hunks dev and the branch agree, so the Basis column from #354 and #355's backend, host and tier columns and panels stay as dev has them. Screenshots: every 1440x900 screenshot written by the contracts and OMN-20009 E2E specs was re-rendered from the merged tree (Chromium headless shell 1217), not picked from either side, because the Overview layout differs from both parents. Onex-Lane: omn20009-served-exposures Onex-Session: e72e9157-677b-4b13-bd77-daa2be198c16
Patel230
added a commit
that referenced
this pull request
Oct 5, 2026
…9980 This branch was cut from #359 before #354's per-run Basis landed, so the run tables and the Overview run bindings conflicted with this PR's removal of per-run savings. Resolution: per-run savings stay removed (no Savings, Baseline cost or savings_usd binding), and #354's Basis column stays. On Runs, the Usage source column becomes #354's Basis and reads usage_source only, without falling back to savings_method, which #354's provenance spec forbids. B2 in LocalDashboardPage.omn19980.test.tsx now expects the Basis header for that reason; it was red on the merge until then. Screenshots written by the contracts and OMN-20009 E2E specs were re-rendered from the merged tree rather than picked from either side.
#353 (OMN-19994) adds a tenant-not-configured failure kind to the local page loader and a typed TenantNotConfiguredState in WidgetReadState. It touched local-page-loader.ts and LocalDashboardPage.tsx in hunks apart from this branch's, so git merged them without conflicts. The two compose: a tenant-scoped binding with no tenant fails with the new kind, bindingFailedEmpty() then keeps the Run locally and Avg saving / call cards empty, and WidgetReadState shows the typed state once. The metering-summary filter in resolveLocalPageEmptyState and the Basis, Host, Backend and tier columns from #354 and #355 stay as they were. Screenshots: every 1440x900 screenshot written by the contracts and OMN-20009 E2E specs was re-rendered from the merged tree, not picked from either side. Onex-Lane: omn20009-served-exposures Onex-Session: 369fcbc6-265e-4c01-a677-b457ad3d09b2
Patel230
added a commit
that referenced
this pull request
Oct 6, 2026
…MN-19980 #359's branch took dev's #353 (OMN-19994): a tenant-not-configured failure kind in the local page loader and a typed TenantNotConfiguredState in WidgetReadState. Those hunks sit apart from this branch's, so local-page-loader.ts and LocalDashboardPage.tsx merged without conflicts. The per-run savings stay removed from Runs and Overview, and #354's Basis column still reads usage_source only. Only the 1440x900 screenshots conflicted (binary). Every one written by the contracts and OMN-20009 E2E specs was re-rendered from the merged tree, not picked from either side.
This was referenced Oct 6, 2026
…ses only a tie metering_summary is keyed by tenant/window/baseline_model, so a baseline change leaves the previous and current all rows together. Select the newest as_of and refuse only rows tied on it, the rule omnidash#364's metering cards use. S4 now ties on as_of; S4b (different as_of) and S4c (newer day row) added. Onex-Lane: omn20009-served-exposures Onex-Session: e72e9157-677b-4b13-bd77-daa2be198c16
…rview-served-exposures
Patel230
added a commit
that referenced
this pull request
Oct 6, 2026
…tate Step B binds Overview's four headline cards to metering-summary.v1, so its rows reached resolveLocalPageEmptyState. A store with a metering row but no decisions then lost its NO_RUNS_YET page, and a metering row carrying baseline_state BASELINE_UNRESOLVED switched the whole Overview to Baseline unresolved, when each card already says why its own figure is missing. Components whose rows come from metering-summary.v1 are now left out of that decision. This is the same filter OMN-20009 (omnidash#359) adds as its C3, line for line, so when #359 is brought onto dev the conflict is trivial.
2 tasks done
Patel230
added a commit
that referenced
this pull request
Oct 6, 2026
…ings stays on the one definition (#365) * feat(OMN-19980): guard savings to metering-summary.v1 and drop per-run savings The ruling for AC2 is "window totals only": every savings figure on the six local pages comes from metering-summary.v1. Merged dev broke that in four bindings, and no test could catch it. That is why the closer rejected AC2. Step A of the approved Amendment 1: - omn19980.savings-source.test.ts is the T2.4 page test that AC2's falsifier names. It has four rules, each with planted controls: R1 checks bindings, R2 widgets, R3 per-run figures, and R4 scans the page and loader source. It also has a field classifier, instrument checks, and cost false-positive controls. The one total still on cost.savings-overview.v1 (overview-savings-snapshot) has a time-boxed allowlist entry, asserted by name. Step B empties it, so AC2 stays open until then. - Overview last run, Overview recent runs and the Runs lookup no longer ask delegation.savings.v1 for savings_usd, counterfactual_baseline_usd or cloud_cost_usd. The Savings and Baseline cost columns and the savingsOf helper are gone. Each run keeps its local_cost_usd. Runs keeps the baseline_model, usage_source and savings_method labels, as ruled. - The loader no longer reads savings_usd to decide the page state. Only the served baseline_state does. - A metric card with no served row reads Not measured instead of Baseline unresolved, because nothing was priced. A savings card with no served source still names metering-summary.v1. The old-shape assertions in pages.test.ts, the LocalDashboardPage overview/runs/RunsTable tests and the local-dashboard e2e spec are updated. The Overview and Runs screenshots are regenerated, since their columns changed. (cherry picked from commit a163a49) * feat(OMN-19980): Overview Savings total reads metering-summary.v1's all-time row Step B of AC2. The Overview Savings total was the last savings figure on the six local pages still read from another exposure (cost.savings-overview.v1, let through by the guard's Step A allowlist), and its baseline caption came from delegation.savings.v1. Both now come from one metering-summary.v1 row, the all-time row `onex metering` renders, so the figure and the baseline under it cannot disagree. The allowlist is empty and asserted empty, so the guard now holds AC2 as written. The fold keys rows by baseline model and keeps an older baseline's rows, so a baseline change leaves two all rows. The card shows the one refreshed last (every refresh runs under the resolved baseline) and refuses, by name, two refreshed at the same moment rather than picking one. An unresolved baseline reads "Baseline unresolved", a null saving "Not measured", no all row "Not measured: no all-time row", and an unserved exposure names itself; never $0. Spend and Measured runs stay on cost.savings-overview.v1 (ruling 2: AC2 covers savings only). Known red until the lab serves the exposure: four pages.test.ts cases that compare Overview's bindings with served-catalogue.lab.json. The capture (2026-10-02) has no metering-summary.v1 and the catalogue is not edited by hand; it turns green when a release carrying omnimarket#3368 is deployed and the catalogue is re-captured. (cherry picked from commit 10c3450) * feat(OMN-19980): Overview Spend, Measured runs and Tokens read the same metering all row Amendment 2 (Lakshman, 2026-10-06 ~07:20Z: "Move all to metering"). After the Savings rebind, Overview still took Spend and Measured runs from cost.savings-overview.v1 and left Tokens unbound, so the four headline figures came from different rows and could disagree with `onex metering` and with each other. All four now bind metering-summary.v1 and pick one row the same way: the all-time row refreshed last, refusing by name two refreshed at the same moment. Spend is spend_usd, Measured runs is runs_measured with the same row's run classes (runs_total, unknown-token and unknown-spend runs) as its caption, and Tokens shows tokens_in and tokens_out side by side, never summed. Only Savings is priced at the baseline, so an unresolved baseline is that card's state alone; MetricCard applies it only to a binding that asks for baseline_state. A null spend or measured count reads Not measured, and token totals with no run that recorded tokens read Not measured rather than 0. Overview no longer binds cost.savings-overview.v1 at all. The old Measured runs caption (estimated and unknown run counts) had no metering equivalent for "estimated"; it is replaced by metering's own run classes, every run in exactly one. delegation.savings.v1 stays only for the per-run cost lookups (ruling 2). The guard gains a headline rule with planted controls (another exposure, a second caption exposure, an unbound card, no all-row fields, the wrong column), and the page tests cover served, unserved, unresolved baseline, null figures, a baseline change, a same-moment tie and day rows only, for every card. (cherry picked from commit 1a6159b) * test(OMN-19980): Avg saving / call binds only once the field is served, not the exposure The lakshman lane (omnimarket 0.4.301, read 2026-10-06T07:34:32Z) now serves onex.snapshot.projection.metering-summary.v1 ok, but its columns are tenant_id, window_kind, window_start, window_end, as_of, baseline_model, pricing_manifest_version, baseline_state, runs_total, runs_measured, runs_unknown_tokens, runs_unknown_spend, tokens_in, tokens_out, spend_usd, counterfactual_usd, savings_usd, summary_json. savings_per_measured_run_usd is not among them (git grep over omnimarket origin/dev finds nothing), and the dashboard must never divide savings by runs in the browser. C2 assumed "exposure served" meant "field served" and went red on the new catalogue. The catalogue entries now carry the served columns, taken from the saved GET (no new remote read), and C2 binds the card only when the exposure is ok AND lists savings_per_measured_run_usd. Until then the card stays unbound with upstream-blocked. Planted controls (in-test catalogue copies) cover served without the field, served with the field, and degraded with the field. OMN-20009 owns the card and binds it when the field is served. On dev only the catalogue half is carried: omn20009.contracts.test.ts and the Avg saving / call card belong to OMN-20009 (omnidash#359), which is not on dev, so its C2 test change has nothing to apply to. (cherry picked from commit f9d10bf) * test(OMN-19980): point the savings guards at dev's Overview, without OMN-20009's cards The OMN-19980 commits were written on top of OMN-20009 (omnidash#359), which is still waiting on the omnibase_infra release, and they leaned on three of its pieces that dev does not have. Carrying them onto dev on their own needs these adjustments, and nothing else changes in behaviour: - local-page-loader exports METERING_SUMMARY_TOPIC. Step B's renderer dispatches the four headline cards on it; on the stack the constant came from #359. The C3 empty-state filter #359 added next to it stays with OMN-20009. - C2 to C5 exercised #359's Avg saving / call card through a mocked binding. On dev the Savings card is the one bound to metering-summary.v1, so they now exercise it, and the loader mock is gone. - R4 allowed SavingsPerRunCard, which dev does not have; on dev it allows no owner, its positive control plants a read in each real source file, and the planted case now expects SavingsPerRunCard to be caught too. - With Tokens bound, no Overview card is left unbound on dev, so the unbound-savings population is asserted empty (its planted case remains the control) and the SV-3 pending-state test uses an unbound card that falls back to "its exposure". * test(OMN-19980): re-render the Overview and Runs screenshots on dev The cherry-picked commits kept dev's screenshots, because theirs were rendered on the OMN-20009 stack (Avg saving / call and Run locally cards). These come from local-dashboard-contracts on this branch: Overview's headline cards now read metering-summary.v1, and Runs drops the per-run savings columns and says "with its cost". Only Overview and Runs pages are here; the other screenshot is unchanged. * test(OMN-19980): R4 catches a camelCase or PascalCase savings read Codex's review of omnidash#361 found that R4 only matched lower_snake tokens, so row.savingsUsd, row['SavingsUsd'] or a made-up savingsPerRunUsd in the page or loader code walked past the guard while the suite read clean. R4 now folds camelCase and PascalCase identifiers and string keys to lower snake before classing them, so every alias of every savings field is caught, as a property or as a bracket key. What it still leaves out is on purpose and tested: cost names in any case, SCREAMING_SNAKE constants (DELEGATION_SAVINGS_TOPIC), single words and prose, and the name a function, class, interface or type declares, which names code and reads nothing. The two real source files give no finding. * fix(OMN-19980): metering-summary rows never decide the page's empty state Step B binds Overview's four headline cards to metering-summary.v1, so its rows reached resolveLocalPageEmptyState. A store with a metering row but no decisions then lost its NO_RUNS_YET page, and a metering row carrying baseline_state BASELINE_UNRESOLVED switched the whole Overview to Baseline unresolved, when each card already says why its own figure is missing. Components whose rows come from metering-summary.v1 are now left out of that decision. This is the same filter OMN-20009 (omnidash#359) adds as its C3, line for line, so when #359 is brought onto dev the conflict is trivial.
Keeps both behaviours: the Run locally card and the Avg saving / call card (SavingsPerRunCard, newest as_of rule) beside the four metering headline cards. - local-page-loader.ts: one METERING_SUMMARY_TOPIC export, one metering empty-state filter (the C3 copy and #365's are the same line). - LocalDashboardPage.tsx: one pickAllTimeRow helper shared by MeteringTotalCard and SavingsPerRunCard; PENDING_SOURCES keeps only the Avg saving entry. - savings-source guard R4: SavingsPerRunCard may read savings_per_measured_run_usd and nothing else; planted controls prove another component, a look-alike name and a second field in the same card still fail; JSX tag names are not reads. - C2 binds the card only once the catalogue lists the column, not the topic. - Screenshots regenerated from the merged page. Onex-Lane: omn20009-served-exposures Onex-Session: e72e9157-677b-4b13-bd77-daa2be198c16
2 tasks done
Patel230
added a commit
that referenced
this pull request
Oct 7, 2026
omnidash#359 landed first and added one R4 savings-read exception for the Avg saving / call card, as an owner-name map. This branch had replaced the R4 allowlist with typed allowances bound to a file, owner, field set and component, each checked against that component's metering-summary.v1 binding. The two collided in omn19980.savings-source.test.ts. The merge keeps the typed allowances and gives each a state. #359's card reads savings_per_measured_run_usd before any exposure serves it, so its component binds nothing; it enters as 'pending', which is valid only while that component has no binding and fails, naming the fix, the moment one appears. Both name exemptions are kept: topic-key properties from this branch and JSX tag names from #359. #359's own planted test now passes its allowance explicitly, since allowances are scoped to a file. Mutation: dropping the pending check turns the new planted test red. Onex-Lane: omn20006-usage-page Onex-Session: d99c149d-35af-4f1e-b791-28a28187bfc4
Contributor
Author
|
Close-out for OMN-20009: merged 2026-10-06 as
Thank you! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
For OMN-20009 AC1, AC2 and AC4: two Overview cards, Run locally and Avg saving / call, each bound to one served field, with no arithmetic in the browser and a typed reason, never
0,0%or$0, when there is no measurement.Stacked on omnidash#355 (OMN-20225) at
850f939. #355 removes the reader's hand-written model-routing fold, which this card must not read. Until #355 merges, this PR's diff carries its commits too. Merge order: after omnidash#354 (OMN-20008) and #355, which edit the same Overview files. The served fields come from the omnimarket PR for OMN-20009, which is stacked on omnimarket#3368.What changed
Two commits on #355's head:
eacb277the cards.overview-run-locally) bindsonex.snapshot.projection.delegation.model-routing.v1and showsby_tier.local_call_share(local runs over all runs) as a percent. Under it are the servedlocal_call_count,total_call_countandnot_tier_routed_count, so the gap to the tier-routed share stays visible. On the lab catalogue fixture that is 83.9%, "104 of 124 runs local · 20 not tier-routed".overview-avg-saving-per-call) is declared with no binding andupstream-blocked, and renders "Not served yet: waits on metering-summary.v1". The captured catalogue does not serve that exposure yet, and the page contract test refuses a binding to an exposure the catalogue does not mark ok. Once it is served,SavingsPerRunCardreadssavings_per_measured_run_usdfrom the all-time row as served decimal text.src/layout/local-page-loader.ts, a metering-summary row with no saving no longer switches the whole page to Baseline unresolved: that card states its own reason.overview.page.yamlplaces both cards in row 8 and moves feat(OMN-20225): run rows show the served verdict, backend, host and tier; Overview gains quality and tier-mix panels #355's Quality and Tier mix panels to row 10.src/pages/local/omn20009.contracts.test.ts,src/pages/LocalDashboardPage.omn20009.test.tsx(21 tests), the Overview metric-key assertion inpages.test.ts, andtests/e2e/local-dashboard-omn20009.spec.tswith two 1440x900 screenshots.be39357drops an unused binding from the E2E spec. The repo's pre-commit ESLint coverstests/, whichnpm run lintdoes not.How it was verified
Update 2026-10-06 08:18Z: review finding fixed, dev brought in (head
d33d431)Supersedes the counts in the 03:43Z update for the current head.
SavingsPerRunCardrefused whenever metering-summary.v1 had more than oneallrow.metering_summaryis keyed by tenant, window and baseline model, so a baseline change can leave twoallrows, and the card would show "Not measured: 2 baseline models" for good.c1ff9cf, signed): the card takes the newestas_ofamongwindow_kindallrows and refuses only a tie on it, the same rule as omnidash#364. The caption shows the shown row's own baseline.as_ofare refused.as_ofshows the newest row's value and baseline, in either order.allrow is ignored.4517cf3: 2 failed, 19 passed (the two S4b cases). GREEN: 21/21.allrow" fails the same 2 tests.61a76f4asd33d431, no conflicts.d33d431: full vitest 2025 passed, 15 skipped, 0 failed.npm run check,npx tsc -p tsconfig.node.json --noEmit, lint and build all exit 0. Playwright omn20009 and contracts specs pass 20/20.pre-commit run --filespasses.Update 2026-10-06 03:43Z: dev brought in again (head
4517cf3)Supersedes the counts in the 22:07Z update for the current head.
bad7af9, which adds omnidash#353 (typed tenant-not-configured state). It is a fast-forward overb1064cb.local-page-loader.tsandLocalDashboardPage.tsxin other hunks. With no tenant configured, the Run locally and Avg saving / call cards stay empty, and the page showsTenantNotConfiguredStateonce.4517cf3:npm run check,npm run lintandnpm run buildeach exit 0.pre-commit run --filesexits 0.Update 2026-10-05 22:07Z: dev brought in (head
b1064cb)The verification above is from the earlier head
be39357; the counts below supersede it for the current head.6210f3d(omnidash#354, feat(OMN-20225): run rows show the served verdict, backend, host and tier; Overview gains quality and tier-mix panels #355, test(OMN-19921): verify current local dashboard default #357) into this branch; fast-forward over the previous head6d9589c.overview.contracts.yaml,overview.page.yamland two screenshots, because this branch carried feat(OMN-20225): run rows show the served verdict, backend, host and tier; Overview gains quality and tier-mix panels #355's unsquashed commits.b1064cb:npm run check,npm run lint, ESLint on e2e and test files, andnpm run buildeach exit 0.pre-commit run --fileson the merged files exits 0.Branch head
be39357, stack base850f939(#355's live head), dev58b2f89.#355's head contains dev.pages.test.ts, 89 in all): branch 89 passed. At850f939with only the test files laid over it: 22 failed, the 21 new tests and the updated metric-key case. At dev: 29 failed, the extra 7 being feat(OMN-20225): run rows show the served verdict, backend, host and tier; Overview gains quality and tier-mix panels #355's own tests inpages.test.ts.savings_usd / runs_measuredin the browser: 4 failed;0.0%: 1 failed;$0: 2 failed;cost.savings-overview.v1: 3 failed.850f939: 1921 passed, 1 failed, 15 skipped. Dev: 1898 passed, 1 failed, 15 skipped. The one base failure issynthetic-fixtures-hygiene.test.ts, which runsgit ls-filesand failed only because the base was an export with no.git; it passes on the branch. Branch minus850f939is +21, the new tests.local-dashboard-contracts.spec.tsand the new spec): 19 of 19 passed on the branch; the 2 new scenarios fail at850f939. The render shows Run locally 83.9% with its counts, and Avg saving / call "Not served yet: waits on metering-summary.v1". This run re-rendered 13 committed screenshots a few KB differently: Playwright's browsers had to be reinstalled (chromium-headless-shell build 1217). The committed bytes were kept, because 11 of the 13 belong to feat(OMN-20225): run rows show the served verdict, backend, host and tier; Overview gains quality and tier-mix panels #355's scenarios.npm run check(tsc),npm run lintand ESLint on the three new test files: all exit 0.model-routing.v1servedlocal_call_share0.84 = 105/125, andmetering-summary.v1answered 404unknown_topic, which is why the saving card waits.Failure paths
n/a: this change renders served fields; it writes no receipt, verdict or report.
Open defects
metering-summary.v1served on the lab (omnimarket#3368, then the OMN-20009 omnimarket PR deployed) and the captured catalogue recaptured, then a one-line binding inoverview.contracts.yaml. Until then the card says what it waits on. The spec's matching-value criterion for this card is not done.Local gates
--no-verify, noSKIP=, no--no-gpg-sign, nocore.hooksPathoverride. Every pre-commit and pre-push hook ran.Not in this change
The served fields themselves (omnimarket), the vendor copy (omnibase_infra) and the evidence contract (onex_change_control), all under OMN-20009. Compression and Cache hit rate belong to OMN-20226; the billing figures are out of scope.
Overlapping open PRs
Read live at 2026-10-05T03:47Z. Each was trial-merged with this head (
git merge-tree), and again with #355's head to see which conflicts this PR adds.Overlap-Reviewed: #355 — the parent of this stack (OMN-20225): it removes the hand-written model-routing fold and adds the Quality and Tier mix panels, which this PR moves down a row; this PR carries its commits until it merges, and merges clean onto it.
Overlap-Reviewed: #354 — OMN-20008 edits
LocalDashboardPage.tsx,overview.contracts.yamland the Overview screenshots for savings provenance. It conflicts with #355's head already, in the same files (LocalDashboardPage.tsx,LocalDashboardPage.runs.test.tsx, 9 screenshots); this PR adds no new conflicting file. Both are needed, and the spec orders this PR after it.Overlap-Reviewed: #358 — omnidash#358 (OMN-20006) rewrote the OMN-19980 guard
src/pages/local/omn19980.savings-source.test.ts(2026-10-06, head2246648). Its R4 now follows every in-repo import from the page and allows a savings read only as a named entry (file, owner, fields, component). Against this PR's headf65ea8bthat guard fails 4 tests: this PR'sSavingsPerRunCardread ofsavings_per_measured_run_usdand the<SavingsPerRunCard>tag. A trial merge (git merge-treeoff65ea8band2246648) conflicts only in that file;LocalDashboardPage.tsxandpages.test.tsmerge cleanly, and #358 no longer adds aPENDING_SOURCESentry. A reconciled guard is drafted and run on the merged tree: 38 of 38. It models this PR's read as a pending entry whileoverview-avg-saving-per-callbinds nothing, and becomes a bound entry once omnimarket#3406 serves the field. It keeps this PR's JSX-tag exemption. Full vitest on the merged tree: 2107 passed, plus 1 failure from a git-tracked-files test that cannot pass in an export. Whichever PR merges second carries it.Overlap-Reviewed: #353 — OMN-19994 shares only
.github/workflows/call-receipt-gate.yml, which comes from #355's commits, not this PR's. Merges clean.Overlap-Reviewed: #350 — OMN-20375 repins the receipt gate in
.github/workflows/call-receipt-gate.yml; the conflict is with #355's change to that file, present at #355's head; this PR does not touch it.Evidence-Ticket: OMN-20009
Evidence-Source: OCC#12863