# Phase Provenance, Ledger Reference, and Policy/Profile Spec-File Addendum `workplans/TREV-WP-0012-phase-provenance-and-policy-modeling.md` T05. Synthesizes T02–T04's accepted decisions (all accepted; see that workplan's Result sections for the full reasoning behind each) into one reviewable document, covering the concrete schema, spec-file, and Control Plane UI changes they imply. **Accepted 2026-08-03.** Implementation is tracked in `workplans/TREV-WP-0015-phase-provenance-implementation.md`, whose seven tasks map 1:1 to this document's §1–§6 (this document remains the authoritative source for field shapes and rationale; that workplan does not repeat them). --- ## 1. Phase Manifest schema changes (T02) `schemas/phase_manifest.schema.json`, `phase.milestone_release`: four new **required** properties, alongside the two that already exist (`name`, `source_revision`; `artifact_sha256` stays optional): ```jsonc "milestone_release": { "type": "object", "required": ["name", "source_revision", "repo_hub", "repo_hub_uri", "repo_id", "repo_name"], "additionalProperties": false, "properties": { "name": { "type": "string" }, "source_revision": { "type": "string" }, "artifact_sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, "repo_hub": { "type": "string", "pattern": "^[a-z0-9-]+$", "description": "Stable, human-assigned slug for the Forgejo instance hosting this repo, e.g. \"forgejo-coulomb\". Minted by this project, not read from Forgejo — Forgejo has no instance-level identity endpoint (confirmed live 2026-07-30, /api/v1/nodeinfo -> 404)." }, "repo_hub_uri": { "type": "string", "format": "uri", "description": "The Forgejo service's current base URL, e.g. \"https://forgejo.coulomb.social\". Retained alongside repo_hub (not derived from it) so a domain change stays repairable without breaking every Phase's provenance link." }, "repo_id": { "type": "integer", "minimum": 1, "description": "Forgejo's own internal numeric repo id (GET /api/v1/repos/{owner}/{repo} -> id). Stable across repo renames, unlike repo_name." }, "repo_name": { "type": "string", "pattern": "^[\\w.-]+/[\\w.-]+$", "description": "\"owner/repo\" slug at time of Phase registration, e.g. \"coulomb/target-revenue\". Human-readable; repo_id is authoritative if this ever diverges after a rename." } } } ``` `phase`: one new **optional** property, `base_phase_id` — present only for a successive Phase, absent for a first-ever Phase (no sentinel value): ```jsonc "base_phase_id": { "type": "string", "pattern": "^trsl:phase:[a-zA-Z0-9._-]+$", "description": "The prior Phase this one succeeds (FR-11, Rule 7). Absent for a repo's first-ever Phase." } ``` `phase.ledger` is **unchanged** by this addendum — see §4. ### Not part of this schema The `repo_hub` → `repo_hub_uri` mapping is **not** schema-level or file-level data in this repo. `target-revenue` is the generic framework, not a specific deployment's list of repos it monetizes. The mapping is hosted Trust Service data — see §5. --- ## 2. Degeneration-policy and calculation-model spec files (T03) New `specs/policies/` subdirectory, one file per policy, replacing the current situation where `linear-longstop-v0`'s actual definition is a subsection (Q7) inside `specs/OpenQuestions-WorkingDefaults.md`, mixed with many unrelated open questions: - `specs/policies/linear-longstop-v0.md` — extracted from Q7's prose (`R(t) = T0 × clamp((t − t0)/(tL − t0), 0, 1)`, discrete `remission-credit` entries on a published cadence). Frontmatter: ```yaml --- policy_id: trsl:policy:linear-longstop-v0@1.0 title: Linear Longstop v0 --- ``` `OpenQuestions-WorkingDefaults.md` Q7 becomes a pointer to this file (the *decision record* — "this is the accepted v1 norm" — stays there; the policy's own *content* moves out). New `specs/profiles/` subdirectory, one file per canonical monetization profile, splitting `specs/CanonicalMonetizationProfiles.md`'s six sections into their own files — these already have stable ids used in real Phase Manifests today (`extensions: ["trsl:extension:development-license@1.0", ...]`), so the id→file mapping is immediate, not something newly invented: - `specs/profiles/development-license.md` - `specs/profiles/cost-plus-operations.md` - `specs/profiles/phase-sponsorship.md` - `specs/profiles/service-with-development-allocation.md` - `specs/profiles/product-ideation.md` - `specs/profiles/general-consulting.md` Each with frontmatter in the same shape: ```yaml --- extension_id: trsl:extension:development-license@1.0 title: Development License --- ``` `specs/CanonicalMonetizationProfiles.md` §7 (cross-profile summary) and §8 (non-goals) stay as the comparison/overview document; the six per-profile sections (§1–§6) move out. `specs/DevelopmentEffortCalculatorConcept.md` requires **no move** — one file, one model, already a distinct name. Add matching frontmatter for consistency: ```yaml --- calculator_id: development-effort-calculator-candidate-a revision: "1.0" title: Development Effort Calculator — Candidate A --- ``` (No `trsl:...` schema id exists for calculators — this is a documentation-traceability convention only, since calculators aren't referenced by id from inside a Phase Manifest the way `degeneration_policy` is.) **Id and revision, built on git internals**: the id is the stable identifier already in use (with its existing `@version` suffix where applicable), mapped to exactly one file by naming convention; the revision/audit trail is that file's own git history (`git log -- specs/policies/linear-longstop-v0.md`). No new versioning table, no pinned-commit scheme — nothing beyond what git already provides. ### Control Plane rendering One new read-only route family, server-side markdown → HTML at request time (a small Python markdown library — e.g. `markdown` or `mistune` — not a new build pipeline): ``` GET /reference/policies/{slug} GET /reference/profiles/{slug} ``` A plain link next to wherever a policy id already appears (`phase_detail.html`, `phase_new.html`) — never in-UI editable, matching every governance-action pattern already established in this project (credential revocation, extension canonicalization, proposed-entry review — all view/act, never edit-in-place). This deliberately does **not** adopt state-hub's Observable-Framework static-build + "?" overlay pattern (`~/state-hub/dashboard/src/docs/*.md`) — confirmed live 2026-07-30 that it's a real static-site generator, a materially heavier dependency than this repo's existing lightweight FastAPI+Jinja2 stack needs for six profile pages and one policy page. --- ## 3. Ledger UI treatment (T04) - `phase_new.html` drops the `ledger` input entirely. The registration route (`control_plane.register_phase`, or its caller) sets `phase.ledger` to the canonical `/phases/{phase_id}/ledger` reference automatically, before the manifest is validated/persisted — no human types it in v0. - `phase_detail.html` gains a small "ledger reference" link/disclosure (not a new prominent field) showing the raw `ledger` string from the manifest, plus — only when it resolves to this same Trust Service instance — a link to `GET /phases/{id}/ledger`'s live JSON. - The existing Ledger entries table on `phase_detail.html` is **unaffected** — this addendum only concerns the otherwise-invisible raw reference string, not the ledger data itself. - The schema field `phase.ledger` is unchanged (still required, still federation-ready for a future Phase whose ledger genuinely lives elsewhere) — this section is a Control Plane UI change only. --- ## 4. Hosted Trust Service: `forgejo_hubs` table A new migration (numbered after `0006_control_plane.sql`, so `0007`), analogous to `licensor_identities` (`migrations/0005_licensor_credentials.sql`): ```sql CREATE TABLE IF NOT EXISTS forgejo_hubs ( hub_slug text PRIMARY KEY, service_uri text NOT NULL, first_seen_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); ``` Auto-populated the first time a `repo_hub` is seen during Phase registration (mirroring `ensure_licensor_identity`'s auto-create-on-first- INSERT trigger pattern) — no separate onboarding step required. This table is purely an admin/repair convenience for the hosting layer (e.g. "this hub's URI changed, update it in one place") — a Phase Manifest never needs it to be reachable for offline verification, since the manifest itself always carries `repo_hub_uri`/`repo_name` directly at registration time (use case 9, `specs/PhaseLifecycleUseCases.md`). Whether correcting a hub's URI after the fact should be a governance action (a `SECURITY DEFINER` function, like `revoke_credential`) or a plain `UPDATE` is left to the implementation workplan to decide — not load-bearing enough to need a T02-style human gate here, but flagged so it isn't silently decided by whichever pattern is fastest to type. --- ## 5. Backfilling the three example manifests Once the schema change above lands, `examples/pilot-candidates/*/manifest.json` (all three still `trsl:phase:draft-*`, never registered) get: - `repo_hub`/`repo_hub_uri`/`repo_id`/`repo_name` filled in from each repo's real Forgejo data (the same `GET /api/v1/repos/{owner}/{repo}` call already confirmed working for `target-revenue` itself applies identically to `net-kingdom`, `railiance-apps`/`vergabe-teilnahme`, and `info-tech-canon`). - No `base_phase_id` — all three are first-ever Phases for their repos. - `ledger` set to each candidate's own canonical reference, matching whatever the Control Plane would auto-compute (§3), for consistency between examples and real registered Phases. --- ## 6. Suggested implementation task breakdown Once this addendum is accepted, file as its own workplan (working title `TREV-WP-0015` or similar — not created yet, per this task's own charter of producing a document, not a workplan): 1. Schema change (§1) + `validation.py`/`tests/` updates. 2. `specs/policies/`, `specs/profiles/` extraction (§2) + `DevelopmentEffortCalculatorConcept.md` frontmatter. 3. Control Plane reference-rendering route (§2) + linking from `phase_detail.html`/`phase_new.html`. 4. Ledger UI change (§3) — drop the form field, add the drill-down link. 5. `forgejo_hubs` migration + auto-populate-on-first-registration logic (§4). 6. Backfill the three example manifests (§5). 7. Tests (offline schema/validation tests for §1, Docker-gated tests for §4/§5, a rendering smoke test for §2's new routes) and a `README.md`/ workplan Result update, following this project's existing pattern for every prior task in this session.