diff --git a/.changeset/fn-8636-card-cost-badge-dash.md b/.changeset/fn-8636-card-cost-badge-dash.md
new file mode 100644
index 0000000000..a554c84c6d
--- /dev/null
+++ b/.changeset/fn-8636-card-cost-badge-dash.md
@@ -0,0 +1,7 @@
+---
+"@runfusion/fusion": patch
+---
+
+summary: Hide unavailable cost badges on board task cards.
+category: fix
+dev: Board cards now omit unavailable costs while detail and analytics surfaces retain the guess-free sentinel.
diff --git a/packages/dashboard/app/components/TaskCard.tsx b/packages/dashboard/app/components/TaskCard.tsx
index 381d4bc993..a336883801 100644
--- a/packages/dashboard/app/components/TaskCard.tsx
+++ b/packages/dashboard/app/components/TaskCard.tsx
@@ -1158,13 +1158,15 @@ function TaskCardComponent({
const retryWarningThreshold = useRetryWarning();
const costBadge = useCostBadge();
/*
- FNXC:TaskCardCostBadge 2026-07-11-12:20:
- The optional spend chip sits beside execution time but stays fully absent unless the default-off setting is enabled and the task has positive token usage. Use the shared read-time taskTokenCost helper so unpriced models show the guess-free “—” sentinel instead of a fabricated $0.
+ FNXC:TaskCardCostBadge 2026-07-31-23:39:
+ Board cards must omit the optional spend chip entirely when the derived cost is unavailable, avoiding a dash-only badge or empty Promote row. Detail and analytics surfaces retain formatCost's guess-free “—” sentinel rather than fabricating a $0 value.
*/
const cardCost = costBadge.enabled && hasTaskCost(task as TaskDetail)
? taskTotalCost(task as TaskDetail, costBadge.pricingOverrides)
: null;
- const cardCostLabel = cardCost ? formatCost(cardCost.usd, cardCost.unavailable) : null;
+ const cardCostLabel = cardCost && !cardCost.unavailable && cardCost.usd !== null
+ ? formatCost(cardCost.usd, false)
+ : null;
// Touch gesture detection refs
const touchStartPosRef = useRef<{ x: number; y: number; time: number } | null>(null);
diff --git a/packages/dashboard/app/components/__tests__/TaskCard.cost-badge.test.tsx b/packages/dashboard/app/components/__tests__/TaskCard.cost-badge.test.tsx
index 50d8f7040c..744f5ce015 100644
--- a/packages/dashboard/app/components/__tests__/TaskCard.cost-badge.test.tsx
+++ b/packages/dashboard/app/components/__tests__/TaskCard.cost-badge.test.tsx
@@ -108,33 +108,74 @@ describe("TaskCard cost badge", () => {
expect(badges[0]?.closest(".card-footer-row-right")).not.toBeNull();
});
- it("keeps unpriced usage guess-free and renders once below Promote", () => {
- const { container } = render(
-
-
- ,
- );
+ it.each([1280, 390])("omits unavailable cost chips and their shells at %ipx with or without Promote", (width) => {
+ const originalWidth = window.innerWidth;
+ Object.defineProperty(window, "innerWidth", { configurable: true, value: width });
- const badges = container.querySelectorAll(".card-cost-indicator");
- expect(badges).toHaveLength(1);
- expect(badges[0]?.textContent).toBe("—");
- expect(badges[0]?.closest(".card-promote-cost-row")).not.toBeNull();
- expect(badges[0]?.closest(".card-footer-row-right")).toBeNull();
+ try {
+ const unavailableWithoutPromote = render(
+
+
+ ,
+ );
+ expect(unavailableWithoutPromote.container.querySelector(".card-cost-indicator")).toBeNull();
+ expect(unavailableWithoutPromote.container.querySelector(".card-cost-indicator[aria-label]")).toBeNull();
+ expect(unavailableWithoutPromote.container.querySelector(".card-promote-cost-row")).toBeNull();
+ unavailableWithoutPromote.unmount();
+
+ const unavailableWithPromote = render(
+
+
+ ,
+ );
+ expect(unavailableWithPromote.container.querySelector(".card-cost-indicator")).toBeNull();
+ expect(unavailableWithPromote.container.querySelector(".card-cost-indicator[aria-label]")).toBeNull();
+ expect(unavailableWithPromote.container.querySelector(".card-promote-cost-row")).toBeNull();
+ unavailableWithPromote.unmount();
+
+ const pricedWithoutPromote = render(
+
+
+ ,
+ );
+ const footerBadge = pricedWithoutPromote.container.querySelectorAll(".card-cost-indicator");
+ expect(footerBadge).toHaveLength(1);
+ expect(footerBadge[0]?.textContent).toContain("$");
+ pricedWithoutPromote.unmount();
+
+ const pricedWithPromote = render(
+
+
+ ,
+ );
+ const promoteBadge = pricedWithPromote.container.querySelectorAll(".card-cost-indicator");
+ expect(promoteBadge).toHaveLength(1);
+ expect(promoteBadge[0]?.textContent).toContain("$");
+ expect(promoteBadge[0]?.closest(".card-promote-cost-row")).not.toBeNull();
+ } finally {
+ Object.defineProperty(window, "innerWidth", { configurable: true, value: originalWidth });
+ }
});
it("keeps the shared card cost chip visible at the mobile breakpoint", () => {
diff --git a/packages/dashboard/app/components/__tests__/TaskCard.test.tsx b/packages/dashboard/app/components/__tests__/TaskCard.test.tsx
index 0fcbd36fcd..a7a8e99fe5 100644
--- a/packages/dashboard/app/components/__tests__/TaskCard.test.tsx
+++ b/packages/dashboard/app/components/__tests__/TaskCard.test.tsx
@@ -6617,7 +6617,7 @@ describe("TaskCard", () => {
expect(container.querySelector(".card-footer-row")).toBeNull();
});
- it("places the unavailable cost sentinel inside todo meta without adding an icon", () => {
+ it("omits an unavailable cost chip and its footer shell", () => {
const { container } = render(
{
,
);
- const costBadge = container.querySelector(".card-cost-indicator") as HTMLElement | null;
- expect(costBadge).not.toBeNull();
- expect(costBadge?.textContent).toContain("—");
- expect(costBadge?.querySelector("svg")).toBeNull();
- expect(costBadge?.getAttribute("aria-label")).toBe("Estimated cost —");
- expect(costBadge?.getAttribute("title")).toBe("Estimated cost —");
- expect(costBadge?.closest(".card-meta")).toBe(container.querySelector(".card-meta"));
- expect(costBadge?.closest(".card-footer-row")).toBeNull();
+ expect(container.querySelector(".card-cost-indicator")).toBeNull();
+ expect(container.querySelector(".card-cost-indicator[aria-label]")).toBeNull();
+ expect(container.querySelector(".card-promote-cost-row")).toBeNull();
+ expect(container.querySelector(".card-footer-row-right")).toBeNull();
expect(container.querySelector(".card-footer-row")).toBeNull();
});