target-revenue/specs/PhaseProvenanceSpecAddendum.md
tegwick 9c7576e8de Accept PhaseProvenanceSpecAddendum, file TREV-WP-0015 implementation
specs/PhaseProvenanceSpecAddendum.md accepted 2026-08-03. Implementation
filed as its own workplan (TREV-WP-0015), seven tasks mapping 1:1 to
the addendum's sections rather than repeating field shapes/rationale:
schema change, specs/policies/ + specs/profiles/ extraction, Control
Plane reference-rendering routes, ledger UI change, forgejo_hubs
migration, backfilling the three example manifests, and tests/closeout.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-03 20:31:52 +02:00

11 KiB
Raw Permalink Blame History

Phase Provenance, Ledger Reference, and Policy/Profile Spec-File Addendum

workplans/TREV-WP-0012-phase-provenance-and-policy-modeling.md T05. Synthesizes T02T04'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):

"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):

"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_hubrepo_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:

    ---
    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:

---
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:

---
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):

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.