Files
kvitt/PLAN-M11.md
T
jacobh2 d9e189abd5
Build Kvitt images / build (push) Successful in 5m45s
SPEC v0.24: D-125 self-contained Pi compose project; workflow results
D-125 (owner decision 2026-10-20, landed 6e5981d): the Kvitt Pi runs Kvitt
from /home/jacobhagstedt/kvitt, a self-contained compose project with
relative bind mounts, both containers as the owner's uid/gid, the seven
.env keys compose reads, no scripts and nothing under /opt; operations are
plain docker compose commands. Revises D-103, D-105, D-117 point 5 and
D-120 point 6. SPEC §10.2, §10.6, §10.8 to §10.10, §11.2 and §14.7
rewritten; KVITT_PI_HOST placeholders become <kvitt-pi-address>.

§10.3 records the Gitea workflow's first results (run 69: v1.0.0, backup
5 min 20 s, server 2 h 42 min; v1.0.1 from the sparse-checkout push) and
the sparse checkout of 3820f9c. HANDOFF, prompts/README.md and notes in
PLAN-M11.md and PLAN-M2c-pi.md follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
2026-10-20 19:29:35 +02:00

7.5 KiB

PLAN-M11: Import history

Path changed by D-125 (SPEC v0.24, 2026-10-20). /opt/kvitt/import is now ~/kvitt/import (/home/jacobhagstedt/kvitt/import), and every Pi command runs in ~/kvitt; SPEC §10.8 "Production run" is current.

1. Goal

Run the Splitwise importer once against the production database with the owner's two real exports, so six years of history appear on both phones. Prove it by comparing the imported per-account balances with Splitwise's own total rows and record the comparison, aggregates only, in docs/validation/splitwise-import.md.

2. Read first

  1. SPEC §10.8 Splitwise import, §4 FR-17 Splitwise import.
  2. SPEC §12 OQ-2 (answered) and docs/decisions.md D-003.
  3. SPEC §10.8 "Production run" (D-025), §10.2 (the /opt/kvitt/import mount), §10.9 Backups and restore (backup run), §10.4 (running commands on the Pi), §10.10 Operations.
  4. SPEC §9.3 Balances.
  5. SPEC §11.2 (row M11), §14.7.
  6. PLAN-M9.md §3 (mapping notes: header lines and member names), infra/README.md, infra/RESTORE.md.
  7. CLAUDE.md (ANTI-PATTERNS: never commit personal data or the Splitwise CSV; THE OWNER: confirm before anything destructive; EXECUTION PROTOCOLS: Reports are deliverables).

3. Already established

  • Files: export/export_hemma.csv (English variant, group "hemma") into account "Hemma"; export/export_general.csv (Swedish variant, general group, about 1750 rows) into account "Vardag"; both in the main checkout, git-ignored, all SEK (D-003).
  • Command per file: kvitt-import --db /data/kvitt.sqlite --file <csv> --account "<name>" --user "<Splitwise name of the owner>=<owner user id>" --user "<Splitwise name of the partner>=<partner user id>" [--dry-run], run on the Pi with the server stopped as docker compose run --rm --entrypoint kvitt-import server … (§10.8 "Production run", D-025). Names exactly as in the file headers (PLAN-M9 mapping notes).
  • Re-running creates nothing new (import key); imported rows get audit entries with source import and no pushes (AC-17.2, AC-17.6).
  • Unknown categories map to other and are counted as a warning (§10.8).
  • Splitwise's trailing Total balance / Totalsumma row is ignored by the importer and is the reference for the comparison (§10.8, §11.2).
  • Reports carry aggregates only; no descriptions, no individual amounts, no names beyond the account names (CLAUDE.md).

4. Owned paths

From SPEC §14.7, verbatim: docs/validation/<file> | "the milestone that names the file", here docs/validation/splitwise-import.md.

Edit nothing outside these paths; a needed change elsewhere is a contract request to the orchestrator.

Never edit SPEC.md or docs/decisions.md: put proposed decision entries in the report.

Resolved open points (SPEC v0.3, D-025):

  • (a) The CSVs are copied from the Mac to the Pi into /opt/kvitt/import/, bind-mounted read-only into server as /import; the import runs on the Pi as docker compose run --rm --entrypoint kvitt-import server --db /data/kvitt.sqlite --file /import/<file> … (--entrypoint because the image's entrypoint is kvitt-server); the CSVs are deleted from the Pi afterwards.
  • (b) A backup is taken first (docker compose exec backup /backup.sh, confirmed on Drive).
  • (c) The production user ids are resolved by display name from the database (read-only SELECT id, role, display_name FROM users through the backup container's sqlite3); the owner is the admin user.

5. Deliverables

  • docs/validation/splitwise-import.md: per file: rows read, expenses, settlements, skipped duplicates, unmapped category names with counts; per account: the imported balance (net from the owner's side) and Splitwise's total-row balance, equal or not; the re-run result (nothing new); the phones' confirmation; a "Results (owner's phone)" section if anything is still to come.
  • Production database on the Pi with the imported history (not in git).

6. Steps

  1. Worktree. From the main checkout /Users/jhag/projects/kvitt: git worktree add .claude/worktrees/agent-m11 -b agent-m11 main, then in .claude/worktrees/agent-m11: git reset --hard main. Copy infra/secrets/ from the main checkout if SSH to the Pi needs it. Do not copy export/ into the worktree; read the files from /Users/jhag/projects/kvitt/export/. Run tools/install-git-hooks.sh.
  2. Read §2 in order. Confirm M9 and M10 have landed and production is healthy; if not, stop and report.
  3. Backup (resolved point b), resolve the user ids (resolved point c), copy the CSVs to /opt/kvitt/import/, stop server, and dry-run each file with the command of resolved point (a) and --dry-run; start server again; record the aggregates; stop and ask if any row is invalid, any currency is not SEK, or unmapped categories are more than the owner expects.
  4. Confirm with the owner (through the orchestrator) before writing to production. If anything was written since the backup of step 3, take a new one (docker compose exec backup /backup.sh, confirmed on Drive).
  5. docker compose stop server; run the import for "Hemma" then "Vardag" with the command of resolved point (a); docker compose start server; check /health. Delete the CSVs from /opt/kvitt/import/.
  6. Re-run both imports with --dry-run (copy the CSVs again, delete them afterwards): zero new rows expected.
  7. Compute each account's balance from the database with the §9.3 definition (or via the app's numbers pulled over USB), and compare with the total row of each file (parsed by a script that prints only the two member totals); record equal or not.
  8. The phones sync; the owner confirms the balances (§8).
  9. Write docs/validation/splitwise-import.md. Commit on agent-m11 (only that file; git status shows no CSV), git rebase main. Never merge, push or remove the worktree.

7. Gate

From SPEC §11.2, verbatim: "Imported balances equal Splitwise's "Total balance" rows per group". Green means: for "Hemma" and "Vardag" the imported balance equals the file's total row (Total balance in the English file, Totalsumma in the Swedish file) to the öre, recorded in docs/validation/splitwise-import.md; the re-run dry run reports zero new rows.

8. HUMAN steps

From SPEC §11.2, verbatim: "Provide the CSVs; confirm the balances on the phone match Splitwise".

  • Provide the CSVs: done for both files (export/, D-003). If the owner exports newer files before the run, they replace these in export/ before step 3.
  • Before step 5: the owner says yes to writing into production (CLAUDE.md, destructive-data rule).
  • After step 8, on each phone: open Kvitt Home and Splitwise side by side; answer yes/no: "Hemma" and "Vardag" show the same balance and direction as Splitwise.

9. Out of scope

  • Changing the importer (M9; findings go in the report as new work).
  • Importing other groups or later Splitwise data.
  • Editing imported rows by hand; deleting anything.
  • Committing, copying into the repository, or quoting the CSVs.

10. Report

  • Branch agent-m11, head commit hash after rebasing on main.
  • Gate: the per-account comparison lines (equal or not) and the re-run result.
  • Dry-run aggregates per file; unmapped category names with counts.
  • Backup stamp taken before the import.
  • How resolved points (a) to (c) were applied.
  • Proposed decision entries (never written to SPEC.md or docs/decisions.md by this agent) or TODO(spec) occurrences.
  • HUMAN steps done and outstanding, worded for relay.
  • Contract requests, open questions, spec gaps (with section numbers).

11. Numbering

Decision ids, spec version and schema version are assigned by the orchestrator in the start prompt; use TODO(spec) if none was given.