Files
kvitt/PLAN-M1.md
T
jacobh2 5e1be38364 Plans and start prompts for every milestone; SPEC v0.2
PLAN-<id>.md for U1, U2, M1, M2a, M2b, M2c-dev, M2c-pi, M3, M4, M5, M6,
M7a-M7f, M8, M9, M10, M11, each with the eleven fixed sections, and a
start prompt per plan in prompts/ plus prompts/README.md (dependency
order and parallel waves).

SPEC v0.2: owner answers D-001 (development-first, M2c split into
M2c-dev and M2c-pi, Gitea registry), D-002 (local backups deleted once
on Drive), D-003 (Splitwise CSV format, English and Swedish variants,
category mapping), OQ-1, OQ-2, OQ-4 answered; M1's readings of
ambiguous contract points stated explicitly in §14.3 to §14.5.
HANDOFF §2 lists the plans.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 21:05:34 +02:00

6.9 KiB

PLAN-M1: Contracts and vectors

On record only: M1 was implemented in parallel by an agent working directly from SPEC §14 and §9 and landed on main as c6df2ab; its readings of ambiguous points are stated in SPEC v0.2. This plan describes the same scope; if the two disagree, SPEC wins.

1. Goal

Write the cross-team contract files from SPEC §14: the OpenAPI document, the SQLite schema v1, the shared test vectors, the JSON examples, and a stdlib-only Python checker. After M1 lands, server and Android agents work in parallel against these files without talking to each other.

2. Read first

  1. SPEC §14 Contracts: §14.1 SQLite schema v1, §14.3 OpenAPI 3.1 outline (14.3.1 to 14.3.5), §14.4 Sync protocol v1 (examples), §14.5 Domain rules as pure functions (14.5.1 to 14.5.5), §14.6 Push payload v1, §14.7 Module layout and ownership map, §14.8 Contract files and the change rule.
  2. SPEC §9.1 Splits, §9.3 Balances, §9.4 Settlement allocation proposal, §9.6 Limits and validation.
  3. SPEC §7 API outline (endpoint ids E-1 to E-33).
  4. SPEC §11.2 (row M1) and §11.3 (note M1).
  5. CLAUDE.md.

3. Already established

  • The OpenAPI file is written by hand from §14.3, OpenAPI 3.1.0, info.title Kvitt, info.version 1, one server https://icehack.asuscomm.com:8443, security scheme device = HTTP bearer; the outline is binding and the YAML adds or drops nothing (§14.3, §11.3).
  • contracts/schema/server-v1.sql is the §14.1 DDL byte for byte; M2a's 0001_init.sql must equal it (§14.1).
  • Vector files, one per function: compute_split.json, compute_balances.json, propose_allocation.json, validate_settlement.json, over_settled_accounts.json, spend_series.json, resolve_range.json; format of §14.5.5; minimum cases listed in §14.5.5.
  • Examples: one per request and response body of §14.3.4 in contracts/examples/<E-id>-<name>/{request,response}.json, one per error code in contracts/examples/errors/<code>.json, the sync examples of §14.4 in contracts/examples/sync/, push examples in contracts/examples/push/ (§14.3.5, §14.7).
  • Error codes are exactly the 25 of §14.3.2; error tokens exactly those of §14.5.3.
  • Ratio rule rounds the non-payer down; payer absorbs the remainder (§9.1). Proposal algorithm §9.4 is normative.
  • A contract change after landing needs a spec bump and a decisions entry (§14.8).

4. Owned paths

From SPEC §14.7, verbatim: contracts/**, tools/contracts-check.py (owner M1; after the owner lands: contract-change plans only (§14.8)).

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

5. Deliverables

  • contracts/openapi.yaml: OpenAPI 3.1.0 document of §14.3 (every endpoint E-1 to E-33, wire objects, error responses).
  • contracts/schema/server-v1.sql: the §14.1 DDL, byte for byte.
  • contracts/test-vectors/compute_split.json, compute_balances.json, propose_allocation.json, validate_settlement.json, over_settled_accounts.json, spend_series.json, resolve_range.json: vectors per §14.5.5 with at least its minimum cases.
  • contracts/examples/<E-id>-<name>/{request,response}.json: one per request and response body of §14.3.4.
  • contracts/examples/errors/<code>.json: one per error code of §14.3.2.
  • contracts/examples/sync/*.json: pull page, push request, push response with accepted and rejected results (§14.4).
  • contracts/examples/push/*.json: one FCM message per push type of §14.6.
  • tools/contracts-check.py: stdlib-only checker (JSON parses, vector format, Python reference of §9.1, §9.3, §9.4, example key sets against §14.3 shapes).

6. Steps

  1. Worktree. From the main checkout /Users/jhag/projects/kvitt: git worktree add .claude/worktrees/agent-m1 -b agent-m1 main, then in .claude/worktrees/agent-m1: git reset --hard main. No git-ignored files are needed; copy none. Run tools/install-git-hooks.sh.
  2. Read §2 in order.
  3. Copy the §14.1 DDL into contracts/schema/server-v1.sql exactly (no trailing changes); verify with a byte comparison against the block extracted from SPEC.md.
  4. Write tools/contracts-check.py first (Python 3 stdlib only): JSON parse of every file under contracts/; vector envelope (format 1, function, cases with name, input, expected holding ok or error); reference implementations of compute_split, compute_balances, propose_allocation, validate_settlement, over_settled_accounts per §9.1, §9.3, §9.4 and §14.5.2 recomputing every expected value; key-set validation of each example against the wire shapes of §14.3.3 and the endpoint bodies of §14.3.4.
  5. Write the vector files with the §14.5.5 minimum cases (and the §9.1 example table rows); run the checker after each file. For spend_series and resolve_range (Kotlin only) the checker validates format and that dates are well formed; state in the report whether it also recomputes them.
  6. Write the examples, using UUIDv7-shaped ids and the fixed timestamp form; run the checker.
  7. Write contracts/openapi.yaml from §14.3; cross-check every endpoint, status code and error list against §14.3.4 and the always-possible codes of §14.3.2. Python stdlib has no YAML parser: state in the report how the YAML was checked.
  8. Run the gate (§7). Commit on agent-m1, git rebase main, run the gate again. Never merge, push or remove the worktree.

7. Gate

From SPEC §11.2, verbatim: "python3 tools/contracts-check.py passes: all JSON parses, every vector file matches §14.5.5, a stdlib-only Python reference of §9.1, §9.3, §9.4 reproduces every expected value, every example validates against the shapes of §14.3 by key set". Command: python3 tools/contracts-check.py from the repository root. Green means exit code 0 and its summary line reporting zero failures.

8. HUMAN steps

From SPEC §11.2, verbatim: "none".

9. Out of scope

  • Any Rust or Kotlin code (M2a, M2b), including the utoipa annotations (M2a) and DTOs (M5).
  • The server migration file server/crates/kvitt-server/migrations/0001_init.sql (M2a).
  • The Room exported schema android/data/schemas/** (M5).
  • Changing §14 of the spec; anything that would is a gap for the report.
  • docs/, SPEC.md, CLAUDE.md, other tools/ files.

10. Report

  • Branch agent-m1, head commit hash after rebasing on main.
  • Gate: the checker's output summary lines (files, cases, examples, failures) from this session.
  • Count of vector cases per function; list of example directories.
  • How the YAML was checked; whether spend_series/resolve_range are recomputed.
  • Versions chosen: Python version used (no third-party packages).
  • Numbering used (decision ids, spec version) or TODO(spec) occurrences.
  • Open questions and spec gaps (any place §14 is ambiguous or self-contradicting), 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.