File size: 1,490 Bytes
1c0c94d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import { pct } from '../../lib/format.js'
import { routeInfo } from '../../lib/constants.js'
import { useRuntime } from '../../context/RuntimeContext.jsx'

// Plain-English "what action was taken and why" β€” mirrors core/pipeline._adjudicate.
export default function RouteExplainer({ v }) {
  const rt = useRuntime()
  const r = routeInfo(v.route)
  const fused = pct(v.scores?.fused)
  const autoT = pct(rt.auto_confirm_threshold ?? 0.85)
  const reviewT = pct(rt.review_threshold ?? 0.55)
  let why = ''
  switch (v.route) {
    case 'auto_confirmed':
      why = `Tier-A appearance violation with fused confidence ${fused}% β‰₯ ${autoT}% β€” no human or AI review needed.`
      break
    case 'vlm_confirmed':
      why = `Confirmed by the AI auditor (VLM) and fused confidence ${fused}% β‰₯ ${reviewT}%.`
      break
    case 'human_review':
      why = `Fused confidence ${fused}% is below the auto-confirm bar (or the tier forbids auto-confirm), so a human makes the final call.`
      break
    case 'abstain':
      why = `The photo doesn't carry enough evidence to decide β€” the system abstained rather than guess.`
      break
    default:
      why = ''
  }
  const icon =
    v.route === 'auto_confirmed' || v.route === 'vlm_confirmed' ? 'βœ…' : v.route === 'human_review' ? 'πŸ§‘β€βš–οΈ' : '🚫'
  return (
    <div>
      <div style={{ fontWeight: 600, marginBottom: 4 }}>
        {icon} {r.action}
      </div>
      <div className="muted">{why}</div>
    </div>
  )
}