* [AI] initial
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] review pass 1
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] review pass 2
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] review pass 3
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] review pass 4
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* remove dev localstorage gate
* [AI] block migration for #goal and #cleanup directives
Detect goal templates (`type: 'goal'`) and `#cleanup` lines in the
category notes before rendering the editor. When either is present,
show an unsupported-directive notice instead of the modal body so the
migration helper never runs and the Save flow can't silently overwrite
`template_settings.source` to `'ui'` (which would stop the engine from
reading the notes for those directives).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* note
* [autofix.ci] apply automated fixes
* [AI] don't synthesise refill for monthly+limit simple templates
A `#template 50 up to 200` line parses to a simple template with both
monthly and limit set. The migration helper previously expanded that
into limit + refill + periodic, but the engine's runSimple just budgets
the monthly amount and clamps to the cap — there is no implicit refill
to undo.
Only synthesise the refill for the limit-only form
(`#template up to 200`), where runSimple's fallback returns
limitAmount - fromLastMonth. Update the migration test to match.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] coderabbit fixes
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] coderabbit fixes v2
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] attribute schedule batch contributions per template
Replace the equal-split fallback with each schedule's actual monthly
contribution (computed inside runSchedule) so the per-row UI projection
reflects real cost rather than total / count.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] coderabbit v3
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] coderabbit v4
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] fix unclearable integer inputs in automation editors
GenericInput type=number fired onChange on every keystroke and the
dispatcher coerced empty/0 back to 1, so the field snapped back before
the user could retype. Switch to a local string state that commits and
clamps on blur, with min/step constraints on the native input.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] coderabbit v5
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Update packages/desktop-client/src/components/budget/goals/templateHelpers.tsx
Co-authored-by: Julian Dominguez-Schatz <julian.dominguezschatz@gmail.com>
* Update packages/desktop-client/src/components/budget/goals/templateHelpers.tsx
Co-authored-by: Julian Dominguez-Schatz <julian.dominguezschatz@gmail.com>
* [autofix.ci] apply automated fixes
* remove comment
* s/rule/automation
* update note wording
* tweak hold checkbox wording
* deduplicate translation strings
* rename buildPresetSeeds
* deduplicate example text
* update wording about how easy automations are
* s/P/Priority
* reuse week template
* replace week displayType with fixed
* lodash debounce
* [autofix.ci] apply automated fixes
* more graceful error handling
* change default priority to 1
* align ui and text language for available funds & all income
* fix tests
* coderabbit v{lost_count}
* more coderabbit
* extract isValidYearMonth
* extract automationExamples
* split out into multiple files
* split down templateHelpers
* [AI] cover PR additions to template engine
Tests targeting code introduced in this PR:
- per-template attribution map (perTemplateContribution) on
CategoryTemplateContext for sibling periodic templates, batched `by`
templates, limit-clamped totals, and remainder weights
- runBy now returns { toBudget, perTemplateNeed }
- runSchedule now returns perScheduleMonthly keyed by trimmed name,
including a multi-schedule pay-month-of + sinking-fund mix
- dryRunCategoryTemplate end-to-end through computeTemplates, in both
narrow-scope and wide-scope (remainder / available funds) branches
- checkPercentage now accepts category ids (UI form), not just names
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix repeat every month error
* coderabbit again.
* Update packages/desktop-client/src/components/budget/goals/automationMessages.tsx
Co-authored-by: Julian Dominguez-Schatz <julian.dominguezschatz@gmail.com>
* Update packages/desktop-client/src/components/budget/goals/automationMessages.tsx
Co-authored-by: Julian Dominguez-Schatz <julian.dominguezschatz@gmail.com>
* [autofix.ci] apply automated fixes
* plural aware translation strings
* move + out of translation string
* fix types
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Julian Dominguez-Schatz <julian.dominguezschatz@gmail.com>
* Enable auto enrichment in coderabbit configuration
* Add release notes for PR #7664
* Change category to Maintenance and update description
Updated the category from 'Features' to 'Maintenance' and revised the description.
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* [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>
* Release notes
* [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] Widen toMatchThemeScreenshots matcher to accept Page
The matcher's runtime impl already handled both Page and Locator
(via `typeof locator.page === 'function'` branch), but the type only
declared Locator. Call sites pass a Page (`expect(page).toMatchThemeScreenshots()`),
which now compiles cleanly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] Type window.Actual in e2e fixtures and refactor matcher
- Pull loot-core/typings/window.ts into the e2e tsconfig include so
the ambient `window.Actual` augmentation is visible.
- Refactor toMatchThemeScreenshots to derive a Page once via
`'page' in target`, then call evaluate on the page consistently.
The previous union-typed access (locator.evaluate, locator.page)
didn't typecheck on Locator | Page.
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>
* [AI] Standardise crdt to drop tsconfig.build.json
Apply the same simplification as loot-core and api: a single tsconfig
per package, with `files`-field negations preventing test
declarations from being published.
Note: this also fixes a pre-existing issue where crdt was shipping
`crdt/timestamp.test.d.ts` and `crdt/merkle.test.d.ts` to npm. The
old `tsconfig.build.json` excluded test files from declaration emit,
but the `typecheck` script (`tsgo -b` via the main tsconfig) had
already emitted them into `dist/` and the build did not clean
first, so they were packed via `"files": ["dist"]`.
After this change, `yarn pack --dry-run` packs only production
declarations (10 .d.ts files) and excludes the test ones.
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 iconography, placeholders, and dropdown chevrons to mobile transaction form
Brings the mobile new-transaction screen in line with the design mockup so
each field shows a leading icon, dropdown affordances surface on the right,
and empty pickers display a placeholder hint.
- Extend TapField with optional icon/placeholder props (placeholder uses
formInputTextPlaceholder when value is empty).
- Extend InputField with an optional icon prop that wraps the input in a
bordered row when present.
- Wire SvgUser/SvgTag/SvgWallet/SvgCalendar/SvgNotesPaper into Payee,
Category, Account, Date, and Notes for both the main form and split
child rows; add a SvgCheveronDown rightContent on Category and Account.
* Add release notes for PR #7639
* [AI] Forward consumer style to inner Input in icon path of InputField
CodeRabbit flagged that consumer-supplied `style` was being applied to the
wrapper View in the icon branch, so input-targeted properties like
`appearance: 'none'` and `minWidth: '150px'` on the Date field never reached
the underlying `<input type="date">`. Move the style spread onto the inner
Input so those styles take effect.
* [AI] Add dropdown chevron to mobile Payee field
Show the chevron on the Payee TapField in both the main form (as the
fallback when neither the Save-location nor Nearby-payee button applies)
and in split child rows, matching Category and Account.
* Update VRT screenshots
Auto-generated by VRT workflow
PR: #7639
* [AI] Hide native date picker icon on mobile transaction form
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Update VRT screenshots
Auto-generated by VRT workflow
PR: #7639
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* Simplify desktop client browser build
* [AI] Move browser build orchestration into vite config and lage
Moves loot-core worker build, public/ staging (migrations, default-db,
sql-wasm, data-file-index), and build-stats wiring from the deleted
packages/desktop-client/bin/build-browser shell script into a
lootCoreBackend vite plugin in packages/desktop-client/vite.config.mts.
Adds a build:browser target to lage.config.js so bin/package-browser
runs as a single `lage build:browser --to=@actual-app/web` call, with
crdt + loot-core built via lage's ^build dependency before the
desktop-client build.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Refactor e2e-test workflow and update desktop-client configurations
* [AI] Move plugins-service staging into desktop-client vite config
Declares plugins-service as a workspace devDependency of @actual-app/web
so lage's ^build edge picks it up automatically in the build:browser
pipeline, and moves the cross-package file staging (production copy +
dev serving) into vite.config.mts, mirroring the lootCoreBackend
pattern. Drops the plugins-service shell wrapper script and simplifies
its package.json scripts to invoke vite build directly. Updates root
start:browser to run plugins-service watch in parallel with the dev
server instead of pre-building once.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] Sync tsconfig project references for plugins-service edge
Follow-up to the plugins-service workspace edge: adds the
../plugins-service project reference in packages/desktop-client/tsconfig.json
via yarn sync:tsconfig-references.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Release notes
* [AI] Ignore .venv/ so lage's git hasher skips Electron CI's Python venv
Electron CI provisions a Python virtualenv at the repo root for
setuptools. With browser builds now routed through lage, lage's
git hash-object pass walks untracked-not-ignored files and fails on
the venv's broken lib64 symlink ("fatal: Unable to hash .../.venv/lib64").
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] Bake Weblate translations back into VRT/e2e bundle
build-web set download-translations: false and relied on bin/package-browser's
ad-hoc git clone + git pull. That path is fragile inside the playwright
container, so vite's import.meta.glob('/locale/*.json') frequently produced an
empty languages map and the bundle shipped with no en.json. VRTs then rendered
source-code English and diffed against snapshots authored from Weblate strings.
Route translation provisioning back through actions/checkout (download-translations: true)
in build-web and vrt-update-generate, and add --skip-translations to bin/package-browser
(mirroring bin/package-electron) so the in-script git pull is bypassed when CI
has already staged the locale dir.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] Skip translation cloning in build-web bundle for VRT determinism
bin/package-browser used to unconditionally clone actualbudget/translations
before vite ran, baking Weblate en.json into the build artifact. With the
e2e-test pipeline now serving that artifact via serve-build.mjs, VRT
screenshots ended up rendering Weblate strings — drifting from the snapshots,
which were authored against source-code English (master VRTs ran on vite dev
without a locale dir).
Pass --skip-translations to bin/package-browser from build-web so the bundle
ships with no locale chunks. download-translations stays 'false' across the
e2e-test and vrt-update-generate workflows, matching the prior behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: disable contextual alternates in Inter font to prevent unwanted character substitution
Fixes#6351
The Inter font calt feature replaces x between digits with a
multiplication sign. Disable it while preserving ss01 and ss04.
* [autofix.ci] apply automated fixes
* Add release notes for PR #7375
* [autofix.ci] apply automated fixes
* Fix release notes category casing
* Add authors field to release notes
* Update upcoming-release-notes/7375.md
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>
* fix: show consistent 'Date is a required field' error in add transaction
When adding a new transaction with a missing date, the UI surfaced the
generic 'Something internally went wrong' error instead of a specific
message, while missing account showed 'Account is a required field'.
Add an analogous date validation mirroring the existing account check,
so both required-field errors are consistent.
Fixes#7424
* fix: prevent empty date from reaching server when clearing date input
When adding a transaction, clearing the date field and clicking Add
caused DateSelect's blur handler to save an empty string as the date
via onSelect(''). The server rejected '' as an invalid date, emitting
a generic "Something internally went wrong" error.
The previous fix (adding a date check in the shouldAdd guard) didn't
work because the cell save fires on blur BEFORE shouldAdd runs — by
the time shouldAdd checks the date, it's either already sent to the
server or reverted by DateSelect's clearOnBlur logic.
Fix: change DateSelect's empty-input blur path to restore the previous
valid date instead of propagating ''. This prevents the invalid value
from ever reaching the cell save handler or the server.
Verified locally: clear date → click Add → date restores to previous
value and transaction saves normally. No generic error.
Fixes#7424
* [AI] Keep mobile transaction amount field at a consistent height when empty
When the user backspaced every digit while editing the amount on the
mobile transaction form, the inner <Text> span had no content and
collapsed the pill to zero height. Fall back to a non-breaking space so
the line box keeps its natural font-driven height even when the input
is momentarily empty.
* Add release notes for PR #7638
* [AI] Use amountToCurrency(0) instead of nbsp when amount input is empty
Mirrors the unfocused-zero display rather than rendering a blank pill,
and respects the user's hideFraction pref and locale separators
through amountToCurrency.
* Simplify description of mobile transaction field height
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* [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>
* [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>
* [AI] Cache the CLI's local budget between invocations
Every `actual <cmd>` call currently delta-syncs the budget with the
sync server via `api.downloadBudget`, which hits the server's
500-req/min rate limit on scripted workflows. Actual is local-first:
once the budget is on disk, most read commands do not need fresh
server data.
Introduce a CLI-only cache layer inside `withConnection` that
decides per invocation whether to skip, sync, or re-download:
- Cache state lives at `{dataDir}/.actual-cli/{syncId}/state.json`,
keyed by `syncId` to avoid the chicken-and-egg of not knowing the
on-disk `budgetId` before the first download. The on-disk id is
resolved via `api.getBudgets()` and persisted after first download.
- Read commands (list, balance, query run, …) skip the `/sync`
call while `now - lastSyncedAt < cacheTtl`. Write commands
(create, update, delete, set-*, etc.) sync before and after the
operation to keep server state consistent.
- Encrypted budgets force a sync per call since `api/load-budget`
does not re-verify the password.
- New `proper-lockfile`-backed shared/exclusive lock serializes
writes while allowing parallel reads. Reader markers live in
`{meta}/readers/`; writers sweep stale markers by PID.
New `actual sync` command with three modes: default (sync now),
`--status` (print cache age, TTL, stale flag), `--clear` (delete
cache, holding the exclusive lock to avoid racing writers).
New config surface, following the existing flag → env → config file
→ default precedence chain:
- `--cache-ttl <s>` / `ACTUAL_CACHE_TTL` / `cacheTtl` (default 60)
- `--refresh` / `--no-cache`
- `--lock-timeout <s>` / `ACTUAL_LOCK_TIMEOUT` / `lockTimeout` (10)
- `--no-lock` / `ACTUAL_NO_LOCK` / `noLock`
Every `withConnection` call site now passes an explicit
`{ mutates: boolean, skipBudget?: boolean }` so read/write intent is
visible at the edge.
The old `budgets sync` subcommand is removed — it silently diverged
from the new top-level `actual sync`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] Simplify CLI cache/lock internals
Use a discriminated SyncDecision union so connection.ts no longer needs
non-null assertions on the cached state. Thread the resolved CliConfig
through withConnection's callback to drop duplicate resolveConfig calls
in the sync and budgets commands. Extract an errorCode helper and
replace the existsSync+readdirSync TOCTOU pattern in the reader-wait
polling loop with a single readdir that tolerates ENOENT.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* [AI] Address PR #7539 review comments
- cache.ts: use a unique per-writer tmp filename so concurrent shared-
lock writers (encrypted budgets, --refresh, stale TTL) don't clobber
each other's publish and silently drop state updates.
- index.ts/config.ts: fix --no-cache and --no-lock flags. Commander
stores --no-foo under the positive key (cache/lock) and an explicit
false default makes the flag a no-op; the previous code also read
the wrong keys (noCache/noLock) so the flags had no effect at all.
Derive refresh/noLock from the correct keys.
- budgets.ts: invert encryption password precedence so the subcommand
flag (--encryption-password) wins over env/config-file values.
- sync.ts: report stale=true in --status when lastSyncedAt is in the
future, matching decideSyncAction's clock-skew handling.
- connection.ts: drop unnecessary `as` cast on api.getBudgets() now
that the return type is Promise<APIFileEntity[]>.
- utils.ts: parseBoolEnv throws on unrecognized values instead of
silently returning undefined so typos like ACTUAL_NO_LOCK=yes fail
loudly.
- Shorten 7539 release note to a single user-facing sentence.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
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>