target-revenue/migrations/0003_attestations.sql
tegwick 28f0f429b2 Implement hosted Conversion Attestation publication (WP-0006-T06)
migrations/0003_attestations.sql: one row per Phase, no UPDATE/DELETE
grant for trf_app (a published attestation is permanent).

src/target_revenue/attestation.py: publish_attestation() is idempotent
(existing row returned unchanged, never regenerated) and derives
conversion_timestamp from the ledger itself via _find_conversion_prefix(),
which finds the earliest prefix where the fold reaches Outstanding
Target = 0 rather than trusting entries[-1] or wall-clock publish time -
so a later unrelated entry can never change an already-converged Phase's
recorded conversion moment. Raises NotConvertedError rather than
fabricating a conversion. Reuses conversion.generate_attestation()
unchanged. Guarded the psycopg import behind TYPE_CHECKING so the pure
helper stays testable under plain system Python.

service/app.py adds GET /phases/{id}/attestation (unauthenticated,
publish-on-first-observation).

tests/test_attestation.py (3 tests, no Docker/Postgres) proves the
earliest-crossing behavior. 6 new Docker-gated tests in
test_ledger_hosting.py cover pre-conversion 404, the core
legal-technical property that /metrics facts/calculations/forecasts
are identical whether or not /attestation was ever called, one-time
publication, signature verification, and DB-level UPDATE/DELETE
rejection.
2026-07-29 21:50:02 +02:00

24 lines
1,023 B
PL/PgSQL

-- WP-0006-T06: Conversion Attestation publication.
-- Depends on migrations/0001_registries.sql and 0002_ledger.sql.
BEGIN;
-- One attestation per Phase, ever. A Phase converts at most once (there is
-- no "un-converting"), so this table has no update path by design, not
-- merely by omitted grant: publish_attestation() in
-- src/target_revenue/attestation.py treats an existing row as the
-- authoritative, already-published record and never attempts to replace it.
CREATE TABLE IF NOT EXISTS attestations (
phase_id text PRIMARY KEY REFERENCES phase_manifests(phase_id),
attestation jsonb NOT NULL,
signature text NOT NULL,
published_at timestamptz NOT NULL DEFAULT now()
);
GRANT SELECT, INSERT ON attestations TO trf_app;
-- Deliberately no UPDATE, no DELETE for trf_app: a published attestation is
-- a permanent historical record of an already-true fact, never subject to
-- revision (ADR-0002 compensating guardrail pattern, same as
-- phase_manifests and ledger_entries).
COMMIT;