mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
The arrival-reaction claim carried its own copy of the reservation upsert so the write could ride the claim transaction. The copy drifted: it learned to write operator_tokens and kept gating on the day's mixed total, at the halved background ceiling. On the row the wave was written for, 1.2 million tokens served by the user's own plan, the reaction was refused all day at half the old ceiling, reproducing the defect the wave exists to remove on the one surface it forgot to convert. reserveBudget now takes the transaction client, so both writes still roll back together and there is no second copy left to drift. The row also remembers who paid for its reservation. The supersede refund in data-arrival has no chain to ask and could only reverse the total, so the operator share of every superseded reservation stayed on the row forever, walking the operator's ceiling upward against spend that never happened. A supersede is routine, not an edge case: any second measurement of the same kind on the same local date before the worker runs. The resume path re-derived the owner from a chain the health ledger may have reordered since, which leaks the same way or drains another request's balance. A structural guard makes the invariant executable. Every INSERT column list, SET clause and RETURNING list that names one counter must name the other, and the set of files holding raw ledger SQL is pinned. Both defects above were a grep away and both would have failed this test.
prisma/
The database layer. HealthLog runs PostgreSQL 16 via Prisma 7. This directory holds the single schema file and the forward-only migration history; the generated client is emitted elsewhere.
Contents
schema.prisma— 61 models, one file.cuid()primary keys,snake_casecolumns via@map(...), encrypted columns mostly suffixed*Encrypted(search the file; there are 24 encrypted column references). Thegenerator clientblock emits to../src/generated/prisma(imported as@/generated/prisma).migrations/— numbered, forward-only SQL migrations (0001_…upward).migration_lock.tomlpins the provider topostgresql.
Migration workflow
# author a migration from a schema edit
pnpm dlx prisma migrate dev --name <short_snake_case_name>
# regenerate the client after any schema change
pnpm dlx prisma generate
Number a new migration with the next free prefix. The production entrypoint runs prisma migrate deploy on boot, so every migration must apply cleanly forward — there is no down-migration path. Always include the migration file in the same change as the schema edit.
Conventions
- Encryption is fail-closed:
*Encryptedcolumns are written through@/lib/crypto(AES-256-GCM, versioned key ids); never store plaintext in an encrypted column. Key rotation isscripts/rotate-encryption-key.ts+docs/ops/encryption-key-rotation.md. - The generated client under
src/generated/prismais build output — do not edit it. - Schema-touching changes that affect the API surface must regenerate
docs/api/openapi.yaml(see../CLAUDE.md).
Related: ../src/lib/README.md (db.ts, rollups/), ../docs/ops/ (migrations + backup runbooks).