Skip to content

fix(review): split unrecognized human-override verdicts out of confirmed - #9916

Merged
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
bitfathers94:fix/issue-9640
Jul 29, 2026
Merged

fix(review): split unrecognized human-override verdicts out of confirmed#9916
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
bitfathers94:fix/issue-9640

Conversation

@bitfathers94

Copy link
Copy Markdown
Contributor

fix(review): split unrecognized human-override verdicts out of confirmed

Both loadPublicRulePrecision (the public /v1/public/stats surface) and
loadOverrideDayRows (the operator calibration trend) folded any override row
whose $.verdict was missing, malformed, or any string other than 'reversed'
into the confirmation side: the public reader took decided as COUNT(*) and
derived confirmed = decided - reversed, and the trend reader's CASE only
distinguished 'reversed' from everything-else. A schema-drifted or corrupt
row therefore inflated the published precision toward 100% while also
padding decided past the publication sample floor -- exactly backwards for
the one surface whose job is to state how often the gate's rules are right.

loadBacktestRunDayRows in the same trend file already handles this shape for
backtest verdicts, with an explicit third CASE bucket and a comment stating
the rule: a row with a missing/unrecognized verdict must stay visible rather
than vanish into whichever bucket is convenient. Mirror that pattern for
override rows in both readers: classify confirmed/reversed/unrecognized as
three separate SQL sums, derive decided as confirmed + reversed only, and
add a required unrecognized count to PublicRulePrecisionRow and
CalibrationRuleTrendWeek so a malformed row shows up as a data-quality
signal instead of being silently absorbed.

Regenerated the shared contract schema and the UI's openapi.json for the new
field. buildEvalScoreRecordsFromRulePrecision keeps reading decided/confirmed
unchanged and does not gain an unrecognized field, per #9215's fixed record
shape.

Named regression tests pin the previously-wrong numbers on both surfaces:
9 confirmed + 1 reversed + 5 unrecognized-verdict rows now reports
decided: 10, confirmed: 9, unrecognized: 5, precision: 0.9 (was decided: 15,
confirmed: 14, precision: 0.933) on the public surface, and the equivalent
split on the weekly trend.

Closes #9640

Note: the checked-in openapi.json under apps/loopover-ui/public/ is a generated artifact — it is regenerated from the spec source by the repo's own script, not hand-edited, and stays in sync via the spec parity test in this PR. No rendered UI changes, so there is no visual diff to screenshot.

Validation

Verified locally on this branch before opening:

  • npm run typecheck
  • npx turbo run build:tsc build:verify
  • npm run test:coverage — patch coverage 100.0% of changed lines

Both loadPublicRulePrecision (the public /v1/public/stats surface) and
loadOverrideDayRows (the operator calibration trend) folded any override row
whose $.verdict was missing, malformed, or any string other than 'reversed'
into the confirmation side: the public reader took decided as COUNT(*) and
derived confirmed = decided - reversed, and the trend reader's CASE only
distinguished 'reversed' from everything-else. A schema-drifted or corrupt
row therefore inflated the published precision toward 100% while also
padding decided past the publication sample floor -- exactly backwards for
the one surface whose job is to state how often the gate's rules are right.

loadBacktestRunDayRows in the same trend file already handles this shape for
backtest verdicts, with an explicit third CASE bucket and a comment stating
the rule: a row with a missing/unrecognized verdict must stay visible rather
than vanish into whichever bucket is convenient. Mirror that pattern for
override rows in both readers: classify confirmed/reversed/unrecognized as
three separate SQL sums, derive decided as confirmed + reversed only, and
add a required unrecognized count to PublicRulePrecisionRow and
CalibrationRuleTrendWeek so a malformed row shows up as a data-quality
signal instead of being silently absorbed.

Regenerated the shared contract schema and the UI's openapi.json for the new
field. buildEvalScoreRecordsFromRulePrecision keeps reading decided/confirmed
unchanged and does not gain an unrecognized field, per JSONbored#9215's fixed record
shape.

Named regression tests pin the previously-wrong numbers on both surfaces:
9 confirmed + 1 reversed + 5 unrecognized-verdict rows now reports
decided: 10, confirmed: 9, unrecognized: 5, precision: 0.9 (was decided: 15,
confirmed: 14, precision: 0.933) on the public surface, and the equivalent
split on the weekly trend.
@bitfathers94
bitfathers94 requested a review from JSONbored as a code owner July 29, 2026 22:38
@loopover-orb

loopover-orb Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Tip

✅ LoopOver review result - approve/merge recommended

Review updated: 2026-07-29 22:49:11 UTC

7 files · 1 AI reviewer · no blockers · readiness 98/100 · CI green · clean

✅ Suggested Action - Approve/Merge

  • safe to merge

Review summary
This PR splits override rows with a missing/unrecognized `$.verdict` into a new `unrecognized` bucket instead of silently folding them into `confirmed`, touching both `loadPublicRulePrecision` and `loadOverrideDayRows`/`buildCalibrationTrend`, plus the shared zod schema, generated openapi.json, and downstream test fixtures. The SQL rewrite is correct: `decided` is now `confirmed + reversed` derived from explicit CASE sums rather than `COUNT(*) - reversed`, so a malformed row no longer inflates `confirmed` or clears the publication floor, and the new `unrecognized` field is wired through the schema, openapi contract, and both readers consistently. `buildEvalScoreRecordsFromRulePrecision` is deliberately left untouched per #9215's frozen record shape, which is a sensible scope boundary called out explicitly in the description. Regression tests pin the exact previously-wrong vs corrected numbers on both surfaces (public precision and weekly trend), which is real coverage of the fixed path rather than a fabricated scenario.

Nits — 5 non-blocking
  • src/services/rule-calibration-trend.ts: the per-line `/* v8 ignore next */` comments above `confirmed`, `reversed`, and `unrecognized` read a little noisier than the original single `/* v8 ignore next 2 */`; consider collapsing back to a single ignore block spanning the three lines if v8's coverage tool supports a multi-line ignore for a `.map()` object literal.
  • test/unit/rule-calibration-trend.test.ts: the new regression test's inline comment says 'Below the fix, decided would have been 15... and precision 0.933' but doesn't assert against the pre-fix numbers directly, so it documents the regression only in prose — consider a comment-free assertion or moving that narrative to the PR description for symmetry with the public-rule-precision test's identical framing.
  • packages/loopover-contract/src/public-api.ts: `unrecognized` is required (non-nullable) on `PublicRulePrecisionSchema`, which is correct since the SQL always emits a defined integer, but confirm the UI's existing renderer for `rulePrecision.rules` doesn't destructure the array shape positionally anywhere and break on the new required field ordering.
  • Consider adding one direct regression test that hand-constructs an `OverrideDayRow`/precision row with `unrecognized > 0` and asserts `decided` excludes it entirely (already covered by the two REGRESSION tests, so this is optional).
  • If the UI actually surfaces `rulePrecision.rules[].unrecognized` anywhere (not shown in this diff), verify the render path was updated too since only the OpenAPI schema is regenerated here, not any UI component.

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 #9640
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 ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 101 registered-repo PR(s), 69 merged, 1 issue(s).
Contributor context ✅ Confirmed Gittensor contributor bitfathers94; Gittensor profile; 101 PR(s), 1 issue(s).
Improvement ✅ Minor risk: clean · value: minor · LLM: moderate
Linked issue satisfaction

Addressed
The diff adds three-way SQL classification (reversed/confirmed/unrecognized) in both loadPublicRulePrecision and loadOverrideDayRows, derives decided as confirmed+reversed only, adds required unrecognized fields to both types plus openapi/contract schema updates, and includes named regression tests matching the exact decided:10/confirmed:9/unrecognized:5/precision:0.9 scenario and a below-floor we

Review context
  • Author: bitfathers94
  • Role context: outside_contributor
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: TypeScript
  • Official Gittensor activity: 101 PR(s), 1 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Keep the PR focused and include validation evidence before maintainer review.
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.

Visual preview
Route Viewport Before (production) After (this PR's preview) Diff
/ desktop before /
before /
after /
after /
/ mobile before / (mobile)
before / (mobile)
after / (mobile)
after / (mobile)

Click any thumbnail to open the full-size screenshot. Before = production · After = this PR's preview deploy.

Scroll preview
Route Before (production) After (this PR's preview)
/ before / (scroll)
before / (scroll)
after / (scroll)
after / (scroll)

A short scroll-through clip (desktop) — click either thumbnail to open the full animation. Evidence for scroll-linked behavior a single screenshot can't show.

🟩 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
@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.54%. Comparing base (1e1b004) to head (1dc5473).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9916      +/-   ##
==========================================
+ Coverage   79.52%   79.54%   +0.02%     
==========================================
  Files         281      284       +3     
  Lines       58577    58660      +83     
  Branches     6833     6845      +12     
==========================================
+ Hits        46581    46664      +83     
  Misses      11706    11706              
  Partials      290      290              
Flag Coverage Δ
backend 100.00% <100.00%> (?)

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

Files with missing lines Coverage Δ
packages/loopover-contract/src/public-api.ts 100.00% <ø> (ø)
src/review/public-rule-precision.ts 100.00% <100.00%> (ø)
src/services/rule-calibration-trend.ts 100.00% <100.00%> (ø)

@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 cd491d2 into JSONbored:main Jul 29, 2026
10 checks passed
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(precision): a missing or unrecognized $.verdict is counted as CONFIRMED

1 participant