approval-engine/docs/storage-operations.md
tegwick 7e756773de Implement GH-DEC-2026-008: declared PDP-path intent, enforced at issue
Gate House ruled binding.pdp_digest is the binding correspondence on the
GH-DEC-2026-003 path and is required there, having rejected a vocabulary
mapping for the reasons we gave. It asked this engine to record the PDP
digest at issue for approvals intended for that path, and to have the
claim state which approvals those are rather than leaving it to the
requester's memory.

Schema v3 adds approvals.pdp_path. create() refuses pdp_path true without
a pdp_digest, so an approval that would be unusable on the path fails at
issue rather than at the protected side effect. The claim exposes
binding.pdp_path, which makes it a guarantee rather than a hint: pdp_path
true implies pdp_digest is non-null.

Intent is declared and never inferred. A pdp_digest that happens to be
present is not a declaration anybody made, so a recorded digest alone
leaves pdp_path false, legacy rows migrate to false rather than being
back-filled from their digests, and a successor inherits its
predecessor's declaration. Approvals issued before the ruling stay usable
by consumers in this engine's own vocabulary and are simply not usable on
the PDP path -- the ruling's intended cost, stated as such.

Schema, both published examples, a v2-to-v3 migration test asserting
survivors keep their digest while declaring no path intent, and tests for
refusal at issue, claim exposure, non-inference, and successor
inheritance. 102 tests pass (8 new).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TvyJPAaVCGsVheVhcCwNND

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 411227@bnt-lap001
Assistant-Session: d566f6d3-bcaf-43c3-bc5e-3ddd0f64b535
2026-09-06 14:51:23 +02:00

2 KiB

Storage operations

Production uses one SQLite writer on persistent ReadWriteOnce storage. Production serve disables automatic migration and refuses schema drift or an in-memory database.

approval-engine migrate --db /data/approvals.sqlite
approval-engine verify --db /data/approvals.sqlite
approval-engine backup --db /data/approvals.sqlite --output /backup/approval.sqlite

Migration is repeatable and sets an explicit PRAGMA user_version. Backup uses SQLite's online backup API, verifies PRAGMA integrity_check, writes mode 0600, and refuses to overwrite a target.

Restore is a stopped-single-writer operation:

  1. Stop the StatefulSet and confirm no approval-engine or migration process has the PVC open.
  2. Preserve the failed database and its -wal/-shm companions for analysis.
  3. Copy the verified backup to a new database path with owner 10001 and mode 0600. Do not merge a backup with old WAL files.
  4. Run verify, then migrate if the release schema is newer, then verify again.
  5. Start exactly one replica and prove approval/entry/outbox counts, readiness, claim retrieval, and idempotent audit drain before reopening callers.

Approval mutation and outbox insertion share BEGIN IMMEDIATE and one commit; a failed outbox insert rolls the mutation back. Delivery occurs afterward and does not roll back a committed mutation.

Schema v3 — declared PDP-path intent

GH-DEC-2026-008 added approvals.pdp_path (INTEGER NOT NULL DEFAULT 0). Migration is the usual additive ALTER TABLE; run approval-engine migrate before a production start, which refuses an unmigrated store.

Legacy rows default to 0. Intent is not back-filled from a recorded pdp_digest: an approval issued before the ruling was never declared for the PDP path, and inferring the declaration from an incidental digest would manufacture a statement nobody made. Such approvals stay usable by consumers in this engine's own vocabulary and are simply not usable on the PDP path — which is the ruling's intended cost, not a migration defect.