From 79940202f1a7feaac9ced946d38256efb6299629 Mon Sep 17 00:00:00 2001 From: siddharthd Date: Wed, 12 Aug 2026 17:10:13 +1000 Subject: [PATCH] orders: fix the 42703 that broke EVERY order detail page getOrderDetail joined order_merchant_cadence on merchant_entity_id. Migration 021 rekeyed that view to merchant_key (merchant_entity_id is NULL on ~9% of the feed, and duplicate merchant entities split one shop's history); 027 and 029 moved order_feed and order_spend across, and this query was missed. Every /orders/ request has since failed with column rec.merchant_entity_id does not exist The reason nobody saw a 500 is the second half of this commit: the page collapsed every failure into 'That order could not be found.' A server fault wearing the costume of a data condition reads as an empty spine and gets investigated in the wrong repo. The hook now carries the status and only a genuine 404 says the order is missing. --- src/app/orders/[entityKey]/page.tsx | 14 +++++++++++++- src/lib/hooks.ts | 11 ++++++++++- src/lib/order-feed.ts | 18 +++++++++++++++++- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/app/orders/[entityKey]/page.tsx b/src/app/orders/[entityKey]/page.tsx index 1aa4f13..dd9849e 100644 --- a/src/app/orders/[entityKey]/page.tsx +++ b/src/app/orders/[entityKey]/page.tsx @@ -63,10 +63,22 @@ export default function OrderDetailPage({ params }: { params: Promise<{ entityKe if (isLoading) return
Loading order…
; if (error || !o) { + // A missing order and a broken query are different problems and must not + // share a sentence. Only a 404 means "no such order"; anything else is this + // page failing, and saying so is what sends the next person to the server + // log instead of to the spine. + const status = (error as (Error & { status?: number }) | null)?.status; return (
← All orders -

That order could not be found.

+ {status === 404 ? ( +

That order could not be found.

+ ) : ( +

+ This order could not be loaded{status ? ` (server error ${status})` : ""}. The order exists — + something in this page failed. Check the finance-app log. +

+ )}
); } diff --git a/src/lib/hooks.ts b/src/lib/hooks.ts index 79100a5..1a1e765 100644 --- a/src/lib/hooks.ts +++ b/src/lib/hooks.ts @@ -1243,7 +1243,16 @@ export function useOrderDetail(entityKey: string | null) { staleTime: 60_000, queryFn: async () => { const res = await fetch(`/api/orders/${encodeURIComponent(entityKey!)}`); - if (!res.ok) throw new Error("Failed to load order"); + if (!res.ok) { + // Carry the status. Collapsing every failure into one Error is how a + // 500 on this route rendered as "that order could not be found" on + // every single order for weeks — a server fault wearing the costume of + // a data condition, which reads as "the spine is empty" and gets + // investigated nowhere near the actual bug. + const err = new Error(res.status === 404 ? "Order not found" : "Failed to load order"); + (err as Error & { status?: number }).status = res.status; + throw err; + } return res.json(); }, }); diff --git a/src/lib/order-feed.ts b/src/lib/order-feed.ts index ed379e2..6b4cc1f 100644 --- a/src/lib/order-feed.ts +++ b/src/lib/order-feed.ts @@ -73,6 +73,12 @@ export interface OrderRow { /** Merchant orders on a regular cadence: the subscription signal, derived * not extracted. NULL unless the gaps are tight relative to their mean. */ cadence_days: number | null; + /** Worst sender-authentication verdict across the order's source documents. + * 93% are 'pass' — the badge exists for the 7% that are not. Do NOT badge + * source_trust instead: it is 'untrusted_external' on 100% of rows, because + * every order here came from email. */ + auth_verdict: string | null; + injection_flagged: boolean | null; txn_count: number; first_txn_id: number | null; } @@ -220,6 +226,7 @@ export async function getOrderFeed(filters: OrderFilters) { f.ordered_at, f.eta_date, f.delivered_at, f.currency, f.order_total, f.line_item_count, f.content_class, f.display_name, f.cadence_days, + f.auth_verdict, f.injection_flagged, m.canonical_name AS merchant_name, link.txn_count, link.first_txn_id, (SELECT sp.refunded_amount FROM order_spend sp @@ -399,7 +406,16 @@ export async function getOrderDetail(entityKey: string): Promise