target-revenue/specs/PhaseProvenanceSpecAddendum.md

265 lines
11 KiB
Markdown
Raw Normal View 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):
```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.