mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-30 16:20:35 +02:00
fix(views): stop showing backfilled attribution as a warning (MUL-4768)
The transcript/activity AttributionBadge colored the "on behalf of <name>" chip yellow (text-warning) whenever attribution.precise === false. That flag is the backend's attribution-*coverage* health bit — owner_fallback, backfill, and unattributed all fail it — but coverage is an ops metric, not a reader-facing signal. A backfilled attribution names a human the same waterfall resolved, just retroactively, so it is confident; rendering it identically to a genuine owner_fallback guess made a correct "on behalf of Bohan" read like an error. Fire the cautionary tone only for a fallback guess (any non-precise source except backfill). Keeping the precise === false base means a future unknown degraded source still warns (fail-safe). The backfill nuance stays in the tooltip. Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
@@ -35,7 +35,7 @@ describe("AttributionBadge", () => {
|
||||
expect(screen.getByTitle("Direct member action")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("marks degraded (non-precise) attribution with a warning tone", () => {
|
||||
it("marks a fallback guess (owner_fallback) with a warning tone", () => {
|
||||
const attribution: TaskAttribution = {
|
||||
source: "owner_fallback",
|
||||
precise: false,
|
||||
@@ -51,6 +51,40 @@ describe("AttributionBadge", () => {
|
||||
.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows a backfilled attribution in the normal tone, not a warning (MUL-4768)", () => {
|
||||
// Backfill is non-precise for the coverage metric, but it names a human the
|
||||
// same waterfall resolved — just after the fact — so it must read like any
|
||||
// other confident attribution rather than a yellow warning.
|
||||
const attribution: TaskAttribution = {
|
||||
source: "backfill",
|
||||
precise: false,
|
||||
initiator: { id: "u5", name: "Bohan" },
|
||||
};
|
||||
const { container } = renderWithI18n(
|
||||
<AttributionBadge attribution={attribution} />,
|
||||
);
|
||||
|
||||
expect(screen.getByText("on behalf of Bohan")).toBeInTheDocument();
|
||||
// No warning tone — the confusing yellow that flagged backfilled runs is gone.
|
||||
expect(container.querySelector(".text-warning")).toBeNull();
|
||||
expect(container.querySelector(".text-muted-foreground")).not.toBeNull();
|
||||
// The "backfilled" nuance still lives in the tooltip for anyone who looks.
|
||||
expect(screen.getByTitle("Backfilled attribution")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("avatar variant shows no warning ring for a backfilled attribution (MUL-4768)", () => {
|
||||
const attribution: TaskAttribution = {
|
||||
source: "backfill",
|
||||
precise: false,
|
||||
initiator: { id: "u5", name: "Bohan" },
|
||||
};
|
||||
const { container } = renderWithI18n(
|
||||
<AttributionBadge attribution={attribution} variant="avatar" />,
|
||||
);
|
||||
|
||||
expect(container.querySelector(".ring-warning\\/60")).toBeNull();
|
||||
});
|
||||
|
||||
it("falls back to a generic name when the initiator has no display name", () => {
|
||||
const attribution: TaskAttribution = {
|
||||
source: "delegation",
|
||||
|
||||
@@ -23,8 +23,11 @@ function initialsOf(name: string): string {
|
||||
|
||||
/**
|
||||
* AttributionBadge renders who an agent run is accountable to (MUL-4302 §9):
|
||||
* the "on behalf of <member>" provenance, with the resolution source and a
|
||||
* distinct warning tone for degraded (non-precise) attribution.
|
||||
* the "on behalf of <member>" provenance, with the resolution source in a
|
||||
* tooltip and a cautionary tone ONLY when the named human is a fallback guess
|
||||
* (owner_fallback) rather than a confidently resolved one. A backfilled
|
||||
* attribution is confident (resolved after the fact by the same waterfall), so
|
||||
* it reads like any other attribution instead of a warning (MUL-4768).
|
||||
*
|
||||
* Two shapes, both silent when no responsible member resolved (MUL-4765):
|
||||
* - `variant="badge"` (default): the full "on behalf of <name>" chip. Renders
|
||||
@@ -82,9 +85,20 @@ export function AttributionBadge({
|
||||
sourceLabel = attribution.source;
|
||||
}
|
||||
|
||||
// Degraded attribution (owner_fallback / backfill / unattributed) is marked
|
||||
// distinctly so it never reads as a compliance-grade "who is responsible".
|
||||
const degraded = attribution.precise === false;
|
||||
// The backend's `precise` flag is an attribution-*coverage* health bit:
|
||||
// owner_fallback, backfill, and unattributed all fail it. But coverage is an
|
||||
// ops metric, not a reader-facing signal. The only thing a viewer of "on
|
||||
// behalf of <name>" cares about is whether that named human might NOT actually
|
||||
// be who's responsible — true only for a fallback guess (owner_fallback:
|
||||
// nothing resolved, so we defaulted to the agent owner). A backfilled
|
||||
// attribution names a human the same waterfall resolved, just after the fact,
|
||||
// so it is confident and must read like any other attribution, never as a
|
||||
// warning (MUL-4768). The cautionary tone therefore fires for any non-precise
|
||||
// source EXCEPT backfill; keeping the `precise === false` base means a future
|
||||
// unknown degraded source still warns (fail-safe) instead of silently reading
|
||||
// as confident.
|
||||
const uncertain =
|
||||
attribution.precise === false && attribution.source !== "backfill";
|
||||
const initiator = attribution.initiator;
|
||||
|
||||
// Avatar-only shape: just the accountable member's face, with the name +
|
||||
@@ -99,9 +113,9 @@ export function AttributionBadge({
|
||||
<span
|
||||
className={cn(
|
||||
"inline-flex shrink-0",
|
||||
// A subtle ring flags degraded attribution so an owner-fallback
|
||||
// face never silently reads as a precise responsible member.
|
||||
degraded && "rounded-full ring-1 ring-warning/60",
|
||||
// A subtle ring flags a fallback guess so an owner-fallback face
|
||||
// never silently reads as a confidently resolved responsible member.
|
||||
uncertain && "rounded-full ring-1 ring-warning/60",
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -122,7 +136,7 @@ export function AttributionBadge({
|
||||
<span
|
||||
className={cn(
|
||||
"text-[11px]",
|
||||
degraded ? "text-warning" : "text-muted-foreground"
|
||||
uncertain ? "text-warning" : "text-muted-foreground"
|
||||
)}
|
||||
>
|
||||
{sourceLabel}
|
||||
@@ -145,7 +159,7 @@ export function AttributionBadge({
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"max-w-40 min-w-0 gap-1 font-normal",
|
||||
degraded ? "text-warning" : "text-muted-foreground",
|
||||
uncertain ? "text-warning" : "text-muted-foreground",
|
||||
className
|
||||
)}
|
||||
title={sourceLabel}
|
||||
|
||||
Reference in New Issue
Block a user