mirror of
https://github.com/actualbudget/actual.git
synced 2026-11-14 21:15:41 -06:00
+104![dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>](/assets/img/avatar_default.png)









Tim
GitHub
Matiss Janis Aboltins
Claude Fable 5
youngcw
Kennedy242
Matt Fiddaman
Michael Clark
Wing Chang
dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
lelemm
Cursor Agent
lelemm
Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
github-actions[bot] <github-actions[bot]@users.noreply.github.com>
github-merge-queue
github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Mats Nilsson
Juulz
Sebastián Maluk
Joseph Coffa
narutamaaurum
Henghui Zhu
Kyle Slattery
coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copilot
Manaf
David Bettis
dzickusrh
Gemini
Matiss Janis Aboltins
Kevin
Alec Bakholdin
Stephen Brown II
yoshiboi3
Awais Saeed
Awais Saeed
Trevin Chow
Matt Farrell
kikiminyes
Pavel Voronov
mheiland
ItsThatDude
Julian Dominguez-Schatz
Tifan Dwi Avianto
Andy Fiddaman
Tom Mills
thromer
JinHyuk Sung
Oskar
Zannis Kalampoukis
Dustin Brewer
tabedzki
clintharris
Darwin Do
Carlisle Nightingale
Julien Vincent
edwei06
Frithjof Engel
Martin Lukas
Archit Goyal
Shivam S
deathblade666
Lucas
Ehtasham Yasin
tianrking
tianrking
distantvapor
Abdullah Khawaja
rznn7
rznn7
Tim Newsome
Nustinua
Atur Dana
Hossein Moosavi
Saahil Jaffer
Shahid
BasilTh
mooons
Ngoc Do
Russlan Ramdowar
Mustafa Akcanca
Binny Zupnick
Moshe Brevda
Gaurav Yadav
DiegoC
Kesley David
matt-fidd
Benoît Viguier
Gautam Singh
Andrea
mestredosmagos314
JerryNee
ReticentEclectic
Reza Namazi
Thelosopher
Marcin Radoszewski
Harsh Mathur
parapsychic
divinevalley
Sanidhya Awasthi
Sanidhya Awasthi
Caleb Burton
Som Samantray
cakeni
cakeni
Patrick Danilevici
Anju
Junyuan Zheng
davidlitmanowicz
DavidGrinberg
Haim Gelfenbeyn
Joel Brenstrum
007a1e2457
* [AI] Minify web bundle while keeping original identifiers (#8194) Enable the Oxc minifier for desktop-client and loot-core (kcab worker) production builds, but disable identifier mangling so user-reported stack traces keep the original function and variable names. The loot-core dev watch build stays unminified. Compression and whitespace removal cut the client JS from 14.0 MB to 10.5 MB and the backend worker from 5.2 MB to 3.6 MB. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * [AI] Fix git-guard false positives on heredoc and multi -m commit messages (#8192) * [AI] Fix git-guard false positives on heredoc and multi -m commits The [AI]-prefix check extracted the commit message with a greedy ".*-m" sed pattern, which broke in two real-world cases: - "git commit -m \"$(cat <<'EOF' ...)\"" — the pattern matched the command-substitution text on the first line and reported the message as "$(cat <<", blocking a correctly prefixed commit. - Multiple -m flags — the greedy match grabbed the LAST -m (typically a Co-Authored-By trailer), so valid commits were blocked and a missing prefix on the subject could slip through if the last paragraph happened to start with [AI]. Replace the extraction: find the FIRST -m/--message via shortest-prefix stripping (git takes the first -m as the subject), detect the heredoc-in-command-substitution form and read the subject from the heredoc body's first line, and otherwise check the inline string as before. Editor/amend/file-based commits remain untouched, as do the push, hook-skip, git-config and yarn-workspace guards. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Add release notes * [AI] Fail closed when heredoc detector matches without a body line The heredoc pattern also matches single-line command substitutions containing a here-string (e.g. <<< on one line), where reading the "second line" produced an empty message that the final check treated as "no inline message" and allowed — letting unprefixed subjects through. Take the first non-blank heredoc body line instead (matching git's leading-blank-line cleanup), and when no body line exists fall back to inline parsing so a statically unknowable message is blocked rather than waved through. Found by CodeRabbit review on #8192. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * fix import (#8201) * Memoizing useRechartsAnimation() (#8150) Fixes cash flow card text flashing Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv> * prioritise category name matches over group matches in the category autocomplete (#8171) * rank name matches above group matches * make testable * [AI] add test * note * :electron: Fix desktop app workflow for windows builds (#8211) * fix desktop app workflow for windows builds * release notes * append variable to github env so its available in other steps * pin to 2022 * release notes * Added Rose Pine themes to catalog (#8214) * Added Rose Pine themes to catalog * Added release notes * Fixed misplaced commas * Bump esbuild in the npm_and_yarn group across 1 directory (#8213) Bumps the npm_and_yarn group with 1 update in the / directory: [esbuild](https://github.com/evanw/esbuild). Updates `esbuild` from 0.25.10 to 0.25.12 - [Release notes](https://github.com/evanw/esbuild/releases) - [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG-2025.md) - [Commits](https://github.com/evanw/esbuild/compare/v0.25.10...v0.25.12) --- updated-dependencies: - dependency-name: esbuild dependency-version: 0.25.12 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Budget averages now exclude incomplete months (#8114) * add `includeIncomplete` to average templates Default behavior is now to exclude incomplete months when calculating the average. * add release note * Remove `includeIncomplete` setting * update menu actions * fix lint * exclude months before the first budgeted month * use first activity month instead of first budget month * update docs * [autofix.ci] apply automated fixes * use valid months in average e2e test * Apply suggestion from @tim-smart * round averages to integers * align e2e tests with new behavior * update vrt * Update upcoming-release-notes/average-complete.md --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: youngcw <calebyoung94@gmail.com> * Formula feedback improvements (#7989) * [AI] Add FORMATNUMBER and FORMATCURRENCY functions to address TEXT() formatting limitations Feedback from GitHub issue #5949: - Users reported TEXT(value, "$#,##0.00") doesn't properly format numbers with thousands separators - Users requested proper currency formatting for formula cards - Tooltip examples had inconsistent quote usage (backticks vs no backticks) Changes: 1. Added FORMATNUMBER() function for number formatting with thousands separators - Supports custom decimal places, thousands separator, and decimal separator - Example: FORMATNUMBER(1234567.89, 2) returns "1,234,567.89" 2. Added FORMATCURRENCY() function for currency formatting - Supports custom currency symbol, decimals, and separators - Handles negative values correctly - Example: FORMATCURRENCY(1234567.89, "$") returns "$1,234,567.89" 3. Fixed tooltip quote inconsistency - Removed backticks from formula examples in tooltips for consistency - All examples now use plain text without backticks 4. Updated function documentation in both queryModeFunctions.ts and transactionModeFunctions.ts 5. Added comprehensive tests for new formatting functions Note: BALANCEONDAY() and LASTSYNCEDBALANCE() functions were not implemented as they would require database access not available in the formula execution context. These would be better suited as future enhancements with proper architecture support. Co-authored-by: lelemm <lelemm@users.noreply.github.com> * [AI] Add support for line breaks in formula card results Feedback from GitHub issue #5949: - User @Juulz reported that line breaks don't work in formula cards - Requested ability to use CHAR(10) or CHAR(13) in CONCATENATE for multi-line displays - Example: Displaying multiple totals on separate lines instead of one long line Changes: 1. Added whiteSpace: 'pre-wrap' to FormulaResult span to preserve line breaks 2. Added textAlign: 'center' to keep multi-line text centered 3. Added wordBreak: 'break-word' to handle long words gracefully 4. Updated font size calculation to account for multi-line text: - Splits display value by line breaks - Calculates based on longest line length - Divides available height by number of lines to fit all lines Usage: Users can now use CHAR(10) in formulas to create line breaks: =CONCATENATE("Total 1: ", value1, CHAR(10), "Total 2: ", value2, CHAR(10), "Total 3: ", value3) This will display: Total 1: 100 Total 2: 200 Total 3: 300 Instead of all on one line. Co-authored-by: lelemm <lelemm@users.noreply.github.com> * [AI] Remove unusable formula functions that require cell ranges User requested cleanup of formula functions that don't make sense in Actual's context. Actual uses named expressions (like QUERY results and transaction fields) rather than spreadsheet-style cell ranges. Removed from queryModeFunctions.ts: - AVERAGEA, COUNT, COUNTA, COUNTBLANK, COUNTIF, COUNTIFS (use QUERY_COUNT instead) - MAXA, MINA (kept MAX and MIN which work with individual values) - SUMIF, SUMIFS, SUMPRODUCT, SUMSQ (use QUERY with filters instead) - Statistical functions: MEDIAN, MODE, STDEV, STDEVP, VAR, VARP, PERCENTILE, QUARTILE, RANK (these require arrays of data which don't exist in our context) - Lookup functions: VLOOKUP, HLOOKUP, INDEX, MATCH, LOOKUP (these require table arrays and cell references) - ISREF (checks for cell references which don't exist) Kept useful functions: - CHOOSE (works with index and individual values) - All math functions that work with individual numbers (SUM, AVERAGE, MAX, MIN, etc.) - All text functions (CONCATENATE, UPPER, LEFT, etc.) - All date functions (TODAY, YEAR, MONTH, etc.) - All logical functions (IF, AND, OR, etc.) - Query-specific functions (QUERY, QUERY_COUNT, BUDGET_QUERY, etc.) Updated syntax highlighting in codeMirror-excelLanguage.tsx to match. This cleanup makes the autocomplete suggestions more relevant and less confusing for users working with Actual's formula system. Co-authored-by: lelemm <lelemm@users.noreply.github.com> * [AI] Add comprehensive integration tests for formula rules and formula cards User requested integration tests with real database queries and complex nested formulas. These tests go beyond unit testing HyperFormula to validate the full integration with Actual's transaction rules and query system. Formula Rule Integration Tests (36 tests): - Basic formula operations with FORMATCURRENCY and nested IFs - Text manipulation: CONCATENATE, UPPER, LEFT, RIGHT, TRIM, PROPER - Math functions: ROUND, ABS, MAX, MIN, CEILING, SQRT, SUM, PRODUCT - Date functions: YEAR, MONTH, DAY, DAYS, EOMONTH, WEEKDAY - Logical functions: AND, OR, NOT, IFERROR, SWITCH - New formatting functions: FORMATNUMBER, FORMATCURRENCY with various options - Multi-line output with CHAR(10) for line breaks - Information functions: ISNUMBER, ISTEXT, ISEVEN, ISODD, ISBLANK - Error handling and validation - Complex nested formulas combining multiple function types Formula Card Integration Tests (14 tests): - Single and multiple query integration - Query results with FORMATCURRENCY and FORMATNUMBER - Percentage calculations across queries - Date-based query filtering - Complex nested calculations with multiple queries - Multi-line output with query results - Error handling with empty queries and division by zero - Running totals across accounts - MAX/MIN operations with query results Test Coverage: - Every formula function type is tested in at least one integration test - Tests use real database operations (insertAccount, insertPayee, insertCategory, etc.) - Tests validate complex nested formulas with 3-4 levels of nesting - Tests cover edge cases (empty results, division by zero, type mismatches) - All 50 integration tests pass ✅ Note: Tests demonstrate proper usage patterns for users: - Transaction amounts are in cents, divide by 100 for dollar display - Rules must have conditions on 'imported_payee' or 'payee' to be indexed - Query results are automatically converted from cents to dollars - FORMATCURRENCY places negative sign before currency symbol (standard format) Co-authored-by: lelemm <lelemm@users.noreply.github.com> * Potential fix for pull request finding 'Unused variable, import, function or class' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> * [AI] Make formatting functions respect app settings with locale-based fallbacks User feedback: FORMATCURRENCY and FORMATNUMBER should use app currency and number format settings instead of hardcoded defaults. Implementation approach: - Added async loadUserPreferencesForFormulas() function that queries preferences DB - Caches preferences globally to avoid repeated DB queries - Called on module initialization to load preferences once - Custom functions access cached preferences synchronously (no reactor needed) Settings hierarchy (priority order): 1. Explicit function parameters (if provided) 2. User's app settings (currency code, number format) 3. Locale-based defaults (inferred from user's language setting) Locale-based defaults: - en-GB → GBP (£) with comma-dot format - de/fr/es/it/nl → EUR (€) with dot-comma format - ja → JPY (¥) with comma-dot format - en-IN/hi → INR (₹) with comma-dot format - en-CA → CAD ($) with comma-dot format - en-AU → AUD ($) with comma-dot format - Default → USD ($) with comma-dot format Number format patterns: - comma-dot: 1,000.00 (US/UK/Canada) - dot-comma: 1.000,00 (Europe) - space-comma: 1 000,00 (France) - apostrophe-dot: 1'000.00 (Switzerland) - comma-dot-in: 1,00,000.00 (India) Usage examples: - FORMATCURRENCY(1234.56) → Uses app currency symbol and format - FORMATNUMBER(1234.56) → Uses app number format - FORMATCURRENCY(1234.56, "€") → Override with Euro symbol - FORMATNUMBER(1234.56, 2, ".", ",") → Override with custom separators Updated documentation to reflect that parameters are optional and use app settings. All tests pass ✅ Co-authored-by: lelemm <lelemm@users.noreply.github.com> * Update VRT screenshots Auto-generated by VRT workflow PR: #7451 * [AI] Fix type errors after rebase with master Co-authored-by: lelemm <lelemm@users.noreply.github.com> * [autofix.ci] apply automated fixes * Add release notes for PR #7451 * [AI] Fix category group filter support in formula card queries BUG: When adding a Category Group filter to a Query Definition in formula cards, nothing happened. Only category filters worked. FIX: Updated extractCategoryConditions to include both 'category' and 'category_group' fields, and updated getCategoriesFromConditions to properly expand category group conditions to their member categories using cat.group. The fix follows the same pattern used in budget-analysis-spreadsheet.ts. Co-authored-by: lelemm <lelemm@users.noreply.github.com> * [AI] Exclude adm-zip from browser bundle to fix 'process is not defined' error BUG: Browser console shows 'Uncaught ReferenceError: process is not defined' at adm-zip.js when loading the app. This is caused by adm-zip (a Node.js-only package) being incorrectly bundled into browser code. FIX: Add adm-zip to optimizeDeps.exclude in Vite config to prevent it from being bundled for browser environments. Co-authored-by: lelemm <lelemm@users.noreply.github.com> * [AI] HACK: Add global process polyfill in HTML to fix 'process is not defined' error - Removed ineffective optimizeDeps.exclude for adm-zip - Added inline script in index.html that runs before any module loads - Ensures process global is available before CommonJS wrappers execute - Polyfill includes process.env.NODE_ENV and mocked process.emit() - Fixes ReferenceError in inflater.js/adm-zip when theme chunks load Co-authored-by: lelemm <lelemm@users.noreply.github.com> * [autofix.ci] apply automated fixes * [AI] Fix 'process is not defined' by making db import dynamic in customFunctions Root cause: customFunctions.ts is imported by client-side code, but had a static import of '#server/db', causing entire server db module (and transitive dependencies like adm-zip) to be bundled into browser. Solution: Changed to dynamic import inside loadUserPreferencesForFormulas() so server code is only imported when actually called (server-side only). - Removed static 'import * as db from "#server/db"' from top of file - Added dynamic 'const db = await import("#server/db")' inside function - Removed HTML polyfill hack that was masking the root cause - Browser clients can still use CustomFunctionsPlugin (falls back to locale defaults) Co-authored-by: lelemm <lelemm@users.noreply.github.com> * [AI] Fix 'process is not defined' by separating client and server code Root cause: customFunctions.ts was imported by client-side code but had server dependencies, causing db module (and transitive dependencies like adm-zip) to be bundled into browser. Solution: Split into two files to properly separate concerns: 1. customFunctions.ts - Plugin only (no server imports, safe for browser) 2. customFunctionsPreferences.ts - Server-only preferences loading (uses aqlQuery) Changes: - Created customFunctionsPreferences.ts with loadUserPreferencesForFormulas() - Uses aqlQuery (Actual's custom SQL) instead of direct db imports - Moved helper functions (getLocaleDefaults, getCurrencyFromLocale) to new file - customFunctions.ts now only has setCachedUserPreferences() setter - action.ts loads prefs and caches them at module init - Client code can still import CustomFunctionsPlugin safely Co-authored-by: lelemm <lelemm@users.noreply.github.com> * [AI] Clean up feedback-related comments from code Removed explanatory comments referencing user feedback: - Removed feedback comments from customFunctions.ts - Removed feedback comments from formula-action.test.ts - Removed feedback comments from formula-action-integration.test.ts - Removed feedback comment from FormulaResult.tsx Code functionality remains unchanged, only comments were cleaned up. Co-authored-by: lelemm <lelemm@users.noreply.github.com> * Improve formula autocomplete popover scrolling * [autofix.ci] apply automated fixes * renamed MD for the new PR * wording * code cleanup * [autofix.ci] apply automated fixes * more cleanup * comment on a workaround to make scroll work inside codemirror not on focus/under a react aria popover * [autofix.ci] apply automated fixes * fixing FORMATCURRENCY and FORMATNUMBER to use proper send() method * resolving issue when bundling the application * removed useless re-export * code review feedbacks * retrigger actions * Adjusted minimum formula card to 1x1 and allows the title to be removed from the dashboard * 🔖 (26.6.0) (#7947) * 🔖 (26.6.0) * Generate release notes for v26.6.0 * Generate release notes for v26.6.0 * Update check-spelling metadata * Generate release notes for v26.6.0 * add release summary * links * Generate release notes for v26.6.0 * update links * Generate release notes for v26.6.0 * Generate release notes for v26.6.0 * Generate release notes for v26.6.0 * add EB link * Add initial docs for Enable Banking (#7961) * Add initial docs for Enable Banking * Delete upcoming-release-notes/7961.md * [Docs] UI Budget automations & month-end cleanup (#7863) * Add files via upload * Create documentation for budget automation feature Added documentation for the budget automation feature, detailing its functionalities, usage, and examples. * Add files via upload * Revise budget automation documentation for clarity Updated the documentation for budget automation, enhancing clarity and consistency in the descriptions of features and options. * [autofix.ci] apply automated fixes * Update Markdown headers for budget automation section * Add files via upload * Improve formatting in budget automation documentation Updated formatting and added line breaks for better readability in the budget automation documentation. * [autofix.ci] apply automated fixes * Fix HTML line breaks in budget-automation.md Updated HTML line breaks from '</br>' to '<br>' in budget automation documentation. * Fix HTML line breaks in budget automation documentation * Fix headings and formatting in budget-automation.md Updated formatting and corrected headings in budget automation documentation. * Add budget-automation to sidebar items * Add files via upload * Add files via upload * Revise budget automation documentation Updated budget automation documentation to clarify usage of notes templates and detailed instructions for month-end cleanup processes, including named pools and weight calculations. * [autofix.ci] apply automated fixes * Refactor budget automation documentation for clarity Updated various sections for clarity and consistency, including grammar corrections and improved phrasing. * [autofix.ci] apply automated fixes * Enhance budget automation documentation Updated budget automation documentation with new blog links and improved formatting. * Fix links in budget-automation.md Updated links in the budget automation documentation to remove 'docs' prefix for consistency. * Revise budget automation documentation with updates Updated images and text for budget automation documentation, including corrections and enhancements to clarity. * Improve clarity and add save reminders in budget automation Updated wording for clarity and added reminders for saving work. * Update budget automation documentation for clarity Clarify the process of handling leftover funds in the budget automation script. * [autofix.ci] apply automated fixes * Add 'overfund' and 'overfunded' to spelling expect list * Update spelling expectations by removing 'overfunded' Removed 'overfunded' from the spelling expectations. * Add files via upload * 'overfund' to 'overfunded' First the bot tells me overfunded isn't needed if overfund is there. Now, it tells me that overfunded IS needed. I give up. * Enhance budget automation documentation Added new sections on automation adjustments, moved some sections around, added horizontal separators. * Update images and descriptions in budget automation docs * Add files via upload * Add automation notes section to budget automation docs Added a section on automation notes to document how automations, balance caps, and long-term goals can have associated notes for clarity and tracking. * Add files via upload * Update budget automation examples in documentation * Add files via upload * Apply suggestions from code review Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk> * [autofix.ci] apply automated fixes * Add 'unmigrate' to spelling expectations * Improve clarity in budget automation documentation Clarified descriptions for budget automation features and improved wording for better understanding. * Correct spelling of 'unmigrate' to 'Unmigrate' They are the same and github is punking me again. * Clarify automation notes description * Add files via upload * Add files via upload * Add files via upload * Add files via upload * Add files via upload * Fix typos in budget-automation documentation Corrected typos and improved clarity in the budget automation documentation. --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk> --------- Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk> Co-authored-by: Mats Nilsson <matni403@gmail.com> Co-authored-by: Juulz <julesmcn@gmail.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * [AI] Improve Balance Forecast captions (#7957) * [AI] Add visible low point helper for forecasts * [AI] Show ending balance in forecast report * [AI] Show ending balance on forecast card * [AI] Update balance forecast report snapshots * [AI] Add release note for forecast captions * [AI] API: improve messaging for version/schema mismatch errors (#8026) * [AI] Improve messaging for API version/schema mismatch errors When an outdated client or @actual-app/api connects to a budget whose data uses a newer database schema, sync-apply fails with a cryptic "no such column" error that surfaced as a generic download/sync error. Add a version-mismatch message in getDownloadError/getSyncError, gated on the actual SQLite schema-error signature (no such column/table) so other invalid-schema failures keep their generic messaging and otherwise compatible API versions remain usable. * [AI] Set release note author to MatissJanis * Add release notes for PR #8026 * [AI] Address review: use Vitest globals and remove duplicate release note - Drop the explicit `vitest` import in errors.test.ts and rely on the configured test globals (per CodeRabbit review). - Remove the duplicate 8021.md release note, keeping the PR-numbered 8026.md with the clearer description. * [AI] Avoid i18n in loot-core for schema-mismatch message Return the schema-mismatch error as a plain string instead of going through i18next, and drop the i18next setup from the test. loot-core does not depend on i18n, and this message is surfaced verbatim through the programmatic API. * [AI] Remove unnecessary code comments * [AI] Add missing-meta test for getDownloadError --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * Fix typo in Budget Automation math expression (#8035) * Fix typo in Budget Automation math expression * Remove release note --------- Co-authored-by: youngcw <calebyoung94@gmail.com> * [AI] docs: update reset-password instructions (#7950) * [AI] docs: update reset-password instructions Clarify the current host-side command and keep the source-checkout workflow documented. Closes #7943 * [AI] docs: keep npm reset path alongside actual-server * Fix CSV import settings reset when skipping end lines (#7827) * [AI] fix(api): prevent navigator crash when loading @actual-app/api in Node.js (#8037) * [AI] fix(api): prevent navigator crash when loading @actual-app/api in Node.js Problem ------- @actual-app/api 26.5.0 and 26.6.0 throw "ReferenceError: navigator is not defined" on import in Node.js environments. The loot-core shared bundle includes three platform variants: platform.ts – browser (references navigator at module load time) platform.electron.ts – Electron/desktop (uses Node.js os module) platform.api.ts – Node.js API (safe constants only) The API vite build uses `resolve: { conditions: ['api'] }` to pick the right variant via the loot-core package imports map (#shared/platform). However, months.ts was importing via a bare relative path (`./platform`) instead of the imports-map alias (`#shared/platform`). Vite's conditions-based resolution does not apply to relative imports, so the build fell back to platform.ts (the browser variant), bundling navigator references that crash at load time. Fix --- Change months.ts to import `#shared/platform` instead of `./platform`. This goes through the loot-core package imports map, which the vite `api` condition resolves to platform.api.ts — the Node.js-safe variant. platform.api.ts was already added to the repo alongside this condition infrastructure (in the same 26.6.0 release) but the months.ts import was missed, leaving the crash in the published package. Testing ------- Added packages/api/platform.test.ts, which imports @actual-app/core/shared/ platform under the `api` vite condition and asserts that isBrowser, isPlaywright and isIOSAgent are all false. This test: - Fails if platform.api.ts is deleted (module not found) - Fails if platform.api.ts is modified to reference navigator (ReferenceError) - Fails if the api vite condition is removed, falling back to platform.ts which throws ReferenceError at import time in Node.js Verified by Kyle Slattery against a live Actual Budget instance via the actual-flow integration tool (https://github.com/lunchflow/actual-flow). * Add release note * Update upcoming-release-notes/8037.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Delete packages/api/platform.test.ts --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * [AI] Move i18n usage outside of loot-core (#8027) * [AI] Move i18n usage outside of loot-core loot-core should be platform-agnostic and free of i18n concerns. This moves all user-facing translated string helpers (rule/schedule labels, error formatters) into desktop-client's #util/{rule,schedule,error} modules, while keeping the underlying logic in loot-core. - Headless @actual-app/api error formatters in loot-core now return plain English strings (the API has no i18n runtime). - The persisted "Unknown" institution name and the platform storage alert use plain strings. - Removed the unused i18next dependency from loot-core. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [AI] Address review feedback on i18n-out-of-loot-core PR - Wrap switch default arms in blocks to satisfy noSwitchDeclarations (desktop-client and loot-core getDownloadError) - getSecretsError: return a localized generic message instead of leaking the raw backend error token - getRecurringDescription: always separate the weekend annotation so it no longer renders as "Monday(after weekend)"; add test coverage - IndexedDB quota error: surface a typed 'indexeddb-quota-error' event from loot-core and present the localized alert in desktop-client instead of a hard-coded English string - Persist a null bank name (instead of English "Unknown") when the provider reports no institution, and render a localized fallback in the bank sync UI --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [AI] Fix Balance Forecast chart colors with custom themes (#8031) * [AI] Use report theme colors for Balance Forecast chart lines * [AI] Add release notes for Balance Forecast custom theme fix * fix react compiler on Windows (#8049) * hmm.. * note * [Docs] Revise fly.io deployment instructions for nightly builds (#8059) * Revise deployment instructions for nightly builds Updated instructions for deploying unstable versions of Actual. * Clarify fly deploy commands in installation guide Updated deployment commands for clarity and added example for nightly version. * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * [AI] Scope Electron update to v41 (#8044) * Initial plan * Bump desktop Electron runtime to v42.3.0 * Add release notes entry for PR #8044 * Change author from Copilot to MikesGlitch Updated author information in release notes. * Update node-abi mappings to support Electron 42 ABI detection * Address validation feedback for Electron update PR * Add better-sqlite3 Electron 42 patch * Update lockfile for better-sqlite3 patch * Remove better-sqlite patch protocol and target Electron 41 * Align release note with Electron 41.7.1 change * Update better-sqlite3 ranges to ^12.8.0 --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Michael Clark <5285928+MikesGlitch@users.noreply.github.com> Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk> * [Enhancements] Removed regex from excel formula execution (#7990) * Removed regex from excel formula execution * [autofix.ci] apply automated fixes * Surface error for BUDGET_QUERY * code review --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * [AI] Fix balance forecast report Y-axis label clipping (#8030) * [AI] Fix balance forecast report Y-axis label clipping * [AI] Add release notes for #8030 * docs: add actual-bench to community projects (#8045) * docs: add actual-bench to community projects * [autofix.ci] apply automated fixes * docs: add release note for actual-bench community entry * [autofix.ci] apply automated fixes * fix: address coderabbit comments * fix: remove upcoming release notes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * [AI] Add Sort A to Z / Sort Z to A options to budget category groups (#7831) * [AI] Add Sort A to Z / Sort Z to A options to budget category groups (#7831) * Simplify release notes and apply coderabbitai suggestions * [AI] fix(api): trigger SimpleFin batch sync with a single account (#8052) The condition required more than one SimpleFin account before running batch sync, causing a single SimpleFin account to be skipped entirely. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * add pikapods desktop warning (#8061) * add pikapods desktop warning * better link * add to spelling list * more spelling * [AI] Fix bug when linking accounts (#8006) Fixes a bug that when selecting Link Account from the account page one has to reselect the same account in the modal even though its prepopulated. Co-authored-by: Gemini <noreply@google.com> * add MXN peso (#8060) * ♻️ remove unnecessary playwright container pulls to speed up CI (#8067) * don't use playwright where it's not needed * note * [AI] Add auto-generated Upcoming Release docs page (#8065) * [AI] Add auto-generated Upcoming Release docs page Adds a docs page that always reflects the current contents of the repo-root upcoming-release-notes/ directory — the changes that have been merged but not yet published in a stable release (i.e. what ships in the nightly/edge builds). - Extract parseReleaseNotes/formatNotes from the release-notes generator into the shared ci-actions util so both the release generator and the docs build reuse the same formatting. - Add a docs build script that regenerates docs/upcoming-release-notes.md on every start/build, with an intro explaining the notes are unreleased and how to try them, plus the categorized notes list. - Wire the generator into the docs start/build scripts, add a sidebar entry next to Release Notes, and gitignore the generated page. https://claude.ai/code/session_01MuY9Phome8uJKH51HbrsQw * [AI] Simplify upcoming-release-notes generator - Declare @actual-app/ci-actions as a workspace dependency and import the shared util by package name instead of a relative cross-package path, so the dependency is registered in the workspace graph. - Resolve the repo root once via new URL() and drop the dirname import. - Use a ternary for the page body instead of let + if/else. https://claude.ai/code/session_01MuY9Phome8uJKH51HbrsQw * Add release notes for PR #8065 * [AI] Remove edge references, keep only nightly in docs Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com> * Update packages/docs/scripts/generate-upcoming-release-notes.mjs Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com> Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk> * Bump the npm_and_yarn group across 2 directories with 1 update (#8071) Bumps the npm_and_yarn group with 1 update in the / directory: [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router). Bumps the npm_and_yarn group with 1 update in the /packages/desktop-client directory: [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router). Updates `react-router` from 7.13.1 to 7.14.2 - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router@7.14.2/packages/react-router) Updates `react-router` from 7.13.1 to 7.14.2 - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router@7.14.2/packages/react-router) --- updated-dependencies: - dependency-name: react-router dependency-version: 7.14.2 dependency-type: direct:development dependency-group: npm_and_yarn - dependency-name: react-router dependency-version: 7.14.2 dependency-type: direct:development dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * When resetting SimpleFIN credentials, parse error and error_code if response body exists (#8068) * Initial commit * Add release note * Update release note file name to match PR number * Flip operands when checking for 2xx * Release notes grammar * Better release note for non-devs * Simplify to naive solution of checking on nullable response * Hiding tags and tag table bulk actions (#7919) * [AI] Make TypeScript work in test files across packages Match the CRDT package's tsconfig pattern in loot-core, desktop-client, api, and desktop-electron so test files participate in the project graph (IDE intellisense, project-wide typecheck) while production builds still emit clean declaration files. - Remove test-file exclusions from each package's main tsconfig - Add tsconfig.build.json for loot-core and api with test exclusions, used by the build scripts - Add e2e/tsconfig.json for desktop-client and desktop-electron with Playwright types - Fix latent type errors in test files now caught by typecheck - Disable typescript/unbound-method for test files (mock matcher pattern) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [AI] Address review feedback on test type fixes - goal-template.test.ts: extract amounts to typed locals so single-value assertions no longer compare against unknown - category-template-context.test.ts: replace `as unknown as DbCategory` double-cast with a fully-typed object using `satisfies DbCategory` (the previous mock had `is_income: true` which doesn't match the `1 | 0` shape the cast was hiding) - api/tsconfig.build.json: broaden test exclude pattern to `**/*.test.ts` Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [AI] Drop tsconfig.build.json for loot-core and api The build-config indirection was incomplete protection: typecheck (`tsgo -b` against the main tsconfig, which now includes test files) already emits `*.test.d.ts` into `@types/`, and the build step does not clean before re-emitting. The same is observable in crdt's `dist/`, which currently contains test declarations on disk. What actually keeps test declarations out of the npm tarball is the `files` field in package.json — and loot-core already uses that mechanism for source files (`\!src/**/*.test.ts`). Extending the same pattern to `@types/` is more direct than maintaining a duplicate tsconfig that doesn't reliably do its job. - Delete loot-core/tsconfig.build.json; revert build to `tsgo -b`; add `\!@types/**/*.test.d.ts*`, `\!@types/**/__tests__/**`, `\!@types/**/__mocks__/**` to `files`. - Delete api/tsconfig.build.json; revert build to `vite build && tsgo --emitDeclarationOnly`; add `\!@types/**/*.test.d.ts*` to `files`. Verified: `yarn pack --dry-run` excludes all test declarations from both packages while production declarations still pack (428 .d.ts files for loot-core, methods.d.ts for api). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * first pass at implementation * finished implementing what I feel would be a good user experience * padding update * added ability to use tab to select * release notes * updated release notes * auto highlights first entry * removed debug info * linting formatting * updated behavior with tags in the middle of stuff * minor improvements to ux * extracted TagAutocomplete functionality into its own file * rewrote TagAutocomplete using react-aria-components from scratch * linting * restored old autocomplete since I'm no longer using it * linting * capped results at 10 * bugfix * used tag css hook instead of notes tag formatter * added mobile support * fixed bug where inserting a tag at the end of the note wouldn't fire onChange handler * fixed bug where note would hover even though field was not focused * fixed bug with currentWord detection * some more minor UI bugs with the mobile UI * removed log statement * added aria-label * [autofix.ci] apply automated fixes * name to Input * updatedt ests * fixed tests * whitespace adjustment * removed debug from test * added tests and made a few fixes * tried to fix vrt * some new hooks to use * removed log statement * removed input from useCursorPosition * input ref value hook now triggers on mount * add extra padding * hide browser autocomplete when our popup is shown to avoid doubles * useFilteredTags hook and adjustments to mobile tag UI * switched from opacity to height transitioning * sorted filtered tags by startsWith first * updated highlighting logic for mouseover * [autofix.ci] apply automated fixes * fix tag ordering * button type * [autofix.ci] apply automated fixes * release note update * release note to trigger ci * reverted db change * Revert "reverted db change" This reverts commit 24ce5450e5e4261e77fe5056620c240da22869a3. * can now filter by tags without hashes * extracted a method and added a new hasAnyTags rule * added hasAnyTag, still need to figure out why tests are failing * fixed unit tests * release notes * fixed based on coderabbit * tag.toLowerCase() * variable scope fix * fixed some focus-related bugs * allow creating tags in the dropdown for desktop * fixed focus issues with useCursorPosition * added Create Tag functionality to mobile * tests and added some length clipping as qol * can now use enter to submit new tag properly * centralized overflow CSS for tags * centered the text horizontally * added textnowrap * release notes * linting * fixed issue with async keydown handler * specified only to use new tag css for the autocomplete stuff * forgot to add new option to tag table * removed Trans * started working on multiselect * multiselect is now functional * finished multiAutocomplete implementation * [autofix.ci] apply automated fixes * removed double input * added multi autocomplete to rule editor * adjusted padding for TagMultiAutocomplete * adjusted hover cursor * release notes * fixed rule conditions * added tag styling to filter multiselect * spacing adjustment * release notes for retriggering build * added hidden migration * added hidden to models * added tags-hide-all and tags-unhide-all actions * added hide and unhide tags actions * added SelectedTagsButton * added UI elements for hiding/showing tags * release notes * removed delete from ManageTags * added icons to menu * coderabbit * coderabbit * updated mutations for translations * coderabbit * removed not null requirement * attempted to fix tests? * removed duplicate filterTags * [autofix.ci] apply automated fixes * updated release notes * release note update * updated migration timestamp * moved tags menu button --------- Co-authored-by: github-actions[bot] <matiss@mja.lv> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Alec Bakholdin <alecbakholdin.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * automation UI: improve scaling (#8080) * improve scaling on high zoom levels * note * automation UI: rework balance cap and fix error when clearing fields (#8082) * fix safeNumber error * rework balance cap UI to be more readable * note * add explanation to balance cap * Bump react-router in the npm_and_yarn group across 1 directory (#8072) Bumps the npm_and_yarn group with 1 update in the / directory: [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router). Updates `react-router` from 7.14.2 to 7.15.0 - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router@7.15.0/packages/react-router) --- updated-dependencies: - dependency-name: react-router dependency-version: 7.15.0 dependency-type: direct:development dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * [AI] feat(currencies): add currency-precision-aware helpers (#8064) * [AI] feat(currencies): add getDecimalPlaces/getCurrencyPrecisionMultiplier helpers, encodeAmount, currency-aware integerToCurrencyWithDecimal * Fix typo in release note * Use it.each in unit tests * Rename encodeAmount and replace its body; remove getCurrencyPrecisionMultiplier * [AI] Remove AI-generated release notes workflow and support custom filenames (#7963) * [AI] Relax release-notes filename convention Allow descriptive filenames (e.g. add-payee-autocomplete.md) for upcoming release notes instead of requiring {PR_NUMBER}.md. The PR number was only used to build the [#1234](.../pull/1234) link in the generated changelog. The generation script now resolves the PR per file by finding the commit that added it (git log --diff-filter=A --follow) and querying GitHub's commits/{sha}/pulls endpoint. Numeric filenames remain valid via a fast path. Also retires the CodeRabbit/OpenAI-triggered auto-creator workflow, which existed to guess summaries and stamp the PR-numbered filename; neither is needed once contributors can pick a slug up front. * Add release notes for PR #7963 * [AI] Use execFile to look up release-note add-commits resolvePrNumber() interpolated a path from fs.readdir into a shell- evaluated `git log` command. Switch to execFile with an argv array so filenames containing shell metacharacters can't break out of the intended command. * [AI] Drop duplicate release note added by the to-be-removed auto-creator The CodeRabbit-triggered workflow ran one last time from master before this PR removes it, and committed upcoming-release-notes/7963.md duplicating the existing relax-release-notes-filenames.md entry. Keep the slug-named note since it exercises the new flexible-filename code introduced in this PR. * [AI] Don't fail release-notes generation on transient API errors fetchPrForCommit() handled non-OK HTTP responses but let fetch() and res.json() exceptions propagate, so a single network blip or malformed response would abort the whole release-notes generation. Wrap them in a try/catch that logs and returns null, matching the pattern already used in resolvePrNumber for execFile errors. * [AI] Address CodeRabbit full-review nits - bin/release-note-generator.ts: tighten slug regex to reject trailing and consecutive dashes (matching slugify output), and make slugify fall back to "untitled" so an all-non-alphanumeric input can't produce a hidden ".md" filename. - packages/ci-actions/bin/release-notes-check.mjs: switch to execFile for git fetch/diff, matching release-notes-generate.mjs and removing shell interpolation of BASE_REF. - packages/ci-actions/bin/release-notes-generate.mjs: explicitly check GITHUB_REPOSITORY before splitting, consistent with the other env var guards a few lines below. - upcoming-release-notes/relax-release-notes-filenames.md: fix author casing (matiss -> MatissJanis) so changelog attribution is correct. * [AI] Reject empty release-note bodies content.trim().split('\n').length === 1 is true for the empty string (''.split('\n') returns [''] of length 1), so blank notes slipped past the single-line check. Reject empty trimmed content explicitly. * [AI] Resolve PR numbers from commit subjects instead of GitHub's API parseReleaseNotes was doing one GitHub API round-trip per non-numeric release-note file. That scales badly for release generation, and the docs site's generate-upcoming-release-notes.mjs runs on every docs build — so contributors were hitting the GitHub API (or failing 401 without a token) every time they previewed docs locally. actualbudget squash-merges every PR through the GitHub UI, which appends "(#NNNN)" to the resulting commit subject. So: git log -1 --format=%s -- <path> plus a /\(#(\d+)\)\s*$/ match recovers the PR number without touching the network, and works offline / without a GITHUB_TOKEN. Side effect: switched from "first commit that added this file" to "most recent commit touching this file", which is what we actually want — when a file is renamed (e.g. 7907.md -> 7954.md when a wrong PR number is corrected), the changelog should point at the rename PR, not the original add. The SHA->PR cache and fetchPrForCommit go away; no longer needed. If a file's subject doesn't match (direct push to master, manually rewritten subject), the entry still emits without a PR-link prefix — same graceful degradation as before. * [AI] Pin PR-number lookup to each note's add commit resolvePrNumber was using git log -1 with no diff filter, returning the latest commit that touched the file. That's fine until someone edits an existing release note in a follow-up PR (typo fix, author correction) — at which point the changelog entry would suddenly point at the wrong PR. Add --diff-filter=A so the lookup pins to the commit that originally added the path. Empirically verified against this repo: renames keep working (git records a rename as A at the new path when --follow isn't used), and the edit case now correctly returns the original add commit instead of the most recent touch. --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * [AI] Add GitHub workflow to block PRs with "do not merge" label (#8090) * [AI] Add CI job that fails when 'do not merge' label is set * [AI] Rename do not merge check job to "Block PRs with do not merge label" * [AI] Rename do not merge workflow to "Block PRs with do not merge label" * [AI] Add release notes for do not merge CI check * Update 8090.md --------- Co-authored-by: Claude <noreply@anthropic.com> * [AI] Persist bank sync status in the accounts table (#8017) * [AI] Persist bank sync status in the accounts table Extracts the persisted bank-sync-status half of #7782 (by @jcam): adds a bank_sync_status column to the accounts table, writes the status (pending/ok/failed/reauth-required/attention-required) during native bank syncs, and reads the persisted value in the UI (sidebar, account header, mobile account pages, and the sync banner) instead of ephemeral Redux state. https://claude.ai/code/session_014MXn6Qi8skvFL9kXw7ctFD * [AI] Add release note for persisted bank sync status https://claude.ai/code/session_014MXn6Qi8skvFL9kXw7ctFD * [AI] Persist only durable failure states, keep pending ephemeral Addresses review feedback: a transient 'pending' write could get stuck in the DB (and propagated via CRDT) if the app closed mid-sync. The DB now only stores durable result states (ok / failed / reauth-required / attention- required); pending stays in ephemeral Redux state (account.accountsSyncing) as before. The UI reads only the persisted failure state via isAccountFailedSync, and the BankSyncStatus banner reverts to the ephemeral in-flight count. Also extracts a persistBankSyncError helper and inlines a redundant wrapper. https://claude.ai/code/session_014MXn6Qi8skvFL9kXw7ctFD * [AI] Un-export AccountSyncSidebar, refine release note - AccountSyncSidebar is internal again; drop the export-only-for-test and its test (the failure logic is already covered by syncStatus.test.ts). - Credit MatissJanis in the release note and make it user-centric. https://claude.ai/code/session_014MXn6Qi8skvFL9kXw7ctFD * [AI] Publish account changes in bank-sync sync-events handleSyncResponse writes accounts.last_sync and accounts.bank_sync_status, but the sync-events following it only declared tables: ['transactions'], so clients only refetch useAccounts when the event includes 'accounts'. Add 'accounts' to the six emissions that follow handleSyncResponse (the four link flows and the two batch-sync finals) so the persisted account changes are published everywhere, not just on the triggering client. https://claude.ai/code/session_014MXn6Qi8skvFL9kXw7ctFD * [AI] Re-timestamp bank_sync_status migration after master A newer migration (add_tags_hidden) landed on master, so bump the bank_sync_status migration's timestamp to remain the latest and satisfy the migration-ordering check. https://claude.ai/code/session_014MXn6Qi8skvFL9kXw7ctFD --------- Co-authored-by: Claude <noreply@anthropic.com> * Remove environment from release notes workflow (#8091) * Remove environment from release notes workflow Removed the environment specification from the release notes workflow. * Update release-notes CI job configuration Removed the unnecessary 'environment' from the release-notes CI job. * [AI] Enable "do not merge" check on merge queue events (#8092) * [AI] Run do-not-merge check on merge_group events The block-do-not-merge workflow only triggered on pull_request events, so it never ran in the merge queue and could not be made a required check. Adding the merge_group trigger lets the job run and pass in the queue context (where pull_request labels are absent), allowing it to be required. * [AI] Add release notes for merge queue do-not-merge fix --------- Co-authored-by: Claude <noreply@anthropic.com> * Make payee & categories searching to be case-insentive (#8079) * make payee & categories searching to be case-insentive * add release notes * [autofix.ci] apply automated fixes * Update VRT screenshots Auto-generated by VRT workflow PR: #8079 * update release-note * [autofix.ci] apply automated fixes * fix release-note format --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * Add files via upload (#8096) * [AI] Fix incorrect schedule value when saving full amount (#8085) If one has a yearly schedule and the budget template uses "Cover each occurence when it occurs", it still showed a value for the yearly schedule even though it's not the month where the schedule occurs. Co-authored-by: Gemini <noreply@google.com> * feat: add SharedArrayBuffer warning indicator in title bar (#7922) * feat: add SharedArrayBuffer warning indicator in title bar * improve styling * add warning label * fix typo * review comments fixed * use link instead of button * fix tooltip styles * add hover effect --------- Co-authored-by: Awais Saeed <dev@awaissaeed.com> * [AI] Remove experimental status from Actual CLI (#8102) * [AI] Move actual-cli out of experimental status * [AI] Rename CLI release note to a feature slug --------- Co-authored-by: Claude <noreply@anthropic.com> * add warning when schedule/save by automation priorities don't match (#8088) * add warning when schedle/save by automation priorities don't match * note * italicise * feat(reports): scoped ErrorBoundaries for individual report routes (#7658) * feat(reports): scoped ErrorBoundaries for individual report routes (#7391) Wrap each route in ReportRouter for NetWorth, CashFlow, Spending, etc. in ErrorBoundary with FeatureErrorFallback. Matches the pattern used for /rules routes in FinancesApp.tsx so a render error in a single report no longer takes down the whole app. * chore: rename release note to PR number * refactor(reports): extract withReportBoundary helper to remove route boilerplate Each report route was wrapping its element in the same ErrorBoundary block with FeatureErrorFallback and resetKeys={[location.pathname]}. Pull that into a small withReportBoundary(element) helper inside ReportRouter so each route is one line again. Behavior is unchanged. The ErrorBoundary, FallbackComponent, and resetKeys are identical to before; the helper just closes over location.pathname. Addresses review feedback from @joel-jeremy and the CodeRabbit nitpick on the same file (a custom Route component is not viable since react-router v6's <Routes> requires literal <Route> children, but a helper function gives the same boilerplate reduction). * refactor(reports): hoist withReportBoundary to ReportBoundary component Move the per-route error-boundary helper out of ReportRouter as a module-scoped component. ReportBoundary calls useLocation() internally so each call site stays clean. Addresses review feedback. --------- Co-authored-by: github-actions[bot] <matiss@mja.lv> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [AI] Add configurable average ranges to Monthly Spending report (#7920) * Add configurable average ranges to Monthly Spending report * Delete upcoming-release-notes/temp.md * Update packages/desktop-client/src/components/reports/spendingAverageRange.test.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Fix spending average range test import * Update VRT screenshots Auto-generated by VRT workflow PR: #7920 --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: youngcw <calebyoung94@gmail.com> * [AI] Fix custom report transfer drilldown filters (#8001) Co-authored-by: youngcw <calebyoung94@gmail.com> * Make tag search case-insensitive (#8093) * makes tags case insensitive * release notes --------- Co-authored-by: Alec Bakholdin <alecbakholdin.com> * docs: add Hostim to Additional Installation Options (#8066) * docs: add Hostim to community install options Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: add release note for #8066 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: drop release note (not required for docs-only change) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: youngcw <calebyoung94@gmail.com> * [AI] Add SSRF protection to SimpleFIN bank sync integration (#8012) * [AI] Add SSRF protection to SimpleFIN bank sync integration The SimpleFIN integration made server-side HTTP requests to user-controlled URLs (the base64-encoded claim token and the resulting access-key base URL) without any validation, unlike the CORS proxy which blocks private IPs via ipaddr.js. Add a shared assertUrlAllowed() helper that rejects non-http(s) URLs and blocks requests to private, loopback, link-local, unique-local, reserved, broadcast and unspecified addresses. Hostnames are resolved via DNS so names pointing at internal addresses are blocked too, not just literal IPs. Apply it before both outbound requests in getAccessKey() and getAccounts(). * [AI] Unify CORS proxy private-IP check with shared SSRF helper The CORS proxy had its own inline ipaddr.js private-IP check that duplicated the logic in the SimpleFIN SSRF helper. Export isBlockedIp() from util/ssrf and reuse it in app-cors-proxy, removing the duplicate. This also widens the CORS proxy's coverage to the reserved and broadcast ranges and normalizes IPv4-mapped IPv6 addresses, matching the SimpleFIN helper. The cors-proxy tests now exercise the real ipaddr.js against real private IPs instead of mocking it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [AI] Rename SimpleFIN SSRF release note to match PR number Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Implement SSRF protection for SimpleFIN bank sync Requests to private, loopback, link-local, and other internal addresses are now blocked. * [AI] Allow self-hosted private SimpleFIN servers by default The SSRF protection blocked all private/loopback addresses, which broke self-hosters running their own SimpleFIN bridge on a LAN or VPN IP with no way to opt back in. Split the blocked ranges into two tiers: link-local (cloud metadata), reserved, broadcast and unspecified stay blocked unconditionally, while private/loopback/unique-local are blocked by default but can be permitted per-caller via { allowPrivateNetwork: true }. SimpleFIN opts in, so self-hosting works out of the box with no env var, while the worst-case cloud-metadata credential-theft vector remains closed. The CORS proxy keeps the strict default. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [AI] Re-validate SSRF rules on each SimpleFIN redirect hop getAccounts used fetch with redirect: 'follow', so a 3xx response from the validated SimpleFIN URL could redirect to a blocked internal address after only the initial URL had been checked. Follow redirects manually instead, calling assertUrlAllowed on every hop before fetching, capping at 5 redirects, and dropping Authorization on cross-origin hops so the bridge credentials cannot leak to a redirect target. getAccessKey uses https.request, which does not auto-follow redirects, so it is unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> * [AI] Skip unimportable Enable Banking transactions instead of failing the whole sync (#8086) * [AI] Skip unimportable Enable Banking transactions instead of failing the whole sync Enable Banking bank-sync aborts the whole account import with a client-side SQLITE_ERROR when a fetched transaction cannot be inserted — most commonly a pending transaction with no booking/value/transaction date (normalized to date ''), or a non-numeric amount. Add an isImportableTransaction helper and skip such records in the /transactions handler (logging the count) so the rest of the account imports. Dated pending transactions are unaffected. * [AI] Add release note for #8086 * [AI] Reject empty Enable Banking transaction amounts Number('') is 0 (finite), so an empty/whitespace amount slipped through isImportableTransaction as a zero transaction. Trim and reject empty amounts explicitly, with a test. Addresses PR review feedback. * Update packages/sync-server/src/app-enablebanking/services/enablebanking-service.ts Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv> * Update packages/sync-server/src/app-enablebanking/app-enablebanking.ts Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv> * [AI] Remove now-unused skippedCount counter The summary log that read it was removed when accepting the review suggestion; drop the dangling write-only counter so the loop matches the per-transaction skip log that remains. --------- Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv> * [AI] Disable ESLint in CodeRabbit configuration (#8110) * [AI] Disable ESLint in CodeRabbit config * [AI] Add release notes for CodeRabbit ESLint change * [AI] Rename release notes to match PR number 8110 * [AI] Rename release notes file to a slug --------- Co-authored-by: Claude <noreply@anthropic.com> * Feature: Add Akahu New Zealand bank sync (#6041) * add akahu integration for nz banks * akahu fix bank name being set to account name * rename apiToken and fix reset pointing to the wrong function * fix apitoken wording in akahu init modal * add upcoming-release-notes * fix lint issues * fix lint issues * add loan account type to starting balance inversion * fix initial sync balance * initially select 365 days of transactions on first sync for akahu sync * add SyncServerAkahuAccount to onSetLinkedAccount * set transaction currency to account currency * remove unnecessary code * handle TFR TO/FROM payees and account for loan account type in transactions * [autofix.ci] apply automated fixes * rename Error to ErrorAlert and fix intial sync start date * extract note from description for TFR TO/FROM transactions * fix normalizeNotes not referencing the transaction * refactor app-akahu code * [autofix.ci] apply automated fixes * Add Akahu to ExternalAccount type * Update yarn.lock * update yarn.lock * Remove unused error var in catch block * [autofix.ci] apply automated fixes * require authentication for akahu endpoint * fix lint issue in mutations.ts * fix up import paths * fix lint issues * reorder form fields * remove unnecessary handling for debt accounts * lint fixes * Put Akahu bank sync under feature flag * remove incorrect feedback link * [autofix.ci] apply automated fixes * Add feedback link for feature toggle * use uuidv4 * prevent fetch if feature not enabled * change app-akahu to ts and tidy up * fix typecheck errors * [autofix.ci] apply automated fixes * fix browser client build issues * [autofix.ci] apply automated fixes * update akahu npm package to latest version * use amountToInteger for balance reducer * add additional details to transactions * change initial sync start date logic * add akahu fields to mappable fields in desktop-client * [autofix.ci] apply automated fixes * getDate use formatISO to get the timezone adjusted date * remove duplicate payeeName --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * automation UI: add mobile support 📱 (#8099) * generalise out to hooks * add mobile modal * wire up mobile * tidy up field styling on mobile * [AI] add mobile automations e2e test Covers opening the editor from the mobile category menu, the drill-down list/editor navigation, touch-sized fields, and that Cancel dismisses it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * note * Update VRT screenshots Auto-generated by VRT workflow PR: #8099 * fix date picker on schedule page * fix conflict * fix cramped space in save by automation --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * [Docs] Starting at the top, Introduction & Vision (#8083) * Enhance introduction and navigation details in docs Revised wording for clarity and improved navigation instructions in the documentation. * Update vision.md for clarity and formatting Refine language for clarity and consistency, and update formatting for better readability. * Improve formatting of third-party use section Reformatted the third-party use section for clarity. * Revise documentation introduction and navigation details Updated the introduction and navigation instructions for clarity and conciseness. * Revise documentation overview and navigation details Updated documentation for clarity and navigation instructions. * Refine Local-first explanation in vision.md Removed redundant text in the Local-first section. * [autofix.ci] apply automated fixes * Revise contribution guidelines in documentation Updated links to contributing guides and documentation readme. * Revise headings and improve hyphenation in vision.md Updated headings and corrected hyphenation in vision.md. * [autofix.ci] apply automated fixes * Fix wording in contribution guidelines section Corrected the wording from 'documentation standard' to 'documentation standards' for clarity. --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv> * [AI] Fix bank sync deadlock from nested mutator (#8111) * [AI] Fix bank sync deadlock from nested mutator PR #8017 wrapped the accounts-bank-sync and simplefin-batch-sync handlers in mutator(). runHandler runs mutators via runMutator, which is sequential() — only one mutator may run at a time and re-entrant calls are queued. These handlers call syncAccount -> reconcileTransactions, which already invokes runMutator internally. The inner runMutator gets queued behind the still-running outer one while the outer awaits it, deadlocking forever. The bank sync send() never resolves, so the mobile spinner spins indefinitely. Remove the mutator() wrapping (restoring pre-#8017 registration). The DB writes for bank_sync_status/last_sync already ran outside a mutator before #8017, and transaction reconciliation manages its own mutator context. * [AI] Add regression tests for bank sync mutator deadlock Guards against re-wrapping the accounts-bank-sync / simplefin-batch-sync handlers in mutator(), which deadlocks because the sync internally calls runMutator (reconcileTransactions) and runMutator is sequential. - An invariant test asserts neither handler is a mutating method. - A behavioral test drives accounts-bank-sync through runHandler with a syncAccount mock that performs its own runMutator, and fails via a timeout if the nested mutators deadlock. * [AI] Simplify deadlock regression test Collapse the two-step Promise.race into a single race result and clear the timeout timer on the success path so it doesn't linger after the test. * [AI] Remove explanatory comments from deadlock regression test * [AI] Add release note for bank sync deadlock fix * Update packages/loot-core/src/server/accounts/app-bank-sync.test.ts Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk> * Update packages/loot-core/src/server/accounts/app-bank-sync.test.ts Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk> * [AI] Fix desktop settings resetting after an interrupted update (#8101) * [AI] Fix desktop settings resetting after an interrupted update Desktop global prefs (language, theme, etc.) live in a single global-store.json that was written non-atomically and silently reset to {} whenever it failed to parse. An app update's forced quit can truncate the file mid-write, so the next launch wiped every global pref - the language reverted to "System default" (the OS language). - Write the store via a unique temp file + atomic rename so it is never left half-written. - Serialize writes so a slower older write can't land after and clobber a newer one. - Back up an unparseable store to .corrupt and log loudly instead of silently discarding the user's preferences. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [AI] Reject non-object global-store contents during load JSON.parse could return a non-object (null, an array, a primitive) for parseable-but-wrong-shaped contents, which would then break store[key] access. Validate the parsed value is a plain object and route anything else through the existing back-up-and-default recovery path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * :electron: Removed new shared array buffer warning on Electron (#8118) * removed shared array buffer warning one electron * release notes * cleanup a bit * Enhance translation contribution guidelines in i18n.md (#8113) * Enhance tra… * ⬆️ mid month dependency updates (#8235) * @monorepo-utils/workspaces-to-typescript-project-references (^2.10.3 → ^2.11.0) * @types/node (^22.19.17 → ^22.19.21) * eslint (^10.2.0 → ^10.5.0) * eslint-plugin-perfectionist (^5.8.0 → ^5.9.0) * lage (^2.15.5 → ^2.15.13) * typescript (^6.0.2 → ^6.0.3) * vitest (^4.1.2 → ^4.1.8) * better-sqlite3 (^12.8.0 → ^12.10.0) * vite (^8.0.5 → ^8.0.16) * cosmiconfig (^9.0.1 → ^9.0.2) * react-aria-components (^1.16.0 → ^1.18.0) * @chromatic-com/storybook (^5.1.1 → ^5.2.1) * @storybook/addon-a11y (^10.3.4 → ^10.4.4) * @storybook/addon-docs (^10.3.4 → ^10.4.4) * @storybook/react-vite (^10.3.4 → ^10.4.4) * @types/react (^19.2.14 → ^19.2.17) * @vitejs/plugin-react (^6.0.1 → ^6.0.2) * eslint-plugin-storybook (^10.3.4 → ^10.4.4) * storybook (^10.3.4 → ^10.4.4) * @bufbuild/protobuf (^2.11.0 → ^2.12.0) * @bufbuild/protoc-gen-es (^2.11.0 → ^2.12.0) * @babel/core (^7.29.0 → ^7.29.7) * @codemirror/autocomplete (^6.20.1 → ^6.20.3) * @react-aria/interactions (^3.27.1 → ^3.28.1) * @reduxjs/toolkit (^2.11.2 → ^2.12.0) * @tanstack/react-query (^5.96.2 → ^5.101.0) * @uiw/react-codemirror (^4.25.9 → ^4.25.10) * date-fns (^4.1.0 → ^4.4.0) * hyperformula (^3.2.0 → ^3.3.0) * lru-cache (^11.2.7 → ^11.5.1) * react-aria (^3.47.0 → ^3.49.0) * react-error-boundary (^6.1.1 → ^6.1.2) * react-hotkeys-hook (^5.2.4 → ^5.3.2) * react-redux (^9.2.0 → ^9.3.0) * react-spring (^10.0.3 → ^10.0.4) * rolldown (^1.0.0-rc.13 → ^1.1.1) * sass (^1.99.0 → ^1.101.0) * vite-plugin-pwa (^1.2.0 → ^1.3.0) * @docusaurus/core (^3.10.0 → ^3.10.1) * @docusaurus/plugin-content-docs (^3.10.0 → ^3.10.1) * @docusaurus/plugin-ideal-image (^3.10.0 → ^3.10.1) * @docusaurus/preset-classic (^3.10.0 → ^3.10.1) * @docusaurus/theme-common (^3.10.0 → ^3.10.1) * @docusaurus/theme-mermaid (^3.10.0 → ^3.10.1) * @easyops-cn/docusaurus-search-local (^0.55.1 → ^0.55.2) * @docusaurus/module-type-aliases (^3.10.0 → ^3.10.1) * @oxlint/plugins (^1.60.0 → ^1.69.0) * ua-parser-js (^2.0.9 → ^2.0.10) * fast-check (^4.6.0 → ^4.8.0) * jest-diff (^30.3.0 → ^30.4.1) * workbox-precaching (^7.4.0 → ^7.4.1) * ipaddr.js (^2.3.0 → ^2.4.0) * jws (^3.2.2 → ^3.2.3) * http-proxy-middleware (^3.0.5 → ^3.0.6) * oxlint (^1.59.0 → ^1.69.0) * @codemirror/view (^6.41.0 → ^6.43.1) * yarn dedupe * nano-staged (^0.8.0 → ^1.0.2) * commander (^14.0.3 → ^15.0.0) * react (>=19.2 → 19.2.7) * react-dom (>=19.2 → 19.2.7) * @rolldown/plugin-babel (~0.1.8 → ~0.2.3) * downshift (9.3.2 → 9.3.6) * i18next (^25.10.10 → ^26.3.1) * react-i18next (^16.6.6 → ^17.0.8) * react-router (7.15.0 → 7.17.0) * vite-plugin-node-polyfills (^0.27.0 → ^0.28.0) * jws (^3.2.3 → ^4.0.1) * oxlint-tsgolint (^0.20.0 → ^0.23.0) * jsdom (^27.4.0 → ^29.1.1) * note * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * crdt regenerate and version bump * fix test * yarn dedupe --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * [AI] Migrate CI workflows to Depot-managed runners (#8205) * [AI] Migrate CI workflows to Depot-managed runners Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Rename release notes to match PR number Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Empty commit to trigger CI Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Keep VRT jobs on GitHub-hosted runners Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Use 4-vCPU Depot Windows runners for Electron builds Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Use 4-vCPU Depot Ubuntu runners for Electron builds Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Use 4-vCPU Depot runners for functional e2e tests Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Scale Electron build runners to 8 vCPUs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Scale Ubuntu Electron builds to 4 vCPUs and move Windows builds back to GitHub runners Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Move Microsoft Store publish back to GitHub Windows runner Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Add actionlint config for Depot runner labels Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com> * Start desktop split transactions with two splits instead of one (#8234) * Start desktop split transactions with two splits instead of one * Update VRT screenshots Auto-generated by VRT workflow PR: #8234 --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * show the bank sync error banner on the accounts based on persisted status (#8170) * persist more bank sync error status' * wire up the UI * note * Upgrade pluggy-sdk and migrate away from deprecated page-size transactions to cursor-based transactions (#8103) * Bump pluggy-sdk to include new fetchTransactionsCursor GET /v2/transactions * Use fetchAllTransactions, which uses cursor-based /v2/transactions * Remove weak helper function * Add release note * Fix unused result mapping * Add lelemm's changes Co-authored by: lelemm * Fix lint * Add lelemm as co-author on release note * Update message * Fix comma typo * Update yarn.lock --------- Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk> * Bump joi in the npm_and_yarn group across 1 directory (#8239) Bumps the npm_and_yarn group with 1 update in the / directory: [joi](https://github.com/hapijs/joi). Updates `joi` from 17.13.3 to 17.13.4 - [Commits](https://github.com/hapijs/joi/compare/v17.13.3...v17.13.4) --- updated-dependencies: - dependency-name: joi dependency-version: 17.13.4 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Use ± symbol for 'between' schedule amounts and improve tooltip text (#8222) * Add between symbol and update tooltip text * Add release note * [autofix.ci] apply automated fixes * Fix i18n strings and args * Do not translate amount only --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: youngcw <calebyoung94@gmail.com> * [AI] Replace archived check-spelling action with typos for docs spell-check (#8249) * [AI] Replace archived check-spelling action with typos for docs spell-check The check-spelling/check-spelling action was archived upstream and shipped a self-disabling "secpoll" kill-switch after a maintainer-account compromise (2026-06-16), causing the Check Spelling (Docs) workflow to fail fatally with a security-advisory error. There is no patched release to bump to. Replace it with crate-ci/typos, which is actively maintained and runs entirely from the checked-out tree (no PR-comment or bot-apply machinery, so the workflow only needs contents: read): - Rewrite .github/workflows/docs-spelling.yml to run crate-ci/typos, dropping the check-spelling comment/apply jobs and the pull_request_target trigger. - Replace the check-spelling config files with typos.toml: scope to packages/docs, exclude auto-generated release notes and binary assets, and allowlist words typos otherwise mis-flags (HSA, Lage/lage, BA). - Fix genuine typos surfaced in current docs (schedules.md, paying-in-full.md). - Update the contributing guide and docs-writing skill to describe the new allowlist location. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DstzjPjGYdWMCG4jsrH6a1 * [AI] Add docs-spelling release note and format README Fix the remaining CI checks on the spell-check migration: - Add upcoming-release-notes/8249.md (Maintenance) so the "Release notes" check passes. - Reformat .github/actions/docs-spelling/README.md with oxfmt so the lint ("Test") and autofix.ci checks pass (autofix.ci is not permitted to modify files under .github, so the formatting has to be committed directly). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DstzjPjGYdWMCG4jsrH6a1 * [AI] Disable credential persistence in docs-spelling checkout Address the code-scanning (zizmor) and CodeRabbit review finding: the read-only spell-check job performs no authenticated git operations, so set persist-credentials: false on actions/checkout as a hardening measure. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DstzjPjGYdWMCG4jsrH6a1 --------- Co-authored-by: Claude <noreply@anthropic.com> * [AI] Add Docker, GitHub, and Anthropic sponsor logos to README (#8247) * [AI] Add Docker, GitHub, and Anthropic sponsor logos to README https://claude.ai/code/session_01Kq3v49wAVgQxW5H7LoTrgV * [AI] Add release notes for sponsor logos README update * Fix sponsor links in README.md --------- Co-authored-by: Claude <noreply@anthropic.com> * fix flaky tests (#8248) * fix split sort order on new splits * hide tooltips on report page to reduce vrt flakes * note * Update VRT screenshots Auto-generated by VRT workflow PR: #8248 --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * Bump the npm_and_yarn group across 1 directory with 6 updates (#8253) Bumps the npm_and_yarn group with 6 updates in the / directory: | Package | From | To | | --- | --- | --- | | [dompurify](https://github.com/cure53/DOMPurify) | `3.4.2` | `3.4.10` | | [form-data](https://github.com/form-data/form-data) | `4.0.5` | `4.0.6` | | [launch-editor](https://github.com/vitejs/launch-editor) | `2.12.0` | `2.14.1` | | [tar](https://github.com/isaacs/node-tar) | `7.5.13` | `7.5.16` | | [webpack-dev-server](https://github.com/webpack/webpack-dev-server) | `5.2.4` | `5.2.5` | | [ws](https://github.com/websockets/ws) | `7.5.10` | `7.5.11` | Updates `dompurify` from 3.4.2 to 3.4.10 - [Release notes](https://github.com/cure53/DOMPurify/releases) - [Commits](https://github.com/cure53/DOMPurify/compare/3.4.2...3.4.10) Updates `form-data` from 4.0.5 to 4.0.6 - [Release notes](https://github.com/form-data/form-data/releases) - [Changelog](https://github.com/form-data/form-data/blob/master/CHANGELOG.md) - [Commits](https://github.com/form-data/form-data/compare/v4.0.5...v4.0.6) Updates `launch-editor` from 2.12.0 to 2.14.1 - [Commits](https://github.com/vitejs/launch-editor/compare/v2.12.0...v2.14.1) Updates `tar` from 7.5.13 to 7.5.16 - [Release notes](https://github.com/isaacs/node-tar/releases) - [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md) - [Commits](https://github.com/isaacs/node-tar/compare/v7.5.13...v7.5.16) Updates `webpack-dev-server` from 5.2.4 to 5.2.5 - [Release notes](https://github.com/webpack/webpack-dev-server/releases) - [Changelog](https://github.com/webpack/webpack-dev-server/blob/main/CHANGELOG.md) - [Commits](https://github.com/webpack/webpack-dev-server/compare/v5.2.4...v5.2.5) Updates `ws` from 7.5.10 to 7.5.11 - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/compare/7.5.10...7.5.11) --- updated-dependencies: - dependency-name: dompurify dependency-version: 3.4.10 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: form-data dependency-version: 4.0.6 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: launch-editor dependency-version: 2.14.1 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: tar dependency-version: 7.5.16 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: webpack-dev-server dependency-version: 5.2.5 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: ws dependency-version: 7.5.11 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * improve CI yarn cache to make the fetch step quicker (#8252) * restore partial yarn caches * note * [AI] refactor(build): replace process.env with import.meta.env in browser code (#8212) * [AI] refactor(desktop-client): use import.meta.env for REACT_APP_* in client-only code Client-only UI/hook code read process.env.REACT_APP_* directly. process is not defined in the browser, so these relied on the build's process shim and were sensitive to chunk placement (a module-level read in useThemeCatalog landed in the eager entry chunk and crashed startup). Switch them to Vite's native import.meta.env (envPrefix is REACT_APP_), which is statically replaced at build time. Cross-platform shared code (shared/environment.ts) and the bootstrap (browser-preload.js) keep process.env since they also run under Node and rely on the browser shim. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] refactor(build): remove process shim by migrating remaining browser process.env reads * Rename 8212.md to process-env.md --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv> * [AI] Add Dependency Review scan on pull requests (#8251) * [AI] Add Dependency Review scan on pull requests Adds a Dependency Review workflow that runs on every pull request and fails the check when a PR introduces a dependency with a known high or critical severity vulnerability. A summary comment is posted on failure. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012JdxWJL3mUMVSJuyDakVRZ * [AI] Scope Dependency Review to lockfile changes, drop PR comment Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012JdxWJL3mUMVSJuyDakVRZ * [AI] Post Dependency Review summary comment on lockfile PRs Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012JdxWJL3mUMVSJuyDakVRZ * [AI] Lower Dependency Review threshold to moderate severity Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012JdxWJL3mUMVSJuyDakVRZ * [AI] Shorten Dependency Review release note Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012JdxWJL3mUMVSJuyDakVRZ --------- Co-authored-by: Claude <noreply@anthropic.com> * [AI] Migrate remaining CI workflows to Depot action runners (#8254) * [AI] Migrate remaining workflows to depot action runners Switch all remaining GitHub-hosted runners over to their depot equivalents: - ubuntu-latest -> depot-ubuntu-latest in the VRT-related jobs (e2e-test vrt/merge-vrt, e2e-vrt-comment, vrt-update-generate, vrt-update-apply) - windows-2022/windows-latest -> depot-windows-2022 in the electron build matrices (electron-pr, electron-master, publish-nightly-electron) and publish-microsoft-store Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013dQEH47R5kTkPCvhC2cpoB * [AI] Add release note for depot runner migration Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013dQEH47R5kTkPCvhC2cpoB --------- Co-authored-by: Claude <noreply@anthropic.com> * CLI: Search for config file in additional directories. (#8208) * CLI: Search for config file in additional directories. Searches in parents up to home directory, and in global config directory. See changes to `packages/cli/README.md#config-file` for full directory and filename details. * Update CLI documention for config file location. Also sync CLI config file documentation in packages/cli/README.md and packages/docs/docs/api/cli.md. * [autofix.ci] apply automated fixes * Address CodeRabbit comments. * Fix typos. * Mention option of trusting CA cert in CLI docs. Tangential to the rest of the PR, but hardly worth a standalone PR. * Mention global config directory in release note --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv> * [AI] Fix postBinary network-failure on Alpine by removing manual Content-Length (#8195) * [AI] Fix postBinary network-failure on Alpine by removing manual Content-Length (#8168) Stop hand-setting the Content-Length header in postBinary and let fetch derive it from the body, and preserve the underlying fetch error as the PostError cause so network failures are diagnosable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Preserve underlying error as cause in del() and patch() network failures --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * [AI] Fix auto-post schedule catch-up (#7997) * [AI] Fix auto-post schedule catch-up * [AI] Add release note for schedule catch-up * [AI] Address schedule catch-up review * [AI] Add one-time auto-post regression coverage * [AI] Preserve one-time schedule auto-posting * Add Docs page for Akahu Bank Sync (#8145) * create akahu bank sync doc * add akahu to bank sync sidebar menu * add akahu to supported providers on bank-sync page * Update link for Akahu provider documentation * Refine Akahu setup documentation warnings Removed redundant warning about experimental features and streamlined the message. --------- Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv> * automation UI: fix various bugs (#8258) * sort automations in tooltip * numerical inputs should save on change not blur * fix undefined weekly balance cap start * fix phantom 0 contribution * correctly migrate limit automations from remainder and periodic templates * note * allow template 0 templates * move publish jobs back to github runners (#8263) * move publish jobs back to github runners * note * Update upcoming-release-notes/github-runners-publish.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * comments --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * [AI] Document user enumeration trade-off on /admin/users endpoint (#8270) * [AI] Document accepted user-enumeration trade-off on /admin/users endpoint Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SffVjwqvL1TJPyR2KVjKaN * [AI] Add release note for /admin/users user-enumeration documentation Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SffVjwqvL1TJPyR2KVjKaN --------- Co-authored-by: Claude <noreply@anthropic.com> * Bump the npm_and_yarn group across 1 directory with 2 updates (#8272) Bumps the npm_and_yarn group with 2 updates in the / directory: [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) and [undici](https://github.com/nodejs/undici). Updates `http-proxy-middleware` from 3.0.6 to 3.0.7 - [Release notes](https://github.com/chimurai/http-proxy-middleware/releases) - [Changelog](https://github.com/chimurai/http-proxy-middleware/blob/v3.0.7/CHANGELOG.md) - [Commits](https://github.com/chimurai/http-proxy-middleware/compare/v3.0.6...v3.0.7) Updates `undici` from 7.27.2 to 7.28.0 - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](https://github.com/nodejs/undici/compare/v7.27.2...v7.28.0) --- updated-dependencies: - dependency-name: http-proxy-middleware dependency-version: 3.0.7 dependency-type: direct:development dependency-group: npm_and_yarn - dependency-name: undici dependency-version: 7.28.0 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * [AI] Fix CORS proxy GitHub API allowlist prefix bypass (#8273) * [AI] Fix CORS proxy GitHub API allowlist prefix bypass The api.github.com branch of the plugin allowlist check used a boundary-less startsWith, so an allowlisted `owner/repo` also authorized prefix-matched repos such as `owner/repo-private`. With a server GitHub token configured, this let authenticated users read private repositories through the proxy. Require an exact match or a trailing-slash path boundary instead, and add regression tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] Shorten CORS proxy fix release note Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * [AI] api: add browser build (#8166) * [AI] api: add browser build Continuation of #7545 (and #8143, which moved the browser Web Worker bootstrap into loot-core). This adds the browser-compatible build of @actual-app/api: the full loot-core stack runs inside a Web Worker and a thin main-thread facade exposes the same public surface as the Node entry. loot-core: - Add platform/client/backend-worker.ts: createBackendWorker(worker) centralizes absurd-sql's main-thread initBackend + __absurd:* message filtering, and is exported for the api to reuse. - Refactor browser-preload's start.ts and worker-bridge.ts to route through it (drops the duplicated __absurd:* filtering; terminate() now also clears listeners). Behavior is preserved. api: - browser-worker.ts: Web Worker entry owning real loot-core, speaking loot-core's {id,name,args} backend protocol; wraps fetch to map the .js -> .js.data migration names. - index.browser.ts: main-thread facade that spawns the worker. - browser/rpc.ts: main-thread <-> worker RPC bridge (uses createBackendWorker). - browser/lib-stub.ts: stubs lib.send so methods.ts routes over postMessage. - vite.browser.config.mts / vite.browser-worker.config.mts and asset bundling in vite.config.mts (sql-wasm.wasm + data/ + data-file-index.txt); package.json build chain, browser export, and absurd-sql/npm-run-all/vite-plugin-node-polyfills deps. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [AI] Rename release note to match PR number 8166 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [AI] api: move browser worker plumbing into loot-core Address review feedback: the browser worker logic shouldn't live in the api package since it's generic "loot-core as a browser npm package" plumbing, not api-specific. Resolve it via loot-core's existing conditional-export system instead of a vite alias. - loot-core: add server/main.api-browser.ts (worker-routing `lib` that delegates to the existing platform/client/connection), selected via a new `api-browser` export condition on "./server/main". Move the worker entry to server/api-browser-worker.ts. - api: the facade (index.browser.ts) now reuses loot-core's client connection via a minimal global.Actual.getServerSocket shim and spawns the worker built from loot-core's entry. Deletes browser-worker.ts, browser/rpc.ts and browser/lib-stub.ts (~270 lines) in favor of the shared loot-core code. - The facade build selects the impl with resolve.conditions: ['api-browser'] instead of resolve.alias, removing the enforce-boundaries lint exception. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [AI] api: simplify browser build cleanups Post-review cleanups (no behavior change): - backend-worker.ts: fix stale doc comment referencing the removed packages/api/browser/rpc.ts (the consumer is now index.browser.ts). - api-browser-worker.ts: drop the redundant `endsWith('/data-file-index.txt')` disjunct (subsumed by `endsWith('data-file-index.txt')`) and the no-op `input as RequestInfo | URL` re-casts on the fetch pass-throughs. - index.browser.ts: replace the single-field `state` holder with a plain module-level `let backend`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [AI] api: drop the createBackendWorker wrapper, revert browser-preload churn Now that the facade rides on loot-core's platform/client/connection (which already ignores absurd-sql's `__`-prefixed internal messages), the only thing the main thread still needs is absurd's one-line initBackend. So: - Replace the 60-line createBackendWorker wrapper (onMessage/postMessage/ terminate/listener filtering) with a tiny initBrowserBackend(worker) helper. - Revert browser-preload/start.ts and worker-bridge.ts to their merged state — they keep importing absurd's initBackend directly, so this PR no longer refactors just-merged files and the loot-core change is purely additive. - Facade holds a plain Worker handle and calls worker.terminate() on shutdown. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [AI] api: trim verbose comments in the browser build Keep only the load-bearing "why" notes (the absurd-sql init, the fetch/.js.data workaround, the global.Actual shim, the api-browser condition); drop protocol diagrams, usage examples, and restated context. No code/behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [AI] api: drop redundant absurd-sql dependency No api source imports absurd-sql; it's reached only through @actual-app/core (the facade's initBrowserBackend and the bundled worker), which declares it. It's bundled into the build output, so it isn't a runtime dependency of the published package either. Bundles are byte-for-byte unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [AI] api: drop the api-browser condition, inject the worker into the connection Review feedback: the api-browser export condition fought loot-core's idiom (browser IS the default platform; node/"api" is the override) and hid a bug — the condition-swapped lib only had `send`, so api.utils.amountToInteger was silently undefined in the browser build. Replace the condition with a plain transport seam: - methods.ts no longer imports server/main; it sends through api/send.ts, which the Node entry wires to in-process lib.send and the browser entry wires to loot-core's client connection. utils.ts re-exports amountToInteger/integerToAmount from shared/util directly (fixes the bug). - loot-core's client connection init() now accepts an optional injected socket; connectBackendWorker(worker) installs absurd-sql's bridge and connects in one call. This removes the forged global.Actual shim and the facade's `define: { global: 'globalThis' }` workaround. Desktop's argless init() path is unchanged. - Delete main.api-browser.ts, the "./server/main" conditional export, and the facade's resolve.conditions. The 4 loot-core snapshot failures (main.test.ts / transfer.test.ts) are pre-existing on the branch without these changes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [AI] api: route methods through the client connection, slim the facade Review feedback: get rid of the _setSend seam and move the worker init logic into loot-core. - methods.ts now imports `send` from loot-core's client connection. The node build resolves it via the existing `api` condition to a new in-process implementation (platform/client/connection/index.api.ts — previously a dead re-export of the electron client), matching the platform idiom: browser is the default, node/"api" overrides. api/send.ts is deleted and the node entry (index.ts) reverts to master byte-for-byte. - loot-core's startBackendWorker(worker, config, assetsBaseUrl) now owns the whole worker boot (absurd-sql bridge + connection.init + server init RPC), so the browser entry just spawns the worker and calls it. - Trims: single chained build script (drops the npm-run-all dep), copyFileSync instead of the linkOrCopy helper, drop the worker entry's error listeners (browsers already log worker uncaughts to DevTools). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [AI] api: trim prose comments to the essential ones Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [AI] fix client connection stalling when no sends are queued at connect Found while smoke-testing the browser api build in a real consumer app: the client connection only nulled messageQueue (switching from queueing to direct postMessage) when the queue was non-empty at 'connect'. The desktop app always has queued sends by then, so it never hits this; the api facade connects before sending anything, so every later send queued forever and init() hung. Drop the queue on connect unconditionally. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [AI] fix client connection dropping api/* error replies Found via the browser smoke test: opening an e2e-encrypted budget without the key surfaced as "undefined is not an object" instead of an error. The server connection forwards api/* handler failures as {type:'reply', id, error}, but the web client connection only read `result`, silently resolving undefined for every failed api/* call. Reject when the reply carries an error, matching the server's envelope (and the old api rpc behavior). The electron client has the same latent gap; left untouched here. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [AI] api: move the worker entry back into the api package Review feedback: browser-worker.ts is the api package's build entry, not generic loot-core code — move it back (it now imports loot-core through the public @actual-app/core exports instead of #-subpaths). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [AI] api: drop the manifest rewrite, align shutdown, trim comments The data-file-index.txt manifest now lists migrations under their real .js names (the worker's fetch shim already redirects those to the .js.data files on disk), so the manifest-rewriting half of the shim is gone. The worker init payload carries config and assetsBaseUrl as separate fields instead of smuggling a private key into the config object, browser shutdown() mirrors the node facade, and the client connection's queue flush drops a redundant branch. * [AI] fix rejected handler promises poisoning close-budget runHandler only removed a method's promise from runningMethods when it fulfilled, so any handler rejection left a stale promise in the set. The next close-budget then failed: flushRunningMethods' Promise.all rejected with that old, unrelated error before closeBudget even ran. Remove settled promises on rejection too, and flush with Promise.allSettled since rejected methods already reported their error to their caller. * [AI] api: add a browser e2e test A Playwright test loads the built browser bundle in chromium from a static COOP/COEP server and exercises the full stack: worker boot, the rejected-error contract, budget import, transaction read-back, and IndexedDB persistence across a page reload. A dedicated workflow runs it in CI on api/loot-core changes. * [AI] api: drop the worker fetch shim; strip .data names in loot-core fs The .js.data scheme was spread across three coordinated pieces: the build renamed migrations on disk, the manifest listed names that didn't exist, and a global fetch monkey-patch in the worker un-lied at runtime. Now the manifest lists the true on-disk names and populateDefaultFilesystem (the only fetcher of these files) strips the .data suffix when naming the virtual file — the fetch patch is gone and the suffix becomes a small documented fs convention. Also: e2e static server uses a single stat and drops a dead MIME entry. * [AI] fix electron client connection dropping api/* error replies Both server connection variants post api/* handler failures as {type: 'reply', id, error}, but only the web client rejected on it (778ed25cf); the electron renderer client still resolved undefined. Mirror the check so the wire contract is implemented uniformly. * [AI] api: exclude the playwright e2e from vitest; review polish vitest's default include matched e2e/browser.test.ts and failed CI - a playwright test file defines no vitest tests. Exclude e2e/ from the unit-test run (it has its own yarn e2e script). Also from review: the browser exports condition now carries its own declaration file (the browser init's return type differs from the node facade), and a failed init terminates the worker it just spawned before rethrowing instead of leaving it running. * [AI] api: trim code comments to the essentials * [AI] api: always terminate the worker when browser shutdown fails Review feedback: a rejected close-budget left the worker running. Terminate in a finally block so cleanup is guaranteed while the failure still reaches the caller. * [AI] docs: document experimental browser support in the api package * [AI] docs: drop the CORS note from the api browser section * [AI] docs: trim the api browser section to the basics * [AI] api: drop the import comment in methods.ts * [AI] Run API browser test on depot runner Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] Align api vite-plugin-node-polyfills to 0.28.0 The api workspace was pinned to ^0.27.0 (left over from an earlier merge conflict resolution) while loot-core uses ^0.28.0. This both tripped the workspace version-consistency constraint and broke the browser worker build: under vite 8.0.16 the 0.27 process shim no longer round-trips runtime writes to process.env.PUBLIC_URL, so sql-wasm.wasm was fetched from /dist/undefinedsql-wasm.wasm (404) and api.init() failed in the browser e2e. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ⚡ Make transactions list re-renders non-blocking and perform optimistic updates (#8209) * make re-renders non blocking for trasactions and optimistic updates on transaction list * release notes * fix spelling * Update Titlebar colors and bare button theme colors (#8267) * Update bare button colors to use page color variables * Refactor button text color variables in dark theme Updated button text colors to use page text variables. * Refactor button text color variables in light.css Updated button text colors to use page text variables. * Fix indentation for buttonBareText variable * Update button text colors in midnight theme * Refactor Titlebar component styles and props Updated button text color and removed unnecessary color styles. * Fix button text color styling in LoggedInUser component * Correct color style in LoggedInUser Button Fix color style assignment in Button component. * Remove color prop from LoggedInUser component Removed the 'color' prop from LoggedInUser component. * [autofix.ci] apply automated fixes * Update VRT screenshots Auto-generated by VRT workflow PR: #8267 --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * EasyBank PayeeName fallback fix (#8243) * fix fallback logic for easybank payeeName * yarn generate:release-notes * fix type error * expand hasCreditor check * [autofix.ci] apply automated fixes * simplify changelog --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * disable include current month when looking at last montn in report (#8250) * Replace mobile amount input with calculator (#8200) * Add mobile calculator input * reduce SplitAmountInput props * [AI] Move typescript-strict-plugin to devDependencies in loot-core (#8284) * [AI] Move typescript-strict-plugin to devDependencies in loot-core It is a dev-only TypeScript tooling plugin used during typecheck, so it belongs in devDependencies like in all other packages. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hm1ibCg4H96ww51eDwRBiT * [AI] Add release note for typescript-strict-plugin devDependencies move Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hm1ibCg4H96ww51eDwRBiT --------- Co-authored-by: Claude <noreply@anthropic.com> * [AI] Replace vite-plugin-peggy-loader with an in-repo Vite plugin (#8285) * [AI] Replace vite-plugin-peggy-loader with in-repo Peggy plugin Drop the third-party vite-plugin-peggy-loader dependency in favor of a small in-repo Vite plugin (packages/loot-core/scripts/peggy.mts) that compiles .pegjs grammars using the existing peggy dependency. Wire it into the loot-core and api vite/vitest configs, and remove the now-dead loader from the component-library config (it imports no grammars). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Up3H4YR7L8Qb4UKxbasyMp * [AI] Host the Peggy Vite plugin in a workspace package Move the in-repo Peggy loader out of packages/loot-core/scripts and into a dedicated private workspace package, @actual-app/vite-plugin-peggy, so the loot-core and api configs reference it as a normal workspace dependency instead of reaching across packages with ../loot-core relative imports. The peggy dependency now lives with the plugin that uses it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Up3H4YR7L8Qb4UKxbasyMp * [AI] Rename release note to match PR number Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Up3H4YR7L8Qb4UKxbasyMp --------- Co-authored-by: Claude <noreply@anthropic.com> * Bump dompurify in the npm_and_yarn group across 1 directory (#8286) Bumps the npm_and_yarn group with 1 update in the / directory: [dompurify](https://github.com/cure53/DOMPurify). Updates `dompurify` from 3.4.10 to 3.4.11 - [Release notes](https://github.com/cure53/DOMPurify/releases) - [Commits](https://github.com/cure53/DOMPurify/compare/3.4.10...3.4.11) --- updated-dependencies: - dependency-name: dompurify dependency-version: 3.4.11 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * wire tests up to depot test results analysis (#8262) * emit junit files * wire up the actions * note * [autofix.ci] apply automated fixes * fix overly broad error * scope to master runs --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * [AI] Reduce dependencies (lodash, ua-parser-js, memoize-one, promise-retry, resize-observer polyfill) (#8278) * [AI] Reduce dependencies (lodash, ua-parser-js, memoize-one, promise-retry, resize-observer polyfill) Remove or replace several low-usage / native-coverable dependencies to shrink the dependency surface and transitive deps: - lodash -> es-toolkit (es-toolkit/compat) for the 5 functions in use (debounce, keyBy, isMatch, isEqual, uniqueId) - ua-parser-js -> inline UA regex checks in loot-core shared/platform - memoize-one -> small local memoizeOne helper in loot-core shared - promise-retry -> small local retry helper (loot-core shared + a local copy in desktop-electron, which loads compiled JS at runtime) - @juggle/resize-observer polyfill removed; native ResizeObserver is supported by all current targets uuid was intentionally left untouched: crypto.randomUUID is only available in secure contexts, which would break self-hosted instances served over plain HTTP (including loot-core code running in the browser web worker). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JdZrw7VVwyF6VJgb34U9Qo * [AI] Use es-toolkit main entry for keyBy in report spreadsheets The two report spreadsheet files only used keyBy from es-toolkit/compat. Switch them to the lighter main es-toolkit entry (which takes a key function instead of a string), so these chunks no longer pull in the compat layer. Runtime behavior is identical. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JdZrw7VVwyF6VJgb34U9Qo * [AI] Rename release note to slug and reword for readability Rename the dependency-reduction release note from the guessed PR number to a descriptive slug, and reword the body to read clearly as changelog content. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JdZrw7VVwyF6VJgb34U9Qo * [AI] Shorten release note wording Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JdZrw7VVwyF6VJgb34U9Qo * [AI] Harden retry helper against sync throws; simplify memoizeOne typing - retry: run fn inside a promise chain so synchronous throws (and synchronous retry() calls) are routed through the rejection handler instead of escaping uncaught. Applied to both loot-core and desktop-electron copies. - memoizeOne: use separate Args/Result generics and drop the `as T` cast. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JdZrw7VVwyF6VJgb34U9Qo * [AI] Update yarn.lock after merge and install Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com> * remove forced waits to speed up some Playwright tests (#8293) * speed up some playwright tests * note * [AI] Sync `set amount` rule actions when a schedule's amount is edited (#8266) * Sync `set amount` rule actions when a schedule's amount is edited A schedule's amount lives in its rule's amount *condition*, which is what the schedule editor reads and writes. If the rule was customized to also carry a plain `set amount` *action*, editing the amount only updated the condition, leaving the action stale. Auto-posting runs the rule, so the stale action reverted the posted amount to the old value even though the editor showed the new one. `updateSchedule` now keeps plain `set amount` actions in sync with the amount condition. Templated, formula, and `set-split-amount` actions compute their own value and are left untouched. Fixes #8265 * Add release note for #8266 * Clarify updateAmountActions comments (set-split-amount exclusion, null amount) * [AI] Rename updateAmountActions to typed updateActions * [AI] 🐛 Exclude completed schedule rules from payee rule counts (#8281) * [AI] Exclude completed schedule rules from payee rule counts * Update rule count logic on Payee page Fixes the rule count on the Payee page to exclude rules linked to complete schedules. * Correct category label in release notes Updated category from 'Bugfix' to 'Bugfixes'. * [autofix.ci] apply automated fixes * Update upcoming-release-notes/fix-payee-rule-count.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * [AI] Fix typecheck errors in payee rule count tests * [AI] Fix createSchedule typing in payee rule count test * [AI] Type createSchedule parameters for strict mode * [AI] Use ts-strict-ignore in payee rule count test instead of typing createSchedule * [AI] Use getCompletedScheduleRuleIds and type createSchedule for payee counts * [AI] Fix link-schedule action value type to use schedule id * [AI] Fix downstream types for schedule conditions and link-schedule value * [AI] Resolve schedule id before rendering ScheduleValue label --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Attempt to fix entered amount being lost sometimes when tapping the Nearby payee chip before field loses focus (#8196) Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv> * [AI] Fix append/prepend notes rules on new transactions (#8300) Append/prepend notes rules were ignored when adding a new transaction that already had a note, because rule changes are only applied to fields the user left empty (so manual input isn't overwritten). Add a shared `shouldApplyRuleChange` helper that still protects user-entered values but lets the notes field through when the rule result preserves the existing note as a prefix or suffix (append/prepend). The check is idempotent: rules re-run on every keystroke during entry, so it refuses to re-add text that a previous run already applied, preventing the appended text from duplicating. Used by both the desktop register and the mobile transaction editor. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * [AI] DX: fix CI Lage cache (#8305) * [AI] Fix CI Lage cache to target the real cache directory The "Cache Lage" step in the shared setup action cached the repo-root .lage directory, but Lage writes its build/test cache to node_modules/.cache/lage. The configured directory was always empty (the step even mkdir -p's it first), so the cache step did nothing and Lage results only persisted incidentally inside the yarn.lock-keyed node_modules cache, frozen between lockfile changes. Point both the mkdir and the cache step at node_modules/.cache/lage so the existing sha key + prefix restore-key actually roll the cache forward: each run restores the most recent prior entry and saves a fresh one, letting CI jobs/shards and reruns reuse earlier build/test results. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013fQ9w27xTJdsb4Q9QUmHD9 * [AI] Address review: drop comment, use slug release note Remove the explanatory comment from the Cache Lage step and rename the release note to a descriptive slug with a shorter, user-facing body, per the repo's release-note conventions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013fQ9w27xTJdsb4Q9QUmHD9 --------- Co-authored-by: Claude <noreply@anthropic.com> * [AI] DX: replace ts-node with Node native TypeScript execution (#8304) * [AI] Replace ts-node with Node native TypeScript execution Node 22.18+ runs TypeScript files directly via type stripping, so the ts-node dependency is no longer needed for the repo's helper scripts. - Run bin/ and loot-core pack-hook scripts with `node` instead of `ts-node` - Rename bin/*.ts scripts to .mts (unambiguous ESM, runs warning-free) - ESM-ify validate-publish-imports: __dirname / require.main via import.meta - Drop ts-node devDependency and the tsconfig ts-node override block - Bump engines.node to >=22.18.0 (first release with stable type stripping) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019d6S7BWQdmtAcYg89WuAVd * [AI] Use .mts import specifier and simplify release note Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019d6S7BWQdmtAcYg89WuAVd * [AI] Bump .nvmrc to v22.18.0 to match engines floor Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019d6S7BWQdmtAcYg89WuAVd --------- Co-authored-by: Claude <noreply@anthropic.com> * [AI] DX: strip color codes in expectSnapshotWithDiffer (#8310) * [AI] Strip ANSI codes in expectSnapshotWithDiffer to make snapshots deterministic The expectSnapshotWithDiffer helper snapshots the output of jest-diff's diff(), which chalk colorizes with ANSI escape codes when a color-capable terminal is detected. The committed snapshots were generated with color off (CI/no-TTY), so they are plain text and mismatched when the same tests ran locally in a color-capable terminal. Wrap the diff() output in util.stripVTControlCharacters so the snapshotted string is plain text regardless of terminal color detection. This fixes every test using the helper, not just the four currently failing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NEq4AkTQ5gSh1xKMfZXDQE * [AI] Add release note for snapshot helper ANSI fix Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NEq4AkTQ5gSh1xKMfZXDQE * [AI] Rename release note to slug-based filename Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NEq4AkTQ5gSh1xKMfZXDQE --------- Co-authored-by: Claude <noreply@anthropic.com> * [AI] DX: scope CI Lage cache key per job (#8311) * [AI] DX: scope CI Lage cache key per job The Lage cache key was shared across every job in the Build and Test workflows. The `setup` job (a dependency that finishes first) runs no Lage task, so it persisted an empty cache under the shared key; every other job then saw the key already existed and skipped saving its real cache. The next run restored that empty cache, causing a 100% cache miss. Key the cache per job so each job restores and persists its own Lage cache. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GsWzGLUnt9TWkmNVDiYTof * [AI] DX: remove comment and shorten release note Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GsWzGLUnt9TWkmNVDiYTof --------- Co-authored-by: Claude <noreply@anthropic.com> * [AI] Add knip to detect unused files, dependencies and exports (#8309) Adds the knip tool with a monorepo-tuned config (knip.json) and a CI gate in check.yml, plus the dead-code and dependency cleanup it surfaced. The unused-export/type/duplicate rules are disabled so exporting an unused symbol is allowed. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * [AI] DX: enable oxlint no-useless-default-assignment in strict packages (#8306) * [AI] Disable oxlint no-useless-default-assignment rule This rule requires the `strictNullChecks` compiler option to function, but the project intentionally keeps `strict: false` (per-file strictness is handled by typescript-strict-plugin, which oxc's type-aware linter doesn't understand). As a result the rule only emitted noise — over 1,200 warnings telling us it couldn't run — so turn it off explicitly, matching how other ill-fitting type-aware rules are already disabled. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014K4HhohrAYT7K4vY1L7Q2G * [AI] Simplify no-useless-default-assignment disable comment Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014K4HhohrAYT7K4vY1L7Q2G * [AI] Add release note for disabling no-useless-default-assignment Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014K4HhohrAYT7K4vY1L7Q2G * [AI] Enable no-useless-default-assignment, disable only in non-strict packages Turn the rule on by default so the strict packages (ci-actions, cli, component-library, crdt) enforce it, and add an override disabling it in the packages that still set `strict: false` (api, desktop-client, desktop-electron, loot-core, plugins-service, sync-server), where the rule can't function without strictNullChecks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014K4HhohrAYT7K4vY1L7Q2G * [AI] Trim no-useless-default-assignment override comment to just the TODO Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014K4HhohrAYT7K4vY1L7Q2G * [AI] Rename release note to a slug Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014K4HhohrAYT7K4vY1L7Q2G --------- Co-authored-by: Claude <noreply@anthropic.com> * [AI] DX: add recommended VS Code extensions for onboarding (#8303) * [AI] Add recommended VS Code extensions for onboarding Add a .vscode/extensions.json so contributors are prompted to install the oxc formatter (matching the configured editor.defaultFormatter), plus the Playwright and Vitest extensions. Whitelist the file in .gitignore. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G1m2LJehnDTuhbvBhRdCze * [AI] Add release notes for VS Code recommended extensions Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G1m2LJehnDTuhbvBhRdCze --------- Co-authored-by: Claude <noreply@anthropic.com> * automation UI: reference schedule by ID not name (#8308) * schedule by id * migrate schedule indicators * add tests * update release note * fix loading states * [AI] feat(budget analysis report): Add Balance & Category selector (#8162) * [AI] feat(budget-report): add balance-only view mode Add a 'Balance only' toggle to the Budget Analysis report that hides the Budgeted, Spent, and Overspending Adjustment series and renders the balance as a plain line chart, similar to the Net Worth graph. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] fix(budget-report): always display ending balance for consistent header spacing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] feat(budget-report): replace balance toggle buttons with series dropdown Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] fix(budget-report): capitalize Categories in balance mode dropdown Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] fix(budget-report): fix test imports and lint errors in BudgetAnalysisGraph test Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: added in release note --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] Fix Age of Money report widget title not saving correctly (#8320) * [AI] Fix Age of Money report widget title not saving correctly Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] Use mutateAsync to properly await mutations in AgeOfMoney widget Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix flaky vrt by increasing timeout (#8323) * fix test * note * Update docs with Actuali iOS app (#8324) * Update docs with Actuali iOS app * Update packages/docs/docs/community-repos.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Initial payee location documentation (#8241) * Initial payee location documentation * Move to dedicated experimental section Based on PR feedback * [AI] feat(budget analysis report): show hidden categories (#8164) * [AI] feat(budget-report): add toggle to include hidden categories Add a 'Show hidden categories' button that includes hidden expense categories in the Budget Analysis report. This prevents historic data from being misrepresented when a category (e.g. a car fund) is hidden. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] feat(budget-report): replace hidden categories button with icon toggle Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] fix(budget-analysis): use correct package alias in test import Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: added in release note * [AI] refactor(budget-analysis): extract isBaseCategory helper and reuse in tests Assisted-by: ClaudeCode:claude-sonnet-4-6 * [AI] feat(budget-report): clarify hidden categories toggle state Replace the ambiguous icon-only toggle with a highlighted active state and explicit click-to-show/hide tooltip text so the current selection is clear. Add aria-pressed for accessibility. Assisted-by: ClaudeCode:claude-opus-4.8 * [AI] feat(budget-report): consolidate chart options into Options dropdown menu Replace the separate chart-type icon toggle, balance/category Select dropdown, and hidden-categories eye icon with a single Options popover (Popover + Menu) matching the pattern used by other reports (Sankey). The menu contains: - Switch to line/bar chart - Show balance (toggle) - Show categories (toggle, replaces balanceOnly) - Show hidden categories (toggle) Also rebases onto upstream/master to incorporate the balanceOnly/Select changes from #8162 before consolidating them. Assisted-by: ClaudeCode:claude-sonnet-4-6 * [AI] fix(budget-report): use static 'Bar chart' toggle in Options menu for i18n Replace the dynamic 'Switch to line/bar chart' string with a static 'Bar chart' label and a boolean toggle (on = bar, off = line), matching the toggle pattern used by other menu items and making the string straightforward to translate. Assisted-by: ClaudeCode:claude-sonnet-4-6 * Enhance budget analysis report with dropdown options Introduced a dropdown to group options and added the ability to show hidden categories in the budget analysis report. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * update release process (#8334) * [AI] Add cross-platform hook adding 🤖 emoji to comments (#8331) * [AI] Add cross-platform hook keeping GitHub comments/issues in Chinese or pirate voice Adds a shared agent hook that requires anything an agent posts to GitHub (PR/issue comments, PR reviews, created issues) to be written in 简体中文 (preferred) or, failing that, a fun pirate voice — never plain English. - scripts/agent-hooks/github-comment-style.sh: shared guard. Reads tool_input.body/.title, allows CJK or pirate-flavoured text, blocks plain English via exit 2. Fails open on malformed payloads. - Wired for Claude (.claude/settings.json PreToolUse), Codex (.codex/config.toml PreToolUse) and Cursor (beforeMCPExecution adapter). - Documents the rule in AGENTS.md, the canonical pr-and-commit-rules.md and the Cursor rule so platforms without hook support apply it manually. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht * [AI] Use a slug for the release-note filename; document slug naming in AGENTS.md Release-note filenames don't need to be the PR number — a short descriptive slug works too (the PR link is resolved at release time). Rename the new note accordingly and note this in AGENTS.md's directory reference. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht * [autofix.ci] apply automated fixes * [AI] Auto-label PRs with Chinese descriptions as "ai spam" via CodeRabbit Add a CodeRabbit labeling instruction (auto_apply_labels is already on) that applies the "ai spam" label when a PR description contains Chinese/CJK characters — a strong signal of AI-generated spam for this English-language project. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht * [AI] Exempt CodeRabbit-addressed comments from the Chinese/pirate voice hook CodeRabbit parses its commands (@coderabbitai review/resolve/etc.) as plain English, so the github-comment-style guard now skips any comment mentioning @coderabbitai / @coderabbit instead of forcing it into Chinese or pirate. Documented the exception in the canonical and Cursor rule files. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht * [AI] Key CodeRabbit voice exemption on authorship, not mentions The hook only ever runs on the agent's own outgoing comments, so CodeRabbit's own comments are already out of scope (it posts under its own identity). Drop the @coderabbitai mention bypass — a comment merely name-dropping the bot is still the agent's prose and must follow the Chinese/pirate rule, closing an easy plain-English bypass. Docs updated to match. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht * [AI] Pin CodeRabbit reviews to English CodeRabbit was reading AGENTS.md / pr-and-commit-rules.md (which ask contributors to comment in Chinese/pirate) and applying that to its own reviews, posting them in Chinese. Set `language: en-US` and `tone_instructions` so the bot's own reviews, summaries and replies stay in English; the Chinese/pirate rule is for contributor/agent comments, not CodeRabbit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht * [AI] Replace Chinese/pirate comment voice with a robot-emoji prefix Scrap the 简体中文/pirate voice scheme (and its CodeRabbit language override and Chinese-based "ai spam" label) in favour of one simple rule: every GitHub comment, review or issue an agent posts must be prefixed with 🤖. The shared guard now blocks any body — or, for issues, title — that doesn't start with the robot emoji; docs and per-platform wiring updated to match. .coderabbit.yaml is restored to its original state. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht * [AI] Silence SC1007 false positive on CDPATH= cd in Cursor MCP adapter shellcheck flags the intentional `CDPATH= cd` empty-env prefix as SC1007 (mistaking it for an assignment). Add a scoped inline disable directive on that line; behaviour is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht * [AI] Fail closed on guard execution errors in Cursor MCP adapter The adapter previously allowed the call on any non-0/2 exit from github-comment-style.sh, so a broken or missing guard would silently disable enforcement. Deny on unexpected exits instead (matching guard-shell.sh), with a message that marks it as an execution problem rather than a real policy denial. The shared guard still fails open on malformed payloads (its own exit-0 choice). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht * [AI] Add hook requiring a blank PR template when agents create PRs New shared guard pr-template-blank.sh blocks mcp__github__create_pull_request unless the body is the repo's PR template, unmodified (cosmetic whitespace aside) — agents must leave it blank for the human, per AGENTS.md. Wired for Claude (PreToolUse), Codex (PreToolUse) and Cursor (the beforeMCPExecution adapter now dispatches per-tool to the right guard). Docs and a release note updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht * [AI] Simplify agent-hook guard scripts - github-comment-style.sh: fold the empty/whitespace-only case into the prefix check, dropping a redundant tr subshell per field. - pr-template-blank.sh: drop the dead CR strip in canon() (the trailing whitespace sub already removes a trailing carriage return). - before-mcp-github.sh: emit the constant allow payload with printf instead of spawning jq on the common (allow) path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] Drop duplicated PR-template/robot-emoji rules now that hooks enforce them The PR-template-blank and GitHub-comment robot-emoji-prefix rules are enforced by cross-platform hooks, so the copies scattered across AGENTS.md and the Cursor rules file are redundant. Remove them and point at the canonical pr-and-commit-rules.md, which keeps the full rule (including the PR-template Chinese exception that no hook can enforce). Also delete the internal PR-template-guard release note. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] Drop "enforced by hooks" wording from the PR/comment rule docs State the rules plainly without the meta-commentary about hook enforcement. Keeps the actual rules (don't fill the PR template, prefix GitHub comments/reviews/issues with 🤖, the Chinese exception, and the your-own-comments-only note) intact. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] Trim enforcement meta-commentary; shorten robot-emoji release note Drop the "isn't enforced automatically" / "handled by tooling" framing from AGENTS.md and pr-and-commit-rules.md (keeping the rule and the "apply it yourself" responsibility), and shorten the robot-emoji-prefix release note. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] Fail closed on unreadable payloads in the GitHub agent guards Align the new guards with the git-guard.sh / guard-shell.sh convention: fail closed when the hook payload can't be read, fail open only on a genuinely absent optional field. - github-comment-style.sh: block on invalid JSON / missing / non-object .tool_input; allow only an absent body/title within a valid object. - before-mcp-github.sh: deny on a jq parse failure / missing .tool_name instead of falling through to allow. - pr-template-blank.sh: an absent/null body now fails open (previously it normalized to "" and wrongly blocked PR creation); an empty-string body is still treated as a real non-template submission. - .codex/config.toml: describe the full guard scope (comment body + issue title) in the hook comment and statusMessage. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * speed up electron builds (#8337) * size up runners, skip translations where possible * note * mdx -> md (#8338) * mdx -> md * [AI] Outlaw .mdx in the docs package Docusaurus 3 compiles .md through MDX, so JSX and imports work in plain .md files and there is no need for the .mdx extension. Narrow the enforce-doc-links remark plugin to only accept .md: reject any .mdx source file at build time, flag links that target a .mdx file, and drop the .mdx branches from the directory scanner and link resolution. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [AI] Add performance indexes for transactions table (#8335) * [AI] Add performance indexes for transactions table * release notes * [AI] Remove unselective tombstone-only index from performance migration * [AI] api: make the browser build work in consumer production bundles (#8289) * api: make the browser build work in consumer production bundles The browser build shipped `new Worker(new URL("./worker.js", import.meta.url))` in dist/browser.js. A consumer bundler (Vite/Rollup) recognizes that as a worker entry and re-bundles the already-prebuilt worker.js from scratch, which desyncs the absurd-sql/connection RPC so `init` throws a structured-clone error. It only worked in dev and in the package's own e2e because both serve dist verbatim. Make the browser build fully self-contained instead: - Inline the worker into browser.js and spawn it from a Blob URL, so consumer bundlers never see a worker entry to re-bundle. The worker is built as an IIFE (classic worker), mirroring the web app's kcab.worker. - Embed the sql.js wasm and the default filesystem data (migrations + default DB) into the worker so it performs no PUBLIC_URL asset fetches: - loot-core sqlite gains an opt-in `setWasmBinary` (dormant for web/node); - the worker installs a scoped fetch shim serving the embedded data/* files from a sentinel base URL. - Share the asset-collection logic between the Node disk copy and the embedded build via scripts/embedded-assets.mjs so the two never drift. Result: `import '@actual-app/api'` + `init()` works in any bundler with zero config; only COOP/COEP headers remain required (SharedArrayBuffer). Adds an e2e (e2e/consumer + e2e/global-setup.mjs) that builds a real consumer app for production and boots it under COOP/COEP — coverage the verbatim-dist harness can't provide. Docs note the build is self-contained and that cross-origin isolation is still required. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] api: address review feedback (guard DOM lookup, revoke blob URL on worker failure) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] api: address review nitpicks (drop BodyInit cast, reword release note) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] api: build the browser worker via Vite ?worker&inline Replace the hand-rolled worker inlining (a custom Vite plugin that read the prebuilt worker.js off disk and inlined it as a string, plus a second build config and a build-ordering dependency) with Vite's native `?worker&inline` import. This collapses the two browser build configs into one, removes the `virtual:actual-worker-code` module and the manual Blob URL spawn, and drops the worker-before-facade build step. Vite's `?worker&inline` sub-build doesn't receive vite-plugin-node-polyfills' global-shim injection (the plugin writes it to `build.rollupOptions`, but the worker sub-build reads `worker.rollupOptions`), so the worker crashed on `process is not defined`. Patch the plugin to also emit the injection on `worker.rollupOptions` — additive and backwards compatible (the web app build, including loot-core's nodePolyfills worker, is unaffected). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] api: embed browser assets via Vite, drop the embedded-assets script Replace scripts/embedded-assets.mjs with Vite-native asset imports in the worker entry: `?inline` for the sql.js wasm and default DB, `import.meta.glob` (`?raw`) for the migrations. The worker builds the default-filesystem wire format from those at module load, so the embedded set comes straight from loot-core and can't drift. The Node build only needs migrations + the default DB on disk (it reads them at runtime); the old script also wrote sql-wasm.wasm and the data/ fetch tree, which are dead now that the browser worker is self-contained. Replace writeEmbeddedAssetsToDist with a small closeBundle copy of just those two. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] api: simplify embedded data map; drop loot-core comment churn Merge the worker's binData/textData into a single dataFiles map (Response accepts both bytes and strings), collapsing the fetch shim's two lookups into one. Revert the unrelated comment edits to loot-core's backend-worker.ts and fs/index.ts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] api: rename e2e consumer-dist to consumer/dist; drop a prose comment Use the standard `dist` directory name for the consumer fixture's build output (now e2e/consumer/dist), and remove the verbose comment from index.browser.ts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] loot-core: own the default-filesystem assets; consumers stop reaching in Add packages/loot-core/default-filesystem.mjs as the single source of truth for loot-core's runtime assets (sql.js wasm, default DB, migrations) and the data-file-index wire format, exported as @actual-app/core/default-filesystem. - @actual-app/api: the browser worker embeds them via the actual-embedded-assets Vite plugin (which calls collectEmbeddedAssets), replacing the relative ?inline/?raw/import.meta.glob reaches into ../loot-core; the Node build copies migrations + default DB using the helper's paths. Drops the direct @jlongster/sql.js devDependency (loot-core resolves the wasm). - @actual-app/web: stagePublicData copies from the helper's paths instead of hardcoding loot-core's tree and the sql.js wasm location. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] loot-core: scan migrations once when collecting embedded assets collectEmbeddedAssets() read the migrations directory twice — once in its own loop and once via buildDataFileIndex(). List the names once and pass them through, keeping the index wire-format defined in a single place. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * Restore budget table scroll position when navigating back from spent transactions (#8144) * feat: Restore budget table scroll position when navigating back from spent transactions page * fix: Update transaction tests to include new categories and adjust assertions * Add release notes for budget scroll position restoration * refactor: Improve error handling when clicking on visible spent-amount cells, per suggestion from coderabbit bot * refactor: revert changes to test budget, add separate scroll-test test budget * revert previous snapshot changes * Add gitignore pattern for personal devcontainer config * fix: broken tests * refactor: address coderabbitai feedback https://github.com/actualbudget/actual/pull/8144#discussion_r3446850158 * fix: update VRT snapshots for onboarding screen * [autofix.ci] apply automated fixes * refactor: modify scrolling test to add categories to existing test budget instead of creating separate budget --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * [AI] Add design competition blog post for sidenav redesign (#8297) * [AI] Add design competition blog post for sidenav redesign Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HbXFM9ooWVw9SRA9No7vhF * [AI] Set design competition blog post publish date to 27th July 2026 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HbXFM9ooWVw9SRA9No7vhF * [AI] Remove stray closing tags from design competition blog post Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HbXFM9ooWVw9SRA9No7vhF * Update 2026-07-27-design-competition-sidenav.md --------- Co-authored-by: Claude <noreply@anthropic.com> * Always show stacked bar graph tooltip label on hover (#8356) * Always show stacked bar graph tooltip label on hover * Handle edge case with maxToolTipItems+1 labels showing * Replace last item label instead of adding new one * Filter visible items on non zero values for consistency * make SimpleFIN token 'forbidden' check more flexible (#8339) * make SimpleFIN token 'forbidden' check more flexible * clear accessKey when token is updated * https -> fetch * [AI] Add tests for SimpleFIN token claim fix Cover the flexible 'Forbidden' matching, full claim-response handling, and access-key invalidation when the setup token changes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * note * Update upcoming-release-notes/fix-simplefin-token-claim.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * coderabbit feedback * [AI] Test SimpleFIN blank access key handling Cover rejecting a blank claim response (not persisted) and re-claiming when an empty access key is cached. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Add Thyrium to custom theme catalog (#8295) * Update customThemeCatalog.json Add Thyrium to custom theme catalog * Create add-thyrium-theme.md * Update repository reference for Thyrium theme --------- Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv> * reorganise the SimpleFIN key saving logic (#8362) * remove the simplefin intercept in the secrets handler * note * Resolve payee name during rule evaluation (#8360) When a rule action sets payee_name, resolve it to the actual payee ID immediately during rule execution instead of waiting until finalization. This allows later staged rules to match on the resolved payee. Previously, payee_name actions temporarily set payee to "new", and the real payee ID was only resolved in finalizeTransactionForRules after rule matching had completed. Fixes #8359 * Fix mobile category delete confirmation (#8301) * Formulas code clean up (#8369) * Clean up * [autofix.ci] apply automated fixes * md * code review from coderabbit --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * Added an importer for German DKB and comdirect banks to the list of community importers (#8371) * Added link to German DKB and Comdirect bank importer * made text bold * [AI] resolve release-note PR links from base branch history (#8368) * [AI] Resolve release-note PR links from base branch history The release-note generator recovered each note's PR number by reading the "(#NNNN)" suffix that GitHub appends to squash-merge commit subjects, searching HEAD — the release-notes/X.Y.Z branch it commits to. That branch only carries the note file *contents* (checked out from origin/release), not the commit history, so any note added to the release branch after it was cut (e.g. release cherry-picks) has no add-commit reachable from HEAD and silently loses its link. Search origin/master instead, where every note traces back to its original squash commit with the correct PR number. The release branch is unsuitable because "bring release branch up to date" squashes collapse original PRs. Verified across all 63 descriptively-named notes in the 26.7.0 set: 60 resolve identically to before, 3 previously-unlinked cherry-picks (#8301, #8335, #8339) are now linked, 0 regressions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * make the note human * Update upcoming-release-notes/release-notes-resolve-pr-link-from-master.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * 🔖 (26.7.0) (#8332) * 🔖 (26.7.0) * Generate release notes for v26.7.0 * update blog author * Generate release notes for v26.7.0 * release notes * Generate release notes for v26.7.0 * Generate release notes for v26.7.0 * Generate release notes for v26.7.0 * relink PRs from cherry-picked changes * tweak wording of release summary * update blog post date * akahu experimental --------- Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk> * [AI] Improved context menu (#8106) * Context Menu is implemented * allow Document and Window in ref event listener hook * did stupid examples to showcase api usage * recreated TransactionMenu using new context action * fixed styling and removed excess calls to useContextMenuAction * removed extra call * extra imports * started converting files to useContextMenu * fixed context menus in reports * linting and formatting * deleted TransactionMenu * release notes * fixed coderabbit * light context menu refactor * added nested contexts for state and actions * refactored useContextMenuAction * [autofix.ci] apply automated fixes * removed log * updated contextMenuSlice * removed package.json change * filter => some * tried to improve test * [AI] test: fix payees e2e right-click test by scoping row query to table * linting * [AI] chore: fix all linting issues from new context menu files * fixed compiler * fixed linting/formatting * coderabbit fix * [AI] fix(electron): connect to sync server on 127.0.0.1 instead of localhost Node.js > 17 resolves localhost to ::1 (IPv6) by default, while the Electron sync server binds explicitly to 127.0.0.1 (IPv4). This caused setServerUrl to fail when the desktop app starts its internal sync server, trapping the user in an onboarding loop between /config-server and /bootstrap. This commit updates the URL to use 127.0.0.1 to match the server binding. * [AI] Fix isPlaywright detection for Electron Playwright tests The userAgent check was changed from includes('playwright') to === 'playwright', but the Electron main process appends ' playwright' to the default userAgent (preserving the 'Electron' substring that isElectron() relies on). Use includes() so the flag is detected correctly in Electron e2e tests. Also reverts the localhost→127.0.0.1 change which did not resolve the VRT pixel diffs. * re-added missing menu option --------- Co-authored-by: Alec Bakholdin <alecbakholdin.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * ✨ Added support to pluggy date correction based on installments (#8380) * Added support to pluggy date correction based on installments * md * Update packages/sync-server/src/app-pluggyai/app-pluggyai.js Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * date now considers instalments, originalDate is the date without it * creating new instance instead of same obj * code review * aligning to code review * simpler --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * 🐛 Fix Formula query sliding-window timeframes (#8244) * clean up sliding window to use the default feature * more feedbacks * Fix formula sliding window helper import * Use report time range for formula queries * fixes * Trim formula sliding window fix * code rabbit code review * removing local helpers * change query definitions to show the mode when not static / sliding window * [autofix.ci] apply automated fixes * Simplified even more the code * Added test to sliding window + feedback --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * Bump js-yaml in the npm_and_yarn group across 1 directory (#8379) Bumps the npm_and_yarn group with 1 update in the / directory: [js-yaml](https://github.com/nodeca/js-yaml). Updates `js-yaml` from 3.14.2 to 3.15.0 - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodeca/js-yaml/compare/3.14.2...3.15.0) --- updated-dependencies: - dependency-name: js-yaml dependency-version: 3.15.0 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Reload when PWA shared worker is killed by Android (#8322) * Reload when PWA shared worker is killed by OS Android can kill a SharedWorker process while the PWA is in the background. The the PWA become unresponsive when user reopens it. This change introduces a 1s reload timer whenever the tab becomes visible, and uses the 'resume association' mechanism to track if the shared worker is responsive (which cancels the timer). If it's not, it reloads the page and recovers the shared worker. * [autofix.ci] apply automated fixes * Improve release note * Simplify guard for app init (started safer, initialized was redundant) * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * [AI] Clarify nYNAB export alternatives (#8330) * bcrypt -> argon2id (#7829) * bcrypt -> argon2id * coderabbit feedback * copilot review * switch hashing settings * [AI] Add unit tests for sync-server password hashing Cover password.js, focused on the bcrypt -> argon2id migration: - loginWithPassword upgrades a legacy bcrypt hash to argon2id on a successful login, leaves it untouched on a failed login, and does not needlessly rehash an existing argon2id hash - hashPassword/verifyPassword round-trip, bcrypt backward-compat, and graceful handling of non-string/malformed hashes - bootstrapPassword/changePassword/checkPassword behaviour Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * dedupe minimatch --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [AI] Add tag button mobile (#8344) * added hash button to enable one-click mobile usage of tags feature * added tags to demo * release notes * [autofix.ci] apply automated fixes * fixed long tags wrapping * removed duplicate <Button> usages and fixed coderabbit smell * removed lint * added null check per coderabbit * moved Menus to after the menu to be consistent with transaction menus * textWrap => whiteSpace for coderabbit * updated tsconfig to fix typecheck issue * Update VRT screenshots Auto-generated by VRT workflow PR: #8344 * typecheck * improved button ergonomics * also don't add space if beginning of note * fixed not working on iOS * handle focus properly for add tag button * fixed focus issue when clicking button on new transaction * added delay because without it chrome doesnt work * Added some more logic for button focusing * fix typecheck * extracted common style into iconStyle * added aria label * removed log statement --------- 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> * Bank Sync Provider per budget file [1/3] - Sync server - Add file-scoped bank sync secrets (#8316) * First PR for secrets per file * md * Code Review * Code rabbit code review * Removed migration (new column) in favor of string concatenation * automation UI: reference schedule by ID not name (#8308) * schedule by id * migrate schedule indicators * add tests * update release note * fix loading states * [AI] feat(budget analysis report): Add Balance & Category selector (#8162) * [AI] feat(budget-report): add balance-only view mode Add a 'Balance only' toggle to the Budget Analysis report that hides the Budgeted, Spent, and Overspending Adjustment series and renders the balance as a plain line chart, similar to the Net Worth graph. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] fix(budget-report): always display ending balance for consistent header spacing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] feat(budget-report): replace balance toggle buttons with series dropdown Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] fix(budget-report): capitalize Categories in balance mode dropdown Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] fix(budget-report): fix test imports and lint errors in BudgetAnalysisGraph test Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: added in release note --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] Fix Age of Money report widget title not saving correctly (#8320) * [AI] Fix Age of Money report widget title not saving correctly Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] Use mutateAsync to properly await mutations in AgeOfMoney widget Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix flaky vrt by increasing timeout (#8323) * fix test * note * Update docs with Actuali iOS app (#8324) * Update docs with Actuali iOS app * Update packages/docs/docs/community-repos.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Initial payee location documentation (#8241) * Initial payee location documentation * Move to dedicated experimental section Based on PR feedback * [AI] feat(budget analysis report): show hidden categories (#8164) * [AI] feat(budget-report): add toggle to include hidden categories Add a 'Show hidden categories' button that includes hidden expense categories in the Budget Analysis report. This prevents historic data from being misrepresented when a category (e.g. a car fund) is hidden. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] feat(budget-report): replace hidden categories button with icon toggle Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] fix(budget-analysis): use correct package alias in test import Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: added in release note * [AI] refactor(budget-analysis): extract isBaseCategory helper and reuse in tests Assisted-by: ClaudeCode:claude-sonnet-4-6 * [AI] feat(budget-report): clarify hidden categories toggle state Replace the ambiguous icon-only toggle with a highlighted active state and explicit click-to-show/hide tooltip text so the current selection is clear. Add aria-pressed for accessibility. Assisted-by: ClaudeCode:claude-opus-4.8 * [AI] feat(budget-report): consolidate chart options into Options dropdown menu Replace the separate chart-type icon toggle, balance/category Select dropdown, and hidden-categories eye icon with a single Options popover (Popover + Menu) matching the pattern used by other reports (Sankey). The menu contains: - Switch to line/bar chart - Show balance (toggle) - Show categories (toggle, replaces balanceOnly) - Show hidden categories (toggle) Also rebases onto upstream/master to incorporate the balanceOnly/Select changes from #8162 before consolidating them. Assisted-by: ClaudeCode:claude-sonnet-4-6 * [AI] fix(budget-report): use static 'Bar chart' toggle in Options menu for i18n Replace the dynamic 'Switch to line/bar chart' string with a static 'Bar chart' label and a boolean toggle (on = bar, off = line), matching the toggle pattern used by other menu items and making the string straightforward to translate. Assisted-by: ClaudeCode:claude-sonnet-4-6 * Enhance budget analysis report with dropdown options Introduced a dropdown to group options and added the ability to show hidden categories in the budget analysis report. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * update release process (#8334) * [AI] Add cross-platform hook adding 🤖 emoji to comments (#8331) * [AI] Add cross-platform hook keeping GitHub comments/issues in Chinese or pirate voice Adds a shared agent hook that requires anything an agent posts to GitHub (PR/issue comments, PR reviews, created issues) to be written in 简体中文 (preferred) or, failing that, a fun pirate voice — never plain English. - scripts/agent-hooks/github-comment-style.sh: shared guard. Reads tool_input.body/.title, allows CJK or pirate-flavoured text, blocks plain English via exit 2. Fails open on malformed payloads. - Wired for Claude (.claude/settings.json PreToolUse), Codex (.codex/config.toml PreToolUse) and Cursor (beforeMCPExecution adapter). - Documents the rule in AGENTS.md, the canonical pr-and-commit-rules.md and the Cursor rule so platforms without hook support apply it manually. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht * [AI] Use a slug for the release-note filename; document slug naming in AGENTS.md Release-note filenames don't need to be the PR number — a short descriptive slug works too (the PR link is resolved at release time). Rename the new note accordingly and note this in AGENTS.md's directory reference. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht * [autofix.ci] apply automated fixes * [AI] Auto-label PRs with Chinese descriptions as "ai spam" via CodeRabbit Add a CodeRabbit labeling instruction (auto_apply_labels is already on) that applies the "ai spam" label when a PR description contains Chinese/CJK characters — a strong signal of AI-generated spam for this English-language project. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht * [AI] Exempt CodeRabbit-addressed comments from the Chinese/pirate voice hook CodeRabbit parses its commands (@coderabbitai review/resolve/etc.) as plain English, so the github-comment-style guard now skips any comment mentioning @coderabbitai / @coderabbit instead of forcing it into Chinese or pirate. Documented the exception in the canonical and Cursor rule files. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht * [AI] Key CodeRabbit voice exemption on authorship, not mentions The hook only ever runs on the agent's own outgoing comments, so CodeRabbit's own comments are already out of scope (it posts under its own identity). Drop the @coderabbitai mention bypass — a comment merely name-dropping the bot is still the agent's prose and must follow the Chinese/pirate rule, closing an easy plain-English bypass. Docs updated to match. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht * [AI] Pin CodeRabbit reviews to English CodeRabbit was reading AGENTS.md / pr-and-commit-rules.md (which ask contributors to comment in Chinese/pirate) and applying that to its own reviews, posting them in Chinese. Set `language: en-US` and `tone_instructions` so the bot's own reviews, summaries and replies stay in English; the Chinese/pirate rule is for contributor/agent comments, not CodeRabbit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht * [AI] Replace Chinese/pirate comment voice with a robot-emoji prefix Scrap the 简体中文/pirate voice scheme (and its CodeRabbit language override and Chinese-based "ai spam" label) in favour of one simple rule: every GitHub comment, review or issue an agent posts must be prefixed with 🤖. The shared guard now blocks any body — or, for issues, title — that doesn't start with the robot emoji; docs and per-platform wiring updated to match. .coderabbit.yaml is restored to its original state. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht * [AI] Silence SC1007 false positive on CDPATH= cd in Cursor MCP adapter shellcheck flags the intentional `CDPATH= cd` empty-env prefix as SC1007 (mistaking it for an assignment). Add a scoped inline disable directive on that line; behaviour is unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht * [AI] Fail closed on guard execution errors in Cursor MCP adapter The adapter previously allowed the call on any non-0/2 exit from github-comment-style.sh, so a broken or missing guard would silently disable enforcement. Deny on unexpected exits instead (matching guard-shell.sh), with a message that marks it as an execution problem rather than a real policy denial. The shared guard still fails open on malformed payloads (its own exit-0 choice). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht * [AI] Add hook requiring a blank PR template when agents create PRs New shared guard pr-template-blank.sh blocks mcp__github__create_pull_request unless the body is the repo's PR template, unmodified (cosmetic whitespace aside) — agents must leave it blank for the human, per AGENTS.md. Wired for Claude (PreToolUse), Codex (PreToolUse) and Cursor (the beforeMCPExecution adapter now dispatches per-tool to the right guard). Docs and a release note updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lqu3eZi5djm2cPAEm3yVht * [AI] Simplify agent-hook guard scripts - github-comment-style.sh: fold the empty/whitespace-only case into the prefix check, dropping a redundant tr subshell per field. - pr-template-blank.sh: drop the dead CR strip in canon() (the trailing whitespace sub already removes a trailing carriage return). - before-mcp-github.sh: emit the constant allow payload with printf instead of spawning jq on the common (allow) path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] Drop duplicated PR-template/robot-emoji rules now that hooks enforce them The PR-template-blank and GitHub-comment robot-emoji-prefix rules are enforced by cross-platform hooks, so the copies scattered across AGENTS.md and the Cursor rules file are redundant. Remove them and point at the canonical pr-and-commit-rules.md, which keeps the full rule (including the PR-template Chinese exception that no hook can enforce). Also delete the internal PR-template-guard release note. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] Drop "enforced by hooks" wording from the PR/comment rule docs State the rules plainly without the meta-commentary about hook enforcement. Keeps the actual rules (don't fill the PR template, prefix GitHub comments/reviews/issues with 🤖, the Chinese exception, and the your-own-comments-only note) intact. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] Trim enforcement meta-commentary; shorten robot-emoji release note Drop the "isn't enforced automatically" / "handled by tooling" framing from AGENTS.md and pr-and-commit-rules.md (keeping the rule and the "apply it yourself" responsibility), and shorten the robot-emoji-prefix release note. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] Fail closed on unreadable payloads in the GitHub agent guards Align the new guards with the git-guard.sh / guard-shell.sh convention: fail closed when the hook payload can't be read, fail open only on a genuinely absent optional field. - github-comment-style.sh: block on invalid JSON / missing / non-object .tool_input; allow only an absent body/title within a valid object. - before-mcp-github.sh: deny on a jq parse failure / missing .tool_name instead of falling through to allow. - pr-template-blank.sh: an absent/null body now fails open (previously it normalized to "" and wrongly blocked PR creation); an empty-string body is still treated as a real non-template submission. - .codex/config.toml: describe the full guard scope (comment body + issue title) in the hook comment and statusMessage. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * speed up electron builds (#8337) * size up runners, skip translations where possible * note * mdx -> md (#8338) * mdx -> md * [AI] Outlaw .mdx in the docs package Docusaurus 3 compiles .md through MDX, so JSX and imports work in plain .md files and there is no need for the .mdx extension. Narrow the enforce-doc-links remark plugin to only accept .md: reject any .mdx source file at build time, flag links that target a .mdx file, and drop the .mdx branches from the directory scanner and link resolution. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [AI] Add performance indexes for transactions table (#8335) * [AI] Add performance indexes for transactions table * release notes * [AI] Remove unselective tombstone-only index from performance migration * [AI] api: make the browser build work in consumer production bundles (#8289) * api: make the browser build work in consumer production bundles The browser build shipped `new Worker(new URL("./worker.js", import.meta.url))` in dist/browser.js. A consumer bundler (Vite/Rollup) recognizes that as a worker entry and re-bundles the already-prebuilt worker.js from scratch, which desyncs the absurd-sql/connection RPC so `init` throws a structured-clone error. It only worked in dev and in the package's own e2e because both serve dist verbatim. Make the browser build fully self-contained instead: - Inline the worker into browser.js and spawn it from a Blob URL, so consumer bundlers never see a worker entry to re-bundle. The worker is built as an IIFE (classic worker), mirroring the web app's kcab.worker. - Embed the sql.js wasm and the default filesystem data (migrations + default DB) into the worker so it performs no PUBLIC_URL asset fetches: - loot-core sqlite gains an opt-in `setWasmBinary` (dormant for web/node); - the worker installs a scoped fetch shim serving the embedded data/* files from a sentinel base URL. - Share the asset-collection logic between the Node disk copy and the embedded build via scripts/embedded-assets.mjs so the two never drift. Result: `import '@actual-app/api'` + `init()` works in any bundler with zero config; only COOP/COEP headers remain required (SharedArrayBuffer). Adds an e2e (e2e/consumer + e2e/global-setup.mjs) that builds a real consumer app for production and boots it under COOP/COEP — coverage the verbatim-dist harness can't provide. Docs note the build is self-contained and that cross-origin isolation is still required. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] api: address review feedback (guard DOM lookup, revoke blob URL on worker failure) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] api: address review nitpicks (drop BodyInit cast, reword release note) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] api: build the browser worker via Vite ?worker&inline Replace the hand-rolled worker inlining (a custom Vite plugin that read the prebuilt worker.js off disk and inlined it as a string, plus a second build config and a build-ordering dependency) with Vite's native `?worker&inline` import. This collapses the two browser build configs into one, removes the `virtual:actual-worker-code` module and the manual Blob URL spawn, and drops the worker-before-facade build step. Vite's `?worker&inline` sub-build doesn't receive vite-plugin-node-polyfills' global-shim injection (the plugin writes it to `build.rollupOptions`, but the worker sub-build reads `worker.rollupOptions`), so the worker crashed on `process is not defined`. Patch the plugin to also emit the injection on `worker.rollupOptions` — additive and backwards compatible (the web app build, including loot-core's nodePolyfills worker, is unaffected). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] api: embed browser assets via Vite, drop the embedded-assets script Replace scripts/embedded-assets.mjs with Vite-native asset imports in the worker entry: `?inline` for the sql.js wasm and default DB, `import.meta.glob` (`?raw`) for the migrations. The worker builds the default-filesystem wire format from those at module load, so the embedded set comes straight from loot-core and can't drift. The Node build only needs migrations + the default DB on disk (it reads them at runtime); the old script also wrote sql-wasm.wasm and the data/ fetch tree, which are dead now that the browser worker is self-contained. Replace writeEmbeddedAssetsToDist with a small closeBundle copy of just those two. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] api: simplify embedded data map; drop loot-core comment churn Merge the worker's binData/textData into a single dataFiles map (Response accepts both bytes and strings), collapsing the fetch shim's two lookups into one. Revert the unrelated comment edits to loot-core's backend-worker.ts and fs/index.ts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] api: rename e2e consumer-dist to consumer/dist; drop a prose comment Use the standard `dist` directory name for the consumer fixture's build output (now e2e/consumer/dist), and remove the verbose comment from index.browser.ts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] loot-core: own the default-filesystem assets; consumers stop reaching in Add packages/loot-core/default-filesystem.mjs as the single source of truth for loot-core's runtime assets (sql.js wasm, default DB, migrations) and the data-file-index wire format, exported as @actual-app/core/default-filesystem. - @actual-app/api: the browser worker embeds them via the actual-embedded-assets Vite plugin (which calls collectEmbeddedAssets), replacing the relative ?inline/?raw/import.meta.glob reaches into ../loot-core; the Node build copies migrations + default DB using the helper's paths. Drops the direct @jlongster/sql.js devDependency (loot-core resolves the wasm). - @actual-app/web: stagePublicData copies from the helper's paths instead of hardcoding loot-core's tree and the sql.js wasm location. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * [AI] loot-core: scan migrations once when collecting embedded assets collectEmbeddedAssets() read the migrations directory twice — once in its own loop and once via buildDataFileIndex(). List the names once and pass them through, keeping the index wire-format defined in a single place. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * Restore budget table scroll position when navigating back from spent transactions (#8144) * feat: Restore budget table scroll position when navigating back from spent transactions page * fix: Update transaction tests to include new categories and adjust assertions * Add release notes for budget scroll position restoration * refactor: Improve error handling when clicking on visible spent-amount cells, per suggestion from coderabbit bot * refactor: revert changes to test budget, add separate scroll-test test budget * revert previous snapshot changes * Add gitignore pattern for personal devcontainer config * fix: broken tests * refactor: address coderabbitai feedback https://github.com/actualbudget/actual/pull/8144#discussion_r3446850158 * fix: update VRT snapshots for onboarding screen * [autofix.ci] apply automated fixes * refactor: modify scrolling test to add categories to existing test budget instead of creating separate budget --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * [AI] Add design competition blog post for sidenav redesign (#8297) * [AI] Add design competition blog post for sidenav redesign Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HbXFM9ooWVw9SRA9No7vhF * [AI] Set design competition blog post publish date to 27th July 2026 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HbXFM9ooWVw9SRA9No7vhF * [AI] Remove stray closing tags from design competition blog post Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HbXFM9ooWVw9SRA9No7vhF * Update 2026-07-27-design-competition-sidenav.md --------- Co-authored-by: Claude <noreply@anthropic.com> * Always show stacked bar graph tooltip label on hover (#8356) * Always show stacked bar graph tooltip label on hover * Handle edge case with maxToolTipItems+1 labels showing * Replace last item label instead of adding new one * Filter visible items on non zero values for consistency * make SimpleFIN token 'forbidden' check more flexible (#8339) * make SimpleFIN token 'forbidden' check more flexible * clear accessKey when token is updated * https -> fetch * [AI] Add tests for SimpleFIN token claim fix Cover the flexible 'Forbidden' matching, full claim-response handling, and access-key invalidation when the setup token changes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * note * Update upcoming-release-notes/fix-simplefin-token-claim.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * coderabbit feedback * [AI] Test SimpleFIN blank access key handling Cover rejecting a blank claim response (not persisted) and re-claiming when an empty access key is cached. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk> Co-authored-by: tabedzki <35670232+tabedzki@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: youngcw <calebyoung94@gmail.com> Co-authored-by: Matt Farrell <10377148+MattFaz@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Dustin Brewer <mannkind@thenullpointer.net> Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Alec Bakholdin <43560338+alecbakholdin@users.noreply.github.com> Co-authored-by: clintharris <336401+clintharris@users.noreply.github.com> Co-authored-by: Darwin Do <8429648+dsmaugy@users.noreply.github.com> * fix: add scoped ErrorBoundaries to mobile budget, account, and transaction pages (#8336) * fix: add scoped ErrorBoundaries to mobile budget, account and transaction pages Wrap the mobile BudgetPage, AccountPage and TransactionEdit in <ErrorBoundary FallbackComponent={FeatureErrorFallback}> so a rendering error in one of these pages is contained instead of crashing the whole app. Relates to #7391 * [autofix.ci] apply automated fixes * fix(desktop-client): enclose mobile page hooks within scoped ErrorBoundary The scoped ErrorBoundary added for the mobile budget, account, and transaction-edit pages only wrapped the returned JSX, so errors thrown by the pages' own hooks and initialization logic (data loading, useEffect) still escaped to the global fatal screen, defeating the boundary. Move each page's body into a child component rendered inside the boundary so the boundary encloses the full hook/render path, and add resetKeys so the feature recovers when the account (accountIdParam) or month (startMonth) changes. Addresses CodeRabbit review feedback on the mobile error-boundary PR. * refactor(desktop-client): remove prose comments from mobile error boundaries Drop the explanatory comments added with the scoped error boundaries on the mobile account, budget, and transaction edit pages, per maintainer review feedback. The code is self-explanatory without them; behavior is unchanged. * refactor(desktop-client): move mobile error boundaries to the route level Wrap the mobile budget, account, and transaction edit routes in FinancesApp with the same ErrorBoundary/FeatureErrorFallback pattern already used for the /rules routes, instead of splitting each page into a wrapper and inner component. Each page renders as a child of the boundary, so its hooks and initialization logic are still enclosed, and the page components are left unchanged. --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * add Zero Dark theme (#8396) * Add Zero-Dark theme * Update customThemeCatalog.json * Create add-zero-dark-theme.md * Check in mobile app scaffolding generated by Capacitor (#8387) * Check in app scaffolding generated by Capacitor All files generated by `npx cap init` and `npx cap sync`. * Add release notes * Add package.json file * Update capacitor * Prune deps * CodeRabbit feedback * [AI] Add writing-release-notes skill (#8398) * [AI] Add writing-release-notes skill Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015pA2AZaXmt8ebDNNYEdBDA * [AI] Remove em-dashes from writing-release-notes skill Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015pA2AZaXmt8ebDNNYEdBDA * [AI] Add release note for writing-release-notes skill Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015pA2AZaXmt8ebDNNYEdBDA --------- Co-authored-by: Claude <noreply@anthropic.com> * Change single quote to double quotes usage (#8402) * prevent duplicate draft releases (#8381) * prevent duplicate draft releases * note * recognise EUA expired GoCardless responses (#8383) * recognise EUA expired GoCardless responses * note * add test * Bank Sync Provider per budget file [2/3] - Sync server - Pluggy.ai use per-budget credentials when available (#8317) * PR for pluggy ai wiring per file budget ni pluggy * Fixes from rebase * Code rabbit code review * [autofix.ci] apply automated fixes * added md * Restoring client cache * code review --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * [AI] Add api.getPreferences() for reading synced preferences (#8410) * [AI] Add api.getPreferences() for reading synced preferences Expose the internal 'preferences/get' handler as a public API method so consumers can read the budget's synced preferences (number format, currency, date format, etc.) without calling internal handlers directly. Re-export the SyncedPrefs type from @actual-app/api/models and document the new method in the API reference. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KxwSQJiPo5mEk79mA54TWg * Update models.ts --------- Co-authored-by: Claude <noreply@anthropic.com> * Improve crossover reloads (#8352) * fix: Preserve crossover report view during refresh * fix: Preserve crossover report view during refresh --------- Co-authored-by: youngcw <calebyoung94@gmail.com> * [AI] Add api.importBudget() and api.exportBudget() (#8414) * [AI] Add api.importBudget() and api.exportBudget() Add first-class import/export functions to @actual-app/api so consumers no longer have to drive the low-level worker RPC and the engine filesystem by hand. - api.importBudget(pathOrBytes, { type?, filename? }) imports an Actual .zip export (or a YNAB4/YNAB5 export), loads it, and resolves with the imported budget id. Raw bytes are passed straight to the import-budget handler instead of the upload-file-web temp-file dance, so it works in both the Node and browser builds and leaves no temp files behind. - api.exportBudget() exports the currently-loaded budget and resolves with the zip contents as a Uint8Array. - Both reject with an Error on failure instead of resolving { error }. - The import-budget handler now optionally accepts raw file contents (buffer + filename) alongside the existing filepath input, and returns the id of the loaded budget on success; existing callers are unaffected. - Add round-trip, file-path, and failure integration tests; update the API reference docs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014wq2oMcHb99mLb9qpaR8SN * [AI] Move ImportableBudgetType re-export to models.ts Type re-exports for API consumers live in models.ts (like Preferences), not methods.ts — follow the existing pattern. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014wq2oMcHb99mLb9qpaR8SN * [AI] Address review: relax error assertions, document type union - Assert importBudget failures on the stable "Error importing budget:" prefix instead of backend-specific error codes - Document options.type as the 'actual' | 'ynab4' | 'ynab5' union in the API reference instead of a generic string Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014wq2oMcHb99mLb9qpaR8SN --------- Co-authored-by: Claude <noreply@anthropic.com> * [AI] feat(budget analysis report): export csv feature (#8163) * [AI] feat(budget-report): add CSV export for budget analysis report Add an 'Export CSV' button that downloads a CSV file containing Month, Budgeted, Spent, Overspending Adjustment, and Balance columns for the currently displayed date range. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] feat(budget-report): replace CSV export button with download icon Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] refactor(csv-export): centralize CSV utility to loot-core with formula injection protection Extracts CSV field escaping and row building into a shared loot-core/shared/csv.ts module so desktop-client and cli packages can reuse it without duplication. Adds single-quote prefix guard against spreadsheet formula injection for values starting with =, +, -, @, tab, or carriage-return. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] fix(csv-export): fix loot-core import path and remove over-broad formula injection protection for negative numbers * fix: added in release note * Update packages/desktop-client/src/components/reports/reports/BudgetAnalysis.tsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update BudgetAnalysis.tsx removed extra </Tooltip> introduced by coderabbitai * fix: applied coderabbitai's suggestions for this PR * [autofix.ci] apply automated fixes * [AI] fix: resolve typecheck errors and test failures in csv export - Make labels param optional with English defaults in buildBudgetAnalysisCsv - Pass translated labels from BudgetAnalysis component call site - Fix escapeCsvField: remove tab from quote-wrapping check (formula prefix handles it) Assisted-by: ClaudeCode:claude-sonnet-4-6 * [AI] refactor(budget-analysis-export): use csv-stringify, move labels inside buildBudgetAnalysisCsv - Replace custom buildCsv with csv-stringify/sync (already used in loot-core) - Move translated column labels inside buildBudgetAnalysisCsv; callers no longer pass labels - Pass numbers directly to csv-stringify instead of converting with toFixed(2) - Add csv-stringify to desktop-client package.json deps - Fix release notes title to user-facing prose Assisted-by: ClaudeCode:claude-sonnet-4-6 * [AI] fix: remove orphaned csv.ts, fix release note wording - Delete loot-core/src/shared/csv.ts and csv.test.ts (orphaned after switch to csv-stringify) - Remove [AI] prefix from release note body (commit-style prefix inappropriate in release note content) Assisted-by: ClaudeCode:claude-sonnet-4-6 * [AI] fix: add csv-stringify to yarn.lock to fix immutable install The rebase onto main reset yarn.lock without the csv-stringify entry under the @actual-app/web workspace, while package.json still declared it. CI runs yarn install --immutable, which refused to reconcile the mismatch and failed the build. Assisted-by: ClaudeCode:claude-opus-4.8 * attempt to resolve the hanging error * [AI] fix: restore Tooltip import dropped during rebase Resolving the import-collision conflict against master accidentally dropped the Tooltip import used by the CSV export button. Restore it. Assisted-by: ClaudeCode:claude-opus-4.8 --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * [AI] Fix mobile swipe-to-delete broken by React Aria Button stopping pointerdown propagation (#8421) The a11y commit (65dee4c) replaced the plain div with a React Aria Button in ActionableGridListItem. React Aria's usePress unconditionally calls stopPropagation() on pointerdown, preventing the event from reaching the parent animated.div where useDrag listens. Adding onPressStart with continuePropagation() lets the event bubble through so the swipe gesture works. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * custom report clickthrough for category groups (#8391) * hide focus styling * allow clickthrough to group filters * note * [AI] Skip unused transfer payees in nYNAB import (#8424) * [AI] fix(ynab-import): skip literal transfer payees in nYNAB import YNAB auto-generates a 'Transfer : <Account Name>' payee for every account-to-account link. Actual already has its own account-transfer payee per account and transactions resolve to that instead, so the literal-text payee was being created but never used, just cluttering the Payees list after import. * [AI] test(ynab-import): cover transfer-payee resolution in importTransactions Regression test proving a transfer transaction resolves to the real Actual account-transfer payee via transfer_account_id, and never needs an entityIdMap entry for the (now-skipped) YNAB transfer payee. * [AI] docs: add release note for skipping ynab transfer payees * 🐛 Prevent Pluggy bank sync from repeatedly rewriting transactions (#8392) * Prevent Pluggy bank sync from repeatedly rewriting uncategorized and transfer transactions * Regression test * Changed solution for the fix * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * ✨ Adding visual improvements to excel formulas (#8245) * Adding visual improvements to excel formulas * cursor aware fix * code clean up * small fix * md * bug fix * bug fix * code rabbit code review * Code review * code rabbit code review * Bug fix when AST tree is invalid * code rabbit code review --------- Co-authored-by: youngcw <calebyoung94@gmail.com> * added dracula and alucard themes (#8447) * chore(api): migrate node version validation to TypeScript (#8461) * chore(api): migrate node version validation to TypeScript * [autofix.ci] apply automated fixes * docs: add release note for API TypeScript migration * Update upcoming-release-notes/wip-chore-api-migrate-node-version-validation-to-typescript.md Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv> * Update upcoming-release-notes/wip-chore-api-migrate-node-version-validation-to-typescript.md Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv> --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv> * Disable the shared worker on Android (same as is done for iOS already) (#8457) * Revert "Reload when PWA shared worker is killed by Android (#8322)" This reverts commit62ced9aa2b. * Disable shared worker in PWA on Android * 🐛 Theme fetch cache bursting (#8448) * cache bursting * retrigger actions * [AI] Remove grouped option from api/categories-get (#8415) The categories-get API now always returns a flat list of categories. Use category-groups-get for grouped data instead. * [AI] Run agent hooks and skills in Codex-based harnesses (ChatGPT desktop app) (#8462) * [AI] Run agent hooks and skills in Codex-based harnesses (ChatGPT desktop app) The ChatGPT desktop app (and Codex CLI/IDE) never picked up the repo's agent skills or lifecycle hooks: - Skills: Codex surfaces scan .agents/skills, not .claude/skills. Add a symlink mirror so both harness families load the same skill files. - Hooks: Codex lifecycle hooks are feature-flagged; enable them in the project .codex/config.toml and document the trust steps (project .codex layer trust + one-time /hooks trust) required for them to run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FMdDFdaErx9dqieTGh8ETg * [AI] Drop .agents/skills/README.md; document the mirror in AGENTS.md instead Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FMdDFdaErx9dqieTGh8ETg * [AI] Ignore .agents/** in knip like .claude/** The .agents/skills symlink mirror exposes skill reference files under a path knip scanned, flagging highlight-element.js as an unused file. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FMdDFdaErx9dqieTGh8ETg --------- Co-authored-by: Claude <noreply@anthropic.com> * [AI] Enable React Compiler for all monorepo packages with React code (#8463) * [AI] Enable React Compiler for all monorepo packages with React code The compiler was previously scoped to desktop-client/src only. Now: - desktop-client's Vite build also compiles component-library source (consumed as raw source through the same build), covering web, desktop, and mobile bundles - component-library's Storybook and Vitest pipelines apply the compiler so they exercise the same compiled output - docs (Docusaurus) enables babel-plugin-react-compiler for the site's own React components Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PdcKq244khwKhedvLCwu7p * [AI] Declare npm-run-all in component-library so its test script resolves Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PdcKq244khwKhedvLCwu7p * [AI] Generalize React Compiler include to any workspace package source Match packages/<name>/src instead of enumerating package names, so a future workspace library with React components is compiled automatically. Third-party code stays out via the babel plugin's default node_modules exclude, which applies alongside a custom include. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PdcKq244khwKhedvLCwu7p * [AI] Inline component-library test script instead of using npm-run-all Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PdcKq244khwKhedvLCwu7p * [AI] Drop React Compiler from docs The docs site is static content with a handful of simple components, so auto-memoization buys nothing there, and an unscoped babel plugin would also process Docusaurus theme code and every MDX page. Reverting keeps the compiler where it pays off: desktop-client and component-library. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PdcKq244khwKhedvLCwu7p --------- Co-authored-by: Claude <noreply@anthropic.com> * fix split transaction error popover from being shrunk vertically (#8479) * prevent TransactionError from having max-height:0 applied * note * [AI] Add tooltips to titlebar theme, privacy, and server status icons (#8440) * [AI] Add tooltips to titlebar theme, privacy, and server status icons Explain what the theme switcher, amount-visibility toggle, and server sync/status indicators do on hover, including what having a server connected means when none is configured yet. Assisted-by: ClaudeCode:claude-sonnet-5 * [AI] Merge duplicate server status branching in LoggedInUser Combine serverMessage() and serverTooltip() into a single getServerStatus() resolver so the button label and tooltip stay in sync as server states change. Assisted-by: ClaudeCode:claude-sonnet-5 * Add in requested line break * [AI] Add running-vrts skill for visual regression testing (#8477) * [AI] Add running-vrts skill for visual regression testing Documents how VRTs work in this repo (toMatchThemeScreenshots matcher, three-theme snapshots, data-vrt-mask, strict diff tolerance), the docker-based snapshot generation workflow, and the /update-vrt CI fallback. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AZAw7AR6LMHTGss1BtnBDT * [AI] Add codex skill symlink for running-vrts Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AZAw7AR6LMHTGss1BtnBDT * [AI] Add release note for running-vrts skill Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AZAw7AR6LMHTGss1BtnBDT --------- Co-authored-by: Claude <noreply@anthropic.com> * 🔒 reconciliation on mobile (#8384) * extract shared logic * entry point for mobile reconciliation * mobile reconciliation * note * e2e tests * generalise tests more * allow reconciling closed accounts * try to stop VRT updating * Update VRT screenshots Auto-generated by VRT workflow PR: #8384 * drop line break in reconciling banner * move reconciled/cleared icon to right side and shrink * Update VRT screenshots Auto-generated by VRT workflow PR: #8384 * fix icon spacing * fix layout shift * Update VRT screenshots Auto-generated by VRT workflow PR: #8384 --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * [AI] api: attach stable error codes that survive the worker boundary (#8437) * [AI] Attach stable error codes that survive the worker boundary Rejections from the backend worker reached consumers as an English message only: the machine-readable reason was stripped when the error was serialized for postMessage, so API consumers had to regex the prose to categorize failures. - Serialize rejected handler errors through a shared helper that lifts the reason slug onto a `code` field (plus `name`) on the envelope, so it survives the worker/utility-process boundary. `message` is unchanged, so the change is purely additive. - Tag the common connect failures with codes at their throw sites: init auth failures (network-failure, invalid-password, token-expired) and api/download-budget, api/load-budget, api/sync, api/bank-sync (budget-not-found, missing-key, decrypt-failure, ...). - Harden the error post: if the original error holds a non-cloneable value (e.g. an Emscripten ErrnoError carrying methods), postMessage used to throw a DataCloneError in the worker and that clone failure replaced the real error. Retry with a plain cloneable shape instead. - Add an integration test driving the real web client/server connection pair across a structured-clone channel, document the codes in the API reference, and add a release note. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AG6f77cRxhwPFjsNpQRmYo * [AI] Remove worker-boundary connection integration test Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AG6f77cRxhwPFjsNpQRmYo * [AI] Remove prose comment from connection error helpers Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AG6f77cRxhwPFjsNpQRmYo * [AI] Simplify worker-boundary error posting - Hoist the three-way reply-envelope dispatch out of the two platform connection files into a single postErrorReply helper, so the envelope rules and the DataCloneError fallback live in one place. - Return APIError envelopes from coerceError unchanged, and drop the unreachable fallbacks and unused meta field from the cloneable shape. - Let withErrorCode accept an undefined code, removing the one-off ternary at the bank-sync throw site. - Declare the client-side connection error shape once as ConnectionError instead of twice inline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AG6f77cRxhwPFjsNpQRmYo * [AI] Address review feedback on error-code contract wording and types - Docs and release note: scope the stable-code promise to the common documented failures, and say errors "usually" carry a message. - ConnectionError: model APIError (message required) and ServerError (message may be absent) as separate variants to match the wire shape, and adapt the sendCatch consumers in SavedFilterMenuButton. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AG6f77cRxhwPFjsNpQRmYo --------- Co-authored-by: Claude <noreply@anthropic.com> * [AI] migrations: enforce append-only migration files in CI (#8446) * [AI] Enforce append-only migration files in CI Shipped migrations are append-only: existing installs never re-run them, so editing one silently forks the database schema across the user base, and deleting one breaks the migration check for every existing budget file. The CI migration check now diffs blob hashes against the merge base to reject edits/deletions, keeps the existing timestamp-ordering rule, emits advisory PR annotations for migrations containing DROP/RENAME statements, and fails loudly when git commands fail instead of passing on empty output. First of a series that makes clients on different app versions sync safely against the same budget file. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Derive the newest upstream migration id with an explicit max The ls-tree listing is alphabetical, so taking the last entry only worked while every migration id has the same digit count. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Post advisory migration warnings as a PR comment The risky-SQL warnings were only visible as job annotations, and since the job passes there was no prompt to look at them. The migrations job now uploads its findings as an artifact, and a new workflow_run workflow (trusted base-repo context, so it also works for fork PRs whose tokens are read-only) validates the artifact against a fixed allowlist and posts/updates a PR comment — removing it again once the warnings are resolved. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Align overview numbering with the inline check comments --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * [AI] Refresh running balances after transaction edits (#8490) Co-authored-by: tianrking <tianrking@users.noreply.github.com> * [AI] fix(loot-core): replace adm-zip with fflate for zip handling (#8393) * [AI] fix(loot-core): replace adm-zip with fflate for zip handling Replaces the adm-zip dependency (which requires Node's `process`/`Buffer` and can break in browser bundle contexts) with fflate, a zero-dependency, isomorphic zip library that works identically in node and browser builds. Adds a small `zip-utils.ts` wrapper (`safeZip`/`safeUnzip`) around fflate to preserve the safety guarantees a hardened zip library provides by default, since all three call sites parse zips that may cross a trust boundary (cloud sync downloads, user-supplied budget/import files): - Rejects zip-slip/path-traversal entry names (absolute paths, `..` segments, backslashes, drive letters, NUL bytes). - Caps total archive size and per-entry uncompressed size, enforced via fflate's `filter` callback before it allocates the decompression buffer for each entry, to guard against decompression bombs. - Rejects duplicate/case-variant entry names to avoid ambiguity attacks. Updated call sites: budgetfiles/backups.ts (local backup zip write/read), cloud-storage.ts (cloud sync export/import), importers/ynab4.ts (YNAB4 budget import). * [AI] test(desktop-client): drop export/import perf e2e test Removes packages/desktop-client/e2e/export-import-performance.test.ts, added in an earlier commit to benchmark adm-zip vs fflate. Per review feedback (matt-fidd): this zip-handling logic changes rarely, and a future library swap would warrant its own fresh benchmarking rather than maintaining a ~300 line permanent regression test for it. The benchmark numbers already recorded in the PR description remain accurate for this change. * [AI] fix(loot-core): distinguish oversized-zip errors from generic parse failures cloud-storage.ts's importBuffer (shared by both cloud-sync download and the local "Import my budget -> Actual" file picker) caught every safeUnzip failure the same way and threw FileDownloadError('not-zip-file'). Since safeZip has no size limit but safeUnzip enforces one by default, a self-generated export that grows past that cap (e.g. a large db.sqlite) would fail to re-import with a misleading "this isn't a zip file" message, even though the file is perfectly valid, just larger than the cap. Catches UnsafeZipError separately and surfaces a new 'zip-too-large' reason, with matching UI messages in both places that already handle not-zip-file/invalid-zip-file/invalid-meta-file (loot-core's shared error mapper and desktop-client's local import modal), reusing the same support- link phrasing already used for invalid files. * [AI] fix(loot-core): stop faking runtime config for zip upload size limit Vite bakes process.env.ACTUAL_UPLOAD_FILE_SIZE_LIMIT_MB into the client bundle at build time, so it was never actually runtime-configurable in the browser build despite looking like sync-server's real env var. Drop the env indirection and hardcode a fixed 20MB entry cap / 60MB archive cap instead. * [AI] fix(loot-core): tighten zip entry-name matching in importBuffer * [AI] feat(loot-core): raise zip size limits to 500MB, warn on oversized/low-memory exports Matt-fidd's review on #8393 argued 20MB was too tight for real budgets and asked for (1) a more generous cap, (2) a warning when a backup is created that won't be re-importable, and (3) test coverage for zip.ts's safety checks (path traversal, size limits, duplicate entries, unsafe names, round-trip). Later review passes asked to trim the verbose comments in zip.ts and to use <Trans> instead of t() for the new warning strings. - Collapse the three separate default caps into one MAX_ZIP_SIZE (500MB), since archive size, per-entry size, and total uncompressed size all shared the same value anyway. safeUnzip can run outside the browser (Electron, or Node via @actual-app/api) where the whole archive is held in memory at once, and some self-hosting providers (PikaPods) run containers with as little as 256MB RAM, so the cap stays flat rather than compounding into a multi-GB worst case. - Add exceedsSafeUnzipLimits() to zip.ts so exportBuffer() can warn upfront, using the same cap import enforces, instead of drifting. - Add #platform/server/memory (default/api/electron) exposing os.freemem() on node/electron and null in the browser, per the existing platform-conditional-export pattern. exportBuffer() warns if the uncompressed db.sqlite is bigger than currently-free memory; skipped entirely when the platform has no such API. - Surface both warnings in the Export settings screen via <Trans>. - Add zip.test.ts covering round trip, path traversal, unsafe names, duplicate entries, and all three size caps. - Trim zip.ts's file-header comment per review feedback that it was too verbose to be useful. * [AI] feat: surface descriptive, translatable errors for unsafe zip imports UnsafeZipError now carries structured meta (zipReason + entryName/maxSize) instead of only a free-text English message. The meta rides on FileDownloadError('zip-too-large', ...) through the download and import-budget paths, and the desktop-client maps it to detailed, translated messages (which file, which limit) in getUnsafeZipError(); loot-core's shared getDownloadError() gets a plain-English equivalent for the headless API. Why structured meta rather than translating the message itself: i18n must not live in loot-core (see platform/client/connection/index.ts) - the backend worker has no i18next instance and doesn't know the user's locale, and a message translated at throw time would bake a locale into a string that also feeds the headless @actual-app/api and server logs, which want stable English. i18next also needs a static key plus interpolation params to extract and look up translations, so the error must cross the worker boundary as code + params (zipReason + entryName/maxSize) and be rendered with t() at display time in the client. The English Error.message is kept for logs and the API. * Update release note to include safe wrappers * [AI] fix(loot-core): require db.sqlite and metadata.json from the same archive directory importBuffer previously located db.sqlite and metadata.json independently, so an archive with a/db.sqlite and b/metadata.json would pair unrelated files, and duplicate matches were resolved by archive order. Now prefer the root-level pair; otherwise both files must come from exactly one shared directory, and anything ambiguous is rejected as invalid-zip-file. * [AI] fix(loot-core): restrict safeZip to flat entries so every name is validated safeZip only ran assertSafeEntryName on top-level keys, but fflate's Zippable type permits nested directory objects whose child names would reach zipSync unvalidated. Both callers pass flat records, so narrow the parameter to Record<string, Uint8Array> and let the compiler rule out nested input. * [AI] api: support custom dataDir (#8397) * [AI] Support custom dataDir in API browser build and stop masking worker errors Two fixes for the browser build of @actual-app/api: 1. A dataDir other than /documents failed with a cryptic DataCloneError: the directory didn't exist in the worker's virtual filesystem, and the resulting Emscripten ErrnoError (which carries a function-valued property) couldn't be structured-cloned, so postMessage's own DataCloneError replaced the real error entirely. The web worker connection now retries a failed postMessage with a serialized, cloneable copy of the message so the client always receives the actual failure. Cloneable errors pass through unchanged. 2. The web filesystem registers the configured document dir as a persisted root: it is created automatically on init, its files are persisted to IndexedDB (sqlite files symlinked into the blocked fs), and files recorded in IndexedDB are restored at boot regardless of prefix. The API browser worker defaults dataDir to /documents when omitted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Trim trailing slashes without a regex CodeQL flags the anchored /\/+$/ replace as quadratic on strings with many repeated slashes; walk the index back instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Address review: single persisted root, guarded fallback post, plain release note - Replace the accumulating persistedRoots set with the default /documents plus a single replaceable custom root, so switching document dirs no longer leaves stale roots marked as persisted - Guard safePost's final fallback post so a broken channel can't escape as an unhandled rejection - Reword the release note in plain user-facing language Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] loot-core: route server push messages through safePost --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * Added new theme "4Rules Blurple - Dark" and renamed existing theme "You Need A Dark Mode" -> "4Rules Cyan - Dark" (#8436) * Add files via upload * Update theme entries in customThemeCatalog.json * Update upcoming-release-notes/add-4rules-blurple-dark-theme.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> * [AI] Fix budget template notification translations (#8478) * [AI] Fix budget template notification translations * [AI] Address PR review feedback * [AI] Preserve cleanup error notifications * [AI] Use nested cleanup translation keys * [AI] Allow null in SQL query parameter types via shared SqlParam type (#8493) SQLite accepts null as a bound parameter (verifyParamTypes already allowed it at runtime), but the runQuery/all/first/firstSync/run signatures only declared string | number, forcing callers to cast. Extract a shared SqlParam type (string | number | null) from the sqlite platform modules and use it everywhere. Adds a test covering null params through all/first. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * [AI] redesign onboarding (#8495) * [AI] Redesign first-run onboarding for web/PWA users Fresh web/PWA installs whose origin does not serve a sync server now start in local-only mode on a redesigned welcome screen instead of the "Where's the server?" page, with syncing offered as an option. The server connection page is retitled and redesigned with a back button and friendlier copy. Transient probe failures (server unreachable) keep the previous behavior and persist nothing, so a briefly-down self-hosted server is retried on the next load. Electron keeps its existing server-config first run, and origins that serve a sync server still go to bootstrap/login. VRT snapshots for the onboarding test were deleted and need regenerating with yarn vrt:docker. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Update VRT screenshots Auto-generated by VRT workflow PR: #8495 * coderabbit --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * [AI]: Replacing month picker with day picker (#8427) * [AI] feat(reports): add browser-independent month range picker Replace the report date dropdowns with a popover month range picker built from React and Actual's own components instead of a native month input (which does not work in Firefox). Includes start/end month grids, quick-select presets folded into the popover, a draft selection that only recomputes the report on close, today highlighting, and an unbounded future range. Assisted-by: ClaudeCode:claude-opus-4.8 Co-authored-by: rznn7 <160644362+rznn7@users.noreply.github.com> * [AI] refactor(desktop-client): replace pikaday with react-aria in DateSelect Swap the Pikaday-based day picker in DateSelect for react-aria-components' Calendar primitives (backed by @internationalized/date). The user's firstDayOfWeekIdx preference is encoded via the Unicode 'fw' locale extension, and the prev/next arrows now use the existing SvgCheveron icons instead of the two bundled PNGs. Removes the pikaday and @types/pikaday dependencies, deletes the unused DateSelect.left/right.png assets, and adds @internationalized/date as a direct dependency. Assisted-by: ClaudeCode:claude-opus-4.8 * [AI] fix(desktop-client): resolve CI failures in month picker PR Remove four devDependencies (@juggle/resize-observer, @types/lodash, @types/promise-retry, memoize-one) that were added but never used, fixing the knip check. Dedupe the split @codemirror/state and @codemirror/view resolutions that the added/removed deps introduced, fixing typecheck. Pin shell-quote (a transitive dependency of launch-editor/npm-run-all) to >=1.8.4 via resolutions, matching the existing socks/serialize-javascript overrides, to clear the critical CVE flagged by Dependency Review. Assisted-by: ClaudeCode:claude-sonnet-5 * updated yarn.lock * Updated yarn.lock to address dependency issue * address more dependency issues * `yarn up -R '**'` to hopefully address all dependency issues * fixed donutgraph.tsx typing * [AI] feat(reports): add Month/Day granularity toggle to range picker Add a Month/Day toggle above the quick-select column in the report range picker so users can pick exact days, not just whole months. Switching to Day expands the range to cover whole days (start -> first of month, end -> last of month); switching back collapses to months. In day mode the picker emits yyyy-MM-dd start/end. Make the month-based reports tolerate day-level values: month range helpers in monthUtils already coerce day strings to months, and this replaces the few fragile 'start + "-01"' concatenations (BalanceForecast, BalanceForecastCard, BudgetAnalysis, balanceForecastChartData) with firstDayOfMonth/getMonth so a yyyy-MM-dd value is normalized instead of corrupted. Assisted-by: ClaudeCode:claude-opus-4.8 * [AI] feat(reports): honor day granularity, live end-offset, format-aware picker Task 3: derive the range picker trigger's display format from the committed value's shape (yyyy-MM-dd vs yyyy-MM) via a new valueIsDay() helper instead of the granularity flag, so the label stays correct (day vs month) after the popover closes. Task 1: let a live/sliding range end before the current month. getLatestRange(offset, endOffset=0) and calculateTimeRange's sliding-window branch derives endOffset from the saved frame (clamped >= 0) so the window's gap-from-now is preserved as months roll over. Surface it with an 'Exclude current month' checkbox in the picker popover (shifts the whole range back one month, keeping its width), enabled for past ranges only. Task 2: add a maintainer-facing granularities prop on MonthRangePicker (threaded through Header), defaulting to ['month','day']. Pass ['month'] to hide the Day toggle on month-only reports; Budget Analysis is locked to month. AgeOfMoney's granularity stays a distinct concept. Add unit tests for getLatestRange/calculateTimeRange endOffset and the picker util helpers. Use named imports throughout. Assisted-by: ClaudeCode:claude-opus-4.8 Co-authored-by: rznn7 <rznn7@users.noreply.github.com> * [AI] feat(reports): single-calendar range picker, fix granularity reset on remount Replaces the two side-by-side Start/End calendar grids in the reports date-range picker with a single calendar (RangeSelector) supporting click-to-pick range selection with a live hover preview, for both Month and Day granularity. Also fixes the Month/Day toggle silently reverting after a pick: CashFlow, NetWorth, AgeOfMoney, and BalanceForecast unmount their Header/MonthRangePicker subtree on every date change (useReport nulls out data during refetch), which wiped the picker's internal granularity state. Lifted that state into each report so it survives the remount. Assisted-by: ClaudeCode:claude-sonnet-5 * [AI] fix(reports): persist sliding-window endOffset so live ranges actually slide forward calculateTimeRange's sliding-window branch re-derived the gap between a saved end date and 'now' on every call, using the stale, decaying persisted end. Since most reports only rewrite that persisted end when the user explicitly saves the widget, the derived gap grew by however much time had passed since the last save — meaning a live range never actually advanced once its gap was nonzero; it stayed pinned to the last-saved dates indefinitely. Caught by useFormulaExecution.test.ts's 'shifts each formula report query window when the current month changes' test, which was failing on this branch. Adds TimeFrame.endOffset, persisted explicitly alongside start/end/mode instead of re-derived from decaying absolute dates. getLatestRange and calculateTimeRange now return the resolved endOffset as a 4th tuple element; MonthRangePicker computes it at commit time and threads it through Header's onChangeDates into every report that persists a timeFrame (CashFlow, NetWorth, AgeOfMoney, BalanceForecast, Calendar, Sankey, Crossover, Summary, BudgetAnalysis). Legacy saved widgets without endOffset fall back to the old derivation once, as a migration. Assisted-by: ClaudeCode:claude-sonnet-5 * [AI] fix(reports): route day-grid clicks through pick directly DayGrid's per-day onSelect was wired to selectMonth, a handler meant for month-grid clicks and month-navigation day-carry-over. Fed a full day string it built a malformed candidate (e.g. 2026-07-20-10) that never string-matched the clicked day, so the range endpoint lost its selected (primary) styling and only showed as part of the in-range band. Assisted-by: ClaudeCode:claude-sonnet-5 * [AI] fix(reports): keep day-mode range width when reopening picker openPopover unconditionally expanded start/end to the full month via toDayStart/toDayEnd whenever reopening in day mode, even when the range was already day-shaped from a prior precise pick. Only expand when the incoming value is still month-shaped (an actual month-to-day switch). Assisted-by: ClaudeCode:claude-sonnet-5 * [AI] fix(reports): address code review findings on month/day range picker Fix clamp() returning granularity-mismatched bounds when min/max are month-shaped but the picker is in day mode. Add accessible labels to NavRow's prev/next icon buttons. Deduplicate the Pie shape index coercion in DonutGraph into a shared helper. Drop the unnecessary useMemo around MonthRangePicker's label string. Skipped: the Header.tsx allMonths[allMonths.length - 1] access can't crash since every caller already guards on allMonths being non-empty before rendering Header. The RangeEndSelector.tsx dead-code finding refers to a file that no longer exists in this codebase. The DateSelect.tsx firstDayOfWeek suggestion doesn't apply: react-aria-components' public CalendarGrid props don't expose firstDayOfWeek in the installed version, so the locale '-u-fw-' extension is still the only way to configure it. Assisted-by: ClaudeCode:claude-sonnet-5 * [AI] fix(reports): stop widening day-shaped bounds in balance forecast chart data buildBalanceForecastChartData's Daily branch applied firstDayOfMonth/lastDayOfMonth to start/end unconditionally, which expanded an already day-shaped range (e.g. 2024-03-15) out to its whole month. Only expand when the bound is month-shaped (isValidYearMonth), otherwise use it as-is. Assisted-by: ClaudeCode:claude-sonnet-5 * [AI] fix(reports): address CodeRabbit findings on month/day range picker (round 2) Header: fall back minDate to the current month when allMonths is still empty (Summary/Calendar render before their async load populates it). RangeSelector/DayGrid: normalize min/max to day granularity in DayGrid so a month-shaped bound doesn't disable every day in its own month. AgeOfMoney: derive the picker's initial Month/Day mode from the restored range's shape instead of hardcoding month, since that mode isn't persisted separately from timeFrame. BudgetAnalysis: normalize start/end to yyyy-MM inside calculateIsConcise so a legacy day-shaped persisted timeFrame can't corrupt the date-fns parse. DonutGraph: read props.index directly instead of coercing a second shape-callback argument. MonthRangePicker: use && short-circuit JSX per the style guide. AgeOfMoney: drop an unnecessary useCallback with no deps, matching sibling reports. DayGrid/RangeSelector/MonthRangePicker/Header: thread firstDayOfWeekIdx through so the day grid's weekday header and alignment follow the user's configured first day of week instead of hardcoding Sunday-first. Skipped: the Crossover.tsx 'double shift' finding — the extra -1 month shift is pre-existing, documented, intentional business logic (excludes the current month), applied symmetrically on both load and onChangeDates regardless of whether endOffset was legacy-derived or explicitly persisted; the suggested fix wouldn't touch that code path anyway. Assisted-by: ClaudeCode:claude-sonnet-5 * Update category and authors in release notes Address technical debt by introducing a day picker for the calendar. * Update VRT screenshots Auto-generated by VRT workflow PR: #8427 * [AI] Address review feedback on month/day range picker and DateSelect - Crossover: treat legacy widgets' missing endOffset as 0 (no double shift) - Only offer "Exclude current month" in month mode for ranges ending at the current or previous month - Cap past-oriented reports at the newest month; future stays for forecasts - Don't commit on Month/Day switch so the popover survives report reloads - Month-only picker for Calendar, Summary, Sankey and Crossover - Transaction date picker: visible month-nav arrows that keep focus in the input, normal-sized heading, square day cells, tighter popover - Give unselected grid cells a transparent border so selecting doesn't resize the grid - Derive picker granularity from the committed value shape and drop the lifted per-report state and Header props - Simplify per review: reuse ModeButton/monthUtils/Calendar firstDayOfWeek, remove allowFuture, add useLanguage, fix double render on keystroke, strip prose comments - Restore master's yarn.lock (only the pikaday removal remains), drop the shell-quote resolution, revert unrelated DonutGraph change - Release note: describe the feature, add MatissJanis to authors Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Update VRT screenshots Auto-generated by VRT workflow PR: #8427 * [AI] Cap day-mode picking at today and clamp exclude-toggle shifts - A month-shaped max that reaches the current month no longer widens to the month's last day in day mode; day cells after today are disabled and the Month->Day draft widening clamps its end to today - DayGrid and clamp() keep already day-shaped bounds exact instead of widening them back to whole months - toggleExcludeCurrentMonth clamps the shifted range to minDate/maxDate like every other draft mutation - granFor: function declaration per code style Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Use one SpaceBetween for consistent report header spacing The header row was two SpaceBetween groups (gap 15 and gap 3) with no gap between them, and reports wrapped their own controls in ad-hoc gap-10 Views. Merge the mode toggle, date picker, filter button and report controls into a single SpaceBetween, render header children through one too, and drop the per-report wrappers (Balance Forecast, Cash Flow, Budget Analysis, Sankey). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Drop endOffset; sliding windows always anchor to the current month Remove the persisted TimeFrame.endOffset and its wiring through reportRanges, the range picker, Header and all report pages. A live (sliding-window) range now always ends at the current month; "Exclude current month" still shifts the draft range and commits it as static. Also address code review findings: guard the "All time" preset while allMonths is still loading, remount the range selector on granularity switch so a month-shaped click-anchor can't produce a mixed-shape range, only offer the exclude toggle when both of its states fit the configured bounds, and give the month/day grid cells full localized accessible names and a pressed state. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Update VRT screenshots Auto-generated by VRT workflow PR: #8427 * [AI] Remove the "Exclude current month" checkbox Its only unique capability was creating a live range ending last month via endOffset, which is gone; picking the end month in the grid now does the same thing. Deletes the toggle component, the allowExcludeCurrentMonth prop and Header wiring, and the now-unused shiftMonths util. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Simplify month-range picker after cleanup review Drop the util.ts aliases of shared months.ts helpers, derive picker granularity from the draft value's shape instead of separate state, normalize min/max bounds once in MonthRangePicker, inline the Header preset builders, and make month-only the default granularity with day-capable reports opting in explicitly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Rebuild day-mode picking on react-aria RangeCalendar Replace the hand-rolled DayGrid with react-aria's RangeCalendar, which adds keyboard navigation and built-in hover range preview, and use month/year dropdowns (CalendarMonthPicker plus a bounds-based year select) in the calendar header for quick jumping. The month grid stays custom since react-aria has no month-granularity range picker. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Address review feedback and fix picker styling glitches Validate the full yyyy-MM-dd shape in valueIsDay instead of negating the year-month check, cap the year dropdown when the report has no upper date bound (the far-future sentinel would render ~8000 options), use the bare-button hover color so hovered dates and month/year selects stay readable, and stretch the day grid to the calendar's full width. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Simplify month-range picker after cleanup review - YearSelect reads bounds from RangeCalendarStateContext instead of props - Dedupe first-day-of-week pref decoding into getFirstDayOfWeek in DateSelect - Hoist the day-calendar emotion css() call to module scope - RangeSelector tracks viewYear directly instead of a month it only read the year from - Inline the single-consumer Grid component into MonthGrid Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Let live report ranges slide by days for day-shaped windows Day-shaped sliding-window ranges now keep their width in days and re-anchor to today, so Live works with day granularity in reports. Formula queries stay month-granular via asMonthSlidingTimeFrame. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Update VRT screenshots Auto-generated by VRT workflow PR: #8427 * [AI] Move the date range picker into the component library Extract the reports MonthRangePicker into @actual-app/components as DateRangePicker: translated strings arrive via a labels prop, date formatting runs on Intl.DateTimeFormat with a BCP 47 locale string instead of loot-core's date-fns wrappers, and the first-day-of-week pref is resolved by the caller. Header.tsx supplies all of these; the component library gains @internationalized/date and subpath import mappings for the picker's internals. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Add VRT for the reports date range picker Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Add VRT snapshots for the reports date range picker Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Clean up the date range picker after review - Move ModeButton into the component library and reuse it for the granularity toggle instead of a near-identical SegmentButton copy - Move the day-shaped value check into loot-core as isValidYearMonthDay so reportRanges no longer imports it through the picker component - Cache Intl.DateTimeFormat instances in formatDate - Build lastDayOfMonth/currentDay on @internationalized/date - Pass label subsets as one labels object instead of scalar props Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Update VRT screenshots Auto-generated by VRT workflow PR: #8427 * [AI] Revert "Update VRT screenshots" This reverts commit8cd01e5148. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Hoist DateRangePicker to src root, add ModeButton story, respect app date format - Move DateRangePicker.tsx and its stories out of the date-range-picker/ folder so the story sorts alphabetically in the Storybook sidenav; only child components remain in the folder. - Add a Storybook file for ModeButton. - Add an optional formatDayLabel prop so day-shaped trigger labels can follow the app's date format preference; reports Header now passes the dateFormat synced pref instead of falling back to locale-numeric dates. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Address review feedback on the date range picker - Bound scheduled-occurrence counts to the visible chart range so day-mode ranges don't count occurrences outside the selected days - Carry the running balance forward from points before a day-shaped start instead of starting the daily chart at zero - Drop the day granularity from the tracking-budget forecast header, which only resolves monthly - Validate the day against the month and year (incl. leap years) in isValidYearMonthDay - Target the date range picker trigger by test id in the reports e2e test - Drop a redundant useMemo in useLocale (React Compiler memoizes) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Keep the date range picker open when a quick-select preset is clicked Presets now preview their range in the open picker draft, like manual selection, and commit on close so live-range modes are preserved. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Improve date range picker colors and highlight the active preset Style the calendar header dropdown options so they stay readable in dark themes, introduce a datePickerRangeBackground theme variable so the light theme's range band is purple like the other themes, and derive the active quick-select preset from the current range so it stays highlighted when reopening. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Update VRT screenshots Auto-generated by VRT workflow PR: #8427 * Update VRT screenshots Auto-generated by VRT workflow PR: #8427 --------- Co-authored-by: rznn7 <160644362+rznn7@users.noreply.github.com> Co-authored-by: rznn7 <rznn7@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <matiss@mja.lv> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * 🐛 Fix OIDC login callback on ipad (#8505) * fix oidc login callback on ipad * no need to run on electron * add dep to useeffect * fix race * reverting a bit * fix electron GoCardless linking (#8510) * fix electron invalid origin * [AI] Add tests for the desktop GoCardless linking fix Covers the electron fetch wrapper's derived Origin header and the sync-server /gocardless/create-web-token origin and id validation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TKqNWc6Lbigv8e61uLoopU * note * allow old origin and move link so that it exists on electron bundled server * provide a valid redirect URL to GoCardless --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * [AI] Add tooltip with status icon legend to cleared column header (#8470) * [AI] Add tooltip with status icon legend to cleared column header Hovering the ✓ header in the transactions table now shows a tooltip explaining the column, with a legend of each status icon that can appear there (uncleared, cleared, reconciled, and the upcoming/due/ missed scheduled-transaction states). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Broaden cleared/uncleared tooltip wording beyond bank sync Reviewer feedback: cleared status doesn't require bank sync — it can be a manual "I've verified this transaction" mark. Reword the legend to avoid implying the bank performs the confirmation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * [AI] Promote Payee Locations to a stable feature (#8511) * [AI] Promote Payee Locations to a stable feature Remove the experimental payeeLocations feature flag and make payee locations always available on the mobile transaction screen. Move the documentation out of the experimental section into the transactions area. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C3sk6D2TPLFBBaE17VptxJ * [AI] Shorten payee locations release note Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C3sk6D2TPLFBBaE17VptxJ * Update VRT screenshots Auto-generated by VRT workflow PR: #8511 --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * add month picker component to automations UI (#8515) * add month picker component * add automation MonthPicker * wire it up! * note * move month input out to utils * add bank sync provider setup to the mobile Bank Sync page (#8497) * [AI] Add bank sync provider setup to the mobile Bank Sync page The mobile Bank Sync page previously only listed existing accounts, with no way to configure a provider or link a new bank account, and its empty state wrongly claimed an account must be created first (linking creates accounts). It now renders the same provider section as desktop, corrects the empty-state guidance on both platforms, and hides the account filter when there are no accounts. VRT snapshots for the mobile bank sync test were deleted and need regenerating with yarn vrt:docker. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Update VRT screenshots Auto-generated by VRT workflow PR: #8497 * collapsible provider * Update VRT screenshots Auto-generated by VRT workflow PR: #8497 --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * [AI] improve the first-account creation flow (#8496) * [AI] Improve the first-account creation flow Rewrites the empty-account states on desktop and mobile in plain language, with a centered layout and a visible Add account button on mobile. When no sync server is configured, the add-account chooser is skipped in favor of the account creation form directly, which gains a name placeholder, an explanation of off-budget accounts, and a pointer to QIF/OFX/QFX file import. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * coderabbit * add linebreak --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * :electron: Fix logon not working after signout when using oidc (#8526) * cleanup oidc server ref after login * release notes * feedback * fix relinking a bank account (#8525) * fix relinking an account * new files * No duplicate labels and types (#8536) * dont add the bug label * note * fix note * add features * fix fix note * ⬆️ bump playwright (#8534) * @playwright/test (1.59.1 → 1.61.1) * playwright docker updates * note * [AI] CI: Support parallel e2e shards locally with per-shard port (#8456) * [AI] ci: support parallel e2e shards with per-shard port - Add 'Set E2E port' step to functional and vrt jobs (E2E_PORT = 3000 + shard) - Use vars.E2E_SHARD_TOTAL (with fallback '3') for shard denominator in job names and test commands; pass --var E2E_SHARD_TOTAL=1 to run full suite in a single local shard without relaunching - Add .actrc with depot runner image mappings * [AI] fix(ci): address review feedback on e2e shard guard/injection * Skip Enable Banking PSU headers for local IPs (#8032) * Skip Enable Banking PSU headers for local IPs * Add release note for Enable Banking PSU headers * Include IPv4 link-local addresses in PSU header filter * Use trusted proxy resolved IP for Enable Banking PSU headers * Avoid logging Enable Banking PSU header values * Use shared SSRF helper for Enable Banking PSU headers * Fix space * Add shared SSRF helper for Enable Banking PSU headers * Use public IP in Enable Banking PSU header test * [autofix.ci] apply automated fixes * Add tests for shared SSRF helper --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * [AI] Fix updateTransaction crash when converting a transaction to a split (#8467) * [AI] Fix updateTransaction crash when converting a transaction to a split * [AI] Clear is_child/parent_id when promoting a transaction to a split parent * [AI] Type-fix: clear parent_id with undefined, not null (parent_id is an optional string) --------- Co-authored-by: Hossein Moosavi <h.moosavi@kif-devs.com> * [AI] Count filtered-out on-budget transfers in Age of Money (#8503) * [AI] Count filtered-out on-budget transfers in Age of Money When account filters are applied, transfers to/from accounts outside the filtered set should count as expenses/income so filtering to a cash account reflects age of cash rather than budget-wide AoM. Co-authored-by: Cursor <cursoragent@cursor.com> * [AI] Fix Age of Money lint: template expressions and formatting Co-authored-by: Cursor <cursoragent@cursor.com> * [AI] Address CodeRabbit feedback on AoM transfer filters Treat empty notOneOf as a tautology, narrow array values without assertions, and clarify the release note covers both transfer directions. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> * ⬆️ mid month dependency update (#8533) * @types/node (^22.19.21 → ^22.20.1) * eslint (^10.5.0 → ^10.7.0) * eslint-plugin-perfectionist (^5.9.0 → ^5.10.0) * knip (^6.17.1 → ^6.27.0) * lage (^2.15.13 → ^2.15.17) * vitest (^4.1.8 → ^4.1.10) * better-sqlite3 (^12.10.0 → ^12.11.1) * vite (^8.0.16 → ^8.1.5) * react-aria-components (^1.18.0 → ^1.19.0) * @storybook/addon-a11y (^10.4.4 → ^10.5.2) * @storybook/addon-docs (^10.4.4 → ^10.5.2) * @storybook/react-vite (^10.4.4 → ^10.5.2) * @vitejs/plugin-react (^6.0.2 → ^6.0.3) * eslint-plugin-storybook (^10.4.4 → ^10.5.2) * storybook (^10.4.4 → ^10.5.2) * @bufbuild/protobuf (^2.12.0 → ^2.12.1) * @bufbuild/protoc-gen-es (^2.12.0 → ^2.12.1) * @codemirror/language (^6.12.3 → ^6.12.4) * @uiw/react-codemirror (^4.25.10 → ^4.25.11) * csv-stringify (^6.7.0 → ^6.8.1) * es-toolkit (^1.47.1 → ^1.49.0) * i18next (^26.3.1 → ^26.3.6) * lru-cache (^11.5.1 → ^11.5.2) * react-aria (^3.49.0 → ^3.50.0) * react-hotkeys-hook (^5.3.2 → ^5.3.3) * react-i18next (^17.0.8 → ^17.0.10) * rolldown (^1.1.1 → ^1.2.0) * @docusaurus/core (^3.10.1 → ^3.10.2) * @docusaurus/plugin-content-docs (^3.10.1 → ^3.10.2) * @docusaurus/plugin-ideal-image (^3.10.1 → ^3.10.2) * @docusaurus/preset-classic (^3.10.1 → ^3.10.2) * @docusaurus/theme-common (^3.10.1 → ^3.10.2) * @docusaurus/theme-mermaid (^3.10.1 → ^3.10.2) * unist-util-visit-parents (^6.0.0 → ^6.0.2) * @docusaurus/module-type-aliases (^3.10.1 → ^3.10.2) * @docusaurus/types (^3.10.1 → ^3.10.2) * @oxlint/plugins (^1.69.0 → ^1.74.0) * @types/estree (^1.0.8 → ^1.0.9) * fast-check (^4.8.0 → ^4.9.0) * @capacitor/android (^8.0.0 → ^8.4.2) * @capacitor/core (^8.0.0 → ^8.4.2) * @capacitor/ios (^8.0.0 → ^8.4.2) * @capacitor/splash-screen (^8.0.1 → ^8.0.2) * @capgo/capacitor-updater (^8.50.1 → ^8.51.2) * @capacitor/cli (^8.0.0 → ^8.4.2) * @capgo/cli (^8.22.0 → ^8.28.2) * express-rate-limit (^8.5.2 → ^8.6.0) * @types/supertest (^7.2.0 → ^7.2.1) * http-proxy-middleware (^4.1.1 → ^4.2.0) * oxlint (^1.69.0 → ^1.74.0) & oxlint-tsgolint (^0.23.0 → ^0.25.0) * @codemirror/state (^6.6.0 → ^6.7.1) & @codemirror/view (^6.43.1 → ^6.43.6) * @tanstack/react-query (^5.101.0 → ^5.101.2) * recharts (^3.8.1 → ^3.9.2) * note * oxfmt (^0.44.0 → ^0.59.0) * @babel/core (^7.29.7 → ^8.0.1) * downshift (9.3.6 → 9.4.0) * react-router (7.17.0 → 8.2.0) * csv-parse (^6.2.1 → ^7.0.1) * pluggy-sdk (^0.85.3 → ^0.89.0) * yarn (4.13.0 -> 4.17.1) * yarn dedupe * Update VRT screenshots Auto-generated by VRT workflow PR: #8533 * crdt version bump * [autofix.ci] apply automated fixes * Update packageManager version to yarn@4.17.1 --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv> * tweak testing framework for templates/automations (#8528) * drive automations UI * use testing date * add priority label for testing * note * Update VRT screenshots Auto-generated by VRT workflow PR: #8528 --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * fix maintainer points counting script PR categorisation (#8535) * fix count points release note finding * note * coderabbit * [AI] Promote Age of Money report to a stable feature (#8540) * [AI] Promote Age of Money report to a stable feature Remove the experimental ageOfMoneyReport feature flag so the report is always available, matching the recent Payee Locations promotion. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Change release note category to Features Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * Bank Sync Provider per budget file [3/3] - Frontend + loot-core - Pluggy wiring for per budget sync (#8318) * PR 3 for pluggy frontend and loot-core * Update VRT screenshots Auto-generated by VRT workflow PR: #8318 * md * Code Rabbit code review * fix for code review of PR 02 * Code Review * Code review + VRT revert + new message when file owner is not admin * [autofix.ci] apply automated fixes * Update VRT screenshots Auto-generated by VRT workflow PR: #8318 * Update VRT screenshots Auto-generated by VRT workflow PR: #8318 * Removing unwanted permission message for no server or offline * Update VRT screenshots Auto-generated by VRT workflow PR: #8318 --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv> * Bump the npm_and_yarn group across 1 directory with 10 updates (#8541) Bumps the npm_and_yarn group with 10 updates in the / directory: | Package | From | To | | --- | --- | --- | | [axios](https://github.com/axios/axios) | `1.16.1` | `1.18.1` | | [body-parser](https://github.com/expressjs/body-parser) | `1.20.5` | `1.20.6` | | [brace-expansion](https://github.com/juliangruber/brace-expansion) | `1.1.13` | `1.1.16` | | [dompurify](https://github.com/cure53/DOMPurify) | `3.4.11` | `3.4.12` | | [fast-uri](https://github.com/fastify/fast-uri) | `3.1.2` | `3.1.3` | | [immutable](https://github.com/immutable-js/immutable-js) | `5.1.5` | `5.1.9` | | [shell-quote](https://github.com/ljharb/shell-quote) | `1.8.4` | `1.10.0` | | [svgo](https://github.com/svg/svgo) | `3.3.3` | `3.3.4` | | [webpack-dev-server](https://github.com/webpack/webpack-dev-server) | `5.2.5` | `5.2.6` | | [websocket-driver](https://github.com/faye/websocket-driver-node) | `0.7.4` | `0.7.5` | Updates `axios` from 1.16.1 to 1.18.1 - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.16.1...v1.18.1) Updates `body-parser` from 1.20.5 to 1.20.6 - [Release notes](https://github.com/expressjs/body-parser/releases) - [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md) - [Commits](https://github.com/expressjs/body-parser/compare/1.20.5...1.20.6) Updates `brace-expansion` from 1.1.13 to 1.1.16 - [Release notes](https://github.com/juliangruber/brace-expansion/releases) - [Commits](https://github.com/juliangruber/brace-expansion/compare/v1.1.13...v1.1.16) Updates `dompurify` from 3.4.11 to 3.4.12 - [Release notes](https://github.com/cure53/DOMPurify/releases) - [Commits](https://github.com/cure53/DOMPurify/compare/3.4.11...3.4.12) Updates `fast-uri` from 3.1.2 to 3.1.3 - [Release notes](https://github.com/fastify/fast-uri/releases) - [Commits](https://github.com/fastify/fast-uri/compare/v3.1.2...v3.1.3) Updates `immutable` from 5.1.5 to 5.1.9 - [Release notes](https://github.com/immutable-js/immutable-js/releases) - [Changelog](https://github.com/immutable-js/immutable-js/blob/main/CHANGELOG.md) - [Commits](https://github.com/immutable-js/immutable-js/compare/v5.1.5...v5.1.9) Updates `shell-quote` from 1.8.4 to 1.10.0 - [Changelog](https://github.com/ljharb/shell-quote/blob/main/CHANGELOG.md) - [Commits](https://github.com/ljharb/shell-quote/compare/v1.8.4...v1.10.0) Updates `svgo` from 3.3.3 to 3.3.4 - [Release notes](https://github.com/svg/svgo/releases) - [Commits](https://github.com/svg/svgo/compare/v3.3.3...v3.3.4) Updates `webpack-dev-server` from 5.2.5 to 5.2.6 - [Release notes](https://github.com/webpack/webpack-dev-server/releases) - [Changelog](https://github.com/webpack/webpack-dev-server/blob/v5.2.6/CHANGELOG.md) - [Commits](https://github.com/webpack/webpack-dev-server/compare/v5.2.5...v5.2.6) Updates `websocket-driver` from 0.7.4 to 0.7.5 - [Changelog](https://github.com/faye/websocket-driver-node/blob/main/CHANGELOG.md) - [Commits](https://github.com/faye/websocket-driver-node/compare/0.7.4...0.7.5) --- updated-dependencies: - dependency-name: axios dependency-version: 1.18.1 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: body-parser dependency-version: 1.20.6 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: brace-expansion dependency-version: 1.1.16 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: dompurify dependency-version: 3.4.12 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: fast-uri dependency-version: 3.1.3 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: immutable dependency-version: 5.1.9 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: shell-quote dependency-version: 1.10.0 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: svgo dependency-version: 3.3.4 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: webpack-dev-server dependency-version: 5.2.6 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: websocket-driver dependency-version: 0.7.5 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * respect reduced motion settings in the new onboarding screen (#8516) * respect useReducedMotion * note * coderabbit * Change query to typescript (#8545) * Change query to typescript * Change query to typescript * Add release note * Use string keys for unfilter method * fix: add scoped ErrorBoundaries to mobile schedules, payees, and bank sync pages (#8547) * Update build configuration to support mobile app (#8388) * Update build configuration to support mobile app * Add release notes * Fix build * Depcheck * Typo * Deps * Typo * fix * PR feedback * Typo * CodeRabbit * Changing approach * Scope changes * Hot-reload * Remove mobile update and simplify build * Fix android build * Unsigned builds * Revert compat * Pull from workspace, not relative path * Attempt to fix build (run on macos) * [AI] Diagnose Android SDK on mobile runner * [AI] Configure Android SDK on mobile runner * [AI] Match mobile app branding to PWA (#8552) * Bump fast-uri in the npm_and_yarn group across 1 directory (#8554) Bumps the npm_and_yarn group with 1 update in the / directory: [fast-uri](https://github.com/fastify/fast-uri). Updates `fast-uri` from 3.1.3 to 3.1.4 - [Release notes](https://github.com/fastify/fast-uri/releases) - [Commits](https://github.com/fastify/fast-uri/compare/v3.1.3...v3.1.4) --- updated-dependencies: - dependency-name: fast-uri dependency-version: 3.1.4 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * [AI] Fix budget templates with a priority not allocating funds in tracking budgets (#8549) * [AI] Fix budget templates with a priority not allocating funds in tracking budgets Tracking budgets never define a `to-budget` sheet cell (only envelope budgets do), so the priority-tier engine always saw $0 available and clamped every priority>0 template to 0. Seed the pool from `total-saved` instead when in tracking mode, since it's the structural analog (budgeted income minus budgeted expenses) and preserves genuine priority-tier competition rather than disabling the clamp outright. Fixes #8422 * Trigger Build * Trigger Build * Trigger Build * [AI] Fix bank sync account relinking (#8365) * decrease time-to-stale (#8539) * decrease time-to-stale * note * post wording * only consider assigned PRs * update wording * Bump the npm_and_yarn group across 2 directories with 1 update (#8566) Bumps the npm_and_yarn group with 1 update in the / directory: [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router). Bumps the npm_and_yarn group with 1 update in the /packages/desktop-client directory: [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router). Updates `react-router` from 8.2.0 to 8.3.0 - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router@8.3.0/packages/react-router) Updates `react-router` from 8.2.0 to 8.3.0 - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router@8.3.0/packages/react-router) --- updated-dependencies: - dependency-name: react-router dependency-version: 8.3.0 dependency-type: direct:development dependency-group: npm_and_yarn - dependency-name: react-router dependency-version: 8.3.0 dependency-type: direct:development dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * [AI] Fix feature-request workflows to use the Feature issue type instead of the feature label (#8572) * [AI] Fix feature-request automation to use the Feature issue type instead of the feature label GitHub issues migrated feature requests from a 'feature' label to a 'Feature' issue type, which silently broke the auto-close workflow, the help-wanted cleanup workflow, and the feature-implemented handler. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * note --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * better simplefin errors (#8568) * better simplefin errors * note * coderabbit * [AI] Add toggleable Group column to transaction list (#8441) * [AI] Add toggleable Group column to transaction list * [autofix.ci] apply automated fixes * [AI] Add tests and release note for Group column * [AI] Show Group column for child split transactions * Apply suggestions from code review: Rename `group` label to `category group` label Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk> * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk> * [AI] Complete mobile error boundaries (#8573) * Bump tar in the npm_and_yarn group across 1 directory (#8592) Bumps the npm_and_yarn group with 1 update in the / directory: [tar](https://github.com/isaacs/node-tar). Updates `tar` from 7.5.19 to 7.5.22 - [Release notes](https://github.com/isaacs/node-tar/releases) - [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md) - [Commits](https://github.com/isaacs/node-tar/compare/v7.5.19...v7.5.22) --- updated-dependencies: - dependency-name: tar dependency-version: 7.5.22 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update PikaPods deployment cost in README (#8597) * Update PikaPods deployment cost in README Updated the monthly cost for one-click deployment via PikaPods from $1.40 to $2.00. * Add release notes * Allow searching for 'Transfer: ' prefix on payees page (#8602) * [AI] Add a column manager to the transaction table (#8580) * [AI] Add a column manager to the transaction table Adds a modal for showing, hiding, and reordering transaction table columns, saved per view or budget-wide. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Fix running balance e2e test to use the column manager The 'Show running balance' menu item was replaced by the column manager, so this test now toggles the column via the modal instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Address code review feedback for the column manager - Apply-to-all now clears every stored view override, derived from the synced prefs themselves (covers closed/deleted accounts) - The column manager shows the user's underlying cleared-column preference while a reconciliation is in progress - At least one amount column always stays visible so new transactions keep an amount input (enforced at parse time and in the modal) - Column toggles now have an accessible name (Toggle forwards aria-label) - E2e: idempotent setTransactionColumnVisibility page-model helper, shared openTransactionColumnsModal, and a fixed expected header order in the reorder test so a skipped reorder cannot pass Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Assert the exact header cell sequence in the reorder e2e test Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Use a checkbox instead of a toggle for applying columns to all views --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * Update to Node 24 (#8604) * [AI] Add opt-in checkbox to sync transfer date to other side (#8454) * [AI] Add opt-in checkbox to sync transfer date to other side PR #7722 removed forced date sync between transfer sides, breaking some workflows. Re-add it as opt-in: a checkbox under the calendar popup, only shown for transfer transactions, unchecked by default. When checked, committing a date change also updates the linked transaction's date via a direct transactions-batch-update (runTransfers: false), same pattern as onSetTransfer. - DateSelect.tsx: render the checkbox below the calendar, styled to match calendar theme colors; square off the calendar's bottom corners and move rounding/shadow to the checkbox row so the popup reads as one card when the checkbox is present. - TransactionsTable.tsx: track checkbox state per row, reset on each popover open. Read it via a ref (in addition to state) because Pikaday binds its onSelect handler once at mount, so a plain closure would always see the initial (false) value. - LabeledCheckbox.tsx: add onMouseDown passthrough so clicking the checkbox doesn't blur the date input and close the popover before the check registers. * [AI] Inline single-use calendar corner style, rename prop squareBottomCornersStyle was a top-level const used at exactly one call site. Inline it into the css([...]) array instead. Renamed squareBottomCorners to attached: the prop now also zeroes the box-shadow, so the old name no longer matched what it controls. * [AI] Address CodeRabbit: render transfer-sync checkbox on mobile DateSelectMobile silently dropped transferDateSyncChecked and onTransferDateSyncChange, so narrow-width users never saw the checkbox even though TransactionsTable always passes it for transfer transactions. Render the same LabeledCheckbox below the native date input when onTransferDateSyncChange is provided. * [AI] Address CodeRabbit: type guard + error handling for transfer-date send Replace the `as string` assertion with a `typeof value === 'string'` guard. Add a .catch to the fire-and-forget transactions-batch-update call so a failed sync doesn't fail silently. * [AI] Cascade transfer date sync to the split parent of the other side Restores the pre-#7722 behaviour for the opt-in transfer-date-sync checkbox: when the other side of the transfer is itself a split sub-transaction, also update that sub-transaction's parent date so the split stays consistent with its transfer leg. * [AI] Shrink transfer-date-sync checkbox label to fit narrower Calendar picker The react-aria Calendar picker is narrower than the old Pikaday one, so the full label wrapped/overflowed its margin. Shorten the text and swap the word "transfer" for the same direction-aware arrow icon already used on the transaction row (SvgLeftArrow2/SvgRightArrow2 by transaction.amount > 0). * [AI] Extract getFirstDayOfWeek to fix Vite Fast Refresh DateSelect.tsx exported both a component and getFirstDayOfWeek, which broke Fast Refresh (react-refresh flags files that export non-component values alongside a component). Moved the function into its own module so HMR works again. Also registered the new module in package.json's #components/* imports map: the wildcard entry only covers .tsx files, so a plain .ts file needs its own explicit mapping to resolve. * [AI] Use shared TransferDirectionIcon in TransactionListItem Mobile TransactionListItem duplicated the isDeposit-based left/right arrow logic that TransferDirectionIcon already encapsulates. Swapped it in. * [AI] Remove dead transfer-date-sync checkbox from DateSelectMobile DateSelectMobile renders a native <input type="date">, so the LabeledCheckbox for transfer-date-sync was unreachable dead code on mobile. * [AI] Remove stale Pikaday references from comments and code The syncTransferDateRef workaround and its comment dated back to Pikaday's date-click handler being bound once at mount (a stale-closure hazard). The react-aria Calendar receives onChange fresh on every render, and onUpdate here is a plain inline function redefined each render too, so the ref no longer serves a purpose. Dropped it back to plain useState and trimmed the matching Pikaday footnote in DateSelect.tsx's blur handler comment. * [AI] Simplify transfer-date-sync checkbox label Replace the icon-based "Also update other side of <arrow>" label with plain text: "Sync both transfer dates". Drops the now-unused transferIsDeposit prop, TransferDirectionIcon import, and transferIconStyle from DateSelect. * [AI] fix: keep transfer-date sync enabled through full date-edit session * [AI] Make transfer-date sync checkbox sticky per account Persist the transfer-date sync checkbox as a synced pref keyed by account id (sync-transfer-date-<accountId>) instead of local component state, so the choice sticks per account across date-edit sessions instead of resetting every time the date cell is opened. * [AI] Make transfer-date sync pref budget-level, not per-account Change sync-transfer-date-<accountId> to a single flat sync-transfer-date pref so the checkbox state is shared across the whole budget instead of being scoped to one account. * [AI] Fix race in transfer-date sync applying stale dates Concurrent date edits on the same transfer leg could interleave: each edit fires an independent async query+update, and a slower older edit could finish after a newer one, overwriting it with a stale date. Track the latest edit with a sequence ref and drop the batch-update call if a newer edit has started since. * [AI] Narrow aql query result with a type guard, drop unsafe casts Replace the Pick<...>[] blanket cast and parent_id as string assertion with a single typed aqlQuery result and a type-guard filter, so the compiler verifies is_child/parent_id narrowing instead of trusting an assertion. * [AI] Document the transfer date sync option * 🔖 (26.8.0) (#8563) * 🔖 (26.8.0) * Generate release notes for v26.8.0 * Add release notes for version 26.8.0, highlighting new stable features, mobile banking improvements, and day-level date range selection in reports. * Update authorship in release notes for version 26.8.0 to reflect MatissJanis as the author. * [AI] Apply review feedback to 26.8.0 release notes: clarify onboarding/mobile bank sync wording and note per-file credentials are Pluggy.ai only * [AI] Copy review-feedback wording fixes into releases.md for 26.8.0 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <matiss@mja.lv> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * [AI] Add Discord notifications for new tags and published releases (#8611) * [AI] Add Discord notifications for new tags and drafted/published releases Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018a3c6gvegH5cGjpMBfkV3N * [AI] Remove draft release notification step Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018a3c6gvegH5cGjpMBfkV3N * [AI] Update release note to drop drafted release mention Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018a3c6gvegH5cGjpMBfkV3N * [AI] Use nightly-alerts environment for Discord webhook secret Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018a3c6gvegH5cGjpMBfkV3N * Update .github/workflows/discord-release-notifications.yml Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update .github/workflows/discord-release-notifications.yml Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * [AI] Show schedule name in mobile notes in upcoming schedules (#8609) * [AI] Show schedule name in mobile notes when a preview schedule has no notes Mirrors the existing desktop fallback (schedule name shown in place of empty notes for upcoming scheduled transactions) which was missing on the mobile transaction list. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Update VRT screenshots Auto-generated by VRT workflow PR: #8609 --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * [AI] 📖 Clarify transfer docs (#8622) * [AI] Point to the sync-detail section from the transfers doc intro The intro said updating one side of a transfer "always" updates the other, but the Modifying a Transfer section below shows some fields are optional or independent. Link to that breakdown instead. * Update packages/docs/docs/transactions/transfers.md Co-authored-by: Stephen Brown II <Stephen.Brown2@gmail.com> --------- Co-authored-by: Stephen Brown II <Stephen.Brown2@gmail.com> * FIX-8612: add "delete" back to scheduled transactions (#8616) Co-authored-by: youngcw <calebyoung94@gmail.com> * feat: Add ILS currency (#8559) * [AI] Drag-and-drop reordering for upcoming scheduled transactions (#8385) * [AI] Add sort_order column to schedules table * [AI] Add db.moveSchedule for reordering schedules * [AI] Add schedule/move server method * [AI] Sort preview transactions by schedule sort_order * [AI] Allow dragging preview transactions against same-date previews * [AI] Route preview-to-preview drops to schedule/move * [AI] Add release note for schedule preview drag-and-drop * [AI] Guard schedule/move against missing schedule id Skip the schedule/move send in TransactionList's preview drag handler when draggedTrans.schedule is undefined, instead of issuing a backend request with id: undefined. * [AI] Fix missing payee on schedule previews if the schedule has a split (#8624) * [AI] Fix missing payee on schedule previews with a split underneath useDisplayPayee determined a split parent's payee by querying the DB for its children, which works for persisted transactions but not for schedule preview rows, whose split children only ever exist in memory. Merge in-memory is_child rows into the subtransaction lookup so preview splits get the same computed payee real splits already do. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix note --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * 📊 Monte Carlo Analysis report (#8551) * add a monte carlo sim * multiple pots with separate config * withdrawal rules * tabs for config, pension accessible age * improving the runs * docs for monte carlo * small ux niggles * add monte carlo to docs sidebar * update docs wording * inflation no longer static * small ui niggles, common market crashes in a year * minor ui styling tweaks * cleaning up * ui tweaks * update some screenshots * another screenshot * ui tweaks * adjusting the guardrails rule for phased spending and minor fix for ran out of money age * add withdrawn value from runs table and new withdrawal orders * allow linking accounts * new images * clamping extremely high values to prevent any parsing errors * feedback link * ignore locked pots when applying withdrawal rules * ui improvements * accounting for tax and updating screenshots * screenshots without your cursor ya daftie * add tax into simulation * ui improvements * taxable amount added to run * bit of code cleanup * adding more columns to run detail so tax/fees is easier to work out * images for docs * feedback * move docs to experimental * Bump the npm_and_yarn group across 1 directory with 5 updates (#8630) Bumps the npm_and_yarn group with 5 updates in the / directory: | Package | From | To | | --- | --- | --- | | [brace-expansion](https://github.com/juliangruber/brace-expansion) | `1.1.16` | `1.1.18` | | [fast-uri](https://github.com/fastify/fast-uri) | `3.1.4` | `3.1.5` | | [ip-address](https://github.com/beaugunderson/ip-address) | `10.2.0` | `10.4.0` | | [postcss](https://github.com/postcss/postcss) | `8.5.19` | `8.5.25` | | [undici](https://github.com/nodejs/undici) | `7.28.0` | `7.29.0` | Updates `brace-expansion` from 1.1.16 to 1.1.18 - [Release notes](https://github.com/juliangruber/brace-expansion/releases) - [Commits](https://github.com/juliangruber/brace-expansion/compare/v1.1.16...v1.1.18) Updates `fast-uri` from 3.1.4 to 3.1.5 - [Release notes](https://github.com/fastify/fast-uri/releases) - [Commits](https://github.com/fastify/fast-uri/compare/v3.1.4...v3.1.5) Updates `ip-address` from 10.2.0 to 10.4.0 - [Release notes](https://github.com/beaugunderson/ip-address/releases) - [Commits](https://github.com/beaugunderson/ip-address/compare/v10.2.0...v10.4.0) Updates `postcss` from 8.5.19 to 8.5.25 - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.5.19...8.5.25) Updates `undici` from 7.28.0 to 7.29.0 - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](https://github.com/nodejs/undici/compare/v7.28.0...v7.29.0) --- updated-dependencies: - dependency-name: brace-expansion dependency-version: 1.1.18 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: fast-uri dependency-version: 3.1.5 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: ip-address dependency-version: 10.4.0 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: postcss dependency-version: 8.5.25 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: undici dependency-version: 7.29.0 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(crossover-report): render empty state when no accounts exist (#8512) (#8523) * fix(crossover-report): render empty state when no accounts exist When no accounts exist in a fresh budget, two guards in Crossover.tsx prevent the component from ever leaving the loading state: - The init useEffect bails out on accounts.length === 0, never setting selectionsInitialized=true - The render guard returns <LoadingIndicator /> when accounts.length === 0 The spreadsheet already handles empty accounts gracefully (returns empty data with zeros). Remove both guards so the report renders its empty state instead of hanging indefinitely. Fixes #8512 * Add release note for crossover report empty state fix * [AI] Add blog post: sidenav design competition voting is open (#8644) * [AI] Add blog post: sidenav design competition voting is open Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016q58nrYhWogyXH7jvEbXQQ * [AI] Add voting and discussion links to sidenav voting blog post Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016q58nrYhWogyXH7jvEbXQQ * [AI] Remove em dashes from sidenav voting blog post Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016q58nrYhWogyXH7jvEbXQQ * Revise voting announcement and community message Updated language for clarity and added a note about contacting finalists. --------- Co-authored-by: Claude <noreply@anthropic.com> * [AI] Fail agent hooks with an actionable error when jq is missing (#8651) * [AI] Fail agent hooks with an actionable error when jq is missing The shared agent-hook scripts all parse their payload with jq but never checked it exists. A missing jq made git-guard.sh block every Bash call with a misleading 'could not parse the hook payload' message, while other hooks silently failed open and skipped their checks (issue #8629). Add require_jq to common.sh — called by every hook before its first jq use — so a missing jq fails closed with a clear install message, distinct from a malformed-payload error. The script name in the message is derived from $0 rather than passed by each caller. The Cursor adapters emit a static JSON deny (their deny() helper itself needs jq to build JSON) or an advisory where blocking isn't possible, and AGENTS.md now documents the prerequisite. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Address review: advisory wording for PostToolUse hooks, hook tests PostToolUse hooks (format-edited-file, prefer-one-component) run after the edit already happened, so a missing jq now reports 'Agent hook skipped' instead of claiming a block. Exit 2 stays: it is the only exit code that feeds stderr back to the agent. Add scripts/agent-hooks/test-hooks.sh, a standalone runnable check covering the missing-jq path for every hook plus malformed-JSON, missing-field, and guard-behavior cases, and soften the release-note verb. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Remove standalone hook test script Not wired into CI, so it only served as a manual check; the hook behavior was verified by hand and the script isn't worth maintaining. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * [AI] Run React Compiler on js/ts files in addition to jsx/tsx (#8650) The React Compiler include regex only matched .jsx/.tsx, so hooks and components defined in plain .ts/.js files were never compiled. The babel plugin already selects the TypeScript parser for .ts files, and the compiler's code pre-filter keeps non-React files cheap to skip, so widening the include to .[jt]sx? is all that's needed. Claude-Session: https://claude.ai/code/session_013D6rbX4YueKKPZing9DRgm Co-authored-by: Claude <noreply@anthropic.com> * update vrt-update comment to reflect a more realistic time (#8657) * reduce vrt time * note * [AI] Fix Butterfly custom theme mode (#8668) Set the Butterfly custom theme's catalog mode to light instead of dark. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * [AI] Stop scanning every transaction once per rendered register row (#8663) * [AI] Stop scanning every transaction once per rendered register row The context menu refactor (#8106) turned TransactionMenu, which only mounted inside an open Popover, into useTransactionRowContextActions — a hook that runs for every rendered row. That hook calls useSchedules, so every visible row opened live queries. For an ordinary transaction the selection holds no preview ids, so the schedule list came back empty and getHasTransactionsQuery built `filter({ $or: [] })`. compileConditions drops an empty $or entirely, leaving `WHERE 1` — a scan of every transaction in the budget, per row, re-run on every change to the transactions table. That covers adding, clearing, deleting and linking a schedule, plus reconciling, and it scales with both budget size and how many rows are on screen. Three fixes: - Skip the schedules query in useTransactionRowContextActions when the selection contains no previews, so ordinary rows open nothing. - Make getHasTransactionsQuery match nothing rather than everything for zero schedules. advanceSchedulesService passes a possibly-empty list on every sync, so a budget with no schedules hit the same scan. - Unsubscribe the previous status query in useSchedules before replacing it. onData fires on every schedules refresh and each orphaned query stayed subscribed to sync events, so the number of scans per mutation grew over a session. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * [AI] Type the useSchedules test against the real liveQuery signature The test cast the query fixture to `never` and the mock to `Mock`, which turned off the checks it should have been getting. The `as never` was not needed at all, and typing the mock with `vi.mocked` immediately surfaced that `Listener` takes `(data, previousData)` — the casts had hidden the arity mismatch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * remove unneeded comment * poke tests --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> * fix context menu showing on multi-account views (#8662) * fix context menu showing on multi-account views * note * [AI] Add regression test for sidebar account group context menu The All accounts / On budget / Off budget sidebar rows have no account, so 'account && needsTooltip' evaluated to undefined and useContextMenu's 'enabled = true' destructuring default silently enabled the menu. The test right-clicks the group rows and a real account row and asserts the menu only opens for the real account. Also correct the release note category to Bugfix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RNjhpBsEGKx5CFtLBG9kqe --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * fix hanging when switching accounts & reconciling (#8628) * fix infinite loop hang * [AI] add test * note * Update packages/desktop-client/src/queries/pagedQuery.test.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * [AI] Add UYU currency (#8638) Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: youngcw <calebyoung94@gmail.com> Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk> * [AI] Fix payee placeholder for deposit transactions on mobile (#8658) When entering a deposit (inflow) on the mobile transaction entry form, the payee placeholder now reads 'Who paid you?' instead of the payment-style 'Who did you pay?'. Fixes #8605 * Bump the npm_and_yarn group across 2 directories with 1 update (#8659) Bumps the npm_and_yarn group with 1 update in the / directory: [electron](https://github.com/electron/electron). Bumps the npm_and_yarn group with 1 update in the /packages/desktop-electron directory: [electron](https://github.com/electron/electron). Updates `electron` from 41.7.1 to 41.10.3 - [Release notes](https://github.com/electron/electron/releases) - [Commits](https://github.com/electron/electron/compare/v41.7.1...v41.10.3) Updates `electron` from 41.7.1 to 41.10.3 - [Release notes](https://github.com/electron/electron/releases) - [Commits](https://github.com/electron/electron/compare/v41.7.1...v41.10.3) --- updated-dependencies: - dependency-name: electron dependency-version: 41.10.3 dependency-type: direct:development dependency-group: npm_and_yarn - dependency-name: electron dependency-version: 41.10.3 dependency-type: direct:development dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk> * [AI] Fix transaction row context-menu listener rebinding on every render (#8606) * [AI] Fix transaction row context-menu listener rebinding on every render useContextMenu passed a fresh inline callback to useRefEventListener on every render, and the effect depended on that unstable callback, so the native contextmenu listener was torn down and re-added on every render of every visible transaction row. Since delete, reconcile-lock, and schedule-link all trigger a full re-render of the transaction list, this added unnecessary DOM listener churn to those actions. useRefEventListener now keeps the latest callback in a ref and only depends on [ref, event], so it binds once and stays current instead of rebinding on every render. Fixes all four call sites, not just the context menu. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * note --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk> * 🔖 (26.8.1) (#8671) * 🔖 (26.8.1) * generate release notes * add header * add account context note * change date --------- Co-authored-by: matt-fidd <81489167+matt-fidd@users.noreply.github.com> Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk> * upsize docker build runners (#8677) * upsize docker build runners * note * Bump mermaid in the npm_and_yarn group across 1 directory (#8679) Bumps the npm_and_yarn group with 1 update in the / directory: [mermaid](https://github.com/mermaid-js/mermaid). Updates `mermaid` from 11.15.0 to 11.16.1 - [Release notes](https://github.com/mermaid-js/mermaid/releases) - [Commits](https://github.com/mermaid-js/mermaid/compare/mermaid@11.15.0...mermaid@11.16.1) --- updated-dependencies: - dependency-name: mermaid dependency-version: 11.16.1 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * [AI] Omit empty WHERE clauses from AQL instead of emitting WHERE 1 (#8678) * [AI] Omit empty WHERE clauses from AQL instead of emitting WHERE 1 Empty or null top-level filters (including $or: []) no longer force a tautology placeholder, and executors append conditions safely when WHERE is absent. * Add AQL release notes for omitting empty WHERE clauses Document the change that omits empty WHERE clauses from AQL, preventing the emission of a tautology placeholder. This update enhances query efficiency and clarity in the upcoming release notes. * [Enhancements] Add quarter date range presets (#8607) * [Enhancements] Add quarter date range presets * Revert unnecessary changes * Add back changes * do not need .yarn/release file * [AI] Update date range picker VRT snapshots for quarter presets The Current quarter/Previous quarter presets added to the date range picker grew the popover height, invalidating the committed VRT screenshots. * Add max height * VRT regenerated * [AI] Remove agentic stop-hook from all platforms (#8684) * [AI] Remove agentic stop-hook from all platforms Drop the Stop hook wiring from Claude (.claude/settings.json) and Codex (.codex/config.toml), and delete the now-unreferenced check-on-stop.sh. Cursor never had a stop hook. common.sh stays — the remaining hook scripts still use it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FoeRXh1A85w8RhAFdzJdZd * [AI] Add release note Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FoeRXh1A85w8RhAFdzJdZd --------- Co-authored-by: Claude <noreply@anthropic.com> * [AI] Make BALANCE_OF available in formula reports (#8591) * [AI] Make BALANCE_OF available in query/report formulas * [AI] Address CodeRabbit review comments on BALANCE_OF PR * 🛣️ tour: add initial tour framework (#8656) * add react-joyride * open budget files on the budget page * add anchors for the tour * add tour and tour components * add tour links * [AI] add tour testing * note * Update upcoming-release-notes/add-guided-tour.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update VRT screenshots Auto-generated by VRT workflow PR: #8656 * make tour budget type aware * make tour respect budget type * coderabbit: avoid translating the app name * change to function definition * swap month at a time wording --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * Return an error when changing password with no password method (#8603) * Return an error when changing password with no password method changePassword ran an UPDATE against method = 'password' and returned success even when it matched zero rows. On an instance configured with only openid auth, the reset-password script therefore reported success without saving a password. * Add release note * 📊 Monte carlo contributions (#8641) * monte carlo contributions * update doc * remove secondary sentence in desc and update image * feedback * privacy filter on monte carlo * [AI] docs(api): drop the orphaned Payee Rule type (#8698) getPayeeRules returns ordinary Rule objects: packages/loot-core/src/server/payees/app.ts returns rules.getRulesForPayee(id).map(rule => rule.serialize()), and Rule.serialize() emits exactly { id, stage, conditionsOp, conditions, actions }. There is no payee_id on the returned objects, and no API method accepts or returns a payeeRule, so the type was documentation-only. The reference already annotates getPayeeRules as Promise<Rule[]>. Remove the Payee rule entry from the Rules APIList too, since its anchor pointed at the deleted heading, and explain the payee association under getPayeeRules instead: it is carried by a condition or action on the payee field rather than by a column. Closes #8697 * [AI] Fix custom reports for empty budgets (#8521) * [AI] Fix custom reports for empty budgets * [AI] fix(reports): pass dates directly to month utilities --------- Co-authored-by: youngcw <calebyoung94@gmail.com> * [AI] Fix scheduled split transfer previews (#8699) * [AI] Fix scheduled split transfer previews * [AI] Preserve split preview status * [AI] Fix category_group rule conditions never matching live transactions (#8587) * [AI] Fix category_group rule conditions never matching live transactions Rule/filter conditions using category_group were silently broken for live rule application (transaction import, manual entry, bank sync): prepareTransactionForRules never populated the field Condition.eval() checked against, so the condition always evaluated false. The query/report path (conditionsToAQL) was unaffected, since it maps the field to a real join instead of reading this property. - prepareTransactionForRules now sets category_group to the transaction's category's group id - finalizeTransactionForRules strips the synthetic field before the transaction is persisted - Add two tests covering the live-matching case and a true negative * [AI] Refresh category_group after category-changing rule actions CodeRabbit review feedback on #8587: category_group was computed once in prepareTransactionForRules, before any rules ran. If an earlier rule's action changed or cleared `category`, a later rule's category_group condition would incorrectly read a stale group id. - Add refreshCategoryGroupIfChanged, called after each rule application in runRules; only re-resolves the group when category actually changed, to avoid an extra lookup per rule - Add a regression test chaining two rules across pre/post stages to prove the later rule now sees the updated category - Assert category_group doesn't leak onto the returned transaction in the existing live-matching tests (nitpick from the same review) * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> * [AI] feat(transactions): auto-widen amount columns to fit content (#8645) Payment, Deposit, and Balance columns in the account register now compute their width from the widest formatted value across the account's transactions, instead of a fixed 100/103px guess kept in sync by hand across six call sites. Fixes clipped amounts for zero-decimal currencies (JPY, CLP) and high-magnitude or high-precision values (KWD, BTC). Unblocks the historical decimal-precision migration (formerly gated on a separate out-of-chain "resizable columns" dependency). * [AI] Add loan functions to formulas (#8705) * [AI] Add loan/investment financial functions to formula autocomplete HyperFormula already executes IPMT, PPMT, NPER, CUMIPMT, CUMPRINC, MIRR, XNPV, and FVSCHEDULE natively, but they had no autocomplete, parameter hints, or docs. Document them alongside the existing PMT/FV/PV/NPV/IRR/RATE entries, and fix a pre-existing doc gap for FIXED. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * [AI] Make loan payment functions available in rules * [AI] Document that numeric rule formula results are in dollars Rule formula inputs and outputs use different units: the amount and balance variables are integer cents, but executeFormulaSync runs a numeric result through amountToInteger, so the formula's output is read as dollars. The Result types section never said so, which makes it easy to write a rule that sets amounts 100x off. Call the asymmetry out in Result types, and note on the loan example that its principal is written in dollars. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * [AI] Merge the rule formula units tip and caution into one admonition The standalone "Tip: if you want dollars from amount" line covered half of the same ground as the new units caution, from a different section. Fold both into a single "Cents in, dollars out" admonition at the end of Available variables, and point Result types and the loan example at it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * [AI] Request persistent storage so browsers do not evict local budget data (#8667) * [AI] Request persistent storage so browsers do not evict local budget data Without persistent storage, the browser may silently delete the origin's data (including the local budget database) under storage pressure, forcing a full re-download of the budget and losing any changes not yet synced. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Handle rejection of the persistent storage request Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * Improve monte carlo contributions tab on mobile (#8709) * improve monte carlo contributions tab on mobile * release note * [AI] Fix stuck update button and useless [object Object] fatal error details (#8718) - FatalError: synthesize the wrapper Error message from JSON.stringify of plain payload objects (app-init-failure messages) so the Show Error details carry the real cause instead of "[object Object]" (#8704). - applyAppUpdate: force a reload after 15s if the new service worker never takes control, so the "Update now" button can't hang silently. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * [AI] Fix stale session token recovery (#8648) * Mkd currency support (#8716) * Update Currency.tsx to include MKD * Update currencies.ts to include MKD * Create add-mkd-currency.mk release note * Update currencies.ts to represent MKD with 2 decimal places to conform with the ISO standard There is no longer any legal tender in North Macedonia of a denomination of less than one Denar (the 50 Deni coin was the last one, and it was phased out in 2013), but I was unaware that neither North Macedonian law nor the ISO standard was ever updated. Adding the two decimal places is thus technically correct, and should be reflected in Actual. * Rename add-mkd-currency.mk to add-mkd-currency.md --------- Co-authored-by: youngcw <calebyoung94@gmail.com> * [AI] Fix flickering text on the formula dashboard card (#8676) `useFormulaExecution` keys its effect on the identity of the `queries` object. `Formula.tsx` honours that by passing a stable ref plus a `queriesVersion` counter, but `FormulaCard` passed `meta?.queries || {}`, which allocates a fresh object every render. React Compiler would normally memoize that away, but it skips `FormulaCard` entirely: babel-plugin-react-compiler@1.0.0 bails out of any component using a default value in its destructured props (`meta = {}`). So the effect re-ran on every render, and since each run toggles `isLoading`, it rendered again -- an endless loop that swapped the card's value for the loading skeleton over and over. - Depend on the serialized contents of `queries`/`namedExpressions` instead of their identity, reading the live objects through refs. This fixes every caller and still re-executes on real query edits, which matters because `meta.queriesVersion` is declared on the widget type but never actually persisted. - Clear `isLoading` on the invalid-formula early return, which previously left it stuck on. - Drop the destructured default from `FormulaCard` so React Compiler compiles it again, and use a module-level constant for the empty queries object. - Only show the skeleton when there is no value yet, so a refresh leaves the previous value on screen rather than blanking the card. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> * [AI] Add impeccable design skill and project design context (#8653) * [AI] Add impeccable design skill wiring and project design context Commit only the shareable parts of the impeccable frontend-design skill setup: defensive hook wiring (.codex, .cursor, .github hooks that no-op when the skill is not installed locally) and the generated design context files (PRODUCT.md, DESIGN.md, DESIGN.json) derived from the existing component-library tokens and themes. The skill payload itself is gitignored; developers install it locally. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Recommend installing the impeccable skill to agents at boot AGENTS.md documents the install step, a Claude Code SessionStart hook surfaces it when the skill is missing from the checkout, and the Codex and Copilot hook wiring echoes the same hint instead of silently no-oping. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Mention DESIGN.json among committed design context files Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Keep DESIGN.json out of the repo as a generated artifact It is a regenerable sidecar of DESIGN.md consumed only by the locally installed impeccable live panel, so gitignore it like the skill payload. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Clarify that DESIGN.md hex values are the light-theme mapping Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * [AI] Add blog post announcing sidenav design competition winner (#8724) * [AI] Add blog post announcing sidenav design competition winner Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EKXivBzxyCeySkqbsqCC9Z * [AI] Remove dashes from sidenav winner blog post Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EKXivBzxyCeySkqbsqCC9Z * [AI] Use absolute docs links in blog post previews to fix paginated tag pages Relative ../../docs/ links resolve against the page URL, so when a post's truncated preview renders on a paginated listing page (e.g. /blog/tags/announcement/page/2) the link breaks and fails the Docusaurus build. Absolute /docs/ paths resolve correctly everywhere. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EKXivBzxyCeySkqbsqCC9Z --------- Co-authored-by: Claude <noreply@anthropic.com> * fix monte carlo explaination of fees and taxes (#8732) * GoCardless: derive Alior payee from the card transaction description (#8738) * GoCardless: derive Alior payee from the card transaction description Alior Bank stopped returning creditorName on outgoing transactions in early July 2026. Without it, formatPayeeName falls through to debtorName, which on an outgoing transaction is the account holder, so every card payment is booked against the user instead of the merchant. The merchant is still available behind a fixed prefix in remittanceInformationUnstructured, so take it from there when creditorName is missing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * rename release note to the PR number Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> * [AI] Fix category autocomplete keyboard selection (#8570) * [AI] Fix category autocomplete selection indices * test: update category autocomplete VRT snapshot * test: update dark category autocomplete VRT snapshot * test: update midnight category autocomplete VRT snapshot --------- Co-authored-by: youngcw <calebyoung94@gmail.com> * [AI] feat(budget analysis report): allow selecting future months in date range (#8722) * [AI] feat(budget-report): allow selecting future months in date range Extend the allMonths picker list 12 months past the current month so users can select future months for long-term goal planning. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] fix(budget-report): preserve running balance for future months with no budget data Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] fix(budget-report): fix balance zeroing for future months with no budget data Use monthBalance as the next month's running balance instead of carryoverToNextMonth, which is 0 for months where no category has any budget or spending data. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * [AI] fix(budget-report): fix import path in budget-analysis-spreadsheet test * [AI] fix: added in release note Assisted-by: ClaudeCode:claude-opus-5 * [AI] fix(budget-report): address CodeRabbit review findings Addresses both review comments from CodeRabbit on PR #8722: 1. "Keep later-dated transaction months selectable" — the month-range endpoint now takes the later of the latest transaction month and December of next year, so existing future-dated transactions are no longer hidden from the picker. This PR had replaced latestMonth with a hardcoded December-of-next-year, which dropped transactions dated beyond it. 2. "Preserve the no-carryover reset when propagating balances" — running-balance propagation goes back to carrying over only what actually carries over, instead of the full month balance. Using monthBalance had re-included negative balances for categories with carryover disabled, so an overspend both dragged forward through later months and still surfaced as an overspending adjustment. A month with no budget data at all now passes the running balance through untouched, which fixes the original future-month bug without defeating the carryover reset. Extracts summarizeMonthCategories, getNextRunningBalance and getLastSelectableMonth so this logic is unit-testable, and replaces the self-referential range tests with ones that exercise the real functions. Assisted-by: ClaudeCode:claude-opus-5 * [AI] test(budget-report): cover never-budgeted months in data detection CodeRabbit's follow-up review on PR #8722 suggested setting hasBudgetData from cell presence rather than cell value. That change would be wrong here: envelope-budget-month builds its response by iterating the category list, so it emits a cell for every category in every month and normalizes missing sheet values to 0 (see loot-core/src/server/budget/app.ts). Cells are always present, so presence-based detection would report every month as populated and re-introduce the running-balance bug the previous commit fixed. The zero-value case it worries about is not reachable in a way that changes the result: leftover already folds in the previous month's balance, so a month with a real balance carrying in always has a non-zero cell and is detected correctly. When all three are zero there is nothing to carry, and both detection strategies agree. Adds a regression test pinning the never-budgeted month to hasBudgetData=false, and documents why detection is value-based so the suggestion is not re-applied later. Assisted-by: ClaudeCode:claude-opus-5 --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * add cleared to rule conditions (#8740) * add cleared to rule conditions * test * add to docs * note * Fix amount resetting to zero (#6056) (#8733) * fix amount resetting to zero (#6056) * Add release notes * Update upcoming-release-notes/fix-amount-reset-when-clicking-cleared.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Add tests suggested by CodeRabbit * Remove null assertion in tests (Code review change) --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * [AI] Make summary card formula fit narrow screens (#8583) * [AI] Make summary card formula fit narrow screens Scale the sigma, parentheses spacing, filter text and result down on narrow widths, stack the fraction above the result instead of using the equals signs, and shrink the fraction bar margins so the percentage formula no longer overflows on mobile. * [AI] Reword summary card release note * [AI] Stop summary range labels from wrapping onto the sigma The `to`/`from` labels are absolutely positioned with `right: -30` inside a wrapper whose content box collapses to 6px on narrow screens, giving them a 36px shrink-to-fit budget. `Aug 26` at 11px needs ~37px, so it wrapped onto a second line that landed on the sigma glyph. `whiteSpace: nowrap` is applied unconditionally — desktop had the same latent margin (50px budget, ~46px of text). Also pulls the repeated conditionals in the touched style objects into named values: the two fraction numbers now share one style object, and the paired margin/width ternaries and the grid track list are each stated once. * [AI] Block AI agents from creating GitHub issues (#8711) * [AI] Prohibit agents from creating GitHub issues via agent hooks * [AI] Simplify issue-creation guard after review --------- Co-authored-by: Claude <noreply@anthropic.com> * Update pikapods.md PWA intro sentence for clarity (#8690) * Update pikapods.md PWA intro sentence for clarity added a sentence to define PWA to make section easier to understand to people who are unfamiliar * Update pikapods.md * Revise PWA installation instructions for clarity Updated the description of using a PWA for clarity and conciseness. --------- Co-authored-by: Michael Clark <5285928+MikesGlitch@users.noreply.github.com> * Skip overspent sources when a named cleanup pool collects funds (#8749) The end of month cleanup did not check whether a category set to send its leftover funds to a named pool actually had a positive balance. An overspent category was still processed, so its negative balance was budgeted back onto itself and subtracted from the amount available to the pool, pushing the overspending onto the other categories in the group. Global sources already guard against this and warn instead. Apply the same check to named pool sources so an overspent category is skipped and reported. Closes #8739 Co-authored-by: Sanidhya Awasthi <sanidhya.awasthi@bfhl.in> Co-authored-by: youngcw <calebyoung94@gmail.com> * [AI] Fix phantom overspending after deleting a category (#8756) * [AI] Fix phantom overspending after deleting a category Deleting a category with a negative budgeted amount left a permanent "Overspent in [Month]" that could never be cleared, because the category is no longer visible in the UI. `handleCategoryChange` added the category's `leftover`/`carryover` cells to `last-month-overspent` when a category was created or un-deleted, but never removed them when it was tombstoned. The deleted category keeps its budgeted amount while its spend is transferred away, so its `leftover` goes negative and keeps counting as overspending. `addDeps` and `removeDeps` now both derive their work from a single `getDeps` list so they cannot drift apart again. This also covers `buffered-auto`, which had the same add-only asymmetry for income categories. Closes #8754 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * clear comment --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> * Replace "convert to schedule" prompt with inline button (#8588) * feat: centralize schedule settings * feat: undo settings-based approach * feat: new approach with inline button * fix: make it work on mobile too * feat: add shift+enter as keyboard shortcut * feat: code review comments * Update VRT screenshots Auto-generated by VRT workflow PR: #8588 * feat: address all coderabbit comments --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * [AI] Pin bundle size baselines to the base commit (#8753) * Pin bundle comparisons to the base commit * Organize bundle run resolver files * Pin resolver checkout to the trusted base * Resolve bundle baselines from the merge base * Pair bundle baselines with the PR base * Strengthen bundle workflow regression coverage * Delete packages/ci-actions/src/build-runs/size-compare-workflow.test.ts * [AI] Fix duplicate payees in rules after merging payees (#6486) (#8719) * [AI] fix: deduplicate mapped rule ids (#6486) * [AI] docs: add release note for #6486 * chore: retrigger CI (flaky sharp/libvips build on setup job) --------- Co-authored-by: SomSamantray <> * [AI] Default new transaction amount filters to positive values (#8726) * [AI] Default new amount filters to positive values Initialize new transaction amount filters with a positive zero sign so values entered without toggling the sign match the displayed greater-than and less-than operators. Preserve the sign of existing filters and cover both paths with component tests. * [AI] Handle the amount filter directly instead of adding a GenericInput prop Takes @tim-smart's suggested shape. `FiltersMenu` now renders `AmountInput` itself for the amount field, and `GenericInput` is left byte-identical to master — the `zeroSign` prop it grew in the previous commit is gone. Three reasons this is the better shape, beyond it being tidier: - The prop was not actually general. `FIELD_INFO` has exactly one `type: 'number'` entry, `amount`, so `field === 'amount'` and `type === 'number'` select the same thing. A prop on a shared component serving one field and one call site is the same specificity, moved somewhere harder to find. - It type-checks. The `GenericInput` call site carries four `@ts-expect-error`s, and the previous commit's `value={type === 'number' ? 0 : ...}` sat under one of them, so nothing verified the value shape matched the `number` variant. The new branch has no suppressions. - `zeroSign` had been declared at the top level of `GenericInputProps`, outside the discriminated union, so it was accepted on `id`, `date`, `boolean` and `string` inputs where it does nothing. Dropping the `<View style={{ flex: 1, ...style }}>` wrapper costs nothing: `AmountInput`'s own container already sets `flex: 1` and spreads `...style` after its defaults, so `marginTop: 10` still lands. Using `value` rather than `formattedValue` is equivalent here too — `formattedValue` only diverges for `field === 'date' && subfield === 'month'`. `subfieldToOptions(field, subfield)` is hoisted to a `fieldOptions` const rather than called twice inside the new branch; it was already being called three times per render. The regression test moves from `GenericInput.test.tsx` up to `FiltersMenu`, which is an improvement rather than a concession: #8725 reports "pick greater than, type 150, and it filters as greater than -150". The new test drives the editor and asserts on what it submits, so it reproduces that; the old one only proved a prop was plumbed through. Verified it fails without the fix, with `value: -15000` against the expected `15000`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * [AI] Simplify amount filter test comments --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Tim Smart <hello@timsmart.co> * [AI] Fix bulk edit date picker formatting regression (#8765) * [AI] Fix bulk edit date picker formatting regression The react-aria calendar that replaced Pikaday rendered a compact grid pinned to the left of the bulk edit modal, with the header arrows spanning the full width and six-row months clipped by the fixed modal height. Stretch the grid to the container in embedded mode (same technique as DayRangeCalendar) and let the date modal size to its content, restoring the pre-v26.8.0 layout. Fixes #8654. * [AI] Give embedded date picker containers definite widths The embedded calendar's 100%-wide grid blows up shrink-to-fit containers to their max width: on wide screens the bulk edit modal stretched to 90vw. Pin the date modal to tokens.breakpoint_small and the formula timeframe picker popover to its existing 260px clamp so the stretched grid fills a bounded container, matching the v26.7.0 look at any screen size. Extend the e2e test with a wide-viewport assertion on the modal width. * [AI] fix(api): translate default rule stage at API boundary (#8689) Co-authored-by: cakeni <211545599+cakeni@users.noreply.github.com> * [AI] Use noble Playwright image for the desktop VRT job to compile against Electron 43 headers (#8770) The /update-vrt workflow always runs from the default branch, so this container fix must land there for the desktop shard to build native modules against Electron 43's V8 headers, which gcc 11/12 (jammy) cannot parse. Matches the change to the functional-desktop-app job in e2e-test.yml on the Electron 43 upgrade branch (#8769). Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * feat: add Palenight to the custom theme catalog (#8771) * feat: add Palenight to the custom theme catalog * docs: release note for the Palenight theme * Don't send a starting balance of 0 when only the starting date is set (#8750) Linking a bank account defaulted the custom starting balance to 0. Editing only the starting date kept that default and sent it to the server, which treats any defined balance as the real opening balance instead of deriving one from the bank, so the account opened out of sync. Leave the balance undefined until the user enters one. The amount input still shows 0, and a balance the user deliberately sets to 0 is still sent. Closes #8105 Co-authored-by: Sanidhya Awasthi <sanidhya.awasthi@bfhl.in> * [AI] Migrate health-check script to TypeScript (#8599) * [AI] Migrate health-check script to TypeScript * [AI] Include TypeScript scripts in knip entries * [AI] Validate health status response type * Change title util to TypeScript (#8553) * Migrate title util to TypeScript * Add release notes * Type the options parameter to preserve custom specials * Move title options type inline * [AI] Fix bank sync showing the wrong bank for a second connection (#8625) Bank records were looked up by the provider identifier alone. SimpleFIN reports the institution's domain as that identifier, so two separate logins at the same bank (e.g. two connections for different family members) collapsed into a single bank record, and accounts linked through the second connection were listed under the first connection's name on the Bank Sync page. Match on the institution name as well, so connections that report different institution names get their own bank record while relinking the same connection still reuses the existing one. Also handle institutions reported as a plain string, which previously bound `undefined` as a SQL parameter and threw. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> * [AI] Add Peruvian Sol (PEN) to the currency list (#8773) Co-authored-by: Claude Opus 5 <noreply@anthropic.com> * ⬆️ Upgrade electron to 43 (#8769) * upgrade electron to 43 * [AI] Use noble Playwright image for desktop jobs to compile against Electron 43 headers Electron 43's V8 headers expand to 'class [[deprecated(...)]] __attribute__((visibility("default")))' when node-gyp defines USING_V8_SHARED and V8_DEPRECATION_WARNINGS. gcc 11 and 12 (jammy) cannot parse a GNU attribute after a standard attribute in a class-head; gcc 13 (noble) can. Verified by compiling the headers with g++-11/12 (fail) and g++-13/14 (pass) in Ubuntu 22.04/24.04 containers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * glboal setup for playwright due to new download behaviour * Update VRT screenshots Auto-generated by VRT workflow PR: #8769 * upgrading runner * add exit codes back * build for arm64 --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * sidebar pt. 1: add account grouping models and handlers (#8764) * add account_group schema and field * add loot-core handlers * add handlers to the API * cleanup type docs * note * coderabbit feedback * add _id suffix * coderabbit: use await in batchMessages * [AI] Long note values will show a full tooltip on hover in the transactions table (#8759) * [AI] Long note values will show a full tooltip on hover in the transactions table * Fix typo in tx-notes-tooltip.md * [AI] Rename tags from the Tags page (#8752) * [AI] Rename tags from the Tags page Adds a Rename action to the tag row context menu. Renaming updates the tag record and rewrites the hashtag in every transaction note that uses it, so users no longer have to find and replace it by hand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * [AI] Address PR feedback and simplify tag rename Insert the new tag name literally in notes (replacement tokens like $& were interpreted before), drop a non-null assertion in the ManageTags test, and remove dead code from the tag InputCell. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Close the tag rename input when Enter is pressed The 'tag' field is not registered with the table navigator, so the navigator's Enter handler fell through to flashInput, which reopened the rename input 100ms after it closed. Handle Enter on the input itself: commit the rename and exit the editing state. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Keep tag rename tests focused * [AI] Reject deleted tag rename collisions * [AI] Reference the navigator type for renderItem onEdit The renderItem prop receives navigator.onEdit verbatim, so point its type at TableNavigator['onEdit'] instead of duplicating the signature. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AI] Offer tag rename from the selection menu Rename appears in the selection menu when exactly one tag is selected, reusing the inline rename editor the row context menu already opens. The table navigator moves up to ManageTags so the toolbar can start the edit. Choosing Rename clears the selection first: the closing menu restores focus to its trigger, which would otherwise leave focus outside the input with the delete/hide hotkeys still live. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Apply suggestion from @tim-smart * [AI] Register the tag field with the table navigator Tab and Shift+Tab in the rename input navigated wrongly while 'tag' was absent from the navigator's field list: Tab moved backwards to 'select' and Shift+Tab jumped to the previous row's description. Registering the field makes both move within the row as expected. The local Enter handler stays. Without it the navigator carries editing on to the next row's tag field, which after the list re-sorts is an unrelated tag; the submit test now asserts no editor is left open. Also constrain the rename menu item with `satisfies MenuItemObject` in place of `as const`, which additionally validates the action name. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * [AI] Drop tag rename from the context menu for a multi-selection Delete and Hide act on every id in the selection, while Rename edits the one row it was opened on. Offer it only when those are the same tag. Right-clicking a row outside the selection still offers Rename, since the menu then acts on that row alone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Tim Smart <tim.smart@effectful.co> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> * [AI] Add payeeNameNormalization option to import transactions API (#8710) * [AI] Add rawPayeeName option to import transactions API Title casing lowercases the whole string before re-capitalizing each word, so acronyms in bank strings don't survive it: "New York NY" becomes "New York Ny". Custom importers that already supply payee names in final form had no way to opt out. * [AI] Rename import option to payeeNameNormalization A boolean named after the implementation detail is a poor public API. Replace `rawPayeeName?: boolean` with `payeeNameNormalization?: 'original' | 'title-case'`, defaulting to 'title-case' so existing behavior is unchanged. * [AI] Cover imported_payee in all transactions-import tests * Do not allow payeeNameNormalization to be null * [AI] Convert reconcileTransactions trailing flags to an options object `reconcileTransactions` had grown to nine positional parameters with mixed true/false defaults, so `payeeNameNormalization` would have landed as a bare string in ninth position. Replace the trailing flags on `reconcileTransactions` and `matchTransactions` with a typed options object, and rename the internal `rawPayeeName` boolean to `payeeNameNormalization` so the API option keeps its name all the way down to `normalizeTransactions`. * reduce repetition to improve maintainability --------- Co-authored-by: Tim Smart <hello@timsmart.co> * [AI] docs(migration): explain nYNAB credit card payment overbudgeting in To Budget (#8784) [autofix.ci] apply automated fixes * [AI] Fix mobile report drill-downs landing on the unfiltered accounts screen (#8774) Issue: on mobile, tapping a category in a report (or a tag on the tags screen) navigated to /accounts with filter conditions in the location state, but the mobile accounts screen never read them — the user just saw the plain account list. The same navigation worked on desktop because the wide account view applies location.state.filterConditions (resizing the browser to full width revealed the filtered list). Mobile budget drill-downs are unaffected; they use their own /categories/:id route. Fix: the mobile accounts page now redirects such navigations to /accounts/all, and the all-accounts transaction list translates the conditions via make-filters-from-conditions and applies them to its query, mirroring the desktop applyFilters flow in Account.tsx. A "Filters applied" chip next to the balance shows the list is filtered, scheduled-transaction previews are excluded while filtered, and searching within the filtered list keeps the filters. Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: youngcw <calebyoung94@gmail.com> * 🔖 (26.9.0) --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: youngcw <calebyoung94@gmail.com> Co-authored-by: Kennedy242 <80504103+Kennedy242@users.noreply.github.com> Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk> Co-authored-by: Michael Clark <5285928+MikesGlitch@users.noreply.github.com> Co-authored-by: Wing Chang <96088121+PencilKnot@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: lelemm <lelemm@gmail.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: lelemm <lelemm@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Mats Nilsson <matni403@gmail.com> Co-authored-by: Juulz <julesmcn@gmail.com> Co-authored-by: Sebastián Maluk <samaluk@miuandes.cl> Co-authored-by: Joseph Coffa <josephco440@gmail.com> Co-authored-by: narutamaaurum <narutamaaurum@gmail.com> Co-authored-by: Henghui Zhu <2193638829@qq.com> Co-authored-by: Kyle Slattery <k@kslat.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: Manaf <manaf.aboalrous@gmail.com> Co-authored-by: David Bettis <david.bettis@gmail.com> Co-authored-by: dzickusrh <dzickus@redhat.com> Co-authored-by: Gemini <noreply@google.com> Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com> Co-authored-by: Kevin <36412364+KevinIsMyName@users.noreply.github.com> Co-authored-by: Alec Bakholdin <43560338+alecbakholdin@users.noreply.github.com> Co-authored-by: Stephen Brown II <Stephen.Brown2@gmail.com> Co-authored-by: yoshiboi3 <josed.hc96@gmail.com> Co-authored-by: Awais Saeed <awaissaeed530@gmail.com> Co-authored-by: Awais Saeed <dev@awaissaeed.com> Co-authored-by: Trevin Chow <trevin@trevinchow.com> Co-authored-by: Matt Farrell <10377148+MattFaz@users.noreply.github.com> Co-authored-by: kikiminyes <66829008+kikiminyes@users.noreply.github.com> Co-authored-by: Pavel Voronov <31508908+pavel1337@users.noreply.github.com> Co-authored-by: mheiland <15824364+mheiland@users.noreply.github.com> Co-authored-by: ItsThatDude <81509659+ItsThatDude@users.noreply.github.com> Co-authored-by: Julian Dominguez-Schatz <julian.dominguezschatz@gmail.com> Co-authored-by: Tifan Dwi Avianto <33323177+tifandotme@users.noreply.github.com> Co-authored-by: Andy Fiddaman <andy@omnios.org> Co-authored-by: Tom Mills <accounts+github@tomjmills.co.uk> Co-authored-by: thromer <tromer@gmail.com> Co-authored-by: JinHyuk Sung <163989462+sjh9714@users.noreply.github.com> Co-authored-by: Oskar <github@neuhuber.eu> Co-authored-by: Zannis Kalampoukis <zannis@users.noreply.github.com> Co-authored-by: Dustin Brewer <mannkind@thenullpointer.net> Co-authored-by: tabedzki <35670232+tabedzki@users.noreply.github.com> Co-authored-by: clintharris <336401+clintharris@users.noreply.github.com> Co-authored-by: Darwin Do <8429648+dsmaugy@users.noreply.github.com> Co-authored-by: Carlisle Nightingale <33028362+Carlisle96@users.noreply.github.com> Co-authored-by: Julien Vincent <m@julienvincent.io> Co-authored-by: edwei06 <80562201+edwei06@users.noreply.github.com> Co-authored-by: Frithjof Engel <75177010+FridoE@users.noreply.github.com> Co-authored-by: Martin Lukas <martinlukasgit@protonmail.com> Co-authored-by: Archit Goyal <go4archit@gmail.com> Co-authored-by: Shivam S <152616194+BIGSUS24@users.noreply.github.com> Co-authored-by: deathblade666 <halo2ofdeath@gmail.com> Co-authored-by: Lucas <lucasb2727@gmail.com> Co-authored-by: Ehtasham Yasin <ehtasham.yasin.dev@gmail.com> Co-authored-by: tianrking <10758833+tianrking@users.noreply.github.com> Co-authored-by: tianrking <tianrking@users.noreply.github.com> Co-authored-by: distantvapor <94718132+distantvapor@users.noreply.github.com> Co-authored-by: Abdullah Khawaja <57410389+DarthAmk97@users.noreply.github.com> Co-authored-by: rznn7 <160644362+rznn7@users.noreply.github.com> Co-authored-by: rznn7 <rznn7@users.noreply.github.com> Co-authored-by: Tim Newsome <tim@casualhacker.net> Co-authored-by: Nustinua <77891103+Nustinua@users.noreply.github.com> Co-authored-by: Atur Dana <aturzone@gmail.com> Co-authored-by: Hossein Moosavi <h.moosavi@kif-devs.com> Co-authored-by: Saahil Jaffer <38600046+saahiljaffer@users.noreply.github.com> Co-authored-by: Shahid <shahid.sarker@gmail.com> Co-authored-by: BasilTh <116033419+BasilTh@users.noreply.github.com> Co-authored-by: mooons <10822203+mooons@users.noreply.github.com> Co-authored-by: Ngoc Do <ngochust56@gmail.com> Co-authored-by: Russlan Ramdowar <rrusslanjr@gmail.com> Co-authored-by: Mustafa Akcanca <MAkcanca@users.noreply.github.com> Co-authored-by: Binny Zupnick <github@binny.zupnick.com> Co-authored-by: Moshe Brevda <mbrevda@gmail.com> Co-authored-by: Gaurav Yadav <gaurav3.141592@gmail.com> Co-authored-by: DiegoC <dacc360@gmail.com> Co-authored-by: Kesley David <39314443+KesleyDavid@users.noreply.github.com> Co-authored-by: matt-fidd <81489167+matt-fidd@users.noreply.github.com> Co-authored-by: Benoît Viguier <ildyria@users.noreply.github.com> Co-authored-by: Gautam Singh <gautamsingh2189@gmail.com> Co-authored-by: Andrea <a.gigante@gmail.com> Co-authored-by: mestredosmagos314 <134573006+mestredosmagos314@users.noreply.github.com> Co-authored-by: JerryNee <37407632+JerryNee@users.noreply.github.com> Co-authored-by: ReticentEclectic <ReticentEclectic@icloud.com> Co-authored-by: Reza Namazi <rezanmz@ymail.com> Co-authored-by: Thelosopher <contact@thelosophy.net> Co-authored-by: Marcin Radoszewski <moriturius@gmail.com> Co-authored-by: Harsh Mathur <58532371+harshmathurx@users.noreply.github.com> Co-authored-by: parapsychic <63157522+parapsychic@users.noreply.github.com> Co-authored-by: divinevalley <divinevalley@gmail.com> Co-authored-by: Sanidhya Awasthi <141642661+Islinger19@users.noreply.github.com> Co-authored-by: Sanidhya Awasthi <sanidhya.awasthi@bfhl.in> Co-authored-by: Caleb Burton <ccburton4@gmail.com> Co-authored-by: Som Samantray <92726151+SomSamantray@users.noreply.github.com> Co-authored-by: cakeni <2150015994@qq.com> Co-authored-by: cakeni <211545599+cakeni@users.noreply.github.com> Co-authored-by: Patrick Danilevici <patrick@danilevici.ro> Co-authored-by: Anju <168076172+anjupathak03@users.noreply.github.com> Co-authored-by: Junyuan Zheng <53073448+junyuanz1@users.noreply.github.com> Co-authored-by: davidlitmanowicz <51731704+davidlitmanowicz@users.noreply.github.com> Co-authored-by: DavidGrinberg <David@egrinberg.com> Co-authored-by: Haim Gelfenbeyn <haim@g8n.me> Co-authored-by: Joel Brenstrum <joel.brenstrum@gmail.com>