]}`}
@@ -3544,6 +3532,23 @@ function TaskCardComponent({
{showInReviewMoveControl && !metaRowVisible && renderInReviewMoveControl()}
)}
+ {isAgentCreated && (
+
+ {/**
+ * FNXC:TaskCardLayout 2026-07-10-00:00:
+ * FN-7780 moves the created-by-agent chip below the task content and before the workflow identity row so the header keeps only ID/status/actions metadata and no longer wraps on narrow/mobile cards. The badge content, tooltip, and accessible name remain unchanged.
+ */}
+
+
+ {agentCreatedTitle}
+ {agentCreatedVisibleLabel}
+
+
+ )}
{hasWorkflowBadge && (
{/*
diff --git a/packages/dashboard/app/components/__tests__/TaskCard.badge-wrap.test.tsx b/packages/dashboard/app/components/__tests__/TaskCard.badge-wrap.test.tsx
index 26bd13d82a..8d23797502 100644
--- a/packages/dashboard/app/components/__tests__/TaskCard.badge-wrap.test.tsx
+++ b/packages/dashboard/app/components/__tests__/TaskCard.badge-wrap.test.tsx
@@ -123,15 +123,81 @@ describe("TaskCard badge wrapping (FN-5162)", () => {
expect(styles.whiteSpace).toBe("nowrap");
});
+ it("places the agent badge in a left-aligned bottom row outside the header badge cluster", () => {
+ const agentRow = container.querySelector(".card-agent-badge-row") as HTMLElement;
+ const agentBadge = container.querySelector(".card-agent-created-badge") as HTMLElement;
+ const header = container.querySelector(".card-header") as HTMLElement;
+ const metaBadges = container.querySelector(".card-meta-badges") as HTMLElement;
+
+ expect(agentRow).toBeTruthy();
+ expect(agentBadge).toBeTruthy();
+ expect(agentRow.contains(agentBadge)).toBe(true);
+ expect(header.contains(agentBadge)).toBe(false);
+ expect(metaBadges.contains(agentBadge)).toBe(false);
+ expect(agentBadge.getAttribute("title")).toBe("Created by agent: agent-badge-wrap");
+ expect(agentBadge.getAttribute("aria-label")).toBe("Created by agent: agent-badge-wrap");
+ expect(agentBadge.querySelector(".visually-hidden")?.textContent).toBe("Created by agent: agent-badge-wrap");
+ expect(agentBadge.querySelector("span[aria-hidden='true']")?.textContent).toBe("agent-badge-...");
+
+ const styles = getComputedStyle(agentRow);
+ expect(styles.display).toBe("flex");
+ expect(styles.justifyContent).toBe("flex-start");
+ expect(styles.minWidth).toBe("0px");
+ });
+
+ it("does not render an empty header badge shell when the agent badge is the only grouped affordance", () => {
+ const { container: agentOnlyContainer } = render(
+ ,
+ );
+
+ const agentBadge = agentOnlyContainer.querySelector(".card-agent-created-badge");
+ expect(agentBadge).not.toBeNull();
+ expect(agentBadge?.closest(".card-agent-badge-row")).not.toBeNull();
+ expect(agentBadge?.closest(".card-header")).toBeNull();
+ expect(agentOnlyContainer.querySelector(".card-meta-badges")).toBeNull();
+ });
+
+ it("omits the agent bottom row for non-agent-created tasks", () => {
+ const { container: nonAgentContainer } = render(
+ ,
+ );
+
+ expect(nonAgentContainer.querySelector(".card-agent-created-badge")).toBeNull();
+ expect(nonAgentContainer.querySelector(".card-agent-badge-row")).toBeNull();
+ expect(nonAgentContainer.querySelector(".card-meta-badges")).toBeNull();
+ });
+
it("places the workflow badge in a left-aligned bottom row outside the header badge cluster", () => {
const workflowRow = container.querySelector(".card-workflow-badge-row") as HTMLElement;
const workflowBadge = container.querySelector(".card-workflow-badge") as HTMLElement;
const metaBadges = container.querySelector(".card-meta-badges") as HTMLElement;
+ const agentRow = container.querySelector(".card-agent-badge-row") as HTMLElement;
expect(workflowRow).toBeTruthy();
expect(workflowBadge).toBeTruthy();
expect(workflowRow.contains(workflowBadge)).toBe(true);
expect(metaBadges.contains(workflowBadge)).toBe(false);
+ expect(agentRow.compareDocumentPosition(workflowRow) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
const styles = getComputedStyle(workflowRow);
expect(styles.display).toBe("flex");
diff --git a/packages/dashboard/app/components/__tests__/TaskCard.test.tsx b/packages/dashboard/app/components/__tests__/TaskCard.test.tsx
index 841d85b1d4..d6954751a0 100644
--- a/packages/dashboard/app/components/__tests__/TaskCard.test.tsx
+++ b/packages/dashboard/app/components/__tests__/TaskCard.test.tsx
@@ -2594,7 +2594,7 @@ describe("TaskCard", () => {
});
- it("keeps priority, fast mode, and agent-created in meta while time moves to footer", () => {
+ it("keeps priority and fast mode in meta while agent-created and time move to bottom rows", () => {
const { container } = render(
{
const expectedMetaSelectors = [
".card-priority-badge",
".card-execution-mode-badge",
- ".card-agent-created-badge",
];
expectedMetaSelectors.forEach((selector) => {
@@ -2637,10 +2636,15 @@ describe("TaskCard", () => {
// longer renders a per-card badge (FN-7539) — an inherited default is not
// meaningfully-configured oversight, so it does not appear among the
// opt-in meta badges here.
+ const agentBadge = container.querySelector(".card-agent-created-badge");
+ expect(agentBadge).not.toBeNull();
+ expect(agentBadge?.closest(".card-agent-badge-row")).not.toBeNull();
+ expect(agentBadge?.closest(".card-meta-badges")).toBeNull();
+ expect(agentBadge?.closest(".card-header")).toBeNull();
+
expect(Array.from(group?.children ?? []).map((child) => child.className)).toEqual([
"card-priority-badge card-priority-badge--high",
"card-execution-mode-badge card-execution-mode-badge--fast",
- "card-agent-created-badge",
]);
});
@@ -2663,7 +2667,8 @@ describe("TaskCard", () => {
expect(group).not.toBeNull();
expect(group?.querySelector(".card-priority-badge")).not.toBeNull();
expect(group?.querySelector(".card-execution-mode-badge")).not.toBeNull();
- expect(group?.querySelector(".card-agent-created-badge")).not.toBeNull();
+ expect(group?.querySelector(".card-agent-created-badge")).toBeNull();
+ expect(container.querySelector(".card-agent-created-badge")?.closest(".card-agent-badge-row")).not.toBeNull();
expect(group?.querySelector(".card-time-indicator")).toBeNull();
expect(container.querySelector(".card-footer-row")).toBeNull();
expect(container.querySelector(".card-footer-row-right")).toBeNull();
@@ -2706,6 +2711,7 @@ describe("TaskCard", () => {
expect(container.querySelector(".card-priority-badge")).toBeNull();
expect(container.querySelector(".card-execution-mode-badge")).toBeNull();
expect(container.querySelector(".card-agent-created-badge")).toBeNull();
+ expect(container.querySelector(".card-agent-badge-row")).toBeNull();
});
@@ -4497,7 +4503,10 @@ describe("TaskCard", () => {
const badge = container.querySelector(".card-agent-created-badge");
expect(badge).not.toBeNull();
+ expect(badge?.closest(".card-agent-badge-row")).not.toBeNull();
+ expect(badge?.closest(".card-header")).toBeNull();
expect(badge?.getAttribute("title")).toBe("Created by agent: Task Robot");
+ expect(badge?.getAttribute("aria-label")).toBe("Created by agent: Task Robot");
expect(badge?.querySelector("span[aria-hidden='true']")?.textContent).toBe("Task Robot");
expect(badge?.querySelector(".visually-hidden")?.textContent).toBe("Created by agent: Task Robot");
});
@@ -4518,6 +4527,8 @@ describe("TaskCard", () => {
const badge = container.querySelector(".card-agent-created-badge");
expect(badge).not.toBeNull();
+ expect(badge?.closest(".card-agent-badge-row")).not.toBeNull();
+ expect(badge?.closest(".card-meta-badges")).toBeNull();
expect(badge?.getAttribute("title")).toBe("Created by agent: Scheduler Bot");
expect(badge?.getAttribute("aria-label")).toBe("Created by agent: Scheduler Bot");
});
@@ -4539,6 +4550,7 @@ describe("TaskCard", () => {
const badge = container.querySelector(".card-agent-created-badge");
expect(badge).not.toBeNull();
+ expect(badge?.closest(".card-agent-badge-row")).not.toBeNull();
expect(badge?.getAttribute("title")).toBe("Created by agent: Legacy Robot");
expect(badge?.querySelector("span[aria-hidden='true']")?.textContent).toBe("Legacy Robot");
});
@@ -4559,7 +4571,9 @@ describe("TaskCard", () => {
const badge = container.querySelector(".card-agent-created-badge");
expect(badge).not.toBeNull();
+ expect(badge?.closest(".card-agent-badge-row")).not.toBeNull();
expect(badge?.getAttribute("title")).toBe("Created by agent");
+ expect(badge?.getAttribute("aria-label")).toBe("Created by agent");
expect(badge?.querySelector("span[aria-hidden='true']")?.textContent).toBe("Agent");
expect(badge?.querySelector(".visually-hidden")?.textContent).toBe("Created by agent");
});
@@ -4579,6 +4593,7 @@ describe("TaskCard", () => {
);
expect(container.querySelector(".card-agent-created-badge")).toBeNull();
+ expect(container.querySelector(".card-agent-badge-row")).toBeNull();
});
it("coexists with GitHub badge and timer metadata", () => {
@@ -4606,7 +4621,7 @@ describe("TaskCard", () => {
expect(container.querySelector(".card-github-badge")).not.toBeNull();
expect(container.querySelector(".card-source-provenance")).not.toBeNull();
- expect(container.querySelector(".card-agent-created-badge")).not.toBeNull();
+ expect(container.querySelector(".card-agent-created-badge")?.closest(".card-agent-badge-row")).not.toBeNull();
expect(container.querySelector(".card-time-indicator")).not.toBeNull();
});
@@ -5813,7 +5828,7 @@ describe("TaskCard workflow badges", () => {
expect(screen.queryByTestId("card-workflow-badge-row")).toBeNull();
});
- it("keeps top badges in the meta cluster while rendering workflow identity below card rows", () => {
+ it("keeps top badges in the meta cluster while rendering agent and workflow identity below card rows", () => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-06-30T12:10:00.000Z"));
@@ -5839,11 +5854,16 @@ describe("TaskCard workflow badges", () => {
const metaBadges = screen.getByTestId("card-meta-badges");
const badge = screen.getByTestId("card-workflow-badge");
const workflowRow = screen.getByTestId("card-workflow-badge-row");
+ const agentRow = screen.getByTestId("card-agent-badge-row");
+ const agentBadge = container.querySelector(".card-agent-created-badge");
expect(metaBadges.querySelector(".card-priority-badge")).not.toBeNull();
expect(metaBadges.querySelector(".card-execution-mode-badge")).not.toBeNull();
- expect(metaBadges.querySelector(".card-agent-created-badge")).not.toBeNull();
+ expect(metaBadges.querySelector(".card-agent-created-badge")).toBeNull();
expect(metaBadges.querySelector(".card-workflow-badge")).toBeNull();
+ expect(agentRow).toContainElement(agentBadge as HTMLElement);
+ expect(agentBadge?.closest(".card-header")).toBeNull();
expect(workflowRow).toContainElement(badge);
+ expect(agentRow.compareDocumentPosition(workflowRow) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
[".card-footer-row", ".card-meta", ".card-agent-row"].forEach((selector) => {
const row = container.querySelector(selector);