Skip to content

feat(AF-654): show advisory approval likelihood to reviewers - #706

Merged
babltiga merged 2 commits into
mainfrom
feature/AF-654-approval-prediction-frontend
Aug 7, 2026
Merged

feat(AF-654): show advisory approval likelihood to reviewers#706
babltiga merged 2 commits into
mainfrom
feature/AF-654-approval-prediction-frontend

Conversation

@babltiga

@babltiga babltiga commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Closes #654. Part of the approval-outcome prediction epic (#645); consumes the read side landed in #653.

What this does

Renders the advisory approval-outcome prediction to reviewers:

  • components/review/ApprovalPredictionBadge.tsx — compact percentage pill for review-queue rows, with a tooltip explaining it is a statistical estimate. Renders nothing when the row is unscored.
  • components/review/ApprovalPredictionPanel.tsx — "Approval likelihood" DetailCard body on the query detail page: the label, the percentage, and a permanent advisory note, with pending / unavailable / failed / skipped fallbacks.
  • Review queue — a new "Approval likelihood" column after Risk; a muted with an explanatory tooltip when the row carries no probability.
  • WSquery.prediction_complete now invalidates ['queries','detail',id] and ['reviews','pending'], so both surfaces fill in without polling.
  • i18napproval_prediction.* plus queries.detail.card_approval_prediction and reviews.col_approval_likelihood, in all 7 locales.

Advisory-only guarantee. Copy is strictly non-directive — "Historical approval likelihood: 78%" and "Statistical estimate based on this organization's past review decisions. Advisory only — it never approves or rejects anything." The badge deliberately uses the neutral --fg-muted / --bg-sunken / --border tokens rather than the --risk-* palette, so the number never reads as a risk level. No hardcoded colours anywhere.

Deliberate decisions worth a reviewer's attention

  1. The detail card is gated on QUERY_REVIEW. The backend serves approval_prediction to any authorized reader, so this is a client-side product call: showing a submitter how their peers are likely to vote on their own still-open request invites cancel-and-resubmit gaming. Issue frontend: approval prediction — badge + detail panel + i18n #654's goal line is "Show the prediction to reviewers".
  2. The queue's tooltip does not name the skip reason. PendingReviewItem carries only approvalProbability — no reason field exists on the list payload — so the tooltip points at the detail page rather than guessing which sentinel applies. This is a deviation from issue step 3, which asked for the reason in the queue tooltip.
  3. The "Computing…" state is bounded by a five-minute grace window off the query's updated_at. Scoring fires off the transition into review and takes seconds; without the bound, any query already sitting in PENDING_REVIEW at deploy time would show "Computing the approval likelihood…" forever.
  4. skipped_reason is modelled as a closed ApprovalPredictionSkipReason union, and the lookup map is Record<ApprovalPredictionSkipReason, string> so a future backend token breaks compilation instead of silently degrading. The runtime fallback stays, for the window where the server ships a token ahead of the frontend. The raw machine token can never reach the DOM.
  5. Both new API fields are declared optional (approval_prediction?, approval_probability?) because the backend sets default-property-inclusion: non_null — these fields are absent, not null.

E2E

No new Playwright spec, deliberately. Per the issue: the model needs 50+ decided queries before it serves, so the only state reachable in the e2e stack is the cold-start "skipped" notice. Regression coverage was verified instead — the full suite ran locally against a stack rebuilt from this branch: 251 passed, 2 skipped. No reviews spec addresses the queue table positionally (they all use getByRole('row').filter({ hasText })), so the new column shifts nothing.

Docs

  • docs/06-frontend.md — the approval-likelihood card and its states, the new review-queue column, and the query.prediction_complete row in the default-invalidations table.

website/index.html, website/docs/index.html and the remaining docs sweep are out of scope by design — they are issue #655, the last item in the epic.

Verification

npm run lint (0 errors; 24 pre-existing warnings, none in touched files) · npm run typecheck · npm run test:coverage (175 files, 1361 tests passed, 93.88% lines / 83.67% branches) · npm run build · locale parity green · e2e 251 passed.

Review notes

Three agents reviewed this branch before it opened (af-verifier, af-reviewer, af-frontend-reviewer). No Blockers from any of them. Every concern raised is fixed in 49708d7f — the reviewer gate, the queue tooltip, the bounded pending state, the closed skip-reason union, and a docs correction (the panel has five states, not four). Two nits were consciously not taken:

  • --risk-med on the failure-warning icon (af-frontend-reviewer) — it colours the warning glyph, not the prediction value, and is copied verbatim from CostEstimatePanel. tokens.css has no semantic --warn token; introducing one is wider than this PR. A comment marks the intent.
  • Math.round(0.996 * 100) renders "100%" (af-reviewer) — real, but clamping the display would misreport the model's actual output. The permanent "Advisory only" note is the mitigation.

Two standing observations, neither introduced here:

  • Coverage is enforced — frontend/vite.config.ts:136 sets thresholds (90 lines/functions/statements, 80 branches) and npm run test:coverage fails below them. But its include list scopes coverage to pure .ts modules plus a couple of named components, so the new .tsx components sit outside the percentage entirely. Their guarantee is the 19 dedicated tests, not the headline number.
  • ApprovalPredictionBadge hand-rolls the af-pill classes rather than using the Pill primitive, following the precedent set by AnomalyBadge.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Frontend Test Results

1 361 tests  +28   1 361 ✅ +28   4m 52s ⏱️ + 1m 34s
  175 suites + 2       0 💤 ± 0 
    1 files   ± 0       0 ❌ ± 0 

Results for commit 49708d7. ± Comparison against base commit 5f225ed.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for Frontend Coverage (frontend)

Status Category Percentage Covered / Total
🟢 Lines 93.88% (🎯 90%) 2102 / 2239
🟢 Statements 91.87% (🎯 90%) 2342 / 2549
🟢 Functions 91.01% (🎯 90%) 638 / 701
🟢 Branches 83.67% (🎯 80%) 1322 / 1580
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
frontend/src/realtime/websocketManager.ts 86.25% 70.37% 94.44% 90% 35, 64, 87, 92, 99-100, 146, 149, 155, 163, 193-194, 227-240
frontend/src/utils/dateFormat.ts 100% 100% 100% 100%
Generated in workflow #910 for commit 49708d7 by the Vitest Coverage Report Action

@babltiga
babltiga merged commit fd32581 into main Aug 7, 2026
14 checks passed
@babltiga
babltiga deleted the feature/AF-654-approval-prediction-frontend branch August 7, 2026 11:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

frontend: approval prediction — badge + detail panel + i18n

1 participant