docs(recurring): two schedules, and why they must not be unified
AGENTS.md §1's old invariant was half wrong the moment anchored costs lost their slots: occurrenceDate is identity for calendar only. The replacement says what each schedule is and, more usefully, warns against collapsing them — that collapse is exactly the bug where paying twice in September pushed the next charge to November. §2's termination bullet now describes a defensive assert rather than a live guard, because the anchored walk steps forward from a real date and the grid walk never rebases. Also added the anchoring-change-refused-once-charged guard, which was undocumented in AGENTS.md despite having its own test suite and UI-level guard. DECISIONS.md, PRODUCT.md, ARCHITECTURE.md and TODO.md catch up to what shipped: two projection models and the three guards it deleted, a recurring cost charging more than once per cycle and being creatable from the purchase dialog, the anchored walk beside the grid walk, and schema 8 at 730 tests. The 2026-09-03 spec gets a header note pointing at the new spec and nothing else — it stays an accurate record of what was decided then. Demo data seeds two charges in one month on an anchored cost (Car insurance), so the fixed behaviour is the first thing a screenshot shows. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
35caa123cc
commit
49811f6795
7 files changed
+111
-25
No files matched your search
+11
-1
@@ -77,7 +77,7 @@ actions.addRecurringCost(d, {
|
||||
});
|
||||
// Rebases on the actual charge date rather than the calendar, so an
|
||||
// off-schedule payment moves the whole series — the phone-bill behaviour.
|
||||
actions.addRecurringCost(d, {
|
||||
const carInsurance = actions.addRecurringCost(d, {
|
||||
name: "Car insurance",
|
||||
archived: false,
|
||||
amount: { amount: 349, currency: "DKK" },
|
||||
@@ -93,6 +93,16 @@ actions.addRecurringCost(d, {
|
||||
// already reconverged for this one.
|
||||
actions.confirmOccurrence(d, wowSub.id, "2026-06-05", { date: "2026-06-05" });
|
||||
|
||||
// TWO CHARGES IN ONE MONTH on an anchored cost — the reported case. Car
|
||||
// insurance rebases on whatever it last actually charged, not on a calendar
|
||||
// slot, so charging twice in September must not push the next due date two
|
||||
// cycles out to November; it lands one cycle (30 days) after the LATER
|
||||
// charge, in October. Confirming these through `confirmOccurrence` carries no
|
||||
// `occurrenceDate` (anchored costs have no slots to settle) — only
|
||||
// `Purchase.date` matters here.
|
||||
actions.confirmOccurrence(d, carInsurance.id, "2026-09-03");
|
||||
actions.confirmOccurrence(d, carInsurance.id, "2026-09-19");
|
||||
|
||||
const buy = (date: string, description: string, amount: number, currency: "DKK" | "EUR" | "USD", postId: string, note?: string) =>
|
||||
actions.addPurchase(d, {
|
||||
date, description, note,
|
||||
|
||||
Reference in new issue
Block a user