diff --git a/apps/web/app/terminal.tsx b/apps/web/app/terminal.tsx index 24d951b..2eb5da5 100644 --- a/apps/web/app/terminal.tsx +++ b/apps/web/app/terminal.tsx @@ -473,7 +473,61 @@ const extractUnderlying = (contractId: string): string => { const extractEquityTraceFromJoin = (joinId: string): string | null => { const match = joinId.match(/^equityjoin:(.+)$/); - return match?.[1] ?? null; + if (match?.[1]) { + return match[1]; + } + return joinId.trim().length > 0 ? joinId.trim() : null; +}; + +const normalizeJoinRefCandidates = (value: string): string[] => { + const ref = value.trim(); + if (!ref) { + return []; + } + + if (ref.startsWith("equityjoin:")) { + const rawTrace = ref.slice("equityjoin:".length); + return rawTrace ? [ref, rawTrace] : [ref]; + } + + return [ref, `equityjoin:${ref}`]; +}; + +const resolveJoinFromRef = ( + ref: string, + joins: Map +): EquityPrintJoin | null => { + const candidates = normalizeJoinRefCandidates(ref); + for (const key of candidates) { + const match = joins.get(key); + if (match) { + return match; + } + } + return null; +}; + +const formatDarkTrace = (traceId: string): string => { + const normalized = traceId.trim(); + if (!normalized) { + return "unknown"; + } + + if (normalized.startsWith("equityjoin:")) { + return normalized.slice("equityjoin:".length); + } + + const parts = normalized.split(":").filter(Boolean); + if (parts.length < 2) { + return normalized; + } + + const kind = parts[1]?.replace(/_/g, " ") ?? "event"; + const remainder = parts.slice(2).join(" -> "); + if (!remainder) { + return kind; + } + return `${kind}: ${remainder}`; }; const inferDarkUnderlying = ( @@ -482,7 +536,7 @@ const inferDarkUnderlying = ( equityJoins: Map ): string | null => { for (const ref of event.evidence_refs) { - const join = equityJoins.get(ref); + const join = resolveJoinFromRef(ref, equityJoins); if (!join) { continue; } @@ -2935,7 +2989,7 @@ const DarkDrawer = ({ event, evidence, underlying, onClose }: DarkDrawerProps) =

Trace path

Event trace
-

{event.trace_id}

+

{formatDarkTrace(event.trace_id)}

{traceRefs.length === 0 ? (

No evidence references attached.

@@ -2944,7 +2998,7 @@ const DarkDrawer = ({ event, evidence, underlying, onClose }: DarkDrawerProps) = {traceRefs.map((ref) => (
Evidence ref
-

{ref}

+

{formatDarkTrace(ref)}

))} @@ -3386,15 +3440,23 @@ const useTerminalState = () => { return; } - const missingIds = selectedDarkEvent.evidence_refs.filter((id) => !resolvedEquityJoinMap.has(id)); + const missingIds = selectedDarkEvent.evidence_refs.filter( + (id) => resolveJoinFromRef(id, resolvedEquityJoinMap) === null + ); if (missingIds.length === 0) { return; } incrementRetentionMetric("pinnedFetchMisses", missingIds.length); const url = new URL(buildApiUrl("/equity-joins/by-id")); + const requested = new Set(); for (const id of missingIds) { - url.searchParams.append("id", id); + for (const candidate of normalizeJoinRefCandidates(id)) { + if (!requested.has(candidate)) { + requested.add(candidate); + url.searchParams.append("id", candidate); + } + } } void fetch(url.toString()) .then(async (response) => { @@ -3407,6 +3469,10 @@ const useTerminalState = () => { const next = new Map(); for (const item of payload.data ?? []) { next.set(item.id, item); + next.set(item.trace_id, item); + if (item.print_trace_id) { + next.set(item.print_trace_id, item); + } } if (next.size > 0) { const now = Date.now(); @@ -3451,7 +3517,7 @@ const useTerminalState = () => { } return selectedDarkEvent.evidence_refs.map((id) => { - const join = resolvedEquityJoinMap.get(id); + const join = resolveJoinFromRef(id, resolvedEquityJoinMap); if (join) { return { kind: "join", id, join }; } @@ -3782,7 +3848,9 @@ const useTerminalState = () => { const keys = new Set(); if (selectedDarkEvent) { for (const id of selectedDarkEvent.evidence_refs) { - keys.add(id); + for (const candidate of normalizeJoinRefCandidates(id)) { + keys.add(candidate); + } } } return keys; diff --git a/packages/storage/src/clickhouse.ts b/packages/storage/src/clickhouse.ts index 6c08623..44e04ed 100644 --- a/packages/storage/src/clickhouse.ts +++ b/packages/storage/src/clickhouse.ts @@ -1317,8 +1317,34 @@ export const fetchEquityPrintJoinsByIds = async ( return []; } + const joinIds = new Set(); + const printTraceIds = new Set(); + for (const id of uniqueIds) { + joinIds.add(id); + if (id.startsWith("equityjoin:")) { + const trace = id.slice("equityjoin:".length); + if (trace) { + printTraceIds.add(trace); + } + } else { + joinIds.add(`equityjoin:${id}`); + printTraceIds.add(id); + } + } + + const joinIdList = Array.from(joinIds); + const printTraceList = Array.from(printTraceIds); + const whereParts = [ + `id IN (${buildStringList(joinIdList)})`, + `trace_id IN (${buildStringList(joinIdList)})` + ]; + if (printTraceList.length > 0) { + whereParts.push(`print_trace_id IN (${buildStringList(printTraceList)})`); + } + const lookupLimit = clampLookupLimit(joinIdList.length + printTraceList.length); + const result = await client.query({ - query: `SELECT * FROM ${EQUITY_PRINT_JOINS_TABLE} WHERE id IN (${buildStringList(uniqueIds)}) ORDER BY source_ts DESC, seq DESC LIMIT ${clampLookupLimit(uniqueIds.length)}`, + query: `SELECT * FROM ${EQUITY_PRINT_JOINS_TABLE} WHERE ${whereParts.join(" OR ")} ORDER BY source_ts DESC, seq DESC LIMIT ${lookupLimit}`, format: "JSONEachRow" });