Context
packages/loopover-engine/src/claim-ledger-expiry.ts:12-14's claimAgeMs returns null when Date.parse(claim.claimedAt) is NaN (an unparseable timestamp), and findExpiredClaims skips any claim with a null age entirely — it's never swept.
claimedAt is always written via new Date().toISOString() in claim-ledger.ts, so this only becomes reachable through a corrupted or hand-edited claim-ledger row — but if it happens, the row is stuck permanently un-expirable, which contradicts this same file's own fail-closed posture used elsewhere (e.g. addApiBaseUrlScope's migration explicitly drops unusable rows rather than leaving them stuck, per its own comment in this file).
Requirements
- When
claimAgeMs cannot parse claim.claimedAt, findExpiredClaims must treat that claim as expired (fail-closed — an unparseable claim age should be swept, not permanently retained), matching this file's own stated posture rather than silently exempting it.
- Do not change
claimAgeMs's return signature or its behavior for valid timestamps — only how findExpiredClaims handles the null-age case.
Deliverables
Test Coverage Requirements
This file is under src/ via the packages/loopover-engine workspace — confirm current coverage.include scoping for this package before assuming the top-level 99% patch gate applies exactly as src/** does; match whatever this package's existing test file already does for its coverage target. The new branch must be covered by the new test.
Expected Outcome
A claim-ledger row with a corrupted/unparseable claimedAt is swept by findExpiredClaims instead of being permanently retained.
Links & Resources
packages/loopover-engine/src/claim-ledger-expiry.ts:12-14
packages/loopover-engine/src/claim-ledger.ts (where claimedAt is normally written)
Context
packages/loopover-engine/src/claim-ledger-expiry.ts:12-14'sclaimAgeMsreturnsnullwhenDate.parse(claim.claimedAt)isNaN(an unparseable timestamp), andfindExpiredClaimsskips any claim with anullage entirely — it's never swept.claimedAtis always written vianew Date().toISOString()inclaim-ledger.ts, so this only becomes reachable through a corrupted or hand-edited claim-ledger row — but if it happens, the row is stuck permanently un-expirable, which contradicts this same file's own fail-closed posture used elsewhere (e.g.addApiBaseUrlScope's migration explicitly drops unusable rows rather than leaving them stuck, per its own comment in this file).Requirements
claimAgeMscannot parseclaim.claimedAt,findExpiredClaimsmust treat that claim as expired (fail-closed — an unparseable claim age should be swept, not permanently retained), matching this file's own stated posture rather than silently exempting it.claimAgeMs's return signature or its behavior for valid timestamps — only howfindExpiredClaimshandles thenull-age case.Deliverables
findExpiredClaimsinpackages/loopover-engine/src/claim-ledger-expiry.tsupdated to treat a claim with an unparseableclaimedAt(i.e.claimAgeMsreturnsnull) as expired.claimedAt, assertingfindExpiredClaimsincludes it in the swept set.Test Coverage Requirements
This file is under
src/via thepackages/loopover-engineworkspace — confirm currentcoverage.includescoping for this package before assuming the top-level 99% patch gate applies exactly assrc/**does; match whatever this package's existing test file already does for its coverage target. The new branch must be covered by the new test.Expected Outcome
A claim-ledger row with a corrupted/unparseable
claimedAtis swept byfindExpiredClaimsinstead of being permanently retained.Links & Resources
packages/loopover-engine/src/claim-ledger-expiry.ts:12-14packages/loopover-engine/src/claim-ledger.ts(whereclaimedAtis normally written)