* [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>
* [AI] Run setup once per workflow and fan out via needs
Add a prep `setup` job at the top of `check.yml` and `build.yml`, and
make every other job in those workflows declare `needs: setup`.
The composite action in `.github/actions/setup` caches `node_modules`
keyed on `yarn.lock`. When that hash changes (dep-bump PRs, master after
a merge), the cache is cold and every fan-out job races to run
`yarn --immutable` in parallel — one wins the cache save, the rest do
redundant work. Serialising through a single `setup` job warms the
cache once so downstream jobs restore instantly and skip yarn install
via the existing `if: steps.cache.outputs.cache-hit != 'true'` guard.
No changes to the composite action or cache keys. `e2e-test.yml` is
intentionally left alone.
* [AI] Harden setup jobs and add release note
Address zizmor code-scanning findings on the new `setup` jobs added in
the previous commit:
- Scope `permissions: contents: read` so the job no longer inherits
workflow-default write permissions.
- Pass `persist-credentials: false` to `actions/checkout` so the GitHub
token isn't left on disk for later steps that don't need it.
Add `upcoming-release-notes/7551.md` to satisfy the release-notes PR
check.
* [AI] Disable credential persistence on build.yml checkouts
Each of `api`, `crdt`, `web`, `cli`, `server` in build.yml does
`actions/checkout` (which writes the GitHub token into `.git/config`)
and then uploads build artifacts in the same job. Zizmor flags this as
"credential persistence through GitHub Actions artifacts" because a
misconfigured upload path could capture `.git/config` and leak the
token.
None of these jobs push or write to git, so drop the credential
persistence via `persist-credentials: false` on the checkout.
* [AI] Disable credential persistence on check.yml checkouts
None of the jobs in check.yml (`constraints`, `lint`, `typecheck`,
`validate-cli`, `test`, `migrations`) push or write to git, so pass
`persist-credentials: false` to their `actions/checkout` calls to
resolve the zizmor "credential persistence" finding. Mirrors the fix
just applied to build.yml.
---------
Co-authored-by: Claude <noreply@anthropic.com>
* [AI] Disable bundle minification for readable production error messages
The desktop-client had dead terserOptions (no `minify: 'terser'` was set, so
Vite's default esbuild minifier ran with name mangling). The loot-core and
plugins-service workers used Terser with mangle:false but still compressed.
Set `minify: false` across all three browser build configs so production
stack traces are human-readable.
https://claude.ai/code/session_01VEywxebiNYAgJia35fygQx
* [AI] Rename release note to match PR number
https://claude.ai/code/session_01VEywxebiNYAgJia35fygQx
---------
Co-authored-by: Claude <noreply@anthropic.com>