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

264 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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):
```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.