SPEC v0.32: web plans W1 to W5 (D-144 to D-154), APK install failures (D-155)
Build Kvitt images / build (push) Failing after 14m44s
Build Kvitt images / build (push) Failing after 14m44s
D-144 to D-154 record the web feature plans W1 to W5 (934eee7,a65ab71,f9a9bfb,1aecf51,3ebe57d): preset account and payment in the query string, Home empty state, the CSV format, AC-8.6 in the browser and search in the URL, the expense editor's banner and controls, live settle-up errors, analytics drill-down routes and chart accessibility, W-18's route, the partner intro and About's fallback; D-150 unused. D-155 recordsd686ee0: new AC-16.7, UpdateFailure in §14.2.4, design-spec §3.6, S-17 and S-22. SPEC §11.2, §11.3, §15.3, §15.6 and §15.7 follow the code; HANDOFF gets the state, the owner's browser checks and the web follow-ups for a W0b plan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
This commit is contained in:
+37
-14
@@ -3,16 +3,24 @@
|
||||
Living document, rewritten in place (never a second copy). A new orchestrator session reads CLAUDE.md, then this.
|
||||
|
||||
## 1. Where things stand
|
||||
- State as of 2026-10-21 late. `main` at `1c9856b` (W6 Android Insights on S-11, D-141 to D-143), pushed to `origin/main` (Gitea), after `415f9f5` (CLAUDE.md: web client, three gates), `2ae65a5` (W0 web foundation), `3fbd393` (SPEC v0.29) and `c0f4345` (update-check fix, D-140). The Android gate is green again on `main` (W6's E-34/E-35 DTOs fixed the three `:data` example tests red since `50a0d67`). This docs pass (`agent-docs-v31`) records D-140 as SPEC v0.30 and D-141 to D-143 as v0.31. Running: web W1 to W5 (D-144 to D-154 reserved for them). Landed after `1c9856b`, not yet recorded in the spec: `d686ee0`, the APK install-failure fix (D-155; two `strings.csv` keys `update_install_failed`, `update_install_blocked`, so the file has 590 rows against SPEC §13.4's 588; the web dictionaries are regenerated with them in this pass). The push of `1c9856b` started the Pi image build with the web client and E-34/E-35 (in progress on the Pi); then the owner deploys it (§3 step 4), and after the Pi runs the new image the release APK with Insights is published (`infra/publish-apk.sh --deploy`).
|
||||
- State as of 2026-10-21, night. `main` at `83f5293` (web fix: deterministic feature strings, `suggest_category` vectors, raw bodies in the fake server), after the five web feature plans W1 `934eee7`, W3 `f9a9bfb`, W5 `3ebe57d`, W2 `a65ab71`, W4 `1aecf51` (D-144 to D-154; D-150 unused) and `d268dd0` (`.gitignore` anchors `export/`); before them `aaa09d9` (SPEC v0.30, v0.31), `d686ee0` (APK install failures visible, D-155) and `1c9856b` (W6). All five web plans are landed and pushed (orchestrator). The web gate is green on `main` (59 test files, 532 tests at `1aecf51`; 59 files, 598 tests at `83f5293` with the `suggest_category` vectors, run by this docs pass). This docs pass (`agent-docs-v32`) records D-144 to D-155 as SPEC v0.32. The Pi image build with the complete web app is in progress on the Pi; the owner deploys it (§3 step 4), then checks the web client in a browser (§3 step 5). Release APK `kvitt-140` deployed 2026-10-21 (`infra/publish-apk.sh --deploy`): install-failure messages (D-155) and S-11 Insights, which need the new server image (E-34, E-35).
|
||||
- **Kvitt host name** (owner decision 2026-10-19): `kvitt.suorra.duckdns.org`, a subdomain of the owner's DuckDNS family domain `suorra.duckdns.org`; NPM issues its Let's Encrypt certificate; `KVITT_PUBLIC_HOST=kvitt.suorra.duckdns.org`, so `KVITT_PUBLIC_URL=https://kvitt.suorra.duckdns.org` (port 443, D-124). It is in `infra/pi/.env.example` and `infra/pi/.env` (`KVITT_PUBLIC_HOST`) and `android/local.properties` (`kvitt.publicHost`, git-ignored). SPEC §10.1 and §10.5.1 point here.
|
||||
- **NPM proxy host** for `kvitt.suorra.duckdns.org` is Online with a Let's Encrypt certificate (owner, 2026-10-19). The router needs no new rule: external TCP 443 already forwards to the Home Assistant Pi (owner confirmed, D-124).
|
||||
- **Images**: `gitea.lan/jacobh2/kvitt-server` and `gitea.lan/jacobh2/kvitt-backup`, tags `v1.0.0` (run 69) and `v1.0.1` (the sparse-checkout push; the tag the Pi uses), plus `latest`; the packages are public, so the Pi pulls without `docker login`. Gitea Actions on the owner's Pi runner (act_runner 0.6.1, Alpine job container): the first failures were an empty secret (the user-level secret was misnamed on the owner's side, fixed 2026-10-19; a user-level secret of the repository owner reaches the repository); run 69 succeeded: login ok, `v1.0.0`, `kvitt-backup` 5 min 20 s, `kvitt-server` 2 h 42 min on the Pi 3 (first build, `CARGO_BUILD_JOBS=1`; later runs reuse the builder's cargo cache); the tag push works with the `write:package` token. SPEC §10.3.
|
||||
- **Kvitt Pi** (D-125): Kvitt runs from `/home/jacobhagstedt/kvitt` as the owner's user `jacobhagstedt`, uid and gid 1000:1000 (`KVITT_UID`/`KVITT_GID`); Docker and the `gitea.lan` registry trust were already on the Pi; nothing outside that folder is installed or changed. **The stack is running** (owner, 2026-10-21): image `v1.0.1`, `server` and `backup` both healthy after a `chown` of `data/` (it had been created as root); compose's `mem_limit` warning (the Pi kernel has no memory cgroup, so the 160 MiB and 96 MiB limits are not enforced) is accepted. `/health` through NPM from outside answered `"status":"ok"` (public health OK). **Live** (2026-10-21): both phones are paired against it and the Splitwise import (M11) is done.
|
||||
- **App gate** (D-126, owner decision 2026-10-21): outside the home LAN `192.168.1.0/24` NPM closes (`return 444`) every request without `X-Kvitt-App: <key>`, every route included; the key is the build property `kvitt.appKey` (`android/local.properties`, git-ignored), generated by the owner with `openssl rand -hex 32` and pasted into NPM's block (`infra/npm/kvitt.conf`, `7521c01`); the server and `contracts/openapi.yaml` are unchanged. Android side `34e5cad`, docs `2acfc34` (SPEC v0.25, `infra/README.md` step 4a). `android/local.properties` has a `kvitt.appKey` and `infra/pi/out/apk/` holds `kvitt-125.apk` (checked 2026-10-21); whether both phones run a keyed APK and the gate is in NPM is not recorded here (orchestrator confirms with the owner, §3).
|
||||
- **Web client programme** (owner interview 2026-10-21, SPEC v0.28, D-129 to D-134, SPEC §15, FR-21): a React web client in `web/`, served by `kvitt-server` under `/app/` from the server image, with the app's features except the phone-only ones, plus search, CSV export, keyboard shortcuts and a language choice; login with an invite code (device `Web · <browser>`); the NPM gate also passes `/app/`, `POST /invites/accept` and bearer-token requests except `/health`, `/setup/`, `/i/`, `/apk/` (new block in `infra/npm/kvitt.conf`, README step 4, SPEC §10.5.2; checked with nginx 1.31.7, 17 requests); advanced analytics on the server (`/analytics/*`, D-133, forecast = recurring templates due + 3-month average of non-recurring spend). Plans `PLAN-W0.md` to `PLAN-W7.md`, prompts `prompts/W0.md` to `prompts/W7.md`. W7 landed `50a0d67` (D-135 to D-138, SPEC v0.29: `/app/` from `KVITT_WEB_DIR`, E-34 `GET /analytics/trends`, E-35 `GET /analytics/forecast`, rules SPEC §14.5.7, the Node stage and the repository-root build context); W0 landed `2ae65a5` (`web/**`: Vite, React 19, TypeScript strict, sync-pull ledger, invite-code login, ported money rules passing the vectors, tokens, i18n generated from `docs/ux/strings.csv` and `web-strings.csv`); W1 to W5 are running in parallel, each in its own `web/src/features/<name>/`; W6 (Android Insights) landed `1c9856b` and shows on the phones once the image with W7 and W0 is deployed and a release APK with W6 is published. The web gate is `npm run check` in `web/` (third gate, in `CLAUDE.md` since `415f9f5`).
|
||||
- **Web client programme** (owner interview 2026-10-21, SPEC v0.28, D-129 to D-134, SPEC §15, FR-21): a React web client in `web/`, served by `kvitt-server` under `/app/` from the server image, with the app's features except the phone-only ones, plus search, CSV export, keyboard shortcuts and a language choice; login with an invite code (device `Web · <browser>`); the NPM gate also passes `/app/`, `POST /invites/accept` and bearer-token requests except `/health`, `/setup/`, `/i/`, `/apk/` (new block in `infra/npm/kvitt.conf`, README step 4, SPEC §10.5.2; checked with nginx 1.31.7, 17 requests); advanced analytics on the server (`/analytics/*`, D-133, forecast = recurring templates due + 3-month average of non-recurring spend). Plans `PLAN-W0.md` to `PLAN-W7.md`, prompts `prompts/W0.md` to `prompts/W7.md`. W7 landed `50a0d67` (D-135 to D-138, SPEC v0.29: `/app/` from `KVITT_WEB_DIR`, E-34 `GET /analytics/trends`, E-35 `GET /analytics/forecast`, rules SPEC §14.5.7, the Node stage and the repository-root build context); W0 landed `2ae65a5` (`web/**`: Vite, React 19, TypeScript strict, sync-pull ledger, invite-code login, ported money rules passing the vectors, tokens, i18n generated from `docs/ux/strings.csv` and `web-strings.csv`); W1 to W5 landed 2026-10-21 (`934eee7`, `a65ab71`, `f9a9bfb`, `1aecf51`, `3ebe57d`; D-144 to D-154, SPEC v0.32), each in its own `web/src/features/<name>/`, W2 with the shared editor fields in `web/src/components/editor/`; loose ends for a W0b plan in §2 "Web follow-ups"; W6 (Android Insights) landed `1c9856b` and shows on the phones once the image with W7 and W0 is deployed and a release APK with W6 is published. The web gate is `npm run check` in `web/` (third gate, in `CLAUDE.md` since `415f9f5`).
|
||||
- **First release APK**: `apk/kvitt-123.apk` (10.0 MB, git-ignored by `*.apk`) handed to the owner from the main checkout for sideloading on both phones (both paired, 2026-10-21); it was built before any `kvitt.appKey` existed, so it sends no `X-Kvitt-App`: it works only until the gate is pasted (on the home Wi-Fi it keeps working afterwards). The phones need a keyed APK before the gate goes in (§3).
|
||||
- History rewrite and first push, 2026-10-18 (owner authorised): superseded PNG versions were dropped from the whole history (no LFS), the current goldens and UX mock-ups restored in `18c527f`; pack 45 MiB; `origin/main` pushed at `18c527f`. A bundle of the pre-rewrite history (`kvitt-pre-rewrite.bundle`) is kept outside the repository, in the orchestrator's scratchpad. Every commit from M2b on got a new hash; the oxipng pass (`7c453d8` before the rewrite) no longer exists as a commit (it only touched PNGs). SPEC, `docs/decisions.md`, this file and `prompts/README.md` cite the rewritten hashes since v0.18 (`7c453d8` stays as the oxipng pass's name; `docs/validation/ac-coverage.md` keeps its pre-rewrite hashes verbatim); since v0.19 the plans cite rewritten hashes too (eight pre-rewrite hashes in `PLAN-*.md` replaced by the commit with the same subject); `docs/spikes/pi-image-arm64.md` and the READMEs had none stale. For any other old hash, find the commit by its subject with `git log --oneline --grep`.
|
||||
- History (rewritten hashes): `5e1be38` plans and prompts, `c885aba` M2c-dev, `76ee957` SPEC v0.3, `c277ee3` M2b, `06f48ef` M2a, `6dce4c7` SPEC v0.4, `7869ec4` M3, `8f0e156` M5, `853c038` SPEC v0.5, `15056dc` M4, `d9dd584` SPEC v0.6, `50339ae` M9, `86e08a1` SPEC v0.7, `aa765bb` M12a, `d39eb2c` SPEC v0.8, `b09f322` M12c, `6eaccec` M12b, `0006bd7` UX Kalas categories and tokens, `6da6b1b` UX sprint U2, `e676812` SPEC v0.9, `557af7e` M12d, `7730a31` M6, `9ee67d4` SPEC v0.10, `a41f240` and `6453932` app wiring before M8, `1ea752c` M7a, `ea7d494` M7e, `7380e50` M7d, `0e1d99d` M7b, `0b140aa` M7c, `c4ab35f` M7f, `b24c8b5` M6b, `f4dc10c` SPEC v0.11, `3dda414` M7g, `cb832a7` and `3d9eea8` M8, `6159e18` SPEC v0.12, `8c60715` M12e, `b7b7483` M12e launcher icon, `51d6ff8` SPEC v0.13, `a312758` Vico removal, `f56dcbe` FCM `fid`, `3e6c337` Pi image arm64 check, `e2dba84` SPEC v0.14, `328c466` M8b, `6eb3f85` `FcmMessageBody.fid`, `bfde12c` SPEC v0.15, `75b549f` M8c goldens, `a0c25b0` M8c licences row, `c30a49e` R8 release build, `bacbd18` M8d, `bfc3acb` SPEC v0.16, `2476667` M2c-pi bundle, `a59abeb` M8e, `b4d9467` Dockerfile `CARGO_BUILD_JOBS`, `956908a` M8f, `1328afe` chips cleanup, `400eac9` SPEC v0.17, `18c527f` goldens restored after the rewrite; then `2fac1ec` to `e70fe0d` infra overhaul, `8660b7f` server gap pass, `7fb86f5` infra security, `dc7a296` to `a0259ab` M12f, `8103dae` to `b985ce9` server overhaul, `48fe119` SPEC v0.18, `e4e6e5d` to `c582363` Android overhaul, `3ef2c15` SPEC v0.19, `96e4cbd` APK download rate limit (D-119), `481889a` SPEC v0.20, `f1abb0d` `cargo audit` in the server gate, `4f69ae0` Gitea login in the run line, `d05f13a` Pi stack behind NPM, `62f5279` and `9231a1d` infra README for NPM (D-120), `d090c6c` Android host property (D-121), `4bcff2c` SPEC v0.21, `dd07ca4` temporary workflow diagnostic step, `4a4e45f` S-22's release server and `4b1ae0c` `publish-apk.sh` host (D-122), `c9abd24` and `e381e37` server host defaults and OpenAPI `{host}` (D-123), `ac17b41` `install.sh` step number, `cce3e4e` SPEC v0.22, `a86ad24` server and contracts on port 443, `4852803` bundle on port 443 (D-124), `038c603` Android on port 443 and the pinned-root template, `ad35231` portless Android fixtures, `b1f4c10` SPEC v0.23, `3820f9c` sparse-checkout workflow, `200c99f` `.env` without informational keys, `6e5981d` self-contained Pi compose project (D-125), `28fa71b` compose deploy hint in the workflow and `infra/build-images.sh`.
|
||||
- Landed since SPEC v0.31 (recorded in v0.32):
|
||||
- W1 web Home and accounts (`934eee7`, D-144, D-145): W-2 with the balance hero and seesaw, account cards, the archived section (open state per page session) and the "Du och <partner> är med" empty card; W-3 with month groups and the overflow menu; W-4 at `/app/accounts/new` and `/app/accounts/:id/edit`; `?account=<id>` links into W-5 and W-7.
|
||||
- W2 web expenses, search and CSV export (`a65ab71`, D-146 to D-148): W-5 with the §9.11 suggestion, the rejection banner and the discard dialog; W-6 with the audit timeline; W-12 with filters in the URL; the CSV export (`;`, BOM, decimal comma); the shared editor fields `web/src/components/editor/`; `vite.config.ts` `server.fs.allow` for `contracts/category-keywords.csv`.
|
||||
- W3 web settle up (`f9a9bfb`, D-144, D-149): W-7 with live allocation errors, `?account=` and `?settlement=`; W-8 with allocations and timeline.
|
||||
- W4 web analytics (`1aecf51`, D-151, D-152): W-9 stacked bars, W-10 drill-down with the D-128 line chart (bucket route `/app/analytics/bucket/<key>`, filter in the query string), W-11 Insights on E-34/E-35 with 3, 6 or 12 months; "Visa som tabell"; `resolveRange` and `spendSeries` ported with their vectors.
|
||||
- W5 web settings (`3ebe57d`, D-153, D-154): W-13 hub, W-14 profile with the partner's first-login intro, W-15 categories, W-16 recurring, W-17 devices, W-18 invites at `/app/settings/invite`, W-19 about with the `GET /me` header fallback.
|
||||
- Web fix (`83f5293`, no decision): Vitest's module cache kept a stale `catalog.ts` glob result in the main checkout (raw keys such as `web_accounts_load_failed` on screen); the glob lost its type argument and `catalog.test.ts` compares the loaded feature catalogs with the files; `web_accounts_load_failed` became the shared `web_ledger_load_failed` in `web-strings.csv`; `suggest_category` moved to PORTED in `lib/vectors.test.ts`; the fake server keeps non-JSON bodies raw.
|
||||
- APK install failures (`d686ee0`, D-155): installer status reaches `UpdateRepository` by session id; blocked → "Kunde inte installera uppdateringen" with the security-settings text, aborted → `Available`, other failures → the installer's message; "Försök igen" downloads again; new AC-16.7; `strings.csv` 590 rows. Release `kvitt-140` deployed 2026-10-21.
|
||||
- Landed since SPEC v0.29 (recorded in v0.30 and v0.31):
|
||||
- Update-check fix (`c0f4345`, D-140, v0.30): S-22 "Check for update" calls `UpdateRepository.checkNow()`, which always asks `GET /apk/latest` and restarts the 6-hour window; the automatic checks keep the throttle (AC-16.2, AC-16.6).
|
||||
- W6 Android Insights (`1c9856b`, D-141 to D-143, v0.31): `InsightsRepository` (E-34 trends, E-35 forecast, online only, names from Room); an "Insikter" section below S-11's local analytics following the measure toggle, 6 whole months, household and per-category line charts with the server's average and moving average, a forecast card per account ("Återkommande + snitt = prognos"); offline and error banners over the last data; 40 `s11_insights_*` goldens; Android gate green (791 tests in the feature run). `strings.csv` 588 rows (16 `analytics_insights_*` keys); the web dictionaries regenerated.
|
||||
@@ -57,23 +65,23 @@ Living document, rewritten in place (never a second copy). A new orchestrator se
|
||||
- Validation reports (SPEC §11.4): `docs/validation/ac-coverage.md` (taken at `956908a`, `ed1b130` before the rewrite): 270 rows, 260 Done, 5 Partial, 1 Missing, 4 device-only; feature complete in code, not yet against §4's acceptance rule; its S-16 and S-22 mismatches are fixed by M12f. `docs/validation/security-review.md` (taken at `18c527f`): Critical 0, High 1, Medium 3, Low 10, Info 8; L-6 kept (D-116), L-7 fixed (D-116), L-1, L-2, L-5, L-9, M-2 fixed (D-117), L-8 fixed (`e70fe0d`); H-1 (`725901c`, D-115), M-3 (`baa5806`) and L-4 (`b985ce9`, D-114) fixed by the server overhaul; L-10 left as the spec says, decision pending (§4). Owner answers of 2026-10-19: M-1 no (backups stay unencrypted, rclone keeps the `drive` scope; accepted risk in SPEC §8.1 and §8.7), L-3 accepted and fixed (`96e4cbd`, D-119), the 16 MP and 512-character limits (D-114) confirmed; M-2's remaining question (the Home Assistant SSH key form) is moot with the NPM rework (D-120) and closed; D-120 also moves L-1 and the headers to NPM, makes L-2 moot, reverses the L-8 fix and replaces I-5's check with a conntrack check (dated notes in the review, SPEC §11.4). H-1 is fixed (`725901c`).
|
||||
- Server overhaul (landed on `main` `8103dae` to `b985ce9`): shared helpers and one test harness, clippy pedantic wins; H-1 rate limiter (`725901c`, D-115), pictures at most 16 MP and 64 MB decoded (`baa5806`, D-114), `fcm_token` at most 512 characters (`b985ce9`, D-114); shared row inserts, invite creation and CLI helpers, `tests/common/` harness, synchronous `tasks::start`; 268 server tests; all crates at their latest versions. Recorded in SPEC v0.18.
|
||||
- In flight:
|
||||
- This docs pass (`agent-docs-v31`: SPEC v0.30 and v0.31, D-140 to D-143, `strings.csv`, the web dictionaries, design-spec S-11 and §3.10, this file).
|
||||
- Web W1 to W5 (`web/src/features/**`, D-144 to D-154 reserved); the Pi image build with the web client and E-34/E-35.
|
||||
- Record D-155 (`d686ee0`, install failures visible on S-17 and S-22) in the next spec version, v0.32, with `strings.csv` at 590 rows (docs agent).
|
||||
- This docs pass (`agent-docs-v32`: SPEC v0.32, D-144 to D-155, design-spec §3.6, S-17, S-22, this file).
|
||||
- The Pi image build with the complete web app (in progress on the Pi), then the owner's deploy and browser checks (§3 steps 4 and 5).
|
||||
- Earlier outcomes (details in SPEC §11.2, §11.3 and `docs/decisions.md`): M8 end-to-end test passed on the owner's S26 against the dev server on 2026-10-18 (pairing, scripted partner, offline expense synced, push by `fid` delivered); M8b to M8f UI fixes; the M2c-pi bundle `infra/pi/` (D-103 to D-106, owner checklist in `infra/README.md` "Pi: what the owner does"); release builds R8-shrunk and signed (alias `kvitt`, RSA 4096, v2).
|
||||
- Compatibility: an APK from M12e on cannot pair or list devices against a pre-M12e server, so the server is rebuilt and redeployed before a new APK is installed (SPEC §14.8). The dev server already runs schema 3.
|
||||
- Remote: `origin` on Gitea, SSH key works; pushed at `7521c01`. The Gitea secret `kvitt_cicd` (a user-level secret of `jacobh2`, renamed by the owner 2026-10-19) works; the Drive token is `infra/secrets/rclone.conf` (`[gdrive]`; folder id in `.env` as `RCLONE_FOLDER_ID`). Unused on the Mac since D-120: `infra/secrets/ha_ssh_key` and `ha_ssh_key.pub` (git-ignored; may be deleted).
|
||||
- Both gates exist: Android `tools/gradle-serial.sh check` (from `android/`), server `cargo fmt --check && cargo clippy --all-targets -- -D warnings && cargo audit && cargo test` (from `server/`, `cargo audit` since `f1abb0d`). Both green on `main` as far as reported (Android green at `1c9856b`, which fixed the three `:data` example tests red since `50a0d67`; server 299 tests at `50a0d67`). The third gate (W0, D-131): `npm run check` in `web/` (`tsc --noEmit`, `eslint .`, `vitest run`, `vite build`, generated-types freshness); a change to `contracts/openapi.yaml` regenerates `web/src/api/schema.d.ts` in the same landing.
|
||||
- Both gates exist: Android `tools/gradle-serial.sh check` (from `android/`), server `cargo fmt --check && cargo clippy --all-targets -- -D warnings && cargo audit && cargo test` (from `server/`, `cargo audit` since `f1abb0d`). Both green on `main` as far as reported (Android green at `1c9856b`, which fixed the three `:data` example tests red since `50a0d67`; server 299 tests at `50a0d67`). The third gate (W0, D-131): `npm run check` in `web/` (generated files, `tsc --noEmit`, `eslint .`, Prettier, `vitest run`, `vite build`), green on `main` with W1 to W5 (59 files, 598 tests at `83f5293`); a change to `contracts/openapi.yaml` regenerates `web/src/api/schema.d.ts` in the same landing.
|
||||
- Importer: the dry run of 2026-10-18 reconciled on both real files (Vardag 1746 rows, Hemma 37 rows, 0 unmapped). The production import on the Kvitt Pi is done (owner, 2026-10-21). The CSV files stay in `export/` (git-ignored).
|
||||
- Development stack (M2c-dev): running on the Mac at `http://192.168.1.210:8080`, database at schema 3, data kept; backups `infra/dev/backups/kvitt-20261018T093143Z.sqlite.gz` and `kvitt-20261018T130443Z.sqlite.gz` (git-ignored). On this Podman setup run `infra/dev/up.sh --down`, then `infra/dev/up.sh`, to swap in a rebuilt image. Since `7fb86f5` `up.sh` copies only `fcm-service-account.json` into `infra/dev/secrets/` (git-ignored). The owner's phone is paired with it; a scripted partner exists (tokens held by the orchestrator outside the repo). Firebase project `kvitt-1337` with apps `se.hagstedt.kvitt` and `se.hagstedt.kvitt.debug`; service account `kvitt-fcm`.
|
||||
- Installed on the owner's phone (SM-S942B): the debug app `se.hagstedt.kvitt.debug`, paired against the dev server; the release app `se.hagstedt.kvitt`, rebuilt with the host `kvitt.suorra.duckdns.org` and no port (D-124) and installed 2026-10-19; paired against the Kvitt Pi 2026-10-21. The partner's phone is paired too.
|
||||
- Disk: the MacBook had about 72 GB free during the v0.20 docs pass (2026-10-19); run `tools/clean-builds.sh` when it drops under about 5 GB.
|
||||
|
||||
## 2. Open work (one line each, with who owns it: agent or owner)
|
||||
- Land `agent-docs-v31` (orchestrator): SPEC v0.30 and v0.31, D-140 to D-143, `strings.csv`, `web/src/i18n/*.json`, design-spec, HANDOFF; gate `python3 tools/contracts-check.py` (the web gate for the regenerated dictionaries).
|
||||
- Pi image with the web client and E-34/E-35 (built from the `1c9856b` push): watch the workflow run, then the owner deploys it (§3 step 4), then HUMAN step 3; then publish a release APK with W6 (`infra/publish-apk.sh --deploy`) so the phones show Insights.
|
||||
- Land `agent-docs-v32` (orchestrator): SPEC v0.32, D-144 to D-155, design-spec, HANDOFF; gates `python3 tools/contracts-check.py` and `npm run check` in `web/`.
|
||||
- Pi image with the complete web app (built from the push of the web plans): watch the workflow run, then the owner deploys it (§3 step 4) and checks the web client (§3 step 5); HUMAN step 3 if the NPM block is not yet current. Release APK `kvitt-140` (W6 Insights, D-155) is already deployed; its Insights need this image.
|
||||
- W0b web cleanup plan (agent, no plan yet): the "Web follow-ups" below.
|
||||
- Closed by `28fa71b`: the `/opt/kvitt` leftovers in `.gitea/workflows/build-images.yaml` and `infra/build-images.sh` (compose deploy hint, `docker image inspect`). Still stale in the workflow header (agent, next infra pass): "NOT YET RUN" and "First-build duration on the Pi: unknown" (now 2 h 42 min).
|
||||
- Pushed: through `1c9856b` (W6 Android Insights).
|
||||
- Pushed: through the five web plans (orchestrator, 2026-10-21).
|
||||
- Closed by `2acfc34` (`agent-gate-docs`): `infra/README.md`'s stale `CARGO_BUILD_JOBS` text and its "Owner decisions (open)" (now answered).
|
||||
- `infra/backup/Dockerfile`: stale comment "on the Pi the container runs as uid 10001" (since D-125 the owner's uid); left as is, because editing it triggers a 5-minute image build (agent, with the next change there).
|
||||
- Historical `:8443` and `icehack.asuscomm.com` mentions in `docs/ux/` and `docs/validation/` are left as the record of their time (no action unless the owner wants them rewritten).
|
||||
@@ -88,15 +96,30 @@ Living document, rewritten in place (never a second copy). A new orchestrator se
|
||||
- `PLAN-M2c-pi.md` is superseded where it names Caddy, cert-sync, scripts or `/opt/kvitt` (note at its top, D-120 and D-125); SPEC §10 and `infra/README.md` are current. `PLAN-M11.md` has a note that `/opt/kvitt/import` is now `~/kvitt/import`.
|
||||
- Plans (SPEC §11.2 v0.24), in dependency order; prompt `prompts/<id>.md`:
|
||||
- M1, M2a, M2b, M2c-dev, M3, M4, M5, M6, M6b, M7a to M7g, M8 (phone test passed), M8b to M8f, M9, M12a to M12f, U1, U2, R8 release build, M2c-pi bundle with its NPM rework and the self-contained compose project (D-125), server gap pass, infra overhaul, infra security, the server overhaul, the Android overhaul, the APK download rate limit, `cargo audit`, the Android host property and its follow-ups (D-122), the server host defaults (D-123), port 443 (D-124), pull-to-refresh (D-127), the editor keyboard fix, the S-12 line chart (D-128): landed.
|
||||
- Web client programme (D-134, SPEC §11.2 rows W0 to W7): W0 (`2ae65a5`), W6 (`1c9856b`) and W7 (`50a0d67`) landed; W1 to W5 running in parallel (briefs from `prompts/W1.md` to `prompts/W5.md`); their decisions D-144 to D-154 are recorded by a docs agent when they land.
|
||||
- Web client programme (D-134, SPEC §11.2 rows W0 to W7): all landed: W0 (`2ae65a5`), W1 (`934eee7`), W2 (`a65ab71`), W3 (`f9a9bfb`), W4 (`1aecf51`), W5 (`3ebe57d`), W6 (`1c9856b`), W7 (`50a0d67`); D-144 to D-154 recorded in v0.32. Next for the web: W0b (follow-ups below).
|
||||
- Next: the app key and gate if not yet done (D-126, §3); M10 First release (keystore per §10.6, D-101; the reports `first-release.md` and `restore-drill.md`). Done 2026-10-21: release pairing of both phones against the Kvitt Pi and M11 Import history (SPEC §10.8 "Production run").
|
||||
|
||||
### Web follow-ups (for a W0b plan; agent)
|
||||
- Shared pieces built two or three times in feature folders, to move into `web/src/components/` or `web/src/lib/`: an account tile (`features/accounts/AccountTile`, `features/analytics/components/AccountTile`); the loading and error cards (`PageStates` in `accounts`, `expenses`, `analytics`); `useToday` (`features/accounts/useToday.ts`, `features/analytics/shared.ts`); `categoryName`; the audit `Timeline` and its line builders (`features/expenses/Timeline.tsx`, `timelineLines.ts`, `features/settle/timeline.ts`); `apiErrorKey` and the rejection texts (`features/expenses/messages.ts`, `features/settle/messages.ts`); W1's `BalanceHero`, `Seesaw`, `AccountBalanceHeader`, `OverflowMenu` (D-145).
|
||||
- `components/AmountField`: reads `,` as the decimal point in English too (should follow the page language, as `lib/money.ts`); needs `hideLabel` and `placeholder` props.
|
||||
- `lib/ids.ts`: type the `random` parameter as `(bytes: Uint8Array<ArrayBuffer>) => void`.
|
||||
- Vitest cache `web/node_modules/.vitest-cache`: its stale `catalog.ts` result is fixed at the root by `83f5293` (glob without a type argument, `catalog.test.ts` compares with the files); still decide whether to disable the cache or key it on the feature i18n files.
|
||||
- `vite.config.ts`: the dev proxy lacks `/analytics` (W-11 under `npm run dev`); Recharts in its own chunk (the vendor chunk is 515 kB).
|
||||
- `app/routes.ts`: `bucket` in `DrillDownKind` (D-151); builders for `?account=<id>` and `?settlement=<id>` (D-144), replacing `features/accounts/links.ts` and `features/settle/links.ts`.
|
||||
- `lib/vectors.test.ts`: `spend_series` and `resolve_range` are still listed as not ported (their vectors run in `features/analytics/domain/analytics.vectors.test.ts`); move them to `PORTED` against the analytics port (`suggest_category` done in `83f5293`).
|
||||
- W5's recurring editor (`features/settings/recurring/fields.tsx`) uses its own fields; swap them for `components/editor` (D-148).
|
||||
- `editor_paid_by_toggle` says "Dubbeltryck" (TalkBack wording) on the web; a `web_` variant for a click.
|
||||
- `web/package.json` `kvitt.versionCode` is fixed at 1 (`X-Kvitt-App-Version`, W-19); PLAN-W0 wanted the commit count at build time.
|
||||
- `/app/settings/invites` is kept as an alias of `/app/settings/invite` (D-153); drop it if nothing links there.
|
||||
- Closed by `83f5293`: the fake test server keeps non-JSON bodies raw.
|
||||
|
||||
## 3. HUMAN steps pending (relay in chat)
|
||||
Done 2026-10-21: the Pi install (stack running, `v1.0.1`, both containers healthy), the public health check through NPM, bootstrap, release pairing of both phones, the Splitwise import.
|
||||
1. App key (D-126; `infra/README.md` step 4a items 1 to 3; the key exists on the Mac and the Pi serves the keyed `kvitt-133.apk` since the first `--deploy` run, D-139; whether both phones run it is unconfirmed): generate `kvitt.appKey` in `android/local.properties` on the Mac (an agent may run the one-line `openssl` command; the value is never printed); build and publish a keyed APK (`infra/publish-apk.sh`, copy to `data/apk/`); both phones on it (update banner, or sideload).
|
||||
2. Gate (step 4a items 4 and 5), only after both phones run the keyed APK: NPM, Hosts, Proxy Hosts, Kvitt row, Edit, Advanced: paste the step 4 block with the key in place of `KVITT_APP_KEY`, Save (row Online, yes/no). Acceptance test: on home Wi-Fi Chrome opens `https://kvitt.suorra.duckdns.org/i/TEST` and the invite page shows (yes/no); with Wi-Fi off the same URL gives a connection error (yes/no); with Wi-Fi off Kvitt's About screen shows the server version (yes/no).
|
||||
3. Web client gate lines (D-130, SPEC v0.28): when the NPM block is pasted (item 2, or again if the D-126 block is already in NPM), paste the current `infra/npm/kvitt.conf` content (step 4), which also lets the web client through and sets its Content-Security-Policy; Save (row Online, yes/no). After the first image with the web client is deployed (W7, §10.10), acceptance test with Wi-Fi off on the phone: `https://kvitt.suorra.duckdns.org/app/` shows the Kvitt login page (yes/no) and `https://kvitt.suorra.duckdns.org/health` still gives a connection error (yes/no) (step 4a item 5 d and e).
|
||||
4. Deploy the new image (web client and E-34/E-35, SPEC §10.10), once the Gitea workflow run for `1c9856b` has succeeded: on the Kvitt Pi in `~/kvitt`, set `KVITT_TAG` in `.env` to the tag the run printed, then `docker compose pull && docker compose up -d`; `server` and `backup` healthy (yes/no). Then the orchestrator publishes the release APK with Insights.
|
||||
4. Deploy the new image (the complete web app and E-34/E-35, SPEC §10.10), once the Gitea workflow run (in progress on the Pi) has succeeded: on the Kvitt Pi in `~/kvitt`, set `KVITT_TAG` in `.env` to the tag the run printed, then `docker compose pull && docker compose up -d`; `server` and `backup` healthy (yes/no). The release APK `kvitt-140` is already deployed; S-11's Insights work once this image runs.
|
||||
5. Web client in a browser (after step 4): on the phone, Inställningar › Para ihop en enhet › För mig, create a code; on the Mac open `https://kvitt.suorra.duckdns.org/app/`, "Logga in i Kvitt", type the code, log in: Home shows (yes/no). Then, each yes/no: Home and one account open; add, edit and delete an expense; Sök, export a CSV and open it in Numbers (columns and amounts right); Gör upp: record a payment and delete it; Statistik, open a category row (line chart), Insikter; each Settings page opens; on the phone Inställningar › Enheter shows `Web · <browser>`, "Ta bort åtkomst" on it, and the browser shows the login page with the removed-device message.
|
||||
|
||||
## 4. Owner questions (asked, unanswered)
|
||||
- OQ-6 (SPEC §12): a confirm step on S-1 for an unusual server; default "not in v1".
|
||||
@@ -108,9 +131,9 @@ Done 2026-10-21: the Pi install (stack running, `v1.0.1`, both containers health
|
||||
- Nothing.
|
||||
|
||||
## 6. Numbering
|
||||
- Decisions: D-001 to D-143 used, except D-008 and D-009 (never used). Reserved: **D-144 to D-154** for the web feature agents W1 to W5, **D-155** for the APK install-failure fix. Next free after those: **D-156**.
|
||||
- Spec version: v0.31 written (v0.30 D-140, v0.31 D-141 to D-143). Next **v0.32**.
|
||||
- Server schema version **3** (migration 0003); next **4**. Room schema version **1** (`android/data/schemas/se.hagstedt.kvitt.data.db.KvittDatabase/1.json`); next **2**. Unchanged by v0.27 to v0.31 (W7 and W6 needed no migration).
|
||||
- Decisions: D-001 to D-155 used, except D-008, D-009 and D-150 (never used). Next free: **D-156**.
|
||||
- Spec version: v0.32 written (D-144 to D-155). Next **v0.33**.
|
||||
- Server schema version **3** (migration 0003); next **4**. Room schema version **1** (`android/data/schemas/se.hagstedt.kvitt.data.db.KvittDatabase/1.json`); next **2**. Unchanged by v0.27 to v0.32 (no plan since needed a migration).
|
||||
- Sync protocol **1**, push payload **1**.
|
||||
|
||||
## 7. How the work is run
|
||||
|
||||
Reference in New Issue
Block a user