diff --git a/apps/web/app/api/v1/approvals/route.ts b/apps/web/app/api/v1/approvals/route.ts index 91e8824..e3bf84f 100644 --- a/apps/web/app/api/v1/approvals/route.ts +++ b/apps/web/app/api/v1/approvals/route.ts @@ -5,7 +5,10 @@ export async function GET(request: Request) { const traceId = requestTraceId(request); try { return Response.json({ - data: await new ApprovalDomainService().list(await apiSubject(request)), + data: await new ApprovalDomainService().list( + await apiSubject(request), + traceId, + ), traceId, }); } catch (error) { diff --git a/apps/web/features/approvals/governance-inbox.tsx b/apps/web/features/approvals/governance-inbox.tsx index 8d0fd92..8b2c505 100644 --- a/apps/web/features/approvals/governance-inbox.tsx +++ b/apps/web/features/approvals/governance-inbox.tsx @@ -209,7 +209,14 @@ function ApprovalDetail({ }) { const severity = riskSeverity(approval.riskSummary); const highImpact = severity === "critical"; - const pending = approval.status === "pending"; + // A row can still read as pending until the next inbox load expires it, so + // trust the deadline rather than the stored status for what is offerable. + const overdue = new Date(approval.expiresAt) <= new Date(); + const pending = approval.status === "pending" && !overdue; + // Includes rows already stored as `expired` by the inbox's lazy sweep, not + // just ones that are still nominally pending. + const closable = + approval.status === "expired" || (approval.status === "pending" && overdue); return (
@@ -277,9 +284,15 @@ function ApprovalDetail({
- {pending ? ( + {pending || closable ? (
-