diff --git a/README.md b/README.md index 7fef07b..bc0685e 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ The concept's §13 now defines a **Global Contingency Share Determination Rule** | [TREV-WP-0012](workplans/TREV-WP-0012-phase-provenance-and-policy-modeling.md) | Phase provenance, ledger reference, and degeneration-policy modeling — **finished**, all 5 tasks done. Decisions (T02–T04) synthesized into [`specs/PhaseProvenanceSpecAddendum.md`](specs/PhaseProvenanceSpecAddendum.md) (T05) — **not yet accepted for implementation**; that's the document to discuss before any schema/UI work is filed as its own workplan | | [TREV-WP-0013](workplans/TREV-WP-0013-remission-credit-automation.md) | Remission Credit automation (degeneration policy execution) — active; T01–T03 `wait` on WP-0012-T03's policy-spec-file decision. Nothing currently computes or writes `remission-credit` ledger entries | | [TREV-WP-0014](workplans/TREV-WP-0014-control-plane-extensions-breach-attestation-ui.md) | Control Plane UI: Extension Registry, Breach Records, Conversion Attestation — active; T01 next. Backend for all three already exists (WP-0006); UI-only work, not blocked on WP-0012 | -| [TREV-WP-0015](workplans/TREV-WP-0015-phase-provenance-implementation.md) | Implement `specs/PhaseProvenanceSpecAddendum.md` — active; T01 (schema change) next. Seven tasks map 1:1 to the addendum's §1–§6 | +| [TREV-WP-0015](workplans/TREV-WP-0015-phase-provenance-implementation.md) | Implement `specs/PhaseProvenanceSpecAddendum.md` — active; T01, T04, T06 done (schema change + Control Plane form/ledger UI + real-data backfill for the three pilot candidates). T02 (`specs/policies/`/`specs/profiles/` extraction) next | Hub index: [`WORK-RECORDS.md`](WORK-RECORDS.md) · brief: [`.custodian-brief.md`](.custodian-brief.md) diff --git a/examples/phase-001/manifest.json b/examples/phase-001/manifest.json index c312f50..db80822 100644 --- a/examples/phase-001/manifest.json +++ b/examples/phase-001/manifest.json @@ -6,7 +6,11 @@ "milestone_release": { "name": "release-1.0", "source_revision": "abc123", - "artifact_sha256": "44cd1493bd179c1207c7025c1372cdad89aac114e151db27aba2bdf3d55c688a" + "artifact_sha256": "44cd1493bd179c1207c7025c1372cdad89aac114e151db27aba2bdf3d55c688a", + "repo_hub": "example-hub", + "repo_hub_uri": "https://example.invalid", + "repo_id": 1, + "repo_name": "example/example-repo" }, "initial_target": { "amount": 100000, diff --git a/examples/pilot-candidates/info-tech-canon-service-surface/manifest.json b/examples/pilot-candidates/info-tech-canon-service-surface/manifest.json index 6fd464c..6d2c6fa 100644 --- a/examples/pilot-candidates/info-tech-canon-service-surface/manifest.json +++ b/examples/pilot-candidates/info-tech-canon-service-surface/manifest.json @@ -5,7 +5,11 @@ "id": "trsl:phase:draft-info-tech-canon-service-surface", "milestone_release": { "name": "info-tech-canon-service-surface-v1 (ITC-WP-0001..0012)", - "source_revision": "f7ad73d" + "source_revision": "f7ad73d", + "repo_hub": "forgejo-coulomb", + "repo_hub_uri": "https://forgejo.coulomb.social", + "repo_id": 47, + "repo_name": "coulomb/info-tech-canon" }, "initial_target": { "amount": 141800, @@ -20,7 +24,7 @@ "future_license": "MIT", "degeneration_policy": "trsl:policy:linear-longstop-v0@1.0", "longstop_at": "2029-01-01T00:00:00Z", - "ledger": "examples/pilot-candidates/info-tech-canon-service-surface/ledger.json" + "ledger": "/phases/trsl:phase:draft-info-tech-canon-service-surface/ledger" }, "extensions": [ "trsl:extension:development-license@1.0" diff --git a/examples/pilot-candidates/net-kingdom-local-identity/manifest.json b/examples/pilot-candidates/net-kingdom-local-identity/manifest.json index 3e573b6..6f121f2 100644 --- a/examples/pilot-candidates/net-kingdom-local-identity/manifest.json +++ b/examples/pilot-candidates/net-kingdom-local-identity/manifest.json @@ -5,7 +5,11 @@ "id": "trsl:phase:draft-net-kingdom-local-identity", "milestone_release": { "name": "net-kingdom-local-identity-v1 (NK-WP-0002)", - "source_revision": "3890dca" + "source_revision": "3890dca", + "repo_hub": "forgejo-coulomb", + "repo_hub_uri": "https://forgejo.coulomb.social", + "repo_id": 67, + "repo_name": "coulomb/net-kingdom" }, "initial_target": { "amount": 10000, @@ -20,7 +24,7 @@ "future_license": "MIT", "degeneration_policy": "trsl:policy:linear-longstop-v0@1.0", "longstop_at": "2028-01-01T00:00:00Z", - "ledger": "examples/pilot-candidates/net-kingdom-local-identity/ledger.json" + "ledger": "/phases/trsl:phase:draft-net-kingdom-local-identity/ledger" }, "extensions": [ "trsl:extension:development-license@1.0" diff --git a/examples/pilot-candidates/railiance-vergabe-teilnahme/manifest.json b/examples/pilot-candidates/railiance-vergabe-teilnahme/manifest.json index 44ecf94..4986878 100644 --- a/examples/pilot-candidates/railiance-vergabe-teilnahme/manifest.json +++ b/examples/pilot-candidates/railiance-vergabe-teilnahme/manifest.json @@ -5,7 +5,11 @@ "id": "trsl:phase:draft-railiance-vergabe-teilnahme", "milestone_release": { "name": "vergabe-teilnahme-v1 (RAILIANCE-WP-0002/RAILIANCE-WP-0014)", - "source_revision": "398b0fe" + "source_revision": "398b0fe", + "repo_hub": "forgejo-coulomb", + "repo_hub_uri": "https://forgejo.coulomb.social", + "repo_id": 62, + "repo_name": "coulomb/vergabe-teilnahme" }, "initial_target": { "amount": 648800, @@ -20,7 +24,7 @@ "future_license": "Apache-2.0", "degeneration_policy": "trsl:policy:linear-longstop-v0@1.0", "longstop_at": "2029-01-01T00:00:00Z", - "ledger": "examples/pilot-candidates/railiance-vergabe-teilnahme/ledger.json" + "ledger": "/phases/trsl:phase:draft-railiance-vergabe-teilnahme/ledger" }, "extensions": [ "trsl:extension:development-license@1.0", diff --git a/schemas/phase_manifest.schema.json b/schemas/phase_manifest.schema.json index 480dec1..ef69d8c 100644 --- a/schemas/phase_manifest.schema.json +++ b/schemas/phase_manifest.schema.json @@ -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." } } }, diff --git a/src/target_revenue/service/control_plane_app.py b/src/target_revenue/service/control_plane_app.py index 398f2b2..d566c20 100644 --- a/src/target_revenue/service/control_plane_app.py +++ b/src/target_revenue/service/control_plane_app.py @@ -198,26 +198,41 @@ def phase_new_submit( phase_id: str = Form(...), milestone_release_name: str = Form(...), source_revision: str = Form(...), + repo_hub: str = Form(...), + repo_hub_uri: str = Form(...), + repo_id: int = Form(...), + repo_name: str = Form(...), + base_phase_id: str = Form(""), initial_target_amount: float = Form(...), currency: str = Form(...), future_license: str = Form(...), degeneration_policy: str = Form(...), longstop_at: str = Form(...), - ledger_uri: str = Form(..., alias="ledger"), ): manifest = { "framework": "TRF-0.1", "license": "TRSL-0.1", "phase": { "id": phase_id, - "milestone_release": {"name": milestone_release_name, "source_revision": source_revision}, + "milestone_release": { + "name": milestone_release_name, + "source_revision": source_revision, + "repo_hub": repo_hub, + "repo_hub_uri": repo_hub_uri, + "repo_id": repo_id, + "repo_name": repo_name, + }, "initial_target": {"amount": initial_target_amount, "currency": currency}, "future_license": future_license, "degeneration_policy": degeneration_policy, "longstop_at": longstop_at, - "ledger": ledger_uri, + # Auto-computed, never hand-typed (WP-0012-T04) -- this Trust + # Service instance is always the ledger's host in Stage 0. + "ledger": f"/phases/{phase_id}/ledger", }, } + if base_phase_id: + manifest["phase"]["base_phase_id"] = base_phase_id try: control_plane.register_phase(conn, licensor, manifest) except (control_plane.ControlPlaneError, registry.RegistrationError) as exc: diff --git a/src/target_revenue/service/control_plane_templates/phase_detail.html b/src/target_revenue/service/control_plane_templates/phase_detail.html index 4b411de..104d20f 100644 --- a/src/target_revenue/service/control_plane_templates/phase_detail.html +++ b/src/target_revenue/service/control_plane_templates/phase_detail.html @@ -6,6 +6,16 @@
+ Raw reference: {{ manifest.phase.ledger }}
+ {% if manifest.phase.ledger.startswith('/phases/') %}
+ · view live JSON
+ {% endif %}
+
| Initial Target | {{ manifest.phase.initial_target.amount }} {{ manifest.phase.initial_target.currency }} |