- Compute next vX.Y.Z from the latest tag (patch bump) via Gitea API
- Pass it to flutter build via --build-name/--build-number so the APK
versionName/versionCode always increase (installs update in place)
- Auto-create tag + Gitea release + upload both APKs on master runs only
- Verbose logging: version computed, built APK sizes, tag/release/asset results
- Drop the tag trigger: the auto-created tag would otherwise re-trigger the
workflow (double builds / release conflicts)
Guard the Create Gitea release step: fetch origin/master and compare with
the tagged commit; if the tag is not on master's tip, skip release creation
instead of publishing a release for a feature/dev branch.
upload-artifact@v4 relies on the new @actions/artifact 2.x protocol and
fails on Gitea with GHESNotSupportedError. v3 uses the older artifact
API that Gitea implements.
signingConfigs.release was configured unconditionally, so even assembleDebug
failed at configuration time with 'getProperty(...) must not be null' on CI
(build-apk.yml restores the keystore only after the debug build). Guard the
release signing config on key.properties existence: debug builds proceed
unsigned, release builds still sign when the keystore is present.
Two fixes for the notification-parsing pipeline:
- CompanionDeviceManager pairing (DEVICE_PROFILE_WATCH -> COMPANION_DEVICE_WATCH
role -> RECEIVE_SENSITIVE_NOTIFICATIONS) via a new platform channel in
MainActivity.kt, companion_device_channel.dart and companion_access_controller,
surfaced as a card in parsing settings. Lifts the system "Confidential"
redaction that hid VTB/T-Bank notification text from the listener.
- raw_messages.ai_response: the model's raw completion content is now persisted
(schema v4 + migration) and shown in the parsing log detail panel. Unlike
draftJson it survives inbox sweeps and is filled even for partial/ignored
outcomes.
flutter analyze: no issues.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Drop ParseRuleKind from parse_rules (enum survives only in rule_candidates):
a rule is now a condition (pattern + matchMode + optional txTypeGuard) plus
any set of actions (merchantCanonical/categoryId/accountId) or isIgnore
- Field-wise resolution: findClassificationRule fills merchant/category,
findAccountRule feeds the account resolver - one message may use both
- Per-rule autoApply toggle + ruleAutoApplyEnabled gate check; off -> Inbox
with full prefill
- Dedup guard in ParseRulesRepositoryImpl.create: same condition updates or
reactivates the existing row instead of inserting a duplicate
- ParsingPipeline.reapplyRulesToInbox: sweep app inbox cards on cached AI
drafts after createRule/ignoreWithRule (zero tokens)
- Schema v2 -> v3 migration (drop kind, add auto_apply) + migration_v3_test
- Rework rule_editor_screen into a single unified form; update rules list,
rule cards, source app detail, l10n strings, and tests
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the Inbox badge button in MonthHeader with a bottom-nav tab
(hidden when notification parsing is disabled). Redirect /inbox → /home
when parsing is off, register the Inbox shell branch unconditionally,
and add a settings action to the Inbox screen. Move the parsing tile to
the top of the profile screen so settings stay reachable when the tab
is hidden. Add tests for the nav tab visibility and inbox routing, plus
a release-install PowerShell helper that preserves app data.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Notification parsing:
- Drop the account_bindings table/DAO/repo/entity/controller/screen; account
resolution now goes senderToAccount rule -> source_apps.defaultAccountId
(trusted) -> global default (untrusted -> Inbox), via v1->v2 migration.
- Add defaultAccountId to source_apps; per-app settings consolidated into
source_app_detail_screen (/settings/parsing/apps/:pkg).
- Inbox auto-learns an app default on first Confirm/CreateRule; account picker
on the card instead of a disabled button; parse_error_labels extracted.
Analytics:
- Replace placeholder screen with fl_chart cards (chart_card, chart_theme,
month_stepper, month_math domain helper); slim down habit_analysis_screen.
Android: add launcher icon (adaptive foreground + colors.xml) and app_name.
Tests: migration_v2, analytics (screen/month_math), AI retry, inbox
visibility; update resolver/gate/inbox suites for the new resolution path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Set up flutter_launcher_icons to generate Android launcher icons from assets/icon/app_icon.png. Move the app label into a string resource (@string/app_name) with a debug-only "Kitty finances debug" override.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce ParseRuleKind.mixedMerchant to flag merchants whose category
varies (Ozon, marketplaces). When set, the pipeline suppresses the
merchant→category rule suggestion in the Inbox so the card shows
"confirm once" instead of "Create rule". Inbox action "Categories vary"
creates the marker rule, removes any existing merchant→category rule and
observed candidate, and rewrites the draft cache without the suggestion.
Adds findMixedMerchantRule lookup, InboxController.markMerchantMixed,
localizations (en/ru), UI in inbox_card/rule_editor, and tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When enabled, the native listener captures every post from monitored
apps (including empty/duplicate placeholders like "Конфиденциально")
and stores a full dump of key/postTime/visibility/flags/extras in the
new raw_messages.diagnostics column (schemaVersion 9), shown in the
parsing-log detail panel. Dedup is salted with postTime in this mode so
two distinct posts (placeholder then real-text update) both appear while
reboot redelivery still collapses. Toggle lives in parsing settings
(Android), synced to native via the ingest worker.
Also gate the parsing worker drain on the feature being enabled, with an
explicit re-drain when it is turned back on.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the OpenRouter HTTP client with a DeepSeek client:
- data/openrouter/ -> data/deepseek/; OpenRouterClient -> DeepSeekClient
(+ DeepSeekCompletion / DeepSeek*Exception)
- base URL api.deepseek.com, drop OpenRouter-only headers
- DeepSeek has no strict json_schema: use response_format json_object
and rely on the prompt for shape (ai_schema.dart removed)
- secure-storage key openrouter_api_key -> deepseek_api_key
- default model deepseek/deepseek-v4-flash -> deepseek-chat
- l10n strings, test dart-defines (DEEPSEEK_API_KEY/_TEST_MODEL), docs
Also bundles in-progress findBodyIgnoreRule (body-level ignore rules
applied before the AI call) already present in the working tree.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>