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.
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.
* [AI] Show goal funding status and an Assign action in balance menus
Use the existing Budget Automation engine to add a panel to category
balance menus with the month's recommendation, what is assigned so far,
what is still needed, and an Assign action that adds the money still
needed (undoable).
- budget/category-funding reads the dryRunCategoryTemplate projection
without writing goal_def; categories without funding templates get
null, and invalid automations fail closed.
- budget/fund-category recomputes on the server, assigns the full
recommendation and goal in one batch, and is a no-op when already
funded.
- Funding reads are gated by the goal templates flag, only run for
categories with templates, and refresh after data changes and funding.
- Covers desktop and mobile envelope and tracking balance menus,
including tracking income categories.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XJDNR72i3yEZttwMUuk4cq
* [AI] Fold goal funding into the existing goal status and never overbudget
Address review feedback on the category funding change:
- Replace the separate funding panel with the existing goal status. It
now shows at the top of the desktop envelope and tracking balance
menus (as it already did on mobile) with a thin progress bar, and the
desktop hover tooltip is removed along with its tooltipDisabled
workaround.
- Replace the Assign button with a "Fund goal from To Budget" balance
menu item (envelope only, not for long-term goals) that uses the
existing undoable transfer-available action, so it never moves more
than To Budget and never lowers a budget. It is hidden when the goal
is met or To Budget is empty.
- Drop the live dry-run endpoint, the fund-category endpoint, their
client queries and refresh wiring, and the tracking income additions.
Status, bar, colour and fund amount now all read the stored goal.
- Add transferAvailable tests and menu wiring tests, and update the
budget automation docs for the moved goal status.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XJDNR72i3yEZttwMUuk4cq
* [AI] Show goal status as a colored pill on category balances
Draw the balance of a category with a goal as a pill with an icon:
yellow with a clock when underfunded, green with a check when funded
and red with an exclamation when overspent. Categories without a goal,
or with goal templates off, render as before.
- The pill, the balance colour and the goal status share one comparison
(compareToGoal/getGoalFundingState), made on displayed amounts so they
agree when decimals are hidden.
- Desktop envelope and tracking tables and the mobile budget table;
narrow desktop columns drop the icon to keep the amount readable, and
mobile amounts shrink to fit instead of being cut off.
- Status is announced to screen readers, not shown by colour alone.
- Pill colours get their own theme tokens so the funded pill keeps
enough contrast in the midnight theme.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XJDNR72i3yEZttwMUuk4cq
* [AI] Move GoalStatus into its own file
GoalStatus isn't specific to BalanceWithCarryover, so give it a
type-strict module of its own alongside its row helper.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CpAtycTVCRhkLcuD6AJeTM
* [AI] Update goal status screenshots to show the balance menu
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CpAtycTVCRhkLcuD6AJeTM
* [AI] Clarify how funding a goal affects next month's To Budget
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CpAtycTVCRhkLcuD6AJeTM
* [AI] Update docs screenshots to show the goal status pill
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CpAtycTVCRhkLcuD6AJeTM
* [AI] Describe the underfunded goal status as yellow in the docs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CpAtycTVCRhkLcuD6AJeTM
* [AI] Update mobile budget template VRT snapshots for the goal status pill
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CpAtycTVCRhkLcuD6AJeTM
* [AI] Replace the goal status pill with a goal progress ring
Keep category balances in their usual colours and draw a small ring
beside balances with a goal instead: a gold arc that fills as the goal
is funded, and a green check once it is met. Overspent balances stay red
like any other negative amount, without an extra marker.
The ring sits beside the amount in the balance cell on desktop, inside
the balance pill on mobile, and next to the heading of the goal status
in the balance menu, so one shape carries the status everywhere. The
pill-only theme tokens are removed, the docs text and screenshots and
the mobile VRT snapshots are updated, and the release note is replaced.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B4sNu2G32qCLcAe2YznUCM
* [AI] Only mark goals that still need money, with a filling ring
Draw the goal ring only beside balances whose goal is not met yet, so a
fully funded budget looks like one without goals and the one mark in the
table points at rows that still need money. The ring is a single gold
pie: a hollow outline when nothing is funded yet, a wedge that grows
with the funded share and that never closes, since the ring disappears
the moment the goal is met. Funded rows carry no mark and overspent
rows stay red like any other negative amount.
The glyph shrinks to 10px to sit within the cap height of the figures
and draws in one theme token, so it keeps 3:1 contrast on every surface
in all three themes. The amount keeps its full width in narrow month
columns; the ring is what yields. The balance menu shows the same ring
only beside "Underfunded", as its legend. The three-way funding state
is replaced by one helper that returns the progress to draw or nothing.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B4sNu2G32qCLcAe2YznUCM
* [AI] Keep the funded balance green, ring only while the goal is unmet
A category whose goal is met keeps the green balance the goal templates
feature already gives it, so a met goal still reads differently from no
goal. The ring stays the only mark on an unfunded goal; its amount keeps
the normal colour instead of upstream's gold text.
Docs text and screenshots, the release note and the mobile template VRT
snapshots follow.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B4sNu2G32qCLcAe2YznUCM
* [AI] Keep funded balances green in the mobile balance sheet and the ring in view
- Add an explicit isIncome prop instead of reading isDisabled as "income",
so the mobile balance sheet header (which is only disabled because it is
not clickable) colours a funded balance green again, and mobile income
balances keep their hover style
- Let a long amount shrink beside the ring on desktop so the ring is not
pushed out of the column
- Describe the ring instead of yellow balances in the goal directive docs
- Drop a snapshot change that had no visible difference
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CpAtycTVCRhkLcuD6AJeTM
* [AI] Drop the ring from the goal status in the balance menu
The progress bar right below it already shows the same thing, so the menu
keeps its plain status line. Retake the balance menu screenshots in the
docs to match.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CpAtycTVCRhkLcuD6AJeTM
* [AI] Keep the balance yellow while its goal is unmet
Per review, an unmet goal keeps the yellow balance text it has on master and
gains the progress ring beside it, so every category with a goal is
coloured. The balance colour logic is back to master's; the docs and their
screenshots describe and show yellow text with the ring.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CpAtycTVCRhkLcuD6AJeTM
* [AI] Show income goals only in tracking budgets
Envelope budgets don't apply templates to income categories, so the
balance now ignores an income goal only when the budget is not
tracking, instead of on every income category. Tracking income pills
get their goal colour back and show the ring when underfunded. The
envelope income balance sheet no longer shows a goal status, and the
mobile income group menu no longer offers "Overwrite with templates"
in envelope budgets.
The isIncome props on the tracking budget table and balance menu are
removed, since those only ever render expense categories.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018MRjoaUi1WAUGsSNp3sb7p
* [AI] Keep goals from one budget type out of the other
Envelope and tracking budgets share spreadsheet cell names, so a goal
from one type could show up after switching to the other:
- Switching type (by sync, undo or redo) left the old type's cells in
the saved spreadsheet cache, and goals came back from it on the next
open. setType now clears the cache and waits for the budget values to
reload.
- Changes to the inactive type's budget table were still applied to the
sheet. Only the active table's rows are applied now.
- Applying templates to specific categories wrote amounts and goals for
income categories in envelope budgets, and saving automations stored
their goals. Envelope budgets now skip income categories in both.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018MRjoaUi1WAUGsSNp3sb7p
* [AI] Show income goals on the desktop tracking budget
Desktop tracking budgets have no balance column for income categories,
so their goals showed nowhere, while the mobile income pill already
showed them. An income category with a goal now shows the goal colour
on its received amount, with the ring while underfunded. Income
categories without a goal, and budgets with goal templates off, keep
the plain received amount.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018MRjoaUi1WAUGsSNp3sb7p
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: youngcw <calebyoung94@gmail.com>
* Remove actual-appimage link from installation guide
* Remove AUR actual-appimage from install docs
Removed references to AUR actual-appimage from community installation documentation for clarity.
* Remove actual AppImage from documentation
Remove the experimental akahuBankSync feature flag so the Akahu provider
is always shown on the Bank Sync page.
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
MASH moved each service's documentation into its component directory, so docs/services/actual.md now 404s.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* [AI] Add "Average of previous weeks" budget automation
* [AI] Keep weekly average unrounded and label fixed adjustments per week
* [AI] Simplify weekly average template plumbing
- Collapse the historical template type conversions in the automation
reducer into one helper instead of six pairwise cases.
- Dispatch the adjustment update once from AmountAdjustment; the template
type discriminant narrows the payload without branching.
- Drop the redundant early return in trimWeeklyAverageWindow: the general
formula already returns the untouched window when activity predates it.
* Update upcoming-release-notes/average-of-previous-weeks-automation.md
* [AI] Default weekly average automations to 12 weeks
* [AI] Only carry percentage adjustments between monthly and weekly averages
* [AI] Run weekly averages through the average template and share the weekday count
* [AI] Share the weekday names with the date utils
* [AI] Trim weekly average tests to the essential cases
* Apply batched suggestions from code review
Co-authored-by: Tim <hello@timsmart.co>
* Apply suggestion from @tim-smart
* Apply suggestion from @tim-smart
* Apply suggestion from @tim-smart
* [AI] Name the stored scale of amounts
- Add STORED_DECIMAL_PLACES: amounts are stored with two implied decimals whatever the currency, and a currency's own decimals only decide how many digits are shown
- Make integerToCurrencyWithDecimal with a currency divide by 100 and show that currency's digits
- Remove amountToCurrencyInteger, which had no callers and stored amounts with the currency's decimals
- Update the tests
Issue: actualbudget/actual#8858
* [AI] Show and read amounts at the stored scale in every currency
- Make useFormat divide and multiply by 100 for every currency, so balances, totals, budget cells, rules, schedules, filters and reconciliation agree with the register, imports and bank sync
- Take typed digits as whole units for currencies without decimals in the desktop and mobile amount inputs
- Read formula card results at the stored scale
- Add tests for useFormat and the mobile amount input in dollars and yen
Issue: actualbudget/actual#8858
* [AI] Use the stored scale in budget templates and goals
- Convert template, goal, limit and schedule adjustment amounts with two implied decimals for every currency, and round to whole units of the currency when decimals are hidden
- Remove the currency from the template engine, which no longer needs it
- Read and write the goal editors' amounts at the stored scale
- Update the yen template tests
Issue: actualbudget/actual#8858
* [AI] Show budget movement notes at the stored scale
- Divide amounts by 100 and show the currency's digits in the notes written when money moves between categories
- Add a yen test
Issue: actualbudget/actual#8858
* [AI] Document that amounts are always stored x100
- State in the API docs that amounts are always the value x 100, whatever the currency
- Add the release note
Issue: actualbudget/actual#8858
* [AI] Reduce tests to the stored-scale change
- Drop the JPY template tests: the template engine no longer reads the currency
- Drop the round-trip test of the removed amountToCurrencyInteger
- Keep one yen case each for useFormat and the mobile amount input
* [AI] Simplify the stored-scale change
- Remove STORED_DECIMAL_PLACES and the unused decimalPlaces parameter of integerToCurrency
- Format budget movement notes with integerToCurrencyWithDecimal
- Drop the toAmount/fromAmount wrappers in useFormat
- Mobile amount input: format through useFormat so currencies without decimals show whole units while editing and after saving, whatever the hideFraction setting
- Drop the movement notes test, now covered by the integerToCurrencyWithDecimal tests
* [AI] Compare goals at the stored scale
Master's goal helper (#9304) still divided by the currency's decimal places.
Round to the currency's displayed digits through the formatter instead, and
return the difference ×100.
* [AI] Skip separator reformatting for whole-unit mobile entry
The zero-decimal branch strips every non-digit before formatting, so
reapplying thousands separators first did nothing there. Only the
two-decimal branch needs the reformatted text.
* [AI] Tidy comments, API docs and release note for stored amounts
* [AI] Keep stored fractions in edit text
forEdit rounded a stored fraction to the currency's digits, so focusing and
leaving an amount field unedited saved the rounded value (¥1,500.50 became
¥1,501). Show two decimals while editing when the stored value has a
fraction, unless fractions are hidden.
* [AI] Keep the exact amount when an amount field is left unedited
With decimals hidden, the edit text rounds a stored fraction (¥1,500.50
shows as 1,501), and blur, Enter or Tab saved that rounded text. The
desktop amount input, the financial input and budget cells now keep the
original amount when the text is unchanged.
* [AI] Track edits instead of comparing amount text
Comparing the text with the edit form missed the display text left after
Enter or a sign toggle, and treated clearing and retyping the rounded value
as untouched. Track whether the user typed: the desktop amount input keeps
the exact amount its text stands for, and the financial input and budget
cells parse only edited text.
---------
Co-authored-by: Tim Smart <hello@timsmart.co>
* [AI] Center list items correctly near the edges of a list
- Center an item by the offset that puts it in the middle, kept within the list, instead of jumping to the top whenever the list can scroll less than half its height
- Add tests
Issue: rapastranac/actual#10
* [AI] Scroll to and highlight the other side of a transfer
- Pass the linked transaction along when the transfer arrow opens the other account
- Load more of that account until the transaction is there, open its split, center it and point it out briefly with a pulse, or a steady highlight with reduced motion; leave the account at the top when the current view can't show it
- Keep the transaction in view while it's pointed out, also while rows load above it; drop it when the user searches, and ignore results of a query replaced since
- Add tests
Issue: rapastranac/actual#10
* [AI] Document finding the other side of a transfer
- Explain the transfer arrow in the transfers docs and add a release note
Issue: rapastranac/actual#10
* [AI] Reduce the tests for pointing out the other side of a transfer
- Keep one list test for the case that used to jump to the top and one for a centered item
- Check the pulse and its end in a single table test
* [AI] Load up to the other side of a transfer in one query
Use PagedQuery.refetchUpToRow instead of paging until the transaction
shows up, and only look for it in the account's first query so a sort
or filter applied meanwhile drops it. refetchUpToRow keeps the data as
it is when the row isn't in the query instead of throwing.
* [AI] Keep loading the other side of a transfer through a sync refresh
A sync refresh while loading the account up to the transaction dropped
the loaded rows, and the next onData gave up on it. fetchData now tells
whether a newer request superseded it, and the account loads again in
that case, giving up only when the query doesn't have the transaction.
* [AI] Fetch up to a row through sync refreshes in PagedQuery
A refresh during refetchUpToRow now makes it fetch again instead of
replacing its rows, and refetchUpToRow resolves once the rows up to the
row are loaded. The account awaits it and reads the transaction from
the loaded rows instead of guessing from the order of onData calls,
which drops the retry loop and the superseded flag on fetchData.
* [AI] Find the other side of a transfer on the account's current query
React can call componentWillReceiveProps twice for one account change,
which fetches the account twice. The pending transaction was tied to the
first query, which the second replaced, so it was never shown. Look for
it on the first load of the current query instead, as long as the
account isn't sorted.
refetchUpToRow also refetches the loaded rows when the row is missing
instead of keeping them, so a row deleted by a sync during the fetch
doesn't stay listed.
* [AI] Tidy the transfer focus code and its comments
---------
Co-authored-by: Tim Smart <hello@timsmart.co>
* [AI] Record a category's goal when its automations are saved
Saving automations in the editor now also stores the goal it projects
for that month, without changing the budgeted amount, so the goal
status and "Fund goal from To Budget" work without running templates.
Undo now also clears the goal on a newly created budget row.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CpAtycTVCRhkLcuD6AJeTM
* [AI] Make the goal status agree with the balance colour
The balance menu's goal status, progress bar and "Fund goal from To
Budget" item now compare amounts as they are displayed, like the
balance colour does, using the currency's own decimal places so
currencies without decimals are not rounded to the nearest hundred.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CpAtycTVCRhkLcuD6AJeTM
* [AI] Drop an unneeded cast in the goal-on-save tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CpAtycTVCRhkLcuD6AJeTM
* [AI] Show the goal shortfall between the displayed amounts
With hidden decimals the goal status could read "Underfunded (0)", because
the label compared the displayed amounts while the amount in brackets used
the raw difference. Both now use the difference between the displayed
amounts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CpAtycTVCRhkLcuD6AJeTM
---------
Co-authored-by: Claude <noreply@anthropic.com>
* [AI] Submit App Store releases automatically and move TestFlight uploads to nightly
On a published release both the iOS and Mac App Store workflows now wait for
Apple to process the uploaded build, attach it to the App Store version for
the release, fill in the release notes and submit it for review with
automatic release after approval. The TestFlight uploads run on a nightly
schedule instead of on every master push.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Publish App Store builds with the Codemagic App Store Connect CLI
Replace the hand-written submission script and the altool upload with
`app-store-connect publish`, which uploads the build, waits for processing
and submits it for review in one step. The iOS export now writes the .ipa
locally so the CLI can upload it, matching the Mac flow.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Run the App Store Connect CLI through pipx instead of a virtualenv
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Share the App Store Connect publish step between the iOS and Mac workflows
Move the upload and review submission into one composite action so the CLI
version and submission flags live in a single place, drop step outputs that
nothing read, clean up signing material right after signing instead of after
the processing wait, and remove export options that were already defaults.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Do not submit prereleases for App Store review
Prereleases still upload to TestFlight; only full releases are submitted.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Send TestFlight builds to the external beta testers group
Every uploaded build is now added to the "Beta testers" TestFlight group and
submitted for Beta App Review, so external testers get the nightly builds
without a manual step. The FAQ links to the public TestFlight invite.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Wait for the uploaded build to appear before the processing wait
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* [AI] Add per-budget SimpleFIN credentials
* [AI] Clear budget credentials when switching SimpleFIN to global
* [AI] Reject SimpleFIN access to deleted budgets
* [AI] Use distinct keys in SimpleFIN isolation test
* [AI] Show goal funding status and an Assign action in balance menus
Use the existing Budget Automation engine to add a panel to category
balance menus with the month's recommendation, what is assigned so far,
what is still needed, and an Assign action that adds the money still
needed (undoable).
- budget/category-funding reads the dryRunCategoryTemplate projection
without writing goal_def; categories without funding templates get
null, and invalid automations fail closed.
- budget/fund-category recomputes on the server, assigns the full
recommendation and goal in one batch, and is a no-op when already
funded.
- Funding reads are gated by the goal templates flag, only run for
categories with templates, and refresh after data changes and funding.
- Covers desktop and mobile envelope and tracking balance menus,
including tracking income categories.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XJDNR72i3yEZttwMUuk4cq
* [AI] Fold goal funding into the existing goal status and never overbudget
Address review feedback on the category funding change:
- Replace the separate funding panel with the existing goal status. It
now shows at the top of the desktop envelope and tracking balance
menus (as it already did on mobile) with a thin progress bar, and the
desktop hover tooltip is removed along with its tooltipDisabled
workaround.
- Replace the Assign button with a "Fund goal from To Budget" balance
menu item (envelope only, not for long-term goals) that uses the
existing undoable transfer-available action, so it never moves more
than To Budget and never lowers a budget. It is hidden when the goal
is met or To Budget is empty.
- Drop the live dry-run endpoint, the fund-category endpoint, their
client queries and refresh wiring, and the tracking income additions.
Status, bar, colour and fund amount now all read the stored goal.
- Add transferAvailable tests and menu wiring tests, and update the
budget automation docs for the moved goal status.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XJDNR72i3yEZttwMUuk4cq
* [AI] Move GoalStatus into its own file
GoalStatus isn't specific to BalanceWithCarryover, so give it a
type-strict module of its own alongside its row helper.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CpAtycTVCRhkLcuD6AJeTM
* [AI] Update goal status screenshots to show the balance menu
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CpAtycTVCRhkLcuD6AJeTM
* [AI] Clarify how funding a goal affects next month's To Budget
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CpAtycTVCRhkLcuD6AJeTM
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: youngcw <calebyoung94@gmail.com>
* Return what a bank sync reconciled from the API's runBankSync
`accounts-bank-sync` and `simplefin-batch-sync` already reconcile each
account and report the transactions they added and matched, but
`api/bank-sync` destructured `{ errors }` out of that and returned nothing.
An API consumer could tell a sync had succeeded, not whether it had done
anything, so tools built on the API either notify on every scheduled run or
diff the transaction table themselves to find out.
The handler now collects `newTransactions`, `matchedTransactions` and
`updatedAccounts` across both the single-account and batch paths and returns
them. Errors still throw, so a resolved promise continues to mean the sync
succeeded, and `void` to an object breaks no existing caller. This also
makes `runBankSync` consistent with `importTransactions`, which already
surfaces its reconciliation result.
Verified against a live sync server with a GoCardless-linked account, two
runs back to back:
published 26.9.0 undefined
first run 16 newTransactions, 0 matched, 1 updatedAccount
second run 0, 0, 0
All 16 returned ids resolve to real transactions in the ledger. The second
run is the case this is for: successful, and nothing happened.
* Address review: real error fixtures, existing doc convention, bounded appends
Three fixes from the CodeRabbit review.
The docs entry named a `BankSyncResult` type that does not exist anywhere in
the repo. It now spells the shape inline, the way the neighbouring
`importTransactions` entry already does.
The test fixtures passed a bare string where a sync error goes, but a real
SyncError is an object and the handler reads two fields off it:
getBankSyncError(errors[0]) for the message and errors[0].code for the code
it attaches. The code half was therefore never exercised. Fixtures are now
typed SyncResponseWithErrors with a real error object, the mock reads
`.message` like the real helper, and a new test asserts the code reaches the
thrown error. Confirmed it fails if `withErrorCode` is dropped.
The id lists are appended one at a time instead of spread, since they are as
long as the sync is big. `allErrors` keeps its existing spread: it is bounded
by the account count, not by the transaction count.
* docs(api): type runBankSync's result and mark its argument optional
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YPyenH5t6DHfoqqkXLWfEn
* [AI] docs(api): correct runBankSync's updatedAccounts and note partial imports on rejection
updatedAccounts only lists accounts that received new transactions, not
accounts whose balance was refreshed. When syncing every account, one
failing account rejects the call after the others have already imported.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011AwAzzZALs5RFb454j3cMV
* [AI] docs(api): say runBankSync's arrays are empty only when nothing was added or updated
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WnubtGYPLiUFthYYeUxMaY
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Add AI Assistant Integrations section and update entries
Added a new section for AI Assistant Integrations (MCP) and reintroduced the actual-budget-mcp entry with detailed descriptions.
* [AI] Add release note for MCP community projects section
* [AI] Add getScheduleDates to the API
Expand a schedule date (single date or RecurConfig) into the dates it
falls on within a window, honouring frequency, interval, monthly
patterns, end mode and the weekend move.
Also type the return value of recurConfigToRSchedule, and fix the API
vitest setup, where inlined UMD builds of @rschedule registered the
date adapter and recurrence rules on a second @rschedule/core instance.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CfQCMhYExnAqVNC4gJjF8E
* [AI] Build getScheduleDates on getNextDateAfter
Walk the schedule with getNextDateAfter, the step the app uses to
advance a schedule, instead of expanding the rschedule occurrences.
A date that several occurrences move onto (a daily schedule's Saturday
and Sunday both landing on Monday with skipWeekend) is now listed once,
and count counts distinct dates.
Add an API test that pins the first listed date to the next_date the
app stores, so the two cannot drift. Drop the recurConfigToRSchedule
typing change, which is no longer needed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HRp7PBTgAChF39Dv9iFLyc
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Remove "or post a message in the Discord" from the shared
ExperimentalFeatureWarning so feedback goes to GitHub issues.
- Monte Carlo analysis docs now link to feedback issue #8571.
- Multi-currency docs use the shared warning component, linked to the
rule action templating feedback issue (#3606).
- Budget Analysis toggle in Settings links to /issues/6742 instead of
/pull/6742.
Assisted-by: ClaudeCode:claude-opus-5-5
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* [AI] Reject invalid password inputs before hashing
- Require non-empty strings at password validation, hashing and verification
boundaries so JSON objects, arrays and other scalar types cannot reach
Argon2's buffer conversion.
- Add regressions for malformed passwords on fresh and configured servers,
including assertions that rejected input never reaches Argon2.
Advisory:
https://github.com/actualbudget/actual/security/advisories/GHSA-3jjw-2hq7-mmm2
* [AI] Require admin permission for EnableBanking configuration
- Require administrator permission to configure EnableBanking, even when
credentials have not been configured yet.
- Check permission before validating credentials with the provider or
persisting secrets, and disable configuration controls for non-admins.
- Test unauthenticated, BASIC and ADMIN requests against empty and existing
configuration, preserving secrets and avoiding provider calls on denial.
Advisories:
https://github.com/actualbudget/actual/security/advisories/GHSA-hfxx-qc34-gqcw
* [AI] Neutralize BOM-prefixed CSV formulas
- Detect spreadsheet formula triggers after leading BOM characters and
prepend an apostrophe before the original text.
- Protect textual CSV headers and scalar values as well as ordinary strings.
- Preserve numeric output, including negative amounts, and cover repeated
BOMs, formula triggers, quoting and Unicode text in regression tests.
Advisories:
https://github.com/actualbudget/actual/security/advisories/GHSA-7jvc-qfpw-85g2
* [AI] Remove ambiguous Commerzbank payee matching
- Split Commerzbank payees into non-empty whitespace/slash-delimited tokens
and escape their literal text before constructing the cleanup expression.
- Use consuming separators that cannot overlap the literal tokens, avoiding
ambiguous matching on long runs of spaces or slashes.
- Require separators between payee tokens: concatenated words in notes are
no longer stripped. Allow 20 seconds for the hostile-case subprocess,
including Node and Vite startup on slower CI runners.
- Skip matching for empty payees while preserving normal note cleanup.
- Exercise hostile payees, including the report's spaced-word reproducer,
in a subprocess with a hard timeout; retain the existing bank fixtures.
Advisories:
https://github.com/actualbudget/actual/security/advisories/GHSA-p34v-m2gp-w3jm
* [AI] Reject unsafe IPv6 address classes in SSRF checks
- Allow public unicast addresses by default. The private-network option
additionally permits private, loopback, unique-local and carrier-grade NAT addresses.
- Permit the well-known NAT64 prefix only for embedded public IPv4
destinations; private and metadata addresses remain blocked in both modes.
- Reject other IPv6 transition, multicast, reserved and link-local classes in
both modes, and classify IPv4-mapped addresses by the embedded IPv4 address.
- Cover literal URLs, DNS answers, redirects and both SimpleFIN fetch paths,
including NAT64, 6to4 and Teredo addresses, with synthetic upstreams.
Advisories:
https://github.com/actualbudget/actual/security/advisories/GHSA-97j5-6g5r-42v5
* [AI] Exclude callback secrets from ordinary server logs
- Log request method, pathname and response status without query strings,
and restrict serialized metadata to the same safe fields.
- Keep full OpenID and banking provider messages, response bodies, session
identifiers and error stacks behind existing actual-sensitive namespaces.
- Apply the same opt-in handling to shared banking errors, proxy target
URLs and unhandled rejections; do not forward raw streaming errors to
Express's ordinary error logger.
- Stop logging stored secret values, including with debugging enabled.
- Document targeted sensitive debugging and private support exchanges.
- Test ordinary and opt-in output, query and serialized metadata leaks,
provider failures, session URLs, streaming errors and secret storage.
- Preserve provider format sequences by passing SimpleFIN response text as
a debug format argument, rather than part of the format string.
Advisories:
https://github.com/actualbudget/actual/security/advisories/GHSA-ppcp-hx2h-fm35
* [AI] Revoke password sessions when credentials change
- Update the password hash and revoke all password-authenticated sessions
in one transaction, including password changes through the local CLI.
- Issue a fresh token for each successful password login instead of reusing
an existing session token.
- Recheck the verified password hash before creating a session so an
in-flight login cannot restore access after a credential change.
- Correct the UI's claim that other sessions remain signed in, and test
multiple sessions, CLI resets, transaction rollback and login/reset races.
This addresses session revocation and token reuse. Requiring the current
password for remote changes is covered by the following commit.
Advisories:
https://github.com/actualbudget/actual/security/advisories/GHSA-7363-3qq5-2frjhttps://github.com/actualbudget/actual/security/advisories/GHSA-4wgp-48c6-v9w3
* [AI] Require the current password for remote password changes
- Require currentPassword alongside the replacement password for remote
changes, preserving the administrator and password-authentication checks.
- Verify the current credential, rate-limit attempts and reject a change
if the verified password hash changes before the update.
- Collect the current password in the client and handle failure to sign in
after a successful change. Keep the local reset command available without
the old password.
- Test that a session token alone cannot change credentials and that failed
requests preserve the existing password and sessions.
- Clear the redundant success notice when signing in with the new password fails.
Advisories:
https://github.com/actualbudget/actual/security/advisories/GHSA-4wgp-48c6-v9w3
* [AI] Restrict Electron protocol launches and network file reveals
- Allow only parsed HTTP/HTTPS URLs through external-opening IPC,
new-window handling and navigation, addressing GHSA-7pf2-w24j-p6h2.
- Also reject network/UNC paths in the separate file-reveal handler.
A note such as [View receipt](//host/share/invoice.pdf) reaches
showItemInFolder without passing through openExternal. On Windows,
resolving that share can disclose NTLM authentication responses to an
attacker-controlled server where network and authentication policies
permit it. This additional flow was not demonstrated in the advisory.
- Accept native absolute local paths without adding file-URL conversion.
- Test protocol filtering and network-path rejection without invoking OS
handlers. The note-to-file-reveal flow is code-confirmed; an actual
Windows authentication exchange has not been reproduced.
The following commit addresses the advisory's separate internal-origin
classification finding.
Advisory:
https://github.com/actualbudget/actual/security/advisories/GHSA-7pf2-w24j-p6h2
Additional finding references:
https://github.com/electron/electron/blob/v43.4.0/shell/common/platform_util_win.cc#L270-L340https://learn.microsoft.com/en-us/windows-server/storage/file-server/smb-ntlm-blocking
- Exercise Windows path semantics on all test platforms and exclude generated
Electron tests and Vitest configuration from the packaged application.
* [AI] Classify Electron app URLs exactly and block redirects
- Replace substring checks with exact app://actual scheme/host matching
and the existing development origin, http://localhost:3001.
- Use the classification for browser handoffs and focus/log script
execution so remote URLs containing app:// or localhost: are not
mistaken for app pages.
- Preserve the existing global renderer-navigation block and add redirect
blocking there, without duplicating it in the main-window handlers.
- Keep ordinary IPC registration unchanged; broad sender validation is
outside this URL-classification fix.
- Test misleading hosts, ports and URL substrings. Exercise ordinary
clicks, new-window links and explicit browser IPC in Electron, asserting
the exact HTTP/HTTPS URLs handed to the browser without opening real
OS handlers. Exercise a local HTTP redirect to check the app stays put.
Advisory:
https://github.com/actualbudget/actual/security/advisories/GHSA-7pf2-w24j-p6h2
- Retain startup logs until an internal page finishes loading, then forward
them to DevTools; cover startup delivery in the Electron navigation suite.
* [AI] Choose import destinations from trusted local context
- Allocate fresh local IDs for cloud downloads and local archive imports,
ignoring destination IDs supplied in archived metadata.
- Pass an explicit local destination for replacement downloads and verify
its cloud-file association. Do not require matching sync group IDs, so
downloading the reset version of the same cloud file remains possible.
- Reject server metadata describing a different cloud file and close the
budget before an authorized replacement.
- Retain existing import metadata compatibility, collision error handling,
nested archive layouts and backups; do not require a budget name.
- Test targeted collisions, local recovery imports with obsolete IDs or
missing optional metadata, mismatched cloud associations and replacement
after a sync ID reset using real local files and databases.
Advisory:
https://github.com/actualbudget/actual/security/advisories/GHSA-7fr5-fjj8-hwjq
- Reuse the matching local budget for API downloads after a sync reset,
report cloud-file mismatches in API/CLI errors, and document local IDs.
* [AI] Revoke sessions on sign-out and API shutdown
Revoke only the supplied session on explicit sign-out. Clear local credentials if the server cannot be reached, and revoke password-created API sessions during shutdown without revoking caller-supplied tokens.
Cover isolated and repeated revocation, invalid tokens, offline sign-out, borrowed API tokens and shutdown after a close failure. Session expiry policy is unchanged.
Follow-up to:
https://github.com/actualbudget/actual/security/advisories/GHSA-7363-3qq5-2frj
* [AI] Use amounts instead of cents in rule formulas
Rule formulas handed `amount`, `balance`, `parent_amount` and BALANCE_OF()
to the formula as integer cents, but read a numeric result back as an
amount, so `=amount * 1.05` came out 100x too large and users had to write
`=amount / 100 * 1.05`.
Convert the monetary variables with `integerToAmount` when building the
named expressions, so a formula reads and returns the same units either
way. The `amountToInteger` on the result moves out of executeFormulaSync
into the two branches that actually write an amount column, so a numeric
result stringified into a text field is no longer scaled up.
The transaction object itself stays in integers, keeping rule conditions,
Handlebars templates and the split percent/remainder math unchanged.
A migration rewrites stored formulas (`=amount / 100` becomes
`=(amount * 100) / 100`) so they keep producing the same result. It keeps
its own frozen copy of the variable names and the factor of 100, since
those describe how formulas worked when it was written.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* [AI] Stop spelling out the units in rule formula docs
With formulas working in the amounts shown in the app, saying so on every
variable is noise — the values now read the way you would expect.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* [AI] Move the rule formula migration after the account groups one
The migration id has to sort last, since checkDatabaseValidity compares
applied ids to available ones by position — a nightly user who already ran
1787013118115_add_account_groups would otherwise fail to open their budget
with out-of-sync-migrations.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* [AI] Drop the rule formula migration in favour of a documented break
Per review: the migration cannot hold up across devices. A new device
migrates and then replays newer messages, so a formula saved after the
migration ran is never converted; and once one device upgrades, the
converted formula it syncs reads 100x off on a device still running the
old build.
Rule formulas are experimental, so document the break instead and let
people drop the `/ 100` themselves. Also clears the leftover `/ 100` from
the ROUND, ROUNDDOWN, ROUNDUP and TRUNC examples.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* note update
* note update2
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* [AI] feat: auto bank sync
* chore: add release note
* [AI] fix: move auto sync settings to bank sync screen
* fix: typecheck failure due to missing required prop
* fix: update available intervals for auto sync
* [AI] feat: add custom interval logic
* [AI] fix: address review comments
* fix: consider server status to run auto sync
* fix: use oldest sync as reference point
* fix: update description
* [autofix.ci] apply automated fixes
* fix: rm irrelevant comments
* fix: rm cast
* doc: add auto bank sync info
* fix: discard empty non numeric draft val
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* Add Syncloud to the additional installation options
Syncloud is a server operating system for a Raspberry Pi, a spare PC or a VPS,
which packages actual-server as one of the apps it installs and updates. Listed
under the community options alongside the other platform guides, with one link
for what it is and one for setting a device up.
* Drop the Syncloud description and setup link
Keep only the link that is about running Actual, as requested in review.
* monte carlo income streams
* simplify release note
* enhancement
* fix withdrawal rule floor when income is specified
* update screenshots
* suplus pot and images
* way better description in the run details
* covering case where income is all thats setup and spending is the same as the income
* feedback
* screenshots and rename of minimum withdrawal to min spending
* additional docs
* Added api.setPreference({id, value})
Added the setPreference({id, value}) function to the api methods to allow api consumers to set SyncedPerfs values.
* Added return type to api.setPreference(id, value)
Added return type and removed unnecessary function comment. Updated api docs with correct return type Promise<void> instead of void.
* Removed useless setPreference persist test
* Type setPreference value by preference key
Hardened type on setPreference so that values are based on the type corresponding to the id. Also updated api doc reference.md
* fix floor and ceiling and boundaries rules when retirement starts after years of no spending
* updated doc
* added a breakdown of how the withdrwal rule was calculated
* add a bit of a gap between table and explainations
* unneded comment