* [AI] Add AI Usage Policy for contributors
Add a contributor-facing AI Usage Policy page modeled on ESLint's version,
covering disclosure, human-only interaction with maintainers, and author
responsibility. Wire it into the docs sidebar, link it from the contributing
index and the root CONTRIBUTING.md.
https://claude.ai/code/session_012RspFcLedoUjbEYknJYPiL
* [AI] Unwrap AI policy paragraphs, renumber release note
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
---------
Co-authored-by: Claude <[email protected]>
* [AI] Run setup once per workflow and fan out via needs
Add a prep `setup` job at the top of `check.yml` and `build.yml`, and
make every other job in those workflows declare `needs: setup`.
The composite action in `.github/actions/setup` caches `node_modules`
keyed on `yarn.lock`. When that hash changes (dep-bump PRs, master after
a merge), the cache is cold and every fan-out job races to run
`yarn --immutable` in parallel — one wins the cache save, the rest do
redundant work. Serialising through a single `setup` job warms the
cache once so downstream jobs restore instantly and skip yarn install
via the existing `if: steps.cache.outputs.cache-hit != 'true'` guard.
No changes to the composite action or cache keys. `e2e-test.yml` is
intentionally left alone.
* [AI] Harden setup jobs and add release note
Address zizmor code-scanning findings on the new `setup` jobs added in
the previous commit:
- Scope `permissions: contents: read` so the job no longer inherits
workflow-default write permissions.
- Pass `persist-credentials: false` to `actions/checkout` so the GitHub
token isn't left on disk for later steps that don't need it.
Add `upcoming-release-notes/7551.md` to satisfy the release-notes PR
check.
* [AI] Disable credential persistence on build.yml checkouts
Each of `api`, `crdt`, `web`, `cli`, `server` in build.yml does
`actions/checkout` (which writes the GitHub token into `.git/config`)
and then uploads build artifacts in the same job. Zizmor flags this as
"credential persistence through GitHub Actions artifacts" because a
misconfigured upload path could capture `.git/config` and leak the
token.
None of these jobs push or write to git, so drop the credential
persistence via `persist-credentials: false` on the checkout.
* [AI] Disable credential persistence on check.yml checkouts
None of the jobs in check.yml (`constraints`, `lint`, `typecheck`,
`validate-cli`, `test`, `migrations`) push or write to git, so pass
`persist-credentials: false` to their `actions/checkout` calls to
resolve the zizmor "credential persistence" finding. Mirrors the fix
just applied to build.yml.
---------
Co-authored-by: Claude <[email protected]>
* [AI] Disable bundle minification for readable production error messages
The desktop-client had dead terserOptions (no `minify: 'terser'` was set, so
Vite's default esbuild minifier ran with name mangling). The loot-core and
plugins-service workers used Terser with mangle:false but still compressed.
Set `minify: false` across all three browser build configs so production
stack traces are human-readable.
https://claude.ai/code/session_01VEywxebiNYAgJia35fygQx
* [AI] Rename release note to match PR number
https://claude.ai/code/session_01VEywxebiNYAgJia35fygQx
---------
Co-authored-by: Claude <[email protected]>
* [AI] Disable fail-fast for Electron build matrices
Prevents cancellation of in-progress platform builds when one fails, so
Windows/macOS/Linux results are all visible on a single run.
* Add release notes for PR #7547
---------
Co-authored-by: Claude <[email protected]>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* [AI] Persist custom CSS overrides as a standalone global pref
Moves the custom CSS override out of the InstalledTheme JSON blob into
a dedicated customCssOverride global pref so that overrides survive
switching themes, clearing installed themes, or toggling auto/light/dark
mode. Includes a one-time migration that lifts the legacy overrideCss
field out of installedCustomLightTheme / installedCustomDarkTheme JSON.
- Add customCssOverride global pref (loot-core types + server defaults)
- Inject the override as a trailing style layer in CustomThemeStyle so
it layers on top of any installed custom theme
- Drop overrideCss from the InstalledTheme type; extractLegacyOverride
+ migrateLegacyOverride handle the one-time lift with whitespace trim
- Run the migration from CustomThemeStyle with an idempotent effect that
re-runs safely once prefs hydrate
- Bind the ThemeInstaller textarea directly to the new pref
- Add a "Custom CSS is active" indicator button next to the theme
selector that opens the installer for editing the override without
flipping auto mode to light
- Pre-switch out of auto when the user picks "Custom theme" from the
main selector, so the flag that used to distinguish entry points goes
away and handleInstall collapses to a pure slot dispatch
- Tests: hermetic Themes settings tests, expanded customThemes unit
tests covering extraction/migration/trim edge cases, updated
ThemeInstaller tests for the new pref binding
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* [AI] Address review feedback for custom CSS override installer
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* [AI] Defer auto-mode theme switch and guard stale installer callbacks
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
* [AI] sync-server: use workspace reference for @actual-app/crdt
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* Update build script in sync-server package to use TypeScript's build mode
* Package electron
* [AI] crdt: add conditional exports so Node can load the built bundle
Before this change the root exports entry pointed at `./src/index.ts`, so
any pure-Node consumer (notably the sync server that Electron forks as a
utility process) failed to import `@actual-app/crdt` — Node can't execute
TypeScript source directly. Sync-server had been masking this by pulling
`@actual-app/crdt@npm:2.1.0` where `publishConfig.exports` resolves to
`./dist/index.js`; once sync-server switched to `workspace:*`, the
Functional Desktop App CI job timed out waiting for the sync server to
boot.
Switch to conditional exports in the same shape `@actual-app/api` already
uses:
- `types` → `./dist/index.d.ts` for TypeScript tooling
- `development` → `./src/index.ts` for Vite/Vitest (HMR, fast feedback)
- `default` → `./dist/index.js` for Node runtime
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
* Add 'Last 30 days' date range option to custom reports
Add a rolling 30-day date range to the Live mode date filter in custom
reports. The option appears between 'Last month' and 'Last 3 months'
and is available for Daily, Weekly, and Monthly intervals.
Co-authored-by: Copilot <[email protected]>
* [autofix.ci] apply automated fixes
* Fix release note filename and author to match PR #7217
Co-authored-by: Copilot <[email protected]>
---------
Co-authored-by: Copilot <[email protected]>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: youngcw <[email protected]>
* [AI] Emit bundle stats from the crdt package
The crdt package was the only published library without a stats.json
artifact. Migrate its build to Vite (mirroring the api/cli setup), wire
in rollup-plugin-visualizer to emit dist/stats.json, and upload it from
the CRDT CI job. Declarations are still produced by tsgo via
--emitDeclarationOnly.
https://claude.ai/code/session_01CDVAGLGu49q5YMHsRLkYLQ
* Add release notes for PR #7537
* [AI] crdt: drop redundant rm -rf dist from build script
Vite's build.emptyOutDir: true already clears the output directory
before writing, so the leading rm -rf dist is unnecessary.
https://claude.ai/code/session_01CDVAGLGu49q5YMHsRLkYLQ
* [AI] Include crdt in the size-compare bundle stats table
Wait for the crdt build check on both the base branch and the PR,
download the crdt-build-stats artifact for each, and pass it to
bundle-stats-comment.mjs so the summary table rendered on the PR
includes a row for the crdt package alongside desktop-client,
loot-core, api, and cli.
https://claude.ai/code/session_01CDVAGLGu49q5YMHsRLkYLQ
---------
Co-authored-by: Claude <[email protected]>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* Apply suggested fix to packages/desktop-client/src/components/modals/EditFieldModal.tsx from Copilot Autofix
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* Apply suggested fix to packages/desktop-client/src/components/modals/EditFieldModal.tsx from Copilot Autofix
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* note
* fix typo in note
* remove useless conditions
---------
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* [AI] Fix regex checkbox label contrast in notes modal (#7514)
Apply theme.menuAutoCompleteText color to the "Use Regular Expressions"
checkbox label in the find-and-replace tab so it contrasts with the
menuAutoCompleteBackground modal background.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix: rename release note file to match PR number
The CI expects the release note file to be named after the PR number
(7515), not the issue number (7514).
---------
Co-authored-by: liuren.lcy <[email protected]>
Co-authored-by: Claude Opus 4.6 <[email protected]>
* [AI] Refactor IS_GENERIC_BROWSER env var to --mode=browser
Replace the IS_GENERIC_BROWSER environment variable with Vite's built-in
--mode=browser flag to distinguish browser builds from Electron builds.
This aligns with the existing --mode=desktop pattern used for Electron
production builds.
Also fix build-shims.js to derive NODE_ENV from import.meta.env.DEV
instead of import.meta.env.MODE, so custom modes don't leak into
process.env.NODE_ENV.
https://claude.ai/code/session_014HvkpR59Ke4eUoiUzsUruv
* Add release notes for PR #7466
* [AI] Fix COOP/COEP headers not set with --mode=browser
The server.headers config was gated on mode === 'development', which
excluded --mode=browser. Since server.headers only applies during
vite serve (not builds), always set the COOP/COEP headers. These are
required for SharedArrayBuffer support used by the SQLite backend.
https://claude.ai/code/session_014HvkpR59Ke4eUoiUzsUruv
---------
Co-authored-by: Claude <[email protected]>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* [AI] Add scoped ErrorBoundary to Rules page to contain rendering crashes
* [autofix.ci] apply automated fixes
* docs: add release notes for ErrorBoundary PR
* fix(rules): add resetKeys to ErrorBoundary for route navigation reset
In react-error-boundary v6, the boundary does not auto-reset when the
user navigates away and back. Adding resetKeys={[location.pathname]}
ensures the error state clears on route changes.
This contribution was developed with AI assistance (Claude Code).
* fix(rules): show error message in fallback UI and log to console
Addresses reviewer feedback on #7437: surface error.message in the
FeatureErrorFallback so users can copy-paste when reporting issues,
and log the error to the console via useEffect so the error isn't
swallowed by the boundary.
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* [AI] Add publishConfig.imports sync validator with pre-commit integration
Add a TypeScript script that validates publishConfig.imports stays in sync
with imports in all packages/*/package.json files. Runs automatically in
the pre-commit hook via lint-staged with --fix mode.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* [AI] Add release notes for #7469
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* [AI] Guard main() with require.main and respect lint-staged file args
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* [AI] Handle non-string imports targets in derivePublishImports
- Add type guard to check for non-string values in imports
- Throw descriptive error when conditional imports are encountered
- Update type signature to accept Record<string, string | object>
- Add test case for non-string imports error handling
This prevents TypeError when packages have conditional imports (e.g., #browser-preload in desktop-client)
Co-authored-by: Matiss Janis Aboltins <[email protected]>
* [AI] Limit resolvePackageJsonPaths scope to packages directory
- Add packagesRoot constant to restrict path resolution
- Update while loop condition to only traverse within packages directory
- Add additional check to ensure candidate paths are under packages/
- Prevents resolution to repo root package.json for missing/deleted files
This ensures the validator only processes package.json files under packages/* and avoids accidentally targeting the monorepo root manifest.
Co-authored-by: Matiss Janis Aboltins <[email protected]>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Matiss Janis Aboltins <[email protected]>
* [AI] Fix type errors for API consumers by shipping .d.ts declarations from loot-core
Downstream consumers of @actual-app/api with strict: true get type errors
because @actual-app/core exports raw .ts source files. Consumers' tsc
follows the import chain into core's source (compiled with strict: false),
and skipLibCheck doesn't help since it only skips .d.ts files.
Add "types" conditions to all imports/exports entries in loot-core's
package.json, pointing to the pre-built declarations in lib-dist/decl/.
Add .npmignore to include lib-dist/decl/ in the published package.
Fixes#7410
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* [AI] Use prepack/postpack scripts instead of inflating package.json
Replace the inline "types" conditions in imports/exports with a prepack
script that adds them at pack/publish time. This keeps the checked-in
package.json clean while still shipping .d.ts declarations to npm
consumers.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* [AI] Convert prepack/postpack scripts to TypeScript
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* [AI] Add release notes for #7468
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* [AI] Fix recursive ExportValue type and remove redundant comment
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* [AI] Rename scripts to .mts and inline types conditions
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* [AI] Make backup/restore scripts safer
- Check if backup exists before creating it in prepack
- Make restore idempotent by checking if backup exists in postpack
- Prevents overwriting existing backups from interrupted runs
- Addresses CodeRabbit review feedback
Co-authored-by: Matiss Janis Aboltins <[email protected]>
* [AI] Type api-handlers.ts fields to drop implicit any
The `fields` / export-args slots in the ApiHandlers contract were
untyped, surfacing as TS7008 errors in strict consumers. Replace them
with the `Partial<APIXxxEntity>` shapes the `@actual-app/api` wrappers
already pass, and annotate the matching call sites in `api.ts` with
`@ts-expect-error` where the legacy helpers still declare full-entity
parameters despite accepting partial updates at runtime.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* [AI] Replace vite-plugin-dts with tsgo for api types
Drops vite-plugin-dts in favor of running tsgo --emitDeclarationOnly
after the vite bundle, eliminating a heavy dev dependency tree
(api-extractor, volar, vue language-core) from the api package build.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* [AI] Add build script to loot-core to emit declarations via lage
`yarn build:cli` failed in CI with TS6305 because api's
`tsgo --emitDeclarationOnly` depends on loot-core's pre-built
`lib-dist/decl/*.d.ts`, but loot-core had no `build` script, so lage's
`^build` cascade silently skipped it. Add `"build": "tsgo -b"` so loot-core
slots into the dependency chain; its tsconfig already has
`emitDeclarationOnly: true`, so the output is declarations only.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* Simplify API build
* [AI] Document TypeScript moduleResolution requirement for @actual-app/api
The published declarations rely on package.json exports conditions, which
classic node / node10 resolvers don't honor. Document the supported modes
(bundler / nodenext / node16) in the package README and in the Getting
Started section of the API docs.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* [AI] Type-guard default value in add-types-conditions prepack
`value.default` is typed `ExportValue | undefined`, which allows nested
conditional objects. The previous truthy check fell through to
`shouldSkip(defaultValue)` and would crash on `.endsWith()` if that shape
ever appeared. Replace with a `typeof === 'string'` narrowing and drop a
now-redundant "Insert types as the first key" comment.
No runtime change on current package.json — no nested `default` values
exist today — but the script is not covered by loot-core's tsconfig
include, so the latent type issue was silent.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* [AI] Allow "nodenext" in docs spellcheck expect list
Referenced in the new TypeScript moduleResolution note in the API docs.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* [AI] Move loot-core declarations to @types and whitelist publish with files
Relocate loot-core's composite TypeScript output from lib-dist/decl to the
top-level @types directory, matching the api package's convention. Replace
the old .npmignore blacklist with an explicit package.json files whitelist.
- tsconfig.json: outDir @types, exclude test/mock dirs from decl emission
- scripts/add-types-conditions.mts: rewrite paths to ./@types/src/...
- package.json: files whitelist shipping only src, @types, migrations,
typings, default-db.sqlite; drop legacy typesVersions (docs now require
moduleResolution bundler/nodenext/node16, so the classic-resolution
fallback is unused)
- .gitignore: ignore the new @types build artifact
- lage.config.js: factor outputGlob into a shared BUILD_OUTPUT_GLOBS
constant and add @types/** so lage caches loot-core's decl output
- root tsconfig.json: tighten exclude from packages/api/@types to
packages/*/@types to cover both api and loot-core
- delete .npmignore entirely
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
* [AI] Build loot-core declarations inside prepack
yarn workspace @actual-app/core pack is the first non-setup step in the
publish workflow, running before any build. Without a build chained into
prepack the @types/ tree is empty at pack time, so the tarball shipped a
transformed package.json pointing at ./@types/src/... paths that didn't
exist. npm publish doesn't re-run hooks on a pre-packed tarball, so the
frozen snapshot must be self-contained; prepack now runs yarn build first
to populate @types/ before add-types-conditions rewrites the exports.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Matiss Janis Aboltins <[email protected]>
* Update customThemeCatalog.json
* Update customThemeCatalog.json
* [autofix.ci] apply automated fixes
* Update customThemeCatalog.json
* 🎨 You Need A Theme Dark, based on 2026 nYNAB dark
* YNA Theme Dark
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>