Skip to content

Wire computeFindingAcceptance into operator-dashboard so AcceptanceRateCard leaves its empty state #5213

Description

@JSONbored

Context

#1967 ("Maintainer analytics dashboard (gate precision, calibration, acceptance rate)") and #2197 ("feat(ui): finding acceptance-rate card") are both closed, and both pieces exist in the codebase today — but they were never connected to each other, so the shipped AcceptanceRateCard UI has been showing its "not yet available" empty state in production since #2197 landed.

  • Backend (src/review/stats.ts:198-218,326,361): computeFindingAcceptance(env, { days, nowMs })FindingAcceptanceAggregate ({ flagged, addressed, unaddressed, acceptanceRate }) is fully implemented, tested, and already wired into computeStats()'s StatsPayload.findingAcceptance (line 442) — but computeStats/StatsPayload only powers GET /stats/data (handleStats), which is gated by GITTENSORY_REVIEW_STATS_TOKEN. I could not find handleStats mounted as a live route in src/index.ts/src/server.ts at all — it may be effectively dead code today, a separate pre-existing condition worth double-checking while in this area.
  • Frontend (apps/gittensory-ui/src/routes/app.analytics.tsx:129,236 + apps/gittensory-ui/src/components/site/app-panels/acceptance-rate-card.tsx): AcceptanceRateCard already exists, already renders correctly, and already expects data.acceptance?: FindingAcceptance where FindingAcceptance = { windowDays, accepted, total, rate }. Its own header comment says explicitly: "the backend acceptance computation is tracked separately in Maintainer analytics dashboard (gate precision, calibration, acceptance rate) #1967 ... degrades to a 'not yet available' empty state until it lands."
  • The actual dashboard this card lives on is OperatorDashboardPayload (src/services/operator-dashboard.ts:51, built by buildOperatorDashboardPayload), which currently imports and calls only computeCycleTimeAggregate from stats.ts (line 32, 127) — never computeFindingAcceptance. That's the actual gap: Maintainer analytics dashboard (gate precision, calibration, acceptance rate) #1967 built the computation, but wired it to a route (handleStats) that isn't the one the UI card reads from.

Requirements

  1. In src/services/operator-dashboard.ts, add computeFindingAcceptance(env, { days: 90, nowMs: Date.now() }) to the existing Promise.all([...]) batch (around line 115-131), matching the exact pattern and "fails safe to an empty aggregate" comment style already used for computeCycleTimeAggregate (line 126-127) and computeGateEval (line 124-125).
  2. Map the shape, don't pass the aggregate through directlyFindingAcceptanceAggregate ({flagged, addressed, unaddressed, acceptanceRate}) and the UI's FindingAcceptance ({windowDays, accepted, total, rate}) use different field names for the same concepts:
    • windowDays ← the days value passed into the call (90)
    • acceptedaddressed
    • totalflagged
    • rateacceptanceRate
  3. Add the mapped result as acceptance on the object buildOperatorDashboardPayload returns, matching OperatorDashboardPayload's expected shape (currently defined without an acceptance field at all — add it there too, per apps/gittensory-ui/src/routes/app.analytics.tsx:129's acceptance?: FindingAcceptance).
  4. Regenerate apps/gittensory-ui/public/openapi.json (npm run ui:openapi) since this adds a field to a response schema.
  5. Test to the usual 99%-patch-coverage bar: both branches of the flagged→acceptanceRate null case (0 flagged), and a real mapped-value case. AcceptanceRateCard's own existing tests (acceptance-rate-card.test.tsx) already cover the UI side and should need no changes — this is a pure backend-wiring + type change.
  6. Separately worth a quick look (not necessarily in this same PR, flagging for awareness): confirm whether handleStats/GET /stats/data is actually mounted anywhere at all. If it's genuinely dead/unreachable, that's a separate small cleanup opportunity.

Deliverables

  • computeFindingAcceptance called from buildOperatorDashboardPayload, mapped to the UI's FindingAcceptance shape, and included in OperatorDashboardPayload.
  • Updated/added unit test(s) in test/unit/operator-dashboard.test.ts covering both the has-data and zero-flagged cases.
  • Regenerated apps/gittensory-ui/public/openapi.json.

Expected outcome

AcceptanceRateCard on the operator dashboard (/app/analytics or wherever it's mounted) shows real numbers — "X% of flagged findings were acted on, Y of Z" — instead of its empty state, the moment this ships. No feature flag or config needed; this is pure wiring of two already-complete, already-tested pieces that were built against different consumers.

Metadata

Metadata

Assignees

Labels

maintainer-onlyOwner-only work — yields no Gittensor points.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions