Vest: a claim counts on the fresh day only once Vest dates it there, and a day's second cycle gets its own key

A claim's stage time is now taken before the request goes out (sentAt):
Vest dates it no earlier, so a claim answered just after 20:00 ET no longer
puts the fresh Vest day on stage 2 before the history read shows its
WITHDRAW. One Vest dates after the turn reads as the day before until then,
the cautious way round. Claims saved without it count from their answer.

Completions count one per payout on both paths (completionPlace): the
catch-up's stage no longer counts a Vest day's two claims as one, and a
cycle a day's second claim started carries cycleSeq, so it can be told from
that day's first. The master ignores it until it keys on it; old masters and
old instances are unchanged.

bookHistory's claim bookkeeping (WITHDRAWs, lapse, fund rows, the balance
the books check expects) is now the pure readClaims, tested on the case
that held an account for good: a claim that never went in, read an hour on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-10-27 14:39:13 -05:00
co-authored by Claude Opus 5.5
parent 9fe7fc0ea3
commit 35512cd504
9 changed files with 245 additions and 79 deletions
+26
View File
@@ -772,6 +772,32 @@ export function cycleCompletion(opts: {
return 'none';
}
/**
* A completed cycle's place among the account's payouts, counted one per
* payout: `payoutDays` holds each payout's day once per payout
* (PlatformClient.payoutDays — a Vest day's two claims share a fund row —
* else the negative fund rows' dates). stage = 1 + the payouts before the
* cycle's own: those before `end`, the fund date that ended it (the
* catch-up), else every one (today's final day). cycleSeq = the payouts on
* the cycle's start day (`cycle`, '' = the account's first) that came
* before it, beyond the one whose fund row names that day — 0 but for a
* cycle that a second payout the same day started, which `cycle` alone
* can't tell from that day's first.
*/
export function completionPlace(o: {
payoutDays: string[];
fundTransactions: { date: string; amount: number }[];
cycle: string;
end?: string;
}): { stage: number; cycleSeq: number } {
const stage = 1 + o.payoutDays.filter((d) => o.end === undefined || d < o.end).length;
if (o.cycle === '') return { stage, cycleSeq: 0 };
// A deposit's day (Vest's activation) names the cycle it started; each
// payout that day starts another.
const named = o.fundTransactions.some((f) => f.date === o.cycle && f.amount < 0) ? 1 : 0;
return { stage, cycleSeq: Math.max(0, o.payoutDays.filter((d) => d === o.cycle).length - named) };
}
/**
* Catch-up for a missed final day: did the cycle the latest fund transaction
* just ended stand complete — profit target, consistency and minimum days all