Implement WP-0015-T01/T04/T06: Phase provenance schema + ledger UI

Schema: phase.milestone_release gains required repo_hub/repo_hub_uri/
repo_id/repo_name; phase gains optional base_phase_id. Backfilled the
golden phase-001 fixture (placeholder values, it's synthetic) and the
three real pilot-candidate manifests with actual Forgejo repo ids
confirmed live (net-kingdom=67, vergabe-teilnahme=62,
info-tech-canon=47, all coulomb/*).

Discovered along the way: the schema change alone would have broken
the existing Control Plane registration form, since nothing collected
the four new fields. Fixed inline rather than leaving it broken
between tasks -- this also completes T04's ledger UI change (drop the
hand-typed ledger input, auto-compute /phases/{id}/ledger, add a
drill-down reference link on phase_detail.html) since both changes
touch the same form/route.

Full suite: 94 passing offline (was 84), 158 passing with Docker
(was 146).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-08-03 20:43:30 +02:00
parent 11800c7da1
commit 2a176d9961
13 changed files with 204 additions and 20 deletions

View file

@ -35,7 +35,7 @@
},
"milestone_release": {
"type": "object",
"required": ["name", "source_revision"],
"required": ["name", "source_revision", "repo_hub", "repo_hub_uri", "repo_id", "repo_name"],
"additionalProperties": false,
"properties": {
"name": { "type": "string" },
@ -44,6 +44,26 @@
"type": "string",
"pattern": "^[a-f0-9]{64}$",
"description": "Recommended; required if a built artifact (not just source) is governed."
},
"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 (no instance-level identity endpoint exists)."
},
"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."
},
"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. Human-readable; repo_id is authoritative if this ever diverges after a rename."
}
}
},
@ -93,6 +113,11 @@
"ledger": {
"type": "string",
"description": "URL or relative path URI to the authoritative Target Ledger for this Phase."
},
"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; no sentinel value is used."
}
}
},