SPEC v0.3: orchestrator resolutions of the plans' open points

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>
This commit is contained in:
2026-10-17 21:23:38 +02:00
parent c885aba313
commit 76ee957f41
45 changed files with 501 additions and 342 deletions
+14 -9
View File
@@ -6,7 +6,7 @@ Run the Splitwise importer once against the production database with the owner's
## 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.9 Backups and restore (backup run), §10.4 (running commands on the Pi), §10.10 Operations.
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`.
@@ -14,7 +14,7 @@ Run the Splitwise importer once against the production database with the owner's
## 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, or on the Mac against a copy of the database (§10.8). Names exactly as in the file headers (PLAN-M9 mapping notes).
- 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).
@@ -25,7 +25,12 @@ From SPEC §14.7, verbatim: `docs/validation/<file>` | "the milestone that names
Edit nothing outside these paths; a needed change elsewhere is a contract request to the orchestrator.
Open points the orchestrator must settle in the start prompt's notes: (a) how the CSV files reach the importer on the Pi (the `server` image's entrypoint is `kvitt-server`; the CSVs must be mounted or copied, then removed), or whether the import runs on the Mac against a copy of the database that is then restored on the Pi; (b) whether to take a backup immediately before the import (recommended by analogy with §10.10, not stated for imports); (c) the owner's and partner's production user ids (from `GET /me` or the database).
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.
@@ -34,10 +39,10 @@ Open points the orchestrator must settle in the start prompt's notes: (a) how th
## 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. Dry run per file against a copy of the production database (on the Mac or the Pi per open point a); 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. Backup per open point (b): `docker compose exec backup /backup.sh`, confirmed on Drive.
5. `docker compose stop server`; run the import for "Hemma" then "Vardag" per open point (a); `docker compose start server`; check `/health`. Remove any CSV copied to the Pi.
6. Re-run both imports with `--dry-run`: zero new rows expected.
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.
@@ -63,8 +68,8 @@ From SPEC §11.2, verbatim: "Provide the CSVs; confirm the balances on the phone
- 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 open points (a) to (c) were resolved.
- Numbering used or `TODO(spec)` occurrences.
- 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).