Commit Graph

5001 Commits

Author SHA1 Message Date
Sebastián Maluk
fee8fbccd1 [AI] Fix balance forecast all future range (#7849)
* [AI] Fix forecast all future range

* [AI] Add release note for balance forecast range fix
2026-05-17 18:52:54 +00:00
mehmet turac
44f1aac59a [AI] Fix CSV preview category normalization (#7878) 2026-05-17 17:58:37 +00:00
Matiss Janis Aboltins
67a439f13f [AI] Tighten VRT per-pixel threshold so faint overlays fail (#7864)
* [AI] Tighten VRT per-pixel threshold so faint overlays fail

Playwright's default `threshold: 0.2` translates to a pixelmatch YIQ
delta cutoff of ~1408, which silently swallows low-alpha overlays. PR
#7841 striped the transactions table with rgba(..., .15); the resulting
per-pixel deltas (~270 light, ~320 dark) fell below the cutoff, so VRT
reported 0 diff pixels and passed despite a clearly visible change.

Drop `threshold` to 0.05 (cutoff ~88) so faint tints are flagged while
keeping headroom for anti-aliasing noise.

* [AI] Apply VRT threshold to electron config and drop redundant local override

- Remove `maxDiffPixels: 5` from `toMatchThemeScreenshots` — it duplicates
  the global config and obscured the fact that per-call options would
  override the global threshold if added there too.
- Mirror the threshold in desktop-electron's playwright config so its
  VRT screenshots are subject to the same sensitivity.
- Tighten the threshold comment: drop the PR/task reference per
  AGENTS.md and the worked-example numbers; keep the formula and the
  low-alpha rationale.

* Add release notes for PR #7864

* Revert "[AI] Apply VRT threshold to electron config and drop redundant local override"

This reverts commit ec789703ea.

* Revert "[AI] Tighten VRT per-pixel threshold so faint overlays fail"

This reverts commit 334954bb33.

* [AI] Remove stale release note for reverted threshold change

* Revert "[AI] Remove stale release note for reverted threshold change"

This reverts commit 90f97d95f3.

* Reapply "[AI] Tighten VRT per-pixel threshold so faint overlays fail"

This reverts commit 98d0f8b3e7.

* Reapply "[AI] Apply VRT threshold to electron config and drop redundant local override"

This reverts commit 23f94362ec.

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #7864

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-05-17 17:26:05 +00:00
Matt Fiddaman
28f7770913 deprecate rule action templating in favour of formulae (#7865)
* add deprecation warning

* note
2026-05-17 17:14:28 +00:00
Matiss Janis Aboltins
068185751c [AI] Prevent CSV formula injection in exports and CLI output (#7859)
* [AI] Neutralize CSV formula-injection in CLI output and transaction export

Prefix cells starting with =, +, -, @, tab, or CR with a single quote so
that user-controlled strings (payee/account/category/tag names, notes,
etc.) cannot evaluate as formulas when the CSV is opened in Excel,
LibreOffice Calc, or Google Sheets. Numeric values are left unprefixed.

- packages/cli/src/output.ts: harden escapeCsv used by --format csv
- packages/loot-core/src/server/transactions/export/export-to-csv.ts:
  pass a cast.string option to csv-stringify

* [AI] Simplify CSV formula-injection guard

- Drop the FormattedCell type; check typeof value === 'number' inline in
  the CSV path instead of threading an isNumeric flag through formatCellValue
- Shorten verbose comments and drop standards-body references

* [AI] Remove @ts-strict-ignore from export-to-csv test

* [AI] Add release notes for CSV formula-injection fix

* [AI] Quote CSV cells containing carriage returns

Per RFC 4180, bare \r inside an unquoted field can be interpreted as
a record terminator. Extend escapeCsv to also quote on \r so neutralized
formula payloads (e.g. "'\rHELLO") are emitted as a single cell.

* [autofix.ci] apply automated fixes

* [AI] Rephrase release note in user-facing language

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-05-16 21:05:40 +00:00
Matiss Janis Aboltins
8f0265e0b0 [AI] Convert built-in themes from TypeScript to CSS files (#7851)
* [AI] Convert theme color TS modules to CSS files with CSS variables

Replace `palette.ts` and `themes/{light,dark,midnight}.ts` with plain CSS
files that declare the same variables directly via `:root` blocks. The
palette becomes `--palette-*` variables; each theme defines `--color-*`
variables referencing those (or other `--color-*` for intra-theme aliases).

`theme.tsx` and Storybook's `preview.tsx` now import each CSS file as a
string via Vite's `?inline` query and render two `<style>` tags (palette +
active theme) instead of building the variable declarations at runtime
from a TS object.

Auto/dark-preference selection, `prefers-color-scheme` listening, the
custom-theme baseTheme override, and the `CustomThemeStyle` override layer
are all preserved.

* [AI] Move theme CSS files to component-library

Move palette.css and themes/{light,dark,midnight}.css from desktop-client's
style/ directory into component-library at src/themes/, and add explicit
package.json exports for each. desktop-client's theme.tsx now imports them
via `@actual-app/components/themes/*.css?inline`, and Storybook's
preview.tsx uses a normal relative path inside its own package instead of
reaching across the monorepo boundary.

* [AI] Minimize theme.tsx rename churn

Keep the original variable names — `themes[*].colors`, `themeColors` state,
`lightColors` / `darkColors` locals, and `getBaseThemeColors` — even though
they now hold CSS strings rather than color objects. Limits the diff to the
import changes, the state type, and the render path.

* [AI] Add release note for theme CSS conversion

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-16 21:05:02 +00:00
Matiss Janis Aboltins
3494f78c94 [AI] Restrict secrets API access to admins in OpenID mode (#7862)
* [AI] Require admin for GET /secret/:name in OpenID mode

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

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

* [AI] Simplify secrets auth guard after review

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

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

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

* [AI] Undo testSecretName -> validSecretName rename

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

* [AI] Add release note for #7862

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

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-16 20:59:31 +00:00
Matiss Janis Aboltins
46c350613c [AI] Upgrade vite-plugin-node-polyfills to 0.27.0 (#7860)
* [AI] Upgrade vite-plugin-node-polyfills to 0.27.0

Bumps `vite-plugin-node-polyfills` from `^0.26.0` to `^0.27.0` in
`packages/loot-core`.

Changelog: https://github.com/davidmyersdev/vite-plugin-node-polyfills/releases/tag/v0.27.0
Related upstream issue (already referenced from `vite.config.mts`):
https://github.com/davidmyersdev/vite-plugin-node-polyfills/issues/142

* [AI] Add release note for vite-plugin-node-polyfills upgrade

* [AI] Remove stale upstream issue reference from vite config

The link pointed at davidmyersdev/vite-plugin-node-polyfills#142, which
no longer needs a workaround note alongside the `nodePolyfills` call.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-16 15:03:52 +00:00
Matt Fiddaman
9b19cd2616 automation UI: various tweaks and fixes (#7832)
* warning when only a balance cap is used

* add tooltip for short descriptions

* fix parsing issue with template 0 up to templates

* preselect value to make deletion easier

* note

* fix balance cap note

* fix tests

* remove recurring

* Goal/Automation wording
2026-05-15 22:13:30 +00:00
Matiss Janis Aboltins
1f101077d6 [AI] zizmor: add environment references to secret-consuming workflows (#7856)
* [AI] Reference dedicated environments for workflows using secrets

Assigns each secret-consuming workflow to a dedicated GitHub
environment so zizmor's secrets-without-environment audit passes:

- ai-generated-release-notes.yml -> ai-release-notes
- docs-spelling.yml (update job) -> docs-spelling
- i18n-string-extract-master.yml -> i18n
- release-notes.yml -> pr-automation
- vrt-update-apply.yml -> pr-automation

* [AI] Rename release notes file to match PR #7856

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-15 21:45:12 +00:00
Matiss Janis Aboltins
62d7c0e479 [AI] zizmor: fix template injection in setup action's Lage cache step (#7858)
* [AI] Fix template injection in setup action's Lage cache step

The 'Ensure Lage cache directory exists' step expanded
${{ inputs.working-directory }} directly into the shell command via
format(), which zizmor flags as a code-injection risk. Pass the input
through an env var and reference it with shell expansion instead.

* [AI] Add release note for template injection fix

* [AI] Rename release note to match PR #7858

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-15 21:34:22 +00:00
dependabot[bot]
740392941d Bump qs from 6.13.0 to 6.15.1 (#7857)
Bumps [qs](https://github.com/ljharb/qs) from 6.13.0 to 6.15.1.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.13.0...v6.15.1)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.15.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-15 20:53:31 +00:00
dependabot[bot]
d4528e18ea Bump lodash-es from 4.17.21 to 4.18.1 (#7854)
Bumps [lodash-es](https://github.com/lodash/lodash) from 4.17.21 to 4.18.1.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.18.1)

---
updated-dependencies:
- dependency-name: lodash-es
  dependency-version: 4.18.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-15 20:23:31 +00:00
Matiss Janis Aboltins
3d47eae87b [AI] Replace GitHub Actions with native gh CLI commands (#7852)
* [AI] Replace superfluous actions flagged by zizmor

Address zizmor's `superfluous-actions` audit by replacing actions whose
functionality is already provided by the runner's pre-installed `gh` CLI:

- `actions-ecosystem/action-add-labels` -> `gh issue edit --add-label`
- `peter-evans/create-or-update-comment` -> `gh issue comment`
- `softprops/action-gh-release` -> `gh release create` / `gh release upload`

For the Electron release workflow, the create step is race-safe across
the three matrix OS jobs that share the same draft release.

* [AI] Simplify electron release upload script

- Drop the `gh release view` existence check; `gh release create ... || true`
  already handles the matrix-job race against the same draft release.
- Use `extglob` to exclude `Actual-windows.exe` inline instead of looping
  over `.exe` separately.

* Add release notes for PR #7852

* [AI] Narrow error suppression on gh release create

Only swallow the "already_exists" error from the parallel-matrix race;
propagate any other failure (auth, network, API) instead of masking it.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-05-15 19:51:40 +00:00
Sebastián Maluk
90a1e9bdd3 [AI] Color balance forecast line by zero crossing (#7850)
* [AI] Color forecast line by zero crossing

* [AI] Add release note for forecast line coloring
2026-05-15 17:50:40 +00:00
Matiss Janis Aboltins
329a7e81e7 [AI] Keep the mobile page header mounted across navigation to avoid flashing (#7842)
* [AI] Keep the mobile page header mounted across navigation to avoid flashing

On mobile, navigating between pages unmounted and remounted the page header
(including its background), causing a visible flash while the next page
rendered. Mobile pages now publish their header content through a context to a
single persistent `MobilePageHeaderSlot` rendered by the app shell, so only the
header content swaps while its background stays put.

* [AI] Render the persistent mobile page header via a portal

Replaces the context/state + useLayoutEffect plumbing for the persistent
mobile header with a portal into the `MobilePageHeaderSlot` DOM node, so
header content reconciles in place without re-rendering the provider on every
page render.

* Add release notes for PR #7842

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-05-15 16:13:14 +00:00
Sebastián Maluk
e8d95fdf6b [AI] feat: add an experimental balance forecast report (#7310)
* [AI] feat: add balance forecast backend

* [AI] feat: add balance forecast report UI

* [AI] feat: gate balance forecast behind an experimental flag

* [AI] Include account-less schedules in balance forecast via explicit flag

- Add includeAccountlessSchedules to forecast/generate and normalize
  schedules without an account into FORECAST_UNASSIGNED_ACCOUNT_ID
- When enabled, append synthetic bucket and rule stub; skip transfer legs
  for unassigned schedules
- Balance forecast UI sets the flag when widget meta has no account filter
- Add loot-core tests for include vs exclude behavior

* [AI] Improve balance forecast chart refresh UX

Keep forecast charts stable during refetches and let the Y-axis scale to forecast data so balance changes remain visible.

* [AI] Document balance forecast report

Add experimental user documentation and navigation links for the new balance forecast report.

* [AI] Link balance forecast experimental flag to feedback issue #7669

* docs: add PR release notes

* [AI] chore: rerun CI
2026-05-15 02:07:32 +00:00
Maksim Zhukau
2e0342574f [AI] fix: match no transactions when "has tags" input lacks # (closes #7797) (#7808)
* [AI] fix: match no transactions when "has tags" input lacks `#` (closes #7797)

The SQL-side `hasTags` filter extracts `#tag` patterns from the user
input and `$and`s them together. When the input has no `#` (e.g. user
types `foo` instead of `#foo`), the extraction returns an empty array
and the resulting `$and: []` matches every transaction.

Mirror the empty-`oneOf` behaviour and return the match-nothing
sentinel (`{ id: null }`) in that case.

* [AI] Add release notes entry for #7808

---------

Co-authored-by: MaksZhukov <maks_zhukov_97@users.noreply.github.com>
2026-05-14 23:36:14 +00:00
Matt Fiddaman
36e5cb17f5 fix healthcheck script (#7840)
* fix healthcheck script

* note

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

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

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

* Add release notes for PR #7847

* Change category from Bugfixes to Maintenance

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

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

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

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

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

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

---------

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

* note
2026-05-14 21:04:20 +00:00
Matiss Janis Aboltins
872a40f829 Add explicit permissions to GitHub Actions workflows (#7846)
* [AI] Add explicit permissions blocks to GitHub Actions workflows

Resolves zizmor "excessive-permissions" findings by declaring minimal
`permissions:` blocks for workflows that previously relied on the default
GITHUB_TOKEN permissions.

https://claude.ai/code/session_01FsyCaLEqb3C4egMPUoAFRg

* Add release notes for PR #7846

* Update category for release notes

Changed category from Enhancements to Maintenance.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-05-14 20:17:50 +00:00
Matiss Janis Aboltins
fd01bd855c [AI] Stabilize size-compare job by pinning downloads to run_id (#7780)
* [AI] Stabilize size-compare job by pinning downloads to run_id

The compare job in .github/workflows/size-compare.yml was flaky because
fountainhead/action-wait-for-check matched a check by name from any run
on the branch, while dawidd6/action-download-artifact with branch:/pr:
filters and workflow_conclusion: '' resolved to the latest run regardless
of completion. When a new master build started in the seconds between
waiting and downloading, the action picked up the in-progress run and
failed with "artifact not found".

Replaces the eight wait-for-check steps with one actions/github-script
step that polls listWorkflowRuns for a successful build.yml run on
master and the PR head SHA in parallel via Promise.all, then pins all
eight downloads to those run_ids.

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

* Add release notes for PR #7780

* Change category to Maintenance in release notes

Updated category from 'Enhancements' to 'Maintenance'.

* [AI] Clean up comment to remove reference to previous implementation

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com>
2026-05-14 19:41:13 +00:00
Matiss Janis Aboltins
7e0f024c97 Add repository metadata to @actual-app/crdt package.json (#7845)
* [AI] Fix npm provenance for @actual-app/crdt and bump to 3.0.1

Add the missing repository field to packages/crdt/package.json so the npm
provenance bundle can validate the source against
https://github.com/actualbudget/actual. Without it, publishing fails with
"Error verifying sigstore provenance bundle: repository.url is \"\"".

* Add release notes for PR #7845

* [AI] Revert @actual-app/crdt version back to 3.0.0

* Fix metadata formatting in package.json for crdt

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-05-14 19:34:39 +00:00
Matiss Janis Aboltins
2b7bc80f4e release crdt v3.0.0 (#7818)
* release crdt v3.0.0

* Test

* Bump version

* Revert readme
2026-05-14 19:08:51 +00:00
Matiss Janis Aboltins
6c1699f0b0 [AI] Replace any-typed Modal in undo state with structural type (#7813)
* [AI] Replace any-typed Modal in undo state with structural type

loot-core can't import @actual-app/web's Modal union, so the undo MRU
typed openModal as `any`. The undo system only stores the value and
reads `.name`, so a minimal structural shape `{ name: string; options?: unknown }`
is enough. desktop-client's full Modal still assigns to it, and the one
reader (global-events.ts) re-narrows back to Modal when handing the
value to replaceModal().

* Add release notes for PR #7813

* Update 7813.md

* [AI] Add TODO on Modal cast for future type consolidation

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-05-13 18:08:11 +00:00
Matt Fiddaman
4b1b68a353 automation UI: add spend from functionality (#7823)
* add spend from template

* note
2026-05-13 17:35:41 +00:00
Matt Fiddaman
dbf5d7c079 automation UI: tweak font colours to be more readable (#7819)
* s/pagetext{Subdued,Light}

* note
2026-05-13 17:31:25 +00:00
Matt Fiddaman
6bfc299d28 automation UI: add cleanup functionality (#7815)
* [AI] Share cleanup-group helpers and let storeTemplates write cleanup_def

- Extract resolveCleanupGroup and tombstoneOrphanCleanupGroups out of
  cleanup-template-notes.ts into a new cleanup-groups.ts so the
  upcoming UI-driven create flow can reuse the resurrect-aware lookup.
- Let storeTemplates accept an optional cleanup array per category
  (omitted = leave as-is, [] = clear, non-empty = replace), and run the
  orphan tombstone sweep whenever cleanup_def is touched so groups
  removed from the UI don't linger.
- Register budget/store-note-cleanups so the UI can migrate a single
  category's notes on demand, and budget/create-cleanup-group so it can
  create groups inline.

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

* [AI] Add UI editor for end-of-month cleanup automations

- New cleanup row in the BudgetAutomations sidebar with read-only
  summary; selecting it opens an editor with a Global scope card and
  an optional named-group scope (single group per category for now,
  since multi-group ordering depends on category sort).
- Each scope card has independent "send leftover" / "take a share"
  toggles plus a weight; group scopes additionally support
  "only enough to cover overspending".
- Group picker is a typeahead that creates groups inline via
  budget/create-cleanup-group.
- useCategoryCleanup migrates notes to cleanup_def at modal-open for
  unmigrated categories; useCleanupGroups streams the live list.
- Un-migrate flow renders cleanup_def back to #cleanup note lines and
  drops rows whose group can't be resolved, so users never see UUIDs
  in their notes.
- Sidebar/automation-button "has automations" probes also check
  cleanup_def so cleanup-only categories still get the indicator.

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

* note

* review pass 1

* bring automation logic in line with cleanup logic

* review pass 3

* coderabbit pass 1

* wording suggestions

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 17:01:11 +00:00
Julian Dominguez-Schatz
a7e22b023c Disable postinstall scripts except for an allowlist (#7825)
* Disable postinstall scripts except for an allowlist

* Add release notes

* Temp: trigger docs CI

* Revert "Temp: trigger docs CI"

This reverts commit 1c2ca1125c.

* Remove some unneeded builds
2026-05-13 13:29:15 +00:00
Julian Dominguez-Schatz
d3e7c1ee87 Fix some issues caught by zizmor (#7826)
* Fix some issues caught by zizmor

* Add release notes

* Add more cache ignores

* Add comments on reasoning
2026-05-13 13:19:16 +00:00
Aryan Katiyar
a7e100276e [AI] Fix category group filtering for budgeted custom reports (#7629)
* [AI] Fix category group filtering for budgeted custom reports

* [AI] Add release notes for budgeted report filter fix
2026-05-13 10:33:14 +00:00
Tonchain
ee82a16026 Fix split transaction popover wrapping (#7814)
* Fix split transaction popover wrapping

* Add release note for split popover fix

* Uncap split popover width
2026-05-13 10:07:14 +00:00
Matiss Janis Aboltins
b61732e20e [AI] Add workflow to auto-label AI-generated PRs (#7817)
* [AI] Add workflow to label '[AI]'-prefixed PRs as 'AI generated'

https://claude.ai/code/session_018yp3BsEq1CyPcw8t57nLVu

* [AI] Suppress zizmor dangerous-triggers finding and add release note

https://claude.ai/code/session_018yp3BsEq1CyPcw8t57nLVu

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-13 05:17:11 +00:00
Matiss Janis Aboltins
83073b3ee0 [AI] automated publishing workflow for crdt package (#7805)
* [AI] Require @actual-app/crdt version bump and auto-publish

Adds two workflows:
- crdt-version-check: fails PRs that modify files in packages/crdt/
  without bumping the version in packages/crdt/package.json.
- publish-crdt: publishes @actual-app/crdt to npm when the version in
  packages/crdt/package.json changes on master, tagging the release as
  crdt-v<version>.

* [AI] Skip git tagging in @actual-app/crdt publish workflow

Remove the tag-and-push step and the now-unused version output;
downgrade contents permission to read.

* [AI] Simplify crdt version-bump workflows

- Drop the redundant explicit base-branch fetch (fetch-depth: 0 already
  retrieves all remote branches).
- Remove the unreachable "no changes" guard; the pull_request paths
  filter already scopes the workflow to packages/crdt changes.
- Replace the embedded Node semver comparison with `sort -V`.
- Read versions with `jq` instead of inline Node.

* [AI] Add release notes for crdt publish workflows

* [AI] Restrict GITHUB_TOKEN permissions in crdt workflows

Add top-level `permissions: contents: read` to both crdt workflows so
the implicit jobs no longer inherit overly broad permissions (flagged by
zizmor).

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-12 21:23:47 +00:00
diodijon
78234102fa Added scoped ErrorBoundary elements to all the modals in the desktop-client modals folder (#7560)
* add scoped ErrorBoundary to base Modal component

* update release note

* [autofix.ci] apply automated fixes

* Delete packages/loot-core/src/mocks/files/budgets/test-budget/metadata.json

* Delete packages/loot-core/src/mocks/files/budgets/test-budget/db.sqlite

* Add ErrorBoundary to Modal component for error handling

Errors thrown within any modal's content will now display a fallback UI (FeatureErrorFallback) instead of crashing the entire application.

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2026-05-12 21:15:07 +00:00
Matiss Janis Aboltins
2c7f3c7a3d [AI] Replace google-protobuf with @bufbuild/protobuf (#7535)
* [AI] crdt: typecheck test files and clean up lint issues

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

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

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

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

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

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

* [AI] crdt: drop the SyncProtoBuf compat layer

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 17:55:52 +00:00
Yosof Badr
a4cb6dac37 [AI] fix: allow clearing pre-assigned category on new transactions (#7521)
* [AI] fix: allow clearing pre-assigned category on new transactions

Add a "Nothing" button to the category autocomplete modal that allows
users to clear a pre-assigned category when adding or editing
transactions. Previously, when a payee had a pre-assigned category,
there was no way to remove it and leave the transaction uncategorized.

Closes #7390

* [AI] docs: add release notes for PR #7521

* [AI] chore: re-trigger CI for flaky test

The test failure in methods.test.ts (Budgets: successfully update budgets)
is a pre-existing flaky test caused by a race condition in
advanceSchedulesService. The async schedule service fires via
void runMutator() after a sync event, but the database can be closed
before the query completes. This is unrelated to the PR changes which
only touch desktop-client UI code.

* chore: retrigger CI (flaky api test)

* fix type issue, better text

* more type fixes

* actually fixed?

---------

Co-authored-by: youngcw <calebyoung94@gmail.com>
2026-05-12 17:02:18 +00:00
youngcw
d1f9f8aecf release crossover report (#7804)
* release crossover report

* note
2026-05-12 16:45:01 +00:00
youngcw
3b927e754c 🐛 fix crossover report bugs (#7803)
* fix crossover report bugs

* note

* fixes

* refix
2026-05-12 16:02:09 +00:00
Alec Bakholdin
f2f3a5aa6d reserved whitespace for bank sync indicator when non-synced accounts … (#7611)
* moved the bank sync indicator to the right side of the text in mobile accounts view

* release notes

* moved spacing to the left again but made it smaller

* removed react from imports

* compressed space further

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #7611

---------

Co-authored-by: Alec Bakholdin <alecbakholdin.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-05-12 15:56:42 +00:00
Stephen Brown II
8fbad7d64f [AI] No longer adjust date to match on transfers (#7722) 2026-05-12 15:51:08 +00:00
Nikhil Verma
44a3013772 [AI] Add R keyboard shortcut for Make transfer (#7750)
* [AI] Add T keyboard shortcut for Make transfer

* [AI] Add release notes for #7750

* [AI] Switch Make transfer shortcut from T to R and document it

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #7750

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #7750

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #7750

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-05-12 15:40:16 +00:00
Matt Fiddaman
70716a59da automation UI: add goal template functionality (#7792)
* add goal to automations UI

* note

* tweak description

* Update upcoming-release-notes/7792.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>
2026-05-12 14:30:05 +00:00
Matt Fiddaman
e83567216f automation UI: open to the first automation (#7811)
* open to first automation

* note
2026-05-12 14:29:08 +00:00
Matt Fiddaman
92d4f82b66 automation UI: allow "save by date" automations not to repeat (#7810)
* allow save by automations not to repeat

* note
2026-05-12 14:28:58 +00:00
Matiss Janis Aboltins
50feba1afb [AI] Fix flaky API test timeouts and use sync file write in tests (#7806)
* [AI] Fix flaky upload-user-file test

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

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

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

* [AI] Add release note for flaky test fixes

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-11 22:07:25 +00:00
Nadir Miralimov
8263e58eb2 [AI] Replace payee and category autocomplete filter/sort with fzf fuzy search (#7261)
* [AI] feat(web): replace custom autocomplete ranking with fzf

Replace substring-based filter/sort in PayeeAutocomplete and
CategoryAutocomplete with fzf fuzzy search. Remove deprecated
autocompleteRanking utility.

Closes #7261

* Update #7261 release notes

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

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #7261

* Regenerate yarn.lock file

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #7261

* Restore e2e snapshots

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #7261

---------

Co-authored-by: Nadir Miralimov <riid@pm.me>
Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-05-11 21:33:36 +00:00
Matt Fiddaman
d015858e4a persist cleanup templates in the DB (#7794)
* add cleanup DB structure

* persist cleanup templates in the DB

* note

* Update packages/loot-core/src/types/models/cleanup-templates.ts

Co-authored-by: Julian Dominguez-Schatz <julian.dominguezschatz@gmail.com>

* jfdoming feedback

* coderabbit

---------

Co-authored-by: Julian Dominguez-Schatz <julian.dominguezschatz@gmail.com>
2026-05-11 21:30:51 +00:00
dependabot[bot]
f3a9c1a02c Bump mermaid from 11.12.1 to 11.15.0 (#7801)
Bumps [mermaid](https://github.com/mermaid-js/mermaid) from 11.12.1 to 11.15.0.
- [Release notes](https://github.com/mermaid-js/mermaid/releases)
- [Commits](https://github.com/mermaid-js/mermaid/compare/mermaid@11.12.1...mermaid@11.15.0)

---
updated-dependencies:
- dependency-name: mermaid
  dependency-version: 11.15.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-11 21:10:29 +00:00