K0 report, SPEC v0.36: K0 results, windowed history fetch (D-177 to D-180), card artwork cropped
Build Kvitt images / build (push) Failing after 3h10m35s

K0 ran on the owner's SJ Prio Mastercard on 2026-10-24; the report
docs/spikes/K0-enable-banking.md holds aggregates only. SPEC v0.36 answers
every K0 TODO(spec) of §9.12: D-177 K0's outcome and the owner's
assumptions (partner card, pending->booked, next-day session; verified by
K1's owner steps), D-178 the windowed history fetch (100-row cap, no
continuation key: 30-day windows split at 100 rows), D-179 the transaction
mapping (merchant from remittance_information, booking date else value
date, FEE debits to handle, debtor/CPAN fields never stored), D-180 the
artwork cropped to the card (1313 x 829) and committed for Android and the
web. K plans, prompts/README and HANDOFF follow; K1 to K3 spec text goes
into v0.37.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 12:31:06 +02:00
parent 8fa91986f0
commit 8b620c8c6a
11 changed files with 224 additions and 78 deletions
+11 -12
View File
@@ -4,7 +4,7 @@
Card purchases in the Android app (FR-22, SPEC §9.12, D-169, D-170, D-172, D-173): the Kortköp tab as the second bottom destination, Anslut kort in Settings with the SJ Prio artwork, the BankID connect flow in a Custom Tab, the purchase sheet with "Skapa utgift", suggested matches and "Ignorera", "Från kortköp" on account detail, the card line on expense detail, offline-capable link changes, Room version 2. A UI-contract change (`:core:model` is a contract file, §14.8) with data layer, fakes, features and goldens.
## 2. Read first
1. SPEC §9.12 (CP-1 to CP-14, with K0's answers in v0.36), FR-22, §9.9, §9.11 (the suggestion on the prefilled description), §13.2 (four destinations), §13.3 rows S-5, S-7, S-8, S-13, S-23 to S-27 and the "Card purchase strings" table, §14.2 (§14.2.1 card entities, §14.2.3 version 2, §14.2.4 "Changes in v0.35"), §14.3.3 and §14.3.4 E-36 to E-44, §14.4.1, §14.5.8, §6.2 (module table, layering, feature convention), §14.7, §14.8 (compatibility); D-169, D-170, D-172, D-173.
1. SPEC §9.12 (CP-1 to CP-14, with K0's answers in v0.36), FR-22, §9.9, §9.11 (the suggestion on the prefilled description), §13.2 (four destinations), §13.3 rows S-5, S-7, S-8, S-13, S-23 to S-27 and the "Card purchase strings" table, §14.2 (§14.2.1 card entities, §14.2.3 version 2, §14.2.4 "Changes in v0.35"), §14.3.3 and §14.3.4 E-36 to E-44, §14.4.1, §14.5.8, §6.2 (module table, layering, feature convention), §14.7, §14.8 (compatibility); D-169, D-170, D-172, D-173, D-177 to D-180 (K0's results and the artwork).
2. K1's `contracts/openapi.yaml` operations E-36 to E-44, `contracts/examples/E-36-*` to `E-44-*`, `contracts/test-vectors/match_card_purchase.json` (on `main` once K1 lands; until then the spec).
3. `docs/ux/design-spec.md` §2 (tokens), §3.4, §3.5 (ledger rows), §3.6 (banners), §3.11 (scaffolding), S-4, S-5, S-8, S-13, S-16 (for list and banner patterns); `docs/ux/strings.csv`.
4. Files: `android/core/model/**`, `android/domain/**`, `android/data/**` (`KvittDatabase`, sync engine, `KvittApi`, `DataBindingsModule`), `android/core/fakes/**`, `android/core/design/**` (`KvittNavigationBar`, `KvittDestination`), `android/feature/{account,expense,settings}/**`, `android/app/**` (`nav/KvittApp.kt`, the manifest's intent filters), `android/README.md`.
@@ -14,7 +14,7 @@ Card purchases in the Android app (FR-22, SPEC §9.12, D-169, D-170, D-172, D-17
- The UI contract of §14.2.4 "Changes in v0.35" is binding (names and members); additions need a decision, proposed in the report.
- New module `:feature:cards` (`android/feature/cards/`, package `se.hagstedt.kvitt.feature.cards`), included in `android/settings.gradle.kts`, following the feature convention (D-017): one entry composable per screen, navigation as lambdas, ViewModels on `:core:model` interfaces only, strings prefixed `cards_`.
- `:core:design`: `KvittDestination.Cards` between `Home` and `Analytics`, icon `credit_card` (Material Symbols, outlined like the others), label `nav_cards`; every top-level screen's bar shows four items, so the S-3, S-4, S-11, S-13 goldens change (re-record and open each).
- Card artwork (D-173): `CardArt(imageKey, name, modifier)` in `:feature:cards` maps `sj_prio_mastercard` to `R.drawable.card_sj_prio_mastercard` when the owner's file exists at `android/feature/cards/src/main/res/drawable-nodpi/card_sj_prio_mastercard.png`; otherwise, and for any unknown key, it draws the fallback card: aspect ratio 1.586:1, corner radius 16 dp, fill the `green` category token's light value in both themes, the card name in white (contrast 4.95:1), no other text. The goldens use whatever is committed at recording time; when the owner's file arrives later the mapping line is added and the goldens re-recorded (a follow-up, not a blocker). The image's use was cleared by the owner (2026-10-24, the "Licences first" step).
- Card artwork (D-173, D-180): the owner's picture is committed at `android/feature/cards/src/main/res/drawable-nodpi/card_sj_prio_mastercard.png` (1313 × 829, cropped to the card, aspect 1.584; transparent only at its rounded corners and in the semicircular notch on the right edge, as on the physical card). `CardArt(imageKey, name, modifier)` in `:feature:cards` maps `sj_prio_mastercard` to `R.drawable.card_sj_prio_mastercard` from the first commit and draws it at its own aspect ratio inside the card box (`ContentScale.Fit`), without clipping or a background, so its own corners and notch show; content description the card's name. For any unknown key it draws the fallback card: aspect ratio 1.586:1, corner radius 16 dp, fill the `green` category token's light value in both themes, the card name in white (contrast 4.95:1), no other text. The goldens use the committed picture; one golden covers the fallback (a fake catalogue entry with an unknown image key). The picture shows the SJ and Mastercard logos; the owner cleared that use (2026-10-24, the "Licences first" step). Do not edit or re-encode the PNG.
- Custom Tab: `androidx.browser` (catalog entry, latest stable at implementation time, documented usage confirmed from the official docs). The deep link `kvitt://cards/connected?attempt=<id>` is an intent filter on `MainActivity` (exported already); `:app` routes it.
- Room version 2 with `MIGRATION_1_2` and its test (§14.2.3), the exported `2.json` committed. The revoke wipe includes the card tables.
- The card cycle (§14.2.4 `:data`) runs after every household sync cycle and after `refresh`; pending link changes go out after the household push (CP-9).
@@ -47,14 +47,13 @@ From SPEC §14.7 (row K2): `android/feature/cards/**` (new), and as a contract-c
The Android gate, from `android/`: `PATH=/opt/homebrew/opt/openjdk@25/bin:$PATH ../tools/gradle-serial.sh check`. Green: `BUILD SUCCESSFUL` in this session, test counts from fresh XML, screenshot verification passing.
## 8. HUMAN steps (relayed by the orchestrator in chat)
1. The card artwork: put the SJ Prio Mastercard picture (PNG, at least 1012 × 638 px, the card's own aspect ratio) at `/Users/jhag/projects/kvitt/android/feature/cards/src/main/res/drawable-nodpi/card_sj_prio_mastercard.png` and say done; until then the app draws a green card with the name.
2. After K1 is deployed on the Pi and the K2 release APK is installed (Auto Blocker off for the update, back on afterwards): Inställningar › Anslut kort › SJ Prio Mastercard › 30 dagar › "Anslut med BankID"; BankID opens, sign; back in Kvitt "Kortet är anslutet" and the Kortköp tab lists your purchases (yes/no each).
3. Kortköp › a purchase › "Skapa utgift" › "Spara": the expense is in its account and the purchase is under "Kopplade" (yes/no).
4. On a purchase your partner already entered: the row shows "Koppla till …"; tap it: under "Kopplade" (yes/no).
5. Flight mode on: ignore a purchase, then flight mode off: it stays ignored after a sync (yes/no).
6. An account › menu › "Från kortköp": a purchase › "Spara": the expense is in that account (yes/no).
7. Inställningar › Anslut kort › SJ Prio Mastercard › "Koppla från" › "Koppla från": the Kortköp tab shows "Anslut ditt kort" and the linked purchases are kept under "Kopplade" after reconnecting (yes/no).
8. On the partner's phone (after she connects her card): Kortköp shows only her purchases, and none of yours anywhere (yes/no).
1. After K1 is deployed on the Pi and the K2 release APK is installed (Auto Blocker off for the update, back on afterwards): Inställningar › Anslut kort › SJ Prio Mastercard › 30 dagar › "Anslut med BankID"; BankID opens, sign; back in Kvitt "Kortet är anslutet" and the Kortköp tab lists your purchases (yes/no each).
2. Kortköp › a purchase › "Skapa utgift" › "Spara": the expense is in its account and the purchase is under "Kopplade" (yes/no).
3. On a purchase your partner already entered: the row shows "Koppla till …"; tap it: under "Kopplade" (yes/no).
4. Flight mode on: ignore a purchase, then flight mode off: it stays ignored after a sync (yes/no).
5. An account › menu › "Från kortköp": a purchase › "Spara": the expense is in that account (yes/no).
6. Inställningar › Anslut kort › SJ Prio Mastercard › "Koppla från" › "Koppla från": the Kortköp tab shows "Anslut ditt kort" and the linked purchases are kept under "Kopplade" after reconnecting (yes/no).
7. On the partner's phone (after she connects her card, PLAN-K1 §8 step 5): Kortköp shows only her purchases, and none of yours anywhere (yes/no).
## 9. Out of scope
- The server, the contract files, the web (K1, K3). A card notification (OQ-7), bulk add (OQ-8), automatic linking (D-171).
@@ -63,7 +62,7 @@ The Android gate, from `android/`: `PATH=/opt/homebrew/opt/openjdk@25/bin:$PATH
## 10. Report
- Branch `agent-k2`, head commit after rebasing; gate lines; tests; goldens added and re-recorded (names).
- Proposed decision entries and spec text for anything beyond §14.2.4 (numbered from the brief); the strings added to `strings.csv` (keys); proposed design-spec sections for S-23 to S-27.
- Dependency-report lines; whether the owner's artwork was present; HUMAN steps outstanding worded for relay; open questions with SPEC section numbers.
- Dependency-report lines; HUMAN steps outstanding worded for relay; open questions with SPEC section numbers.
## 11. Numbering
Room version 2; decisions and spec version from the start prompt (`TODO(spec)` if none was given).
Room version 2; decisions from the start prompt (`TODO(spec)` if none was given). Proposed spec text goes into SPEC **v0.37** (v0.36 recorded K0's results; K1, K2 and K3 share v0.37).