Add steps to the mobile-release workflow that checkout the gh-pages
branch and update its README.md with direct download links to the
latest Flutter mobile clients (Android APK, debug APK, iOS unsigned
build). Uses HTML comment markers for idempotent updates on subsequent
releases.
https://claude.ai/code/session_01GuUjjmMzxvdSwfvhrjvJr1
Co-authored-by: Claude <noreply@anthropic.com>
* fix: Include investment_contribution in transfer? check and protect transfer entries from sync
Transfer transactions with kind "investment_contribution" were not recognized
as transfers by the UI, causing missing +/- indicators, "Transfer" labels,
and showing regular transaction forms instead of transfer details.
Also adds user_modified: true to entries created via TransferMatchesController
and SetAsTransferOrPayment rule action to protect them from provider sync
overwrites, matching the existing behavior in Transfer::Creator.
https://claude.ai/code/session_019BZ5Z1aqKSK3cRdR81P5Jg
* fix: Centralize transfer/budget kind constants for consistent investment_contribution handling
Define TRANSFER_KINDS and BUDGET_EXCLUDED_KINDS on Transaction to eliminate
hard-coded kind lists scattered across filters, rules, and analytics code.
investment_contribution is now consistently treated as a transfer in search
filters, rule conditions, and UI display (via TRANSFER_KINDS), while budget
analytics correctly continue treating it as an expense (via BUDGET_EXCLUDED_KINDS).
https://claude.ai/code/session_019BZ5Z1aqKSK3cRdR81P5Jg
* fix: Update tests for consistent investment_contribution as transfer kind
- search_test: loan_payment is now in TRANSFER_KINDS, so uncategorized
filter correctly excludes it (same as funds_movement/cc_payment)
- condition_test: investment_contribution is now a transfer kind, so it
matches the transfer filter rather than expense filter
https://claude.ai/code/session_019BZ5Z1aqKSK3cRdR81P5Jg
* fix: Eliminate SQL injection warnings in Transaction::Search
Replace string-interpolated SQL with parameterized queries:
- totals: use sanitize_sql_array with ? placeholders
- apply_category_filter: pass TRANSFER_KINDS as bind parameter
- apply_type_filter: use where(kind:)/where.not(kind:) and
parameterized IN (?) for compound OR conditions
- Remove unused transfer_kinds_sql helper
https://claude.ai/code/session_019BZ5Z1aqKSK3cRdR81P5Jg
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Replace text-tertiary with text-subdued in views
Replace usages of the text-tertiary utility with text-subdued across several view partials to standardize subdued text styling because text-tertiary does not exist in the design system (reports, doorkeeper auth, simplefin items). Also adjust the net worth empty-liabilities markup to use a grid layout for consistent spacing, and update the related controller test selector to match the new CSS class.
* Standardize empty net worth message markup
Replace inconsistent markup and classes for empty asset/liability sections in the net worth partial. Swap text-secondary/p-2/text-center for text-subdued with unified padding (py-3 px-4 lg:px-6), and simplify the liabilities block from a grid/div to a single paragraph for consistent styling and spacing.
* Fix separators in breakdown table view
Correct conditional logic for rendering column separators (rulers) in the reports breakdown table. The top-level check now compares idx to groups.size instead of group.size, and the subcategory check compares idx to group[:subcategories].size. This ensures separators are shown between categories and subcategories correctly, avoiding missing or extra rulers.
* Fix subcategory index variable name in partial
Rename the inner loop index from `idx` to `sub_idx` in app/views/reports/_breakdown_table.html.erb to avoid shadowing the outer `idx`. This ensures the conditional that renders the separator (`shared/ruler`) uses the correct index for subcategories, preventing incorrect rendering of separators between subcategory rows.
* Fix conditional block order in breakdown table
Reorder ERB tags to properly nest the subcategory conditional and the ruler render in the reports breakdown partial. This ensures the divider is only rendered between subcategories and prevents mismatched ERB/end tags that could break template rendering.
* fix: move safe-area padding from body/HTML to navbars. Add script to compute app height dynamically.
* fix: Initialize sankey tooltip with top-0 to avoid overflow
* fix: add fallback to HTML height
* fix: properly set bottom spacing and use position fixed for bottom navbar
* fix: move viewport controller initialization
* fix: locale-dependent category duplication bug
* fix: use family locale for investment contributions category to prevent duplicates and handle legacy data
* Remove v* tag trigger from flutter-build to fix double-runs
publish.yml already calls flutter-build via workflow_call on v* tags,
so the direct push trigger was causing duplicate workflow runs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Refactor mobile release asset flow
* fix: category uniqueness and workflow issues
* fix: fix test issue
* fix: solve test issue
* fix: resolve legacy problem
* fix: solve lint test issue
* fix: revert unrelated changes
---------
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: prevent value overflow in Assets vs Liabilities card
Fixes issue where large asset/liability values overflow the container
when AI side panel is open and reduces horizontal space.
Changes:
- Added flex-wrap to allow values to wrap to next line if needed
- Added break-all to both asset and liability values for long numbers
- Added shrink-0 to minus sign to prevent it from shrinking
Fixes#976
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* refactor: use break-words instead of break-all per code review
Changed from break-all to break-words for currency values to prevent
awkward mid-number breaks (e.g., $1,234,5 / 67.89). break-words only
breaks when content overflows and keeps values intact when possible,
providing cleaner line breaks while still preventing overflow.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
* Show disabled import options before accounts exist
Keep account-dependent import choices visible on /imports/new and render them as disabled with guidance when no accounts are available.
* Refactor disabled import options: extract partial, fix accessibility (#986)
- Extract _import_option partial to eliminate duplicated enabled/disabled
markup across TransactionImport, TradeImport, and MintImport (also
used by AccountImport, CategoryImport, RuleImport for consistency)
- Replace misleading chevron-right with lock icon in disabled state
- Add aria-disabled="true" for screen reader accessibility
- Remove redundant default: parameter from t() call
- Fix locale key ordering (requires_account after import_* keys)
- Fix extra blank line in test file
- Add assertion for aria-disabled attribute in test
https://claude.ai/code/session_016j9tDYEBfWX9Dzd99rAYjX
Co-authored-by: Claude <noreply@anthropic.com>
* Tailwind fixes
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Wire ui layout and AI flags into mobile auth
Include ui_layout and ai_enabled in mobile login/signup/SSO payloads,
add an authenticated endpoint to enable AI from Flutter, and gate
mobile navigation based on intro layout and AI consent flow.
* Linter
* Ensure write scope on enable_ai
* Make sure AI is available before enabling it
* Test improvements
* PR comment
* Fix review issues: test assertion bug, missing coverage, and Dart defaults (#985)
- Fix login test to use ai_enabled? (method) instead of ai_enabled (column)
to match what mobile_user_payload actually serializes
- Add test for enable_ai when ai_available? returns false (403 path)
- Default aiEnabled to false when user is null in AuthProvider to avoid
showing AI as available before authentication completes
- Remove extra blank lines in auth_provider.dart and auth_service.dart
https://claude.ai/code/session_01LEYYmtsDBoqizyihFtkye4
Co-authored-by: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix: Handle empty compound conditions on rules index
* fix: avoid contradictory rule condition summary on /rules
* refactor: move rules condition display logic from view to model
* fix: localize rule title fallback and preload conditions in rules index
Add rswag request specs for merchants index/show and define a MerchantDetail schema used by the docs. Update the generated OpenAPI document with merchants paths and schema.
* Add family moniker selection and dynamic UI labels
Introduce a Family moniker persisted in the database with allowed values Family/Group, add required onboarding selection for it, and thread moniker-aware copy through key user-facing views and locales. Also add helper methods and tests for onboarding form presence and family moniker behavior.
* Small copy edits/change moniker question order
* Conditional Group/Family onboarding flow fixes
* Fix label
* Grouping of fields
* Profile Info page Group/Family changes
* Only admins can change Group/Family moniker
* Repetitive defaults
* Moniker in Account model
* Moniker in User model
* Auth fix
* Sure product is also a moniker
---------
Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
Hovering over category links in the outflow donut chart triggered
Turbo 8's default link prefetching, which made a real request to the
transactions controller. The controller's store_params! before_action
saved those filter params (category + date range) to the session.
When the user later navigated to /transactions via the nav menu,
the stored params were restored, showing an unexpected filtered view.
Adding data-turbo-prefetch="false" prevents the prefetch on hover
while preserving the intended click-to-navigate behavior.
https://claude.ai/code/session_01Na7AF1wyidPwFdPq5w8oaw
* Add SearchFamilyImportedFiles assistant function with vector store support
Implement per-Family document search using OpenAI vector stores, allowing
the AI assistant to search through uploaded financial documents (tax returns,
statements, contracts, etc.). The architecture is modular with a provider-
agnostic VectorStoreConcept interface so other RAG backends can be added.
Key components:
- Assistant::Function::SearchFamilyImportedFiles - tool callable from any LLM
- Provider::VectorStoreConcept - abstract vector store interface
- Provider::Openai vector store methods (create, upload, search, delete)
- Family::VectorSearchable concern with document management
- FamilyDocument model for tracking uploaded files
- Migration adding vector_store_id to families and family_documents table
https://claude.ai/code/session_01TSkKc7a9Yu2ugm1RvSf4dh
* Extract VectorStore adapter layer for swappable backends
Replace the Provider::VectorStoreConcept mixin with a standalone adapter
architecture under VectorStore::. This cleanly separates vector store
concerns from the LLM provider and makes it trivial to swap backends.
Components:
- VectorStore::Base — abstract interface (create/delete/upload/remove/search)
- VectorStore::Openai — uses ruby-openai gem's native vector_stores.search
- VectorStore::Pgvector — skeleton for local pgvector + embedding model
- VectorStore::Qdrant — skeleton for Qdrant vector DB
- VectorStore::Registry — resolves adapter from VECTOR_STORE_PROVIDER env
- VectorStore::Response — success/failure wrapper (like Provider::Response)
Consumers updated to go through VectorStore.adapter:
- Family::VectorSearchable
- Assistant::Function::SearchFamilyImportedFiles
- FamilyDocument
Removed: Provider::VectorStoreConcept, vector store methods from Provider::Openai
https://claude.ai/code/session_01TSkKc7a9Yu2ugm1RvSf4dh
* Add Vector Store configuration docs to ai.md
Documents how to configure the document search feature, covering all
three supported backends (OpenAI, pgvector, Qdrant), environment
variables, Docker Compose examples, supported file types, and privacy
considerations.
https://claude.ai/code/session_01TSkKc7a9Yu2ugm1RvSf4dh
* No need to specify `imported` in code
* Missed a couple more places
* Tiny reordering for the human OCD
* Update app/models/assistant/function/search_family_files.rb
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Juan José Mata <jjmata@jjmata.com>
* PR comments
* More PR comments
---------
Signed-off-by: Juan José Mata <jjmata@jjmata.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Fix property subtype not persisting on edit
* Add regression test for property subtype persistence
This change introduces model specs and factories to cover
property subtype persistence on update.
FactoryBot setup and test dependencies were adjusted to
support the new specs.
* Add regression test for property subtype persistence
* remove unused FactoryBot factories and test
* remove FactoryBot in Gemfile.lock
* Fix no-op regression test for property subtype update
* Delete no-op property_test
* add pimary_residence in properties fixtures
* add capybara system test for property subtype persistence
* fix spelling and indent
* rename test to "can persist property subtype"
Signed-off-by: HugoleDino <135261771+HugoleDino@users.noreply.github.com>
---------
Signed-off-by: HugoleDino <135261771+HugoleDino@users.noreply.github.com>
* feat: Add responsive dialog behavior for transaction modals
Add responsive option to DS::Dialog component that switches between:
- Mobile (< 1024px): Modal style (centered) with inline close button
- Desktop (≥ 1024px): Drawer style (right side panel) with header close button
Update transaction, transfer, holding, trade, and valuation views to use
responsive behavior, maintaining mobile experience while reverting desktop
to drawer style like budget categories.
Changes:
- app/components/DS/dialog.rb: Add responsive parameter and helper methods
- app/components/DS/dialog.html.erb: Apply responsive styling
- app/views/*/show.html.erb: Add responsive: true and hide close icons on mobile
* fix: Enhance close button accessibility in dialog components
* fix: Refactor dialog component to improve close button handling and accessibility
* Include newer providers in automatic family sync
Coinbase, CoinStats, Mercury, and SnapTrade all implement Syncable
and have Syncer classes but were not listed in child_syncables,
meaning their data only refreshed on manual sync button clicks.
* refactor(syncer): Open/Closed principle for provider sync
- Adding new providers requires modifying child_syncables (violates O/C)
- plaid_items missing .active scope (bug: syncs deleted items)
- snaptrade_items can exist without user registration → fails on sync
- Scattered knowledge about 'ready to sync' logic
1. **Registry pattern**: SYNCABLE_ITEM_ASSOCIATIONS constant lists all
provider associations that participate in family sync
2. **Encapsulated sync-readiness**: Each item model defines its own
`syncable` scope that knows when it's ready for auto-sync:
- Most providers: `syncable = active` (not scheduled for deletion)
- SnapTrade: `syncable = active + user_registered` (has API creds)
3. **Single loop**: child_syncables iterates the registry, calling
`.syncable` on each association
- Adding a provider = add to registry + define syncable scope
- Each model owns its 'ready to sync' business logic
- Fixes plaid_items bug (now uses .active via .syncable)
- Fixes snaptrade auto-sync failures (filters unregistered items)
- Easy to extend with new conditions per provider
- family/syncer.rb: Registry + dynamic collection
- *_item.rb (7 files): Add `scope :syncable, -> { active }`
- snaptrade_item.rb: Add syncable with user_registered filter
* Fix rubocop bracket spacing in SnaptradeItem syncable scope
* fix: keep nav bar sticky at top
* fix: sticky on settings page
* fix: keep padding in settings page
* fix: make all settings page title sticky
* fix: make buttons sticky with title
* fix: set header bar min height
* fix: mobile responsive
* fix: reduce header bar
* Add LLM prompt for API endpoint consistency (fixes#944)
- Add .cursor/rules/api-endpoint-consistency.mdc: checklist that applies
when editing app/controllers/api/v1, spec/requests/api/v1, or
test/controllers/api/v1. Enforces (1) Minitest-only behavioral coverage
for new endpoints, (2) rswag docs-only (no expect/assert), (3) same
API key auth pattern in all rswag specs.
- Reference the rule in AGENTS.md under API Development Guidelines.
* Add tests for API endpoint consistency implementation
- Minitest: test/api_endpoint_consistency_rule_test.rb checks rule file
exists, globs, and all three sections (Minitest, rswag docs-only,
API key auth) plus AGENTS.md reference.
- Standalone: test/support/verify_api_endpoint_consistency.rb runs
same checks without loading Rails (use when app fails to boot).
- Rule: add mdc: links for Cursor, note valuations_spec OAuth outlier.
* Address review: add --compliance check, CLAUDE.md section
- Verification script: --compliance scans current APIs and reports
rswag OAuth vs API key, missing Minitest for controllers, expect/assert.
- CLAUDE.md: add Post-commit API consistency subsection under
API Development Guidelines (links to rule, documents script + --compliance).
---------
Co-authored-by: mkdev11 <jaysmth689+github@users.noreply.github.com>