* [AI] Revert crypto.randomUUID back to uuid library
Partial revert of #7529. Restores the `uuid` package dependency in
api, crdt, desktop-client, loot-core, and sync-server, swapping every
`crypto.randomUUID()` call introduced by that PR back to `uuidv4()`
(and the `uuid()` alias in RuleEditor.tsx and ruleUtils.ts where it
was previously used). The lint rule, docs entry, and `vi.mock('uuid')`
test setup are restored as well. The `fs-extra` removals in
desktop-electron from the same PR are left in place.
https://claude.ai/code/session_01KTg1g416Jdjf5feGke8MQw
* Add release notes for PR #7734
* [check-spelling] Update metadata
Update for https://github.com/actualbudget/actual/actions/runs/25480733101/attempts/1
Accepted in https://github.com/actualbudget/actual/pull/7734#issuecomment-4394811498
Signed-off-by: check-spelling-bot <check-spelling-bot@users.noreply.github.com>
on-behalf-of: @check-spelling <check-spelling-bot@check-spelling.dev>
* [AI] Use the uuidv4 alias in RuleEditor and ruleUtils
The previous commit preserved the `v4 as uuid` alias in these two files
to match their pre-#7529 state, but the project convention (and lint
rule message) is `v4 as uuidv4`. CodeRabbit flagged the inconsistency,
so normalize the alias and call sites in both files.
https://claude.ai/code/session_01KTg1g416Jdjf5feGke8MQw
* [AI] Pin uuid to ^11.1.0 to fix Electron e2e
uuid v13 is ESM-only (no CJS entry, only an exports map with `node` and
`default` conditions both pointing to ESM files). The Electron backend is
bundled as CJS by `loot-core/vite.desktop.config.mts` and loaded via a
dynamic `await import(process.env.lootCoreScript!)` in the
desktop-electron utilityProcess; that pipeline appears to fall over on
the ESM-to-CJS transform of uuid v13 in Vite 8 / rolldown, which makes
the Functional Desktop App e2e job fail consistently while every other
check (web e2e, VRT, unit tests, lint, typecheck) passes.
uuid v11 still ships `dist/cjs/index.js`, so pinning each workspace's
uuid range to ^11.1.0 sidesteps the resolution path entirely. The API
is unchanged (`v4` is still the same export), so no source-code changes
are needed.
https://claude.ai/code/session_01KTg1g416Jdjf5feGke8MQw
* [AI] Capture Electron stdout/stderr in desktop e2e fixture (TEMP DEBUG)
Pipe the Electron main+utility process stdout/stderr into both the
playwright runner stderr and an electron.log file inside the test's
output directory. This makes the actual backend error visible in the
Functional Desktop App job output and in the desktop-app-test-results
artifact.
Will be reverted once the failure cause is identified.
https://claude.ai/code/session_01KTg1g416Jdjf5feGke8MQw
* Revert "[AI] Capture Electron stdout/stderr in desktop e2e fixture (TEMP DEBUG)"
This reverts commit 4cb5148859.
* [AI] Fix rebuild-electron and bump uuid back to ^13.0.0
The Functional Desktop App e2e job had been failing with
ERR_DLOPEN_FAILED on better-sqlite3 (NODE_MODULE_VERSION 127 vs 140),
which surfaced because this PR's yarn.lock change invalidated the
GitHub Actions node_modules cache. With a fresh install,
rebuild-electron is responsible for compiling better-sqlite3 and
bcrypt against Electron's ABI — but since #7712 the script has been
running from the repo root, where neither module is a direct
dependency, so electron-rebuild silently exits as a no-op.
Restore the `-m ./packages/desktop-electron` scoping that #7712
removed, so the rebuild actually finds and rebuilds the modules.
This fix is technically out of scope for this PR but it blocks any
PR that touches yarn.lock.
Also revert the `^11.1.0` pin from the previous commit back to
`^13.0.0`. The pin was based on a wrong hypothesis (that uuid v13's
ESM-only packaging was breaking the bundle); now that the real
cause is identified, there is no reason to deviate from the
pre-#7529 version.
https://claude.ai/code/session_01KTg1g416Jdjf5feGke8MQw
---------
Signed-off-by: check-spelling-bot <check-spelling-bot@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com>
* fix: only count failed attempts against auth rate limit
Add skipSuccessfulRequests: true to authRateLimiter so that successful
logins do not consume quota. This fixes breakage for API clients
(actual-cli, actual-mcp, custom scripts) that re-authenticate per
operation — they always provide the correct password, so they should
never be rate-limited.
Brute-force attackers generate repeated failures and still hit the wall.
Fixes#7706
* Update upcoming-release-notes/7706.md
Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>
* fix: rename release note to match PR number
---------
Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>
* [AI] cover existing template engine logic with regression tests
Adds tests for goal template behavior that predates this PR so the
suite can be cherry-picked onto master to confirm no regressions. No
production code changes.
Covers:
- init() validation: schedule names, by/schedule priority match, past
by-target with and without annual/repeat, percentage source not
found, special source aliases, duplicate limit/spend/goal
directives, weekly limit missing start date, invalid limit period,
unrecognized periodic period
- runRemainder cap clamping and hideDecimal fraction removal
- Income-category branch in runTemplatesForPriority
- getLimitExcess against an aggregate weekly cap
- Past by-target rolling forward via the annual period
- runSchedule full=true (no sinking accumulation), percent and fixed
adjustments, completed-schedule filtering, past-date error for
non-repeating schedules, monthly/weekly/daily sinking contribution
branches when interval exceeds the pay-month-of cap, surplus
absorption when last-month balance exceeds the target, and
tracking-budget mode forcing all schedules pay-month-of
- applyMultipleCategoryTemplates orchestration: per-category writes,
cross-category priority clamping when funds run out, error
notification path
- applyTemplate force=false skipping already-budgeted categories
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* note
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: restored default functionality from v26.3.0 for reimport deleted transactions. import-reimport-deleted is now a synced pref that persists between imports
* release notes
* release note update
---------
Co-authored-by: Alec Bakholdin <alecbakholdin.com>
* Refactor to use directed, weighted graph as datamodel
* Fix percentage labels
* Reimplement sorting and topN handling
* Fix typing. Show toBudget on graph.
* Implement better DAG model
* Fix Other-grouping with new datamodel
* Add global sorting
* Reorder spreadsheet code for clarity
* Add percentageLabels back
* Fix all sorting modes
* Better color handling
* Handle if overbudgeted
* Fix filtering issue related to hidden nodes for Spent report
* Implement enums for special names
* Linting and typechecking
* Add layer selectors
* Trim SankeyCard
* Fix issue with empty nodes making the graph unreadable
* Add release note
* Update release note
* Reorder code
* Address coderabbit comments
* Ensure that layer-from and layer-to cannot be equal
* Update layer selectors to match selected view mode
* Fix wrong graph object reference
* Cap regex length
* Fixed wrong layer assignment for budget income categories
* Make translation not optional in createSpreadsheet
* Use predefined suffix for 'Other'
* Avoid invalid layer selection for Budgeted
* Update VRT screenshots
Auto-generated by VRT workflow
PR: #7582
* Import translation in spreadsheet, instead of passing as argument
* Remove all non-null assertions and handle safely
* Fix most uses of 'as'
* Fix issues hiding Other categories and giving wrong toBudget value
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* [AI] Notify Discord when nightly theme catalog scan fails
Adds an if: failure() step to the validate-theme-catalog job that posts a
minimal alert to the DISCORD_WEBHOOK_URL webhook with a link back to the
failing workflow run. Fires on both theme validation failures (script exits
1) and earlier step failures (checkout/setup), so infrastructure breakage
is also surfaced. nofail: true keeps a Discord outage from cascading into
a red job.
* [AI] Drop setup comment from Discord notify step
* [AI] Move Discord notify to its own job gated by an environment
Splits the notify step into a separate notify-failure job that depends on
validate-theme-catalog and runs only on failure. The new job binds to the
nightly-alerts GitHub Environment so the DISCORD_WEBHOOK_URL secret is
scoped to a dedicated environment rather than inherited at the repo level
(zizmor secrets-without-environment).
* [AI] Add release notes for 7595
---------
Co-authored-by: Claude <noreply@anthropic.com>
* [AI] Fix Docker build for workspace:* dependencies
Since @actual-app/crdt became a workspace:* dep, `yarn workspaces focus
--production` creates relative symlinks in node_modules that dangle when
only node_modules is copied into the prod image, breaking local Docker
builds with ERR_MODULE_NOT_FOUND: @actual-app/crdt.
Dereference yarn's workspace symlinks in the builder stage with `cp -RL`
so the prod stage can copy a self-contained node_modules without needing
to enumerate which workspace:* deps exist. Adding a new workspace:* dep
now requires zero Dockerfile changes.
Also move the sync-server .dockerignore to the repo root (and drop stray
local node_modules / .git / .yarn caches from the build context), since
docker builds use the repo root as context — the old sync-server-level
file was no longer being applied.
Fixes#7561.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] Strip dev-only dirs from dereferenced workspace packages
The generic `cp -RL` step copies full workspace package trees into the
image (src/, e2e/, tests, build-stats, etc.). Remove them after the
dereference — they're not needed at runtime, and skipping them recovers
~67MB from the final image on both alpine and ubuntu variants.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] Rephrase 7564 release note to be user-facing
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] Add per-schedule custom upcoming length override
* [AI] Add release notes for PR #7434
* [AI] Add custom length option to per-schedule upcoming length selector
* [AI] Deduplicate preset values and guard against malformed custom upcoming length
* [autofix.ci] apply automated fixes
* [AI] Retrigger CI (flaky accounts E2E test)
* [AI] Improve schedule editor layout for upcoming length field
Restructure the custom upcoming length from a checkbox + conditional
dropdown into a proper FormField with a single Select that includes
"Use global default" as the null option. Place it in a responsive
side-by-side row with the auto-post checkbox, consistent with the
form's existing layout patterns.
* [AI] Address CodeRabbit review feedback
Tighten custom upcoming length validation with a proper regex
instead of a loose hyphen check. Replace fixed height with minHeight
on the auto-post checkbox row to avoid clipping translated labels.
* Update VRT screenshots
Auto-generated by VRT workflow
PR: #7434
* [AI] Fix custom_upcoming_length not persisting on mobile
The mobile schedule save handler was missing the custom_upcoming_length
field from the payload sent to schedule/create and schedule/update.
---------
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>
Co-authored-by: youngcw <calebyoung94@gmail.com>
* [AI] Expose API entity types via @actual-app/api/models
Adds a new `./models` subpath export on `@actual-app/api` that re-exports
the public API entity types (`APIAccountEntity`, `APICategoryEntity`,
`APICategoryGroupEntity`, `APIFileEntity`, `APIPayeeEntity`,
`APIScheduleEntity`, `APITagEntity`, `AmountOPType`) from
`@actual-app/core/server/api-models`. Consumers can now import these types
from a stable public entry point instead of reaching into core internals:
import type {
APICategoryEntity,
APICategoryGroupEntity,
} from '@actual-app/api/models';
Uses `export type *` so the compiled `dist/models.js` is empty and no
runtime code is added. The Vite lib config is expanded to a multi-entry
map (`index`, `models`) so both bundles are produced, and tsgo already
emits `@types/models.d.ts` via the existing `declarationDir` setup.
* Add release notes for PR #7581
* Modify release notes for API model exports
Updated category from 'Features' to 'Enhancements' and added API export details.
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* [AI] Add nightly CI scan for custom theme catalog
Adds a scheduled GitHub Actions workflow that fetches `actual.css` from
every repo in `customThemeCatalog.json` and runs it through the same
`embedThemeFonts` + `validateThemeCss` pipeline the app uses at install
time. Failing themes fail the job so maintainers get an alert when a
third-party repo introduces a regression.
The scan treats fetched CSS as opaque text: never executed, never
injected into a DOM, size-capped at 512 KB per file, 15s per fetch,
restricted to raw.githubusercontent.com with redirects disabled, and
run with `contents: read` permissions only. Each catalog `repo` is
schema-checked against `owner/repo` before being interpolated into
the URL.
* [AI] Simplify theme catalog scan
- Reuse `CatalogTheme` type from customThemes instead of duplicating as
`CatalogEntry` in the script.
- Hoist `appendFileSync` to the static `node:fs` import; drop the dynamic
import inside `writeStepSummary`.
- Drop the narrative header docstring and the trailing `// ...` comments
that just restated constant names.
- Drop the redundant URL-prefix re-check inside the CSS fetch helper;
the single call site constructs the URL from a pinned literal.
- Drop the 250 ms inter-request delay (GitHub Raw rate limits are not
relevant for 21 requests, and the trailing delay was idle wall-clock
against the 10-min job budget).
- Give each font fetch inside `embedThemeFonts` its own 15 s timeout
via `AbortSignal.any`, instead of sharing one signal across every
font in a theme. Drop the now-unnecessary caller-supplied signal
from the CI call site.
* [AI] Fix lint on theme catalog scan imports
* [AI] Speed up and stabilize Playwright e2e tests
- Serve the prebuilt browser bundle via bin/serve-build.mjs in CI to
skip per-shard Vite startup; 3-shard matrix with 4 workers each.
- Disable CSS animations in non-VRT runs via a fixture-level init
script; bump expect timeout to 10s for AutoSizer-bound assertions.
- Use page.evaluate() for React Aria button clicks and a native value
setter + single input event for controlled-input fills to eliminate
React Aria re-render races in createAccount and Payee/Category
autocompletes.
- Click the matching option directly (instead of Enter on a not-yet-
highlighted list) in mobile transaction and schedule autocompletes.
- FocusableAmountInput.applyText reads the DOM input value so the
typed amount survives a blur that fires before React flushes the
onChange state update under CPU contention.
- MobileTransactionEntryPage.fillAmount waits for the outer display
button (reads parent props.value) so async rules-run completes
before the next fillField snapshots the transaction.
- MobileNavigation dispatches nav link clicks through evaluate() to
bypass Playwright's viewport-stability check against the navbar's
react-spring transforms.
- MobileBudgetPage summary-button lookups use locator.or().waitFor()
instead of an isVisible() cascade.
- ConfigurationPage.startFresh/createTestFile wait for the account
header / budget table to mount before returning.
- Workflow hardening: persist-credentials=false on all actions/checkout
and top-level permissions: contents: read (zizmor findings).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] Apply animation-disable init script to browser.newPage pages
The previous implementation extended the test-scoped `page` fixture,
but every test creates its own page via `browser.newPage()` and never
uses the fixture-provided page — so the init script was a no-op in
every test.
Move the wrap to the worker-scoped `browser` fixture: intercept
`browser.newPage` so each page created that way has `addInitScript`
applied before the caller can navigate to it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The `Publish nightly npm packages` workflow started failing at the
"Pack the core package" step with:
Cannot find module '@actual-app/crdt' or its corresponding type declarations.
PR #7541 switched `@actual-app/crdt`'s package.json to conditional
exports (`types` → `./dist/index.d.ts`). `yarn pack` for
`@actual-app/core` triggers a prepack that runs `tsgo -b`, which now
resolves `@actual-app/crdt` via the `types` condition and expects
`packages/crdt/dist/index.d.ts`. Nothing was building crdt first
because loot-core's tsconfig didn't declare it as a project
reference.
Fix: declare the project reference so `tsgo -b` walks the graph and
builds crdt before loot-core. Sibling packages already do this.
Also adopt `@monorepo-utils/workspaces-to-typescript-project-references`
to keep each package's tsconfig `references` in sync with its
`workspace:*` deps, and wire it into a new `yarn check:tsconfig-references`
step in the `check` CI job plus lint-staged. Running the tool added
`../desktop-client` references to sync-server and desktop-electron
(both declare `@actual-app/web` as a workspace dep even though they
only use it at runtime via `require.resolve`); the extra references
are harmless — in CI the corresponding build is already cached by
earlier steps.
https://claude.ai/code/session_01AA2gEMqX24GWeq5BovNmaz
* [AI] Add AI Usage Policy for contributors
Add a contributor-facing AI Usage Policy page modeled on ESLint's version,
covering disclosure, human-only interaction with maintainers, and author
responsibility. Wire it into the docs sidebar, link it from the contributing
index and the root CONTRIBUTING.md.
https://claude.ai/code/session_012RspFcLedoUjbEYknJYPiL
* [AI] Unwrap AI policy paragraphs, renumber release note
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>