Commit Graph
5726 Commits
Author SHA1 Message Date
Tim Smart c4b845f997 [AI] Keep the blank month's budget values when the range moves back past it 2026-11-06 10:33:39 +00:00
Tim Smart 7b34042784 [AI] Keep frozen months in the budget range after turning automatic mode off 2026-11-06 10:33:39 +00:00
Tim Smart bff0a5a4e1 [AI] Left-align wrapped items in the To Budget menu
Menu items are centered buttons, so "Change "For next month" mode", which wraps in the 200px popover, centered its second line.
2026-11-06 10:33:39 +00:00
Tim Smart b7c8fbdef9 [AI] Clear persistent income auto-hold too when turning off automatic mode 2026-11-06 10:33:39 +00:00
Tim Smart 00df014120 [AI] Drop the ellipsis from the "For next month" mode menu item 2026-11-06 10:33:39 +00:00
Tim Smart ef512cf3be [AI] Label the To Budget menu item that opens the "For next month" mode chooser as an action 2026-11-06 10:33:39 +00:00
Tim Smart 68a274423c [AI] Drop the "held for future months" line under To Budget
The "For next month" amount in the summary and its tooltip already show what's held, so the extra line on desktop and in the mobile overbudgeted banner repeated it.
2026-11-06 10:33:39 +00:00
Tim Smart d368ddc022 [AI] Keep the cache dirty until a rebuild has loaded every input
A rebuild whose input load fails part-way has already set some cells,
and the pass that saves them marked the cache clean without the rest.
With the old rows now cleared first, the next open trusted a cache
missing the stored buffers and frozen flags. A cache barrier now spans
`loadUserBudgets`: it marks the cache dirty on entry and only a load
that finishes releases it, so a failed open leaves the next one to
rebuild.
2026-11-06 10:33:39 +00:00
Tim Smart 99e3658422 [AI] Rebuild a spreadsheet cache that an older runtime last saved
A runtime without the frozen cells can open the newer database: it keeps
the `frozen` cells in the cache but never updates them, while a peer's
freeze still lands in the database column. On upgrade the stale cells
read every month as unfrozen. The same holds for any cell an older
runtime doesn't know.

The cache key is now written to a second row as well. An older runtime
rewrites row 1 only, so a cache it marked clean has a mismatch and is
rebuilt from the database. This also covers a cache such a runtime built
from scratch, which replaces the frozen-cell presence check.
2026-11-06 10:33:34 +00:00
Tim Smart 20096db65b [AI] Rebuild a spreadsheet cache that predates the frozen cells
A client with the carryover change but not this one can open a
transferred database that already marks months frozen. It builds a valid
cache with no `frozen` cells, and a later upgrade would load it, read
every month as unfrozen and select the computed buffer over the stored
one. An envelope cache (`buffered` cells) without `frozen` cells is now
rebuilt from the database, like one without `carryover-stored` cells.
Tracking caches have neither cell and are unaffected.
2026-11-06 10:33:34 +00:00
Tim Smart 8ba88f709d [AI] Freeze closed months the way the freeze does when turning off automatic mode 2026-11-06 10:33:34 +00:00
Tim Smart f567aa331f [AI] Write every buffer when turning off automatic mode, even ones that already match 2026-11-06 10:33:34 +00:00
Tim Smart 2badb42d9a [AI] Wait for the extended budget range before navigating in the range e2e test
The client refetches the bounds after a budget edit, and a click before that is clamped to the old last month.
2026-11-06 10:33:34 +00:00
Tim Smart 7c56faf29c [AI] Keep mobile budget banners at their old height unless the held line shows
Letting every banner grow made the two-line overspent banner 2px taller, and the flexible text column shrank the overbudgeted banner's icon.
2026-11-06 10:33:34 +00:00
Tim Smart 4df32a2eaf [AI] Don't switch to Tracking when turning off automatic mode was cancelled 2026-11-06 10:33:34 +00:00
Tim Smart c040f22f8b [AI] Shorten the "held for future months" line on mobile too 2026-11-06 10:33:34 +00:00
Tim 7ec4f22d64 Update packages/desktop-client/src/components/budget/envelope/budgetsummary/ToBudgetAmount.tsx 2026-11-06 10:33:34 +00:00
Tim Smart 91f8e9aeff [AI] Freeze closed months in a file that was never uploaded 2026-11-06 10:33:34 +00:00
Tim Smart 8ebc0acdd0 [AI] Reduce automatic buffer coverage to the essential behaviors 2026-11-06 10:33:34 +00:00
Tim Smart d0563f612a [AI] Keep the freeze range and the sync gate from before the simplification
Review of the simplification found three regressions:

- The budget range reaches back to the start month again, and the freeze
  creates any missing months first, so a month managed before the first
  budget month is still frozen and snapshotted when the mode turns off.
- The range clamp that keeps a created month from becoming the blank
  sheet comes back with it. A transaction-free budget hits that whenever
  the range's start moves past a created month.
- A sync counts for the freeze gate only if the spreadsheet it started
  with is still the open one, so a sync in flight across a reload of the
  same file doesn't authorise the new load.

The `buffered-managed` cell and the client clean-up stay.
2026-11-06 10:33:34 +00:00
Tim Smart 9fc1da688a [AI] Let the sheet say which months automatic "For next month" manages
A `buffered-managed` cell per month replaces three copies of the managed
rule: the `buffered-selected` formula, the server guard that queried the
preference and the `frozen` row, and the client's `isManagedMonth` with
its `month` prop plumbing and `monthFromSheet`. The hook now reads the
cell for the sheet it's rendered in, which also fixes frozen months never
reading as frozen on the client: the cell holds a boolean, not 1.

The freeze only covers months that have a sheet, so the budget range no
longer reaches back to the start month, and the blank-sheet guard that
reach-back needed goes with it. The sync handler keeps the file check
from the carryover seed and tracks only the synced spreadsheet for the
freeze gate.
2026-11-06 10:33:34 +00:00
Tim Smart 3602a548dd [AI] Trim the future buffer tests to one case per failure mode 2026-11-06 10:33:34 +00:00
Tim Smart 0ed65f04a8 [AI] Show the current "For next month" mode when the modal opens
The sheet answers after the modal's first render, so seeding the
selection from the mode picked Manual even while Automatic was on. A
Save without choosing then turned the mode off, which can't be undone.
The selection now follows the sheet until the user picks a choice.

The modal tests use the real sheet hooks with a fake server, so the
cells arrive late as they do in the app.
2026-11-06 10:33:34 +00:00
Tim SmartandNgoc Pham 8ca9b7a923 [AI] Add the automatic "For next month" UI
Desktop and mobile share one hook and one menu. `useFutureBufferMode`
reads `__global!future-buffer-start` and the month's `frozen` cell
through sheet bindings, so a mode change from another device shows up
without a reload, and `isManagedMonth` is a pure rule.

- To Budget menu item "For next month: Automatic…" / "Manual…" opening
  the mode modal. Shown with the experimental flag, and always once the
  mode is on.
- Mode modal with the Manual and Automatic choices, the start month, and
  the consequences of switching either way.
- Managed months hide Hold, Reset, Disable current auto hold, the income
  auto-hold toggle and the rollover arrow, explain "For next month" in a
  tooltip, and say how much is held for future months under
  "Overbudgeted". Frozen months keep Hold and Reset and lose the
  auto-hold controls.
- The budget-type switch says automatic mode is turned off first.
- `useSheetValue` observes the sheet a binding names, which the global
  cell needs.
- `month` reaches `ToBudgetAmount`, `TotalsList` and `BudgetSummary`,
  `TotalsList` provides its own sheet, and `monthFromSheet` maps a sheet
  back to its month.

Co-authored-by: Ngoc Pham <ngocphamm@fastmail.fm>
2026-11-06 10:33:34 +00:00
Tim Smart c9cda5b690 [AI] Turn automatic "For next month" on and off, and guard manual holds in managed months
setFutureBufferMode writes the start month to turn the mode on. Turning
it off freezes closed months, keeps every other managed month's computed
buffer as a manual hold, clears income auto-hold flags in zero_budgets
and deletes the preference, in one batch outside undo. Both directions
clear the undo stack. Switching to Tracking runs the off steps first.

holdForNextMonth, resetHold and resetIncomeCarryover write nothing and
return false in managed months, and api/budget-month returns the
selected buffer as forNextMonth.
2026-11-06 10:33:34 +00:00
Tim Smart 0a3f9afde1 [AI] Don't make a created month the blank sheet when the budget range starts later
createAllBudgets passed its range to createBudget, and envelope budgets
use the month before the range's first month as a blank sheet. When the
range starts after months already created, as it does when automatic
"For next month" is turned off after reaching back to its start month,
a real month became the blank sheet and the next new month cleared its
cells. The range passed to createBudget now starts at the first month
already created.
2026-11-06 10:33:34 +00:00
Tim Smart b8798e1cef [AI] Keep stale freezes out of a reopened budget, and freeze every closed month
A freeze queued for one load of a file could run after the same file was
reopened, before the new load's first full sync. Loads are now told
apart by their spreadsheet, which every load creates afresh, as well as
by file and group. Queued freezes and the full-sync callback drop work
from an earlier load, and the freeze writes nothing if the spreadsheet
changed while it waited for it.

The freeze skipped closed months without a sheet, which happens when
automatic mode started more than three months before the earliest
transaction. createAllBudgets now reaches back to the start month, and
the freeze calls it first, since the start month can arrive through
sync after the months were created.
2026-11-06 10:33:34 +00:00
Tim Smart e87ce5ae8b [AI] Freeze "For next month" buffers when a month ends
In automatic mode, each month from the start month to last month that
isn't frozen yet gets its computed buffer written as the stored buffer,
with frozen set, in one batch. It reads the clock once per run, and
skips months that are already frozen, so running it twice or on two
devices gives the same result.

It runs in a mutator but never an undoable one, because an undo would
reopen the month on every device. It's queued after budget load,
get-budget-bounds and each full sync. A file with a sync server only
freezes once a full sync of it has completed, so a closed month uses
every device's rows.
2026-11-06 10:33:34 +00:00
Tim Smart 2a61058bfd [AI] Compute the "For next month" buffer from future months
Adds a frozen column to zero_budget_months and a frozen cell per month,
loaded with the stored buffer. buffered-computed holds what the months
after a month need from it: next month's budgeted amount, less its
income, plus overspending that reaches it, plus its own need.

__global!future-buffer-start holds the futureBufferMode preference. It's
read on every budget open, after the months are created, and updated
when the preference row changes locally or through sync.
buffered-selected uses the computed buffer in managed months, the stored
buffer in frozen months, and today's rule otherwise.

setBuffer takes an optional frozen flag and only writes the columns
that change. envelope-budget-month also emits frozen and
buffered-selected.
2026-11-06 10:33:34 +00:00
Tim Smart 158eb3b8be [AI] Discard stale cells when rebuilding a dirty spreadsheet cache 2026-11-06 10:33:12 +00:00
Tim Smart 19c566c2e4 [AI] Restore budget inputs when replacing the blank month 2026-11-06 09:06:03 +00:00
Tim Smart d28498c4c6 [AI] Rebuild legacy boolean rollover caches 2026-11-06 08:39:07 +00:00
Tim Smart 9d827d5b58 [AI] Ignore deleted categories when the rollover range looks back
A deleted category keeps its budget rows, so an old persistent rollover
on one pulled the budget range back to that month even though the
category has no cells. The lookup now joins live categories, as the
latest-allocation lookup already does. Hidden categories still count.
2026-11-06 08:38:48 +00:00
Tim 669c4d9b23 Apply suggestion from @tim-smart 2026-11-06 08:38:48 +00:00
Tim 1d8570d6b8 Apply suggestion from @tim-smart 2026-11-06 08:38:48 +00:00
Tim Smart 02a53448de [AI] Start the budget range at the earliest persistent rollover
A month's rollover reaches later months through the previous month's
chain cell, so a budget opened after its created months had moved past
the toggled month lost the flag. Also note in the docs that auto hold
enabled before this version still stops until it's enabled again.
2026-11-06 08:38:48 +00:00
Tim Smart 312f3cc922 [AI] Make new rollover toggles persistent instead of seeding old windows
Switching rollover or auto hold on now stores 2, which following months
without a flag continue. A 1 keeps its old meaning of on for that month
only, so runs written by earlier versions end where they always did and
no existing balance changes. That removes the need for the carryover
seed, its post-sync and load-time scheduling, the seeded preference and
the budget table filter it relied on.
2026-11-06 08:38:48 +00:00
Tim Smart 92807b8397 [AI] Report clock drift when carryover seeding prevents loading 2026-11-06 08:38:34 +00:00
Tim Smart 9e60a7175b [AI] Finish a sync's queued work before reporting it complete
The success event starts the carryover seed behind the mutator queue
without waiting for it, so `sync-budget` and `sync` could return while a
read still saw the pre-seed figures. Both now wait for the queue and for
the spreadsheet to settle before returning. The callers are plain
handlers, not mutators, so waiting on the queue can't deadlock.
2026-11-06 08:38:34 +00:00
Tim 9efd688257 Apply suggestion from @tim-smart 2026-11-06 08:38:34 +00:00
Tim Smart 4f14e99ae8 [AI] Reduce carryover tests to the essential regressions 2026-11-06 08:38:34 +00:00
Tim Smart 34d362194e [AI] Don't open a budget whose carryover seed failed at load
Review showed what continuing costs: closed-month balances that change
on a later load, and in the stacked buffer PR a freeze that stores an
amount computed from unseeded flags, which no later seed repairs. The
load path refuses again, as it did before the simplification. The
shorter cache check stays.
2026-11-06 08:38:34 +00:00
Tim Smart 765ba6e5ba [AI] Shorten the carryover cache check and keep a failed seed from blocking load
The stale-cache test is two `some` calls instead of a helper with a flag.
A seed that fails at load is captured and retried on the next load, the
same as a seed that fails after a sync, rather than closing the budget.
2026-11-06 08:38:34 +00:00
Tim Smart edff72e6aa [AI] Let sync start a budget row's carryover flag as inherited, instead of sending it 2026-11-06 08:38:34 +00:00
Tim Smart fb9b376b0e [AI] Seed never-uploaded files at load, and report a failed post-sync seed 2026-11-06 08:38:34 +00:00
Tim Smart c8537f814a [AI] Keep the carryover release note to the change itself 2026-11-06 08:38:34 +00:00
Tim Smart 815081e0d4 [AI] Keep only the essential carryover regressions 2026-11-06 08:38:34 +00:00
Tim Smart 274933979e [AI] Trim the carryover tests to one case per failure mode 2026-11-06 08:38:34 +00:00
Tim Smart 23bf333c86 [AI] Don't open a budget whose carryover seed failed 2026-11-06 08:38:34 +00:00
Tim Smart c730677139 [AI] Keep the carryover seed to the active budget table and a completed sync 2026-11-06 08:38:34 +00:00