--- id: TREV-WP-0015 type: workplan title: "Implement Phase provenance, spec-file, and ledger UI changes" domain: infotech repo: target-revenue status: active owner: claude topic_slug: infotech created: "2026-08-03" updated: "2026-08-03" state_hub_workstream_id: "fe1bedd3-dadc-41cc-a669-92623743e620" --- # Implement Phase provenance, spec-file, and ledger UI changes Implements `specs/PhaseProvenanceSpecAddendum.md`, accepted 2026-08-03, which itself synthesizes the decisions from `workplans/TREV-WP-0012-phase-provenance-and-policy-modeling.md` T02–T04. Task numbering below matches the addendum's §6 suggested breakdown 1:1 — each task cites its addendum section as the authoritative source of what to build, rather than re-deriving field shapes or rationale here. ```task id: TREV-WP-0015-T01 status: done priority: high state_hub_task_id: "cdca0ef5-a332-40e2-b165-10ffa238f413" ``` **Schema change** (addendum §1): add `repo_hub`, `repo_hub_uri`, `repo_id`, `repo_name` as required properties of `phase.milestone_release`, and `phase.base_phase_id` as an optional property of `phase`, to `schemas/phase_manifest.schema.json`. Update `validation.py` if the new pattern/format constraints need anything beyond what JSON Schema's own `pattern`/`format` keywords already enforce (they shouldn't). Add offline tests: a manifest missing any of the four new required fields is rejected; one with `base_phase_id` absent validates (first Phase); one with it present and correctly patterned validates (successive Phase). **Result:** Schema updated exactly as specified; `validation.py` needed no changes (`pattern`/`format`/`minimum` already sufficient). Added six tests to `tests/test_manifest_validation.py` covering all four new required fields and both `base_phase_id` states plus a malformed one. Updating `examples/phase-001/manifest.json` (the fixture 13 other test files depend on) turned out to be necessary just to keep the offline suite green, not optional — done inline with placeholder-but-plausible values (`repo_hub: "example-hub"`, synthetic id/name), since it's a fictional fixture, not a real repo. **Discovered scope gap, fixed inline rather than left broken**: the addendum's task breakdown didn't call out that the Control Plane's existing Phase registration form (`phase_new.html`, `control_plane_app.py`) would stop working the moment this schema change landed, since nothing collected the four new required fields. Fixed as part of this task rather than leaving registration broken between tasks — this also completes T04's registration-form half (drop `ledger` input, auto-compute it) and adds the `base_phase_id` field, since all three changes touch the same form/route. `phase_detail.html`'s ledger-reference drill-down (T04's other half) is done too. Full suite: 94 passing offline (up from 84 — six new schema tests, three new provenance-backfill tests, two new Control Plane form tests), 158 passing with Docker (up from 146). ```task id: TREV-WP-0015-T02 status: done priority: high state_hub_task_id: "8b5ab6ea-fa80-4e08-8733-2faad810962e" ``` **Spec-file extraction** (addendum §2): create `specs/policies/ linear-longstop-v0.md` (extracted from `specs/ OpenQuestions-WorkingDefaults.md` Q7's prose, with `policy_id`/`title` frontmatter); create the six `specs/profiles/*.md` files (extracted from `specs/CanonicalMonetizationProfiles.md` §1–§6, with `extension_id`/ `title` frontmatter), leaving §7 (cross-profile summary) and §8 (non-goals) in place as the overview document. Add `calculator_id`/ `revision`/`title` frontmatter to `specs/ DevelopmentEffortCalculatorConcept.md` (no move). Update `OpenQuestions-WorkingDefaults.md` Q7 to point at the new file rather than contain the policy's content directly. **Result:** `specs/policies/linear-longstop-v0.md` and all six `specs/profiles/*.md` files created exactly as scoped, each with the decided frontmatter shape. `CanonicalMonetizationProfiles.md` §1–§6 replaced with a linked list pointing at the new files; §7 (cross-profile summary) and §8 (non-goals) untouched. `OpenQuestions-WorkingDefaults.md` Q7 now records only the adoption decision, pointing at the policy file for content. `DevelopmentEffortCalculatorConcept.md` got its `calculator_id`/`revision`/`title` frontmatter, no move (already conformed). Checked for stale cross-references before editing — nothing in the repo links to the removed `CanonicalMonetizationProfiles.md` section anchors (`grep` confirmed), so no other file needed updating. Offline suite unaffected (no code reads these doc files' content) — 94 passing, unchanged. ```task id: TREV-WP-0015-T03 status: todo priority: medium state_hub_task_id: "681002d5-582b-4ddd-8b1f-dc25ce5b0e5a" ``` **Control Plane reference-rendering routes** (addendum §2): add `GET /reference/policies/{slug}` and `GET /reference/profiles/{slug}` to `service/control_plane_app.py`, rendering the corresponding `specs/policies/`/`specs/profiles/` markdown file to HTML server-side at request time (a small Python markdown library — confirm one is already an acceptable addition to the `service` extras in `pyproject.toml`, or add it). Read-only, no edit capability. Link to these routes from wherever a policy id or extension id already appears in `phase_detail.html`/`phase_new.html`. ```task id: TREV-WP-0015-T04 status: done priority: medium state_hub_task_id: "af660317-e732-4ad7-bd4a-86ada8602ec7" ``` **Ledger UI change** (addendum §3): drop the `ledger` input from `phase_new.html`; have the registration route auto-compute `phase.ledger` as the canonical `/phases/{phase_id}/ledger` reference before validation/persistence. Add a small "ledger reference" link/disclosure to `phase_detail.html` showing the raw reference plus a live-data link when it resolves to this same instance. The existing Ledger entries table is untouched. **Result:** Done as part of T01's fix (same form/route touched by both tasks — see T01's Result). `control_plane_app.py`'s registration route sets `phase["ledger"] = f"/phases/{phase_id}/ledger"` before validation; `phase_new.html` no longer has a `ledger` input at all. `phase_detail.html` gained a `
` disclosure showing the raw reference plus a live-JSON link when it resolves to this instance (`manifest.phase.ledger.startswith('/phases/')`). Regression test: `test_phase_new_form_has_no_ledger_input`. ```task id: TREV-WP-0015-T05 status: todo priority: high state_hub_task_id: "816f1ac2-da40-4550-8d6c-64a1a335a660" ``` **`forgejo_hubs` migration** (addendum §4): new migration `migrations/0007_forgejo_hubs.sql`, table `forgejo_hubs (hub_slug PRIMARY KEY, service_uri, first_seen_at, updated_at)`, auto-populated on first sight of a `repo_hub` during Phase registration (mirroring `ensure_licensor_identity`'s auto-create-on-first-INSERT trigger pattern in `migrations/0005_licensor_credentials.sql`). Decide during implementation (not a T02-style human gate, per the addendum) whether correcting a hub's URI after the fact is a `SECURITY DEFINER` governance action or a plain `UPDATE`, and document whichever is chosen. ```task id: TREV-WP-0015-T06 status: done priority: medium state_hub_task_id: "719aa6db-93d8-4eaa-ac1b-e7ee4c00bd89" ``` **Backfill the three example manifests** (addendum §5): `examples/pilot-candidates/{net-kingdom-local-identity, railiance-vergabe-teilnahme, info-tech-canon-service-surface}/manifest.json` each get `repo_hub`/`repo_hub_uri`/`repo_id`/`repo_name` from their real Forgejo data (`GET /api/v1/repos/{owner}/{repo}`, same call already confirmed working for `target-revenue` itself during WP-0012-T02), no `base_phase_id` (all three are first Phases), and `ledger` set to match what the Control Plane would now auto-compute. **Result:** Confirmed live against `forgejo.coulomb.social` (all three repos are `coulomb/*`, same org as `target-revenue`): `net-kingdom` id `67`, `vergabe-teilnahme` id `62` (the actual application repo per `history/260730-EffortCalculator-CandidateApplication.md` §3, not the `railiance-apps` deployment repo), `info-tech-canon` id `47`. All three manifests backfilled with real, not placeholder, ids; `ledger` set to `/phases/{phase_id}/ledger` matching §3's auto-compute convention. New regression test: `test_pilot_candidate_manifest_carries_real_repo_provenance`. ```task id: TREV-WP-0015-T07 status: todo priority: high state_hub_task_id: "39b34438-a1b1-49da-a88a-f3bec926aa89" ``` **Tests, docs, and closeout**: offline schema/validation tests (T01), Docker-gated `TestClient` tests for T03's new routes and T04/T05's changes (extending `tests/test_control_plane_app.py`), a rendering smoke test that each `specs/policies/`/`specs/profiles/` file actually renders without error. Update `README.md`'s WP-0015 row and this workplan's Result sections; run the full offline + Docker-gated suite; fence-count check before committing any workplan edit, per this project's standing practice.