Where: src/lib/scoreUpdateCron.ts, inside the per-project success path
(lines ~58-95).
What's wrong: after a successful updateScoreForProject, the function
calls, in order:
recordScoreHistory(projectId, scoreResult.creditQuality, scoreResult.greenImpact);
triggerWebhooks({ project_id: projectId, credit_quality: ..., green_impact: ..., tx_hash: ..., timestamp: Date.now() });
// email alert logic using getHistory(projectId).slice(-2) ...
const timestamp = Date.now();
recordScoreHistory(projectId, scoreResult.creditQuality, scoreResult.greenImpact, timestamp);
triggerWebhooks({ project_id: projectId, credit_quality: ..., green_impact: ..., tx_hash: ..., timestamp });
broadcastScoreUpdate({ ... });
recordScoreHistory and triggerWebhooks are each called twice for the
exact same on-chain update — once with an implicit timestamp, once with an
explicit timestamp = Date.now() a few lines later. This reads like a
copy-paste/merge artifact (the second block also happens to be the one that
adds broadcastScoreUpdate, which is otherwise only called once).
Impact:
- Every successful hourly score update writes two entries into score
history for the same real update (with two different timestamps a few
milliseconds apart), corrupting trend/history queries
(GET /v1/projects/:id/history) and any downstream analytics/forecast
logic that assumes one entry per actual update.
- Every registered webhook subscriber receives two notifications per
score update, with two different timestamp values, for every project,
every hour, indefinitely.
Suggested fix: remove the first recordScoreHistory/triggerWebhooks
pair (or the second — whichever isn't needed for the interstitial
sendAlertIfSignificant delta calculation) so each successful update is
recorded and broadcast exactly once. Add a regression test asserting
recordScoreHistory/triggerWebhooks are each invoked exactly once per
successful project in runHourlyScoreUpdate.
Where:
src/lib/scoreUpdateCron.ts, inside the per-project success path(lines ~58-95).
What's wrong: after a successful
updateScoreForProject, the functioncalls, in order:
recordScoreHistoryandtriggerWebhooksare each called twice for theexact same on-chain update — once with an implicit timestamp, once with an
explicit
timestamp = Date.now()a few lines later. This reads like acopy-paste/merge artifact (the second block also happens to be the one that
adds
broadcastScoreUpdate, which is otherwise only called once).Impact:
history for the same real update (with two different timestamps a few
milliseconds apart), corrupting trend/history queries
(
GET /v1/projects/:id/history) and any downstream analytics/forecastlogic that assumes one entry per actual update.
score update, with two different
timestampvalues, for every project,every hour, indefinitely.
Suggested fix: remove the first
recordScoreHistory/triggerWebhookspair (or the second — whichever isn't needed for the interstitial
sendAlertIfSignificantdelta calculation) so each successful update isrecorded and broadcast exactly once. Add a regression test asserting
recordScoreHistory/triggerWebhooksare each invoked exactly once persuccessful project in
runHourlyScoreUpdate.