From 61ba8f9208fa6a43bd5a3de866adb07e3d76f48a Mon Sep 17 00:00:00 2001 From: philipp Date: Fri, 10 Apr 2026 15:31:11 +0200 Subject: [PATCH] Expire submitted quotes using latest inventory snapshot Proof: Fixes quote outcome expiry so no-settlement submissions age to not_filled using the latest durable inventory snapshot, even when the last actual inventory movement is older. Assumptions: A later unchanged inventory snapshot after the quote deadline plus settlement grace is valid evidence that no matching settled inventory delta occurred in the repo-owned path. Still fake: Not-filled remains inferred from deadline plus inventory evidence until venue-native terminal outcome events are persisted. --- src/core/quote-outcomes.mjs | 4 +--- test/quote-outcomes.test.mjs | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/core/quote-outcomes.mjs b/src/core/quote-outcomes.mjs index 191d338..7ab409f 100644 --- a/src/core/quote-outcomes.mjs +++ b/src/core/quote-outcomes.mjs @@ -37,9 +37,7 @@ export function deriveQuoteOutcomeRecords({ inventorySnapshots, activeAssetIds, }); - const latestInventoryAt = inventoryDeltas.length - ? inventoryDeltas[inventoryDeltas.length - 1].observed_at - : latestSnapshotTimestamp(inventorySnapshots); + const latestInventoryAt = latestSnapshotTimestamp(inventorySnapshots); const candidatesByMovement = new Map(); const candidatesByQuote = new Map(); diff --git a/test/quote-outcomes.test.mjs b/test/quote-outcomes.test.mjs index a73c47b..73a4dab 100644 --- a/test/quote-outcomes.test.mjs +++ b/test/quote-outcomes.test.mjs @@ -130,6 +130,34 @@ test('submitted quote without settlement becomes not filled only after deadline assert.equal(outcome.attributed_inventory_delta, null); }); +test('older unrelated inventory movement does not block later no-fill expiry', () => { + const [outcome] = deriveQuoteOutcomeRecords({ + submissions: [submittedResult('quote-after-old-move', '2026-04-02T18:20:00.000Z')], + commands: [exactOutCommand('quote-after-old-move')], + inventorySnapshots: [ + inventorySnapshot('2026-04-02T18:13:00.000Z', { + [BTC.assetId]: '0', + [EURE.assetId]: '100000000000000000000', + }), + inventorySnapshot('2026-04-02T18:13:33.000Z', { + [BTC.assetId]: '1', + [EURE.assetId]: '99999999999999999999', + }), + inventorySnapshot('2026-04-02T18:22:00.000Z', { + [BTC.assetId]: '1', + [EURE.assetId]: '99999999999999999999', + }), + ], + btcAsset: BTC, + eureAsset: EURE, + now: '2026-04-02T18:22:00.000Z', + }); + + assert.equal(outcome.outcome_status, 'not_filled'); + assert.equal(outcome.outcome_reason, 'deadline_elapsed_without_settlement'); + assert.equal(outcome.payload.evidence.latest_inventory_observed_at, '2026-04-02T18:22:00.000Z'); +}); + test('ambiguous inventory movement is not counted as completed settlement', () => { const outcomes = deriveQuoteOutcomeRecords({ submissions: [