Skip to content

fix(eval): state the coverage an EvalScoreRecord already determines - #9861

Merged
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
devmixa702:fix/eval-score-coverage
Jul 29, 2026
Merged

fix(eval): state the coverage an EvalScoreRecord already determines#9861
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
devmixa702:fix/eval-score-coverage

Conversation

@devmixa702

Copy link
Copy Markdown
Contributor

Summary

buildEvalScoreRecordsFromRulePrecision hardcoded coverage: null while asserting
abstained: 0 two lines below. #9215 defines coverage = decided / (decided + abstained),
so with abstained structurally 0 the value is fully determined: any rule that decided
anything covered all of it. Publishing null told a consumer "this record does not state
its coverage" about a quantity the record itself pins down, and a validator re-deriving the
score per the #9215 contract computed 1 and disagreed with the published field — the one
thing an independently re-derivable record format exists to prevent.

Root cause. The doc comment justifies recall: null and abstained: 0 explicitly, per
field. coverage was filled in as null by symmetry with recall and never reasoned about
— an omission in a justification block rather than a logic error, which is why it survived
review.

Changes.

  1. New exported evalScoreCoverage(decided, abstained): number | null in
    src/review/eval-score-records.tstotal > 0 ? decided / total : null, mirroring the
    guard-the-denominator-else-null shape PublicRulePrecisionRow.precision uses below its
    sample floor. Exported so benchmark: leaderboard artifacts as spec-conformant EvalScoreRecords #9265's benchmark_run emitter states coverage by calling this
    rather than restating the formula.
  2. The emit site calls it: coverage: evalScoreCoverage(row.decided, 0), passing the same
    literal 0 the sibling abstained field publishes so the two cannot drift into a record
    whose coverage contradicts its own abstention count.
  3. The doc comment gains a coverage justification in the established per-field
    null-vs-zero style.

decided === 0 still yields null0/0 is genuinely undefined, never a masked zero.

recordDigest covers the whole record, so digests change for outcome_confirmed_precision
records. No migration is needed: records are built on read (routes.ts), never persisted,
and no golden-corpus or fixture file pins a digest.

Closes #9643

Scope

  • The PR title follows type(scope): short summary Conventional Commit format
  • This PR is focused and does not mix unrelated backend, UI, MCP, docs, dependency, and deploy changes
  • This follows CONTRIBUTING.md and does not reintroduce GitHub Pages, VitePress, site/, or CNAME
  • I linked a currently open issue this PR resolves (Closes orb(eval): EvalScoreRecord.coverage is published as null #9643)

Validation

  • git diff --check — clean
  • npm run actionlint
  • npm run typecheck — exit 0 (root + packages)
  • npm run test:coverage — see note below
  • npm run test:workers
  • npm run build:mcp
  • npm run test:mcp-pack
  • npm run ui:openapi:check
  • npm run ui:lint
  • npm run ui:typecheck
  • npm run ui:build
  • npm audit --audit-level=moderate — 0 vulnerabilities
  • New or changed behavior has unit/integration tests for new branches, fallback paths, and sanitizer boundaries

Targeted runs that passed in full:

  • npx vitest run test/unit/eval-score-records.test.ts — 27/27
  • Affected set (eval-score-records, public-eval-scores-route, benchmark-eval-records,
    threshold-backtest-run, public-rule-precision) — 74/74
  • Fix reverted, tests kept: 7 failed. Fix restored: 0 failed. Every new assertion genuinely
    fails without the change.
  • 22 repository drift/lint checks pass individually (actionlint, db:migrations:check,
    db:migrations:immutable:check, db:schema-drift:check, cf-typegen:check,
    coverage-boltons:check, selfhost:env-reference:check, miner:env-reference:check,
    selfhost:validate-observability, docs:drift-check, manifest:drift-check,
    engine-parity:drift-check, branding-drift:check, engines-nvmrc:check,
    release-manifest:sync:check, command-reference:check, ui:openapi:check,
    workspace-dep-ranges:check, server-manifest:check, dispatch-gate-reasons:check,
    ui-derived-types:check, contract:api-schemas:check)

npm run test:coverage ran to completion locally (1013 s, 1320 files): 1258 files / 25084
tests passed; 58 files / 207 tests failed, exit 1. Those 58 files fail identically on
unmodified main — verified with a baseline run over the same file set at the same base
commit (57 failed / 206 tests). The one-file delta,
test/unit/miner-cross-repo-evaluation.test.ts, passes 60/60 with these changes applied in
isolation and references nothing this PR touches; it is a load-sensitive flake. The failures
are environmental to a Windows dev box (spawn claude ENOENT, spawn codex ENOENT, missing
sqlite temp state, Windows path handling in several of the repo's own scripts). No failure
anywhere in the run log references evalScoreCoverage, eval-score-records, or
public-eval-scores. The unchecked boxes above were not run to completion for the same
reason — they are not skipped as unnecessary, and CI is the authoritative signal for them.

Safety

  • No secrets, wallet details, hotkeys, coldkeys, user PATs, private keys, raw trust scores, private rankings, or private maintainer evidence are exposed
  • Public GitHub text stays sanitized, low-noise, and does not imply compensation guarantees or optimization tactics
  • Auth, cookie, CORS, GitHub App, Cloudflare, or session changes include negative-path tests — n/a, no auth/CORS surface touched
  • API/OpenAPI/MCP behavior is updated and tested where needed — the coverage field was already typed number | null; ui:openapi:check is clean
  • UI changes use live API data or real empty/error/loading states, not production mock/demo fallbacks — n/a, no UI change
  • Visible UI changes include a UI Evidence section — n/a, no visible change
  • Public docs/changelogs are updated where needed; changelogs are only edited for release-prep PRs — no changelog edit

UI Evidence

Not applicable — backend-only change to a published JSON field with no visible UI, frontend,
docs, or extension surface. .loopover.yml's screenshotTableGate is scoped to
apps/loopover-ui/src/{components,routes}/** and styles.css; this PR touches none of them.

Notes

Risks and limitations.

  • Three committed assertions pinned the old coverage: null (eval-score-records.test.ts
    lines 75/79, public-eval-scores-route.test.ts line 59). They are corrected to 1, which
    is the behavioural change this issue asks for, not a weakened test.
  • deriveBenchmarkLeaderboard ranks ties by coverage ?? -1, so null → 1 could in
    principle reorder it. It filters to workUnit.kind === "benchmark_run" first and these are
    outcome_confirmed_precision records, so they never reach the ranking — no ordering change.
  • benchmark-eval-records.ts reads coverage straight off report.coverage.coverage rather
    than re-deriving it, so there is no existing duplicate formula to unify. Retrofitting it to
    call the helper is deliberately out of scope here: the Deliverables do not ask for it and
    the value originates in packages/loopover-engine.
  • Consumers caching a recordDigest across the deploy will see it change once, by design.

@devmixa702
devmixa702 requested a review from JSONbored as a code owner July 29, 2026 14:44
@loopover-orb

loopover-orb Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Warning

⏸️ LoopOver review result - manual review recommended

Review updated: 2026-07-29 14:52:19 UTC

3 files · 1 AI reviewer · no blockers · CI green · clean

⏸️ Suggested Action - Manual Review

Review summary
This PR fixes a real defect: `coverage` was hardcoded to `null` even though `abstained` is structurally `0` for this work-unit kind, making `decided/(decided+abstained)` fully determined (equal to 1 whenever `decided > 0`). The new `evalScoreCoverage` helper correctly guards the `0/0` case and is wired into the single emit site, with the doc comment updated consistently and existing tests updated to reflect the new expected values. The change is narrow, well-tested (including a digest round-trip regression test), and traces cleanly to the linked issue #9643.

Nits — 3 non-blocking
  • `test/unit/eval-score-records.test.ts`'s `filterEvalScoreRecords` test helper `record()` still hardcodes `coverage: null` for its synthetic records; harmless since coverage isn't exercised by those filter tests, but worth a comment noting it's intentionally unrelated to the coverage fix.
  • `evalScoreCoverage` is exported purely for a future consumer (benchmark: leaderboard artifacts as spec-conformant EvalScoreRecords #9265's `benchmark_run`) that doesn't exist yet in this diff — verify that speculative export doesn't sit unused for long before that lands.
  • Consider inlining the digest-verification regression test's comment about 'records built on read, never persisted' into the module header near `finalizeRecord` for future readers who don't see the test file.

Decision drivers

  • ✅ Code review — No blockers (1 reviewer)
  • ✅ Gate result — Passing (No configured blocker found.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ✅ Linked #9643
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (1 linked issue).
Validation posture ❌ 5/25 Preflight is holding this PR: the review lane is unavailable, so it is not ready for automated review.
Contributor workload ✅ 10/10 Author activity: 0 registered-repo PR(s), 0 merged, 0 issue(s).
Contributor context ℹ️ No public Gittensor match devmixa702; not a blocker.
Improvement ✅ Minor risk: clean · value: minor · LLM: moderate
Review context
  • Author: devmixa702
  • Role context: outside_contributor
  • Public audience mode: oss maintainer
  • Lane context: Repository is registered but has no active allocation in the current snapshot.
  • Public profile languages: Rust, TypeScript
  • Contributor context: Public profile only; not a blocker.
  • PR-specific overlap: none found.
Contributor next steps
  • Start here: Await review-lane availability.
  • Then work through the remaining 2 steps in the Signals table above.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
🧪 Chat with LoopOver

Ask LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.

  • @loopover ask <question> answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat <question> answers in natural prose from cached decision-pack facts via local inference (maintainer/collaborator; read-only).
  • A plain-language @loopover mention with a real question is routed to the closest matching read-only command automatically — no exact syntax required.

Full command reference: https://loopover.ai/docs/loopover-commands

🧪 Experimental — new and may change.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.

  • Re-run LoopOver review

@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@loopover-orb loopover-orb Bot added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jul 29, 2026

@loopover-orb loopover-orb Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LoopOver approves — the gate is satisfied and CI is green.

@loopover-orb
loopover-orb Bot merged commit 79fc935 into JSONbored:main Jul 29, 2026
6 checks passed
@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.05%. Comparing base (d574e9f) to head (9b1f918).
⚠️ Report is 7 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff             @@
##             main    #9861       +/-   ##
===========================================
- Coverage   91.64%   79.05%   -12.60%     
===========================================
  Files         916      282      -634     
  Lines      112753    58432    -54321     
  Branches    27084     8565    -18519     
===========================================
- Hits       103338    46194    -57144     
- Misses       8126    11955     +3829     
+ Partials     1289      283     -1006     
Flag Coverage Δ
backend 100.00% <100.00%> (+4.32%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/review/eval-score-records.ts 100.00% <100.00%> (ø)

... and 767 files with indirect coverage changes

`buildEvalScoreRecordsFromRulePrecision` hardcoded `coverage: null` while
asserting `abstained: 0` two lines below. JSONbored#9215 defines coverage as
`decided / (decided + abstained)`, so with abstained structurally zero the
value is fully determined -- a validator re-deriving the record computed 1 and
disagreed with the published field, which is exactly what an independently
re-derivable record format exists to prevent.

Compute it through a new exported `evalScoreCoverage(decided, abstained)`, so
the JSONbored#9265 benchmark_run emitter states coverage by calling the same definition
rather than restating the formula, and `decided === 0` still yields null (0/0
is undefined, never a masked zero). Extends the doc comment's existing
per-field null-vs-zero justifications to cover the third field.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

orb(eval): EvalScoreRecord.coverage is published as null

1 participant