Implement hosted Phase/Extension Registry (WP-0006-T03)

Adds migrations/0001_registries.sql (licensors, phase_manifests,
extensions tables; trf_app role with no UPDATE/DELETE grant on either
table, canonicalization only via a SECURITY DEFINER function), and
src/target_revenue/registry.py + service/app.py: a thin FastAPI layer
wrapping the existing validation.py checks with persistence and
per-Licensor token auth, adding no new validation logic per ADR-0002.
New optional service/service-dev dependency groups keep the core
offline library dependency-free. tests/test_registry_hosting.py (7
tests, Docker-gated, auto-skip otherwise) spins an ephemeral disposable
Postgres container and verifies registration, rejection, duplicate/
unknown-token handling, extension canonicalization, and two explicit
database-privilege checks that the app role cannot bypass the
append-only/governance-gated guarantees.
This commit is contained in:
tegwick 2026-07-29 21:03:52 +02:00
parent e8e8629efd
commit 7e0c62a8b5
8 changed files with 580 additions and 2 deletions

View file

@ -108,7 +108,7 @@ thin HTTP layer. T03T08 may now build against this decision.
```task
id: TREV-WP-0006-T03
status: todo
status: done
priority: high
state_hub_task_id: "fb9f00e5-5755-44e4-ad07-ba0dc73596b8"
```
@ -120,6 +120,29 @@ than one golden fixture. Reject non-conformant manifests/extensions at
registration, exactly as the Stage 0 validators already do — this task
adds multi-tenancy and persistence, not new validation logic.
**Result:** Implemented per ADR-0002. `migrations/0001_registries.sql`
creates `licensors`, `phase_manifests`, `extensions` tables, a
`trf_app` role with **no UPDATE/DELETE grant** on `phase_manifests` or
`extensions` (database-enforced append-only, not just application
convention), and a `set_extension_status()` SECURITY DEFINER function as
the only sanctioned path to `canonical`/`deprecated` — canonicalization is
therefore a recorded, attributable governance action the application role
cannot perform via ordinary UPDATE. `src/target_revenue/registry.py` wraps
the existing `validation.py` checks (no new validation logic) with
persistence and per-Licensor token authentication. `src/target_revenue/service/app.py`
exposes a thin FastAPI surface (`POST/GET /phases`, `POST/GET /extensions`).
Added a `service`/`service-dev` optional-dependency group
(`pyproject.toml`) so the core offline library keeps zero new hard
dependencies. `tests/test_registry_hosting.py` (7 tests, requires Docker,
auto-skips otherwise) spins an ephemeral, disposable Postgres container —
never the shared state-hub `infra-postgres-1`/`custodian` instance —
covering registration, rejection with diff, duplicate-`phase_id` rejection,
unknown-token rejection, extension canonicalization via the governance
function, and two explicit database-privilege tests proving `trf_app`
cannot UPDATE/DELETE `phase_manifests` or UPDATE `extensions` directly.
Original 36-test offline suite verified unchanged and passing with plain
system Python (no service deps required).
## Hosted Target Ledger append API
```task