:feature:home: Home with balance hero, sync pill, accounts, archived section,
empty and first-sync states, update banner and "Tillåt installation" dialog;
sync status with pending rows, rejections of every §14.4.6 reason and the
clock-skew banner. :feature:account: account detail with header, ledger by
month, overflow menu, archive/unarchive/delete and their errors; account
editor with name, percent fields (two decimals), slider, presets, preview,
name_taken/validation errors and offline state. Fake scenarios in
fakes/home and fakes/account; full-screen Roborazzi goldens for every state
in sv/en × light/dark (200 % for long states); ViewModel unit tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
SettleScreen (global, per account, editing) and SettlementDetailScreen with
Hilt ViewModels over the :core:model interfaces; proposals, remaining,
warnings and enablement come from SettlementRepository. Fake scenarios in
fakes/settle/, 19 states x sv/en x light/dark goldens (+ 200 % for long),
unit tests for amount input and both state holders.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
AppBuildInfo from BuildConfig for both variants (buildConfig enabled); the
release source set includes DataBindingsModule (D-041), so features'
@HiltViewModels resolve in release as they do against the debug fakes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
Features' @HiltViewModel classes need bindings in :app's graph; until M8 binds :data, debug builds bind
FakeRepositories(FakeScenarios.empty). Debug source set only; M8 deletes the file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
KvittTheme with every Material and Kvitt colour role light and dark, the 14
category colours with containers, chart and avatar colours, type scale with
tabular amount styles, spacing, shapes (stable fallbacks: MaterialShapes is not
in Material 3 1.4.0), elevation and motion springs; 191 Material Symbols
Rounded vector drawables (33 category tokens filled and outline, UI icons);
253 shared strings from docs/ux/strings.csv in values and values-sv with
plurals; pure-Kotlin MoneyFormatter, DateFormatter (ISO weeks) and AmountInput
with unit tests; every component of design-spec §3 with TalkBack semantics,
including the ledger rows and editor fields (D-019) and Canvas charts; a
preview catalogue in the debug source set; 212 Roborazzi goldens (49 sheets x
light/dark x sv/en plus 16 at 200 %). The two :app empty-screen goldens are
re-recorded because they render the real theme now (PNG only).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
ExpenseEditorOptions.frequentCategoryIds per active account (up to five active
categories by non-deleted expense count in the account, ties by sort_order) and
CategoryRepository.deletable(id) (AC-7.6), from Room queries in :data and the
same rule in :core:fakes. Room schema unchanged (version 1).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
- D-055 build-time copy of the keyword table, D-056 parseCategoryKeywords,
D-057 enum declaration orders, D-058 E-7 example shows server_version
15 and 16 and its test compares them again.
- D-051 fulfilled: §9.7 hex values from docs/ux/design-spec.md §2.3;
design-spec.md binding, strings.csv the source of string resources.
- D-059: ExpenseEditorOptions.frequentCategoryIds and
CategoryRepository.deletable; new milestone M12d with PLAN-M12d and
prompts/M12d.md; M7a-f depend on M6 and M12d.
- suggestCategory readings (null when other is archived), §11.2 landed
marks and the 2026-10-18 dry run, dev stack on schema 2, PLAN-M12b
SQLite 3.51.x, design-spec §6 notes in PLAN-M6/M7c/M7f; HANDOFF rewritten.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
Kalas (muted) design spec with final tokens, components and every screen S-1 to
S-22 in all states; 80-frame screen catalogue with 155 PNGs in docs/ux/screens/;
strings.csv with D-017 module prefixes (558 rows, en and sv); colour tokens
renamed to the landed contract names slate and amber; fixed category ids;
proposed-decisions.md with the final §9.7 hex values and UX-9. Supersedes
tokens.md, components.md, screens.md and the U1 Kalas PNGs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U2TeboHB8AtnzTrt3bWFzY
Owner picked direction 3 Kalas with calmer colours. The palette, category and chart
colours are muted (closest category pair CIEDE2000 13.7 light, 12.3 dark), the page
uses the Material 3 default typeface (D-020), S-7 shows the category chosen from the
description, and a before/after section compares bright and muted. Owner answers,
findings and proposed decision and spec text are in docs/ux/proposed-decisions.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three visual directions (Skärgård, Kvitto, Kalas) as self-contained HTML pages with
every SPEC §13.3 screen in light and dark, Swedish, plus PNG exports and the exporter
script. Direction-independent docs: ia.md, screens.md, flows.md, components.md,
tokens.md (values for all three directions), strings.csv (key,en,sv,notes), and the
comparison with a recommendation in directions/README.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- migrations/0002_categories_v2.sql, byte-identical to contracts/schema/migrations/0002_categories_v2.sql
(SPEC §14.1.1), with an identity test; SCHEMA_VERSION = 2 (E-1 reports it, the importer's check follows).
- Seed test on a fresh database (the fourteen rows of AC-7.2, server_version 11 to 14) and an upgrade test of a
v1 database in use (0001 applied through the migrator, then users, a custom, a renamed and an archived default,
an account, a recurring template, expenses): every earlier row unchanged, the four rows above the old
sequence, foreign_key_check empty, schema_version 2, a pull from the old cursor returns exactly the four rows.
- DefaultCategoryKey with the fourteen keys (wire enum and contracts/openapi.yaml); icons 33 and colours 14
(§9.7) in validation.
- The two category examples M12a held back (E-26 response-default, sync/pull-page-categories-v2) and its
contracts-check patch for them and the enum.
- Importer: splitwise_categories.csv per the §10.8 v0.8 table (D-053), DEFAULT_KEYS of fourteen, synthetic
fixtures for the finer mapping (Insurance/Försäkringar to insurance among them), a schema-1 refusal test.
- Test harnesses seed users at server_version 15/16 (after the fourteen seeds); pull counts 10 -> 14.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
- :core:model: DefaultCategoryKey in AC-7.2 order (14 keys: housing, insurance, children, fees added),
ColorToken + SLATE, AMBER, CategoryIcons.ALL + key, shield, account_balance (33),
ExpenseRepository.suggestCategory(accountId, description); ContractTest updated.
- :domain: parseCategoryKeywords (pure, shared by :data and :core:fakes) with a parser test; the
suggestCategory records and rule unchanged, suggest_category vectors pass.
- :data: PlatformUnicodeText (java.text.Normalizer, Character), CategoryKeywords parsed once from the
Java resource that copyCategoryKeywords copies from contracts/category-keywords.csv at build time
(missing file fails the build), ExpenseRepositoryImpl.suggestCategory over the Room snapshot and the
DataStore last-used category; tests: byte identity and the 14.8 rules of the bundled table, the
UnicodeText binding, and Room/Robolectric cases for history, first token, ties, keywords, fallback,
archived categories, deleted expenses, both users and accounts, null account.
- :core:fakes: the 14 defaults with the schema v2 ids, fake suggestCategory through :domain with its own
build-time copy of the table; tests.
- Room schema unchanged (version 1).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
- contracts/schema/migrations/0002_categories_v2.sql: server migration 0002 rebuilds categories with the
14-key default_key CHECK, inserts housing, insurance, children and fees with server_versions taken from
the global sequence, sets schema_version 2. server-v1.sql unchanged.
- contracts/category-keywords.csv: the keyword table of the suggestion (148 entries, from docs/ux).
- contracts/test-vectors/suggest_category.json: 65 Kotlin-only cases of CS-4 to CS-6.
- contracts/examples/E-1-health: schema_version 2.
- tools/contracts-check.py: reference suggestCategory, keyword-table checks, 0002 load checks on a fresh
and an in-use database, plain icon/color check, new icon and colour tokens.
- android/domain: suggestCategory (CS-4 to CS-6, code points, platform NFC and categories through
UnicodeText), vector runner for suggest_category, eight vector files.
- server/crates/kvitt-domain/tests/vectors.rs: suggest_category is Kotlin-only.
The OpenAPI DefaultCategoryKey extension and the two new category examples are held back until the
server's wire types know the new keys (M12b).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
D-044 adds nine Swedish Splitwise category names to the SPEC §10.8 table and
splitwise_categories.csv. D-045 records the reconciliation against the totals
rows; D-046 the importer CLI semantics and exit codes. M9 marked landed at
ab298ab, the M11 rehearsal recorded, server/README.md and prompts/README.md
updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
CLI per §10.8 with --help, --dry-run and a summary; English and Swedish
headers with member columns matched by name; CSV quoting; payments as
settlements with one allocation; trailing totals row used to reconcile
the account balance (mismatch rolls back, exit 4); non-SEK rows abort
unless --fx; committed category table data/splitwise_categories.csv;
idempotent re-runs through import_key; rows validated with the server's
push checks, audited with source import, no pushes; one BEGIN IMMEDIATE
transaction. Tests over synthetic fixtures only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
- D-035 unusable mutation_id/entity_type/entity.id fails the whole push with 400; other shape errors per mutation.
- D-036 0 mutations is batch_too_large ("outside 1 to 100").
- D-037 never-synced delete runs full step-8 validation; current on every rejection; created_at kept; canonical ids.
- D-038 FCM retries: one send plus three retries (1/5/25 s), 401 drops the token, other 4xx not retried.
- D-039 pruning strictly older than 90 days; tasks::start returns AppState with the push queue, SqlAuditWriter default.
- Clarifications: step 8 order, negative since, pull read transaction, FCM JWT kid and cache, token clearing only if
unchanged, unusable credentials, scheduler clock jumps, rotate-epoch exit codes, /i/ empty code, image build needs
a C toolchain and CA roots (dev image rebuilt 2026-10-18).
- PLAN-M4 pruning wording, prompts/README status, HANDOFF at bd6f10a.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
- src/sync/: pull with UNION ALL paging and cursor_ahead; push processing of SPEC §14.4.4 steps 1 to 12 in one
BEGIN IMMEDIATE with a savepoint per mutation, LWW with delete-wins, tombstones, clock_skew, replay from
sync_mutations, sync_batch for more than 3 notifiable changes; 90-day pruning.
- src/audit/: SqlAuditWriter and the §14.4.7 diff (now the default in AppState::new).
- src/push/: bounded PushHub (100, drop oldest), FCM HTTP v1 client with service-account JWT (RS256, jsonwebtoken
on aws-lc-rs) and token cache, sender with 1/5/25 s retries and token clearing; disabled without credentials.
- src/tasks/: start installs push and spawns the 06:00 generation (and at start) and 04:00 pruning loops on the
injectable clock.
- cli: rotate-epoch; serve uses into_make_service_with_connect_info; stale doc comments updated; /i/ empty code
gets the HTML 404.
- tests: sync_api, push_fcm (local stub of Google's endpoints), scheduler_daily; skeleton routes test now asserts
no route is 501.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
Records the decisions proposed by M3 (503cee5) and M5 (c28c52b). M5's proposals
D-030 to D-033 are renumbered D-040 to D-043; D-035 to D-039 are reserved for M4.
Adds the D-027 clock-skew re-stamp rule, M5's contract requests for M8 (PLAN-M8,
SPEC 11.3), the connect-info note for M4 (PLAN-M4), the picture size note (PLAN-M6),
and updates prompts/README status.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
DAOs on M2b's Room 3 entities and KvittDatabase v1 (schema 1.json unchanged), DTOs for every wire
object and endpoint body with a round-trip test over all 80 contract examples, the Ktor client with the
section 14.3.1 headers and 14.3.2 error mapping, the Keystore-encrypted token store in auth.preferences_pb,
the sync engine of section 14.4 (push batches, pull pages, epoch recovery, rejections, clock skew, 401 and
426 handling) tested with MockEngine including AC-15.6, WorkManager triggers, every :core:model repository,
the APK updater behind an installer interface, Coil pictures, Hilt modules, Room migration test
infrastructure. Fakes: the five settled TODO(spec) comments resolved per SPEC v0.4 (settlement row in an
account's ledger carries that account's allocation).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Records the decisions proposed in the M2a (fdf2093) and M2b (fad6fc9)
reports: D-005 OpenAPI equality normalisations and CommonRules, D-006
bp_sum_not_10000 token, D-007 per-crate no-unwrap deny, D-010 Roborazzi,
D-011 IncompleteCustomRangeException, D-012 Room 3 with M2b's entities and
1.json as a contract file, D-013 JDK 25 Robolectric flags, D-014
failOnNoDiscoveredTests (D-008, D-009 unused). Clarifications in §6, §8.2,
§8.6, §9.6, §10.3, §10.4, §11, §14.2, §14.3, §14.5, §14.7, §14.8 and
AC-11.7. PLAN-M5 builds on the existing entities; v0.4 readings added to
PLAN-M3, M5, M7b to M7f. HANDOFF rewritten: next free D-040, spec v0.5,
server schema 2, Room schema 2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cargo workspace (kvitt-domain, kvitt-server, kvitt-import), Rust 1.99.1 pinned.
kvitt-domain passes every Rust vector and adds next_occurrence_date (D-015).
kvitt-server: config, SQLite pool (WAL, §8.9 pragmas), migration 0001 identical to
the contract schema, error type for all 25 codes, auth extractor and protocol
layer, all 33 routes (E-1/E-2 implemented, the rest 501), utoipa document equal
to contracts/openapi.yaml, seams for M3/M4/M9, serve/migrate/healthcheck.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Gradle project in android/ with every module of SPEC §6.2 (build-logic convention plugins, version catalog,
architecture check in check), :core:model types and repository interfaces of §14.2.4 and §14.5, :domain
implementing §14.5.2 and §14.5.4 (125 vector cases), scenario-driven fakes with the empty scenario, Room 3
entities and KvittDatabase v1 with exported schema, :app shell with Hilt, en/sv strings with MissingTranslation
as a lint error, Roborazzi goldens of the empty screen in light and dark.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Records D-004 and D-015 to D-025: shells and ownership, server seams
(router middleware, rate_limit layer, tasks::start, AuditWriter and PushQueue
traits, next_occurrence_date in kvitt-domain, M3 owns recurring generation,
M4 lands after M3), OpenAPI parsed-value equality, feature convention and
strings, empty fake scenario and resolveRange exception, shared ledger rows and
editor fields in :core:design, no bundled font, pruning at 04:00, development
infrastructure as M2c-dev built it, M2c-pi allowances, release keystore,
production import procedure. Plans and prompts close their open points;
HANDOFF rewritten to the real state; CLAUDE.md: implementation agents never
edit SPEC.md or docs/decisions.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
infra/dev: compose stack on the Mac (server kvitt-server:dev, on-demand backup),
.env.example, build/up/logs/backup/copy-google-services scripts. No Caddy, no TLS
(debug builds accept http to private LAN addresses, AC-2.8). Bind-mounted data.
infra/backup: alpine 3.24.2 image and backup.sh (SPEC 10.9; Drive upload skipped
when RCLONE_FOLDER_ID is empty). infra/README.md for the dev stack and Firebase.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PLAN-<id>.md for U1, U2, M1, M2a, M2b, M2c-dev, M2c-pi, M3, M4, M5, M6,
M7a-M7f, M8, M9, M10, M11, each with the eleven fixed sections, and a
start prompt per plan in prompts/ plus prompts/README.md (dependency
order and parallel waves).
SPEC v0.2: owner answers D-001 (development-first, M2c split into
M2c-dev and M2c-pi, Gitea registry), D-002 (local backups deleted once
on Drive), D-003 (Splitwise CSV format, English and Swedish variants,
category mapping), OQ-1, OQ-2, OQ-4 answered; M1's readings of
ambiguous contract points stated explicitly in §14.3 to §14.5.
HANDOFF §2 lists the plans.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
contracts/openapi.yaml (OpenAPI 3.1, E-1..E-33, error shape and the
exhaustive error code enum, device bearer scheme), contracts/schema/
server-v1.sql (SPEC §14.1 verbatim), seven test-vector files per §14.5.5,
80 examples (every JSON body of §14.3.4, every error code, sync pull/push,
every FCM push type), and tools/contracts-check.py (stdlib-only reference
of §9.1, §9.3, §9.4 and §14.5.4 plus shape checks; the M1 gate).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Write SPEC.md v0.1 from the owner interview (requirements, data model,
stack, API outline, security, business rules, deployment, milestones,
UX sprint brief, contracts). Fill the CLAUDE.md blanks, move the Gradle
pin files into android/, point the tools at android/ and server/, and
record the git bootstrap exception in the handoff.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>