diff --git a/packages/views/issues/components/attribution-badge.test.tsx b/packages/views/issues/components/attribution-badge.test.tsx
index 75dcd538f1..4221592b57 100644
--- a/packages/views/issues/components/attribution-badge.test.tsx
+++ b/packages/views/issues/components/attribution-badge.test.tsx
@@ -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(
+ ,
+ );
+
+ 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(
+ ,
+ );
+
+ 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",
diff --git a/packages/views/issues/components/attribution-badge.tsx b/packages/views/issues/components/attribution-badge.tsx
index 91ab2a4b74..d84f18d6da 100644
--- a/packages/views/issues/components/attribution-badge.tsx
+++ b/packages/views/issues/components/attribution-badge.tsx
@@ -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 " provenance, with the resolution source and a
- * distinct warning tone for degraded (non-precise) attribution.
+ * the "on behalf of " 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 " 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 " 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({
@@ -122,7 +136,7 @@ export function AttributionBadge({
{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}