SPEC v0.7: M9 importer decisions (D-044 to D-046), category table, HANDOFF rewritten

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
This commit is contained in:
2026-10-18 07:51:04 +02:00
parent 50339aef6d
commit 86e08a12cf
6 changed files with 61 additions and 43 deletions
+5 -2
View File
@@ -6,7 +6,7 @@ The Rust backend of Kvitt (SPEC §6.3): one Cargo workspace with three crates.
|---|---|---|
| `crates/kvitt-domain` | library, no I/O | Split, balance, allocation and settlement rules (SPEC §9.1, §9.3, §9.4, §14.5) and `next_occurrence_date` with the day clamping of §9.5. Passes every vector in `contracts/test-vectors/`. |
| `crates/kvitt-server` | binary `kvitt-server` plus `lib.rs` | Config, SQLite pool and migrations, HTTP API, auth, CLI. |
| `crates/kvitt-import` | binary `kvitt-import` | Splitwise importer (SPEC §10.8, milestone M9). In M2a it prints its usage and exits 2. |
| `crates/kvitt-import` | binary `kvitt-import` plus `lib.rs` | Splitwise importer, implemented per SPEC §10.8 (M9, D-044 to D-046): one CSV export per run into one account, in one transaction that commits only when every row is valid and the account balance reconciles with the file's totals rows. Exit codes: 0 ok, 1 database or file error, 2 usage, 3 cannot import, 4 reconciliation mismatch. Category table in `crates/kvitt-import/data/splitwise_categories.csv`. |
The Rust toolchain is pinned in `rust-toolchain.toml` (rustup installs it on first use); `Dockerfile` uses the same
version.
@@ -112,7 +112,7 @@ the wire contract makes `tests/openapi_contract.rs` fail against `contracts/open
`Period` (`YYYY-MM`, `FromStr`/`Display`, `next()`, `days()`, `clamped_date(day_of_month)`),
`next_candidate_period(start_period, end_period, last_generated_period) -> Result<Option<Period>, RecurrenceError>`
and `next_occurrence_date(&RecurrenceSchedule) -> Result<Option<String>, RecurrenceError>`.
* **Importer (M9):** `kvitt-import` already depends on `kvitt-domain` and `kvitt-server` (the library).
* **Importer (M9):** `kvitt-import` depends on `kvitt-domain` and `kvitt-server` (the library); it reuses the server's validation, row helpers and `SqlAuditWriter`, and never opens a database it would have to create or migrate (SPEC §10.8).
## Tests and the gate
@@ -135,6 +135,9 @@ From `server/`: `cargo fmt --check && cargo clippy --all-targets -- -D warnings
(an RSA key is generated per test; none is committed).
* `crates/kvitt-server/tests/scheduler_daily/` (M4): 06:00 and 04:00 across both DST changes, run at start, clock
jumps, catch-up through `generate_all_due`, paused and deleted templates skipped, 90-day pruning.
* `crates/kvitt-import/tests/import.rs` (M9): both header variants, split and settlement rules, `--fx`, the import key and
idempotent re-runs, reconciliation and its exit codes, dry run, the CLI, and the §10.8 category table against
`data/splitwise_categories.csv`; synthetic fixtures in `crates/kvitt-import/tests/fixtures/` only.
The M4 binaries reuse M3's harness (`tests/core_api/common.rs`) and `tests/sync_api/m4.rs` through `#[path]`. No `unwrap`/`expect` in non-test code (a crate-level `deny` enforces it under clippy).