Interleave failed runs with issues and approvals in inbox

Failed runs are no longer shown in a separate section. They are now
mixed into the main work items feed sorted by timestamp, matching
how approvals are already interleaved with issues.

Replaced the large FailedRunCard with a compact FailedRunInboxRow
that matches the ApprovalInboxRow visual style.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
dotta 2026-03-19 07:24:52 -05:00
parent 72a0e256a8
commit 25af0a1532
3 changed files with 140 additions and 153 deletions

View file

@ -289,7 +289,11 @@ describe("inbox helpers", () => {
getInboxWorkItems({
issues: [olderIssue, newerIssue],
approvals: [approval],
}).map((item) => item.kind === "issue" ? `issue:${item.issue.id}` : `approval:${item.approval.id}`),
}).map((item) => {
if (item.kind === "issue") return `issue:${item.issue.id}`;
if (item.kind === "approval") return `approval:${item.approval.id}`;
return `run:${item.run.id}`;
}),
).toEqual([
"issue:1",
"approval:approval-between",

View file

@ -23,6 +23,11 @@ export type InboxWorkItem =
kind: "approval";
timestamp: number;
approval: Approval;
}
| {
kind: "failed_run";
timestamp: number;
run: HeartbeatRun;
};
export interface InboxBadgeData {
@ -146,9 +151,11 @@ export function approvalActivityTimestamp(approval: Approval): number {
export function getInboxWorkItems({
issues,
approvals,
failedRuns = [],
}: {
issues: Issue[];
approvals: Approval[];
failedRuns?: HeartbeatRun[];
}): InboxWorkItem[] {
return [
...issues.map((issue) => ({
@ -161,6 +168,11 @@ export function getInboxWorkItems({
timestamp: approvalActivityTimestamp(approval),
approval,
})),
...failedRuns.map((run) => ({
kind: "failed_run" as const,
timestamp: normalizeTimestamp(run.createdAt),
run,
})),
].sort((a, b) => {
const timestampDiff = b.timestamp - a.timestamp;
if (timestampDiff !== 0) return timestampDiff;