-
Notifications
You must be signed in to change notification settings - Fork 0
[dashboard] AIN-251 + AIN-266 W4: shared Crumbs on list pages + candidates card #72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feat/port-v15-design-canon
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -215,6 +215,74 @@ export function InferenceDetailClient({ inferenceId, initial }: Props) { | |
| ) : null} | ||
| </div> | ||
|
|
||
| {/* AIN-266 W3 · candidates card — the routing decision artifact. | ||
| * Surfaces every model Ainfera Inference considered for this call, | ||
| * the rank order, q_prior, projected cost, and drop reasons. | ||
| * Renders only when the api returned a non-empty candidates list | ||
| * (null on pre-§16 rows + pinned-passthroughs that bypass the | ||
| * brain). Authed-tenant-only on the wire (§D3 lock). */} | ||
| {inf.candidates && inf.candidates.length > 0 ? ( | ||
| <div className="rounded-lg border border-hairline-strong bg-bg-elev"> | ||
| <div className="flex items-center justify-between border-b border-hairline-strong px-6 py-3"> | ||
| <p className="font-mono text-xs uppercase tracking-[0.15em] text-ink-muted"> | ||
| Candidates ({inf.candidates.length}) | ||
| </p> | ||
| <span className="font-mono text-[10px] uppercase tracking-[0.18em] text-ink-faint"> | ||
| the decision · ranked | ||
| </span> | ||
| </div> | ||
| <div className="overflow-x-auto"> | ||
| <table className="w-full font-mono text-xs"> | ||
| <thead className="border-b border-hairline"> | ||
| <tr className="text-ink-faint"> | ||
| <th className="px-6 py-2 text-left font-normal uppercase tracking-chip text-[10px]">#</th> | ||
| <th className="px-3 py-2 text-left font-normal uppercase tracking-chip text-[10px]">Model</th> | ||
| <th className="px-3 py-2 text-left font-normal uppercase tracking-chip text-[10px]">Brand</th> | ||
| <th className="px-3 py-2 text-right font-normal uppercase tracking-chip text-[10px]">q_prior</th> | ||
| <th className="px-3 py-2 text-right font-normal uppercase tracking-chip text-[10px]">proj. cost</th> | ||
| <th className="px-3 py-2 text-left font-normal uppercase tracking-chip text-[10px]">eligible</th> | ||
| <th className="px-6 py-2 text-left font-normal uppercase tracking-chip text-[10px]">drop reason</th> | ||
| </tr> | ||
| </thead> | ||
| <tbody> | ||
| {inf.candidates.map((c, i) => { | ||
| const chosen = c.model_slug === inf.model_used; | ||
| return ( | ||
| <tr | ||
| key={`${c.model_id ?? c.model_slug ?? i}-${i}`} | ||
| className={ | ||
| chosen | ||
| ? "bg-accent/5 border-l-2 border-accent" | ||
| : "hover:bg-bg-card" | ||
| } | ||
| > | ||
| <td className="px-6 py-2 text-ink-muted">{c.rank ?? i + 1}</td> | ||
| <td className="px-3 py-2 text-ink"> | ||
| {c.model_slug ?? "—"} | ||
| {chosen ? ( | ||
| <span className="ml-2 rounded bg-accent/15 px-1.5 py-0.5 text-[9px] font-medium uppercase tracking-chip text-accent"> | ||
| chosen | ||
| </span> | ||
| ) : null} | ||
| </td> | ||
| <td className="px-3 py-2 text-ink-muted">{c.brand_slug ?? "—"}</td> | ||
| <td className="px-3 py-2 text-right text-ink">{c.q_prior ?? "—"}</td> | ||
| <td className="px-3 py-2 text-right text-ink"> | ||
| {c.projected_cost_usd ? fmtCost(c.projected_cost_usd) : "—"} | ||
| </td> | ||
| <td className="px-3 py-2 text-ink-muted"> | ||
| {c.m_allowed === true ? "yes" : c.m_allowed === false ? "no" : "—"} | ||
| </td> | ||
| <td className="px-6 py-2 text-ink-muted">{c.drop_reason ?? "—"}</td> | ||
| </tr> | ||
| ); | ||
| })} | ||
| </tbody> | ||
| </table> | ||
| </div> | ||
| </div> | ||
| ) : null} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Candidates card added to component never renderedMedium Severity The new candidates card JSX (~65 lines) and Additional Locations (1)Reviewed by Cursor Bugbot for commit 0bf414c. Configure here. |
||
|
|
||
| {/* Audit chain block — AIN-182 §4 "Verify with curl" requirement. */} | ||
| <div className="rounded-lg border border-hairline-strong bg-bg-elev"> | ||
| <div className="flex items-center justify-between border-b border-hairline-strong px-6 py-3"> | ||
|
|
||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Null-null match gives false "chosen" badge
Low Severity
The comparison
c.model_slug === inf.model_usedevaluates totruewhen both values arenull(null === null), which would incorrectly render the "chosen" badge on a candidate row. Bothmodel_slug(string | null) andmodel_used(string | null) allownull. Adding a null guard likeinf.model_used != null && c.model_slug === inf.model_usedprevents a misleading "chosen" highlight when no model was actually selected.Reviewed by Cursor Bugbot for commit 0bf414c. Configure here.