Files
actual/.coderabbit.yaml
Matt Fiddaman 3b9e79ed5e bring release branch up to date (#8333)
* 🔧 Prevent release branch workflow from running on forks (#7949)

* prevent release branch worfklow from running on forks

* release notes

* automation UI: add per-category notes and tooltip (#7906)

* add per-automation note

* add tooltip to automation button

* note

* tweak placeholder

* popover placement

* tooltip styling

* [AI] Fix split double-counting in balance forecast (#7955)

* [AI] Fix split double-counting in balance forecast

* [AI] Add release note for forecast split fix

* [AI] Move UserDirectoryPage to admin directory (#7951)

* [AI] Fix ineffective dynamic import of responsive/wide

UserDirectoryPage was statically imported from ./responsive/wide in
FinancesApp.tsx, which prevented the bundler from splitting the wide
chunk into its own bundle (since responsive/index.tsx dynamically
imports it). Import UserDirectoryPage directly from its source so the
dynamic import in responsive/index.tsx can move the module into the
expected chunk.

* [AI] Add release note for #7951

---------

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

* only count PRs based on master towards maintainer points (#7958)

* only count PRs to master

* note

* [AI] Add blog post announcing no-advertising Discord rule (#7914)

* [AI] Add blog post announcing no-advertising Discord rule

https://claude.ai/code/session_01VPN1uPikyDXd1A5mSFppr4

* [AI] Rewrite no-advertising Discord blog post in maintainer voice

https://claude.ai/code/session_01VPN1uPikyDXd1A5mSFppr4

* [AI] Quote blog front matter description to fix YAML parse error

https://claude.ai/code/session_01VPN1uPikyDXd1A5mSFppr4

* [AI] Hyphenate "privacy-oriented" compound adjective in blog post

https://claude.ai/code/session_01VPN1uPikyDXd1A5mSFppr4

* [AI] Reword advertising policy scope per review feedback

https://claude.ai/code/session_01VPN1uPikyDXd1A5mSFppr4

---------

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

* Add stacked net worth docs (#7948)

Add some information regarding how to use the stacked net worth report.

* move crossover report docs out of experimental (#7953)

* move crossover report docs out of experimental

* coderabbit

* Trim schedule names on save (#7959)

* [AI] Trim schedule names on save

* [AI] Rename release note for PR number

* [AI] Fix last zizmor issues (#7975)

* [AI] Address zizmor findings on workflows

Replace actions-ecosystem/action-add-labels and action-remove-labels
with `gh` CLI calls (superfluous-actions). Silence the six
dangerous-triggers findings on pull_request_target/workflow_run
workflows with inline `# zizmor: ignore[dangerous-triggers]` and a
short rationale — each workflow already follows GitHub's documented
safe pattern (no PR code checkout, or validated artifacts only).

* [AI] Add release note for zizmor workflow fixes

---------

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

* [AI] Add Claude Code skills for docs, commits, and PR review (#7967)

* [AI] Add Claude Code skills for docs, commits, and PR review

Add three project-scoped skills under .claude/skills/ so any contributor
using Claude Code in this repo gets the same conventions applied
automatically: writing-actual-docs (points Claude at writing-docs.md
before any docs work), committing-actual-changes (points Claude at
pr-and-commit-rules.md before any commit/PR so the [AI] prefix and
template-blank rule are followed), and review-actual-pr (end-to-end
offline PR review with browser testing via playwright-cli, never posts
to GitHub).

All skill paths and tool assumptions are repo-relative (resolved via
git rev-parse --show-toplevel) so they work on any contributor's
machine, not just the author's.

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

* [AI] Fix remaining hardcoded $HOME path in PR-review playbook

The Step 3 highlight-overlay example still referenced
$HOME/.claude/skills/review-actual-pr/... which only resolves on
the original author's machine. Switch to the same git rev-parse
--show-toplevel pattern used elsewhere in the skill so it works
for any contributor with a checkout of the repo.

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

* [AI] Add release note for #7967

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

* [AI] Drop "add AI generated label" instruction now that it auto-applies

The "AI generated" PR label is now applied automatically by GitHub
Actions to any PR whose title starts with [AI], so the manual "add
this label" instruction is stale. It was also misleading for outside
contributors, who cannot apply labels on PRs against this repo
regardless.

Updated the canonical rules file, the committing-actual-changes
skill, and the review-actual-pr code-review rubric to describe the
prefix as the single trigger and note that the label follows
automatically.

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

* Update Claude Code skills description

Simplified the description of Claude Code skills.

* [AI] Allow cat and mv in PR-review skill's tool allowlist

The workflow saves the final report via `cat > review.md <<'EOF'`
(step 6) and rotates a stale prior report via `mv` (step 2,
re-runs), but the allowed-tools line did not permit either, so
those commands would prompt or fail at runtime. Add Bash(cat:*) and
Bash(mv:*) — keeping the allowlist tight rather than broadening to
Bash(*), so unrelated shell commands still require explicit
authorization.

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

---------

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

* [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>

* Bump the npm_and_yarn group across 1 directory with 3 updates (#7980)

Bumps the npm_and_yarn group with 3 updates in the / directory: [ip-address](https://github.com/beaugunderson/ip-address), [tmp](https://github.com/raszi/node-tmp) and [webpack](https://github.com/webpack/webpack).


Updates `ip-address` from 10.0.1 to 10.1.0
- [Commits](https://github.com/beaugunderson/ip-address/compare/v10.0.1...v10.1.0)

Updates `tmp` from 0.2.5 to 0.2.7
- [Changelog](https://github.com/raszi/node-tmp/blob/master/CHANGELOG.md)
- [Commits](https://github.com/raszi/node-tmp/compare/v0.2.5...v0.2.7)

Updates `webpack` from 5.102.1 to 5.107.2
- [Release notes](https://github.com/webpack/webpack/releases)
- [Changelog](https://github.com/webpack/webpack/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack/compare/v5.102.1...v5.107.2)

---
updated-dependencies:
- dependency-name: ip-address
  dependency-version: 10.1.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: tmp
  dependency-version: 0.2.7
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: webpack
  dependency-version: 5.107.2
  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>

* [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>

* [AI] Fix fatal error when launching PWA while offline (#7978)

* [AI] Fix fatal error when launching PWA while offline (#7886)

The Vite migration in 26.5 changed how `data-file-index.txt` is
generated. The previous shell-based `cp ... migrations/*` and
`find * -type f` skipped dotfiles by default, but the new
`fs.cp`/`fs.readdir` recursive calls include them. As a result the
legacy `.force-copy-windows` marker file ended up in the index and
the app fetched it on every startup. The workbox precache globs only
match files with known extensions, so this dotfile is never cached;
opening the PWA offline therefore failed the entire
`populateDefaultFilesystem` step and rendered a fatal error.

Skip dotfiles when generating the index (restoring the pre-26.5
behavior) and remove the stale marker file.

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

* Add release notes for PR #7978

* Delete upcoming-release-notes/7886.md

---------

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>

* 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>

* [AI] Fix small visual issues on mobile transaction entry (#7962)

* [AI] Fix small visual issues on mobile transaction entry

- Left-align text in the date field (was centered on mobile browsers).
- Show focus border on the whole icon-prefixed field (date, notes)
  instead of only under the input, and drop the rounded corners that
  leaked through from the inner Input's box-shadow.
- Balance focused vs unfocused borders on the amount field so the
  page no longer shifts down by 1px when it gains focus.

* [AI] Left-align mobile date field via WebKit pseudo-element

iOS/WebKit renders <input type="date"> text inside the
::-webkit-date-and-time-value pseudo-element, which ignores the
input's own text-align. Target the pseudo-element directly.

* Add release notes for PR #7962

* [AI] Prevent icon shift when disabling mobile transaction fields

When the disabled state of a mobile transaction field changed, the
icon could drift up by 1px. Two contributing factors:

- The wrapper's emotion class was regenerated when disabled toggled
  (because backgroundColor was baked into it). Moving the background
  to an inline style keeps the wrapper's className constant.
- The icon was a flex item with intrinsic height being centered by
  the wrapper in an even-pixel content area, which is a half-pixel
  centering that can round inconsistently across re-layouts. Stretch
  the icon container to the full wrapper height and center the SVG
  internally so the icon's final position no longer depends on the
  wrapper recalculating cross-axis offsets.

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #7962

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* [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>

* 📱 Fallback to direct worker on ios (#7971)

* fallback to old worker on ios

* release note

* Correct spelling of 'iOS' in release notes

* Improve Tag Filter UX (#7848)

* [AI] Make TypeScript work in test files across packages

Match the CRDT package's tsconfig pattern in loot-core, desktop-client,
api, and desktop-electron so test files participate in the project graph
(IDE intellisense, project-wide typecheck) while production builds still
emit clean declaration files.

- Remove test-file exclusions from each package's main tsconfig
- Add tsconfig.build.json for loot-core and api with test exclusions,
  used by the build scripts
- Add e2e/tsconfig.json for desktop-client and desktop-electron with
  Playwright types
- Fix latent type errors in test files now caught by typecheck
- Disable typescript/unbound-method for test files (mock matcher pattern)

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

* [AI] Address review feedback on test type fixes

- goal-template.test.ts: extract amounts to typed locals so single-value
  assertions no longer compare against unknown
- category-template-context.test.ts: replace `as unknown as DbCategory`
  double-cast with a fully-typed object using `satisfies DbCategory`
  (the previous mock had `is_income: true` which doesn't match the
  `1 | 0` shape the cast was hiding)
- api/tsconfig.build.json: broaden test exclude pattern to `**/*.test.ts`

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

* [AI] Drop tsconfig.build.json for loot-core and api

The build-config indirection was incomplete protection: typecheck
(`tsgo -b` against the main tsconfig, which now includes test files)
already emits `*.test.d.ts` into `@types/`, and the build step does
not clean before re-emitting. The same is observable in crdt's
`dist/`, which currently contains test declarations on disk.

What actually keeps test declarations out of the npm tarball is the
`files` field in package.json — and loot-core already uses that
mechanism for source files (`\!src/**/*.test.ts`). Extending the same
pattern to `@types/` is more direct than maintaining a duplicate
tsconfig that doesn't reliably do its job.

- Delete loot-core/tsconfig.build.json; revert build to `tsgo -b`;
  add `\!@types/**/*.test.d.ts*`, `\!@types/**/__tests__/**`,
  `\!@types/**/__mocks__/**` to `files`.
- Delete api/tsconfig.build.json; revert build to
  `vite build && tsgo --emitDeclarationOnly`; add
  `\!@types/**/*.test.d.ts*` to `files`.

Verified: `yarn pack --dry-run` excludes all test declarations from
both packages while production declarations still pack (428 .d.ts
files for loot-core, methods.d.ts for api).

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

* first pass at implementation

* finished implementing what I feel would be a good user experience

* padding update

* added ability to use tab to select

* release notes

* updated release notes

* auto highlights first entry

* removed debug info

* linting formatting

* updated behavior with tags in the middle of stuff

* minor improvements to ux

* extracted TagAutocomplete functionality into its own file

* rewrote TagAutocomplete using react-aria-components from scratch

* linting

* restored old autocomplete since I'm no longer using it

* linting

* capped results at 10

* bugfix

* used tag css hook instead of notes tag formatter

* added mobile support

* fixed bug where inserting a tag at the end of the note wouldn't fire onChange handler

* fixed bug where note would hover even though field was not focused

* fixed bug with currentWord detection

* some more minor UI bugs with the mobile UI

* removed log statement

* added aria-label

* [autofix.ci] apply automated fixes

* name to Input

* updatedt ests

* fixed tests

* whitespace adjustment

* removed debug from test

* added tests and made a few fixes

* tried to fix vrt

* some new hooks to use

* removed log statement

* removed input from useCursorPosition

* input ref value hook now triggers on mount

* add extra padding

* hide browser autocomplete when our popup is shown to avoid doubles

* useFilteredTags hook and adjustments to mobile tag UI

* switched from opacity to height transitioning

* sorted filtered tags by startsWith first

* updated highlighting logic for mouseover

* [autofix.ci] apply automated fixes

* fix tag ordering

* button type

* [autofix.ci] apply automated fixes

* release note update

* release note to trigger ci

* reverted db change

* Revert "reverted db change"

This reverts commit 24ce5450e5e4261e77fe5056620c240da22869a3.

* can now filter by tags without hashes

* extracted a method and added a new hasAnyTags rule

* added hasAnyTag, still need to figure out why tests are failing

* fixed unit tests

* release notes

* fixed based on coderabbit

* tag.toLowerCase()

* variable scope fix

* fixed some focus-related bugs

* allow creating tags in the dropdown for desktop

* fixed focus issues with useCursorPosition

* added Create Tag functionality to mobile

* tests and added some length clipping as qol

* can now use enter to submit new tag properly

* centralized overflow CSS for tags

* centered the text horizontally

* added textnowrap

* release notes

* linting

* fixed issue with async keydown handler

* specified only to use new tag css for the autocomplete stuff

* forgot to add new option to tag table

* removed Trans

* started working on multiselect

* multiselect is now functional

* finished multiAutocomplete implementation

* [autofix.ci] apply automated fixes

* removed double input

* added multi autocomplete to rule editor

* adjusted padding for TagMultiAutocomplete

* adjusted hover cursor

* release notes

* fixed rule conditions

* added tag styling to filter multiselect

* spacing adjustment

* release notes for retriggering build

---------

Co-authored-by: github-actions[bot] <matiss@mja.lv>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Alec Bakholdin <alecbakholdin.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* Add Iranian Rial (#7945)

* Add Iranian Rial

* Rename file so Ci passes

* [AI] Use fixed version for Playwright tests (#7965)

* [AI] Pin Electron Playwright version to 99.9.9

The version number at the bottom of the Management page is sourced from
package.json via app.getVersion(), so each release rotates the dynamic
version string in VRT screenshots. The browser bundle already swaps in a
static '99.9.9' for Playwright (browser-preload.js); mirror that in the
Electron bootstrap data so desktop VRT snapshots don't break on version
bumps.

Existing snapshots need to be regenerated once (via /update-vrt on the PR)
since they were captured with the live package.json version.

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #7965

* [AI] Pin Playwright server version to 99.9.9

The Management page footer also renders the sync server version, which
'get-server-version' fetches from the sync-server's /info endpoint (its
own package.json). Short-circuit getServerVersion() to '99.9.9' when
running under Playwright so VRT snapshots stay stable across releases.

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #7965

* [AI] Set 'playwright' userAgent on Electron BrowserWindow under Playwright

playwright.config.ts only sets userAgent for direct chromium launches —
_electron.launch() leaves the BrowserWindow with Chromium's default UA, so
Platform.isPlaywright (which checks navigator.userAgent) is false in the
renderer. That's why the ServerContext shortcut wasn't pinning the server
version for desktop VRT snapshots. Force the UA on the webContents when
EXECUTION_CONTEXT=playwright so renderer-side Playwright checks light up.

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #7965

* [AI] Preserve Electron substring in Playwright userAgent

Replacing the Electron renderer's UA with bare 'playwright' broke
environment.isElectron() (it greps for 'Electron' in navigator.userAgent),
which rerouted ConfigServer.tsx into the external-server branch — the
sync-server e2e test could no longer find the Start button to navigate to
the bootstrap page, so its snapshot kept falling through every
/update-vrt unchanged.

Append ' playwright' to the existing Electron UA instead, and widen the
Platform.isPlaywright check to a substring match so both
'playwright' (chromium tests) and 'Electron/… playwright' (electron tests)
resolve to true.

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #7965

* Add release notes for PR #7965

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* [AI] New chip on payee field now surfaces a lack of geolocation permissions (#7883)

* New chip on payee field now surfaces a lack of geolocation permissions

* Addressing coderabbit feedback

* Update category in release notes for transaction editing

Changed category from Maintenance to Enhancements for clarity.

---------

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

* fix schedule modal scrollbars (#7992)

* [AI] Custom highlight for added sync items: transaction table and sidebar (#7998)

* Add new CSS variables for text items in dark theme

* Add new CSS variables for table and sidebar items

* Add new CSS variables for table and sidebar items

* Update sidebar account style for updated state

* Enhance valueStyle with color for added items

* [autofix.ci] apply automated fixes

* Add new theme variables for table and sidebar

* Add color variables to added sync items

Add color variables for enhanced visibility of items.

* [AI] Apply added-row styling to transaction notes cell

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

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

* Add Pakistani PKR currency (#7993)

Co-authored-by: Awais Saeed <dev@awaissaeed.com>

* add trend line to line graphs (#7954)

* add trend line to line graphs

* note

* add to options

* split trend generation logic into seperate handler, add test

* tighten types

* Goal Templates keyboard shortcut (#7912)

* add shortcut

* note

* change shortcut to ctrl-t

* shift t

* [AI] Add npm minimal age gate for supply-chain defense (#8011)

* [AI] Add npmMinimalAgeGate to block dependency versions newer than 3 days

* [autofix.ci] apply automated fixes

* [AI] Add release notes for npm minimal age gate

---------

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

* Add initial docs for Enable Banking (#7961)

* Add initial docs for Enable Banking

* Delete upcoming-release-notes/7961.md

* [AI] Fix docusaurus docs build (#8018)

* [AI] Fix docs build by pinning webpackbar to v7

webpack 5.106+ removed deprecated ProgressPlugin options, which breaks
the older webpackbar 6.0.1 that Docusaurus 3.10.0 depends on. The build
fails with "Progress Plugin has been initialized using an options object
that does not match the API schema" (unknown properties name/color/
reporters/reporter).

Add a webpackbar ^7.0.0 resolution, matching the upstream fix in
Docusaurus 3.10.1 (facebook/docusaurus#11981).

* Add release notes for PR #8018

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* add EB docs to sidebar (#8019)

* 🔖 (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>

* [AI] Improve Balance Forecast captions (#7957)

* [AI] Add visible low point helper for forecasts

* [AI] Show ending balance in forecast report

* [AI] Show ending balance on forecast card

* [AI] Update balance forecast report snapshots

* [AI] Add release note for forecast captions

* [AI] API: improve messaging for version/schema mismatch errors (#8026)

* [AI] Improve messaging for API version/schema mismatch errors

When an outdated client or @actual-app/api connects to a budget whose
data uses a newer database schema, sync-apply fails with a cryptic
"no such column" error that surfaced as a generic download/sync error.

Add a version-mismatch message in getDownloadError/getSyncError, gated on
the actual SQLite schema-error signature (no such column/table) so other
invalid-schema failures keep their generic messaging and otherwise
compatible API versions remain usable.

* [AI] Set release note author to MatissJanis

* Add release notes for PR #8026

* [AI] Address review: use Vitest globals and remove duplicate release note

- Drop the explicit `vitest` import in errors.test.ts and rely on the
  configured test globals (per CodeRabbit review).
- Remove the duplicate 8021.md release note, keeping the PR-numbered
  8026.md with the clearer description.

* [AI] Avoid i18n in loot-core for schema-mismatch message

Return the schema-mismatch error as a plain string instead of going
through i18next, and drop the i18next setup from the test. loot-core
does not depend on i18n, and this message is surfaced verbatim through
the programmatic API.

* [AI] Remove unnecessary code comments

* [AI] Add missing-meta test for getDownloadError

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* Fix typo in Budget Automation math expression (#8035)

* Fix typo in Budget Automation math expression

* Remove release note

---------

Co-authored-by: youngcw <calebyoung94@gmail.com>

* [AI] docs: update reset-password instructions (#7950)

* [AI] docs: update reset-password instructions

Clarify the current host-side command and keep the source-checkout workflow documented.

Closes #7943

* [AI] docs: keep npm reset path alongside actual-server

* Fix CSV import settings reset when skipping end lines (#7827)

* [AI] fix(api): prevent navigator crash when loading @actual-app/api in Node.js (#8037)

* [AI] fix(api): prevent navigator crash when loading @actual-app/api in Node.js

Problem
-------
@actual-app/api 26.5.0 and 26.6.0 throw "ReferenceError: navigator is not
defined" on import in Node.js environments. The loot-core shared bundle
includes three platform variants:

  platform.ts      – browser (references navigator at module load time)
  platform.electron.ts – Electron/desktop (uses Node.js os module)
  platform.api.ts  – Node.js API (safe constants only)

The API vite build uses `resolve: { conditions: ['api'] }` to pick the right
variant via the loot-core package imports map (#shared/platform). However,
months.ts was importing via a bare relative path (`./platform`) instead of the
imports-map alias (`#shared/platform`). Vite's conditions-based resolution does
not apply to relative imports, so the build fell back to platform.ts (the
browser variant), bundling navigator references that crash at load time.

Fix
---
Change months.ts to import `#shared/platform` instead of `./platform`. This
goes through the loot-core package imports map, which the vite `api` condition
resolves to platform.api.ts — the Node.js-safe variant.

platform.api.ts was already added to the repo alongside this condition
infrastructure (in the same 26.6.0 release) but the months.ts import was
missed, leaving the crash in the published package.

Testing
-------
Added packages/api/platform.test.ts, which imports @actual-app/core/shared/
platform under the `api` vite condition and asserts that isBrowser, isPlaywright
and isIOSAgent are all false. This test:

  - Fails if platform.api.ts is deleted (module not found)
  - Fails if platform.api.ts is modified to reference navigator (ReferenceError)
  - Fails if the api vite condition is removed, falling back to platform.ts
    which throws ReferenceError at import time in Node.js

Verified by Kyle Slattery against a live Actual Budget instance via the
actual-flow integration tool (https://github.com/lunchflow/actual-flow).

* Add release note

* Update upcoming-release-notes/8037.md

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

* Delete packages/api/platform.test.ts

---------

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

* [AI] Move i18n usage outside of loot-core (#8027)

* [AI] Move i18n usage outside of loot-core

loot-core should be platform-agnostic and free of i18n concerns. This
moves all user-facing translated string helpers (rule/schedule labels,
error formatters) into desktop-client's #util/{rule,schedule,error}
modules, while keeping the underlying logic in loot-core.

- Headless @actual-app/api error formatters in loot-core now return
  plain English strings (the API has no i18n runtime).
- The persisted "Unknown" institution name and the platform storage
  alert use plain strings.
- Removed the unused i18next dependency from loot-core.

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

* [AI] Address review feedback on i18n-out-of-loot-core PR

- Wrap switch default arms in blocks to satisfy noSwitchDeclarations
  (desktop-client and loot-core getDownloadError)
- getSecretsError: return a localized generic message instead of leaking
  the raw backend error token
- getRecurringDescription: always separate the weekend annotation so it no
  longer renders as "Monday(after weekend)"; add test coverage
- IndexedDB quota error: surface a typed 'indexeddb-quota-error' event from
  loot-core and present the localized alert in desktop-client instead of a
  hard-coded English string
- Persist a null bank name (instead of English "Unknown") when the provider
  reports no institution, and render a localized fallback in the bank sync UI

---------

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

* [AI] Fix Balance Forecast chart colors with custom themes (#8031)

* [AI] Use report theme colors for Balance Forecast chart lines

* [AI] Add release notes for Balance Forecast custom theme fix

* fix react compiler on Windows (#8049)

* hmm..

* note

* [Docs] Revise fly.io deployment instructions for nightly builds (#8059)

* Revise deployment instructions for nightly builds

Updated instructions for deploying unstable versions of Actual.

* Clarify fly deploy commands in installation guide

Updated deployment commands for clarity and added example for nightly version.

* [autofix.ci] apply automated fixes

---------

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

* [AI] Scope Electron update to v41 (#8044)

* Initial plan

* Bump desktop Electron runtime to v42.3.0

* Add release notes entry for PR #8044

* Change author from Copilot to MikesGlitch

Updated author information in release notes.

* Update node-abi mappings to support Electron 42 ABI detection

* Address validation feedback for Electron update PR

* Add better-sqlite3 Electron 42 patch

* Update lockfile for better-sqlite3 patch

* Remove better-sqlite patch protocol and target Electron 41

* Align release note with Electron 41.7.1 change

* Update better-sqlite3 ranges to ^12.8.0

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Michael Clark <5285928+MikesGlitch@users.noreply.github.com>
Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>

* [Enhancements] Removed regex from excel formula execution (#7990)

* Removed regex from excel formula execution

* [autofix.ci] apply automated fixes

* Surface error for BUDGET_QUERY

* code review

---------

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

* [AI] Fix balance forecast report Y-axis label clipping (#8030)

* [AI] Fix balance forecast report Y-axis label clipping

* [AI] Add release notes for #8030

* docs: add actual-bench to community projects (#8045)

* docs: add actual-bench to community projects

* [autofix.ci] apply automated fixes

* docs: add release note for actual-bench community entry

* [autofix.ci] apply automated fixes

* fix: address coderabbit comments

* fix: remove upcoming release notes

---------

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

* [AI] Add Sort A to Z / Sort Z to A options to budget category groups (#7831)

* [AI] Add Sort A to Z / Sort Z to A options to budget category groups (#7831)

* Simplify release notes and apply coderabbitai suggestions

* [AI] fix(api): trigger SimpleFin batch sync with a single account (#8052)

The condition required more than one SimpleFin account before running
batch sync, causing a single SimpleFin account to be skipped entirely.

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

* add pikapods desktop warning (#8061)

* add pikapods desktop warning

* better link

* add to spelling list

* more spelling

* [AI] Fix bug when linking accounts (#8006)

Fixes a bug that when selecting Link Account from the account page one
has to reselect the same account in the modal even though its
prepopulated.

Co-authored-by: Gemini <noreply@google.com>

* add MXN peso (#8060)

* ♻️ remove unnecessary playwright container pulls to speed up CI (#8067)

* don't use playwright where it's not needed

* note

* [AI] Add auto-generated Upcoming Release docs page (#8065)

* [AI] Add auto-generated Upcoming Release docs page

Adds a docs page that always reflects the current contents of the
repo-root upcoming-release-notes/ directory — the changes that have been
merged but not yet published in a stable release (i.e. what ships in the
nightly/edge builds).

- Extract parseReleaseNotes/formatNotes from the release-notes generator
  into the shared ci-actions util so both the release generator and the
  docs build reuse the same formatting.
- Add a docs build script that regenerates docs/upcoming-release-notes.md
  on every start/build, with an intro explaining the notes are unreleased
  and how to try them, plus the categorized notes list.
- Wire the generator into the docs start/build scripts, add a sidebar
  entry next to Release Notes, and gitignore the generated page.

https://claude.ai/code/session_01MuY9Phome8uJKH51HbrsQw

* [AI] Simplify upcoming-release-notes generator

- Declare @actual-app/ci-actions as a workspace dependency and import the
  shared util by package name instead of a relative cross-package path, so
  the dependency is registered in the workspace graph.
- Resolve the repo root once via new URL() and drop the dirname import.
- Use a ternary for the page body instead of let + if/else.

https://claude.ai/code/session_01MuY9Phome8uJKH51HbrsQw

* Add release notes for PR #8065

* [AI] Remove edge references, keep only nightly in docs

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

* Update packages/docs/scripts/generate-upcoming-release-notes.mjs

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

---------

Co-authored-by: Claude <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>
Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>

* Bump the npm_and_yarn group across 2 directories with 1 update (#8071)

Bumps the npm_and_yarn group with 1 update in the / directory: [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router).
Bumps the npm_and_yarn group with 1 update in the /packages/desktop-client directory: [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router).


Updates `react-router` from 7.13.1 to 7.14.2
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router@7.14.2/packages/react-router)

Updates `react-router` from 7.13.1 to 7.14.2
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router@7.14.2/packages/react-router)

---
updated-dependencies:
- dependency-name: react-router
  dependency-version: 7.14.2
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: react-router
  dependency-version: 7.14.2
  dependency-type: direct:development
  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>

* When resetting SimpleFIN credentials, parse error and error_code if response body exists (#8068)

* Initial commit

* Add release note

* Update release note file name to match PR number

* Flip operands when checking for 2xx

* Release notes grammar

* Better release note for non-devs

* Simplify to naive solution of checking on nullable response

* Hiding tags and tag table bulk actions (#7919)

* [AI] Make TypeScript work in test files across packages

Match the CRDT package's tsconfig pattern in loot-core, desktop-client,
api, and desktop-electron so test files participate in the project graph
(IDE intellisense, project-wide typecheck) while production builds still
emit clean declaration files.

- Remove test-file exclusions from each package's main tsconfig
- Add tsconfig.build.json for loot-core and api with test exclusions,
  used by the build scripts
- Add e2e/tsconfig.json for desktop-client and desktop-electron with
  Playwright types
- Fix latent type errors in test files now caught by typecheck
- Disable typescript/unbound-method for test files (mock matcher pattern)

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

* [AI] Address review feedback on test type fixes

- goal-template.test.ts: extract amounts to typed locals so single-value
  assertions no longer compare against unknown
- category-template-context.test.ts: replace `as unknown as DbCategory`
  double-cast with a fully-typed object using `satisfies DbCategory`
  (the previous mock had `is_income: true` which doesn't match the
  `1 | 0` shape the cast was hiding)
- api/tsconfig.build.json: broaden test exclude pattern to `**/*.test.ts`

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

* [AI] Drop tsconfig.build.json for loot-core and api

The build-config indirection was incomplete protection: typecheck
(`tsgo -b` against the main tsconfig, which now includes test files)
already emits `*.test.d.ts` into `@types/`, and the build step does
not clean before re-emitting. The same is observable in crdt's
`dist/`, which currently contains test declarations on disk.

What actually keeps test declarations out of the npm tarball is the
`files` field in package.json — and loot-core already uses that
mechanism for source files (`\!src/**/*.test.ts`). Extending the same
pattern to `@types/` is more direct than maintaining a duplicate
tsconfig that doesn't reliably do its job.

- Delete loot-core/tsconfig.build.json; revert build to `tsgo -b`;
  add `\!@types/**/*.test.d.ts*`, `\!@types/**/__tests__/**`,
  `\!@types/**/__mocks__/**` to `files`.
- Delete api/tsconfig.build.json; revert build to
  `vite build && tsgo --emitDeclarationOnly`; add
  `\!@types/**/*.test.d.ts*` to `files`.

Verified: `yarn pack --dry-run` excludes all test declarations from
both packages while production declarations still pack (428 .d.ts
files for loot-core, methods.d.ts for api).

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

* first pass at implementation

* finished implementing what I feel would be a good user experience

* padding update

* added ability to use tab to select

* release notes

* updated release notes

* auto highlights first entry

* removed debug info

* linting formatting

* updated behavior with tags in the middle of stuff

* minor improvements to ux

* extracted TagAutocomplete functionality into its own file

* rewrote TagAutocomplete using react-aria-components from scratch

* linting

* restored old autocomplete since I'm no longer using it

* linting

* capped results at 10

* bugfix

* used tag css hook instead of notes tag formatter

* added mobile support

* fixed bug where inserting a tag at the end of the note wouldn't fire onChange handler

* fixed bug where note would hover even though field was not focused

* fixed bug with currentWord detection

* some more minor UI bugs with the mobile UI

* removed log statement

* added aria-label

* [autofix.ci] apply automated fixes

* name to Input

* updatedt ests

* fixed tests

* whitespace adjustment

* removed debug from test

* added tests and made a few fixes

* tried to fix vrt

* some new hooks to use

* removed log statement

* removed input from useCursorPosition

* input ref value hook now triggers on mount

* add extra padding

* hide browser autocomplete when our popup is shown to avoid doubles

* useFilteredTags hook and adjustments to mobile tag UI

* switched from opacity to height transitioning

* sorted filtered tags by startsWith first

* updated highlighting logic for mouseover

* [autofix.ci] apply automated fixes

* fix tag ordering

* button type

* [autofix.ci] apply automated fixes

* release note update

* release note to trigger ci

* reverted db change

* Revert "reverted db change"

This reverts commit 24ce5450e5e4261e77fe5056620c240da22869a3.

* can now filter by tags without hashes

* extracted a method and added a new hasAnyTags rule

* added hasAnyTag, still need to figure out why tests are failing

* fixed unit tests

* release notes

* fixed based on coderabbit

* tag.toLowerCase()

* variable scope fix

* fixed some focus-related bugs

* allow creating tags in the dropdown for desktop

* fixed focus issues with useCursorPosition

* added Create Tag functionality to mobile

* tests and added some length clipping as qol

* can now use enter to submit new tag properly

* centralized overflow CSS for tags

* centered the text horizontally

* added textnowrap

* release notes

* linting

* fixed issue with async keydown handler

* specified only to use new tag css for the autocomplete stuff

* forgot to add new option to tag table

* removed Trans

* started working on multiselect

* multiselect is now functional

* finished multiAutocomplete implementation

* [autofix.ci] apply automated fixes

* removed double input

* added multi autocomplete to rule editor

* adjusted padding for TagMultiAutocomplete

* adjusted hover cursor

* release notes

* fixed rule conditions

* added tag styling to filter multiselect

* spacing adjustment

* release notes for retriggering build

* added hidden migration

* added hidden to models

* added tags-hide-all and tags-unhide-all actions

* added hide and unhide tags actions

* added SelectedTagsButton

* added UI elements for hiding/showing tags

* release notes

* removed delete from ManageTags

* added icons to menu

* coderabbit

* coderabbit

* updated mutations for translations

* coderabbit

* removed not null requirement

* attempted to fix tests?

* removed duplicate filterTags

* [autofix.ci] apply automated fixes

* updated release notes

* release note update

* updated migration timestamp

* moved tags menu button

---------

Co-authored-by: github-actions[bot] <matiss@mja.lv>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Alec Bakholdin <alecbakholdin.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* automation UI: improve scaling (#8080)

* improve scaling on high zoom levels

* note

* automation UI: rework balance cap and fix error when clearing fields (#8082)

* fix safeNumber error

* rework balance cap UI to be more readable

* note

* add explanation to balance cap

* Bump react-router in the npm_and_yarn group across 1 directory (#8072)

Bumps the npm_and_yarn group with 1 update in the / directory: [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router).


Updates `react-router` from 7.14.2 to 7.15.0
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router@7.15.0/packages/react-router)

---
updated-dependencies:
- dependency-name: react-router
  dependency-version: 7.15.0
  dependency-type: direct:development
  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>

* [AI] feat(currencies): add currency-precision-aware helpers (#8064)

* [AI] feat(currencies): add getDecimalPlaces/getCurrencyPrecisionMultiplier helpers, encodeAmount, currency-aware integerToCurrencyWithDecimal

* Fix typo in release note

* Use it.each in unit tests

* Rename encodeAmount and replace its body; remove getCurrencyPrecisionMultiplier

* [AI] Remove AI-generated release notes workflow and support custom filenames (#7963)

* [AI] Relax release-notes filename convention

Allow descriptive filenames (e.g. add-payee-autocomplete.md) for
upcoming release notes instead of requiring {PR_NUMBER}.md.

The PR number was only used to build the [#1234](.../pull/1234) link in
the generated changelog. The generation script now resolves the PR per
file by finding the commit that added it (git log --diff-filter=A
--follow) and querying GitHub's commits/{sha}/pulls endpoint. Numeric
filenames remain valid via a fast path.

Also retires the CodeRabbit/OpenAI-triggered auto-creator workflow,
which existed to guess summaries and stamp the PR-numbered filename;
neither is needed once contributors can pick a slug up front.

* Add release notes for PR #7963

* [AI] Use execFile to look up release-note add-commits

resolvePrNumber() interpolated a path from fs.readdir into a shell-
evaluated `git log` command. Switch to execFile with an argv array so
filenames containing shell metacharacters can't break out of the
intended command.

* [AI] Drop duplicate release note added by the to-be-removed auto-creator

The CodeRabbit-triggered workflow ran one last time from master before
this PR removes it, and committed upcoming-release-notes/7963.md
duplicating the existing relax-release-notes-filenames.md entry. Keep
the slug-named note since it exercises the new flexible-filename code
introduced in this PR.

* [AI] Don't fail release-notes generation on transient API errors

fetchPrForCommit() handled non-OK HTTP responses but let fetch() and
res.json() exceptions propagate, so a single network blip or malformed
response would abort the whole release-notes generation. Wrap them in a
try/catch that logs and returns null, matching the pattern already used
in resolvePrNumber for execFile errors.

* [AI] Address CodeRabbit full-review nits

- bin/release-note-generator.ts: tighten slug regex to reject trailing
  and consecutive dashes (matching slugify output), and make slugify
  fall back to "untitled" so an all-non-alphanumeric input can't
  produce a hidden ".md" filename.
- packages/ci-actions/bin/release-notes-check.mjs: switch to execFile
  for git fetch/diff, matching release-notes-generate.mjs and removing
  shell interpolation of BASE_REF.
- packages/ci-actions/bin/release-notes-generate.mjs: explicitly check
  GITHUB_REPOSITORY before splitting, consistent with the other env
  var guards a few lines below.
- upcoming-release-notes/relax-release-notes-filenames.md: fix author
  casing (matiss -> MatissJanis) so changelog attribution is correct.

* [AI] Reject empty release-note bodies

content.trim().split('\n').length === 1 is true for the empty string
(''.split('\n') returns [''] of length 1), so blank notes slipped past
the single-line check. Reject empty trimmed content explicitly.

* [AI] Resolve PR numbers from commit subjects instead of GitHub's API

parseReleaseNotes was doing one GitHub API round-trip per non-numeric
release-note file. That scales badly for release generation, and the
docs site's generate-upcoming-release-notes.mjs runs on every docs
build — so contributors were hitting the GitHub API (or failing 401
without a token) every time they previewed docs locally.

actualbudget squash-merges every PR through the GitHub UI, which
appends "(#NNNN)" to the resulting commit subject. So:

  git log -1 --format=%s -- <path>

plus a /\(#(\d+)\)\s*$/ match recovers the PR number without touching
the network, and works offline / without a GITHUB_TOKEN.

Side effect: switched from "first commit that added this file" to
"most recent commit touching this file", which is what we actually
want — when a file is renamed (e.g. 7907.md -> 7954.md when a wrong
PR number is corrected), the changelog should point at the rename PR,
not the original add. The SHA->PR cache and fetchPrForCommit go away;
no longer needed.

If a file's subject doesn't match (direct push to master, manually
rewritten subject), the entry still emits without a PR-link prefix —
same graceful degradation as before.

* [AI] Pin PR-number lookup to each note's add commit

resolvePrNumber was using git log -1 with no diff filter, returning
the latest commit that touched the file. That's fine until someone
edits an existing release note in a follow-up PR (typo fix, author
correction) — at which point the changelog entry would suddenly
point at the wrong PR.

Add --diff-filter=A so the lookup pins to the commit that originally
added the path. Empirically verified against this repo: renames keep
working (git records a rename as A at the new path when --follow
isn't used), and the edit case now correctly returns the original
add commit instead of the most recent touch.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* [AI] Add GitHub workflow to block PRs with "do not merge" label (#8090)

* [AI] Add CI job that fails when 'do not merge' label is set

* [AI] Rename do not merge check job to "Block PRs with do not merge label"

* [AI] Rename do not merge workflow to "Block PRs with do not merge label"

* [AI] Add release notes for do not merge CI check

* Update 8090.md

---------

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

* [AI] Persist bank sync status in the accounts table (#8017)

* [AI] Persist bank sync status in the accounts table

Extracts the persisted bank-sync-status half of #7782 (by @jcam): adds a
bank_sync_status column to the accounts table, writes the status
(pending/ok/failed/reauth-required/attention-required) during native bank
syncs, and reads the persisted value in the UI (sidebar, account header,
mobile account pages, and the sync banner) instead of ephemeral Redux state.

https://claude.ai/code/session_014MXn6Qi8skvFL9kXw7ctFD

* [AI] Add release note for persisted bank sync status

https://claude.ai/code/session_014MXn6Qi8skvFL9kXw7ctFD

* [AI] Persist only durable failure states, keep pending ephemeral

Addresses review feedback: a transient 'pending' write could get stuck in
the DB (and propagated via CRDT) if the app closed mid-sync. The DB now only
stores durable result states (ok / failed / reauth-required / attention-
required); pending stays in ephemeral Redux state (account.accountsSyncing)
as before. The UI reads only the persisted failure state via isAccountFailedSync,
and the BankSyncStatus banner reverts to the ephemeral in-flight count.

Also extracts a persistBankSyncError helper and inlines a redundant wrapper.

https://claude.ai/code/session_014MXn6Qi8skvFL9kXw7ctFD

* [AI] Un-export AccountSyncSidebar, refine release note

- AccountSyncSidebar is internal again; drop the export-only-for-test and its
  test (the failure logic is already covered by syncStatus.test.ts).
- Credit MatissJanis in the release note and make it user-centric.

https://claude.ai/code/session_014MXn6Qi8skvFL9kXw7ctFD

* [AI] Publish account changes in bank-sync sync-events

handleSyncResponse writes accounts.last_sync and accounts.bank_sync_status,
but the sync-events following it only declared tables: ['transactions'], so
clients only refetch useAccounts when the event includes 'accounts'. Add
'accounts' to the six emissions that follow handleSyncResponse (the four link
flows and the two batch-sync finals) so the persisted account changes are
published everywhere, not just on the triggering client.

https://claude.ai/code/session_014MXn6Qi8skvFL9kXw7ctFD

* [AI] Re-timestamp bank_sync_status migration after master

A newer migration (add_tags_hidden) landed on master, so bump the
bank_sync_status migration's timestamp to remain the latest and satisfy the
migration-ordering check.

https://claude.ai/code/session_014MXn6Qi8skvFL9kXw7ctFD

---------

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

* Remove environment from release notes workflow (#8091)

* Remove environment from release notes workflow

Removed the environment specification from the release notes workflow.

* Update release-notes CI job configuration

Removed the unnecessary 'environment' from the release-notes CI job.

* [AI] Enable "do not merge" check on merge queue events (#8092)

* [AI] Run do-not-merge check on merge_group events

The block-do-not-merge workflow only triggered on pull_request events, so
it never ran in the merge queue and could not be made a required check.
Adding the merge_group trigger lets the job run and pass in the queue
context (where pull_request labels are absent), allowing it to be required.

* [AI] Add release notes for merge queue do-not-merge fix

---------

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

* Make payee & categories searching to be case-insentive (#8079)

* make payee & categories searching to be case-insentive

* add release notes

* [autofix.ci] apply automated fixes

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #8079

* update release-note

* [autofix.ci] apply automated fixes

* fix release-note format

---------

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

* Add files via upload (#8096)

* [AI] Fix incorrect schedule value when saving full amount (#8085)

If one has a yearly schedule and the budget template uses "Cover each
occurence when it occurs", it still showed a value for the yearly
schedule even though it's not the month where the schedule occurs.

Co-authored-by: Gemini <noreply@google.com>

* feat: add SharedArrayBuffer warning indicator in title bar (#7922)

* feat: add SharedArrayBuffer warning indicator in title bar

* improve styling

* add warning label

* fix typo

* review comments fixed

* use link instead of button

* fix tooltip styles

* add hover effect

---------

Co-authored-by: Awais Saeed <dev@awaissaeed.com>

* [AI] Remove experimental status from Actual CLI (#8102)

* [AI] Move actual-cli out of experimental status

* [AI] Rename CLI release note to a feature slug

---------

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

* add warning when schedule/save by automation priorities don't match (#8088)

* add warning when schedle/save by automation priorities don't match

* note

* italicise

* feat(reports): scoped ErrorBoundaries for individual report routes (#7658)

* feat(reports): scoped ErrorBoundaries for individual report routes (#7391)

Wrap each route in ReportRouter for NetWorth, CashFlow, Spending, etc.
in ErrorBoundary with FeatureErrorFallback. Matches the pattern used
for /rules routes in FinancesApp.tsx so a render error in a single
report no longer takes down the whole app.

* chore: rename release note to PR number

* refactor(reports): extract withReportBoundary helper to remove route boilerplate

Each report route was wrapping its element in the same ErrorBoundary block
with FeatureErrorFallback and resetKeys={[location.pathname]}. Pull that
into a small withReportBoundary(element) helper inside ReportRouter so
each route is one line again.

Behavior is unchanged. The ErrorBoundary, FallbackComponent, and resetKeys
are identical to before; the helper just closes over location.pathname.

Addresses review feedback from @joel-jeremy and the CodeRabbit nitpick on
the same file (a custom Route component is not viable since react-router
v6's <Routes> requires literal <Route> children, but a helper function
gives the same boilerplate reduction).

* refactor(reports): hoist withReportBoundary to ReportBoundary component

Move the per-route error-boundary helper out of ReportRouter as a
module-scoped component. ReportBoundary calls useLocation() internally so
each call site stays clean. Addresses review feedback.

---------

Co-authored-by: github-actions[bot] <matiss@mja.lv>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* [AI] Add configurable average ranges to Monthly Spending report (#7920)

* Add configurable average ranges to Monthly Spending report

* Delete upcoming-release-notes/temp.md

* Update packages/desktop-client/src/components/reports/spendingAverageRange.test.ts

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

* Fix spending average range test import

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #7920

---------

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

* [AI] Fix custom report transfer drilldown filters (#8001)

Co-authored-by: youngcw <calebyoung94@gmail.com>

* Make tag search case-insensitive (#8093)

* makes tags case insensitive

* release notes

---------

Co-authored-by: Alec Bakholdin <alecbakholdin.com>

* docs: add Hostim to Additional Installation Options (#8066)

* docs: add Hostim to community install options

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

* docs: add release note for #8066

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

* docs: drop release note (not required for docs-only change)

---------

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

* [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>

* [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>

* [AI] Disable ESLint in CodeRabbit configuration (#8110)

* [AI] Disable ESLint in CodeRabbit config

* [AI] Add release notes for CodeRabbit ESLint change

* [AI] Rename release notes to match PR number 8110

* [AI] Rename release notes file to a slug

---------

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

* 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>

* automation UI: add mobile support 📱 (#8099)

* generalise out to hooks

* add mobile modal

* wire up mobile

* tidy up field styling on mobile

* [AI] add mobile automations e2e test

Covers opening the editor from the mobile category menu, the drill-down
list/editor navigation, touch-sized fields, and that Cancel dismisses it.

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

* note

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #8099

* fix date picker on schedule page

* fix conflict

* fix cramped space in save by automation

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* [Docs] Starting at the top, Introduction & Vision (#8083)

* Enhance introduction and navigation details in docs

Revised wording for clarity and improved navigation instructions in the documentation.

* Update vision.md for clarity and formatting

Refine language for clarity and consistency, and update formatting for better readability.

* Improve formatting of third-party use section

Reformatted the third-party use section for clarity.

* Revise documentation introduction and navigation details

Updated the introduction and navigation instructions for clarity and conciseness.

* Revise documentation overview and navigation details

Updated documentation for clarity and navigation instructions.

* Refine Local-first explanation in vision.md

Removed redundant text in the Local-first section.

* [autofix.ci] apply automated fixes

* Revise contribution guidelines in documentation

Updated links to contributing guides and documentation readme.

* Revise headings and improve hyphenation in vision.md

Updated headings and corrected hyphenation in vision.md.

* [autofix.ci] apply automated fixes

* Fix wording in contribution guidelines section

Corrected the wording from 'documentation standard' to 'documentation standards' for clarity.

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>

* [AI] Fix bank sync deadlock from nested mutator (#8111)

* [AI] Fix bank sync deadlock from nested mutator

PR #8017 wrapped the accounts-bank-sync and simplefin-batch-sync handlers
in mutator(). runHandler runs mutators via runMutator, which is
sequential() — only one mutator may run at a time and re-entrant calls are
queued. These handlers call syncAccount -> reconcileTransactions, which
already invokes runMutator internally. The inner runMutator gets queued
behind the still-running outer one while the outer awaits it, deadlocking
forever. The bank sync send() never resolves, so the mobile spinner spins
indefinitely.

Remove the mutator() wrapping (restoring pre-#8017 registration). The DB
writes for bank_sync_status/last_sync already ran outside a mutator before
#8017, and transaction reconciliation manages its own mutator context.

* [AI] Add regression tests for bank sync mutator deadlock

Guards against re-wrapping the accounts-bank-sync / simplefin-batch-sync
handlers in mutator(), which deadlocks because the sync internally calls
runMutator (reconcileTransactions) and runMutator is sequential.

- An invariant test asserts neither handler is a mutating method.
- A behavioral test drives accounts-bank-sync through runHandler with a
  syncAccount mock that performs its own runMutator, and fails via a
  timeout if the nested mutators deadlock.

* [AI] Simplify deadlock regression test

Collapse the two-step Promise.race into a single race result and clear the
timeout timer on the success path so it doesn't linger after the test.

* [AI] Remove explanatory comments from deadlock regression test

* [AI] Add release note for bank sync deadlock fix

* Update packages/loot-core/src/server/accounts/app-bank-sync.test.ts

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

* Update packages/loot-core/src/server/accounts/app-bank-sync.test.ts

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>

* [AI] Fix desktop settings resetting after an interrupted update (#8101)

* [AI] Fix desktop settings resetting after an interrupted update

Desktop global prefs (language, theme, etc.) live in a single
global-store.json that was written non-atomically and silently reset to
{} whenever it failed to parse. An app update's forced quit can truncate
the file mid-write, so the next launch wiped every global pref - the
language reverted to "System default" (the OS language).

- Write the store via a unique temp file + atomic rename so it is never
  left half-written.
- Serialize writes so a slower older write can't land after and clobber a
  newer one.
- Back up an unparseable store to .corrupt and log loudly instead of
  silently discarding the user's preferences.

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

* [AI] Reject non-object global-store contents during load

JSON.parse could return a non-object (null, an array, a primitive) for
parseable-but-wrong-shaped contents, which would then break store[key]
access. Validate the parsed value is a plain object and route anything
else through the existing back-up-and-default recovery path.

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

---------

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

* :electron: Removed new shared array buffer warning on Electron (#8118)

* removed shared array buffer warning one electron

* release notes

* cleanup a bit

* Enhance translation contribution guidelines in i18n.md (#8113)

* Enhance translation contribution guidelines in i18n.md

Updated i18n.md to include new guidelines for contributing translations, account requirements, and voting on suggestions.

* Update i18n.md

* [AI] update contributor guidelines for AI usage (#8135)

* [AI] Surface anti-slop and self-review expectations at PR time

Drive-by, LLM-generated PRs (often many at once from one author) burn
maintainer review time. The repo's AI usage policy already covers this,
but contributors never see it in the PR flow.

- Strengthen the fork-PR welcome comment to ask for a human self-review
  confirmation, a plain-language testing note, and AI disclosure, plus a
  note discouraging opening many PRs at once.
- Add a "Quality Over Quantity" section to the AI usage policy naming the
  volume pattern and its consequences.
- Add an AI-disclosure prompt and tighten the self-review item in the PR
  template.

No mechanical CI gates on PR body content: those are trivially satisfied
by the same tools that generate the slop, so the change relies on human
accountability and a linkable policy instead.

* [AI] Point welcome comment at the PR template instead of inline asks

Replace the three "reply on this PR with" bullets with a single checklist
item asking contributors to use the PR template and fill in its checkboxes,
keeping the welcome comment short.

* [AI] Add AI disclosure checklist item to fork PR welcome comment

---------

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

* [AI] Fix CLI server version without open budget (#8117)

* [AI] fix cli server version without open budget

* Remove redundant expectation in get-server-version test

Remove redundant expectation for get-server-version handler call.

---------

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

* [AI] Skip posted schedule occurrences in balance forecast (#8029)

* [AI] Extract schedule occurrence match start date helper

Centralize the lower-bound rules for matching posted transactions to
schedule dates and reuse them in getHasTransactionsQuery, including the
correct 2-day lookback for recurring schedules stored with op is.

* [AI] Add schedule occurrence posted matching helpers

Add isScheduleOccurrencePosted with the match-start logic built in and
indexPostedScheduleTransactions for efficient per-schedule lookups.

* [AI] Skip posted schedule occurrences in balance forecast

Skip synthetic schedule occurrences when a posted transaction already
covers that date, using indexed per-schedule lookups in the forecast path.

* [AI] Add release notes for balance forecast schedule dedup fix

* [AI] Treat op is schedule dates as exact match start

Restore master semantics: recurring schedules with op is use exact
occurrence matching, not a 2-day lookback. Remove conflicting tests.

* [AI] Add daily recurring forecast dedup regression test

Prove daily schedules with op is do not double-count when posted on the
due date, while subsequent occurrences are still forecast.

* [AI] Move browser web-worker logic into loot-core (#8143)

* [AI] Move browser web-worker logic into loot-core

Relocate the browser Web Worker bootstrap (WorkerBridge + startBrowserBackend)
and the multi-tab SharedWorker coordinator from desktop-client into loot-core
so they can be reused (e.g. by the api package in a browser).

Behavior is preserved as faithfully as possible. The two Vite worker entry
targets stay in desktop-client (the browser-server.js classic worker and the
?sharedworker entry); the ?sharedworker entry now imports the coordinator from
loot-core. console.* calls in the moved code route through loot-core's logger,
except the SharedWorker console-forwarding mechanism, which must keep the real
console. absurd-sql's initBackend is imported directly (loot-core already
depends on absurd-sql); the desktop-client dependency was dropped.

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

* [AI] Rename release note to match PR number 8143

* Update release notes for Web Worker migration

Removed mention of reuse by the API package in the release notes.

* [AI] Drop move-related header comments per review

Remove the "moved from desktop-client" header comments that only made sense
in the context of this PR. worker-bridge.ts and start.ts had no header comment
in the original source, so they're removed entirely; coordinator.ts's header
is restored to its original wording.

---------

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

* 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

* [AI] Drop ACTIONS_UPDATE_TOKEN from release workflows (#8142)

* [AI] Drop ACTIONS_UPDATE_TOKEN from release workflows

Replace the implicit push-triggers-workflow link (which relied on the ACTIONS_UPDATE_TOKEN PAT) with an explicit workflow_run chain from "Cut release branch" to "Release notes", falling back to the built-in GITHUB_TOKEN. The PAT stays in vrt-update-apply.yml, which genuinely needs it to push to contributor forks and re-trigger their PR CI.

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

* [AI] Add release note for ACTIONS_UPDATE_TOKEN removal

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

* Update 8142.md

---------

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

* [AI] Consolidate agent tooling and simplify developer guidance (#8089)

* [AI] Replace mechanical agent rules with shared agent hooks + nano-staged

Move the mechanical "remember to run X" rules out of AGENTS.md and the
agent guidance docs into deterministic, cross-agent hooks, and migrate
the pre-commit runner from lint-staged to nano-staged.

- Add shared hook scripts in scripts/agent-hooks/ (git-guard,
  format-edited-file, no-strict-ignore-new-file, prefer-one-component,
  check-on-stop, common helpers) wired for Claude (.claude/settings.json),
  Codex (.codex/config.toml), and Cursor (.cursor/hooks.json + adapters).
- Enforce "avoid enum" via a new actual/no-enum lint rule, grandfathering
  the two existing declarations in .oxlintrc.json overrides.
- Migrate lint-staged -> nano-staged (.nano-staged.json, .husky/pre-commit,
  package.json).
- Trim AGENTS.md, .github/agents/pr-and-commit-rules.md, the Cursor rule,
  and the committing skill down to the rules that aren't auto-enforced.
- Add release note 8089.md.

* [AI] Align PR-title wording in AGENTS.md with canonical rules

* [AI] Fix check-on-stop hook on bash 3.2 and TSV column collapse

The Stop hook silently no-op'd in two ways:

- `declare -A` isn't supported on stock macOS bash 3.2, and the
  slash/hyphen path subscript aborted under `set -u`. Dedupe via a
  space-padded string match instead.
- jq emitted an empty middle TSV field for a package with test but no
  typecheck; tab is IFS-whitespace so `read` collapsed it and shifted the
  columns (eslint-plugin-actual then ran neither). Emit non-empty
  sentinels (-, yes/no) so no field can collapse.

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

---------

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

* [AI] docs(migration): expand nYNAB export with tabbed alternatives (#8155)

Add CLI tool (ynab-export), YNAB API Documentation UI, and cURL as
fallback export methods for when the third-party web exporter is
unavailable. Converts nynab.md to MDX to support Docusaurus Tabs.
Six new screenshots added for the API Documentation UI method.

* 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>

* [AI] fix: translate transfer menu empty placeholder (#8161)

* fix: translate transfer menu empty placeholder

* docs: add release note for transfer placeholder fix

* [AI] fix release note category

* [AI] Add missing widget types to dashboard import validation (#8159)

* [AI] Add missing widget types to dashboard import validation

* [AI] Add test ensuring all widget types are covered by import validation

* [AI] Add release notes for dashboard import widget type support

* [AI] fix: clear split parent payee (#8005)

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

* Fix Sankey Budgeted view, when an amount is left to budget. (#8169)

* Fix Budget view, when an amount is left to budget.

* Add translation to To budget-node

* docs: fix docs build & broken anchors, migrate to consistent link format (#8173)

* fix broken anchors

* enable build fail on broken anchors

* move to relative links

* Update check-spelling metadata

* update style guide to match

* coderabbit

* [AI] docs: fix broken blog links and add enforce-doc-links remark plugin (#8180)

* [AI] docs: fix broken blog links and add enforce-doc-links remark plugin

- Fix three docs/ pages that linked to blog posts using URL slugs instead of
  file paths, with an extra ../ level in the relative path
- Add src/remark/enforce-doc-links.js: remark plugin that enforces link hygiene
  in docs/ and blog/ at build time (no absolute internal links, .md extension
  required, slug-style links caught via frontmatter cache)
- Upgrade onBrokenMarkdownLinks from 'warn' to 'throw' so broken .md file-path
  links fail the build rather than being silently ignored

* [autofix.ci] apply automated fixes

* [AI] docs: add vfile to spell-check allowlist

* [AI] docs: fix build failures from cross-plugin and absolute links

- Revert docs→blog links to URL-slug style (Docusaurus cannot resolve .md
  file paths across content plugins — blog is a separate plugin from docs)
- Fix absolute /docs/... links in two blog posts to relative URL form
- Fix generate-upcoming-release-notes.mjs hardcoded /docs/releases absolute
  URLs to relative ./releases.md paths
- Update enforce-doc-links plugin: exempt docs→blog URL-slug links from Rule 3
  (cross-plugin constraint means slug URLs are the required format there);
  improve Rule 1 error hint to distinguish docs vs blog context

* [AI] docs: fix blog→docs links to use absolute URLs

Blog posts are embedded in multiple Docusaurus URL contexts (individual post,
paginated list pages, tag pages), so relative URLs resolve differently per
context. Blog→docs links must use absolute URLs (/docs/...) — relative URLs
like ../docs/... incorrectly resolve to /blog/docs/... on list pages.

Update enforce-doc-links plugin to exempt blog files from Rule 1 (absolute
link check) since absolute URLs are the required format for blog→docs links.

---------

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

* docs: shared experimental warning component (#8174)

* move to shared experimental warning component

* coderabbit

* Bump shell-quote in the npm_and_yarn group across 1 directory (#8182)

Bumps the npm_and_yarn group with 1 update in the / directory: [shell-quote](https://github.com/ljharb/shell-quote).


Updates `shell-quote` from 1.8.3 to 1.8.4
- [Changelog](https://github.com/ljharb/shell-quote/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/shell-quote/compare/v1.8.3...v1.8.4)

---
updated-dependencies:
- dependency-name: shell-quote
  dependency-version: 1.8.4
  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>

* docs: point users towards nightly desktop builds (#8175)

* point users towards nightly desktop builds

* signpost nightly docker in contributing page too

* [AI] Fix nYNAB import for YNAB plan API wrapper rename (#8184)

YNAB's API renamed the top-level export wrapper from `budget` to `plan`
(v1.78+), so modern exports wrapped as `{"data":{"plan":...}}` were rejected
with a not-ynab5 error. parseFile now unwraps `plan`, falling back to `budget`
for older and third-party exports, with a unit test covering both wrappers.

Also updates the nYNAB migration docs to use api.ynab.com and the /plans
endpoints/terminology, and replaces the manual plan-to-budget JSON edit step
with a fallback note.

* [AI] Fix desktop PWA title bar theme color (#8181)

* [AI] Fix desktop PWA title bar theme color

* [AI] Re-trigger CI after transient VRT runner image-pull failure

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

---------

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

* 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

* [AI] Fix monthly spending category group budgets (#8043)

* [AI] Fix monthly spending category group budgets

* chore: added release notes

* [AI] Add tests for spending budget category group filters

* [AI] Prevent budget undercounting when spending report conditions are unsupported

---------

Co-authored-by: sys044 <sys044@localhost>
Co-authored-by: youngcw <calebyoung94@gmail.com>

* [Docs] Add docs for sankey report (#8172)

* Add docs for sankey report

* [autofix.ci] apply automated fixes

* Spelling, broken links, experimental warning

* Try to fix merge conflict

---------

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

* [AI] Skip release notes check for Dependabot PRs (#8185)

Dependabot never adds a file under upcoming-release-notes/, so the
check-release-notes job always failed on its PRs. Skip the check when
the PR author is dependabot[bot].

https://claude.ai/code/session_014oT7jczAkGT8RSwJYpPBur

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

* [AI] Batch sum-amount query on cold budget build (#8140)

* [AI] Batch sum-amount query on cold budget build

Opening a budget with many years of history was slow because every
sum-amount cell ran its own SELECT SUM(amount) query during the cold
spreadsheet build, scaling as categories x months (thousands of
synchronous queries). This is shared loot-core code, so it affected
Electron, desktop browser, and mobile alike, and was worst on
web/mobile where SQLite runs via WASM.

Compute all category/month spend totals in a single grouped query and
seed the sum-amount cells before their dynamic cells are created, so the
cells skip their per-cell query. The grouped query is loaded lazily so
warm loads (values restored from cache) never hit the database, and the
seeded values are persisted to the cache so subsequent loads stay
query-free. The per-cell run() is unchanged, so incremental recompute on
a transaction edit still works as before.

https://claude.ai/code/session_01SThbYSwQwvHo1wdiQbfSsb

* [AI] Rename release note to a slug

https://claude.ai/code/session_01SThbYSwQwvHo1wdiQbfSsb

* [AI] Address review: scope seed query to uncached months

Scope the grouped sum-amount query to only the months being created
instead of the full budget range. Extending the budget horizon by a
month no longer rescans the entire transaction history just to seed the
new month. A pure cold build is unchanged since every month is uncached.

Also reword the release note to be user-facing.

https://claude.ai/code/session_01SThbYSwQwvHo1wdiQbfSsb

* [AI] Address review: harden seed-query month span and encapsulate cache saving

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

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

* [AI] Add tracking budget forecast mode (#7884)

* [AI] Add forecast source types

* [AI] Add tracking budget forecast projection helper

* [AI] Route forecast generation by source

* [AI] Add forecast source selector to report

* [AI] Persist forecast source on dashboard widgets

* [AI] Add tracking forecast UI coverage

* [AI] Add tracking budget forecast VRT coverage

* [AI] Document tracking budget forecast mode

* [AI] Add release notes for tracking budget forecast mode

* [AI] Fix shift-click range selection including hidden reconciled transactions (#8177)

* [AI] Fix shift-click range selection including hidden reconciled transactions

When 'Hide reconciled transactions' is enabled while the running
balance is shown, reconciled transactions stay loaded (they are needed
to calculate running balances) and are only hidden at render time.
The selection provider still received the full transaction list, so
shift-click range selection also selected the hidden reconciled
transactions, and batch actions (e.g. delete) silently operated on
them.

Pass only the visible transactions to SelectedProviderWithItems,
mirroring the filtering the transaction table applies when rendering.

Fixes #6579

* [AI] Add release notes

* [AI] Slim down range selection e2e test to reduce runtime

* [AI] Minify web bundle while keeping original identifiers (#8194)

Enable the Oxc minifier for desktop-client and loot-core (kcab worker)
production builds, but disable identifier mangling so user-reported
stack traces keep the original function and variable names. The
loot-core dev watch build stays unminified. Compression and whitespace
removal cut the client JS from 14.0 MB to 10.5 MB and the backend
worker from 5.2 MB to 3.6 MB.

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

* [AI] Fix git-guard false positives on heredoc and multi -m commit messages (#8192)

* [AI] Fix git-guard false positives on heredoc and multi -m commits

The [AI]-prefix check extracted the commit message with a greedy
".*-m" sed pattern, which broke in two real-world cases:

- "git commit -m \"$(cat <<'EOF' ...)\"" — the pattern matched the
  command-substitution text on the first line and reported the message
  as "$(cat <<", blocking a correctly prefixed commit.
- Multiple -m flags — the greedy match grabbed the LAST -m (typically
  a Co-Authored-By trailer), so valid commits were blocked and a
  missing prefix on the subject could slip through if the last
  paragraph happened to start with [AI].

Replace the extraction: find the FIRST -m/--message via
shortest-prefix stripping (git takes the first -m as the subject),
detect the heredoc-in-command-substitution form and read the subject
from the heredoc body's first line, and otherwise check the inline
string as before. Editor/amend/file-based commits remain untouched,
as do the push, hook-skip, git-config and yarn-workspace guards.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* [AI] Add release notes

* [AI] Fail closed when heredoc detector matches without a body line

The heredoc pattern also matches single-line command substitutions
containing a here-string (e.g. <<< on one line), where reading the
"second line" produced an empty message that the final check treated
as "no inline message" and allowed — letting unprefixed subjects
through. Take the first non-blank heredoc body line instead (matching
git's leading-blank-line cleanup), and when no body line exists fall
back to inline parsing so a statically unknowable message is blocked
rather than waved through.

Found by CodeRabbit review on #8192.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

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

* fix import (#8201)

* Memoizing useRechartsAnimation() (#8150)

Fixes cash flow card text flashing

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

* prioritise category name matches over group matches in the category autocomplete (#8171)

* rank name matches above group matches

* make testable

* [AI] add test

* note

* :electron:  Fix desktop app workflow for windows builds (#8211)

* fix desktop app workflow for windows builds

* release notes

* append variable to github env so its available in other steps

* pin to 2022

* release notes

* Added Rose Pine themes to catalog (#8214)

* Added Rose Pine themes to catalog

* Added release notes

* Fixed misplaced commas

* Bump esbuild in the npm_and_yarn group across 1 directory (#8213)

Bumps the npm_and_yarn group with 1 update in the / directory: [esbuild](https://github.com/evanw/esbuild).


Updates `esbuild` from 0.25.10 to 0.25.12
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG-2025.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.10...v0.25.12)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-version: 0.25.12
  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>

* Budget averages now exclude incomplete months (#8114)

* add `includeIncomplete` to average templates

Default behavior is now to exclude incomplete months when calculating the average.

* add release note

* Remove `includeIncomplete` setting

* update menu actions

* fix lint

* exclude months before the first budgeted month

* use first activity month instead of first budget month

* update docs

* [autofix.ci] apply automated fixes

* use valid months in average e2e test

* Apply suggestion from @tim-smart

* round averages to integers

* align e2e tests with new behavior

* update vrt

* Update upcoming-release-notes/average-complete.md

---------

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

* Formula feedback improvements (#7989)

* [AI] Add FORMATNUMBER and FORMATCURRENCY functions to address TEXT() formatting limitations

Feedback from GitHub issue #5949:
- Users reported TEXT(value, "$#,##0.00") doesn't properly format numbers with thousands separators
- Users requested proper currency formatting for formula cards
- Tooltip examples had inconsistent quote usage (backticks vs no backticks)

Changes:
1. Added FORMATNUMBER() function for number formatting with thousands separators
   - Supports custom decimal places, thousands separator, and decimal separator
   - Example: FORMATNUMBER(1234567.89, 2) returns "1,234,567.89"

2. Added FORMATCURRENCY() function for currency formatting
   - Supports custom currency symbol, decimals, and separators
   - Handles negative values correctly
   - Example: FORMATCURRENCY(1234567.89, "$") returns "$1,234,567.89"

3. Fixed tooltip quote inconsistency
   - Removed backticks from formula examples in tooltips for consistency
   - All examples now use plain text without backticks

4. Updated function documentation in both queryModeFunctions.ts and transactionModeFunctions.ts

5. Added comprehensive tests for new formatting functions

Note: BALANCEONDAY() and LASTSYNCEDBALANCE() functions were not implemented as they
would require database access not available in the formula execution context. These
would be better suited as future enhancements with proper architecture support.

Co-authored-by: lelemm <lelemm@users.noreply.github.com>

* [AI] Add support for line breaks in formula card results

Feedback from GitHub issue #5949:
- User @Juulz reported that line breaks don't work in formula cards
- Requested ability to use CHAR(10) or CHAR(13) in CONCATENATE for multi-line displays
- Example: Displaying multiple totals on separate lines instead of one long line

Changes:
1. Added whiteSpace: 'pre-wrap' to FormulaResult span to preserve line breaks
2. Added textAlign: 'center' to keep multi-line text centered
3. Added wordBreak: 'break-word' to handle long words gracefully
4. Updated font size calculation to account for multi-line text:
   - Splits display value by line breaks
   - Calculates based on longest line length
   - Divides available height by number of lines to fit all lines

Usage:
Users can now use CHAR(10) in formulas to create line breaks:
=CONCATENATE("Total 1: ", value1, CHAR(10), "Total 2: ", value2, CHAR(10), "Total 3: ", value3)

This will display:
Total 1: 100
Total 2: 200
Total 3: 300

Instead of all on one line.

Co-authored-by: lelemm <lelemm@users.noreply.github.com>

* [AI] Remove unusable formula functions that require cell ranges

User requested cleanup of formula functions that don't make sense in Actual's context.
Actual uses named expressions (like QUERY results and transaction fields) rather than
spreadsheet-style cell ranges.

Removed from queryModeFunctions.ts:
- AVERAGEA, COUNT, COUNTA, COUNTBLANK, COUNTIF, COUNTIFS (use QUERY_COUNT instead)
- MAXA, MINA (kept MAX and MIN which work with individual values)
- SUMIF, SUMIFS, SUMPRODUCT, SUMSQ (use QUERY with filters instead)
- Statistical functions: MEDIAN, MODE, STDEV, STDEVP, VAR, VARP, PERCENTILE, QUARTILE, RANK
  (these require arrays of data which don't exist in our context)
- Lookup functions: VLOOKUP, HLOOKUP, INDEX, MATCH, LOOKUP
  (these require table arrays and cell references)
- ISREF (checks for cell references which don't exist)

Kept useful functions:
- CHOOSE (works with index and individual values)
- All math functions that work with individual numbers (SUM, AVERAGE, MAX, MIN, etc.)
- All text functions (CONCATENATE, UPPER, LEFT, etc.)
- All date functions (TODAY, YEAR, MONTH, etc.)
- All logical functions (IF, AND, OR, etc.)
- Query-specific functions (QUERY, QUERY_COUNT, BUDGET_QUERY, etc.)

Updated syntax highlighting in codeMirror-excelLanguage.tsx to match.

This cleanup makes the autocomplete suggestions more relevant and less confusing
for users working with Actual's formula system.

Co-authored-by: lelemm <lelemm@users.noreply.github.com>

* [AI] Add comprehensive integration tests for formula rules and formula cards

User requested integration tests with real database queries and complex nested formulas.
These tests go beyond unit testing HyperFormula to validate the full integration with
Actual's transaction rules and query system.

Formula Rule Integration Tests (36 tests):
- Basic formula operations with FORMATCURRENCY and nested IFs
- Text manipulation: CONCATENATE, UPPER, LEFT, RIGHT, TRIM, PROPER
- Math functions: ROUND, ABS, MAX, MIN, CEILING, SQRT, SUM, PRODUCT
- Date functions: YEAR, MONTH, DAY, DAYS, EOMONTH, WEEKDAY
- Logical functions: AND, OR, NOT, IFERROR, SWITCH
- New formatting functions: FORMATNUMBER, FORMATCURRENCY with various options
- Multi-line output with CHAR(10) for line breaks
- Information functions: ISNUMBER, ISTEXT, ISEVEN, ISODD, ISBLANK
- Error handling and validation
- Complex nested formulas combining multiple function types

Formula Card Integration Tests (14 tests):
- Single and multiple query integration
- Query results with FORMATCURRENCY and FORMATNUMBER
- Percentage calculations across queries
- Date-based query filtering
- Complex nested calculations with multiple queries
- Multi-line output with query results
- Error handling with empty queries and division by zero
- Running totals across accounts
- MAX/MIN operations with query results

Test Coverage:
- Every formula function type is tested in at least one integration test
- Tests use real database operations (insertAccount, insertPayee, insertCategory, etc.)
- Tests validate complex nested formulas with 3-4 levels of nesting
- Tests cover edge cases (empty results, division by zero, type mismatches)
- All 50 integration tests pass 

Note: Tests demonstrate proper usage patterns for users:
- Transaction amounts are in cents, divide by 100 for dollar display
- Rules must have conditions on 'imported_payee' or 'payee' to be indexed
- Query results are automatically converted from cents to dollars
- FORMATCURRENCY places negative sign before currency symbol (standard format)

Co-authored-by: lelemm <lelemm@users.noreply.github.com>

* Potential fix for pull request finding 'Unused variable, import, function or class'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>

* [AI] Make formatting functions respect app settings with locale-based fallbacks

User feedback: FORMATCURRENCY and FORMATNUMBER should use app currency and number
format settings instead of hardcoded defaults.

Implementation approach:
- Added async loadUserPreferencesForFormulas() function that queries preferences DB
- Caches preferences globally to avoid repeated DB queries
- Called on module initialization to load preferences once
- Custom functions access cached preferences synchronously (no reactor needed)

Settings hierarchy (priority order):
1. Explicit function parameters (if provided)
2. User's app settings (currency code, number format)
3. Locale-based defaults (inferred from user's language setting)

Locale-based defaults:
- en-GB → GBP (£) with comma-dot format
- de/fr/es/it/nl → EUR (€) with dot-comma format
- ja → JPY (¥) with comma-dot format
- en-IN/hi → INR (₹) with comma-dot format
- en-CA → CAD ($) with comma-dot format
- en-AU → AUD ($) with comma-dot format
- Default → USD ($) with comma-dot format

Number format patterns:
- comma-dot: 1,000.00 (US/UK/Canada)
- dot-comma: 1.000,00 (Europe)
- space-comma: 1 000,00 (France)
- apostrophe-dot: 1'000.00 (Switzerland)
- comma-dot-in: 1,00,000.00 (India)

Usage examples:
- FORMATCURRENCY(1234.56) → Uses app currency symbol and format
- FORMATNUMBER(1234.56) → Uses app number format
- FORMATCURRENCY(1234.56, "€") → Override with Euro symbol
- FORMATNUMBER(1234.56, 2, ".", ",") → Override with custom separators

Updated documentation to reflect that parameters are optional and use app settings.

All tests pass 

Co-authored-by: lelemm <lelemm@users.noreply.github.com>

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #7451

* [AI] Fix type errors after rebase with master

Co-authored-by: lelemm <lelemm@users.noreply.github.com>

* [autofix.ci] apply automated fixes

* Add release notes for PR #7451

* [AI] Fix category group filter support in formula card queries

BUG: When adding a Category Group filter to a Query Definition in formula
cards, nothing happened. Only category filters worked.

FIX: Updated extractCategoryConditions to include both 'category' and
'category_group' fields, and updated getCategoriesFromConditions to properly
expand category group conditions to their member categories using cat.group.

The fix follows the same pattern used in budget-analysis-spreadsheet.ts.

Co-authored-by: lelemm <lelemm@users.noreply.github.com>

* [AI] Exclude adm-zip from browser bundle to fix 'process is not defined' error

BUG: Browser console shows 'Uncaught ReferenceError: process is not defined'
at adm-zip.js when loading the app. This is caused by adm-zip (a Node.js-only
package) being incorrectly bundled into browser code.

FIX: Add adm-zip to optimizeDeps.exclude in Vite config to prevent it from
being bundled for browser environments.

Co-authored-by: lelemm <lelemm@users.noreply.github.com>

* [AI] HACK: Add global process polyfill in HTML to fix 'process is not defined' error

- Removed ineffective optimizeDeps.exclude for adm-zip
- Added inline script in index.html that runs before any module loads
- Ensures process global is available before CommonJS wrappers execute
- Polyfill includes process.env.NODE_ENV and mocked process.emit()
- Fixes ReferenceError in inflater.js/adm-zip when theme chunks load

Co-authored-by: lelemm <lelemm@users.noreply.github.com>

* [autofix.ci] apply automated fixes

* [AI] Fix 'process is not defined' by making db import dynamic in customFunctions

Root cause: customFunctions.ts is imported by client-side code, but had
a static import of '#server/db', causing entire server db module (and
transitive dependencies like adm-zip) to be bundled into browser.

Solution: Changed to dynamic import inside loadUserPreferencesForFormulas()
so server code is only imported when actually called (server-side only).

- Removed static 'import * as db from "#server/db"' from top of file
- Added dynamic 'const db = await import("#server/db")' inside function
- Removed HTML polyfill hack that was masking the root cause
- Browser clients can still use CustomFunctionsPlugin (falls back to locale defaults)

Co-authored-by: lelemm <lelemm@users.noreply.github.com>

* [AI] Fix 'process is not defined' by separating client and server code

Root cause: customFunctions.ts was imported by client-side code but
had server dependencies, causing db module (and transitive dependencies
like adm-zip) to be bundled into browser.

Solution: Split into two files to properly separate concerns:
1. customFunctions.ts - Plugin only (no server imports, safe for browser)
2. customFunctionsPreferences.ts - Server-only preferences loading (uses aqlQuery)

Changes:
- Created customFunctionsPreferences.ts with loadUserPreferencesForFormulas()
- Uses aqlQuery (Actual's custom SQL) instead of direct db imports
- Moved helper functions (getLocaleDefaults, getCurrencyFromLocale) to new file
- customFunctions.ts now only has setCachedUserPreferences() setter
- action.ts loads prefs and caches them at module init
- Client code can still import CustomFunctionsPlugin safely

Co-authored-by: lelemm <lelemm@users.noreply.github.com>

* [AI] Clean up feedback-related comments from code

Removed explanatory comments referencing user feedback:
- Removed feedback comments from customFunctions.ts
- Removed feedback comments from formula-action.test.ts
- Removed feedback comments from formula-action-integration.test.ts
- Removed feedback comment from FormulaResult.tsx

Code functionality remains unchanged, only comments were cleaned up.

Co-authored-by: lelemm <lelemm@users.noreply.github.com>

* Improve formula autocomplete popover scrolling

* [autofix.ci] apply automated fixes

* renamed MD for the new PR

* wording

* code cleanup

* [autofix.ci] apply automated fixes

* more cleanup

* comment on a workaround to make scroll work inside codemirror not on focus/under a react aria popover

* [autofix.ci] apply automated fixes

* fixing FORMATCURRENCY and FORMATNUMBER to use proper send() method

* resolving issue when bundling the application

* removed useless re-export

* code review feedbacks

* retrigger actions

* Adjusted minimum formula card to 1x1 and allows the title to be removed from the dashboard

* 🔖 (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>

* [AI] Improve Balance Forecast captions (#7957)

* [AI] Add visible low point helper for forecasts

* [AI] Show ending balance in forecast report

* [AI] Show ending balance on forecast card

* [AI] Update balance forecast report snapshots

* [AI] Add release note for forecast captions

* [AI] API: improve messaging for version/schema mismatch errors (#8026)

* [AI] Improve messaging for API version/schema mismatch errors

When an outdated client or @actual-app/api connects to a budget whose
data uses a newer database schema, sync-apply fails with a cryptic
"no such column" error that surfaced as a generic download/sync error.

Add a version-mismatch message in getDownloadError/getSyncError, gated on
the actual SQLite schema-error signature (no such column/table) so other
invalid-schema failures keep their generic messaging and otherwise
compatible API versions remain usable.

* [AI] Set release note author to MatissJanis

* Add release notes for PR #8026

* [AI] Address review: use Vitest globals and remove duplicate release note

- Drop the explicit `vitest` import in errors.test.ts and rely on the
  configured test globals (per CodeRabbit review).
- Remove the duplicate 8021.md release note, keeping the PR-numbered
  8026.md with the clearer description.

* [AI] Avoid i18n in loot-core for schema-mismatch message

Return the schema-mismatch error as a plain string instead of going
through i18next, and drop the i18next setup from the test. loot-core
does not depend on i18n, and this message is surfaced verbatim through
the programmatic API.

* [AI] Remove unnecessary code comments

* [AI] Add missing-meta test for getDownloadError

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* Fix typo in Budget Automation math expression (#8035)

* Fix typo in Budget Automation math expression

* Remove release note

---------

Co-authored-by: youngcw <calebyoung94@gmail.com>

* [AI] docs: update reset-password instructions (#7950)

* [AI] docs: update reset-password instructions

Clarify the current host-side command and keep the source-checkout workflow documented.

Closes #7943

* [AI] docs: keep npm reset path alongside actual-server

* Fix CSV import settings reset when skipping end lines (#7827)

* [AI] fix(api): prevent navigator crash when loading @actual-app/api in Node.js (#8037)

* [AI] fix(api): prevent navigator crash when loading @actual-app/api in Node.js

Problem
-------
@actual-app/api 26.5.0 and 26.6.0 throw "ReferenceError: navigator is not
defined" on import in Node.js environments. The loot-core shared bundle
includes three platform variants:

  platform.ts      – browser (references navigator at module load time)
  platform.electron.ts – Electron/desktop (uses Node.js os module)
  platform.api.ts  – Node.js API (safe constants only)

The API vite build uses `resolve: { conditions: ['api'] }` to pick the right
variant via the loot-core package imports map (#shared/platform). However,
months.ts was importing via a bare relative path (`./platform`) instead of the
imports-map alias (`#shared/platform`). Vite's conditions-based resolution does
not apply to relative imports, so the build fell back to platform.ts (the
browser variant), bundling navigator references that crash at load time.

Fix
---
Change months.ts to import `#shared/platform` instead of `./platform`. This
goes through the loot-core package imports map, which the vite `api` condition
resolves to platform.api.ts — the Node.js-safe variant.

platform.api.ts was already added to the repo alongside this condition
infrastructure (in the same 26.6.0 release) but the months.ts import was
missed, leaving the crash in the published package.

Testing
-------
Added packages/api/platform.test.ts, which imports @actual-app/core/shared/
platform under the `api` vite condition and asserts that isBrowser, isPlaywright
and isIOSAgent are all false. This test:

  - Fails if platform.api.ts is deleted (module not found)
  - Fails if platform.api.ts is modified to reference navigator (ReferenceError)
  - Fails if the api vite condition is removed, falling back to platform.ts
    which throws ReferenceError at import time in Node.js

Verified by Kyle Slattery against a live Actual Budget instance via the
actual-flow integration tool (https://github.com/lunchflow/actual-flow).

* Add release note

* Update upcoming-release-notes/8037.md

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

* Delete packages/api/platform.test.ts

---------

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

* [AI] Move i18n usage outside of loot-core (#8027)

* [AI] Move i18n usage outside of loot-core

loot-core should be platform-agnostic and free of i18n concerns. This
moves all user-facing translated string helpers (rule/schedule labels,
error formatters) into desktop-client's #util/{rule,schedule,error}
modules, while keeping the underlying logic in loot-core.

- Headless @actual-app/api error formatters in loot-core now return
  plain English strings (the API has no i18n runtime).
- The persisted "Unknown" institution name and the platform storage
  alert use plain strings.
- Removed the unused i18next dependency from loot-core.

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

* [AI] Address review feedback on i18n-out-of-loot-core PR

- Wrap switch default arms in blocks to satisfy noSwitchDeclarations
  (desktop-client and loot-core getDownloadError)
- getSecretsError: return a localized generic message instead of leaking
  the raw backend error token
- getRecurringDescription: always separate the weekend annotation so it no
  longer renders as "Monday(after weekend)"; add test coverage
- IndexedDB quota error: surface a typed 'indexeddb-quota-error' event from
  loot-core and present the localized alert in desktop-client instead of a
  hard-coded English string
- Persist a null bank name (instead of English "Unknown") when the provider
  reports no institution, and render a localized fallback in the bank sync UI

---------

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

* [AI] Fix Balance Forecast chart colors with custom themes (#8031)

* [AI] Use report theme colors for Balance Forecast chart lines

* [AI] Add release notes for Balance Forecast custom theme fix

* fix react compiler on Windows (#8049)

* hmm..

* note

* [Docs] Revise fly.io deployment instructions for nightly builds (#8059)

* Revise deployment instructions for nightly builds

Updated instructions for deploying unstable versions of Actual.

* Clarify fly deploy commands in installation guide

Updated deployment commands for clarity and added example for nightly version.

* [autofix.ci] apply automated fixes

---------

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

* [AI] Scope Electron update to v41 (#8044)

* Initial plan

* Bump desktop Electron runtime to v42.3.0

* Add release notes entry for PR #8044

* Change author from Copilot to MikesGlitch

Updated author information in release notes.

* Update node-abi mappings to support Electron 42 ABI detection

* Address validation feedback for Electron update PR

* Add better-sqlite3 Electron 42 patch

* Update lockfile for better-sqlite3 patch

* Remove better-sqlite patch protocol and target Electron 41

* Align release note with Electron 41.7.1 change

* Update better-sqlite3 ranges to ^12.8.0

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Michael Clark <5285928+MikesGlitch@users.noreply.github.com>
Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>

* [Enhancements] Removed regex from excel formula execution (#7990)

* Removed regex from excel formula execution

* [autofix.ci] apply automated fixes

* Surface error for BUDGET_QUERY

* code review

---------

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

* [AI] Fix balance forecast report Y-axis label clipping (#8030)

* [AI] Fix balance forecast report Y-axis label clipping

* [AI] Add release notes for #8030

* docs: add actual-bench to community projects (#8045)

* docs: add actual-bench to community projects

* [autofix.ci] apply automated fixes

* docs: add release note for actual-bench community entry

* [autofix.ci] apply automated fixes

* fix: address coderabbit comments

* fix: remove upcoming release notes

---------

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

* [AI] Add Sort A to Z / Sort Z to A options to budget category groups (#7831)

* [AI] Add Sort A to Z / Sort Z to A options to budget category groups (#7831)

* Simplify release notes and apply coderabbitai suggestions

* [AI] fix(api): trigger SimpleFin batch sync with a single account (#8052)

The condition required more than one SimpleFin account before running
batch sync, causing a single SimpleFin account to be skipped entirely.

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

* add pikapods desktop warning (#8061)

* add pikapods desktop warning

* better link

* add to spelling list

* more spelling

* [AI] Fix bug when linking accounts (#8006)

Fixes a bug that when selecting Link Account from the account page one
has to reselect the same account in the modal even though its
prepopulated.

Co-authored-by: Gemini <noreply@google.com>

* add MXN peso (#8060)

* ♻️ remove unnecessary playwright container pulls to speed up CI (#8067)

* don't use playwright where it's not needed

* note

* [AI] Add auto-generated Upcoming Release docs page (#8065)

* [AI] Add auto-generated Upcoming Release docs page

Adds a docs page that always reflects the current contents of the
repo-root upcoming-release-notes/ directory — the changes that have been
merged but not yet published in a stable release (i.e. what ships in the
nightly/edge builds).

- Extract parseReleaseNotes/formatNotes from the release-notes generator
  into the shared ci-actions util so both the release generator and the
  docs build reuse the same formatting.
- Add a docs build script that regenerates docs/upcoming-release-notes.md
  on every start/build, with an intro explaining the notes are unreleased
  and how to try them, plus the categorized notes list.
- Wire the generator into the docs start/build scripts, add a sidebar
  entry next to Release Notes, and gitignore the generated page.

https://claude.ai/code/session_01MuY9Phome8uJKH51HbrsQw

* [AI] Simplify upcoming-release-notes generator

- Declare @actual-app/ci-actions as a workspace dependency and import the
  shared util by package name instead of a relative cross-package path, so
  the dependency is registered in the workspace graph.
- Resolve the repo root once via new URL() and drop the dirname import.
- Use a ternary for the page body instead of let + if/else.

https://claude.ai/code/session_01MuY9Phome8uJKH51HbrsQw

* Add release notes for PR #8065

* [AI] Remove edge references, keep only nightly in docs

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

* Update packages/docs/scripts/generate-upcoming-release-notes.mjs

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

---------

Co-authored-by: Claude <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>
Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>

* Bump the npm_and_yarn group across 2 directories with 1 update (#8071)

Bumps the npm_and_yarn group with 1 update in the / directory: [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router).
Bumps the npm_and_yarn group with 1 update in the /packages/desktop-client directory: [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router).


Updates `react-router` from 7.13.1 to 7.14.2
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router@7.14.2/packages/react-router)

Updates `react-router` from 7.13.1 to 7.14.2
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router@7.14.2/packages/react-router)

---
updated-dependencies:
- dependency-name: react-router
  dependency-version: 7.14.2
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: react-router
  dependency-version: 7.14.2
  dependency-type: direct:development
  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>

* When resetting SimpleFIN credentials, parse error and error_code if response body exists (#8068)

* Initial commit

* Add release note

* Update release note file name to match PR number

* Flip operands when checking for 2xx

* Release notes grammar

* Better release note for non-devs

* Simplify to naive solution of checking on nullable response

* Hiding tags and tag table bulk actions (#7919)

* [AI] Make TypeScript work in test files across packages

Match the CRDT package's tsconfig pattern in loot-core, desktop-client,
api, and desktop-electron so test files participate in the project graph
(IDE intellisense, project-wide typecheck) while production builds still
emit clean declaration files.

- Remove test-file exclusions from each package's main tsconfig
- Add tsconfig.build.json for loot-core and api with test exclusions,
  used by the build scripts
- Add e2e/tsconfig.json for desktop-client and desktop-electron with
  Playwright types
- Fix latent type errors in test files now caught by typecheck
- Disable typescript/unbound-method for test files (mock matcher pattern)

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

* [AI] Address review feedback on test type fixes

- goal-template.test.ts: extract amounts to typed locals so single-value
  assertions no longer compare against unknown
- category-template-context.test.ts: replace `as unknown as DbCategory`
  double-cast with a fully-typed object using `satisfies DbCategory`
  (the previous mock had `is_income: true` which doesn't match the
  `1 | 0` shape the cast was hiding)
- api/tsconfig.build.json: broaden test exclude pattern to `**/*.test.ts`

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

* [AI] Drop tsconfig.build.json for loot-core and api

The build-config indirection was incomplete protection: typecheck
(`tsgo -b` against the main tsconfig, which now includes test files)
already emits `*.test.d.ts` into `@types/`, and the build step does
not clean before re-emitting. The same is observable in crdt's
`dist/`, which currently contains test declarations on disk.

What actually keeps test declarations out of the npm tarball is the
`files` field in package.json — and loot-core already uses that
mechanism for source files (`\!src/**/*.test.ts`). Extending the same
pattern to `@types/` is more direct than maintaining a duplicate
tsconfig that doesn't reliably do its job.

- Delete loot-core/tsconfig.build.json; revert build to `tsgo -b`;
  add `\!@types/**/*.test.d.ts*`, `\!@types/**/__tests__/**`,
  `\!@types/**/__mocks__/**` to `files`.
- Delete api/tsconfig.build.json; revert build to
  `vite build && tsgo --emitDeclarationOnly`; add
  `\!@types/**/*.test.d.ts*` to `files`.

Verified: `yarn pack --dry-run` excludes all test declarations from
both packages while production declarations still pack (428 .d.ts
files for loot-core, methods.d.ts for api).

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

* first pass at implementation

* finished implementing what I feel would be a good user experience

* padding update

* added ability to use tab to select

* release notes

* updated release notes

* auto highlights first entry

* removed debug info

* linting formatting

* updated behavior with tags in the middle of stuff

* minor improvements to ux

* extracted TagAutocomplete functionality into its own file

* rewrote TagAutocomplete using react-aria-components from scratch

* linting

* restored old autocomplete since I'm no longer using it

* linting

* capped results at 10

* bugfix

* used tag css hook instead of notes tag formatter

* added mobile support

* fixed bug where inserting a tag at the end of the note wouldn't fire onChange handler

* fixed bug where note would hover even though field was not focused

* fixed bug with currentWord detection

* some more minor UI bugs with the mobile UI

* removed log statement

* added aria-label

* [autofix.ci] apply automated fixes

* name to Input

* updatedt ests

* fixed tests

* whitespace adjustment

* removed debug from test

* added tests and made a few fixes

* tried to fix vrt

* some new hooks to use

* removed log statement

* removed input from useCursorPosition

* input ref value hook now triggers on mount

* add extra padding

* hide browser autocomplete when our popup is shown to avoid doubles

* useFilteredTags hook and adjustments to mobile tag UI

* switched from opacity to height transitioning

* sorted filtered tags by startsWith first

* updated highlighting logic for mouseover

* [autofix.ci] apply automated fixes

* fix tag ordering

* button type

* [autofix.ci] apply automated fixes

* release note update

* release note to trigger ci

* reverted db change

* Revert "reverted db change"

This reverts commit 24ce5450e5e4261e77fe5056620c240da22869a3.

* can now filter by tags without hashes

* extracted a method and added a new hasAnyTags rule

* added hasAnyTag, still need to figure out why tests are failing

* fixed unit tests

* release notes

* fixed based on coderabbit

* tag.toLowerCase()

* variable scope fix

* fixed some focus-related bugs

* allow creating tags in the dropdown for desktop

* fixed focus issues with useCursorPosition

* added Create Tag functionality to mobile

* tests and added some length clipping as qol

* can now use enter to submit new tag properly

* centralized overflow CSS for tags

* centered the text horizontally

* added textnowrap

* release notes

* linting

* fixed issue with async keydown handler

* specified only to use new tag css for the autocomplete stuff

* forgot to add new option to tag table

* removed Trans

* started working on multiselect

* multiselect is now functional

* finished multiAutocomplete implementation

* [autofix.ci] apply automated fixes

* removed double input

* added multi autocomplete to rule editor

* adjusted padding for TagMultiAutocomplete

* adjusted hover cursor

* release notes

* fixed rule conditions

* added tag styling to filter multiselect

* spacing adjustment

* release notes for retriggering build

* added hidden migration

* added hidden to models

* added tags-hide-all and tags-unhide-all actions

* added hide and unhide tags actions

* added SelectedTagsButton

* added UI elements for hiding/showing tags

* release notes

* removed delete from ManageTags

* added icons to menu

* coderabbit

* coderabbit

* updated mutations for translations

* coderabbit

* removed not null requirement

* attempted to fix tests?

* removed duplicate filterTags

* [autofix.ci] apply automated fixes

* updated release notes

* release note update

* updated migration timestamp

* moved tags menu button

---------

Co-authored-by: github-actions[bot] <matiss@mja.lv>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Alec Bakholdin <alecbakholdin.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* automation UI: improve scaling (#8080)

* improve scaling on high zoom levels

* note

* automation UI: rework balance cap and fix error when clearing fields (#8082)

* fix safeNumber error

* rework balance cap UI to be more readable

* note

* add explanation to balance cap

* Bump react-router in the npm_and_yarn group across 1 directory (#8072)

Bumps the npm_and_yarn group with 1 update in the / directory: [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router).


Updates `react-router` from 7.14.2 to 7.15.0
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router@7.15.0/packages/react-router)

---
updated-dependencies:
- dependency-name: react-router
  dependency-version: 7.15.0
  dependency-type: direct:development
  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>

* [AI] feat(currencies): add currency-precision-aware helpers (#8064)

* [AI] feat(currencies): add getDecimalPlaces/getCurrencyPrecisionMultiplier helpers, encodeAmount, currency-aware integerToCurrencyWithDecimal

* Fix typo in release note

* Use it.each in unit tests

* Rename encodeAmount and replace its body; remove getCurrencyPrecisionMultiplier

* [AI] Remove AI-generated release notes workflow and support custom filenames (#7963)

* [AI] Relax release-notes filename convention

Allow descriptive filenames (e.g. add-payee-autocomplete.md) for
upcoming release notes instead of requiring {PR_NUMBER}.md.

The PR number was only used to build the [#1234](.../pull/1234) link in
the generated changelog. The generation script now resolves the PR per
file by finding the commit that added it (git log --diff-filter=A
--follow) and querying GitHub's commits/{sha}/pulls endpoint. Numeric
filenames remain valid via a fast path.

Also retires the CodeRabbit/OpenAI-triggered auto-creator workflow,
which existed to guess summaries and stamp the PR-numbered filename;
neither is needed once contributors can pick a slug up front.

* Add release notes for PR #7963

* [AI] Use execFile to look up release-note add-commits

resolvePrNumber() interpolated a path from fs.readdir into a shell-
evaluated `git log` command. Switch to execFile with an argv array so
filenames containing shell metacharacters can't break out of the
intended command.

* [AI] Drop duplicate release note added by the to-be-removed auto-creator

The CodeRabbit-triggered workflow ran one last time from master before
this PR removes it, and committed upcoming-release-notes/7963.md
duplicating the existing relax-release-notes-filenames.md entry. Keep
the slug-named note since it exercises the new flexible-filename code
introduced in this PR.

* [AI] Don't fail release-notes generation on transient API errors

fetchPrForCommit() handled non-OK HTTP responses but let fetch() and
res.json() exceptions propagate, so a single network blip or malformed
response would abort the whole release-notes generation. Wrap them in a
try/catch that logs and returns null, matching the pattern already used
in resolvePrNumber for execFile errors.

* [AI] Address CodeRabbit full-review nits

- bin/release-note-generator.ts: tighten slug regex to reject trailing
  and consecutive dashes (matching slugify output), and make slugify
  fall back to "untitled" so an all-non-alphanumeric input can't
  produce a hidden ".md" filename.
- packages/ci-actions/bin/release-notes-check.mjs: switch to execFile
  for git fetch/diff, matching release-notes-generate.mjs and removing
  shell interpolation of BASE_REF.
- packages/ci-actions/bin/release-notes-generate.mjs: explicitly check
  GITHUB_REPOSITORY before splitting, consistent with the other env
  var guards a few lines below.
- upcoming-release-notes/relax-release-notes-filenames.md: fix author
  casing (matiss -> MatissJanis) so changelog attribution is correct.

* [AI] Reject empty release-note bodies

content.trim().split('\n').length === 1 is true for the empty string
(''.split('\n') returns [''] of length 1), so blank notes slipped past
the single-line check. Reject empty trimmed content explicitly.

* [AI] Resolve PR numbers from commit subjects instead of GitHub's API

parseReleaseNotes was doing one GitHub API round-trip per non-numeric
release-note file. That scales badly for release generation, and the
docs site's generate-upcoming-release-notes.mjs runs on every docs
build — so contributors were hitting the GitHub API (or failing 401
without a token) every time they previewed docs locally.

actualbudget squash-merges every PR through the GitHub UI, which
appends "(#NNNN)" to the resulting commit subject. So:

  git log -1 --format=%s -- <path>

plus a /\(#(\d+)\)\s*$/ match recovers the PR number without touching
the network, and works offline / without a GITHUB_TOKEN.

Side effect: switched from "first commit that added this file" to
"most recent commit touching this file", which is what we actually
want — when a file is renamed (e.g. 7907.md -> 7954.md when a wrong
PR number is corrected), the changelog should point at the rename PR,
not the original add. The SHA->PR cache and fetchPrForCommit go away;
no longer needed.

If a file's subject doesn't match (direct push to master, manually
rewritten subject), the entry still emits without a PR-link prefix —
same graceful degradation as before.

* [AI] Pin PR-number lookup to each note's add commit

resolvePrNumber was using git log -1 with no diff filter, returning
the latest commit that touched the file. That's fine until someone
edits an existing release note in a follow-up PR (typo fix, author
correction) — at which point the changelog entry would suddenly
point at the wrong PR.

Add --diff-filter=A so the lookup pins to the commit that originally
added the path. Empirically verified against this repo: renames keep
working (git records a rename as A at the new path when --follow
isn't used), and the edit case now correctly returns the original
add commit instead of the most recent touch.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* [AI] Add GitHub workflow to block PRs with "do not merge" label (#8090)

* [AI] Add CI job that fails when 'do not merge' label is set

* [AI] Rename do not merge check job to "Block PRs with do not merge label"

* [AI] Rename do not merge workflow to "Block PRs with do not merge label"

* [AI] Add release notes for do not merge CI check

* Update 8090.md

---------

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

* [AI] Persist bank sync status in the accounts table (#8017)

* [AI] Persist bank sync status in the accounts table

Extracts the persisted bank-sync-status half of #7782 (by @jcam): adds a
bank_sync_status column to the accounts table, writes the status
(pending/ok/failed/reauth-required/attention-required) during native bank
syncs, and reads the persisted value in the UI (sidebar, account header,
mobile account pages, and the sync banner) instead of ephemeral Redux state.

https://claude.ai/code/session_014MXn6Qi8skvFL9kXw7ctFD

* [AI] Add release note for persisted bank sync status

https://claude.ai/code/session_014MXn6Qi8skvFL9kXw7ctFD

* [AI] Persist only durable failure states, keep pending ephemeral

Addresses review feedback: a transient 'pending' write could get stuck in
the DB (and propagated via CRDT) if the app closed mid-sync. The DB now only
stores durable result states (ok / failed / reauth-required / attention-
required); pending stays in ephemeral Redux state (account.accountsSyncing)
as before. The UI reads only the persisted failure state via isAccountFailedSync,
and the BankSyncStatus banner reverts to the ephemeral in-flight count.

Also extracts a persistBankSyncError helper and inlines a redundant wrapper.

https://claude.ai/code/session_014MXn6Qi8skvFL9kXw7ctFD

* [AI] Un-export AccountSyncSidebar, refine release note

- AccountSyncSidebar is internal again; drop the export-only-for-test and its
  test (the failure logic is already covered by syncStatus.test.ts).
- Credit MatissJanis in the release note and make it user-centric.

https://claude.ai/code/session_014MXn6Qi8skvFL9kXw7ctFD

* [AI] Publish account changes in bank-sync sync-events

handleSyncResponse writes accounts.last_sync and accounts.bank_sync_status,
but the sync-events following it only declared tables: ['transactions'], so
clients only refetch useAccounts when the event includes 'accounts'. Add
'accounts' to the six emissions that follow handleSyncResponse (the four link
flows and the two batch-sync finals) so the persisted account changes are
published everywhere, not just on the triggering client.

https://claude.ai/code/session_014MXn6Qi8skvFL9kXw7ctFD

* [AI] Re-timestamp bank_sync_status migration after master

A newer migration (add_tags_hidden) landed on master, so bump the
bank_sync_status migration's timestamp to remain the latest and satisfy the
migration-ordering check.

https://claude.ai/code/session_014MXn6Qi8skvFL9kXw7ctFD

---------

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

* Remove environment from release notes workflow (#8091)

* Remove environment from release notes workflow

Removed the environment specification from the release notes workflow.

* Update release-notes CI job configuration

Removed the unnecessary 'environment' from the release-notes CI job.

* [AI] Enable "do not merge" check on merge queue events (#8092)

* [AI] Run do-not-merge check on merge_group events

The block-do-not-merge workflow only triggered on pull_request events, so
it never ran in the merge queue and could not be made a required check.
Adding the merge_group trigger lets the job run and pass in the queue
context (where pull_request labels are absent), allowing it to be required.

* [AI] Add release notes for merge queue do-not-merge fix

---------

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

* Make payee & categories searching to be case-insentive (#8079)

* make payee & categories searching to be case-insentive

* add release notes

* [autofix.ci] apply automated fixes

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #8079

* update release-note

* [autofix.ci] apply automated fixes

* fix release-note format

---------

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

* Add files via upload (#8096)

* [AI] Fix incorrect schedule value when saving full amount (#8085)

If one has a yearly schedule and the budget template uses "Cover each
occurence when it occurs", it still showed a value for the yearly
schedule even though it's not the month where the schedule occurs.

Co-authored-by: Gemini <noreply@google.com>

* feat: add SharedArrayBuffer warning indicator in title bar (#7922)

* feat: add SharedArrayBuffer warning indicator in title bar

* improve styling

* add warning label

* fix typo

* review comments fixed

* use link instead of button

* fix tooltip styles

* add hover effect

---------

Co-authored-by: Awais Saeed <dev@awaissaeed.com>

* [AI] Remove experimental status from Actual CLI (#8102)

* [AI] Move actual-cli out of experimental status

* [AI] Rename CLI release note to a feature slug

---------

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

* add warning when schedule/save by automation priorities don't match (#8088)

* add warning when schedle/save by automation priorities don't match

* note

* italicise

* feat(reports): scoped ErrorBoundaries for individual report routes (#7658)

* feat(reports): scoped ErrorBoundaries for individual report routes (#7391)

Wrap each route in ReportRouter for NetWorth, CashFlow, Spending, etc.
in ErrorBoundary with FeatureErrorFallback. Matches the pattern used
for /rules routes in FinancesApp.tsx so a render error in a single
report no longer takes down the whole app.

* chore: rename release note to PR number

* refactor(reports): extract withReportBoundary helper to remove route boilerplate

Each report route was wrapping its element in the same ErrorBoundary block
with FeatureErrorFallback and resetKeys={[location.pathname]}. Pull that
into a small withReportBoundary(element) helper inside ReportRouter so
each route is one line again.

Behavior is unchanged. The ErrorBoundary, FallbackComponent, and resetKeys
are identical to before; the helper just closes over location.pathname.

Addresses review feedback from @joel-jeremy and the CodeRabbit nitpick on
the same file (a custom Route component is not viable since react-router
v6's <Routes> requires literal <Route> children, but a helper function
gives the same boilerplate reduction).

* refactor(reports): hoist withReportBoundary to ReportBoundary component

Move the per-route error-boundary helper out of ReportRouter as a
module-scoped component. ReportBoundary calls useLocation() internally so
each call site stays clean. Addresses review feedback.

---------

Co-authored-by: github-actions[bot] <matiss@mja.lv>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* [AI] Add configurable average ranges to Monthly Spending report (#7920)

* Add configurable average ranges to Monthly Spending report

* Delete upcoming-release-notes/temp.md

* Update packages/desktop-client/src/components/reports/spendingAverageRange.test.ts

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

* Fix spending average range test import

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #7920

---------

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

* [AI] Fix custom report transfer drilldown filters (#8001)

Co-authored-by: youngcw <calebyoung94@gmail.com>

* Make tag search case-insensitive (#8093)

* makes tags case insensitive

* release notes

---------

Co-authored-by: Alec Bakholdin <alecbakholdin.com>

* docs: add Hostim to Additional Installation Options (#8066)

* docs: add Hostim to community install options

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

* docs: add release note for #8066

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

* docs: drop release note (not required for docs-only change)

---------

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

* [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>

* [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>

* [AI] Disable ESLint in CodeRabbit configuration (#8110)

* [AI] Disable ESLint in CodeRabbit config

* [AI] Add release notes for CodeRabbit ESLint change

* [AI] Rename release notes to match PR number 8110

* [AI] Rename release notes file to a slug

---------

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

* 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>

* automation UI: add mobile support 📱 (#8099)

* generalise out to hooks

* add mobile modal

* wire up mobile

* tidy up field styling on mobile

* [AI] add mobile automations e2e test

Covers opening the editor from the mobile category menu, the drill-down
list/editor navigation, touch-sized fields, and that Cancel dismisses it.

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

* note

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #8099

* fix date picker on schedule page

* fix conflict

* fix cramped space in save by automation

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

* [Docs] Starting at the top, Introduction & Vision (#8083)

* Enhance introduction and navigation details in docs

Revised wording for clarity and improved navigation instructions in the documentation.

* Update vision.md for clarity and formatting

Refine language for clarity and consistency, and update formatting for better readability.

* Improve formatting of third-party use section

Reformatted the third-party use section for clarity.

* Revise documentation introduction and navigation details

Updated the introduction and navigation instructions for clarity and conciseness.

* Revise documentation overview and navigation details

Updated documentation for clarity and navigation instructions.

* Refine Local-first explanation in vision.md

Removed redundant text in the Local-first section.

* [autofix.ci] apply automated fixes

* Revise contribution guidelines in documentation

Updated links to contributing guides and documentation readme.

* Revise headings and improve hyphenation in vision.md

Updated headings and corrected hyphenation in vision.md.

* [autofix.ci] apply automated fixes

* Fix wording in contribution guidelines section

Corrected the wording from 'documentation standard' to 'documentation standards' for clarity.

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>

* [AI] Fix bank sync deadlock from nested mutator (#8111)

* [AI] Fix bank sync deadlock from nested mutator

PR #8017 wrapped the accounts-bank-sync and simplefin-batch-sync handlers
in mutator(). runHandler runs mutators via runMutator, which is
sequential() — only one mutator may run at a time and re-entrant calls are
queued. These handlers call syncAccount -> reconcileTransactions, which
already invokes runMutator internally. The inner runMutator gets queued
behind the still-running outer one while the outer awaits it, deadlocking
forever. The bank sync send() never resolves, so the mobile spinner spins
indefinitely.

Remove the mutator() wrapping (restoring pre-#8017 registration). The DB
writes for bank_sync_status/last_sync already ran outside a mutator before
#8017, and transaction reconciliation manages its own mutator context.

* [AI] Add regression tests for bank sync mutator deadlock

Guards against re-wrapping the accounts-bank-sync / simplefin-batch-sync
handlers in mutator(), which deadlocks because the sync internally calls
runMutator (reconcileTransactions) and runMutator is sequential.

- An invariant test asserts neither handler is a mutating method.
- A behavioral test drives accounts-bank-sync through runHandler with a
  syncAccount mock that performs its own runMutator, and fails via a
  timeout if the nested mutators deadlock.

* [AI] Simplify deadlock regression test

Collapse the two-step Promise.race into a single race result and clear the
timeout timer on the success path so it doesn't linger after the test.

* [AI] Remove explanatory comments from deadlock regression test

* [AI] Add release note for bank sync deadlock fix

* Update packages/loot-core/src/server/accounts/app-bank-sync.test.ts

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

* Update packages/loot-core/src/server/accounts/app-bank-sync.test.ts

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>

* [AI] Fix desktop settings resetting after an interrupted update (#8101)

* [AI] Fix desktop settings resetting after an interrupted update

Desktop global prefs (language, theme, etc.) live in a single
global-store.json that was written non-atomically and silently reset to
{} whenever it failed to parse. An app update's forced quit can truncate
the file mid-write, so the next launch wiped every global pref - the
language reverted to "System default" (the OS language).

- Write the store via a unique temp file + atomic rename so it is never
  left half-written.
- Serialize writes so a slower older write can't land after and clobber a
  newer one.
- Back up an unparseable store to .corrupt and log loudly instead of
  silently discarding the user's preferences.

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

* [AI] Reject non-object global-store contents during load

JSON.parse could return a non-object (null, an array, a primitive) for
parseable-but-wrong-shaped contents, which would then break store[key]
access. Validate the parsed value is a plain object and route anything
else through the existing back-up-and-default recovery path.

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

---------

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

* :electron: Removed new shared array buffer warning on Electron (#8118)

* removed shared array buffer warning one electron

* release notes

* cleanup a bit

* Enhance translation contribution guidelines in i18n.md (#8113)

* Enhance tra…

* ⬆️ 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>

* [AI] Migrate CI workflows to Depot-managed runners (#8205)

* [AI] Migrate CI workflows to Depot-managed runners

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* [AI] Rename release notes to match PR number

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* [AI] Empty commit to trigger CI

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* [AI] Keep VRT jobs on GitHub-hosted runners

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* [AI] Use 4-vCPU Depot Windows runners for Electron builds

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* [AI] Use 4-vCPU Depot Ubuntu runners for Electron builds

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* [AI] Use 4-vCPU Depot runners for functional e2e tests

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* [AI] Scale Electron build runners to 8 vCPUs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* [AI] Scale Ubuntu Electron builds to 4 vCPUs and move Windows builds back to GitHub runners

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* [AI] Move Microsoft Store publish back to GitHub Windows runner

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* [AI] Add actionlint config for Depot runner labels

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com>

* Start desktop split transactions with two splits instead of one (#8234)

* Start desktop split transactions with two splits instead of one

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #8234

---------

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

* show the bank sync error banner on the accounts based on persisted status (#8170)

* persist more bank sync error status'

* wire up the UI

* note

* 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>

* Bump joi in the npm_and_yarn group across 1 directory (#8239)

Bumps the npm_and_yarn group with 1 update in the / directory: [joi](https://github.com/hapijs/joi).


Updates `joi` from 17.13.3 to 17.13.4
- [Commits](https://github.com/hapijs/joi/compare/v17.13.3...v17.13.4)

---
updated-dependencies:
- dependency-name: joi
  dependency-version: 17.13.4
  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>

* Use ± symbol for 'between' schedule amounts and improve tooltip text (#8222)

* Add between symbol and update tooltip text

* Add release note

* [autofix.ci] apply automated fixes

* Fix i18n strings and args

* Do not translate amount only

---------

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

* [AI] Replace archived check-spelling action with typos for docs spell-check (#8249)

* [AI] Replace archived check-spelling action with typos for docs spell-check

The check-spelling/check-spelling action was archived upstream and shipped a
self-disabling "secpoll" kill-switch after a maintainer-account compromise
(2026-06-16), causing the Check Spelling (Docs) workflow to fail fatally with
a security-advisory error. There is no patched release to bump to.

Replace it with crate-ci/typos, which is actively maintained and runs entirely
from the checked-out tree (no PR-comment or bot-apply machinery, so the workflow
only needs contents: read):

- Rewrite .github/workflows/docs-spelling.yml to run crate-ci/typos, dropping
  the check-spelling comment/apply jobs and the pull_request_target trigger.
- Replace the check-spelling config files with typos.toml: scope to
  packages/docs, exclude auto-generated release notes and binary assets, and
  allowlist words typos otherwise mis-flags (HSA, Lage/lage, BA).
- Fix genuine typos surfaced in current docs (schedules.md, paying-in-full.md).
- Update the contributing guide and docs-writing skill to describe the new
  allowlist location.

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

* [AI] Add docs-spelling release note and format README

Fix the remaining CI checks on the spell-check migration:

- Add upcoming-release-notes/8249.md (Maintenance) so the "Release notes"
  check passes.
- Reformat .github/actions/docs-spelling/README.md with oxfmt so the lint
  ("Test") and autofix.ci checks pass (autofix.ci is not permitted to modify
  files under .github, so the formatting has to be committed directly).

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

* [AI] Disable credential persistence in docs-spelling checkout

Address the code-scanning (zizmor) and CodeRabbit review finding: the
read-only spell-check job performs no authenticated git operations, so set
persist-credentials: false on actions/checkout as a hardening measure.

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

---------

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

* [AI] Add Docker, GitHub, and Anthropic sponsor logos to README (#8247)

* [AI] Add Docker, GitHub, and Anthropic sponsor logos to README

https://claude.ai/code/session_01Kq3v49wAVgQxW5H7LoTrgV

* [AI] Add release notes for sponsor logos README update

* Fix sponsor links in README.md

---------

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

* fix flaky tests (#8248)

* fix split sort order on new splits

* hide tooltips on report page to reduce vrt flakes

* note

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #8248

---------

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

* Bump the npm_and_yarn group across 1 directory with 6 updates (#8253)

Bumps the npm_and_yarn group with 6 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [dompurify](https://github.com/cure53/DOMPurify) | `3.4.2` | `3.4.10` |
| [form-data](https://github.com/form-data/form-data) | `4.0.5` | `4.0.6` |
| [launch-editor](https://github.com/vitejs/launch-editor) | `2.12.0` | `2.14.1` |
| [tar](https://github.com/isaacs/node-tar) | `7.5.13` | `7.5.16` |
| [webpack-dev-server](https://github.com/webpack/webpack-dev-server) | `5.2.4` | `5.2.5` |
| [ws](https://github.com/websockets/ws) | `7.5.10` | `7.5.11` |



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

Updates `form-data` from 4.0.5 to 4.0.6
- [Release notes](https://github.com/form-data/form-data/releases)
- [Changelog](https://github.com/form-data/form-data/blob/master/CHANGELOG.md)
- [Commits](https://github.com/form-data/form-data/compare/v4.0.5...v4.0.6)

Updates `launch-editor` from 2.12.0 to 2.14.1
- [Commits](https://github.com/vitejs/launch-editor/compare/v2.12.0...v2.14.1)

Updates `tar` from 7.5.13 to 7.5.16
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v7.5.13...v7.5.16)

Updates `webpack-dev-server` from 5.2.4 to 5.2.5
- [Release notes](https://github.com/webpack/webpack-dev-server/releases)
- [Changelog](https://github.com/webpack/webpack-dev-server/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-server/compare/v5.2.4...v5.2.5)

Updates `ws` from 7.5.10 to 7.5.11
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.5.10...7.5.11)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-version: 3.4.10
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: form-data
  dependency-version: 4.0.6
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: launch-editor
  dependency-version: 2.14.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: tar
  dependency-version: 7.5.16
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: webpack-dev-server
  dependency-version: 5.2.5
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: ws
  dependency-version: 7.5.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>

* improve CI yarn cache to make the fetch step quicker (#8252)

* restore partial yarn caches

* note

* [AI] refactor(build): replace process.env with import.meta.env in browser code (#8212)

* [AI] refactor(desktop-client): use import.meta.env for REACT_APP_* in client-only code

Client-only UI/hook code read process.env.REACT_APP_* directly. process
is not defined in the browser, so these relied on the build's process
shim and were sensitive to chunk placement (a module-level read in
useThemeCatalog landed in the eager entry chunk and crashed startup).
Switch them to Vite's native import.meta.env (envPrefix is REACT_APP_),
which is statically replaced at build time. Cross-platform shared code
(shared/environment.ts) and the bootstrap (browser-preload.js) keep
process.env since they also run under Node and rely on the browser shim.

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

* [AI] refactor(build): remove process shim by migrating remaining browser process.env reads

* Rename 8212.md to process-env.md

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>

* [AI] Add Dependency Review scan on pull requests (#8251)

* [AI] Add Dependency Review scan on pull requests

Adds a Dependency Review workflow that runs on every pull request and
fails the check when a PR introduces a dependency with a known high or
critical severity vulnerability. A summary comment is posted on failure.

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

* [AI] Scope Dependency Review to lockfile changes, drop PR comment

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

* [AI] Post Dependency Review summary comment on lockfile PRs

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

* [AI] Lower Dependency Review threshold to moderate severity

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

* [AI] Shorten Dependency Review release note

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

---------

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

* [AI] Migrate remaining CI workflows to Depot action runners (#8254)

* [AI] Migrate remaining workflows to depot action runners

Switch all remaining GitHub-hosted runners over to their depot
equivalents:

- ubuntu-latest -> depot-ubuntu-latest in the VRT-related jobs
  (e2e-test vrt/merge-vrt, e2e-vrt-comment, vrt-update-generate,
  vrt-update-apply)
- windows-2022/windows-latest -> depot-windows-2022 in the electron
  build matrices (electron-pr, electron-master, publish-nightly-electron)
  and publish-microsoft-store

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013dQEH47R5kTkPCvhC2cpoB

* [AI] Add release note for depot runner migration

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013dQEH47R5kTkPCvhC2cpoB

---------

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

* CLI: Search for config file in additional directories. (#8208)

* CLI: Search for config file in additional directories.

Searches in parents up to home directory, and in global config
directory. See changes to `packages/cli/README.md#config-file` for
full directory and filename details.

* Update CLI documention for config file location.

Also sync CLI config file documentation in packages/cli/README.md and
packages/docs/docs/api/cli.md.

* [autofix.ci] apply automated fixes

* Address CodeRabbit comments.

* Fix typos.

* Mention option of trusting CA cert in CLI docs.

Tangential to the rest of the PR, but hardly worth a standalone PR.

* Mention global config directory in release note

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>

* [AI] Fix postBinary network-failure on Alpine by removing manual Content-Length (#8195)

* [AI] Fix postBinary network-failure on Alpine by removing manual Content-Length (#8168)

Stop hand-setting the Content-Length header in postBinary and let
fetch derive it from the body, and preserve the underlying fetch
error as the PostError cause so network failures are diagnosable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* [AI] Preserve underlying error as cause in del() and patch() network failures

---------

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

* [AI] Fix auto-post schedule catch-up (#7997)

* [AI] Fix auto-post schedule catch-up

* [AI] Add release note for schedule catch-up

* [AI] Address schedule catch-up review

* [AI] Add one-time auto-post regression coverage

* [AI] Preserve one-time schedule auto-posting

* Add Docs page for Akahu Bank Sync (#8145)

* create akahu bank sync doc

* add akahu to bank sync sidebar menu

* add akahu to supported providers on bank-sync page

* Update link for Akahu provider documentation

* Refine Akahu setup documentation warnings

Removed redundant warning about experimental features and streamlined the message.

---------

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

* automation UI: fix various bugs (#8258)

* sort automations in tooltip

* numerical inputs should save on change not blur

* fix undefined weekly balance cap start

* fix phantom 0 contribution

* correctly migrate limit automations from remainder and periodic templates

* note

* allow template 0 templates

* move publish jobs back to github runners (#8263)

* move publish jobs back to github runners

* note

* Update upcoming-release-notes/github-runners-publish.md

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

* comments

---------

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

* [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>

* 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>

* [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>

* [AI] api: add browser build (#8166)

* [AI] api: add browser build

Continuation of #7545 (and #8143, which moved the browser Web Worker
bootstrap into loot-core). This adds the browser-compatible build of
@actual-app/api: the full loot-core stack runs inside a Web Worker and a
thin main-thread facade exposes the same public surface as the Node entry.

loot-core:
- Add platform/client/backend-worker.ts: createBackendWorker(worker)
  centralizes absurd-sql's main-thread initBackend + __absurd:* message
  filtering, and is exported for the api to reuse.
- Refactor browser-preload's start.ts and worker-bridge.ts to route through
  it (drops the duplicated __absurd:* filtering; terminate() now also clears
  listeners). Behavior is preserved.

api:
- browser-worker.ts: Web Worker entry owning real loot-core, speaking
  loot-core's {id,name,args} backend protocol; wraps fetch to map the
  .js -> .js.data migration names.
- index.browser.ts: main-thread facade that spawns the worker.
- browser/rpc.ts: main-thread <-> worker RPC bridge (uses createBackendWorker).
- browser/lib-stub.ts: stubs lib.send so methods.ts routes over postMessage.
- vite.browser.config.mts / vite.browser-worker.config.mts and asset bundling
  in vite.config.mts (sql-wasm.wasm + data/ + data-file-index.txt); package.json
  build chain, browser export, and absurd-sql/npm-run-all/vite-plugin-node-polyfills deps.

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

* [AI] Rename release note to match PR number 8166

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

* [AI] api: move browser worker plumbing into loot-core

Address review feedback: the browser worker logic shouldn't live in the api
package since it's generic "loot-core as a browser npm package" plumbing, not
api-specific. Resolve it via loot-core's existing conditional-export system
instead of a vite alias.

- loot-core: add server/main.api-browser.ts (worker-routing `lib` that
  delegates to the existing platform/client/connection), selected via a new
  `api-browser` export condition on "./server/main". Move the worker entry to
  server/api-browser-worker.ts.
- api: the facade (index.browser.ts) now reuses loot-core's client connection
  via a minimal global.Actual.getServerSocket shim and spawns the worker built
  from loot-core's entry. Deletes browser-worker.ts, browser/rpc.ts and
  browser/lib-stub.ts (~270 lines) in favor of the shared loot-core code.
- The facade build selects the impl with resolve.conditions: ['api-browser']
  instead of resolve.alias, removing the enforce-boundaries lint exception.

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

* [AI] api: simplify browser build cleanups

Post-review cleanups (no behavior change):
- backend-worker.ts: fix stale doc comment referencing the removed
  packages/api/browser/rpc.ts (the consumer is now index.browser.ts).
- api-browser-worker.ts: drop the redundant `endsWith('/data-file-index.txt')`
  disjunct (subsumed by `endsWith('data-file-index.txt')`) and the no-op
  `input as RequestInfo | URL` re-casts on the fetch pass-throughs.
- index.browser.ts: replace the single-field `state` holder with a plain
  module-level `let backend`.

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

* [AI] api: drop the createBackendWorker wrapper, revert browser-preload churn

Now that the facade rides on loot-core's platform/client/connection (which
already ignores absurd-sql's `__`-prefixed internal messages), the only thing
the main thread still needs is absurd's one-line initBackend. So:

- Replace the 60-line createBackendWorker wrapper (onMessage/postMessage/
  terminate/listener filtering) with a tiny initBrowserBackend(worker) helper.
- Revert browser-preload/start.ts and worker-bridge.ts to their merged state —
  they keep importing absurd's initBackend directly, so this PR no longer
  refactors just-merged files and the loot-core change is purely additive.
- Facade holds a plain Worker handle and calls worker.terminate() on shutdown.

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

* [AI] api: trim verbose comments in the browser build

Keep only the load-bearing "why" notes (the absurd-sql init, the fetch/.js.data
workaround, the global.Actual shim, the api-browser condition); drop protocol
diagrams, usage examples, and restated context. No code/behavior change.

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

* [AI] api: drop redundant absurd-sql dependency

No api source imports absurd-sql; it's reached only through @actual-app/core
(the facade's initBrowserBackend and the bundled worker), which declares it.
It's bundled into the build output, so it isn't a runtime dependency of the
published package either. Bundles are byte-for-byte unchanged.

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

* [AI] api: drop the api-browser condition, inject the worker into the connection

Review feedback: the api-browser export condition fought loot-core's idiom
(browser IS the default platform; node/"api" is the override) and hid a bug —
the condition-swapped lib only had `send`, so api.utils.amountToInteger was
silently undefined in the browser build.

Replace the condition with a plain transport seam:
- methods.ts no longer imports server/main; it sends through api/send.ts,
  which the Node entry wires to in-process lib.send and the browser entry
  wires to loot-core's client connection. utils.ts re-exports
  amountToInteger/integerToAmount from shared/util directly (fixes the bug).
- loot-core's client connection init() now accepts an optional injected
  socket; connectBackendWorker(worker) installs absurd-sql's bridge and
  connects in one call. This removes the forged global.Actual shim and the
  facade's `define: { global: 'globalThis' }` workaround. Desktop's argless
  init() path is unchanged.
- Delete main.api-browser.ts, the "./server/main" conditional export, and
  the facade's resolve.conditions.

The 4 loot-core snapshot failures (main.test.ts / transfer.test.ts) are
pre-existing on the branch without these changes.

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

* [AI] api: route methods through the client connection, slim the facade

Review feedback: get rid of the _setSend seam and move the worker init logic
into loot-core.

- methods.ts now imports `send` from loot-core's client connection. The node
  build resolves it via the existing `api` condition to a new in-process
  implementation (platform/client/connection/index.api.ts — previously a dead
  re-export of the electron client), matching the platform idiom: browser is
  the default, node/"api" overrides. api/send.ts is deleted and the node
  entry (index.ts) reverts to master byte-for-byte.
- loot-core's startBackendWorker(worker, config, assetsBaseUrl) now owns the
  whole worker boot (absurd-sql bridge + connection.init + server init RPC),
  so the browser entry just spawns the worker and calls it.
- Trims: single chained build script (drops the npm-run-all dep), copyFileSync
  instead of the linkOrCopy helper, drop the worker entry's error listeners
  (browsers already log worker uncaughts to DevTools).

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

* [AI] api: trim prose comments to the essential ones

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

* [AI] fix client connection stalling when no sends are queued at connect

Found while smoke-testing the browser api build in a real consumer app: the
client connection only nulled messageQueue (switching from queueing to direct
postMessage) when the queue was non-empty at 'connect'. The desktop app always
has queued sends by then, so it never hits this; the api facade connects
before sending anything, so every later send queued forever and init() hung.
Drop the queue on connect unconditionally.

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

* [AI] fix client connection dropping api/* error replies

Found via the browser smoke test: opening an e2e-encrypted budget without the
key surfaced as "undefined is not an object" instead of an error. The server
connection forwards api/* handler failures as {type:'reply', id, error}, but
the web client connection only read `result`, silently resolving undefined for
every failed api/* call. Reject when the reply carries an error, matching the
server's envelope (and the old api rpc behavior). The electron client has the
same latent gap; left untouched here.

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

* [AI] api: move the worker entry back into the api package

Review feedback: browser-worker.ts is the api package's build entry, not
generic loot-core code — move it back (it now imports loot-core through the
public @actual-app/core exports instead of #-subpaths).

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

* [AI] api: drop the manifest rewrite, align shutdown, trim comments

The data-file-index.txt manifest now lists migrations under their real .js
names (the worker's fetch shim already redirects those to the .js.data files
on disk), so the manifest-rewriting half of the shim is gone. The worker init
payload carries config and assetsBaseUrl as separate fields instead of
smuggling a private key into the config object, browser shutdown() mirrors
the node facade, and the client connection's queue flush drops a redundant
branch.

* [AI] fix rejected handler promises poisoning close-budget

runHandler only removed a method's promise from runningMethods when it
fulfilled, so any handler rejection left a stale promise in the set. The next
close-budget then failed: flushRunningMethods' Promise.all rejected with that
old, unrelated error before closeBudget even ran. Remove settled promises on
rejection too, and flush with Promise.allSettled since rejected methods
already reported their error to their caller.

* [AI] api: add a browser e2e test

A Playwright test loads the built browser bundle in chromium from a static
COOP/COEP server and exercises the full stack: worker boot, the rejected-error
contract, budget import, transaction read-back, and IndexedDB persistence
across a page reload. A dedicated workflow runs it in CI on api/loot-core
changes.

* [AI] api: drop the worker fetch shim; strip .data names in loot-core fs

The .js.data scheme was spread across three coordinated pieces: the build
renamed migrations on disk, the manifest listed names that didn't exist, and
a global fetch monkey-patch in the worker un-lied at runtime. Now the
manifest lists the true on-disk names and populateDefaultFilesystem (the only
fetcher of these files) strips the .data suffix when naming the virtual file
— the fetch patch is gone and the suffix becomes a small documented fs
convention. Also: e2e static server uses a single stat and drops a dead MIME
entry.

* [AI] fix electron client connection dropping api/* error replies

Both server connection variants post api/* handler failures as
{type: 'reply', id, error}, but only the web client rejected on it
(778ed25cf); the electron renderer client still resolved undefined. Mirror
the check so the wire contract is implemented uniformly.

* [AI] api: exclude the playwright e2e from vitest; review polish

vitest's default include matched e2e/browser.test.ts and failed CI - a
playwright test file defines no vitest tests. Exclude e2e/ from the unit-test
run (it has its own yarn e2e script). Also from review: the browser exports
condition now carries its own declaration file (the browser init's return
type differs from the node facade), and a failed init terminates the worker
it just spawned before rethrowing instead of leaving it running.

* [AI] api: trim code comments to the essentials

* [AI] api: always terminate the worker when browser shutdown fails

Review feedback: a rejected close-budget left the worker running. Terminate
in a finally block so cleanup is guaranteed while the failure still reaches
the caller.

* [AI] docs: document experimental browser support in the api package

* [AI] docs: drop the CORS note from the api browser section

* [AI] docs: trim the api browser section to the basics

* [AI] api: drop the import comment in methods.ts

* [AI] Run API browser test on depot runner

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

* [AI] Align api vite-plugin-node-polyfills to 0.28.0

The api workspace was pinned to ^0.27.0 (left over from an earlier merge
conflict resolution) while loot-core uses ^0.28.0. This both tripped the
workspace version-consistency constraint and broke the browser worker build:
under vite 8.0.16 the 0.27 process shim no longer round-trips runtime writes
to process.env.PUBLIC_URL, so sql-wasm.wasm was fetched from
/dist/undefinedsql-wasm.wasm (404) and api.init() failed in the browser e2e.

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

---------

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

*  Make transactions list re-renders non-blocking and perform optimistic updates (#8209)

* make re-renders non blocking for trasactions and optimistic updates on transaction list

* release notes

* fix spelling

* Update Titlebar colors and bare button theme colors (#8267)

* Update bare button colors to use page color variables

* Refactor button text color variables in dark theme

Updated button text colors to use page text variables.

* Refactor button text color variables in light.css

Updated button text colors to use page text variables.

* Fix indentation for buttonBareText variable

* Update button text colors in midnight theme

* Refactor Titlebar component styles and props

Updated button text color and removed unnecessary color styles.

* Fix button text color styling in LoggedInUser component

* Correct color style in LoggedInUser Button

Fix color style assignment in Button component.

* Remove color prop from LoggedInUser component

Removed the 'color' prop from LoggedInUser component.

* [autofix.ci] apply automated fixes

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #8267

---------

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

* 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>

* disable include current month when looking at last montn in report (#8250)

* Replace mobile amount input with calculator (#8200)

* Add mobile calculator input

* reduce SplitAmountInput props

* [AI] Move typescript-strict-plugin to devDependencies in loot-core (#8284)

* [AI] Move typescript-strict-plugin to devDependencies in loot-core

It is a dev-only TypeScript tooling plugin used during typecheck, so it
belongs in devDependencies like in all other packages.

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

* [AI] Add release note for typescript-strict-plugin devDependencies move

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

---------

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

* [AI] Replace vite-plugin-peggy-loader with an in-repo Vite plugin (#8285)

* [AI] Replace vite-plugin-peggy-loader with in-repo Peggy plugin

Drop the third-party vite-plugin-peggy-loader dependency in favor of a
small in-repo Vite plugin (packages/loot-core/scripts/peggy.mts) that
compiles .pegjs grammars using the existing peggy dependency. Wire it
into the loot-core and api vite/vitest configs, and remove the now-dead
loader from the component-library config (it imports no grammars).

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

* [AI] Host the Peggy Vite plugin in a workspace package

Move the in-repo Peggy loader out of packages/loot-core/scripts and into a
dedicated private workspace package, @actual-app/vite-plugin-peggy, so the
loot-core and api configs reference it as a normal workspace dependency
instead of reaching across packages with ../loot-core relative imports.

The peggy dependency now lives with the plugin that uses it.

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

* [AI] Rename release note to match PR number

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

---------

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

* 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>

* 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>

* [AI] Reduce dependencies (lodash, ua-parser-js, memoize-one, promise-retry, resize-observer polyfill) (#8278)

* [AI] Reduce dependencies (lodash, ua-parser-js, memoize-one, promise-retry, resize-observer polyfill)

Remove or replace several low-usage / native-coverable dependencies to shrink
the dependency surface and transitive deps:

- lodash -> es-toolkit (es-toolkit/compat) for the 5 functions in use
  (debounce, keyBy, isMatch, isEqual, uniqueId)
- ua-parser-js -> inline UA regex checks in loot-core shared/platform
- memoize-one -> small local memoizeOne helper in loot-core shared
- promise-retry -> small local retry helper (loot-core shared + a local copy
  in desktop-electron, which loads compiled JS at runtime)
- @juggle/resize-observer polyfill removed; native ResizeObserver is supported
  by all current targets

uuid was intentionally left untouched: crypto.randomUUID is only available in
secure contexts, which would break self-hosted instances served over plain HTTP
(including loot-core code running in the browser web worker).

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

* [AI] Use es-toolkit main entry for keyBy in report spreadsheets

The two report spreadsheet files only used keyBy from es-toolkit/compat. Switch
them to the lighter main es-toolkit entry (which takes a key function instead of
a string), so these chunks no longer pull in the compat layer. Runtime behavior
is identical.

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

* [AI] Rename release note to slug and reword for readability

Rename the dependency-reduction release note from the guessed PR number to a
descriptive slug, and reword the body to read clearly as changelog content.

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

* [AI] Shorten release note wording

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

* [AI] Harden retry helper against sync throws; simplify memoizeOne typing

- retry: run fn inside a promise chain so synchronous throws (and synchronous
  retry() calls) are routed through the rejection handler instead of escaping
  uncaught. Applied to both loot-core and desktop-electron copies.
- memoizeOne: use separate Args/Result generics and drop the `as T` cast.

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

* [AI] Update yarn.lock after merge and install

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com>

* remove forced waits to speed up some Playwright tests (#8293)

* speed up some playwright tests

* note

* [AI] Sync `set amount` rule actions when a schedule's amount is edited (#8266)

* Sync `set amount` rule actions when a schedule's amount is edited

A schedule's amount lives in its rule's amount *condition*, which is what
the schedule editor reads and writes. If the rule was customized to also
carry a plain `set amount` *action*, editing the amount only updated the
condition, leaving the action stale. Auto-posting runs the rule, so the
stale action reverted the posted amount to the old value even though the
editor showed the new one.

`updateSchedule` now keeps plain `set amount` actions in sync with the
amount condition. Templated, formula, and `set-split-amount` actions
compute their own value and are left untouched.

Fixes #8265

* Add release note for #8266

* Clarify updateAmountActions comments (set-split-amount exclusion, null amount)

* [AI] Rename updateAmountActions to typed updateActions

* [AI] 🐛 Exclude completed schedule rules from payee rule counts (#8281)

* [AI] Exclude completed schedule rules from payee rule counts

* Update rule count logic on Payee page

Fixes the rule count on the Payee page to exclude rules linked to complete schedules.

* Correct category label in release notes

Updated category from 'Bugfix' to 'Bugfixes'.

* [autofix.ci] apply automated fixes

* Update upcoming-release-notes/fix-payee-rule-count.md

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

* [AI] Fix typecheck errors in payee rule count tests

* [AI] Fix createSchedule typing in payee rule count test

* [AI] Type createSchedule parameters for strict mode

* [AI] Use ts-strict-ignore in payee rule count test instead of typing createSchedule

* [AI] Use getCompletedScheduleRuleIds and type createSchedule for payee counts

* [AI] Fix link-schedule action value type to use schedule id

* [AI] Fix downstream types for schedule conditions and link-schedule value

* [AI] Resolve schedule id before rendering ScheduleValue label

---------

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

* Attempt to fix entered amount being lost sometimes when tapping the Nearby payee chip before field loses focus (#8196)

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

* [AI] Fix append/prepend notes rules on new transactions (#8300)

Append/prepend notes rules were ignored when adding a new transaction
that already had a note, because rule changes are only applied to fields
the user left empty (so manual input isn't overwritten).

Add a shared `shouldApplyRuleChange` helper that still protects
user-entered values but lets the notes field through when the rule result
preserves the existing note as a prefix or suffix (append/prepend). The
check is idempotent: rules re-run on every keystroke during entry, so it
refuses to re-add text that a previous run already applied, preventing the
appended text from duplicating.

Used by both the desktop register and the mobile transaction editor.

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

* [AI] DX: fix CI Lage cache (#8305)

* [AI] Fix CI Lage cache to target the real cache directory

The "Cache Lage" step in the shared setup action cached the repo-root
.lage directory, but Lage writes its build/test cache to
node_modules/.cache/lage. The configured directory was always empty
(the step even mkdir -p's it first), so the cache step did nothing and
Lage results only persisted incidentally inside the yarn.lock-keyed
node_modules cache, frozen between lockfile changes.

Point both the mkdir and the cache step at node_modules/.cache/lage so
the existing sha key + prefix restore-key actually roll the cache
forward: each run restores the most recent prior entry and saves a
fresh one, letting CI jobs/shards and reruns reuse earlier build/test
results.

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

* [AI] Address review: drop comment, use slug release note

Remove the explanatory comment from the Cache Lage step and rename the
release note to a descriptive slug with a shorter, user-facing body, per
the repo's release-note conventions.

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

---------

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

* [AI] DX: replace ts-node with Node native TypeScript execution (#8304)

* [AI] Replace ts-node with Node native TypeScript execution

Node 22.18+ runs TypeScript files directly via type stripping, so the
ts-node dependency is no longer needed for the repo's helper scripts.

- Run bin/ and loot-core pack-hook scripts with `node` instead of `ts-node`
- Rename bin/*.ts scripts to .mts (unambiguous ESM, runs warning-free)
- ESM-ify validate-publish-imports: __dirname / require.main via import.meta
- Drop ts-node devDependency and the tsconfig ts-node override block
- Bump engines.node to >=22.18.0 (first release with stable type stripping)

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

* [AI] Use .mts import specifier and simplify release note

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

* [AI] Bump .nvmrc to v22.18.0 to match engines floor

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

---------

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

* [AI] DX: strip color codes in expectSnapshotWithDiffer (#8310)

* [AI] Strip ANSI codes in expectSnapshotWithDiffer to make snapshots deterministic

The expectSnapshotWithDiffer helper snapshots the output of jest-diff's
diff(), which chalk colorizes with ANSI escape codes when a color-capable
terminal is detected. The committed snapshots were generated with color off
(CI/no-TTY), so they are plain text and mismatched when the same tests ran
locally in a color-capable terminal.

Wrap the diff() output in util.stripVTControlCharacters so the snapshotted
string is plain text regardless of terminal color detection. This fixes every
test using the helper, not just the four currently failing.

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

* [AI] Add release note for snapshot helper ANSI fix

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

* [AI] Rename release note to slug-based filename

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

---------

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

* [AI] DX: scope CI Lage cache key per job (#8311)

* [AI] DX: scope CI Lage cache key per job

The Lage cache key was shared across every job in the Build and Test
workflows. The `setup` job (a dependency that finishes first) runs no
Lage task, so it persisted an empty cache under the shared key; every
other job then saw the key already existed and skipped saving its real
cache. The next run restored that empty cache, causing a 100% cache miss.

Key the cache per job so each job restores and persists its own Lage
cache.

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

* [AI] DX: remove comment and shorten release note

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

---------

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

* [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>

* [AI] DX: enable oxlint no-useless-default-assignment in strict packages (#8306)

* [AI] Disable oxlint no-useless-default-assignment rule

This rule requires the `strictNullChecks` compiler option to function, but
the project intentionally keeps `strict: false` (per-file strictness is
handled by typescript-strict-plugin, which oxc's type-aware linter doesn't
understand). As a result the rule only emitted noise — over 1,200 warnings
telling us it couldn't run — so turn it off explicitly, matching how other
ill-fitting type-aware rules are already disabled.

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

* [AI] Simplify no-useless-default-assignment disable comment

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

* [AI] Add release note for disabling no-useless-default-assignment

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

* [AI] Enable no-useless-default-assignment, disable only in non-strict packages

Turn the rule on by default so the strict packages (ci-actions, cli,
component-library, crdt) enforce it, and add an override disabling it in the
packages that still set `strict: false` (api, desktop-client,
desktop-electron, loot-core, plugins-service, sync-server), where the rule
can't function without strictNullChecks.

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

* [AI] Trim no-useless-default-assignment override comment to just the TODO

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

* [AI] Rename release note to a slug

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

---------

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

* [AI] DX: add recommended VS Code extensions for onboarding (#8303)

* [AI] Add recommended VS Code extensions for onboarding

Add a .vscode/extensions.json so contributors are prompted to install the
oxc formatter (matching the configured editor.defaultFormatter), plus the
Playwright and Vitest extensions. Whitelist the file in .gitignore.

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

* [AI] Add release notes for VS Code recommended extensions

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

---------

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

* 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

* 🔖 (26.7.0)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Michael Clark <5285928+MikesGlitch@users.noreply.github.com>
Co-authored-by: Sebastián Maluk <samaluk@miuandes.cl>
Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Mats Nilsson <matni403@gmail.com>
Co-authored-by: Silva Dev BR <116388885+Will-thom@users.noreply.github.com>
Co-authored-by: Juulz <julesmcn@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Alec Bakholdin <43560338+alecbakholdin@users.noreply.github.com>
Co-authored-by: Amirhosein Azhdarnezhad <amir.1381.azhdarnzhad@gmail.com>
Co-authored-by: Dustin Brewer <mannkind@thenullpointer.net>
Co-authored-by: youngcw <calebyoung94@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Awais Saeed <awaissaeed530@gmail.com>
Co-authored-by: Awais Saeed <dev@awaissaeed.com>
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: Joseph Coffa <josephco440@gmail.com>
Co-authored-by: narutamaaurum <narutamaaurum@gmail.com>
Co-authored-by: Henghui Zhu <2193638829@qq.com>
Co-authored-by: Kyle Slattery <k@kslat.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: lelemm <lelemm@gmail.com>
Co-authored-by: Manaf <manaf.aboalrous@gmail.com>
Co-authored-by: David Bettis <david.bettis@gmail.com>
Co-authored-by: dzickusrh <dzickus@redhat.com>
Co-authored-by: Gemini <noreply@google.com>
Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com>
Co-authored-by: Kevin <36412364+KevinIsMyName@users.noreply.github.com>
Co-authored-by: Stephen Brown II <Stephen.Brown2@gmail.com>
Co-authored-by: yoshiboi3 <josed.hc96@gmail.com>
Co-authored-by: Trevin Chow <trevin@trevinchow.com>
Co-authored-by: Matt Farrell <10377148+MattFaz@users.noreply.github.com>
Co-authored-by: kikiminyes <66829008+kikiminyes@users.noreply.github.com>
Co-authored-by: Pavel Voronov <31508908+pavel1337@users.noreply.github.com>
Co-authored-by: mheiland <15824364+mheiland@users.noreply.github.com>
Co-authored-by: ItsThatDude <81509659+ItsThatDude@users.noreply.github.com>
Co-authored-by: Julian Dominguez-Schatz <julian.dominguezschatz@gmail.com>
Co-authored-by: Tifan Dwi Avianto <33323177+tifandotme@users.noreply.github.com>
Co-authored-by: Tim <hello@timsmart.co>
Co-authored-by: Nustinua <77891103+Nustinua@users.noreply.github.com>
Co-authored-by: Nikunj Rustagi <167123694+Procoder1234556@users.noreply.github.com>
Co-authored-by: meaqua9420 <108719967+meaqua9420@users.noreply.github.com>
Co-authored-by: Emil Tveden Bjerglund <emilbp@gmail.com>
Co-authored-by: Kyle Bremont <94015697+kbremont@users.noreply.github.com>
Co-authored-by: PaolinPaperin <github.ember792@passmail.net>
Co-authored-by: sys044 <subsys@gmail.com>
Co-authored-by: sys044 <sys044@localhost>
Co-authored-by: Augusto Xavier <augustocbx.dev@gmail.com>
Co-authored-by: Kennedy242 <80504103+Kennedy242@users.noreply.github.com>
Co-authored-by: Wing Chang <96088121+PencilKnot@users.noreply.github.com>
Co-authored-by: lelemm <lelemm@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Co-authored-by: Andy Fiddaman <andy@omnios.org>
Co-authored-by: Tom Mills <accounts+github@tomjmills.co.uk>
Co-authored-by: thromer <tromer@gmail.com>
Co-authored-by: JinHyuk Sung <163989462+sjh9714@users.noreply.github.com>
Co-authored-by: Oskar <github@neuhuber.eu>
Co-authored-by: Zannis Kalampoukis <zannis@users.noreply.github.com>
Co-authored-by: tabedzki <35670232+tabedzki@users.noreply.github.com>
2026-06-25 20:37:42 +00:00

28 lines
874 B
YAML

issue_enrichment:
auto_enrich:
enabled: true
reviews:
tools:
eslint:
enabled: false
request_changes_workflow: true
review_status: false
high_level_summary: false
finishing_touches:
docstrings:
enabled: false
pre_merge_checks:
docstrings:
mode: off
enabled: false
labeling_instructions:
- label: 'API'
instructions: 'This issue or PR updates the API in `packages/api`.'
- label: 'documentation'
instructions: 'This issue updates the documentation in `packages/docs`.'
- label: 'contains DB migrations'
instructions: 'This issue or PR contains DB migrations in `packages/loot-core/migrations`.'
- label: 'size small'
instructions: 'This issue or PR is a small change (less than 50 lines of code) that is expected to have a small impact on the codebase.'
auto_apply_labels: true