2026-07-30 11:02:45 +02:00
|
|
|
|
---
|
|
|
|
|
|
id: TREV-WP-0009
|
|
|
|
|
|
type: workplan
|
|
|
|
|
|
title: "Target Revenue Control Plane"
|
|
|
|
|
|
domain: infotech
|
|
|
|
|
|
repo: target-revenue
|
Implement WP-0009-T04: Control Plane interactive UI on whynot-design
Builds the Control Plane's browser UI (login, dashboard, Phase
registration, Development Credit entry/proposal/review, credential
admin, audit log) as a FastAPI + Jinja2 app over the already-finished
T03 backend, rather than from scratch — whynot-design's Lit web
components are vendored as static assets (source commit 4b62cffc,
v0.4.1), with lit itself resolved via an esm.sh CDN import map.
Session auth re-checks the credential token against the database on
every request rather than trusting the session cookie's cached rights,
so a mid-session revocation takes effect immediately.
9 new Docker-gated HTTP-level tests via FastAPI's TestClient (no
browser-automation tool available, so real rendering of the <wn-*>
components was never visually verified). All four WP-0009 tasks are
now done; workplan marked finished.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 15:43:16 +02:00
|
|
|
|
status: finished
|
2026-07-30 11:02:45 +02:00
|
|
|
|
owner: claude
|
|
|
|
|
|
topic_slug: infotech
|
|
|
|
|
|
created: "2026-07-30"
|
|
|
|
|
|
updated: "2026-07-30"
|
|
|
|
|
|
state_hub_workstream_id: "0e8dfd43-a742-4458-9e9b-514a9284821b"
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
# Target Revenue Control Plane
|
|
|
|
|
|
|
|
|
|
|
|
An interactive UI over the hosted Trust Service
|
|
|
|
|
|
(`workplans/TREV-WP-0006-trust-service-implementation.md`, finished),
|
|
|
|
|
|
usable by a human user with appropriate rights acting as the `binky`
|
|
|
|
|
|
tenant. The specific, named requirement driving this workplan: make it
|
|
|
|
|
|
possible to interactively register Phases and create Development Credit
|
|
|
|
|
|
ledger entries, rather than requiring raw API calls or the
|
|
|
|
|
|
`scripts/trf_onboard.py` CLI.
|
|
|
|
|
|
|
|
|
|
|
|
**Split from the former combined `TREV-WP-0009-control-plane-and-effort-calculator.md`
|
|
|
|
|
|
(2026-07-30)** — the Development Effort Calculator is a separate concern
|
|
|
|
|
|
with its own formula decision and implementation arc; it now lives in
|
|
|
|
|
|
`workplans/TREV-WP-0010-development-effort-calculator.md`. The two remain
|
|
|
|
|
|
related (the Control Plane's Phase-registration flow is expected to use
|
2026-07-30 13:06:36 +02:00
|
|
|
|
the Calculator's output, per T04 below) but are sequenced and reviewed
|
2026-07-30 11:02:45 +02:00
|
|
|
|
independently.
|
|
|
|
|
|
|
|
|
|
|
|
**Does not include:** declaring any real Phase for any repo, or changing
|
|
|
|
|
|
the Trust Service's core guarantees (determinism, append-only, no
|
|
|
|
|
|
discretionary conversion authority) — the Control Plane is a client of
|
|
|
|
|
|
the existing hosted service, bound by the same rules any other client is.
|
|
|
|
|
|
|
|
|
|
|
|
Concept: `specs/TargetRevenueControlPlaneConcept.md`.
|
|
|
|
|
|
|
|
|
|
|
|
## User rights model — decision (human gate)
|
|
|
|
|
|
|
|
|
|
|
|
```task
|
|
|
|
|
|
id: TREV-WP-0009-T01
|
2026-07-30 13:06:36 +02:00
|
|
|
|
status: done
|
2026-07-30 11:02:45 +02:00
|
|
|
|
priority: high
|
|
|
|
|
|
human_accept_required: true
|
2026-07-30 13:06:36 +02:00
|
|
|
|
human_accepted_by: Bernd
|
|
|
|
|
|
human_accepted_at: "2026-07-30"
|
2026-07-30 11:03:40 +02:00
|
|
|
|
state_hub_task_id: "177117e7-955b-4f12-b17b-75ee3f0357c4"
|
2026-07-30 11:02:45 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`specs/TargetRevenueControlPlaneConcept.md` §2 proposes a four-tier rights
|
|
|
|
|
|
model (Viewer/Contributor/Operator/Admin) and recommends option (b) for
|
|
|
|
|
|
the auth-attribution question: the Control Plane holds the one `binky`
|
|
|
|
|
|
Licensor token server-side and layers its own human-user auth/audit log
|
|
|
|
|
|
in front of it, rather than requiring a WP-0006 schema change for
|
|
|
|
|
|
per-human sub-credentials (option (a)). Confirm the rights tiers and the
|
|
|
|
|
|
(a)-vs-(b) choice, or propose a refinement. Agents may prepare a
|
|
|
|
|
|
recommendation and leave this `todo`.
|
|
|
|
|
|
|
2026-07-30 13:06:36 +02:00
|
|
|
|
**Accepted 2026-07-30 by the maintainer (Bernd):** the four rights tiers
|
|
|
|
|
|
are confirmed as proposed. **Option (a) is adopted, not (b)** — the
|
|
|
|
|
|
Control Plane will issue per-human-user sub-credentials at the Trust
|
|
|
|
|
|
Service layer, mapped to the same underlying `binky` Licensor identity,
|
|
|
|
|
|
so that the Trust Service's own signed ledger records can attest to the
|
|
|
|
|
|
specific human who acted, not merely "the `binky` Licensor did this."
|
|
|
|
|
|
This means a real, scoped extension to WP-0006's already-finished
|
|
|
|
|
|
`licensors`/token auth model is now a firm prerequisite, not a
|
|
|
|
|
|
hypothetical branch — see T02 below, added specifically for this reason.
|
|
|
|
|
|
|
|
|
|
|
|
## WP-0006 auth extension: per-human sub-credentials
|
2026-07-30 11:02:45 +02:00
|
|
|
|
|
|
|
|
|
|
```task
|
|
|
|
|
|
id: TREV-WP-0009-T02
|
Extend WP-0006 auth for per-human sub-credentials (WP-0009-T02)
migrations/0005_licensor_credentials.sql: licensors can now hold
multiple rows per licensor_id (credential_label, rights tier,
issued_by, revoked_at). Real structural finding: licensor_id couldn't
simply become non-unique, since phase_manifests, extensions, and
breach_records all FK to licensors(licensor_id), which only worked
because that column used to be unique. Introduced licensor_identities
(one row per tenant) as the new FK target for all four tables, with an
ensure_licensor_identity trigger auto-creating the identity on first
credential insert - so existing code (including every earlier test
fixture) needed no changes.
registry.py: Licensor gains credential_label/rights; RIGHTS_TIERS +
has_right() ordinal helper (enforcement is Control Plane's job, T03/
T04, not this task's); issue_sub_credential/revoke_sub_credential
(revocation via a SECURITY DEFINER function, matching
set_extension_status's existing pattern - trf_app has no UPDATE grant
on licensors); authenticate() rejects revoked credentials identically
to unrecognized ones.
Attribution scoped honestly: ledger_entry.schema.json stays unmodified
(frozen Stage 0 surface, additionalProperties:false) - per-entry human
attribution is a hosting-layer-only column
(ledger_entries.submitted_by_token, ledger.get_ledger_attribution()),
recorded alongside but never inside the signed entry payload. Narrower
than "the signature names the human," but exactly the "(or an
accompanying attributable field)" alternative this task's own
description anticipated.
All four Docker-gated test files that append Ledger entries needed
migration 0005 added (append_entry's INSERT now references the new
column). New tests/test_licensor_credentials.py (8 tests): multi-
credential resolution, duplicate-label rejection, revocation and its
idempotence, invalid-rights rejection, the has_right helper, per-entry
attribution recorded and not leaking into exported ledger JSON, and
DB-level UPDATE rejection. Full suite: 84 offline, 41 with Docker (up
from 30); no stray containers left running.
2026-07-30 14:26:33 +02:00
|
|
|
|
status: done
|
2026-07-30 11:02:45 +02:00
|
|
|
|
priority: high
|
2026-07-30 13:07:22 +02:00
|
|
|
|
state_hub_task_id: "52ae3a7a-6b55-4694-8bc1-cb0e32a4fe31"
|
2026-07-30 13:06:36 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Extend `migrations/0001_registries.sql`'s `licensors`/token model and
|
|
|
|
|
|
`src/target_revenue/registry.authenticate` so a single Licensor identity
|
|
|
|
|
|
(`binky`) can have multiple, individually-issued, individually-revocable
|
|
|
|
|
|
sub-credentials, each resolving to the same `licensor_id` for phase-
|
|
|
|
|
|
ownership checks (`registry.py`, `ledger.py`) but distinguishable in the
|
|
|
|
|
|
returned `Licensor`/signing context so a ledger entry's signature (or an
|
|
|
|
|
|
accompanying attributable field) can reflect *which* sub-credential
|
|
|
|
|
|
signed it. This is a change to WP-0006's finished, tested auth layer —
|
|
|
|
|
|
treat it with the same care as any change to already-shipped, tested
|
|
|
|
|
|
code: new tests proving existing single-token behavior is unaffected,
|
|
|
|
|
|
plus new tests for the sub-credential path. Does not change the Ledger's
|
|
|
|
|
|
append-only guarantees or the hash-chain/signature scheme itself, only
|
|
|
|
|
|
who may authenticate as `binky` and how that's distinguished.
|
|
|
|
|
|
|
Extend WP-0006 auth for per-human sub-credentials (WP-0009-T02)
migrations/0005_licensor_credentials.sql: licensors can now hold
multiple rows per licensor_id (credential_label, rights tier,
issued_by, revoked_at). Real structural finding: licensor_id couldn't
simply become non-unique, since phase_manifests, extensions, and
breach_records all FK to licensors(licensor_id), which only worked
because that column used to be unique. Introduced licensor_identities
(one row per tenant) as the new FK target for all four tables, with an
ensure_licensor_identity trigger auto-creating the identity on first
credential insert - so existing code (including every earlier test
fixture) needed no changes.
registry.py: Licensor gains credential_label/rights; RIGHTS_TIERS +
has_right() ordinal helper (enforcement is Control Plane's job, T03/
T04, not this task's); issue_sub_credential/revoke_sub_credential
(revocation via a SECURITY DEFINER function, matching
set_extension_status's existing pattern - trf_app has no UPDATE grant
on licensors); authenticate() rejects revoked credentials identically
to unrecognized ones.
Attribution scoped honestly: ledger_entry.schema.json stays unmodified
(frozen Stage 0 surface, additionalProperties:false) - per-entry human
attribution is a hosting-layer-only column
(ledger_entries.submitted_by_token, ledger.get_ledger_attribution()),
recorded alongside but never inside the signed entry payload. Narrower
than "the signature names the human," but exactly the "(or an
accompanying attributable field)" alternative this task's own
description anticipated.
All four Docker-gated test files that append Ledger entries needed
migration 0005 added (append_entry's INSERT now references the new
column). New tests/test_licensor_credentials.py (8 tests): multi-
credential resolution, duplicate-label rejection, revocation and its
idempotence, invalid-rights rejection, the has_right helper, per-entry
attribution recorded and not leaking into exported ledger JSON, and
DB-level UPDATE rejection. Full suite: 84 offline, 41 with Docker (up
from 30); no stray containers left running.
2026-07-30 14:26:33 +02:00
|
|
|
|
**Result:** `migrations/0005_licensor_credentials.sql` — `licensors` can
|
|
|
|
|
|
now hold multiple rows per `licensor_id` (`credential_label`, `rights`
|
|
|
|
|
|
tier, `issued_by`, `revoked_at`). Real structural finding along the way:
|
|
|
|
|
|
`licensor_id` could not simply become non-unique, because
|
|
|
|
|
|
`phase_manifests`, `extensions`, and `breach_records` all carry a foreign
|
|
|
|
|
|
key to `licensors(licensor_id)`, which only worked because that column
|
|
|
|
|
|
used to be unique — a FK target must be unique. Introduced a new
|
|
|
|
|
|
`licensor_identities` table (one row per tenant) as the FK target for all
|
|
|
|
|
|
four tables instead, with an `ensure_licensor_identity` trigger that
|
|
|
|
|
|
auto-creates the identity row on first credential insert (so existing
|
|
|
|
|
|
code that inserts directly into `licensors` — including every earlier
|
|
|
|
|
|
test fixture — needed no changes), plus
|
|
|
|
|
|
`registry.create_licensor_identity` for callers that want tenant
|
|
|
|
|
|
onboarding as its own explicit step. `registry.py` gained
|
|
|
|
|
|
`Licensor.credential_label`/`.rights`, `RIGHTS_TIERS`/`has_right()` (an
|
|
|
|
|
|
ordinal helper — enforcing what each tier may do is Control Plane's own
|
|
|
|
|
|
job, T03/T04, not this task's), `issue_sub_credential`/
|
|
|
|
|
|
`revoke_sub_credential` (revocation via a `revoke_credential()` SECURITY
|
|
|
|
|
|
DEFINER function, matching `set_extension_status`'s existing pattern —
|
|
|
|
|
|
`trf_app` has no UPDATE grant on `licensors`). `authenticate()` now
|
|
|
|
|
|
rejects a revoked credential identically to an unrecognized one.
|
|
|
|
|
|
|
|
|
|
|
|
**Attribution, scoped honestly:** `ledger_entry.schema.json` was
|
|
|
|
|
|
deliberately left unmodified (frozen Stage 0 normative surface,
|
|
|
|
|
|
`additionalProperties: false`) — per-entry human attribution is instead a
|
|
|
|
|
|
hosting-layer-only column, `ledger_entries.submitted_by_token`, recorded
|
|
|
|
|
|
alongside but never inside the signed entry payload
|
|
|
|
|
|
(`ledger.get_ledger_attribution`). This means the claim is narrower than
|
|
|
|
|
|
"the signature itself names the human": the cryptographic signature is
|
|
|
|
|
|
unchanged and still only attests to the entry content and chain; the
|
|
|
|
|
|
*database* additionally knows which credential submitted each entry,
|
|
|
|
|
|
queryable but not portable/exported with the entry itself. Exactly the
|
|
|
|
|
|
"(or an accompanying attributable field)" alternative this task's own
|
|
|
|
|
|
description anticipated.
|
|
|
|
|
|
|
|
|
|
|
|
All four Docker-gated test files that append Ledger entries needed
|
|
|
|
|
|
migration `0005` added to their setup (`ledger.append_entry`'s INSERT now
|
|
|
|
|
|
references the new column) — done for
|
|
|
|
|
|
`test_registry_hosting.py`/`test_ledger_hosting.py`/
|
|
|
|
|
|
`test_hosted_conformance.py`/`test_onboarding_hosted.py`. New
|
|
|
|
|
|
`tests/test_licensor_credentials.py` (8 tests): multi-credential
|
|
|
|
|
|
resolution to the same `licensor_id`, duplicate-active-label rejection,
|
|
|
|
|
|
revocation and its idempotence, invalid-rights rejection, the `has_right`
|
|
|
|
|
|
ordinal helper, per-entry attribution recorded and *not* leaking into the
|
|
|
|
|
|
exported ledger JSON, and DB-level UPDATE rejection on `licensors`. Full
|
|
|
|
|
|
suite: 84 passing offline (unchanged), 41 passing with Docker (up from
|
|
|
|
|
|
30); no stray containers left running.
|
|
|
|
|
|
|
2026-07-30 13:06:36 +02:00
|
|
|
|
## Control Plane backend: auth layer and audit log
|
|
|
|
|
|
|
|
|
|
|
|
```task
|
|
|
|
|
|
id: TREV-WP-0009-T03
|
Implement Control Plane backend: rights enforcement + audit log (WP-0009-T03)
migrations/0006_control_plane.sql: control_plane_audit_log (append-only,
no UPDATE/DELETE for trf_app) and control_plane_proposed_entries (the
Contributor tier's "propose, don't append" workflow from concept §2) -
review decisions go through a review_proposed_entry() SECURITY DEFINER
function, same governance-action pattern as
set_extension_status/revoke_credential, not a direct UPDATE.
src/target_revenue/control_plane.py is the enforcement layer concept
§2 called for: register_phase/append_development_credit require
Operator+; propose_ledger_entry requires Contributor+ and stores a
pending proposal without touching the real Ledger; approve_proposed_entry
(Operator+) appends it under the *reviewer's own* credential/attribution
(not the original proposer's - the reviewer is who's authorizing it into
the real Ledger, while the proposer stays on record in the proposal row
and audit log); reject_proposed_entry (Operator+) discards it. issue_/
revoke_user_credential (Admin+) wrap registry.py's T02 functions with
the same rights check and audit logging. Every action funnels through
record_audit_event, independent of the Trust Service's own signed
records.
tests/test_control_plane.py (12 tests): rights enforcement at each
tier boundary, the full propose -> approve -> appended-under-reviewer
flow, propose -> reject -> nothing appended, double-review rejection,
audit log content/attribution, DB-level UPDATE rejection on both new
tables. Full suite: 84 offline (unchanged), 53 with Docker (up from
41); no stray containers left running.
2026-07-30 15:08:59 +02:00
|
|
|
|
status: done
|
2026-07-30 13:06:36 +02:00
|
|
|
|
priority: high
|
2026-07-30 11:03:40 +02:00
|
|
|
|
state_hub_task_id: "86a58e61-dccd-4678-b694-22a9eaea2b3c"
|
2026-07-30 11:02:45 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-07-30 13:06:36 +02:00
|
|
|
|
Using T02's sub-credential extension, implement the Control Plane's own
|
|
|
|
|
|
human-user authentication/authorization layer (issuing and managing
|
|
|
|
|
|
sub-credentials per the four rights tiers) and its own audit log
|
2026-07-30 11:02:45 +02:00
|
|
|
|
(concept §5) — which human user took which action, timestamped, alongside
|
|
|
|
|
|
the Trust Service's own signed record id for that action. This is the
|
2026-07-30 13:06:36 +02:00
|
|
|
|
piece that must exist before any write-capable UI flow (T04) can be built
|
|
|
|
|
|
responsibly.
|
2026-07-30 11:02:45 +02:00
|
|
|
|
|
Implement Control Plane backend: rights enforcement + audit log (WP-0009-T03)
migrations/0006_control_plane.sql: control_plane_audit_log (append-only,
no UPDATE/DELETE for trf_app) and control_plane_proposed_entries (the
Contributor tier's "propose, don't append" workflow from concept §2) -
review decisions go through a review_proposed_entry() SECURITY DEFINER
function, same governance-action pattern as
set_extension_status/revoke_credential, not a direct UPDATE.
src/target_revenue/control_plane.py is the enforcement layer concept
§2 called for: register_phase/append_development_credit require
Operator+; propose_ledger_entry requires Contributor+ and stores a
pending proposal without touching the real Ledger; approve_proposed_entry
(Operator+) appends it under the *reviewer's own* credential/attribution
(not the original proposer's - the reviewer is who's authorizing it into
the real Ledger, while the proposer stays on record in the proposal row
and audit log); reject_proposed_entry (Operator+) discards it. issue_/
revoke_user_credential (Admin+) wrap registry.py's T02 functions with
the same rights check and audit logging. Every action funnels through
record_audit_event, independent of the Trust Service's own signed
records.
tests/test_control_plane.py (12 tests): rights enforcement at each
tier boundary, the full propose -> approve -> appended-under-reviewer
flow, propose -> reject -> nothing appended, double-review rejection,
audit log content/attribution, DB-level UPDATE rejection on both new
tables. Full suite: 84 offline (unchanged), 53 with Docker (up from
41); no stray containers left running.
2026-07-30 15:08:59 +02:00
|
|
|
|
**Result:** `migrations/0006_control_plane.sql` adds
|
|
|
|
|
|
`control_plane_audit_log` (append-only, no UPDATE/DELETE grant for
|
|
|
|
|
|
`trf_app`) and `control_plane_proposed_entries` (the Contributor tier's
|
|
|
|
|
|
"propose, don't append" workflow from concept §2 — review decisions go
|
|
|
|
|
|
through a `review_proposed_entry()` SECURITY DEFINER function, same
|
|
|
|
|
|
governance-action pattern as `set_extension_status`/`revoke_credential`,
|
|
|
|
|
|
not a direct UPDATE). `src/target_revenue/control_plane.py` is the
|
|
|
|
|
|
enforcement layer concept §2 called for: `register_phase`/
|
|
|
|
|
|
`append_development_credit` require Operator+; `propose_ledger_entry`
|
|
|
|
|
|
requires Contributor+ and stores a pending proposal rather than touching
|
|
|
|
|
|
the real Ledger; `approve_proposed_entry` (Operator+) appends it — under
|
|
|
|
|
|
the *reviewer's own* credential/attribution, not the original proposer's,
|
|
|
|
|
|
since the reviewer is the one authorizing it into the real Ledger, while
|
|
|
|
|
|
the proposer stays on record in the proposal row and audit log;
|
|
|
|
|
|
`reject_proposed_entry` (Operator+) discards it with nothing appended.
|
|
|
|
|
|
`issue_user_credential`/`revoke_user_credential` (Admin+) wrap
|
|
|
|
|
|
`registry.py`'s T02 functions with the same rights check and audit
|
|
|
|
|
|
logging. Every action funnels through `record_audit_event` — actor
|
|
|
|
|
|
credential label, action, Phase, and (where applicable) the resulting
|
|
|
|
|
|
Trust Service record id, independent of the Trust Service's own signed
|
|
|
|
|
|
records.
|
|
|
|
|
|
|
|
|
|
|
|
`tests/test_control_plane.py` (12 tests): rights enforcement at each
|
|
|
|
|
|
tier boundary (Viewer can't register, Contributor can't append directly
|
|
|
|
|
|
or review, non-Admin can't issue credentials), the full propose → approve
|
|
|
|
|
|
→ appended-under-reviewer's-credential flow, propose → reject → nothing
|
|
|
|
|
|
appended, double-review rejection, audit log content and attribution, and
|
|
|
|
|
|
DB-level UPDATE rejection on both new tables. Full suite: 84 passing
|
|
|
|
|
|
offline (unchanged), 53 passing with Docker (up from 41); no stray
|
|
|
|
|
|
containers left running.
|
|
|
|
|
|
|
2026-07-30 11:02:45 +02:00
|
|
|
|
## Control Plane interactive UI: Phase registration and Development Credit entry
|
|
|
|
|
|
|
|
|
|
|
|
```task
|
2026-07-30 13:06:36 +02:00
|
|
|
|
id: TREV-WP-0009-T04
|
Implement WP-0009-T04: Control Plane interactive UI on whynot-design
Builds the Control Plane's browser UI (login, dashboard, Phase
registration, Development Credit entry/proposal/review, credential
admin, audit log) as a FastAPI + Jinja2 app over the already-finished
T03 backend, rather than from scratch — whynot-design's Lit web
components are vendored as static assets (source commit 4b62cffc,
v0.4.1), with lit itself resolved via an esm.sh CDN import map.
Session auth re-checks the credential token against the database on
every request rather than trusting the session cookie's cached rights,
so a mid-session revocation takes effect immediately.
9 new Docker-gated HTTP-level tests via FastAPI's TestClient (no
browser-automation tool available, so real rendering of the <wn-*>
components was never visually verified). All four WP-0009 tasks are
now done; workplan marked finished.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 15:43:16 +02:00
|
|
|
|
status: done
|
2026-07-30 11:02:45 +02:00
|
|
|
|
priority: high
|
2026-07-30 11:03:40 +02:00
|
|
|
|
state_hub_task_id: "01b295f2-8f97-4fc4-a14e-6f68aa85458d"
|
2026-07-30 11:02:45 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-07-30 13:06:36 +02:00
|
|
|
|
Implement the interactive flows themselves, using T03's auth/audit layer
|
|
|
|
|
|
and the existing hosted Trust Service API (`service/app.py`) plus T02's
|
|
|
|
|
|
sub-credential extension as the backend:
|
2026-07-30 11:02:45 +02:00
|
|
|
|
|
|
|
|
|
|
- Phase registration, informed by
|
|
|
|
|
|
`workplans/TREV-WP-0010-development-effort-calculator.md`'s output
|
|
|
|
|
|
once available (not blocking — a manual `target_basis` entry path must
|
|
|
|
|
|
work standalone too, since the Calculator may not be ready first);
|
|
|
|
|
|
- the priority flow: interactively creating a `development-credit` ledger
|
|
|
|
|
|
entry against a selected, already-registered Phase (concept §3's
|
|
|
|
|
|
step-by-step description) — select Phase, fill amount/currency/
|
|
|
|
|
|
recognized-date/evidence-reference/Monetization-Extension, submit,
|
|
|
|
|
|
immediately show the updated `GET /phases/{id}/metrics` result;
|
|
|
|
|
|
- read-only views (Phase status, Ledger, Attestation, Breach/Compliance
|
|
|
|
|
|
Record history) — already public/unauthenticated at the Trust Service
|
|
|
|
|
|
layer, so these need no new backend work, only UI.
|
Implement WP-0009-T04: Control Plane interactive UI on whynot-design
Builds the Control Plane's browser UI (login, dashboard, Phase
registration, Development Credit entry/proposal/review, credential
admin, audit log) as a FastAPI + Jinja2 app over the already-finished
T03 backend, rather than from scratch — whynot-design's Lit web
components are vendored as static assets (source commit 4b62cffc,
v0.4.1), with lit itself resolved via an esm.sh CDN import map.
Session auth re-checks the credential token against the database on
every request rather than trusting the session cookie's cached rights,
so a mid-session revocation takes effect immediately.
9 new Docker-gated HTTP-level tests via FastAPI's TestClient (no
browser-automation tool available, so real rendering of the <wn-*>
components was never visually verified). All four WP-0009 tasks are
now done; workplan marked finished.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 15:43:16 +02:00
|
|
|
|
|
|
|
|
|
|
**Result:** Built on `whynot-design` (vendored, source commit
|
|
|
|
|
|
`4b62cffc86496d587ac8d48a8e199624bc4a5c1f`, v0.4.1 — see
|
|
|
|
|
|
`src/target_revenue/service/static/whynot-design/VENDORED.md` for what is
|
|
|
|
|
|
vendored versus resolved via an `esm.sh` CDN import map for the `lit`
|
|
|
|
|
|
peer dependency, a disclosed v0 trade-off) rather than from scratch, per
|
|
|
|
|
|
the explicit direction to check feasibility first. New FastAPI app
|
|
|
|
|
|
`src/target_revenue/service/control_plane_app.py`, session-based sign-in
|
|
|
|
|
|
(paste-a-credential-token, `itsdangerous`-signed cookie via Starlette's
|
|
|
|
|
|
`SessionMiddleware`, re-authenticated against the database on every
|
|
|
|
|
|
request so a mid-session revocation takes effect immediately, never
|
|
|
|
|
|
trusted from the cookie alone), seven Jinja2 templates under
|
|
|
|
|
|
`service/control_plane_templates/` (`base`, `login`, `dashboard`,
|
|
|
|
|
|
`phase_new`, `phase_detail`, `proposals`, `admin_credentials`, `audit`),
|
|
|
|
|
|
all wired to real `control_plane.py`/`registry.py`/`ledger.py`/`metrics.py`
|
|
|
|
|
|
calls — no mock data. Routes cover: login/logout; dashboard (list Phases
|
|
|
|
|
|
for the signed-in tenant); Phase registration (Operator+); Phase detail
|
|
|
|
|
|
(status/metrics + Ledger table); the priority flow — append a
|
|
|
|
|
|
Development Credit entry directly (Operator+/Admin) or submit it for
|
|
|
|
|
|
review (Contributor), same form, button label switches on rights;
|
|
|
|
|
|
proposal review (approve/reject, Operator+); credential issue/revoke
|
|
|
|
|
|
(Admin); and the Control Plane's own audit log (any signed-in user).
|
|
|
|
|
|
Rights gating is enforced identically to T03's backend — the UI layer
|
|
|
|
|
|
adds no new authorization logic, it only redirects with a flash message
|
|
|
|
|
|
when a route requires more than the signed-in credential's tier.
|
|
|
|
|
|
|
|
|
|
|
|
Tests: `tests/test_control_plane_app.py`, 9 Docker-gated tests via
|
|
|
|
|
|
FastAPI's `TestClient` (same disposable-Postgres-per-module pattern as
|
|
|
|
|
|
`test_control_plane.py`) — covers unauthenticated redirect to `/login`,
|
|
|
|
|
|
invalid-token rejection, successful login/dashboard, rights-gated route
|
|
|
|
|
|
redirects (viewer blocked from Phase registration and admin pages), the
|
|
|
|
|
|
full Operator register-Phase-then-append-entry flow, the
|
|
|
|
|
|
Contributor-proposes/Operator-reviews flow, and admin credential
|
|
|
|
|
|
issuance. Explicit scope disclosure: these are HTTP-level tests against
|
|
|
|
|
|
the FastAPI app, not real-browser tests — no browser-automation tool is
|
|
|
|
|
|
available in this environment, so the `<wn-*>` web components' actual
|
|
|
|
|
|
rendering/interactivity was never visually verified, only the
|
|
|
|
|
|
server-rendered HTML/session/redirect behavior around them. Full suite:
|
|
|
|
|
|
84 passing offline (unchanged), 146 passing with Docker (up from 53); no
|
|
|
|
|
|
stray containers left running.
|
|
|
|
|
|
|
|
|
|
|
|
All four WP-0009 tasks (T01–T04) are now done — this workplan is
|
|
|
|
|
|
finished.
|