Commit Graph

248 Commits

Author SHA1 Message Date
lelemm
4556e9e244 🐛 Prevent Pluggy bank sync from repeatedly rewriting transactions (#8392)
* Prevent Pluggy bank sync from repeatedly rewriting uncategorized and transfer transactions

* Regression test

* Changed solution for the fix

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-08 19:33:28 +00:00
lelemm
744dd1eaff Bank Sync Provider per budget file [2/3] - Sync server - Pluggy.ai use per-budget credentials when available (#8317)
* PR for pluggy ai wiring per file budget ni pluggy

* Fixes from rebase

* Code rabbit code review

* [autofix.ci] apply automated fixes

* added md

* Restoring client cache

* code review

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-04 19:10:51 +00:00
Matt Fiddaman
73c1fb19e9 recognise EUA expired GoCardless responses (#8383)
* recognise EUA expired GoCardless responses

* note

* add test
2026-07-04 13:08:32 +00:00
lelemm
b054e9a013 Bank Sync Provider per budget file [1/3] - Sync server - Add file-scoped bank sync secrets (#8316)
* First PR for secrets per file

* md

* Code Review

* Code rabbit code review

* Removed migration (new column) in favor of string concatenation

* automation UI: reference schedule by ID not name (#8308)

* schedule by id

* migrate schedule indicators

* add tests

* update release note

* fix loading states

* [AI] feat(budget analysis report): Add Balance & Category selector (#8162)

* [AI] feat(budget-report): add balance-only view mode

Add a 'Balance only' toggle to the Budget Analysis report that hides the
Budgeted, Spent, and Overspending Adjustment series and renders the balance
as a plain line chart, similar to the Net Worth graph.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] fix(budget-report): always display ending balance for consistent header spacing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] feat(budget-report): replace balance toggle buttons with series dropdown

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] fix(budget-report): capitalize Categories in balance mode dropdown

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] fix(budget-report): fix test imports and lint errors in BudgetAnalysisGraph test

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: added in release note

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] Fix Age of Money report widget title not saving correctly (#8320)

* [AI] Fix Age of Money report widget title not saving correctly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] Use mutateAsync to properly await mutations in AgeOfMoney widget

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix flaky vrt by increasing timeout (#8323)

* fix test

* note

* Update docs with Actuali iOS app (#8324)

* Update docs with Actuali iOS app

* Update packages/docs/docs/community-repos.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Initial payee location documentation (#8241)

* Initial payee location documentation

* Move to dedicated experimental section

Based on PR feedback

* [AI] feat(budget analysis report): show hidden categories (#8164)

* [AI] feat(budget-report): add toggle to include hidden categories

Add a 'Show hidden categories' button that includes hidden expense
categories in the Budget Analysis report. This prevents historic data
from being misrepresented when a category (e.g. a car fund) is hidden.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] feat(budget-report): replace hidden categories button with icon toggle

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [AI] fix(budget-analysis): use correct package alias in test import

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: added in release note

* [AI] refactor(budget-analysis): extract isBaseCategory helper and reuse in tests

Assisted-by: ClaudeCode:claude-sonnet-4-6

* [AI] feat(budget-report): clarify hidden categories toggle state

Replace the ambiguous icon-only toggle with a highlighted active state
and explicit click-to-show/hide tooltip text so the current selection is
clear. Add aria-pressed for accessibility.

Assisted-by: ClaudeCode:claude-opus-4.8

* [AI] feat(budget-report): consolidate chart options into Options dropdown menu

Replace the separate chart-type icon toggle, balance/category Select dropdown,
and hidden-categories eye icon with a single Options popover (Popover + Menu)
matching the pattern used by other reports (Sankey). The menu contains:
- Switch to line/bar chart
- Show balance (toggle)
- Show categories (toggle, replaces balanceOnly)
- Show hidden categories (toggle)

Also rebases onto upstream/master to incorporate the balanceOnly/Select
changes from #8162 before consolidating them.

Assisted-by: ClaudeCode:claude-sonnet-4-6

* [AI] fix(budget-report): use static 'Bar chart' toggle in Options menu for i18n

Replace the dynamic 'Switch to line/bar chart' string with a static
'Bar chart' label and a boolean toggle (on = bar, off = line), matching
the toggle pattern used by other menu items and making the string
straightforward to translate.

Assisted-by: ClaudeCode:claude-sonnet-4-6

* Enhance budget analysis report with dropdown options

Introduced a dropdown to group options and added the ability to show hidden categories in the budget analysis report.

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* update release process (#8334)

* [AI] Add cross-platform hook adding 🤖 emoji to comments (#8331)

* [AI] Add cross-platform hook keeping GitHub comments/issues in Chinese or pirate voice

Adds a shared agent hook that requires anything an agent posts to GitHub
(PR/issue comments, PR reviews, created issues) to be written in 简体中文
(preferred) or, failing that, a fun pirate voice — never plain English.

- scripts/agent-hooks/github-comment-style.sh: shared guard. Reads
  tool_input.body/.title, allows CJK or pirate-flavoured text, blocks plain
  English via exit 2. Fails open on malformed payloads.
- Wired for Claude (.claude/settings.json PreToolUse), Codex
  (.codex/config.toml PreToolUse) and Cursor (beforeMCPExecution adapter).
- Documents the rule in AGENTS.md, the canonical pr-and-commit-rules.md and
  the Cursor rule so platforms without hook support apply it manually.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Use a slug for the release-note filename; document slug naming in AGENTS.md

Release-note filenames don't need to be the PR number — a short descriptive
slug works too (the PR link is resolved at release time). Rename the new note
accordingly and note this in AGENTS.md's directory reference.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [autofix.ci] apply automated fixes

* [AI] Auto-label PRs with Chinese descriptions as "ai spam" via CodeRabbit

Add a CodeRabbit labeling instruction (auto_apply_labels is already on) that
applies the "ai spam" label when a PR description contains Chinese/CJK
characters — a strong signal of AI-generated spam for this English-language
project.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Exempt CodeRabbit-addressed comments from the Chinese/pirate voice hook

CodeRabbit parses its commands (@coderabbitai review/resolve/etc.) as plain
English, so the github-comment-style guard now skips any comment mentioning
@coderabbitai / @coderabbit instead of forcing it into Chinese or pirate.
Documented the exception in the canonical and Cursor rule files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Key CodeRabbit voice exemption on authorship, not mentions

The hook only ever runs on the agent's own outgoing comments, so CodeRabbit's
own comments are already out of scope (it posts under its own identity). Drop
the @coderabbitai mention bypass — a comment merely name-dropping the bot is
still the agent's prose and must follow the Chinese/pirate rule, closing an
easy plain-English bypass. Docs updated to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Pin CodeRabbit reviews to English

CodeRabbit was reading AGENTS.md / pr-and-commit-rules.md (which ask
contributors to comment in Chinese/pirate) and applying that to its own
reviews, posting them in Chinese. Set `language: en-US` and `tone_instructions`
so the bot's own reviews, summaries and replies stay in English; the
Chinese/pirate rule is for contributor/agent comments, not CodeRabbit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Replace Chinese/pirate comment voice with a robot-emoji prefix

Scrap the 简体中文/pirate voice scheme (and its CodeRabbit language override and
Chinese-based "ai spam" label) in favour of one simple rule: every GitHub
comment, review or issue an agent posts must be prefixed with 🤖. The shared
guard now blocks any body — or, for issues, title — that doesn't start with the
robot emoji; docs and per-platform wiring updated to match. .coderabbit.yaml is
restored to its original state.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Silence SC1007 false positive on CDPATH= cd in Cursor MCP adapter

shellcheck flags the intentional `CDPATH= cd` empty-env prefix as SC1007
(mistaking it for an assignment). Add a scoped inline disable directive on that
line; behaviour is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Fail closed on guard execution errors in Cursor MCP adapter

The adapter previously allowed the call on any non-0/2 exit from
github-comment-style.sh, so a broken or missing guard would silently disable
enforcement. Deny on unexpected exits instead (matching guard-shell.sh), with a
message that marks it as an execution problem rather than a real policy denial.
The shared guard still fails open on malformed payloads (its own exit-0 choice).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Add hook requiring a blank PR template when agents create PRs

New shared guard pr-template-blank.sh blocks mcp__github__create_pull_request
unless the body is the repo's PR template, unmodified (cosmetic whitespace
aside) — agents must leave it blank for the human, per AGENTS.md. Wired for
Claude (PreToolUse), Codex (PreToolUse) and Cursor (the beforeMCPExecution
adapter now dispatches per-tool to the right guard). Docs and a release note
updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht

* [AI] Simplify agent-hook guard scripts

- github-comment-style.sh: fold the empty/whitespace-only case into the
  prefix check, dropping a redundant tr subshell per field.
- pr-template-blank.sh: drop the dead CR strip in canon() (the trailing
  whitespace sub already removes a trailing carriage return).
- before-mcp-github.sh: emit the constant allow payload with printf instead
  of spawning jq on the common (allow) path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] Drop duplicated PR-template/robot-emoji rules now that hooks enforce them

The PR-template-blank and GitHub-comment robot-emoji-prefix rules are enforced
by cross-platform hooks, so the copies scattered across AGENTS.md and the
Cursor rules file are redundant. Remove them and point at the canonical
pr-and-commit-rules.md, which keeps the full rule (including the PR-template
Chinese exception that no hook can enforce). Also delete the internal
PR-template-guard release note.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] Drop "enforced by hooks" wording from the PR/comment rule docs

State the rules plainly without the meta-commentary about hook enforcement.
Keeps the actual rules (don't fill the PR template, prefix GitHub
comments/reviews/issues with 🤖, the Chinese exception, and the
your-own-comments-only note) intact.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] Trim enforcement meta-commentary; shorten robot-emoji release note

Drop the "isn't enforced automatically" / "handled by tooling" framing from
AGENTS.md and pr-and-commit-rules.md (keeping the rule and the "apply it
yourself" responsibility), and shorten the robot-emoji-prefix release note.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] Fail closed on unreadable payloads in the GitHub agent guards

Align the new guards with the git-guard.sh / guard-shell.sh convention: fail
closed when the hook payload can't be read, fail open only on a genuinely
absent optional field.

- github-comment-style.sh: block on invalid JSON / missing / non-object
  .tool_input; allow only an absent body/title within a valid object.
- before-mcp-github.sh: deny on a jq parse failure / missing .tool_name
  instead of falling through to allow.
- pr-template-blank.sh: an absent/null body now fails open (previously it
  normalized to "" and wrongly blocked PR creation); an empty-string body is
  still treated as a real non-template submission.
- .codex/config.toml: describe the full guard scope (comment body + issue
  title) in the hook comment and statusMessage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* speed up electron builds (#8337)

* size up runners, skip translations where possible

* note

* mdx -> md (#8338)

* mdx -> md

* [AI] Outlaw .mdx in the docs package

Docusaurus 3 compiles .md through MDX, so JSX and imports work in plain
.md files and there is no need for the .mdx extension. Narrow the
enforce-doc-links remark plugin to only accept .md: reject any .mdx
source file at build time, flag links that target a .mdx file, and drop
the .mdx branches from the directory scanner and link resolution.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* [AI] Add performance indexes for transactions table (#8335)

* [AI] Add performance indexes for transactions table

* release notes

* [AI] Remove unselective tombstone-only index from performance migration

* [AI] api: make the browser build work in consumer production bundles (#8289)

* api: make the browser build work in consumer production bundles

The browser build shipped `new Worker(new URL("./worker.js", import.meta.url))`
in dist/browser.js. A consumer bundler (Vite/Rollup) recognizes that as a worker
entry and re-bundles the already-prebuilt worker.js from scratch, which desyncs
the absurd-sql/connection RPC so `init` throws a structured-clone error. It only
worked in dev and in the package's own e2e because both serve dist verbatim.

Make the browser build fully self-contained instead:

- Inline the worker into browser.js and spawn it from a Blob URL, so consumer
  bundlers never see a worker entry to re-bundle. The worker is built as an IIFE
  (classic worker), mirroring the web app's kcab.worker.
- Embed the sql.js wasm and the default filesystem data (migrations + default
  DB) into the worker so it performs no PUBLIC_URL asset fetches:
  - loot-core sqlite gains an opt-in `setWasmBinary` (dormant for web/node);
  - the worker installs a scoped fetch shim serving the embedded data/* files
    from a sentinel base URL.
- Share the asset-collection logic between the Node disk copy and the embedded
  build via scripts/embedded-assets.mjs so the two never drift.

Result: `import '@actual-app/api'` + `init()` works in any bundler with zero
config; only COOP/COEP headers remain required (SharedArrayBuffer).

Adds an e2e (e2e/consumer + e2e/global-setup.mjs) that builds a real consumer
app for production and boots it under COOP/COEP — coverage the verbatim-dist
harness can't provide. Docs note the build is self-contained and that
cross-origin isolation is still required.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: address review feedback (guard DOM lookup, revoke blob URL on worker failure)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: address review nitpicks (drop BodyInit cast, reword release note)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: build the browser worker via Vite ?worker&inline

Replace the hand-rolled worker inlining (a custom Vite plugin that read the
prebuilt worker.js off disk and inlined it as a string, plus a second build
config and a build-ordering dependency) with Vite's native `?worker&inline`
import. This collapses the two browser build configs into one, removes the
`virtual:actual-worker-code` module and the manual Blob URL spawn, and drops
the worker-before-facade build step.

Vite's `?worker&inline` sub-build doesn't receive vite-plugin-node-polyfills'
global-shim injection (the plugin writes it to `build.rollupOptions`, but the
worker sub-build reads `worker.rollupOptions`), so the worker crashed on
`process is not defined`. Patch the plugin to also emit the injection on
`worker.rollupOptions` — additive and backwards compatible (the web app build,
including loot-core's nodePolyfills worker, is unaffected).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: embed browser assets via Vite, drop the embedded-assets script

Replace scripts/embedded-assets.mjs with Vite-native asset imports in the
worker entry: `?inline` for the sql.js wasm and default DB, `import.meta.glob`
(`?raw`) for the migrations. The worker builds the default-filesystem wire
format from those at module load, so the embedded set comes straight from
loot-core and can't drift.

The Node build only needs migrations + the default DB on disk (it reads them at
runtime); the old script also wrote sql-wasm.wasm and the data/ fetch tree,
which are dead now that the browser worker is self-contained. Replace
writeEmbeddedAssetsToDist with a small closeBundle copy of just those two.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: simplify embedded data map; drop loot-core comment churn

Merge the worker's binData/textData into a single dataFiles map (Response
accepts both bytes and strings), collapsing the fetch shim's two lookups into
one. Revert the unrelated comment edits to loot-core's backend-worker.ts and
fs/index.ts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: rename e2e consumer-dist to consumer/dist; drop a prose comment

Use the standard `dist` directory name for the consumer fixture's build output
(now e2e/consumer/dist), and remove the verbose comment from index.browser.ts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] loot-core: own the default-filesystem assets; consumers stop reaching in

Add packages/loot-core/default-filesystem.mjs as the single source of truth for
loot-core's runtime assets (sql.js wasm, default DB, migrations) and the
data-file-index wire format, exported as @actual-app/core/default-filesystem.

- @actual-app/api: the browser worker embeds them via the actual-embedded-assets
  Vite plugin (which calls collectEmbeddedAssets), replacing the relative
  ?inline/?raw/import.meta.glob reaches into ../loot-core; the Node build copies
  migrations + default DB using the helper's paths. Drops the direct
  @jlongster/sql.js devDependency (loot-core resolves the wasm).
- @actual-app/web: stagePublicData copies from the helper's paths instead of
  hardcoding loot-core's tree and the sql.js wasm location.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] loot-core: scan migrations once when collecting embedded assets

collectEmbeddedAssets() read the migrations directory twice — once in its
own loop and once via buildDataFileIndex(). List the names once and pass
them through, keeping the index wire-format defined in a single place.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* Restore budget table scroll position when navigating back from spent transactions (#8144)

* feat: Restore budget table scroll position when navigating back from spent transactions page

* fix: Update transaction tests to include new categories and adjust assertions

* Add release notes for budget scroll position restoration

* refactor: Improve error handling when clicking on visible spent-amount cells, per suggestion from coderabbit bot

* refactor: revert changes to test budget, add separate scroll-test test budget

* revert previous snapshot changes

* Add gitignore pattern for personal devcontainer config

* fix: broken tests

* refactor: address coderabbitai feedback

https://github.com/actualbudget/actual/pull/8144#discussion_r3446850158

* fix: update VRT snapshots for onboarding screen

* [autofix.ci] apply automated fixes

* refactor: modify scrolling test to add categories to existing test budget instead of creating separate budget

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* [AI] Add design competition blog post for sidenav redesign (#8297)

* [AI] Add design competition blog post for sidenav redesign

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HbXFM9ooWVw9SRA9No7vhF

* [AI] Set design competition blog post publish date to 27th July 2026

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HbXFM9ooWVw9SRA9No7vhF

* [AI] Remove stray closing tags from design competition blog post

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HbXFM9ooWVw9SRA9No7vhF

* Update 2026-07-27-design-competition-sidenav.md

---------

Co-authored-by: Claude <noreply@anthropic.com>

* Always show stacked bar graph tooltip label on hover (#8356)

* Always show stacked bar graph tooltip label on hover

* Handle edge case with maxToolTipItems+1 labels showing

* Replace last item label instead of adding new one

* Filter visible items on non zero values for consistency

* make SimpleFIN token 'forbidden' check more flexible (#8339)

* make SimpleFIN token 'forbidden' check more flexible

* clear accessKey when token is updated

* https -> fetch

* [AI] Add tests for SimpleFIN token claim fix

Cover the flexible 'Forbidden' matching, full claim-response handling, and access-key invalidation when the setup token changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* note

* Update upcoming-release-notes/fix-simplefin-token-claim.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* coderabbit feedback

* [AI] Test SimpleFIN blank access key handling

Cover rejecting a blank claim response (not persisted) and re-claiming when an empty access key is cached.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>
Co-authored-by: tabedzki <35670232+tabedzki@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: youngcw <calebyoung94@gmail.com>
Co-authored-by: Matt Farrell <10377148+MattFaz@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Dustin Brewer <mannkind@thenullpointer.net>
Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Alec Bakholdin <43560338+alecbakholdin@users.noreply.github.com>
Co-authored-by: clintharris <336401+clintharris@users.noreply.github.com>
Co-authored-by: Darwin Do <8429648+dsmaugy@users.noreply.github.com>
2026-07-03 19:58:35 +00:00
Matt Fiddaman
e8c7f8040e bcrypt -> argon2id (#7829)
* bcrypt -> argon2id

* coderabbit feedback

* copilot review

* switch hashing settings

* [AI] Add unit tests for sync-server password hashing

Cover password.js, focused on the bcrypt -> argon2id migration:
- loginWithPassword upgrades a legacy bcrypt hash to argon2id on a
  successful login, leaves it untouched on a failed login, and does not
  needlessly rehash an existing argon2id hash
- hashPassword/verifyPassword round-trip, bcrypt backward-compat, and
  graceful handling of non-string/malformed hashes
- bootstrapPassword/changePassword/checkPassword behaviour

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* dedupe minimatch

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 00:04:07 +00:00
lelemm
ae0594d294 Added support to pluggy date correction based on installments (#8380)
* Added support to pluggy date correction based on installments

* md

* Update packages/sync-server/src/app-pluggyai/app-pluggyai.js

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* date now considers instalments, originalDate is the date without it

* creating new instance instead of same obj

* code review

* aligning to code review

* simpler

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-07-02 19:07:08 +00:00
github-actions[bot]
8b5b182dbc 🔖 (26.7.0) (#8332)
* 🔖 (26.7.0)

* Generate release notes for v26.7.0

* update blog author

* Generate release notes for v26.7.0

* release notes

* Generate release notes for v26.7.0

* Generate release notes for v26.7.0

* Generate release notes for v26.7.0

* relink PRs from cherry-picked changes

* tweak wording of release summary

* update blog post date

* akahu experimental

---------

Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>
2026-07-02 16:10:16 +00:00
Matt Fiddaman
e891c2e30e reorganise the SimpleFIN key saving logic (#8362)
* remove the simplefin intercept in the secrets handler

* note
2026-06-29 21:04:27 +00:00
Matt Fiddaman
617fe63196 make SimpleFIN token 'forbidden' check more flexible (#8339)
* make SimpleFIN token 'forbidden' check more flexible

* clear accessKey when token is updated

* https -> fetch

* [AI] Add tests for SimpleFIN token claim fix

Cover the flexible 'Forbidden' matching, full claim-response handling, and access-key invalidation when the setup token changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* note

* Update upcoming-release-notes/fix-simplefin-token-claim.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* coderabbit feedback

* [AI] Test SimpleFIN blank access key handling

Cover rejecting a blank claim response (not persisted) and re-claiming when an empty access key is cached.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-06-28 22:29:29 +00:00
Matiss Janis Aboltins
c4acaa2951 [AI] Add knip to detect unused files, dependencies and exports (#8309)
Adds the knip tool with a monorepo-tuned config (knip.json) and a CI gate
in check.yml, plus the dead-code and dependency cleanup it surfaced. The
unused-export/type/duplicate rules are disabled so exporting an unused
symbol is allowed.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 22:11:48 +00:00
Matt Fiddaman
c4775bf288 wire tests up to depot test results analysis (#8262)
* emit junit files

* wire up the actions

* note

* [autofix.ci] apply automated fixes

* fix overly broad error

* scope to master runs

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-06-21 07:50:55 +00:00
dependabot[bot]
c1c4856aa7 Bump dompurify in the npm_and_yarn group across 1 directory (#8286)
Bumps the npm_and_yarn group with 1 update in the / directory: [dompurify](https://github.com/cure53/DOMPurify).


Updates `dompurify` from 3.4.10 to 3.4.11
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.4.10...3.4.11)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-version: 3.4.11
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-21 07:12:44 +00:00
Oskar
4b5642b732 EasyBank PayeeName fallback fix (#8243)
* fix fallback logic for easybank payeeName

* yarn generate:release-notes

* fix type error

* expand hasCreditor check

* [autofix.ci] apply automated fixes

* simplify changelog

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-06-19 20:57:28 +00:00
Matiss Janis Aboltins
0e3991ad62 [AI] Fix CORS proxy GitHub API allowlist prefix bypass (#8273)
* [AI] Fix CORS proxy GitHub API allowlist prefix bypass

The api.github.com branch of the plugin allowlist check used a
boundary-less startsWith, so an allowlisted `owner/repo` also authorized
prefix-matched repos such as `owner/repo-private`. With a server GitHub
token configured, this let authenticated users read private repositories
through the proxy. Require an exact match or a trailing-slash path
boundary instead, and add regression tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] Shorten CORS proxy fix release note

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 22:08:43 +00:00
dependabot[bot]
e0880f3901 Bump the npm_and_yarn group across 1 directory with 2 updates (#8272)
Bumps the npm_and_yarn group with 2 updates in the / directory: [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) and [undici](https://github.com/nodejs/undici).


Updates `http-proxy-middleware` from 3.0.6 to 3.0.7
- [Release notes](https://github.com/chimurai/http-proxy-middleware/releases)
- [Changelog](https://github.com/chimurai/http-proxy-middleware/blob/v3.0.7/CHANGELOG.md)
- [Commits](https://github.com/chimurai/http-proxy-middleware/compare/v3.0.6...v3.0.7)

Updates `undici` from 7.27.2 to 7.28.0
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v7.27.2...v7.28.0)

---
updated-dependencies:
- dependency-name: http-proxy-middleware
  dependency-version: 3.0.7
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: undici
  dependency-version: 7.28.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-18 19:48:40 +00:00
Matiss Janis Aboltins
ac572b5e5f [AI] Document user enumeration trade-off on /admin/users endpoint (#8270)
* [AI] Document accepted user-enumeration trade-off on /admin/users endpoint

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SffVjwqvL1TJPyR2KVjKaN

* [AI] Add release note for /admin/users user-enumeration documentation

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SffVjwqvL1TJPyR2KVjKaN

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-18 19:45:40 +00:00
Kevin
de245df61f Upgrade pluggy-sdk and migrate away from deprecated page-size transactions to cursor-based transactions (#8103)
* Bump pluggy-sdk to include new fetchTransactionsCursor GET /v2/transactions

* Use fetchAllTransactions, which uses cursor-based /v2/transactions

* Remove weak helper function

* Add release note

* Fix unused result mapping

* Add lelemm's changes

Co-authored by: lelemm

* Fix lint

* Add lelemm as co-author on release note

* Update message

* Fix comma typo

* Update yarn.lock

---------

Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>
2026-06-17 15:48:56 +00:00
Matt Fiddaman
fbdad57ff0 ⬆️ mid month dependency updates (#8235)
* @monorepo-utils/workspaces-to-typescript-project-references (^2.10.3 → ^2.11.0)

* @types/node (^22.19.17 → ^22.19.21)

* eslint (^10.2.0 → ^10.5.0)

* eslint-plugin-perfectionist (^5.8.0 → ^5.9.0)

* lage (^2.15.5 → ^2.15.13)

* typescript (^6.0.2 → ^6.0.3)

* vitest (^4.1.2 → ^4.1.8)

* better-sqlite3 (^12.8.0 → ^12.10.0)

* vite (^8.0.5 → ^8.0.16)

* cosmiconfig (^9.0.1 → ^9.0.2)

* react-aria-components (^1.16.0 → ^1.18.0)

* @chromatic-com/storybook (^5.1.1 → ^5.2.1)

* @storybook/addon-a11y (^10.3.4 → ^10.4.4)

* @storybook/addon-docs (^10.3.4 → ^10.4.4)

* @storybook/react-vite (^10.3.4 → ^10.4.4)

* @types/react (^19.2.14 → ^19.2.17)

* @vitejs/plugin-react (^6.0.1 → ^6.0.2)

* eslint-plugin-storybook (^10.3.4 → ^10.4.4)

* storybook (^10.3.4 → ^10.4.4)

* @bufbuild/protobuf (^2.11.0 → ^2.12.0)

* @bufbuild/protoc-gen-es (^2.11.0 → ^2.12.0)

* @babel/core (^7.29.0 → ^7.29.7)

* @codemirror/autocomplete (^6.20.1 → ^6.20.3)

* @react-aria/interactions (^3.27.1 → ^3.28.1)

* @reduxjs/toolkit (^2.11.2 → ^2.12.0)

* @tanstack/react-query (^5.96.2 → ^5.101.0)

* @uiw/react-codemirror (^4.25.9 → ^4.25.10)

* date-fns (^4.1.0 → ^4.4.0)

* hyperformula (^3.2.0 → ^3.3.0)

* lru-cache (^11.2.7 → ^11.5.1)

* react-aria (^3.47.0 → ^3.49.0)

* react-error-boundary (^6.1.1 → ^6.1.2)

* react-hotkeys-hook (^5.2.4 → ^5.3.2)

* react-redux (^9.2.0 → ^9.3.0)

* react-spring (^10.0.3 → ^10.0.4)

* rolldown (^1.0.0-rc.13 → ^1.1.1)

* sass (^1.99.0 → ^1.101.0)

* vite-plugin-pwa (^1.2.0 → ^1.3.0)

* @docusaurus/core (^3.10.0 → ^3.10.1)

* @docusaurus/plugin-content-docs (^3.10.0 → ^3.10.1)

* @docusaurus/plugin-ideal-image (^3.10.0 → ^3.10.1)

* @docusaurus/preset-classic (^3.10.0 → ^3.10.1)

* @docusaurus/theme-common (^3.10.0 → ^3.10.1)

* @docusaurus/theme-mermaid (^3.10.0 → ^3.10.1)

* @easyops-cn/docusaurus-search-local (^0.55.1 → ^0.55.2)

* @docusaurus/module-type-aliases (^3.10.0 → ^3.10.1)

* @oxlint/plugins (^1.60.0 → ^1.69.0)

* ua-parser-js (^2.0.9 → ^2.0.10)

* fast-check (^4.6.0 → ^4.8.0)

* jest-diff (^30.3.0 → ^30.4.1)

* workbox-precaching (^7.4.0 → ^7.4.1)

* ipaddr.js (^2.3.0 → ^2.4.0)

* jws (^3.2.2 → ^3.2.3)

* http-proxy-middleware (^3.0.5 → ^3.0.6)

* oxlint (^1.59.0 → ^1.69.0)

* @codemirror/view (^6.41.0 → ^6.43.1)

* yarn dedupe

* nano-staged (^0.8.0 → ^1.0.2)

* commander (^14.0.3 → ^15.0.0)

* react (>=19.2 → 19.2.7)

* react-dom (>=19.2 → 19.2.7)

* @rolldown/plugin-babel (~0.1.8 → ~0.2.3)

* downshift (9.3.2 → 9.3.6)

* i18next (^25.10.10 → ^26.3.1)

* react-i18next (^16.6.6 → ^17.0.8)

* react-router (7.15.0 → 7.17.0)

* vite-plugin-node-polyfills (^0.27.0 → ^0.28.0)

* jws (^3.2.3 → ^4.0.1)

* oxlint-tsgolint (^0.20.0 → ^0.23.0)

* jsdom (^27.4.0 → ^29.1.1)

* note

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* crdt regenerate and version bump

* fix test

* yarn dedupe

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-06-16 09:21:37 +00:00
Tim
7b0463cd91 Use Akahu transaction description as fallback payee name (#8116)
* Use Akahu transaction description as fallback payee name

* tweak guards

* ensure dates are in NZ time

* update merchant.name to fallback to other_account

* fix date format

* cleanup
2026-06-11 07:52:58 +00:00
Nustinua
34b748c002 Add Enable Banking PSU type selection (#8028)
* Add Enable Banking PSU type selection

* [autofix.ci] apply automated fixes

* Add release note for Enable Banking PSU type selection

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>
2026-06-09 17:25:01 +00:00
Tim
7ee821343b Refresh akahu account if stale (#8115)
* Refresh akahu account if stale

* Update upcoming-release-notes/akahu-refresh.md

* wait for the refresh to complete

* check account is defined

* use local variable

* move into helper

* increase poll delay

* bump refresh interval to 1 hour

* ensure only one account refresh is running at a time
2026-06-08 20:52:16 +00:00
ItsThatDude
f1c0960fee Feature: Add Akahu New Zealand bank sync (#6041)
* add akahu integration for nz banks

* akahu fix bank name being set to account name

* rename apiToken and fix reset pointing to the wrong function

* fix apitoken wording in akahu init modal

* add upcoming-release-notes

* fix lint issues

* fix lint issues

* add loan account type to starting balance inversion

* fix initial sync balance

* initially select 365 days of transactions on first sync for akahu sync

* add SyncServerAkahuAccount to onSetLinkedAccount

* set transaction currency to account currency

* remove unnecessary code

* handle TFR TO/FROM payees and account for loan account type in transactions

* [autofix.ci] apply automated fixes

* rename Error to ErrorAlert and fix intial sync start date

* extract note from description for TFR TO/FROM transactions

* fix normalizeNotes not referencing the transaction

* refactor app-akahu code

* [autofix.ci] apply automated fixes

* Add Akahu to ExternalAccount type

* Update yarn.lock

* update yarn.lock

* Remove unused error var in catch block

* [autofix.ci] apply automated fixes

* require authentication for akahu endpoint

* fix lint issue in mutations.ts

* fix up import paths

* fix lint issues

* reorder form fields

* remove unnecessary handling for debt accounts

* lint fixes

* Put Akahu bank sync under feature flag

* remove incorrect feedback link

* [autofix.ci] apply automated fixes

* Add feedback link for feature toggle

* use uuidv4

* prevent fetch if feature not enabled

* change app-akahu to ts and tidy up

* fix typecheck errors

* [autofix.ci] apply automated fixes

* fix browser client build issues

* [autofix.ci] apply automated fixes

* update akahu npm package to latest version

* use amountToInteger for balance reducer

* add additional details to transactions

* change initial sync start date logic

* add akahu fields to mappable fields in desktop-client

* [autofix.ci] apply automated fixes

* getDate use formatISO to get the timezone adjusted date

* remove duplicate payeeName

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-06-06 18:05:10 +00:00
mheiland
67ea8d9ad0 [AI] Skip unimportable Enable Banking transactions instead of failing the whole sync (#8086)
* [AI] Skip unimportable Enable Banking transactions instead of failing the whole sync

Enable Banking bank-sync aborts the whole account import with a client-side SQLITE_ERROR when a fetched transaction cannot be inserted — most commonly a pending transaction with no booking/value/transaction date (normalized to date ''), or a non-numeric amount. Add an isImportableTransaction helper and skip such records in the /transactions handler (logging the count) so the rest of the account imports. Dated pending transactions are unaffected.

* [AI] Add release note for #8086

* [AI] Reject empty Enable Banking transaction amounts

Number('') is 0 (finite), so an empty/whitespace amount slipped through isImportableTransaction as a zero transaction. Trim and reject empty amounts explicitly, with a test. Addresses PR review feedback.

* Update packages/sync-server/src/app-enablebanking/services/enablebanking-service.ts

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>

* Update packages/sync-server/src/app-enablebanking/app-enablebanking.ts

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>

* [AI] Remove now-unused skippedCount counter

The summary log that read it was removed when accepting the review
suggestion; drop the dangling write-only counter so the loop matches
the per-transaction skip log that remains.

---------

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2026-06-06 16:23:10 +00:00
Matiss Janis Aboltins
f667b5c656 [AI] Add SSRF protection to SimpleFIN bank sync integration (#8012)
* [AI] Add SSRF protection to SimpleFIN bank sync integration

The SimpleFIN integration made server-side HTTP requests to user-controlled
URLs (the base64-encoded claim token and the resulting access-key base URL)
without any validation, unlike the CORS proxy which blocks private IPs via
ipaddr.js.

Add a shared assertUrlAllowed() helper that rejects non-http(s) URLs and
blocks requests to private, loopback, link-local, unique-local, reserved,
broadcast and unspecified addresses. Hostnames are resolved via DNS so names
pointing at internal addresses are blocked too, not just literal IPs. Apply
it before both outbound requests in getAccessKey() and getAccounts().

* [AI] Unify CORS proxy private-IP check with shared SSRF helper

The CORS proxy had its own inline ipaddr.js private-IP check that
duplicated the logic in the SimpleFIN SSRF helper. Export isBlockedIp()
from util/ssrf and reuse it in app-cors-proxy, removing the duplicate.

This also widens the CORS proxy's coverage to the reserved and broadcast
ranges and normalizes IPv4-mapped IPv6 addresses, matching the SimpleFIN
helper. The cors-proxy tests now exercise the real ipaddr.js against real
private IPs instead of mocking it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* [AI] Rename SimpleFIN SSRF release note to match PR number

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Implement SSRF protection for SimpleFIN bank sync

Requests to private, loopback, link-local, and other internal addresses are now blocked.

* [AI] Allow self-hosted private SimpleFIN servers by default

The SSRF protection blocked all private/loopback addresses, which broke
self-hosters running their own SimpleFIN bridge on a LAN or VPN IP with
no way to opt back in.

Split the blocked ranges into two tiers: link-local (cloud metadata),
reserved, broadcast and unspecified stay blocked unconditionally, while
private/loopback/unique-local are blocked by default but can be permitted
per-caller via { allowPrivateNetwork: true }. SimpleFIN opts in, so
self-hosting works out of the box with no env var, while the worst-case
cloud-metadata credential-theft vector remains closed. The CORS proxy
keeps the strict default.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* [AI] Re-validate SSRF rules on each SimpleFIN redirect hop

getAccounts used fetch with redirect: 'follow', so a 3xx response from the
validated SimpleFIN URL could redirect to a blocked internal address after
only the initial URL had been checked.

Follow redirects manually instead, calling assertUrlAllowed on every hop
before fetching, capping at 5 redirects, and dropping Authorization on
cross-origin hops so the bridge credentials cannot leak to a redirect
target. getAccessKey uses https.request, which does not auto-follow
redirects, so it is unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-06 14:17:23 +00:00
Matiss Janis Aboltins
1f492387b7 🔖 (26.6.0) (#7947)
* 🔖 (26.6.0)

* Generate release notes for v26.6.0

* Generate release notes for v26.6.0

* Update check-spelling metadata

* Generate release notes for v26.6.0

* add release summary

* links

* Generate release notes for v26.6.0

* update links

* Generate release notes for v26.6.0

* Generate release notes for v26.6.0

* Generate release notes for v26.6.0

* add EB link

* Add initial docs for Enable Banking (#7961)

* Add initial docs for Enable Banking

* Delete upcoming-release-notes/7961.md

* [Docs] UI Budget automations & month-end cleanup (#7863)

* Add files via upload

* Create documentation for budget automation feature

Added documentation for the budget automation feature, detailing its functionalities, usage, and examples.

* Add files via upload

* Revise budget automation documentation for clarity

Updated the documentation for budget automation, enhancing clarity and consistency in the descriptions of features and options.

* [autofix.ci] apply automated fixes

* Update Markdown headers for budget automation section

* Add files via upload

* Improve formatting in budget automation documentation

Updated formatting and added line breaks for better readability in the budget automation documentation.

* [autofix.ci] apply automated fixes

* Fix HTML line breaks in budget-automation.md

Updated HTML line breaks from '&lt;/br&gt;' to '<br>' in budget automation documentation.

* Fix HTML line breaks in budget automation documentation

* Fix headings and formatting in budget-automation.md

Updated formatting and corrected headings in budget automation documentation.

* Add budget-automation to sidebar items

* Add files via upload

* Add files via upload

* Revise budget automation documentation

Updated budget automation documentation to clarify usage of notes templates and detailed instructions for month-end cleanup processes, including named pools and weight calculations.

* [autofix.ci] apply automated fixes

* Refactor budget automation documentation for clarity

Updated various sections for clarity and consistency, including grammar corrections and improved phrasing.

* [autofix.ci] apply automated fixes

* Enhance budget automation documentation

Updated budget automation documentation with new blog links and improved formatting.

* Fix links in budget-automation.md

Updated links in the budget automation documentation to remove 'docs' prefix for consistency.

* Revise budget automation documentation with updates

Updated images and text for budget automation documentation, including corrections and enhancements to clarity.

* Improve clarity and add save reminders in budget automation

Updated wording for clarity and added reminders for saving work.

* Update budget automation documentation for clarity

Clarify the process of handling leftover funds in the budget automation script.

* [autofix.ci] apply automated fixes

* Add 'overfund' and 'overfunded' to spelling expect list

* Update spelling expectations by removing 'overfunded'

Removed 'overfunded' from the spelling expectations.

* Add files via upload

* 'overfund' to 'overfunded'

First the bot tells me overfunded isn't needed if overfund is there. Now, it tells me that overfunded IS needed. I give up.

* Enhance budget automation documentation

Added new sections on automation adjustments, moved some sections around, added horizontal separators.

* Update images and descriptions in budget automation docs

* Add files via upload

* Add automation notes section to budget automation docs

Added a section on automation notes to document how automations, balance caps, and long-term goals can have associated notes for clarity and tracking.

* Add files via upload

* Update budget automation examples in documentation

* Add files via upload

* Apply suggestions from code review

Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>

* [autofix.ci] apply automated fixes

* Add 'unmigrate' to spelling expectations

* Improve clarity in budget automation documentation

Clarified descriptions for budget automation features and improved wording for better understanding.

* Correct spelling of 'unmigrate' to 'Unmigrate'

They are the same and github is punking me again.

* Clarify automation notes description

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Fix typos in budget-automation documentation

Corrected typos and improved clarity in the budget automation documentation.

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>

---------

Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>
Co-authored-by: Mats Nilsson <matni403@gmail.com>
Co-authored-by: Juulz <julesmcn@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-06-01 16:30:43 +00:00
Matiss Janis Aboltins
18a8dc03c4 [AI] Restrict owner-only sync-server file management endpoints (#7977)
* [AI] Restrict owner-only sync-server file management endpoints

The /delete-user-file, /reset-user-file, and /user-create-key endpoints
guarded access via requireFileAccess, which accepted any user holding a
user_access row. A shared collaborator could therefore mark another
owner's hosted budget file as deleted, reset its sync state, or rewrite
its encryption key.

Add a stricter requireFileOwner helper (owner or server admin only) and
apply it to the three management endpoints. requireFileAccess keeps its
shared-access fallback for the collaboration endpoints (sync, upload,
download, get/update filename, get/create encryption key consumers).

Fixes GHSA-23vm-ffgg-qvjr.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Align release-note filename with PR number

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Update release notes for sync-server endpoint restrictions

Clarified the restriction on sync-server endpoints to only allow file owners and admins to perform owner-only file-management actions.

* Update upcoming-release-notes/7977.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-05-29 07:39:39 +00:00
Matiss Janis Aboltins
f3a5bb3ce5 Revert "[AI] chore: update alpine base image to 3.23.4" (#7985)
* Revert "[AI] chore: update alpine base image to 3.23.4 (#7939)"

This reverts commit 366045a6b0.

* Add release notes for PR #7985

* Delete upcoming-release-notes/7985.md

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-05-28 21:18:31 +00:00
Matiss Janis Aboltins
ff70d2d5f8 [AI] Upgrade dependencies to resolve security advisories (#7982)
* [AI] Upgrade dependencies to resolve security advisories

Resolve 9 Dependabot security alerts via version upgrades:

- rollup: remove the resolutions pin that forced workbox-build's
  rollup ^2.79.2 up to vulnerable 4.40.1; it now resolves to the
  patched 2.80.0 (vite 8 uses rolldown, so no rollup 4.x is needed)
  (CVE-2026-27606)
- serialize-javascript: add resolution ^7.0.5 (build-time only;
  consumers are deep-transitive webpack/terser plugins pinned to ^6)
  (GHSA-5c6j-r48x-rmvq, CVE-2026-34043)
- express-rate-limit ^8.3.2 -> ^8.5.2, pulling ip-address 10.2.0
  (CVE-2026-42338)
- refresh in-range transitives: bn.js 5.2.3, ajv 8.20.0, glob 10.5.0,
  path-to-regexp 8.4.2 (CVE-2026-2739, CVE-2025-69873, CVE-2025-64756,
  CVE-2026-4926, CVE-2026-4923)

Not addressed: elliptic (CVE-2025-14505) has no patched release;
uuid 8.3.2 (dev-only via sockjs, not reachable) left as-is.

https://claude.ai/code/session_018obbND7t9dBZvfvUBBJKFz

* [AI] Remove redundant minimatch resolution pins

Five of the six minimatch resolutions were no-ops: their consumers use
caret ranges (^3.x, ^5.x, ^9.x, ^10.x) that already float to versions
patched against CVE-2026-26996 (3.1.5, 5.1.9, 9.0.9, 10.2.5). Only
serve-handler pins minimatch to exactly 3.1.2 (vulnerable), so the
single targeted "minimatch@3.1.2" override is kept. The resolved
lockfile is unchanged.

https://claude.ai/code/session_018obbND7t9dBZvfvUBBJKFz

* Add release notes for PR #7982

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-05-28 19:03:12 +00:00
camcast3
366045a6b0 [AI] chore: update alpine base image to 3.23.4 (#7939)
* chore: update alpine base image to 3.23.4

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: add release notes for alpine 3.23.4 update

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: use alpine:3.23 minor version tag per reviewer suggestion

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-25 07:46:58 +00:00
Matiss Janis Aboltins
c8cb8a223a [AI] Reject disabled-user sessions in sync-server (#7940)
* [AI] Reject disabled-user sessions in sync-server

Sync-server validateSession previously accepted any unexpired token,
so a disabled user kept post-authentication access until their session
row expired. With the default token_expiration of "never" this could
persist indefinitely.

getSession now joins users and only returns sessions whose user is
enabled, and updateUserWithRole/deleteUser revoke that user's
existing sessions in the same transaction.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Rename release notes to match PR number

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Update release notes for user session invalidation

Sync-server now invalidates sessions for disabled users when using OpenID.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 07:44:13 +00:00
Matt Fiddaman
21fda8ffbc convert GoCardless sync-server code to Typescript (#7904)
* ts migration

* strict mode in tests

* banks strict

* helper/util files strict

* note

* coderabbit
2026-05-21 16:32:22 +00:00
Matt Fiddaman
132b9db11c use separate_continuous_history_consent flag from GoCardless (#7890)
* use separate_continuous_history_consent flag from GoCardless

* add logging for GCL

* note

* test
2026-05-18 22:29:46 +00:00
Matiss Janis Aboltins
3494f78c94 [AI] Restrict secrets API access to admins in OpenID mode (#7862)
* [AI] Require admin for GET /secret/:name in OpenID mode

The GET handler only verified an authenticated session, while the
sibling POST handler enforced an admin gate when the active auth
method is openid. A non-admin BASIC user in an OpenID multi-user
deployment could enumerate which admin-managed bank-sync secrets
were configured by probing 204 vs 404 responses.

Factor the auth-method + admin check into a shared helper used by
both POST and GET, and restrict the GET :name parameter to the
known SecretName enum so unrelated probing returns 404 up front.

* [AI] Simplify secrets auth guard after review

- Use existing getActiveLoginMethod() helper instead of duplicating
  the SELECT inline; drop the redundant try/catch.
- Validate the secret name against the SecretName enum before doing
  the auth-method DB query so bogus probes fail fast.
- Switch the enum membership check from hasOwnProperty.call to the
  more idiomatic in operator.
- Tighten the function-header comment to a single WHY line.
- Drop the dead else-branch from the test helper.

* [AI] Move response building back into the secrets handlers

Reshape the helper as canManageSecrets(userId) - a pure predicate over
the user. Each handler now owns its own 403 response so request/response
plumbing stays inside the route handlers.

* [AI] Undo testSecretName -> validSecretName rename

Reuse the original testSecretName / testSecretValue constants; only
the value of testSecretName changes to a real SecretName so the GET
handler's enum check accepts it.

* [AI] Add release note for #7862

* [AI] Validate POST secret name and tighten GET auth ordering

- POST /secret/ now rejects names not in the SecretName enum with 400.
- GET /secret/:name runs the admin check before the enum check so
  non-admins in OpenID mode get a uniform 403 regardless of whether
  the requested name is valid.
- Add tests for both: admin POST success in OpenID mode, and POST 400
  for unknown secret names.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-16 20:59:31 +00:00
Matt Fiddaman
36e5cb17f5 fix healthcheck script (#7840)
* fix healthcheck script

* note

* test release docker image
2026-05-14 21:39:10 +00:00
Matiss Janis Aboltins
2c9e0af3e4 Fix OpenID auth test flakiness (#7847)
* [AI] Fix flaky openid /config test from cross-worker auth race

Vitest runs sync-server test files in parallel workers that share
account.sqlite. Other files (e.g. app-account.test.js) insert 'openid'
auth rows, and auth.method is a PRIMARY KEY, so a concurrent INSERT in
app-openid.test.ts can hit UNIQUE constraint failed: auth.method.

Use INSERT OR REPLACE in the helper and clear the auth table in
beforeEach for a clean start.

* Add release notes for PR #7847

* Change category from Bugfixes to Maintenance

Fix OpenID authentication test flakiness by ensuring test isolation with INSERT OR REPLACE.

* [AI] Disable file parallelism for sync-server tests

The previous fix only patched insertOpenIdAuth in app-openid.test.ts,
but app-account.test.js's insertAuthRow helper also does plain
INSERT INTO auth ... 'openid' ... (lines 197, 203, 210, 229, 245).
With maxWorkers: 2 and a shared account.sqlite, either file's INSERT
can race the other's and hit UNIQUE constraint failed: auth.method.

Disable cross-file parallelism so test files run sequentially against
the shared DB. Within-file tests still run sequentially by default.
Test suite goes from ~20s to ~36s; trades some speed for stability.

* [AI] Revert openid test changes, reword release note

The fileParallelism: false change in vitest.config.ts already prevents
the auth.method UNIQUE-constraint race across files, so the INSERT OR
REPLACE and extra beforeEach cleanup in app-openid.test.ts are no longer
needed. Revert that file back to its original state and reword the
release note to describe the actual fix.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-05-14 21:30:33 +00:00
Matt Fiddaman
e04924810d clean up GoCardless bank factory loading process (#7809)
* remove TLA in bank-factory

* note
2026-05-14 21:04:20 +00:00
Matiss Janis Aboltins
2c7f3c7a3d [AI] Replace google-protobuf with @bufbuild/protobuf (#7535)
* [AI] crdt: typecheck test files and clean up lint issues

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Replace google-protobuf with @bufbuild/protobuf

Swap the google-protobuf + ts-protoc-gen + protoc-gen-js toolchain for
@bufbuild/protobuf + @bufbuild/protoc-gen-es. The generator now emits a
single pure-TS sync_pb.ts (no .js sidecar, no globalThis.proto hack)
and a thin wrapper in proto/compat.ts preserves the SyncProtoBuf /
SyncRequest / etc. API so call sites stay unchanged. Removes the
loot-core CommonJS require polyfill that only existed to service
google-protobuf.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Align @bufbuild/protobuf version ranges with installed

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] crdt: drop the SyncProtoBuf compat layer

The proto/compat.ts wrapper was introduced alongside the bufbuild
migration to avoid touching call sites. With bufbuild messages already
exposing fields as plain mutable properties, the wrapper was just
boilerplate hiding direct reads and writes — and it had drifted (e.g.
setMessagesList was called in a test but never defined).

Delete compat.ts and migrate the six call sites in loot-core and
sync-server to use @bufbuild/protobuf directly. The crdt package now
re-exports the sync_pb types/schemas and the three bufbuild runtime
helpers (create, fromBinary, toBinary) so consumers keep a single
import source.

Also switch sync-server's @actual-app/crdt dependency from the pinned
"2.1.0" to "workspace:*", matching api/loot-core — the npm pin was
pulling the stale published copy instead of the workspace source.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] CI: drive sync-server build through lage so crdt deps are built

Before: the server job ran `yarn workspace @actual-app/sync-server build`
directly, which invokes tsgo without first emitting the workspace
dependencies' declarations. That worked when sync-server pinned crdt to
the published npm version (declarations bundled in the tarball), but
with `workspace:*` it fails with TS6305 because packages/crdt/dist/*.d.ts
hasn't been built yet.

Switch the CI command to `yarn build --to=@actual-app/sync-server`.
Lage respects the `dependsOn: ['^build']` pipeline and builds
@actual-app/crdt (and the other transitive deps) before sync-server.

Using --to rather than --scope keeps the build set minimal; --scope
would also include dependents like desktop-electron.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] sync-server: build project references via tsgo -b

The build script ran plain `tsgo`, which doesn't compile referenced
projects. With @actual-app/crdt now a `workspace:*` dep (no bundled
declarations from the npm tarball), the sync-server build fails with
TS6305 because packages/crdt/dist/index.d.ts doesn't exist yet.

Switch to `tsgo -b` so the sync-server build is self-contained: it
emits crdt's declarations into packages/crdt/dist on demand. This
mirrors what the sync-server `typecheck` script already does and fixes
all callers (`build:server`, docker-edge, publish workflows, the
direct `yarn workspace @actual-app/sync-server build` invocation in
build.yml) without needing per-workflow lage orchestration.

Revert the build.yml workaround added in the previous commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] sync-server: build @actual-app/crdt before tsgo

The previous tsgo -b approach emitted crdt's .d.ts via the project
reference but never produced dist/index.js — tsgo respects crdt's
tsconfig which has emitDeclarationOnly: true, and the actual JS
runtime is emitted by Vite in crdt's build script. So sync-server
compiled cleanly but crashed at runtime when forked by desktop-electron
(require('@actual-app/crdt') resolved to a package whose main pointed
at a nonexistent file, surfaced in e2e as the onboarding screen never
leaving the "Configure your server" state).

Unlike packages/api (which uses Vite with noExternal: true and bundles
crdt's source inline), sync-server uses plain tsgo compilation and
keeps its deps external — so crdt must be built ahead of time and be
resolvable via node_modules at runtime.

Chain `yarn workspace @actual-app/crdt build` before tsgo so every
caller of sync-server's build (build:server, docker-edge, publish
workflows, direct invocations in CI) gets a complete crdt dist. Revert
tsgo -b back to plain tsgo since crdt's build step now emits both the
JS and the declarations.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] crdt: expose dist/ via conditional exports so Node can load it

The package's `exports` field pointed straight at `./src/index.ts`,
which works for TS tooling and bundlers (vite with noExternal, vitest)
but breaks at plain-Node runtime — Node can't execute `.ts` files and
resolves dependent `./crdt` as a directory import, failing with
ERR_UNSUPPORTED_DIR_IMPORT.

That was invisible before because sync-server pinned
`@actual-app/crdt@2.1.0` and ran against the published npm tarball
(whose `publishConfig.exports` had already been promoted to the main
`exports` by yarn pack). Switching sync-server to `workspace:*` made
the raw workspace exports win at runtime: the compiled server imported
crdt when desktop-electron forked it, Node hit the `.ts` entry, the
utility process crashed before emitting `server-started`, and the
onboarding flow stalled on "Configure your server".

Switch to the same conditional-exports pattern packages/api already
uses: types → dist/index.d.ts, development → src/index.ts (for vitest
runs that enable the `development` condition), default → dist/index.js
(Node runtime and any other consumer). `publishConfig.exports` still
collapses this to just types + default for the npm tarball.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] crdt: split exports per consumer (browser source, node dist)

Previous commit's conditional exports routed everything non-development
to ./dist/index.js. That broke the web build: rolldown runs with
conditions ['electron-renderer', 'module', 'browser', 'default'] — no
match for development, falls through to the dist entry, which isn't
built by bin/package-browser, and fails to resolve @actual-app/crdt
when bundling loot-core's server/undo.ts.

Split the entries so each consumer lands on the right artifact:

  types       → ./dist/index.d.ts   (TypeScript, project references)
  development → ./src/index.ts      (vitest — both configs include it)
  browser     → ./src/index.ts      (web rolldown bundles the source)
  node        → ./dist/index.js     (sync-server forked by Node at
                                     runtime — the failure that kicked
                                     off this whole saga)
  default     → ./src/index.ts      (fallback for bundlers like api's
                                     vite build with conditions=['api'])

Verified: node resolves to dist, yarn build:browser succeeds from a
clean crdt/, sync-server build produces both dist/index.js and
build/app.js, loot-core (552) + sync-server (386) tests pass, full
typecheck clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] address review feedback on crdt/sync-server

- generate-proto: add `set -euo pipefail` so a protoc failure exits the
  script non-zero instead of silently running oxfmt on whatever is in
  src/proto/ from the previous run.
- sync.proto SyncRequest: field numbers jumped from 3 to 5; declare
  `reserved 4;` so the slot can't be silently reused for a new field
  with an incompatible type. Regenerated sync_pb.ts — the reservation
  shows up in the encoded file descriptor.
- sync-simple.js: SQLite stores is_encrypted as a 0/1 integer and
  better-sqlite3 hands it back as a number, but the bufbuild
  MessageEnvelope schema types isEncrypted as bool. Coerce to boolean
  when constructing the envelope so the JS value matches the field
  type before toBinary runs.

Skipped the suggested `types` → ./src/index.ts swap in crdt's exports:
packages/api uses the same `types` → dist pattern and TypeScript's
bundler resolution already falls through when dist/*.d.ts doesn't yet
exist (verified — loot-core typecheck passes with packages/crdt/dist
removed).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] address review feedback on encoder/app-sync test

- encoder.ts: prefs.getPrefs().encryptKeyId is `string | undefined`
  (MetadataPrefs is a Partial<>). The bufbuild SyncRequestSchema's
  keyId field is a non-optional proto3 string. Current code worked by
  accident — passing undefined into `create(Schema, init)` falls back
  to the schema default '' — but relied on bufbuild's undef-handling
  and would break if someone dropped @ts-strict-ignore. Normalize to
  '' explicitly.
- app-sync.test.ts: add a short WHY comment next to
  `syncRequest.since = ''` in "returns 422 if since is not provided".
  The test's intent (missing since) only matches the handler's
  `requestPb.since || null` falsy-check because proto3 strips '' on
  the wire and decodes it back to ''. Not obvious without the comment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] crdt: load source directly in dev, only use dist when published

Local exports point at src/index.ts so consumers (sync-server in
particular) never load a stale Vite bundle. publishConfig keeps the
dist/ mapping for npm consumers. Switched the Vite output to ESM and
added "type": "module" so the published bundle stays consistent.

Sync-server's existing extension-resolution loader is extended to
handle directory imports and is now registered at runtime via
--import ./register-loader.mjs, matching how tests already load it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] desktop-electron: register sync-server loader on the embedded fork

The Electron app starts the sync server via utilityProcess.fork, which
bypasses sync-server's `start` script. With crdt now loaded from
source, the fork needs the same `--import register-loader.mjs` that
the standalone server uses; otherwise it crashes on the extensionless
`from './crdt'` directory import. Adds the loader files to
sync-server's published `files` so they actually ship with the
packaged app.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] sync-server: bootstrap entry that registers the loader for utilityProcess

Electron's utilityProcess.fork accepts execArgv but silently ignores
--import (verified with a minimal repro: the flag shows up in
process.execArgv but the preload module never executes), so the
previous attempt was a no-op and the embedded sync-server still
crashed on crdt's ESM directory imports. Add packages/sync-server/start.mjs
that statically imports register-loader.mjs and then dynamic-imports
build/app.js, so the loader is in place before the app's module graph
resolves. desktop-electron now points utilityProcess.fork at start.mjs
and drops the ineffective --import flag.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 17:55:52 +00:00
Matiss Janis Aboltins
50feba1afb [AI] Fix flaky API test timeouts and use sync file write in tests (#7806)
* [AI] Fix flaky upload-user-file test

The "uploads and updates an existing file successfully" test wrote the
old file content using the async callback form of fs.writeFile without
awaiting it. That write could land after the upload endpoint had already
written the new content, leaving the file with stale content and failing
the assertion. Use fs.writeFileSync so the setup completes before the
request is sent.

* [AI] Increase api test timeouts to fix flaky budget-load test

methods.test.ts loads a budget file and runs all DB migrations in each
test/hook. On busy CI runners this regularly approaches the default 5s
limit, and when it exceeds it the in-flight loadBudget keeps running after
teardown closes the database, producing a cascade of unhandled rejections
("database connection is not open", "no such table: v_schedules",
"Cannot read properties of undefined (reading 'timestamp')") that fail the
suite. Bump testTimeout/hookTimeout to 20s for the api package.

* [AI] Add release note for flaky test fixes

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-11 22:07:25 +00:00
Matiss Janis Aboltins
a95c0ad9b0 [AI] sync server changes; crdt & et al (#7702)
* [AI] Load @actual-app/crdt from source in dev, only bundle for publish

@actual-app/crdt's local exports now point at src/index.ts so consumers
(sync-server, loot-core, desktop-client) never see a stale Vite bundle.
publishConfig keeps the dist/ mapping for npm consumers. crdt's
tsconfig switches to bundler module resolution to match the rest of
the workspace (no extensions in source imports).

Sync-server's existing extension-resolution loader is extended to also
handle directory-index imports (./crdt → ./crdt/index.ts), and the
standalone `start` / `start-monitor` scripts now invoke Node with
--import ./register-loader.mjs so the loader is in place before crdt's
source resolves.

Electron's utilityProcess.fork accepts execArgv but doesn't actually
preload --import modules, so a new packages/sync-server/start.mjs
bootstrap entry registers the loader imperatively and then dynamic-
imports build/app.js. desktop-electron's startSyncServer() points the
fork at start.mjs. sync-server's "files" array now ships start.mjs,
register-loader.mjs and loader.mjs so packaged Electron / npm
consumers actually receive them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Add release notes for PR #7702

* [AI] Restructure sync-server to build with Vite

Replace the hand-rolled tsgo + add-import-extensions + copy-static-assets
+ runtime loader pipeline with a single Vite SSR build. Bundles every
entry (app, bin/actual-server, scripts/*) and inlines @actual-app/crdt
source so Node never has to resolve TS at runtime — the
MODULE_TYPELESS_PACKAGE_JSON warning that surfaced via crdt's source
exports is gone. Migrations and bank handlers move from readdir-based
dynamic imports to import.meta.glob; messages.sql becomes a ?raw import.

Drop loader.mjs, register-loader.mjs, start.mjs, and
bin/add-import-extensions.mjs. Electron's startSyncServer() forks
build/app.js directly. publishConfig.imports goes away (subpath imports
are resolved at build time and don't appear in the bundle).

In dev (start:server-dev) sync-server proxies to Vite, so loosen the CSP
to allow Vite's inline preamble script and HMR websocket — production
CSP is unchanged. desktop-client skips registerSW() in dev (and disables
vite-plugin-pwa's devOptions) so stale cached assets don't override
edits between page loads.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Address review feedback

- sync-server CSP: drop 'unsafe-eval' from the production script-src;
  the bundle has no genuine eval/new Function usage (only a defensive
  branch in setimmediate's polyfill that's never hit). Keep it on the
  dev branch where Vite's HMR runtime relies on it. Add a comment so
  it's obvious which branch needs it and why.
- bank-factory: widen the loader glob to ./banks/*_*.{ts,js} so
  TypeScript handlers are discovered too, mirroring migrations.ts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Restore 'unsafe-eval' in production CSP for Electron

The Electron app needs `'unsafe-eval'` at runtime, so revert the dev-only
restriction and keep `'unsafe-eval'` in both branches. Comment updated to
record the actual reason instead of marking it as removable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Revert bank-factory glob change

Widening the glob to ./banks/*_*.{ts,js} broke the desktop e2e tests in
CI even though every current handler is .js and the brace expansion
matches no .ts files locally. Reverting to ./banks/*_*.js — the change
had no behavioural benefit since there are no TS handlers, so the
nitpick isn't worth chasing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Strip CSP comment to restore identical state to 9513c1e16

The desktop e2e has been failing despite my prior commits being a strict
revert (only difference was a 2-line comment, which can't change runtime).
Removing even the comment so the branch matches 9513c1e16's relevant
files exactly, to isolate whether the failure is from the master merge
or from CI-environment drift.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Make rebuild-electron actually rebuild better-sqlite3

PR #7712 simplified rebuild-electron to just `electron-rebuild -f -o
better-sqlite3,bcrypt` from the repo root. Two problems with that:

  1. Without `-m`, electron-rebuild scans the root workspace's package.json
     for native deps. better-sqlite3 isn't a direct root dep — it lives
     under packages/sync-server/ — so the scan returns no candidates and
     the rebuild silently no-ops.
  2. Without --build-from-source, electron-rebuild defers to
     prebuild-install, which downloads a stale prebuilt binary keyed off
     better-sqlite3's package.json (ABI 127) instead of recompiling
     against Electron 39's bundled Node ABI 140. The download succeeds
     and "Rebuild Complete" prints, but the resulting `better_sqlite3.node`
     can't `dlopen` inside Electron's utility process — sync-server
     crashes immediately on db init, the renderer's startSyncServer IPC
     never resolves, and the e2e test hangs on "Configure your server".

Point -m at packages/desktop-electron (which transitively pulls in
better-sqlite3 and bcrypt via @actual-app/sync-server) and force a real
compile via --build-from-source. Verified locally: better-sqlite3
rebuilds to darwin-arm64-140 and the desktop e2e onboarding test passes
in 6s instead of hanging for 60s.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Restore CSP unsafe-eval comment

Bring back the explanatory comment that was stripped diagnostically in
99682268c. Now that the desktop e2e regression is traced to
rebuild-electron and not to anything in this branch, we can keep the
documentation noting why 'unsafe-eval' is retained in both CSP branches.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Restore bank-factory glob to ./banks/*_*.{ts,js}

Re-apply the glob widening originally added in 145868f9d. It was
reverted in 531b1a191 because the desktop e2e was failing — that
failure is now traced to the rebuild-electron breakage (fixed in
6e8ac0784), not to this glob. Mirroring migrations.ts so future TS
bank handlers are picked up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Fix applyAppUpdate hanging in dev mode

In dev mode browser-preload's updateSW was () => undefined, so
applyAppUpdate() — which calls updateSW() and then awaits a
deliberately never-resolving promise (waiting for the SW-driven page
reload) — hung the renderer instead of refreshing. In prod the page
is replaced by the new service worker, so the never-resolving await is
fine. The dev path now triggers a plain window.location.reload() so
the page reloads and the never-settling await is irrelevant, matching
prod's effective behaviour.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Revert rebuild-electron to master version

* Revert "[AI] Revert rebuild-electron to master version"

This reverts commit 4b6baab79f.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com>
2026-05-11 17:40:42 +00:00
Aurel Demiri
0fd510a1d4 Integrate Enable Banking as a bank sync provider (#7345)
* Integrate Enable Banking as bank sync provider

Rewrite Enable Banking modal to match GoCardless pattern

Resolve Enable Banking bugs and improve auth flow

* [AI] Address code review feedback for Enable Banking integration

Bug fixes:
- Fix double-negative for DBIT transaction amounts (e.g. '--25.99')
- Fix payeeName counterparty mapping (CRDT→debtor, DBIT→creditor)
- Add missing state validation in EnableBankingCallback and /auth_callback
- Fix stuck loading state in useEnableBankingStatus with try/catch/finally
- Make session-expiry error matching case-insensitive
- Prefer CLAV balance type for startingBalance in /transactions route
- Guard setTimeout in post/del/patch when timeout is null
- Distinguish abort from network failure in post() catch

Credential handling:
- Add validateCredentials() to validate before persisting secrets
- Refactor client to use enablebanking-configure instead of manual secret-set
- Distinguish null (loading) from false (not configured) in setup checks

Poll-auth robustness:
- Add unique waiter IDs to prevent superseded waiter cleanup race
- Always cache results in completedAuths for retry resilience
- Add client disconnect cleanup via res.on('close')
- Cancel poll when Enable Banking modal closes via AbortController
- Prevent concurrent poll controller race with local reference check

Code quality:
- Extract buildSessionResult() to deduplicate auth_callback/complete-auth
- Add enabled parameter to useEnableBankingStatus to skip unused requests
- Add re-entrancy guard on onJump, reset bank on country change
- Refetch bank list after Enable Banking setup completes
- Type enableBankingConfigure config, make state required in completeAuth
- Add AbortError→TIMED_OUT test, fix startAuth test assertion
- Add afterAll vi.unstubAllGlobals() for test cleanup
- Add explanatory comments for bank-per-account model and in-memory maps

* [AI] Fix missing patterns in Enable Banking integration

- Add SyncServerEnableBankingAccount to ExternalAccount union and
  getInstitutionName parameter type in SelectLinkedAccountsModal
- Use BankSyncProviders type in mobile BankSyncAccountsList instead of
  hardcoded union missing enableBanking
- Add getSecretsError handling to EnableBankingInitialiseModal for
  proper auth/permission error messages
- Replace hardcoded #666 color with theme.pageTextSubdued
- Wrap onConnectEnableBanking in try/catch with error notification and
  init modal re-open, matching SimpleFin/PluggyAI pattern
- Translate hardcoded error string in enablebanking.ts
- Add 60s timeout to downloadEnableBankingTransactions matching PluggyAI
- Revert out-of-scope changes to del()/patch() in post.ts
- Revert shared starting balance dedup logic back to master pattern

* Forward PSU headers to Enable Banking API

* Fix Enable Banking re-auth dispatch

* Respect ASPSP maximum_consent_validity when starting Enable Banking auth

* Fix missing types for module jws

* Add upcoming release notes

* Fix format

Expected "sign" (value-import) to come before "Algorithm"

* Fix code review findings on Enable Banking integration

* [AI] Disable Enable Banking button while status is loading

* typo

* [AI] Migrate enable-banking files to subpath imports

Update all enable-banking files to use # subpath imports and
@actual-app/core paths, matching the migration done in master.
Add #enablebanking entry to desktop-client package.json imports map.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [AI] Add #app-enablebanking subpath imports to sync-server package.json

Register enablebanking service, utils, and root entries in both
the imports and publishConfig.imports maps.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add jws to dependencies

* [AI] Harden Enable Banking OAuth callback handoff

Enforce exact OAuth state round-trip in the Enable Banking callback so
mismatched/missing state values no longer silently complete the flow.
Replace unsafe `as`/`!` assertions in the auth handoff with typed
locals so the callback path stays sound under strict TypeScript.

* [AI] Tighten Enable Banking type safety

Make the Enable Banking external-msg modal strict-ts compatible,
annotate the id type in linkEnableBankingAccount, derive
AccountSyncSource from a single SYNC_PROVIDERS list, and annotate the
return type of getJWTBody. No behaviour change.

* [AI] Fix Enable Banking poll lifecycle and abort handling

Make the popup-driven auth poll cancellable and isolated:

- Allow the popup retry path to abort the in-flight poll instead of
  leaving it hanging on the previous attempt.
- Clear the Enable Banking stateRef when the retry attempt finishes so
  a new attempt starts from a clean state.
- Start useEnableBankingStatus in loading state until the first fetch
  resolves so the UI doesn't briefly flash "not connected".
- Cancel only the requested poll, not every in-flight Enable Banking
  poll, so unrelated link attempts aren't affected.
- Skip writing the poll response when the client has already
  disconnected, with a regression test covering the disconnect path.

* [AI] Tighten Enable Banking client/test plumbing

Misc code-quality improvements with no behaviour change:

- Parallelize Enable Banking secret reset calls so wiping multiple
  secrets doesn't serialize the request chain.
- Use absolute imports in the enable-banking client module to match the
  rest of desktop-client.
- Document externalSignal usage in the post helper.
- Tighten Enable Banking test fixtures with `satisfies` and dynamic
  dates so they stop drifting when the real "now" moves.

* [AI] Fix Enable Banking initial-balance and post-link bookkeeping

Apply the standard post-sync bookkeeping when linking an Enable Banking
account so the new account picks up the same starting-balance
treatment as other bank-sync providers, and skip pending transactions
when computing the initial balance so the figure isn't inflated by
transactions that haven't cleared yet.

* [AI] Refine Enable Banking error model and bank-sync surface

Carry the human-readable Enable Banking message in
EnableBankingError.error_type and the machine-friendly identifier in
error_code, then map error_code to a bank-sync category in the
/transactions wire format so AccountSyncCheck can match on the same
categories as other providers.

* [AI] Improve Enable Banking bank-sync field mapping

Bring the Enable Banking transaction normalizer in line with how other
bank-sync providers feed the field mapper:

- Strip SEPA structured prefixes from remittance text so notes/payee
  display the human-meaningful portion instead of the SEPA boilerplate.
- Return the notes field and spread the raw transaction so downstream
  field mapping can reach the full payload.
- Expose Enable Banking raw fields in the bank-sync field mapper UI so
  users can map any underlying property, not just the curated subset.

* [AI] Use req.ip for Enable Banking PSU header so trust-proxy whitelist applies

* [AI] Address Enable Banking CodeRabbit pass-3 follow-ups

Three small fixes from the latest CodeRabbit re-review:

- Guard the aspsps fetch in EnableBankingExternalMsgModal against stale
  responses. Switching countries quickly could let an earlier in-flight
  request overwrite the newer selection's bank list. Added a cleanup
  flag in the useEffect so only the latest response updates state.
- Clear `enablebanking_auth_state` from localStorage when the auth flow
  exits, but only if the stored value still matches this attempt's
  state, so a concurrent retry can't wipe a newer session. Wrapping
  the poll in try/finally covers every return path (success, timeout,
  abort, body-level error).
- Use `Boolean(trans.booked)` in the Enable Banking initial-balance
  predicate to match `normalizeBankSyncTransactions`. The Enable
  Banking normalizer always sets `booked` to a boolean today, so this
  is defensive rather than a live bug, but keeping the two predicates
  aligned avoids surprises if the upstream shape ever loosens.

* [AI] Address Enable Banking CodeRabbit pass-3 follow-ups (round 2)

Two more findings from the latest CodeRabbit pass:

- Guard onJump against stale-retry completions. Token each call with a
  monotonic jumpIdRef counter and gate every post-await write
  (setError/setWaiting after onMoveExternal, the second setWaiting,
  and the finally-block ref reset) on `myJumpId === jumpIdRef.current`.
  Without this, a retry click while the previous poll was still
  unwinding could surface the older call's error in the newer
  attempt's UI and clear stateRef/isJumpingRef out from under it,
  leaving the new poll un-cancellable.
- Translate the (beta) suffix on Enable Banking ASPSP names so
  non-English locales don't surface a hardcoded English token in the
  bank list. The existing `actual/no-untranslated-strings` rule misses
  this case (regex requires a leading uppercase, and template-literal
  interpolations aren't visited as standalone strings).

* [AI] Use SEPA prefix allowlist instead of catch-all regex

The previous `^[A-Z]{3,}\+` regex would incorrectly strip merchant
tokens like `BMW+`, `USB+`, or `COVID+` from the start of a remittance
line. Replaced it with an explicit allowlist of known SEPA / ISO 20022
prefixes and added a regression test covering the false-positive case.

* [AI] Use uuidv4 instead of crypto.randomUUID in Enable Banking

Aligns with master's revert in #7734 (crypto.randomUUID back to uuid
library). Two stray spots remained in Enable Banking code: the
link-account flow in loot-core/server/accounts/app.ts and the OAuth
state token in sync-server/app-enablebanking.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-11 14:05:30 +00:00
Matiss Janis Aboltins
18c704b3ba [AI] Sync server: harden CORS proxy method validation (#7788)
* [AI] Sync server: harden CORS proxy method validation

The CORS proxy validated `method` against a fallback-normalized value but
forwarded the raw client-supplied value to fetch(), letting a non-string
input (e.g. ["POST"]) bypass the GET/HEAD allowlist via undici's String()
coercion. Reject non-string method, pass the validated normalized method
to fetch(), and drop the unreachable body-forwarding branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Polish release notes wording

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Rename 7787.md to 7788.md

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 16:11:40 +00:00
dependabot[bot]
db38565524 Bump uuid from 13.0.2 to 14.0.0 (#7739)
* Bump uuid from 13.0.2 to 14.0.0

Bumps [uuid](https://github.com/uuidjs/uuid) from 13.0.2 to 14.0.0.
- [Release notes](https://github.com/uuidjs/uuid/releases)
- [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/uuidjs/uuid/compare/v13.0.2...v14.0.0)

---
updated-dependencies:
- dependency-name: uuid
  dependency-version: 14.0.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* [AI] Add engines field to sync-server package.json for Node.js >=22 requirement

Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com>
2026-05-08 20:40:07 +00:00
Matiss Janis Aboltins
345c99be4d [AI] Bump workspace packages to 26.5.2 and document 26.5.2 in release notes (#7756) 2026-05-08 19:30:10 +00:00
Matiss Janis Aboltins
bc08ed97e9 [AI] 🔖 Release 26.5.1 (#7745) 2026-05-08 17:11:46 +00:00
Matiss Janis Aboltins
852b95524b [AI] Revert crypto.randomUUID back to uuid library (#7734)
* [AI] Revert crypto.randomUUID back to uuid library

Partial revert of #7529. Restores the `uuid` package dependency in
api, crdt, desktop-client, loot-core, and sync-server, swapping every
`crypto.randomUUID()` call introduced by that PR back to `uuidv4()`
(and the `uuid()` alias in RuleEditor.tsx and ruleUtils.ts where it
was previously used). The lint rule, docs entry, and `vi.mock('uuid')`
test setup are restored as well. The `fs-extra` removals in
desktop-electron from the same PR are left in place.

https://claude.ai/code/session_01KTg1g416Jdjf5feGke8MQw

* Add release notes for PR #7734

* [check-spelling] Update metadata

Update for https://github.com/actualbudget/actual/actions/runs/25480733101/attempts/1
Accepted in https://github.com/actualbudget/actual/pull/7734#issuecomment-4394811498

Signed-off-by: check-spelling-bot <check-spelling-bot@users.noreply.github.com>
on-behalf-of: @check-spelling <check-spelling-bot@check-spelling.dev>

* [AI] Use the uuidv4 alias in RuleEditor and ruleUtils

The previous commit preserved the `v4 as uuid` alias in these two files
to match their pre-#7529 state, but the project convention (and lint
rule message) is `v4 as uuidv4`. CodeRabbit flagged the inconsistency,
so normalize the alias and call sites in both files.

https://claude.ai/code/session_01KTg1g416Jdjf5feGke8MQw

* [AI] Pin uuid to ^11.1.0 to fix Electron e2e

uuid v13 is ESM-only (no CJS entry, only an exports map with `node` and
`default` conditions both pointing to ESM files). The Electron backend is
bundled as CJS by `loot-core/vite.desktop.config.mts` and loaded via a
dynamic `await import(process.env.lootCoreScript!)` in the
desktop-electron utilityProcess; that pipeline appears to fall over on
the ESM-to-CJS transform of uuid v13 in Vite 8 / rolldown, which makes
the Functional Desktop App e2e job fail consistently while every other
check (web e2e, VRT, unit tests, lint, typecheck) passes.

uuid v11 still ships `dist/cjs/index.js`, so pinning each workspace's
uuid range to ^11.1.0 sidesteps the resolution path entirely. The API
is unchanged (`v4` is still the same export), so no source-code changes
are needed.

https://claude.ai/code/session_01KTg1g416Jdjf5feGke8MQw

* [AI] Capture Electron stdout/stderr in desktop e2e fixture (TEMP DEBUG)

Pipe the Electron main+utility process stdout/stderr into both the
playwright runner stderr and an electron.log file inside the test's
output directory. This makes the actual backend error visible in the
Functional Desktop App job output and in the desktop-app-test-results
artifact.

Will be reverted once the failure cause is identified.

https://claude.ai/code/session_01KTg1g416Jdjf5feGke8MQw

* Revert "[AI] Capture Electron stdout/stderr in desktop e2e fixture (TEMP DEBUG)"

This reverts commit 4cb5148859.

* [AI] Fix rebuild-electron and bump uuid back to ^13.0.0

The Functional Desktop App e2e job had been failing with
ERR_DLOPEN_FAILED on better-sqlite3 (NODE_MODULE_VERSION 127 vs 140),
which surfaced because this PR's yarn.lock change invalidated the
GitHub Actions node_modules cache. With a fresh install,
rebuild-electron is responsible for compiling better-sqlite3 and
bcrypt against Electron's ABI — but since #7712 the script has been
running from the repo root, where neither module is a direct
dependency, so electron-rebuild silently exits as a no-op.

Restore the `-m ./packages/desktop-electron` scoping that #7712
removed, so the rebuild actually finds and rebuilds the modules.
This fix is technically out of scope for this PR but it blocks any
PR that touches yarn.lock.

Also revert the `^11.1.0` pin from the previous commit back to
`^13.0.0`. The pin was based on a wrong hypothesis (that uuid v13's
ESM-only packaging was breaking the bundle); now that the real
cause is identified, there is no reason to deviate from the
pre-#7529 version.

https://claude.ai/code/session_01KTg1g416Jdjf5feGke8MQw

---------

Signed-off-by: check-spelling-bot <check-spelling-bot@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com>
2026-05-07 19:18:47 +00:00
Dan Hopkins
d787d0ce43 fix: only count failed attempts against auth rate limit (#7707)
* fix: only count failed attempts against auth rate limit

Add skipSuccessfulRequests: true to authRateLimiter so that successful
logins do not consume quota. This fixes breakage for API clients
(actual-cli, actual-mcp, custom scripts) that re-authenticate per
operation — they always provide the correct password, so they should
never be rate-limited.

Brute-force attackers generate repeated failures and still hit the wall.

Fixes #7706

* Update upcoming-release-notes/7706.md

Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>

* fix: rename release note to match PR number

---------

Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>
2026-05-04 22:27:18 +00:00
Matiss Janis Aboltins
6c2c96e826 🔖 (26.5.0) (#7621)
* 🔖 (26.5.0)

* fix release note generation script (#7635)

* fix release note generation script

* note

* fix cherrypicked commits not being respected and lint race in release note generation workflow (#7640)

* fix cherrypicked commits not being respected and lint race

* note

* coderabbit suggestions

* fix lint

* make double restore possibility safe

* fix lint (#7643)

* Generate release notes for v26.5.0

* add release note highlights

* Fix Sankey income bug, when payee it not set (#7632)

* Ensure income categories are shown correct, even if payee is not set

* Add release note

* Generate release notes for v26.5.0

* increase test coverage for budget templates (#7620)

* [AI] cover existing template engine logic with regression tests

Adds tests for goal template behavior that predates this PR so the
suite can be cherry-picked onto master to confirm no regressions. No
production code changes.

Covers:
- init() validation: schedule names, by/schedule priority match, past
  by-target with and without annual/repeat, percentage source not
  found, special source aliases, duplicate limit/spend/goal
  directives, weekly limit missing start date, invalid limit period,
  unrecognized periodic period
- runRemainder cap clamping and hideDecimal fraction removal
- Income-category branch in runTemplatesForPriority
- getLimitExcess against an aggregate weekly cap
- Past by-target rolling forward via the annual period
- runSchedule full=true (no sinking accumulation), percent and fixed
  adjustments, completed-schedule filtering, past-date error for
  non-repeating schedules, monthly/weekly/daily sinking contribution
  branches when interval exceeds the pay-month-of cap, surplus
  absorption when last-month balance exceeds the target, and
  tracking-budget mode forcing all schedules pay-month-of
- applyMultipleCategoryTemplates orchestration: per-category writes,
  cross-category priority clamping when funds run out, error
  notification path
- applyTemplate force=false skipping already-budgeted categories

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* note

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix infinite loop when remainder is impossible to solve (#7623)

* fix infinite loop when remainder is impossible to solve

* note

* Generate release notes for v26.5.0

* Update author

Updated author information in the release notes.

* Fix shared worker resumption after tab suspend (#7656)

* [AI] Fix SharedWorker tab resume recovery

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* [AI] Fix SharedWorker reload readiness

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add release notes

* Update packages/desktop-client/src/shared-browser-server-core.ts

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>

* Update docs release date

* Empty commit to bump CI

* Generate release notes for v26.5.0

* Revert "Generate release notes for v26.5.0"

This reverts commit b42c48bed5.

---------

Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com>
Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Emil Tveden Bjerglund <emilbp@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Julian Dominguez-Schatz <julian.dominguezschatz@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-03 17:41:17 +00:00
Julian Dominguez-Schatz
da1d0a94b9 Migrate file service to TypeScript (#7606)
* Migrate file service to TypeScript

* Add release notes

* Rabbit

* Stricter types
2026-04-24 22:50:43 +00:00
Julian Dominguez-Schatz
227c995155 Disallow reconfiguring OpenID after initialization (#7608)
* Disallow reconfiguring OpenID after initialization

* Add release notes
2026-04-24 15:12:01 +00:00
Matiss Janis Aboltins
7501674613 [AI] Fix Docker build for workspace:* dependencies (#7564)
* [AI] Fix Docker build for workspace:* dependencies

Since @actual-app/crdt became a workspace:* dep, `yarn workspaces focus
--production` creates relative symlinks in node_modules that dangle when
only node_modules is copied into the prod image, breaking local Docker
builds with ERR_MODULE_NOT_FOUND: @actual-app/crdt.

Dereference yarn's workspace symlinks in the builder stage with `cp -RL`
so the prod stage can copy a self-contained node_modules without needing
to enumerate which workspace:* deps exist. Adding a new workspace:* dep
now requires zero Dockerfile changes.

Also move the sync-server .dockerignore to the repo root (and drop stray
local node_modules / .git / .yarn caches from the build context), since
docker builds use the repo root as context — the old sync-server-level
file was no longer being applied.

Fixes #7561.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Strip dev-only dirs from dereferenced workspace packages

The generic `cp -RL` step copies full workspace package trees into the
image (src/, e2e/, tests, build-stats, etc.). Remove them after the
dereference — they're not needed at runtime, and skipping them recovers
~67MB from the final image on both alpine and ubuntu variants.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Rephrase 7564 release note to be user-facing

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 19:20:51 +00:00