policy-nexus/workplans/POLICY-NEXUS-WP-0001-permanent-publication-surface.md
tegwick 03a4fab9e0
Some checks failed
Build and publish policy-nexus image / build-and-push (push) Failing after 2s
Automate policy source freshness and inventory
2026-08-18 13:25:49 +02:00

349 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id: POLICY-NEXUS-WP-0001
type: workplan
title: "Stand up policy.coulomb.social as the permanent publication surface"
domain: infotech
repo: policy-nexus
status: finished
owner: the-custodian
topic_slug: policy-nexus
created: "2026-08-17"
updated: "2026-08-18"
state_hub_workstream_id: "7e3c9720-fe13-482c-9b3d-db1b741cbe6c"
---
# POLICY-NEXUS-WP-0001 — permanent publication surface
## Goal
Replace disposable artifact-page publication with permanent infrastructure at
`policy.coulomb.social` that keeps policy **available**, **addressable**,
**current**, and **honest about its own staleness**.
Done means: a governing document lives in its owning repo, is published
automatically, keeps its URL, shows its status and review date, retains its
superseded versions, and can be reached by someone outside the estate.
## The forcing case
`net-kingdom/canon/standards/tenancy-posture_v0.1.md` (*Tenancy Posture*,
draft-8) was reviewed by six repos —
`tenant-engine`, `flex-auth`, `rapp-postgres`, `railiance-platform`,
`adaptive-pricing`, `audit-core`. It is currently served from a private,
disposable artifact URL. Routing a document that governs six repos to a link
that may not resolve later is the problem this workplan exists to end.
Tenancy Posture is therefore the first publication and the acceptance test. If the site
cannot carry it correctly — six ladders, a threat matrix, an E×P grid, and its
owner-attributed questions and resolutions — the site is not finished.
## Existing structure this workplan must respect
**The renderer is already here.** `tools/render.py` and `tools/style.css`
arrived from `the-custodian` on 2026-08-17, along with `make build`. Stdlib
only, no dependency tree, recognising conventions already present in the
document rather than requiring extra markup. It was written because the page
and the source had diverged. T02 generalises it from one document to many; it
does not start from scratch.
**Source of truth stays upstream.** Canon lives in its owning repo — including
`the-custodian/canon` and `net-kingdom/canon` — while per-repo ADRs live in
their own repos. This repo reads and never writes back. A publication surface
with write authority is a second source of truth, and the estate has a standing
rule against that.
**The adoption stance applies** (Tenancy Posture §14): adopt published standards and
structural patterns; build tooling ground-up unless it is an established
industry standard with broad application. A static-site generator with a
plugin ecosystem is exactly what this rule excludes.
**Platform packages are consumed, not rebuilt.** TLS, DNS, ingress, and any
credential come from the existing platform. If this repo needs storage it is a
`rapp-postgres` consumer and declares a posture vector like anyone else.
## Non-goals
- No editing surface. No CMS, no browser drafting, no comments.
- No ratification workflow. The site may *show* that a draft is in flight and
for how long; advancing it is a canon-process decision.
- No authentication in this workplan. Everything published here is
public-by-intent; anything that is not does not belong on this surface.
- No search index in this workplan. Deferred until there is enough content for
search to beat a good index page.
## Tasks
### T01 — Addressing scheme and permanence contract
```task
id: POLICY-NEXUS-WP-0001-T01
status: done
priority: high
state_hub_task_id: "04988e76-bde8-42a2-9269-90467c767a65"
```
Decide, once, how a document maps to a URL, and write down what the estate is
promising about that URL.
- URL shape for a document, and for a specific revision of it.
- What happens on supersession: the old URL keeps resolving and gains a
superseded banner pointing forward.
- What happens on withdrawal: distinguish *withdrawn* (kept, marked) from
*deleted* (does not happen).
- Where the version history lives, given the source is git in another repo.
- The commitment being made — a URL published here is expected to resolve
indefinitely, and what would have to happen for that to be broken.
**Output:** `docs/adr/ADR-0001-addressing-and-permanence.md`.
**Why first:** everything downstream bakes in the answer, and changing it later
breaks the one promise the repo exists to make.
Completed 2026-08-18 in `docs/adr/ADR-0001-addressing-and-permanence.md`.
The contract distinguishes stable current addresses from immutable revision
addresses and retains superseded and withdrawn publications.
### T02 — Generalise the renderer
```task
id: POLICY-NEXUS-WP-0001-T02
status: done
priority: high
state_hub_task_id: "d20a3d19-65a7-4479-b46a-a42fa96cbe81"
```
Lift `tools/render.py` from one document to many.
- Multi-document: a manifest of sources rather than one path argument.
- Front-matter driven: title, status, revision, review date, owner from the
source document rather than from command-line flags.
- Index generation: a landing page listing documents with status and currency.
- Keep the convention recognisers (level ladders, threat matrix, E×P grid,
evidence chips, section rail) and keep it stdlib-only.
- Keep the "generated from canonical source — do not edit" marker on every page.
**Acceptance:** Tenancy Posture renders byte-identically in substance to the current
generated page, plus an index entry.
Completed 2026-08-18. `publication.json` drives a stdlib-only multi-document
builder. It emits a current page, immutable revision, legacy alias, index and
machine-readable publication manifest. Tests cover path safety, revision
immutability, lifecycle notices and currency.
### T03 — Source ingestion
```task
id: POLICY-NEXUS-WP-0001-T03
status: done
priority: high
state_hub_task_id: "0b76e184-dcd2-4c12-92e1-49d1f2e0a431"
```
Define how a document gets from its owning repo to this one.
- Manifest format: source repo, path, publication URL, owner.
- **Scope is canon and ADRs only.** The corpus is bounded and countable today:
two canon trees (`the-custodian/canon`, `net-kingdom/canon`) and roughly 68
ADRs across 18 repositories. Workplans, evidence and runbooks are out — a
site that publishes everything publishes nothing in particular. Enumerate the
actual list during T03; a glob over `docs/adr/*.md` plus the canon trees is
the starting point, but each canon subdirectory (`standards`, `architecture`,
`constitution`, `values`, `tpsc`, `projects`) needs a yes or no rather than a
wildcard.
- Fetch mechanism for documents in other repositories, and how a fetch failure
is surfaced rather than silently serving stale content.
- Determinism: the same source commit must produce the same page.
- Record the source commit on the published page, so a reader can tell exactly
what was rendered.
**Open decision for T03:** pull (this repo fetches on a schedule) or push (the
owning repo triggers on merge). Pull is simpler and keeps the direction of
dependency clean; push is fresher. Recommend pull with a manual trigger, and
record the choice.
Completed 2026-08-18. The pull model is now explicit and automated. A reviewed
inventory enumerates 124 committed sources across 21 repositories: one is
published, 113 await source-owner publication metadata/addressing, eight are
non-Markdown formats without a renderer, and two are ADR directory indexes.
The inventory also records the explicit exclusion of `values`, `tpsc`, and
`projects`. New or removed matching sources fail the audit until their
disposition is reviewed.
The Forgejo workflow checks out every external source at an exact commit on a
daily schedule and manual dispatch, records a deterministic source-set digest,
and fails visibly on fetch, inventory, currency, or release verification
errors. It publishes an immutable source-set candidate only. Promotion remains
the separate paired OCI/publication-digest approval owned by
`rapp-policy-nexus` and `railiance-apps`. Release builds also retain the prior
generated tree so an unchanged semantic revision cannot acquire rewritten
immutable provenance when an upstream repository advances for unrelated work.
### T04 — Deploy to policy.coulomb.social
```task
id: POLICY-NEXUS-WP-0001-T04
status: done
priority: high
state_hub_task_id: "dd6169e4-9c93-4c5a-b312-d5cd21d0971a"
```
- DNS, TLS, ingress via the existing platform packages.
- Static hosting — the output is static files by construction, so the serving
layer should be the least interesting part of this workplan.
- Availability expectation stated plainly, with the honest caveat that a
single-node rail gives restart recovery, not high availability. Do not claim
an SLA the substrate cannot support.
- Rollback: republishing a previous build must be a single command.
- Smoke check after every deploy: the index resolves, Tenancy Posture resolves, and a
known superseded URL still resolves.
2026-08-18: `policy-nexus` now owns a digest-pinned, non-root OCI artifact
contract and a Forgejo publication workflow. `rapp-policy-nexus` owns the
separately reviewable Helm package, runtime hardening, public smoke checks and
rollback; `railiance-apps` owns only the production digest binding. DNS already
resolves and the reef has the required Traefik/cert-manager substrate. A clean
source release, registry digest, server-side dry-run, deployment and live smoke
evidence were the closing gates described below.
2026-08-18 completion: Forgejo Actions published the clean draft-8 artifact at
OCI digest `sha256:23318442d1b3a5bdced54f0b407fa5b32834020b80c60d72dea04c03bfbf51e7`.
Helm revision 1 is deployed and Ready on `reef-railiance`; Let's Encrypt TLS is
Ready; the index, canonical current page, immutable draft-8 revision, legacy
permanent redirect, image pin, and publication digest all pass the live rApp
verifier. The package records the single-node restart-recovery caveat and
defers rollback exercise until a predecessor revision exists.
### T05 — Currency and staleness
```task
id: POLICY-NEXUS-WP-0001-T05
status: done
priority: medium
state_hub_task_id: "d5a6feee-5b57-47b8-a026-cb0c8dd9c3df"
```
The relevance half of the repo's purpose. A permanently available document that
is quietly out of date is worse than no document.
- Every page shows status, revision, and last-reviewed date.
- A review interval per document, and a visible marker once exceeded.
- A report of documents past review, and where it is delivered.
- Drafts show how long they have been in flight. The
`shared-platform-relational-storage_v0.1` draft has been routed and
unratified since 2026-08-10; that fact should be visible on the site, because
invisibility is precisely why it stalled.
Completed 2026-08-18 for the published corpus. Pages and the index expose
review due dates and overdue state; `make currency` exits non-zero for stale or
undeclared review metadata. The daily source-pull workflow is the delivery
channel for that failure, and expansion follows the reviewed T03 inventory.
### T06 — withdrawn
```task
id: POLICY-NEXUS-WP-0001-T06
status: done
priority: low
state_hub_task_id: "8b71def7-4820-406f-99fd-8c38248f9bea"
```
Regulatory intake moved to `risk-nexus` on 2026-08-17. Deciding what a rule
demands of the estate is a judgement about risk, not an act of publishing, and
it wanted a different owner and a different skill from everything above.
`risk-nexus` publishes *through* this repo. When it has records to publish they
arrive as another source in the T03 manifest, not as a second content type this
repo curates.
The consequence is the point: **this repo now does one thing.** Publication,
permanence, currency. That makes its ownership answerable and its boundary
defensible.
## Sequencing
T01 gates everything. T02 and T03 can proceed in parallel once addressing is
fixed. T04 needs both. T05 is additive. T06 is deliberately last.
The temporary artifact URL for Tenancy Posture stays live until T04 passes its smoke
check, and the review is routed to the permanent URL, not before.
## Risks
**This repo becomes a second source of truth.** The likeliest failure and the
most damaging. Mitigation: no editing surface, generated pages carry a
do-not-edit marker, and the manifest records the source commit for every page.
**Permanence is promised before it can be delivered.** A URL committed to in
T01 and broken in year two is worse than never having promised. Mitigation:
T01 states what would break the promise, and T04 declines to claim an
availability level the single-node rail cannot support.
**Scope drift toward a CMS.** Every publication surface attracts requests for
editing, comments, and workflow. Mitigation: the non-goals above are part of
the workplan, not a preface to it.
**The renderer grows a dependency tree.** The moment it needs a framework, it
becomes something only CI can run. Mitigation: stdlib-only is an acceptance
criterion on T02, not a preference.
## Resolved by the operator, 2026-08-17
- **Publication scope is canon and ADRs.** Not workplans, evidence, runbooks or
general documentation. Folded into T03 and INTENT.
- **Owner is `the-custodian`**, which owns both policy and risk, and carries
the duty of deciding what must be discussed with the operator personally.
- **Regulatory intake left the repo.** It is `risk-nexus`'s (T06, withdrawn).
**Authority is three-way and stated, not held by one repo.** `policy-nexus`
owns the surface — addressing, permanence, rendering, currency.
`the-custodian` owns what counts as canon and when it is ratified; this repo
renders that judgement and never makes it, which is the guard against a
cross-repo reader accreting authority over "what is current".
`railiance-platform` owns the substrate — DNS, TLS, ingress, hosting.
The tell that the split is right: **T01 and T04 need different competences.**
T01 is a canon-process question — what supersession means, what a permanent URL
promises. T04 is infrastructure. A single owner would be weak at one.
The README's one-line description — "a convergence and publication point for
government policies" — reads broader than this. Worth updating so the repo does
not attract the wrong contributions.
## Resolved publication scope
ADR-0001 records the bounded answer: `standards`, `architecture` and
`constitution` are in; `values`, `tpsc` and `projects` are out unless their
owner explicitly identifies an individual governing document. T03 must still
enumerate each publication rather than globbing those trees.
## Deferred: controlled disclosure
**Resolved for now (operator, 2026-08-17): full public disclosure is fine.** The
estate is in build mode, not production. Publishing architecture, known gaps and
residual risks openly costs little while there are no users to expose and no
attacker with anything to gain.
**That stops being true at production.** ADR-0001 §5 publishes a blast radius
because its consumers must read it; the same discipline applied publicly, once
real tenant data exists, hands an attacker a map. The estate will then need a
**controlled-disclosure scheme**: a way to hold a finding while it is fixed, and
publish it once it is — embargo, coordinated timing, and a record that the delay
was deliberate rather than a document quietly going missing.
This workplan does **not** build that, and should not. Two reasons:
1. It is a different problem. Publication is about permanence and currency;
embargo is about risk assessment, severity and timing. Building embargo into
a publication surface would put risk judgement in the repo least qualified to
make it.
2. It likely belongs to a service of its own — a **`risk-nexus`**, by analogy —
owning finding intake, severity, remediation tracking and disclosure timing,
with this repo as its publication surface rather than its brain.
**What T01 must do about it now:** nothing more than leave room. The addressing
scheme should not assume every document is public from the moment it exists, so
that adding an embargo state later is a new status rather than a URL migration.
Recording that constraint costs nothing today and is expensive to retrofit.
**Trigger to revisit:** the first real tenant, or the first finding that would
be dangerous to publish before it is fixed — whichever comes first.