Implement WP-0015-T03: Control Plane reference-rendering routes

New service/reference_docs.py renders specs/policies/*.md and
specs/profiles/*.md read-only at request time via a small markdown
library (added markdown + PyYAML to the service extras) -- not a
static-build pipeline, matching the WP-0012-T03 decision to skip
state-hub's heavier Observable Framework pattern.

One parameterized route, GET /reference/{kind}/{slug}, covers both
addendum URL shapes. Discovered phase_detail.html's Status table never
displayed the degeneration_policy id at all -- added that row (with
the reference link) rather than wiring a link with nothing to attach
it to. phase_new.html gets a plain link next to the field.

Deliberately did not wire extension-id links into the UI in this task
-- extension ids don't appear anywhere in the Control Plane today
(that's WP-0014's gap, not this one's to expand).

6 new Docker-gated tests. Full suite: 94 passing offline, 164 passing
with Docker (up from 158).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-08-03 23:45:59 +02:00
parent 017c23b4c8
commit d894599647
9 changed files with 209 additions and 3 deletions

View file

@ -99,7 +99,7 @@ Offline suite unaffected (no code reads these doc files' content) —
```task
id: TREV-WP-0015-T03
status: todo
status: done
priority: medium
state_hub_task_id: "681002d5-582b-4ddd-8b1f-dc25ce5b0e5a"
```
@ -114,6 +114,39 @@ add it). Read-only, no edit capability. Link to these routes from
wherever a policy id or extension id already appears in
`phase_detail.html`/`phase_new.html`.
**Result:** New module `service/reference_docs.py` (`load_reference_doc`,
`policy_slug_from_id`, `extension_slug_from_id`), kept separate from
`control_plane_app.py` per this project's pattern of separating HTTP
framing from logic. One parameterized route,
`GET /reference/{kind}/{slug}` (`kind` restricted to `policies`/
`profiles`), satisfies both addendum URL shapes without two near-identical
route functions. Added `markdown>=3.6` and `PyYAML>=6.0` to the `service`
extras (frontmatter parsing).
**Discovered along the way**: `phase_detail.html`'s Status table never
displayed `manifest.phase.degeneration_policy` at all — there was no
existing spot to attach a link to. Added the row (with the reference
link) as part of wiring this in, rather than leaving the policy id
invisible on the one page where a human would want to check it.
`phase_new.html` gets a plain link next to the field (only one policy
exists today, so a static link is sufficient; revisit if a second policy
is ever offered as a choice there).
**Deliberately not done in this task**: extension ids don't currently
appear anywhere in the Control Plane UI at all (Phase registration
doesn't collect `extensions`, and the ledger-entry form's `extension_id`
is free-typed, not selected from a registered list) — that's WP-0014's
gap (Extension Registry has no UI yet), not something to silently expand
here. `extension_slug_from_id`/the `profiles` reference route exist and
are tested directly; wiring an actual extension-id link into the UI
waits for WP-0014.
6 new Docker-gated tests (`test_reference_policy_doc_renders`,
`_profile_doc_renders`, `_unknown_slug_is_404`, `_unknown_kind_is_404`,
`_requires_login`, `test_phase_detail_links_to_policy_reference`). Full
suite: 94 passing offline (unchanged, this task touches no offline
code path), 164 passing with Docker (up from 158).
```task
id: TREV-WP-0015-T04
status: done