Server gap pass: invite-partner subcommand, CLI and drain tests, push info log (D-113, D-114)

- kvitt-server invite-partner: a partner invite by the admin as E-12 does (cancels the pending one, 72 h, link with
  ?from=<admin>), exit 2 household full, 3 no admin; README lines in server/ and infra/.
- Tests: invite-partner, migrate (schema at SCHEMA_VERSION, idempotent), healthcheck (200, 503, nothing listening),
  serve SIGTERM drain (in-flight request finishes, new connections refused, exit 0; gives up after SHUTDOWN_DRAIN).
- Push sender: one info line "push sent" per message with push_type, devices_sent, devices_targeted and 8-character
  device-id tails (SPEC §8.7 aggregates); per-device line stays at debug.
- tools/contracts-check.py: stale TODO(spec) replaced by the §9.11 CS-5 citation; shapes updated for invite_kind
  (D-086), app_version_name (D-090) and fid (D-096), 134 checks pass.

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 22:12:21 +02:00
parent e70fe0d9fe
commit 8660b7fa16
9 changed files with 528 additions and 18 deletions
+1
View File
@@ -53,6 +53,7 @@ database is then `$KVITT_DATA_DIR/kvitt.sqlite`. Every connection uses WAL, `for
| `healthcheck` | Works: `GET http://127.0.0.1:<port of KVITT_BIND>/health`, used by the compose healthcheck (no curl in the image). | 0 if 200, else 1 |
| `bootstrap` | Works (M3): cancels a pending `admin_setup` invite, prints a new code, link and expiry; `already initialized` on stderr when users exist. | 0 ok, 2 already initialized |
| `pair-device --user <user_id>` | Works (M3): prints a `device` invite for the user like `bootstrap`. | 0 ok, 3 unknown user |
| `invite-partner` | Works (D-113): what E-12 does for the admin on the server host: cancels a pending `partner` invite, prints a new code, a link naming the admin (`?from=`, D-087) and the expiry; `household full` or `no admin, run bootstrap first` on stderr otherwise. | 0 ok, 2 household full, 3 no admin |
| `rotate-epoch` | Works (M4): sets `meta.sync_epoch` to a new random value and prints it (SPEC §10.9 restore step 5). | 0 ok, 1 database error |
## HTTP API