From d24ac8ee5903fcb4a4350600fe671f7863a1f15d Mon Sep 17 00:00:00 2001 From: philipp Date: Thu, 7 May 2026 16:18:41 +0200 Subject: [PATCH] Keep history replay inside preflight freshness Proof: npm test (143 passing); npm run operator-dashboard:build; git diff --cached --check. Assumptions: Derived portfolio/outcome refreshes are only useful for live freshness when the source event is within the same 30s inventory window enforced by request preflight. Still fake: No live asset migration submitted; legacy btc.omft remains tracked but not converted to nBTC by this change. --- deploy/k8s/base/unrip.yaml | 2 +- src/lib/config.mjs | 2 +- test/history-writer-refresh-config.test.mjs | 37 +++++++++++++++++++++ test/history-writer-refresh-policy.test.mjs | 18 ++++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 test/history-writer-refresh-config.test.mjs diff --git a/deploy/k8s/base/unrip.yaml b/deploy/k8s/base/unrip.yaml index 34d45a0..00093fc 100644 --- a/deploy/k8s/base/unrip.yaml +++ b/deploy/k8s/base/unrip.yaml @@ -75,7 +75,7 @@ data: KAFKA_CONSUMER_GROUP_EXECUTOR: trade-executor-v1 KAFKA_CONSUMER_GROUP_OPS_SENTINEL: ops-sentinel-v1 KAFKA_CONSUMER_GROUP_OPERATOR_DASHBOARD: operator-dashboard-v1 - HISTORY_WRITER_DERIVED_REFRESH_MAX_EVENT_AGE_MS: "300000" + HISTORY_WRITER_DERIVED_REFRESH_MAX_EVENT_AGE_MS: "30000" STRATEGY_STATE_DIR: /var/lib/unrip/strategy-state EXECUTOR_STATE_DIR: /var/lib/unrip/executor-state LIQUIDITY_STATE_DIR: /var/lib/unrip/liquidity-state diff --git a/src/lib/config.mjs b/src/lib/config.mjs index a6ebae1..a88fe97 100644 --- a/src/lib/config.mjs +++ b/src/lib/config.mjs @@ -39,7 +39,7 @@ const DEFAULTS = { kafkaConsumerGroupExecutor: 'trade-executor-v1', kafkaConsumerGroupOpsSentinel: 'ops-sentinel-v1', kafkaConsumerGroupOperatorDashboard: 'operator-dashboard-v1', - historyWriterDerivedRefreshMaxEventAgeMs: 5 * 60 * 1000, + historyWriterDerivedRefreshMaxEventAgeMs: 30_000, strategyStateDir: './var/strategy-state', executorStateDir: './var/executor-state', liquidityStateDir: './var/liquidity-state', diff --git a/test/history-writer-refresh-config.test.mjs b/test/history-writer-refresh-config.test.mjs new file mode 100644 index 0000000..abf6231 --- /dev/null +++ b/test/history-writer-refresh-config.test.mjs @@ -0,0 +1,37 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; + +import { loadConfig } from '../src/lib/config.mjs'; + +const ENV_KEYS = [ + 'HISTORY_WRITER_DERIVED_REFRESH_MAX_EVENT_AGE_MS', + 'INTENT_REQUEST_INVENTORY_MAX_AGE_MS', +]; + +function withCleanEnv(fn) { + const previous = Object.fromEntries(ENV_KEYS.map((key) => [key, process.env[key]])); + for (const key of ENV_KEYS) delete process.env[key]; + try { + return fn(); + } finally { + for (const [key, value] of Object.entries(previous)) { + if (value == null) delete process.env[key]; + else process.env[key] = value; + } + } +} + +test('history writer derived refresh replay cutoff matches request inventory freshness', () => withCleanEnv(() => { + const config = loadConfig({ envPath: '/tmp/unrip-no-such-env-file' }); + + assert.equal(config.historyWriterDerivedRefreshMaxEventAgeMs, 30000); + assert.equal(config.historyWriterDerivedRefreshMaxEventAgeMs, config.intentRequestInventoryMaxAgeMs); +})); + +test('kubernetes history writer replay cutoff matches request inventory freshness', () => { + const manifest = readFileSync(new URL('../deploy/k8s/base/unrip.yaml', import.meta.url), 'utf8'); + + assert.match(manifest, /HISTORY_WRITER_DERIVED_REFRESH_MAX_EVENT_AGE_MS: "30000"/); + assert.match(manifest, /INTENT_REQUEST_INVENTORY_MAX_AGE_MS: "30000"/); +}); diff --git a/test/history-writer-refresh-policy.test.mjs b/test/history-writer-refresh-policy.test.mjs index 313c8bb..0b0b529 100644 --- a/test/history-writer-refresh-policy.test.mjs +++ b/test/history-writer-refresh-policy.test.mjs @@ -27,3 +27,21 @@ test('history writer derived refresh policy runs for fresh and undated events', maxEventAgeMs: 300000, }), true); }); + +test('history writer derived refresh policy skips events outside request freshness window', () => { + assert.equal(shouldRunDerivedRefreshForEvent({ + event: { + ingested_at: '2026-05-07T14:11:20.000Z', + }, + now: '2026-05-07T14:12:00.000Z', + maxEventAgeMs: 30000, + }), false); + + assert.equal(shouldRunDerivedRefreshForEvent({ + event: { + ingested_at: '2026-05-07T14:11:45.000Z', + }, + now: '2026-05-07T14:12:00.000Z', + maxEventAgeMs: 30000, + }), true); +});