Publish startup settlement ntfy notifications
All checks were successful
deploy / deploy (push) Successful in 1m1s
All checks were successful
deploy / deploy (push) Successful in 1m1s
Proof: Targeted notification/history-writer tests pass; full npm test passes with 285 tests; operator dashboard bundle builds. Assumptions: Startup outcome refresh may recompute settlements whose inventory movement occurred before process start, and notification_deliveries prevents duplicate ntfy posts for already delivered keys. Still fake: Venue-native terminal fill ids and fee-complete realized PnL remain unavailable; ntfy remains an external utility service outside the unrip app deployment ownership.
This commit is contained in:
parent
5542a6f00b
commit
4a8766d4ae
3 changed files with 15 additions and 3 deletions
|
|
@ -123,6 +123,7 @@ const rawQuoteTopics = [
|
|||
config.kafkaTopicRawNearIntentsQuote,
|
||||
];
|
||||
const quoteLifecycleRetentionMaintenanceIntervalMs = 60 * 1000;
|
||||
const historyWriterStartedAt = new Date().toISOString();
|
||||
const liveEvidenceTopics = [
|
||||
config.kafkaTopicNormSwapDemand,
|
||||
config.kafkaTopicDecisionTradeDecision,
|
||||
|
|
@ -219,10 +220,14 @@ const state = {
|
|||
await refreshPortfolioMetrics().catch((error) => {
|
||||
state.metrics_error = serializeError(error);
|
||||
});
|
||||
await refreshQuoteOutcomeAttributions().catch((error) => {
|
||||
await refreshQuoteOutcomeAttributions().then((records) => publishQuoteOutcomeNotifications(records, {
|
||||
minObservedAt: historyWriterStartedAt,
|
||||
})).catch((error) => {
|
||||
state.quote_outcomes_error = serializeError(error);
|
||||
});
|
||||
await refreshIntentRequestOutcomeAttributions().catch((error) => {
|
||||
await refreshIntentRequestOutcomeAttributions().then((records) => publishIntentRequestOutcomeNotifications(records, {
|
||||
minObservedAt: historyWriterStartedAt,
|
||||
})).catch((error) => {
|
||||
state.intent_request_outcomes_error = serializeError(error);
|
||||
});
|
||||
await refreshQuoteLifecycleRetentionState().catch((error) => {
|
||||
|
|
|
|||
|
|
@ -43,3 +43,10 @@ test('history writer passes tracked assets into portfolio valuation', () => {
|
|||
assert.match(source, /valuationAssets:\s*inputs\.valuationAssets \|\| \[\]/);
|
||||
assert.match(source, /inputs\.valuationAssets[\s\S]+asset\.priceId/);
|
||||
});
|
||||
|
||||
test('history writer startup outcome refresh publishes newly computed ntfy notifications', () => {
|
||||
assert.match(source, /historyWriterStartedAt\s*=\s*new Date\(\)\.toISOString\(\)/);
|
||||
assert.match(source, /refreshQuoteOutcomeAttributions\(\)\.then\(\(records\) => publishQuoteOutcomeNotifications\(records, \{\s*minObservedAt:\s*historyWriterStartedAt,\s*\}\)\)/);
|
||||
assert.match(source, /refreshIntentRequestOutcomeAttributions\(\)\.then\(\(records\) => publishIntentRequestOutcomeNotifications\(records, \{\s*minObservedAt:\s*historyWriterStartedAt,\s*\}\)\)/);
|
||||
assert.match(source, /outcomeNotificationObservedAtOrAfter/);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ test('notification dispatcher publishes each durable notification key once', asy
|
|||
assert.equal(finished.length, 1);
|
||||
});
|
||||
|
||||
test('outcome recency filter keeps startup refresh from sending historical notifications', () => {
|
||||
test('observed timestamp recency filter rejects stale outcome observations', () => {
|
||||
assert.equal(observedAtOrAfter('2026-04-16T10:00:00.000Z', '2026-04-16T09:59:59.000Z'), true);
|
||||
assert.equal(observedAtOrAfter('2026-04-16T09:00:00.000Z', '2026-04-16T09:59:59.000Z'), false);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue