Implement quote lifecycle investigator
All checks were successful
deploy / deploy (push) Successful in 57s

Proof: Adds durable quote lifecycle lookup by quote_id, decision_id, or command_id through the authenticated dashboard API, pins selected Quote lifecycle evidence outside the rolling table, and covers the Awaiting executor follow-up regression with targeted tests.

Assumptions: Existing swap demand, trade decision, execute command, executor result, and quote outcome history rows are sufficient to reconstruct the selected quote lifecycle; this turn stays read-side and does not alter strategy, pricing, limits, arming, signer identity, pair enablement, relay, or executor behavior.

Still fake: Venue-native final fill truth, fee-aware realized PnL, already-pruned historical detail, and the broader quote analytics workbench remain incomplete.
This commit is contained in:
philipp 2026-06-12 19:28:38 +02:00
parent 0902cb2c93
commit cb87910c95
9 changed files with 1332 additions and 14 deletions

View file

@ -14,6 +14,7 @@ import {
buildDashboardBootstrap,
buildDashboardControlErrorResponse,
buildLiveQuoteLifecycleRows,
buildQuoteLifecycleLookupResponse,
buildLiveStatusBar,
createDashboardLiveState,
isDashboardWebSocketBackpressured,
@ -44,6 +45,7 @@ import {
loadLatestMarketPrice,
loadLatestPortfolioMetric,
loadPairConfigSummary,
loadQuoteLifecycleLookupEvidence,
loadQuoteLifecycleRetentionSummary,
loadRecentAlertTransitions,
loadRecentDepositStatuses,
@ -378,6 +380,38 @@ async function handleApiRequest({ req, res, url, auth }) {
return sendJson(res, 200, submissionPage);
}
const lifecycleLookupMatch = req.method === 'GET'
? url.pathname.match(/^\/api\/strategy\/quote-lifecycle\/(.+)$/)
: null;
if (lifecycleLookupMatch) {
const identifier = decodeURIComponent(lifecycleLookupMatch[1] || '').trim();
if (!identifier) {
return sendJson(res, 400, {
ok: false,
error: 'quote_lifecycle_identifier_required',
lifecycle_row: null,
lookup: {
requested_identifier: '',
matched_identifier_type: null,
found: false,
lookup_at: new Date().toISOString(),
latest_stage_at: null,
matched_identifiers: {
quote_ids: [],
decision_ids: [],
command_ids: [],
},
},
});
}
const payload = await loadQuoteLifecycleLookupPayload({ identifier });
return sendJson(res, payload.ok ? 200 : 404, {
...payload,
...(payload.ok ? {} : { error: 'quote_lifecycle_not_found' }),
});
}
const controlMatch = req.method === 'POST'
? url.pathname.match(/^\/api\/control\/([^/]+)\/([^/]+)$/)
: null;
@ -597,6 +631,19 @@ async function loadBootstrapPayload({ auth, page, pageSize }) {
return payload;
}
async function loadQuoteLifecycleLookupPayload({ identifier }) {
const tradingConfig = await tradingConfigStore.forceRefresh();
const runtimeConfig = buildRuntimeConfig(tradingConfig);
const evidence = await loadQuoteLifecycleLookupEvidence(pool, {
identifier,
});
return buildQuoteLifecycleLookupResponse({
config: runtimeConfig,
identifier,
evidence,
});
}
async function loadServiceSnapshots() {
const services = listDashboardServices(config);
return Promise.all(services.map((service) => loadServiceSnapshot(service)));

View file

@ -1130,6 +1130,7 @@ const HUMAN_REASON_TEXT = {
pending_deposit_not_credited: 'Funding is not credited yet.',
pending_outbound_reserved: 'Pending outbound inventory is reserved.',
quote_expired: 'Quote expired.',
quote_not_found_or_finished: 'Relay reported the quote as not found or already finished.',
quote_response_ack: 'Quote response acknowledged by the relay.',
quote_response_ok: 'Quote response accepted by the relay.',
awaiting_outcome: 'No durable venue outcome is recorded yet.',
@ -1425,7 +1426,11 @@ function finalizeLifecycleRow(row) {
} else if (execution?.status === 'failed') {
lifecycle_state = 'failed';
lifecycle_label = 'Submission failed';
reason_code = normalizeLifecycleToken(execution?.result_code || 'submission_failed');
reason_code = normalizeLifecycleToken(
execution?.failure_category
|| execution?.result_code
|| 'submission_failed',
);
reason_text = buildExecutionFailureText(execution, reason_code);
} else if (execution?.status === 'rejected') {
lifecycle_state = 'blocked';
@ -1504,6 +1509,100 @@ function finalizeLifecycleRow(row) {
};
}
export function buildQuoteLifecycleLookupResponse({
config,
identifier,
evidence = {},
now = new Date().toISOString(),
} = {}) {
const requestedIdentifier = String(identifier || '').trim();
const rows = deriveQuoteLifecycleRows({
recentQuotes: evidence.recentQuotes || [],
recentTradeDecisions: evidence.recentTradeDecisions || [],
recentExecuteTradeCommands: evidence.recentExecuteTradeCommands || [],
recentExecutionResults: evidence.recentExecutionResults || [],
recentQuoteOutcomes: evidence.recentQuoteOutcomes || [],
limit: null,
}).map((row) => enrichLifecycleRowForUi({ config, row }));
const lifecycleRow = selectLookupLifecycleRow(rows, requestedIdentifier);
const found = Boolean(lifecycleRow);
const lookup = {
requested_identifier: requestedIdentifier,
matched_identifier_type: found
? inferLookupIdentifierType(requestedIdentifier, lifecycleRow, evidence)
: null,
found,
lookup_at: evidence.lookup_at || now,
latest_stage_at: lifecycleRow?.latest_stage_at || null,
matched_identifiers: found
? collectLifecycleIdentifiers(lifecycleRow, evidence.matched_identifiers)
: normalizeMatchedIdentifiers(evidence.matched_identifiers),
};
return {
ok: found,
lifecycle_row: lifecycleRow || null,
lookup,
};
}
function selectLookupLifecycleRow(rows, identifier) {
if (!rows.length) return null;
const normalizedIdentifier = String(identifier || '').trim();
if (!normalizedIdentifier) return rows[0] || null;
return rows.find((row) => lifecycleRowHasIdentifier(row, normalizedIdentifier))
|| rows[0]
|| null;
}
function lifecycleRowHasIdentifier(row, identifier) {
return row?.quote_id === identifier
|| row?.decision_id === identifier
|| row?.command_id === identifier;
}
function inferLookupIdentifierType(identifier, row, evidence = {}) {
if (evidence.matched_identifier_type) return evidence.matched_identifier_type;
if (row?.quote_id === identifier) return 'quote_id';
if (row?.decision_id === identifier) return 'decision_id';
if (row?.command_id === identifier) return 'command_id';
return 'linked_identifier';
}
function collectLifecycleIdentifiers(row, matchedIdentifiers = null) {
const normalized = normalizeMatchedIdentifiers(matchedIdentifiers);
return {
quote_ids: uniqueStrings([
...normalized.quote_ids,
row?.quote_id,
]),
decision_ids: uniqueStrings([
...normalized.decision_ids,
row?.decision_id,
]),
command_ids: uniqueStrings([
...normalized.command_ids,
row?.command_id,
]),
};
}
function normalizeMatchedIdentifiers(value = null) {
return {
quote_ids: uniqueStrings(value?.quote_ids),
decision_ids: uniqueStrings(value?.decision_ids),
command_ids: uniqueStrings(value?.command_ids),
};
}
function uniqueStrings(values = []) {
return [...new Set(
(values || [])
.map((value) => String(value || '').trim())
.filter(Boolean),
)];
}
function buildPolicySkipText(decision, reasonCode) {
const base = humanizeReasonCode(reasonCode, 'Policy skipped the response.');
const policy = decision?.response_policy || {};

View file

@ -3868,6 +3868,354 @@ export async function loadRecentQuoteOutcomes(pool, { limit = 200 } = {}) {
return result.rows.map(normalizeQuoteOutcomeRow);
}
export async function loadQuoteLifecycleLookupEvidence(pool, { identifier } = {}) {
const requestedIdentifier = String(identifier || '').trim();
const lookupAt = new Date().toISOString();
if (!requestedIdentifier) {
return buildQuoteLifecycleLookupEvidence({
requestedIdentifier,
lookupAt,
});
}
const candidatesResult = await pool.query(
`
WITH candidates AS (
SELECT
'quote_id' AS matched_identifier_type,
COALESCE(quote_id, payload->>'quote_id') AS quote_id,
payload->>'decision_id' AS decision_id,
payload->>'command_id' AS command_id,
COALESCE(observed_at, ingested_at) AS matched_at
FROM swap_demand_events
WHERE quote_id = $1
OR payload->>'quote_id' = $1
UNION ALL
SELECT
CASE
WHEN quote_id = $1 OR payload->>'quote_id' = $1 THEN 'quote_id'
WHEN decision_key = $1 OR payload->>'decision_id' = $1 THEN 'decision_id'
WHEN payload->>'command_id' = $1 THEN 'command_id'
ELSE 'linked_identifier'
END AS matched_identifier_type,
COALESCE(quote_id, payload->>'quote_id') AS quote_id,
COALESCE(payload->>'decision_id', decision_key) AS decision_id,
payload->>'command_id' AS command_id,
COALESCE(observed_at, ingested_at) AS matched_at
FROM trade_decisions
WHERE quote_id = $1
OR decision_key = $1
OR payload->>'quote_id' = $1
OR payload->>'decision_id' = $1
OR payload->>'command_id' = $1
UNION ALL
SELECT
CASE
WHEN quote_id = $1 OR payload->>'quote_id' = $1 THEN 'quote_id'
WHEN payload->>'decision_id' = $1 THEN 'decision_id'
WHEN decision_key = $1 OR payload->>'command_id' = $1 THEN 'command_id'
ELSE 'linked_identifier'
END AS matched_identifier_type,
COALESCE(quote_id, payload->>'quote_id') AS quote_id,
payload->>'decision_id' AS decision_id,
COALESCE(payload->>'command_id', decision_key) AS command_id,
COALESCE(observed_at, ingested_at) AS matched_at
FROM execute_trade_commands
WHERE quote_id = $1
OR decision_key = $1
OR payload->>'quote_id' = $1
OR payload->>'decision_id' = $1
OR payload->>'command_id' = $1
UNION ALL
SELECT
CASE
WHEN quote_id = $1 OR payload->>'quote_id' = $1 THEN 'quote_id'
WHEN payload->>'decision_id' = $1 THEN 'decision_id'
WHEN decision_key = $1 OR payload->>'command_id' = $1 THEN 'command_id'
ELSE 'linked_identifier'
END AS matched_identifier_type,
COALESCE(quote_id, payload->>'quote_id') AS quote_id,
payload->>'decision_id' AS decision_id,
COALESCE(payload->>'command_id', decision_key) AS command_id,
COALESCE(observed_at, ingested_at) AS matched_at
FROM trade_execution_results
WHERE quote_id = $1
OR decision_key = $1
OR payload->>'quote_id' = $1
OR payload->>'decision_id' = $1
OR payload->>'command_id' = $1
UNION ALL
SELECT
CASE
WHEN quote_id = $1 OR payload->>'quote_id' = $1 THEN 'quote_id'
WHEN decision_id = $1 OR payload->>'decision_id' = $1 THEN 'decision_id'
WHEN command_id = $1 OR payload->>'command_id' = $1 THEN 'command_id'
ELSE 'linked_identifier'
END AS matched_identifier_type,
COALESCE(quote_id, payload->>'quote_id') AS quote_id,
COALESCE(decision_id, payload->>'decision_id') AS decision_id,
COALESCE(command_id, payload->>'command_id') AS command_id,
COALESCE(outcome_observed_at, submitted_at, command_at, computed_at) AS matched_at
FROM ${QUOTE_OUTCOMES_TABLE}
WHERE quote_id = $1
OR decision_id = $1
OR command_id = $1
OR payload->>'quote_id' = $1
OR payload->>'decision_id' = $1
OR payload->>'command_id' = $1
)
SELECT *
FROM candidates
ORDER BY matched_at DESC NULLS LAST
LIMIT 100
`,
[requestedIdentifier],
);
const identifiers = collectQuoteLifecycleLookupIdentifiers(candidatesResult.rows);
if (!hasLookupIdentifiers(identifiers)) {
return buildQuoteLifecycleLookupEvidence({
requestedIdentifier,
lookupAt,
matchedIdentifierType: null,
matchedIdentifiers: identifiers,
});
}
const [
recentQuotes,
recentTradeDecisions,
recentExecuteTradeCommands,
recentExecutionResults,
recentQuoteOutcomes,
] = await Promise.all([
loadLifecycleLookupQuotes(pool, identifiers),
loadLifecycleLookupTradeDecisions(pool, identifiers),
loadLifecycleLookupExecuteTradeCommands(pool, identifiers),
loadLifecycleLookupExecutionResults(pool, identifiers),
loadLifecycleLookupQuoteOutcomes(pool, identifiers),
]);
return buildQuoteLifecycleLookupEvidence({
requestedIdentifier,
lookupAt,
matchedIdentifierType: inferLookupMatchedIdentifierType(
requestedIdentifier,
candidatesResult.rows,
),
matchedIdentifiers: identifiers,
recentQuotes,
recentTradeDecisions,
recentExecuteTradeCommands,
recentExecutionResults,
recentQuoteOutcomes,
});
}
function buildQuoteLifecycleLookupEvidence({
requestedIdentifier,
lookupAt,
matchedIdentifierType = null,
matchedIdentifiers = null,
recentQuotes = [],
recentTradeDecisions = [],
recentExecuteTradeCommands = [],
recentExecutionResults = [],
recentQuoteOutcomes = [],
} = {}) {
return {
found: Boolean(
recentQuotes.length
|| recentTradeDecisions.length
|| recentExecuteTradeCommands.length
|| recentExecutionResults.length
|| recentQuoteOutcomes.length,
),
requested_identifier: requestedIdentifier,
matched_identifier_type: matchedIdentifierType,
matched_identifiers: normalizeQuoteLifecycleLookupIdentifiers(matchedIdentifiers),
lookup_at: lookupAt,
recentQuotes,
recentTradeDecisions,
recentExecuteTradeCommands,
recentExecutionResults,
recentQuoteOutcomes,
};
}
function collectQuoteLifecycleLookupIdentifiers(rows = []) {
return normalizeQuoteLifecycleLookupIdentifiers({
quote_ids: rows.map((row) => row.quote_id),
decision_ids: rows.map((row) => row.decision_id),
command_ids: rows.map((row) => row.command_id),
});
}
function normalizeQuoteLifecycleLookupIdentifiers(value = null) {
return {
quote_ids: uniqueTextValues(value?.quote_ids),
decision_ids: uniqueTextValues(value?.decision_ids),
command_ids: uniqueTextValues(value?.command_ids),
};
}
function uniqueTextValues(values = []) {
return [...new Set(
(values || [])
.map((value) => String(value || '').trim())
.filter(Boolean),
)];
}
function hasLookupIdentifiers(identifiers) {
return Boolean(
identifiers?.quote_ids?.length
|| identifiers?.decision_ids?.length
|| identifiers?.command_ids?.length,
);
}
function inferLookupMatchedIdentifierType(identifier, rows = []) {
const requestedIdentifier = String(identifier || '').trim();
const direct = rows.find((row) => (
row.matched_identifier_type
&& (
row.quote_id === requestedIdentifier
|| row.decision_id === requestedIdentifier
|| row.command_id === requestedIdentifier
)
));
return direct?.matched_identifier_type || rows[0]?.matched_identifier_type || null;
}
async function loadLifecycleLookupQuotes(pool, identifiers) {
if (!identifiers.quote_ids.length) return [];
const result = await pool.query(
`
SELECT observed_at, ingested_at, payload
FROM swap_demand_events
WHERE quote_id = ANY($1::text[])
OR payload->>'quote_id' = ANY($1::text[])
ORDER BY COALESCE(observed_at, ingested_at) DESC
LIMIT 20
`,
[identifiers.quote_ids],
);
return result.rows.map(normalizeRecentQuoteRow);
}
async function loadLifecycleLookupTradeDecisions(pool, identifiers) {
const result = await pool.query(
`
SELECT observed_at, ingested_at, payload
FROM trade_decisions
WHERE quote_id = ANY($1::text[])
OR payload->>'quote_id' = ANY($1::text[])
OR decision_key = ANY($2::text[])
OR payload->>'decision_id' = ANY($2::text[])
ORDER BY COALESCE(observed_at, ingested_at) DESC
LIMIT 20
`,
[identifiers.quote_ids, identifiers.decision_ids],
);
return result.rows.map((row) => ({
observed_at: toIsoTimestamp(row.observed_at),
ingested_at: toIsoTimestamp(row.ingested_at),
payload: row.payload,
}));
}
async function loadLifecycleLookupExecuteTradeCommands(pool, identifiers) {
const result = await pool.query(
`
SELECT observed_at, ingested_at, payload
FROM execute_trade_commands
WHERE quote_id = ANY($1::text[])
OR payload->>'quote_id' = ANY($1::text[])
OR payload->>'decision_id' = ANY($2::text[])
OR decision_key = ANY($3::text[])
OR payload->>'command_id' = ANY($3::text[])
ORDER BY COALESCE(observed_at, ingested_at) DESC
LIMIT 20
`,
[identifiers.quote_ids, identifiers.decision_ids, identifiers.command_ids],
);
return result.rows.map((row) => normalizeExecuteTradeCommandRow(row));
}
async function loadLifecycleLookupExecutionResults(pool, identifiers) {
const result = await pool.query(
`
SELECT
r.observed_at AS result_observed_at,
r.ingested_at AS result_ingested_at,
r.payload AS result_payload,
c.ingested_at AS command_ingested_at,
c.payload AS command_payload,
d.payload AS decision_payload,
o.payload AS outcome_payload
FROM trade_execution_results r
LEFT JOIN execute_trade_commands c
ON c.decision_key = r.decision_key
LEFT JOIN trade_decisions d
ON d.decision_key = COALESCE(c.payload->>'decision_id', r.payload->>'decision_id')
LEFT JOIN ${QUOTE_OUTCOMES_TABLE} o
ON o.quote_id = r.quote_id
WHERE r.quote_id = ANY($1::text[])
OR r.payload->>'quote_id' = ANY($1::text[])
OR r.payload->>'decision_id' = ANY($2::text[])
OR r.decision_key = ANY($3::text[])
OR r.payload->>'command_id' = ANY($3::text[])
ORDER BY COALESCE(r.observed_at, r.ingested_at) DESC
LIMIT 20
`,
[identifiers.quote_ids, identifiers.decision_ids, identifiers.command_ids],
);
return result.rows.map((row) => normalizeExecutionResultRow(row));
}
async function loadLifecycleLookupQuoteOutcomes(pool, identifiers) {
const result = await pool.query(
`
SELECT
quote_id,
decision_id,
command_id,
execution_result_status,
execution_result_code,
submitted_at,
command_at,
outcome_status,
outcome_observed_at,
outcome_source,
attribution_status,
attribution_method,
attributed_inventory_delta,
computed_at,
payload
FROM ${QUOTE_OUTCOMES_TABLE}
WHERE quote_id = ANY($1::text[])
OR payload->>'quote_id' = ANY($1::text[])
OR decision_id = ANY($2::text[])
OR payload->>'decision_id' = ANY($2::text[])
OR command_id = ANY($3::text[])
OR payload->>'command_id' = ANY($3::text[])
ORDER BY COALESCE(outcome_observed_at, submitted_at, command_at, computed_at) DESC
LIMIT 20
`,
[identifiers.quote_ids, identifiers.decision_ids, identifiers.command_ids],
);
return result.rows.map(normalizeQuoteOutcomeRow);
}
export async function loadIntentRequestPreflightByIdOrKey(pool, {
requestId = null,
idempotencyKey = null,

View file

@ -0,0 +1,91 @@
export function lifecycleRowIdentifier(row = null) {
return row?.quote_id || row?.decision_id || row?.command_id || null;
}
export function findLifecycleRowByIdentifier(rows = [], identifier = '') {
const normalizedIdentifier = String(identifier || '').trim();
if (!normalizedIdentifier) return null;
return (rows || []).find((row) => (
row?.quote_id === normalizedIdentifier
|| row?.decision_id === normalizedIdentifier
|| row?.command_id === normalizedIdentifier
)) || null;
}
export function createSelectedLifecycleInvestigation(row, {
source = 'live_row',
lookup = null,
now = Date.now(),
} = {}) {
const identifier = lifecycleRowIdentifier(row);
if (!identifier) return null;
const refreshedAt = toIsoTimestamp(now);
return {
identifier,
row,
source,
lookup,
selected_at: refreshedAt,
refreshed_at: refreshedAt,
error: null,
};
}
export function updateSelectedLifecycleFromLiveRows(selected, rows = [], {
now = Date.now(),
} = {}) {
if (!selected?.identifier) return selected || null;
const liveRow = findLifecycleRowByIdentifier(rows, selected.identifier);
if (!liveRow) return selected;
return {
...selected,
row: liveRow,
source: 'live_row',
refreshed_at: toIsoTimestamp(now),
error: null,
};
}
export function shouldRefreshSelectedFromDurable(selected, rows = []) {
if (!selected?.identifier) return false;
return !findLifecycleRowByIdentifier(rows, selected.identifier);
}
export function updateSelectedLifecycleFromLookup(selected, payload, {
identifier = null,
now = Date.now(),
} = {}) {
const requestedIdentifier = identifier || selected?.identifier || payload?.lookup?.requested_identifier || null;
const refreshedAt = toIsoTimestamp(now);
if (payload?.ok && payload.lifecycle_row) {
return {
...(selected || {}),
identifier: lifecycleRowIdentifier(payload.lifecycle_row) || requestedIdentifier,
row: payload.lifecycle_row,
source: 'durable_lookup',
lookup: payload.lookup || null,
selected_at: selected?.selected_at || refreshedAt,
refreshed_at: refreshedAt,
error: null,
};
}
return {
...(selected || {}),
identifier: requestedIdentifier,
row: selected?.row || null,
source: payload?.error === 'quote_lifecycle_not_found' || payload?.lookup?.found === false
? 'not_found'
: 'lookup_error',
lookup: payload?.lookup || null,
selected_at: selected?.selected_at || refreshedAt,
refreshed_at: refreshedAt,
error: payload?.error || null,
};
}
function toIsoTimestamp(value) {
const timestamp = typeof value === 'number' ? value : Date.parse(value);
if (!Number.isFinite(timestamp)) return new Date().toISOString();
return new Date(timestamp).toISOString();
}

View file

@ -5,6 +5,13 @@ import MetricCard from '../components/MetricCard.jsx';
import Pill from '../components/Pill.jsx';
import TableFrame from '../components/TableFrame.jsx';
import { formatAgeFromTimestamp, formatBoolean, formatEur, formatTimestamp, truncateMiddle } from '../lib/format.js';
import {
createSelectedLifecycleInvestigation,
findLifecycleRowByIdentifier,
shouldRefreshSelectedFromDurable,
updateSelectedLifecycleFromLiveRows,
updateSelectedLifecycleFromLookup,
} from '../lib/quoteLifecycleInvestigator.js';
const RESPONDED_STATES = new Set(['submitted', 'awaiting_outcome', 'not_filled', 'completed']);
const TRADING_PAIR_MODES = new Set(['maker', 'taker', 'both']);
@ -13,6 +20,17 @@ const COMPETITIVENESS_DETAIL_ROW_COUNT = 8;
const COMPETITIVENESS_LATENCY_ROW_COUNT = 6;
const COMPETITIVENESS_ROLLUP_ROW_COUNT = 8;
const QUOTE_LIFECYCLE_ROW_COUNT = 20;
const QUOTE_LIFECYCLE_LOOKUP_DEBOUNCE_MS = 750;
const QUOTE_LIFECYCLE_PROBLEM_FILTERS = [
['all', 'All states'],
['awaiting_executor', 'Awaiting executor'],
['approved_no_command', 'Approved, no command'],
['blocked_before_submit', 'Blocked before submit'],
['submission_failed', 'Submission failed'],
['quote_not_found_or_finished', 'Quote not found or finished'],
['strategy_rejected', 'Strategy rejected'],
['submitted_awaiting_outcome', 'Submitted or awaiting outcome'],
];
async function copyIdentifier(value) {
if (!value || !navigator?.clipboard?.writeText) return;
@ -23,6 +41,22 @@ async function copyIdentifier(value) {
}
}
async function fetchQuoteLifecycleLookup(identifier) {
const response = await fetch(`/api/strategy/quote-lifecycle/${encodeURIComponent(identifier)}`, {
headers: {
accept: 'application/json',
},
});
const text = await response.text();
const payload = text.trim() ? JSON.parse(text) : null;
if (response.status === 404 && payload?.error === 'quote_lifecycle_not_found') return payload;
if (!response.ok) {
throw new Error(payload?.error || text.trim() || `HTTP ${response.status}`);
}
return payload;
}
function useNow(intervalMs = 1000) {
const [now, setNow] = useState(() => Date.now());
@ -179,6 +213,86 @@ function isStrategyRejected(item) {
|| String(item?.lifecycle_label || '').toLowerCase() === 'rejected by strategy';
}
function isQuoteNotFoundOrFinished(item) {
return [
item?.reason_code,
item?.execution?.result_code,
item?.execution?.failure_category,
item?.execution?.outcome_reason,
item?.outcome?.outcome_reason,
item?.outcome_reason,
].some((value) => String(value || '').toLowerCase() === 'quote_not_found_or_finished');
}
function matchesLifecycleProblemFilter(item, filter) {
switch (filter) {
case 'awaiting_executor':
return item?.lifecycle_state === 'command_emitted';
case 'approved_no_command':
return item?.lifecycle_state === 'evaluated';
case 'blocked_before_submit':
return item?.lifecycle_state === 'blocked';
case 'submission_failed':
return item?.lifecycle_state === 'failed';
case 'quote_not_found_or_finished':
return isQuoteNotFoundOrFinished(item);
case 'strategy_rejected':
return isStrategyRejected(item);
case 'submitted_awaiting_outcome':
return item?.lifecycle_state === 'submitted' || item?.lifecycle_state === 'awaiting_outcome';
case 'all':
default:
return true;
}
}
function sourceLabel(source) {
if (source === 'live_row') return 'Live row';
if (source === 'durable_lookup') return 'Durable lookup';
if (source === 'not_found') return 'Not found';
if (source === 'lookup_error') return 'Lookup error';
if (source === 'loading') return 'Lookup loading';
return 'No selection';
}
function sourceTone(source) {
if (source === 'not_found') return 'warning';
if (source === 'lookup_error') return 'critical';
if (source === 'live_row' || source === 'durable_lookup') return 'healthy';
return 'unknown';
}
function formatCommandAge(item, now) {
const age = formatAgeFromTimestamp(item?.command_at, now);
return age === 'Unavailable' ? 'Unavailable' : `${age} old`;
}
function executorResultState(item) {
if (item?.execution?.status) return `Recorded: ${plainCodeLabel(item.execution.status)}`;
if (item?.execution_result_at) return 'Recorded';
if (item?.lifecycle_state === 'command_emitted') return 'Not recorded yet';
return 'Unavailable';
}
function nextExpectedStage(item) {
if (item?.lifecycle_state === 'command_emitted') {
return 'Executor result, then relay or outcome evidence';
}
if (item?.lifecycle_state === 'submitted' || item?.lifecycle_state === 'awaiting_outcome') {
return 'Durable outcome and settlement attribution';
}
if (['failed', 'blocked', 'not_filled', 'completed', 'rejected'].includes(item?.lifecycle_state)) {
return 'No later stage is recorded as expected from current evidence';
}
return 'Strategy decision or execute command';
}
function awaitingExecutorBrief(item, now) {
if (item?.lifecycle_state !== 'command_emitted') return null;
const command = item.command_id ? truncateMiddle(item.command_id, 24) : 'Unavailable';
return `Command ${command}; age ${formatCommandAge(item, now)}; executor result ${executorResultState(item)}; next ${nextExpectedStage(item)}`;
}
function StageCard({ title, at, status, children }) {
return (
<div className="lifecycle-stage-card">
@ -239,7 +353,7 @@ function TimingWaterfall({ timing }) {
);
}
function LifecycleDetails({ item }) {
function LifecycleDetails({ item, now = Date.now() }) {
const executionTiming = formatExecutionTiming(item.execution?.timing);
const makerTerms = formatMakerTerms(item.maker_terms);
const inventoryCheck = formatInventoryCheck(item.inventory_check);
@ -262,12 +376,17 @@ function LifecycleDetails({ item }) {
<StageCard at={item.command_at} status={item.command_id ? 'Command recorded' : 'No command'} title="3. Executor command">
<IdentifierRow label="Command" value={item.command_id} />
<div className="status-subtle">{`Command timestamp: ${formatTimestamp(item.command_at)}`}</div>
<div className="status-subtle">{`Command age: ${formatCommandAge(item, now)}`}</div>
<div className="status-subtle">{`Executor result: ${executorResultState(item)}`}</div>
<div className="status-subtle">{`Next expected: ${nextExpectedStage(item)}`}</div>
<div>{makerTerms || formatTerms(item.submitted_terms)}</div>
{makerTerms ? <div className="status-subtle">{formatTerms(item.submitted_terms)}</div> : null}
</StageCard>
<StageCard at={item.execution_result_at} status={item.execution?.status || 'No relay result'} title="4. Relay response">
<div>{item.execution?.result_code || 'No executor result code stored'}</div>
{item.execution?.failure_category ? <div className="status-subtle">{plainCodeLabel(item.execution.failure_category)}</div> : null}
{item.execution?.error_message ? <div className="status-subtle">{item.execution.error_message}</div> : null}
{!item.execution?.error_message && item.execution?.note ? <div className="status-subtle">{item.execution.note}</div> : null}
{executionTiming ? <div className="status-subtle">{executionTiming}</div> : null}
@ -294,6 +413,81 @@ function LifecycleDetails({ item }) {
);
}
function QuoteLifecycleInvestigator({
selected,
lookupState,
lookupError,
now,
onRefresh,
}) {
const row = selected?.row || null;
const source = lookupState === 'loading' ? 'loading' : selected?.source;
return (
<div className="quote-investigator-panel">
<div className="quote-investigator-head">
<div>
<div className="eyebrow">Pinned quote investigator</div>
<h4>{row?.quote_id ? truncateMiddle(row.quote_id, 46) : selected?.identifier ? truncateMiddle(selected.identifier, 46) : 'No quote selected'}</h4>
<div className="panel-subtitle">
{row
? `${row.lifecycle_label || 'Unavailable'} - ${row.reason_text || 'Unavailable'}`
: selected?.source === 'not_found'
? 'No durable lifecycle evidence matched that identifier.'
: 'Select a row or look up an identifier to inspect durable lifecycle evidence.'}
</div>
</div>
<div className="quote-investigator-actions">
<Pill label={sourceLabel(source)} stateLabel={sourceTone(source)} />
{selected?.refreshed_at ? <Pill label={`Refreshed ${formatRelativeAge(selected.refreshed_at, now)}`} stateLabel="info" /> : null}
<button
className="button secondary"
disabled={!selected?.identifier || lookupState === 'loading'}
onClick={() => onRefresh?.(selected.identifier)}
type="button"
>
Refresh lookup
</button>
</div>
</div>
{lookupError ? <div className="banner error">{lookupError}</div> : null}
{row ? (
<>
<div className="quote-investigator-facts">
<div>
<div className="status-label">Lifecycle</div>
<div className="status-value">{row.lifecycle_label || 'Unavailable'}</div>
<div className="status-subtle mono">{row.reason_code || 'Unavailable'}</div>
</div>
<div>
<div className="status-label">Command age</div>
<div className="status-value">{formatCommandAge(row, now)}</div>
<div className="status-subtle mono">{row.command_id || 'Unavailable'}</div>
</div>
<div>
<div className="status-label">Executor result</div>
<div className="status-value">{executorResultState(row)}</div>
<div className="status-subtle">{formatTimestamp(row.execution_result_at)}</div>
</div>
<div>
<div className="status-label">Next expected</div>
<div className="status-value quote-investigator-next">{nextExpectedStage(row)}</div>
<div className="status-subtle">{formatTimestamp(row.latest_stage_at)}</div>
</div>
</div>
<LifecycleDetails item={row} now={now} />
</>
) : (
<EmptyState>
Durable lifecycle evidence is unavailable for the current identifier.
</EmptyState>
)}
</div>
);
}
function pairDisplayLabel(pairId, pairConfig) {
const pair = (pairConfig?.pairs || []).find((entry) => (
(entry.pair_id || entry.pairId || entry.pair) === pairId
@ -661,15 +855,45 @@ function MakerCompetitivenessSection({ summary, pairConfig }) {
function QuoteLifecycleTable({ items }) {
const [expanded, setExpanded] = useState(() => new Set());
const [showStrategyRejected, setShowStrategyRejected] = useState(true);
const [problemFilter, setProblemFilter] = useState('all');
const latestItemsRef = useRef(items || []);
const [quoteDisplayPaused, setQuoteDisplayPaused] = useState(false);
const [displayItems, setDisplayItems] = useState(() => items || []);
const [lookupInput, setLookupInput] = useState('');
const [lookupState, setLookupState] = useState('idle');
const [lookupError, setLookupError] = useState(null);
const [selectedInvestigation, setSelectedInvestigation] = useState(null);
const selectedInvestigationRef = useRef(null);
const lookupTimerRef = useRef(null);
const liveNow = useNow();
const [displayNow, setDisplayNow] = useState(() => Date.now());
useEffect(() => {
selectedInvestigationRef.current = selectedInvestigation;
}, [selectedInvestigation]);
useEffect(() => () => {
if (lookupTimerRef.current) window.clearTimeout(lookupTimerRef.current);
}, []);
useEffect(() => {
latestItemsRef.current = items || [];
if (!quoteDisplayPaused) setDisplayItems(items || []);
const selected = selectedInvestigationRef.current;
if (!selected?.identifier) return;
const updated = updateSelectedLifecycleFromLiveRows(selected, items || [], {
now: Date.now(),
});
if (updated !== selected) {
setSelectedInvestigation(updated);
setLookupState('live');
setLookupError(null);
return;
}
if (shouldRefreshSelectedFromDurable(selected, items || [])) {
scheduleDurableLookup(selected.identifier);
}
}, [items, quoteDisplayPaused]);
useEffect(() => {
@ -680,9 +904,17 @@ function QuoteLifecycleTable({ items }) {
() => displayItems.filter((item) => isStrategyRejected(item)).length,
[displayItems],
);
const problemFilteredItems = useMemo(
() => displayItems.filter((item) => matchesLifecycleProblemFilter(item, problemFilter)),
[displayItems, problemFilter],
);
const visibleItems = useMemo(
() => (showStrategyRejected ? displayItems : displayItems.filter((item) => !isStrategyRejected(item))),
[displayItems, showStrategyRejected],
() => (
showStrategyRejected || problemFilter === 'strategy_rejected'
? problemFilteredItems
: problemFilteredItems.filter((item) => !isStrategyRejected(item))
),
[problemFilteredItems, problemFilter, showStrategyRejected],
);
const visibleRows = fixedRows(visibleItems, QUOTE_LIFECYCLE_ROW_COUNT);
const emptyRowsMessage = !displayItems.length
@ -700,6 +932,77 @@ function QuoteLifecycleTable({ items }) {
});
}
async function performLookup(identifier, { silent = false } = {}) {
const trimmed = String(identifier || '').trim();
if (!trimmed) return;
if (lookupTimerRef.current) {
window.clearTimeout(lookupTimerRef.current);
lookupTimerRef.current = null;
}
if (!silent) setLookupState('loading');
setLookupError(null);
try {
const payload = await fetchQuoteLifecycleLookup(trimmed);
setSelectedInvestigation((current) => updateSelectedLifecycleFromLookup(
current?.identifier === trimmed ? current : { identifier: trimmed, row: null },
payload,
{ identifier: trimmed, now: Date.now() },
));
setLookupState(payload?.ok ? 'found' : 'not_found');
setLookupError(null);
} catch (error) {
const message = error?.message || 'Lookup failed';
setSelectedInvestigation((current) => updateSelectedLifecycleFromLookup(
current?.identifier === trimmed ? current : { identifier: trimmed, row: null },
{
error: message,
lookup: {
requested_identifier: trimmed,
matched_identifier_type: null,
found: false,
lookup_at: new Date().toISOString(),
latest_stage_at: null,
matched_identifiers: {
quote_ids: [],
decision_ids: [],
command_ids: [],
},
},
},
{ identifier: trimmed, now: Date.now() },
));
setLookupState('error');
setLookupError(message);
}
}
function scheduleDurableLookup(identifier) {
if (!identifier) return;
if (lookupTimerRef.current) window.clearTimeout(lookupTimerRef.current);
lookupTimerRef.current = window.setTimeout(() => {
performLookup(identifier, { silent: true }).catch(() => {});
}, QUOTE_LIFECYCLE_LOOKUP_DEBOUNCE_MS);
}
function inspectRow(item) {
const next = createSelectedLifecycleInvestigation(item, {
source: 'live_row',
now: Date.now(),
});
if (!next) return;
setSelectedInvestigation(next);
setLookupInput(next.identifier);
setLookupState('live');
setLookupError(null);
scheduleDurableLookup(next.identifier);
}
function submitLookup(event) {
event.preventDefault();
performLookup(lookupInput).catch(() => {});
}
function applyLatestLifecycleDisplay() {
setDisplayItems(latestItemsRef.current || []);
setDisplayNow(Date.now());
@ -712,7 +1015,29 @@ function QuoteLifecycleTable({ items }) {
return (
<>
<form className="quote-lifecycle-lookup" onSubmit={submitLookup}>
<label className="field quote-lifecycle-lookup-field">
<span>Lookup identifier</span>
<input
onChange={(event) => setLookupInput(event.target.value)}
placeholder="quote_id, decision_id, or command_id"
type="search"
value={lookupInput}
/>
</label>
<button className="button" disabled={!lookupInput.trim() || lookupState === 'loading'} type="submit">
Lookup
</button>
</form>
<div className="quote-lifecycle-controls">
<label className="field quote-lifecycle-filter-field">
<span>Problem state</span>
<select value={problemFilter} onChange={(event) => setProblemFilter(event.target.value)}>
{QUOTE_LIFECYCLE_PROBLEM_FILTERS.map(([value, label]) => (
<option key={value} value={value}>{label}</option>
))}
</select>
</label>
<label className="toggle-field">
<input
checked={showStrategyRejected}
@ -732,9 +1057,17 @@ function QuoteLifecycleTable({ items }) {
<Pill label={quoteDisplayPaused ? 'Display paused' : 'Live updates'} stateLabel={quoteDisplayPaused ? 'warning' : 'healthy'} />
</div>
<div className="status-subtle quote-lifecycle-snapshot-note">
Live rows update as they arrive. Fixed row slots and clamped cells keep the table height stable.
Pause freezes only the rolling table. The pinned quote investigator can still refresh from durable lookup.
</div>
<QuoteLifecycleInvestigator
lookupError={lookupError}
lookupState={lookupState}
now={liveNow}
onRefresh={(identifier) => performLookup(identifier).catch(() => {})}
selected={selectedInvestigation}
/>
<TableFrame className="quote-lifecycle-frame">
<table className="quote-lifecycle-table">
<thead>
@ -760,13 +1093,18 @@ function QuoteLifecycleTable({ items }) {
}
const rowKey = item.quote_id || item.decision_id || item.command_id || item.latest_stage_at || String(index);
const isExpanded = expanded.has(rowKey);
const isSelected = Boolean(
selectedInvestigation?.identifier
&& findLifecycleRowByIdentifier([item], selectedInvestigation.identifier),
);
const quoteTime = item.quote_activity_at || item.latest_stage_at;
const updatedText = item.latest_stage_at && item.latest_stage_at !== item.quote_activity_at
? `Updated ${formatTimestamp(item.latest_stage_at)} - ${formatRelativeAge(item.latest_stage_at, displayNow)}`
: '';
const awaitingBrief = awaitingExecutorBrief(item, displayNow);
return (
<Fragment key={rowKey}>
<tr className={`quote-lifecycle-row${item.live_flash_at ? ' quote-row-flash' : ''}`} key={`${rowKey}:row`}>
<tr className={`quote-lifecycle-row${item.live_flash_at ? ' quote-row-flash' : ''}${isSelected ? ' is-selected' : ''}`} key={`${rowKey}:row`}>
<td>
<div className="quote-lifecycle-cell">
<div className="lifecycle-line lifecycle-clamp-one">{formatTimestamp(quoteTime)}</div>
@ -788,8 +1126,9 @@ function QuoteLifecycleTable({ items }) {
<td><Pill label={item.lifecycle_label} stateLabel={item.lifecycle_tone} /></td>
<td>
<div className="quote-lifecycle-cell">
<div className="lifecycle-line lifecycle-clamp-two">{item.reason_text}</div>
<div className="status-subtle mono lifecycle-line lifecycle-clamp-one">{item.reason_code || 'reason_unknown'}</div>
<div className="lifecycle-line lifecycle-clamp-two">{item.reason_text || 'Unavailable'}</div>
<div className="status-subtle mono lifecycle-line lifecycle-clamp-one">{item.reason_code || 'Unavailable'}</div>
{awaitingBrief ? <div className="status-subtle lifecycle-line lifecycle-clamp-two">{awaitingBrief}</div> : null}
</div>
</td>
<td>
@ -800,14 +1139,19 @@ function QuoteLifecycleTable({ items }) {
</div>
</td>
<td>
<button className="button secondary" onClick={() => toggle(rowKey)} type="button">
{isExpanded ? 'Hide lifecycle' : 'Show lifecycle'}
</button>
<div className="lifecycle-action-stack">
<button className="button secondary" onClick={() => inspectRow(item)} type="button">
Inspect quote
</button>
<button className="button secondary" onClick={() => toggle(rowKey)} type="button">
{isExpanded ? 'Hide lifecycle' : 'Show lifecycle'}
</button>
</div>
</td>
</tr>
{isExpanded ? (
<tr className="lifecycle-expanded-row" key={`${rowKey}:details`}>
<td colSpan={8}><LifecycleDetails item={item} /></td>
<td colSpan={8}><LifecycleDetails item={item} now={displayNow} /></td>
</tr>
) : null}
</Fragment>

View file

@ -618,10 +618,85 @@ table.lifecycle-table th:nth-child(5) {
margin-bottom: 12px;
}
.quote-lifecycle-lookup {
display: flex;
flex-wrap: wrap;
align-items: end;
gap: 10px;
margin-bottom: 12px;
}
.quote-lifecycle-lookup-field {
flex: 1 1 320px;
}
.quote-lifecycle-filter-field {
min-width: 230px;
}
.quote-lifecycle-lookup-field span,
.quote-lifecycle-filter-field span {
font-size: 0.84rem;
color: var(--muted);
}
.quote-lifecycle-snapshot-note {
margin-bottom: 12px;
}
.quote-investigator-panel {
display: grid;
gap: 14px;
margin-bottom: 14px;
padding: 16px;
border: 1px solid var(--line);
border-radius: 16px;
background: rgba(255, 255, 255, 0.5);
}
.quote-investigator-head {
display: flex;
justify-content: space-between;
gap: 16px;
align-items: flex-start;
}
.quote-investigator-head h4 {
margin: 4px 0 0;
overflow-wrap: anywhere;
}
.quote-investigator-actions {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: 8px;
}
.quote-investigator-facts {
display: grid;
gap: 12px;
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}
.quote-investigator-facts > div {
min-width: 0;
padding: 12px;
border: 1px solid var(--line);
border-radius: 14px;
background: var(--panel-2);
}
.quote-investigator-facts .status-value {
margin-top: 6px;
font-size: 1rem;
overflow-wrap: anywhere;
}
.quote-investigator-next {
line-height: 1.3;
}
.toggle-field {
display: inline-flex;
align-items: center;
@ -651,6 +726,11 @@ table.lifecycle-table th:nth-child(5) {
height: 112px;
}
.quote-lifecycle-table tbody tr.quote-lifecycle-row.is-selected td {
box-shadow: inset 3px 0 0 rgba(31, 122, 90, 0.76);
background: rgba(31, 122, 90, 0.08);
}
.quote-lifecycle-table td {
overflow: hidden;
}
@ -752,7 +832,12 @@ table.lifecycle-table th:nth-child(5) {
.quote-lifecycle-table td:nth-child(8),
.successful-trades-table th:nth-child(7),
.successful-trades-table td:nth-child(7) {
width: 150px;
width: 170px;
}
.lifecycle-action-stack {
display: grid;
gap: 8px;
}
.successful-trades-table {

View file

@ -37,6 +37,16 @@ test('operator dashboard API auth failures are JSON for frontend fetches', () =>
assert.match(source, /sendJson\(res, 401, \{ error: 'authentication_required' \}\)/);
});
test('operator dashboard exposes authenticated durable quote lifecycle lookup API', () => {
assert.match(source, /quote-lifecycle/);
assert.match(source, /lifecycleLookupMatch/);
assert.match(source, /loadQuoteLifecycleLookupEvidence/);
assert.match(source, /buildQuoteLifecycleLookupResponse/);
assert.match(source, /quote_lifecycle_identifier_required/);
assert.match(source, /quote_lifecycle_not_found/);
assert.match(source, /payload\.ok \? 200 : 404/);
});
test('operator dashboard bootstrap does not synchronously refresh intent request outcomes', () => {
assert.match(source, /loadRecentIntentRequests\(pool, \{[\s\S]*refreshOutcomes: false/);
});

View file

@ -32,9 +32,29 @@ test('strategy page owns consolidated quote lifecycle and successful trade table
assert.match(strategySource, /setDisplayItems\(items \|\| \[\]\)/);
assert.match(strategySource, /useNow\(\)/);
assert.match(strategySource, /Live updates/);
assert.match(strategySource, /Live rows update as they arrive/);
assert.match(strategySource, /Pause freezes only the rolling table/);
assert.match(strategySource, /Pinned quote investigator/);
assert.match(strategySource, /Lookup identifier/);
assert.match(strategySource, /Inspect quote/);
assert.match(strategySource, /Refresh lookup/);
assert.match(strategySource, /fetchQuoteLifecycleLookup/);
assert.match(strategySource, /\/api\/strategy\/quote-lifecycle\//);
assert.match(strategySource, /QUOTE_LIFECYCLE_PROBLEM_FILTERS/);
assert.match(strategySource, /awaiting_executor/);
assert.match(strategySource, /quote_not_found_or_finished/);
assert.match(strategySource, /approved_no_command/);
assert.match(strategySource, /submitted_awaiting_outcome/);
assert.match(strategySource, /updateSelectedLifecycleFromLiveRows/);
assert.match(strategySource, /shouldRefreshSelectedFromDurable/);
assert.match(strategySource, /updateSelectedLifecycleFromLookup/);
assert.match(strategySource, /Command age:/);
assert.match(strategySource, /Executor result:/);
assert.match(strategySource, /Next expected:/);
assert.match(strategySource, /visibleRows/);
assert.match(strategySource, /quote-lifecycle-placeholder-row/);
assert.match(stylesSource, /\.quote-investigator-panel/);
assert.match(stylesSource, /\.quote-lifecycle-lookup/);
assert.match(stylesSource, /\.quote-lifecycle-table tbody tr\.quote-lifecycle-row\.is-selected/);
assert.match(stylesSource, /\.shell \{[\s\S]*?width: 100%;[\s\S]*?margin: 0;[\s\S]*?\}/);
assert.doesNotMatch(stylesSource, /^\s*max-width\s*:/m);
assert.doesNotMatch(strategySource, /maxWidth:/);

View file

@ -7,6 +7,7 @@ import {
buildDashboardBootstrap,
buildDashboardControlErrorResponse,
buildLiveStatusBar,
buildQuoteLifecycleLookupResponse,
buildProfitabilitySummary,
createDashboardLiveState,
deriveQuoteLifecycleRows,
@ -15,12 +16,19 @@ import {
resolveDashboardControlTimeoutMs,
} from '../src/core/operator-dashboard.mjs';
import { formatAge, formatAgeFromTimestamp } from '../src/operator-dashboard/static/lib/format.js';
import {
createSelectedLifecycleInvestigation,
shouldRefreshSelectedFromDurable,
updateSelectedLifecycleFromLiveRows,
updateSelectedLifecycleFromLookup,
} from '../src/operator-dashboard/static/lib/quoteLifecycleInvestigator.js';
import { dashboardReducer } from '../src/operator-dashboard/static/state/dashboardReducer.js';
import {
buildDashboardSessionToken,
parseBasicAuthorizationHeader,
resolveDashboardRequestAuth,
} from '../src/core/operator-dashboard-auth.mjs';
import { loadQuoteLifecycleLookupEvidence } from '../src/lib/postgres.mjs';
function buildConfig() {
const tradingBtc = {
@ -564,6 +572,272 @@ test('socket lifecycle messages replace strategy rows without page refresh', ()
assert.equal(next.dashboard.strategy.strategy_state.recent_lifecycle_rows[0].live_flash_at, '2026-04-04T09:00:00.000Z');
});
test('selected awaiting executor quote remains inspectable after live rows move and refreshes from durable result', () => {
const config = buildConfig();
const awaiting = buildQuoteLifecycleLookupResponse({
config,
identifier: 'quote-follow',
evidence: {
lookup_at: '2026-06-12T10:00:00.000Z',
matched_identifier_type: 'quote_id',
matched_identifiers: {
quote_ids: ['quote-follow'],
decision_ids: ['decision-follow'],
command_ids: ['cmd-follow'],
},
recentTradeDecisions: [{
observed_at: '2026-06-12T09:59:58.000Z',
payload: {
quote_id: 'quote-follow',
decision_id: 'decision-follow',
pair: config.activePair,
decision: 'actionable',
decision_reason: 'actionable',
},
}],
recentExecuteTradeCommands: [{
observed_at: '2026-06-12T09:59:59.000Z',
command_id: 'cmd-follow',
decision_id: 'decision-follow',
quote_id: 'quote-follow',
pair: config.activePair,
}],
},
});
assert.equal(awaiting.ok, true);
assert.equal(awaiting.lifecycle_row.lifecycle_state, 'command_emitted');
assert.equal(awaiting.lifecycle_row.lifecycle_label, 'Awaiting executor');
const selected = createSelectedLifecycleInvestigation(awaiting.lifecycle_row, {
now: '2026-06-12T10:00:00.000Z',
});
const afterLiveMove = updateSelectedLifecycleFromLiveRows(selected, [{
quote_id: 'quote-newer',
lifecycle_state: 'observed',
}], {
now: '2026-06-12T10:00:02.000Z',
});
assert.equal(afterLiveMove.row.quote_id, 'quote-follow');
assert.equal(afterLiveMove.row.lifecycle_state, 'command_emitted');
assert.equal(shouldRefreshSelectedFromDurable(afterLiveMove, [{ quote_id: 'quote-newer' }]), true);
const later = buildQuoteLifecycleLookupResponse({
config,
identifier: 'cmd-follow',
evidence: {
lookup_at: '2026-06-12T10:00:04.000Z',
matched_identifier_type: 'command_id',
matched_identifiers: {
quote_ids: ['quote-follow'],
decision_ids: ['decision-follow'],
command_ids: ['cmd-follow'],
},
recentExecuteTradeCommands: [{
observed_at: '2026-06-12T09:59:59.000Z',
command_id: 'cmd-follow',
decision_id: 'decision-follow',
quote_id: 'quote-follow',
pair: config.activePair,
}],
recentExecutionResults: [{
command_id: 'cmd-follow',
decision_id: 'decision-follow',
quote_id: 'quote-follow',
pair: config.activePair,
result_at: '2026-06-12T10:00:03.000Z',
status: 'failed',
result_code: 'submission_failed',
failure_category: 'quote_not_found_or_finished',
note: 'relay no longer has the quote',
}],
},
});
const refreshed = updateSelectedLifecycleFromLookup(afterLiveMove, later, {
identifier: 'cmd-follow',
now: '2026-06-12T10:00:04.000Z',
});
assert.equal(refreshed.source, 'durable_lookup');
assert.equal(refreshed.row.quote_id, 'quote-follow');
assert.equal(refreshed.row.lifecycle_state, 'failed');
assert.equal(refreshed.row.reason_code, 'quote_not_found_or_finished');
assert.match(refreshed.row.reason_text, /not found or already finished/i);
});
test('quote lifecycle lookup response matches quote, decision, and command identifiers from durable evidence', () => {
const config = buildConfig();
const evidence = {
lookup_at: '2026-06-12T10:10:00.000Z',
matched_identifiers: {
quote_ids: ['quote-lookup'],
decision_ids: ['decision-lookup'],
command_ids: ['cmd-lookup'],
},
recentQuotes: [{
quote_id: 'quote-lookup',
pair: config.activePair,
asset_in: config.tradingBtc.assetId,
asset_out: config.tradingEure.assetId,
amount_in: '100',
amount_out: '200',
observed_at: '2026-06-12T10:09:57.000Z',
}],
recentTradeDecisions: [{
observed_at: '2026-06-12T10:09:58.000Z',
payload: {
quote_id: 'quote-lookup',
decision_id: 'decision-lookup',
pair: config.activePair,
decision: 'actionable',
decision_reason: 'actionable',
},
}],
recentExecuteTradeCommands: [{
observed_at: '2026-06-12T10:09:59.000Z',
command_id: 'cmd-lookup',
decision_id: 'decision-lookup',
quote_id: 'quote-lookup',
pair: config.activePair,
}],
};
const byQuote = buildQuoteLifecycleLookupResponse({
config,
identifier: 'quote-lookup',
evidence: { ...evidence, matched_identifier_type: 'quote_id' },
});
const byDecision = buildQuoteLifecycleLookupResponse({
config,
identifier: 'decision-lookup',
evidence: { ...evidence, matched_identifier_type: 'decision_id' },
});
const byCommand = buildQuoteLifecycleLookupResponse({
config,
identifier: 'cmd-lookup',
evidence: { ...evidence, matched_identifier_type: 'command_id' },
});
assert.equal(byQuote.lifecycle_row.quote_id, 'quote-lookup');
assert.equal(byDecision.lifecycle_row.decision_id, 'decision-lookup');
assert.equal(byCommand.lifecycle_row.command_id, 'cmd-lookup');
assert.equal(byQuote.lookup.matched_identifier_type, 'quote_id');
assert.equal(byDecision.lookup.matched_identifier_type, 'decision_id');
assert.equal(byCommand.lookup.matched_identifier_type, 'command_id');
});
test('postgres quote lifecycle lookup loader links durable quote, decision, and command evidence', async () => {
const queries = [];
const pool = {
async query(sql, params = []) {
queries.push({ sql, params });
if (sql.includes('WITH candidates')) {
return {
rows: [{
matched_identifier_type: 'decision_id',
quote_id: 'quote-loader',
decision_id: 'decision-loader',
command_id: 'cmd-loader',
matched_at: '2026-06-12T10:20:00.000Z',
}],
};
}
if (sql.includes('FROM swap_demand_events')) {
return {
rows: [{
observed_at: '2026-06-12T10:19:57.000Z',
ingested_at: '2026-06-12T10:19:57.000Z',
payload: {
quote_id: 'quote-loader',
pair: 'btc->eure',
asset_in: 'btc',
asset_out: 'eure',
amount_in: '100',
amount_out: '200',
},
}],
};
}
if (sql.includes('FROM trade_decisions')) {
return {
rows: [{
observed_at: '2026-06-12T10:19:58.000Z',
ingested_at: '2026-06-12T10:19:58.000Z',
payload: {
quote_id: 'quote-loader',
decision_id: 'decision-loader',
decision: 'actionable',
decision_reason: 'actionable',
},
}],
};
}
if (sql.includes('FROM execute_trade_commands')) {
return {
rows: [{
observed_at: '2026-06-12T10:19:59.000Z',
ingested_at: '2026-06-12T10:19:59.000Z',
payload: {
quote_id: 'quote-loader',
decision_id: 'decision-loader',
command_id: 'cmd-loader',
},
}],
};
}
if (sql.includes('FROM trade_execution_results r')) {
return {
rows: [{
result_observed_at: '2026-06-12T10:20:01.000Z',
result_ingested_at: '2026-06-12T10:20:01.000Z',
result_payload: {
quote_id: 'quote-loader',
decision_id: 'decision-loader',
command_id: 'cmd-loader',
status: 'failed',
result_code: 'submission_failed',
failure_category: 'quote_not_found_or_finished',
},
command_ingested_at: '2026-06-12T10:19:59.000Z',
command_payload: {
quote_id: 'quote-loader',
decision_id: 'decision-loader',
command_id: 'cmd-loader',
},
decision_payload: {
quote_id: 'quote-loader',
decision_id: 'decision-loader',
},
outcome_payload: null,
}],
};
}
if (sql.includes('FROM quote_outcome_attributions')) {
return { rows: [] };
}
return { rows: [] };
},
};
const evidence = await loadQuoteLifecycleLookupEvidence(pool, {
identifier: 'decision-loader',
});
assert.equal(evidence.found, true);
assert.equal(evidence.matched_identifier_type, 'decision_id');
assert.deepEqual(evidence.matched_identifiers, {
quote_ids: ['quote-loader'],
decision_ids: ['decision-loader'],
command_ids: ['cmd-loader'],
});
assert.equal(evidence.recentQuotes[0].quote_id, 'quote-loader');
assert.equal(evidence.recentTradeDecisions[0].payload.decision_id, 'decision-loader');
assert.equal(evidence.recentExecuteTradeCommands[0].command_id, 'cmd-loader');
assert.equal(evidence.recentExecutionResults[0].failure_category, 'quote_not_found_or_finished');
assert.equal(queries[0].params[0], 'decision-loader');
});
test('dashboard age formatting uses seconds first and exact timestamp deltas', () => {
assert.equal(formatAge(999), '999 ms');
assert.equal(formatAge(1_500), '1s');