reuse-surface/workplans/REUSE-WP-0020-coulombcore-retirement-cutover.md
tegwick 3aaf961352 Route evidence-binder index fix to its owner (REUSE-WP-0020-T03)
Inspection showed the defect is larger than the missing id that broke compose:
both rows use a non-conforming shape and registry/capabilities/ is empty, so
they are orphans with no backing entry Markdown.

Left to that repo rather than edited here. Reshaping the rows is mechanical,
but assigning maturity vectors is an assessment of their own delivery state.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 00:16:53 +02:00

274 lines
11 KiB
Markdown

---
id: REUSE-WP-0020
type: workplan
title: "CoulombCore retirement cutover: federation sources, image, and compose resilience"
domain: infotech
repo: reuse-surface
status: active
owner: claude
topic_slug: helix-forge
created: "2026-08-20"
updated: "2026-08-20"
---
# CoulombCore retirement cutover
CoulombCore is switched off **2026-08-31** (operator decision, 2026-08-20).
`gitea.coulomb.social` runs on it. Raised by `prj-state-hub-retirement`
(SHR-WP-0002-T07) as a stale-image report; investigation found a second,
more urgent problem underneath it.
**Two independent failures**, not one:
1. **Federation source data** — the production hub's *registrations* pointed
50 of 61 sources at `gitea.coulomb.social`. This breaks on 08-31 with
nobody restarting anything. It is database state, not code:
`registry/federation/sources.yaml` at HEAD has been 61/61 Forgejo since
`d1c1313` (RAIL-HO-WP-0006).
2. **Deployed image** — `railiance-apps/helm/reuse-surface-values.yaml` pins
`image.tag: "e3ae22e"`. The chart's `repository` was already migrated to
Forgejo in `railiance-apps@04be416`, but `e3ae22e` is commit-dated
2026-07-07 18:25 and CI only began publishing to Forgejo at 21:25 the same
day — so a Forgejo `:e3ae22e` tag most likely never existed. Any restart,
reschedule, or node reboot risks `ImagePullBackOff` **now**, not on 08-31.
Item 1 was resolved on 2026-08-20 (see T01). Doing so exposed a third issue:
a single malformed member index takes the whole federated endpoint down.
**Baseline vector:** `D5 / A4 / C5 / R3` — unchanged by this workplan;
this is operational continuity, not capability growth.
## Scope boundary
The Deployment manifest is **not in this repo**. It lives in `railiance-apps`
(`charts/reuse-surface/`, `helm/reuse-surface-values.yaml`, RAILIANCE-WP-0007).
This repo owns the image contents, the deploy guide, and the federation data;
applying the manifest change requires a working `KUBECONFIG=~/.kube/config-hosteurope`,
which is not reachable from the workstation without an ops-bridge tunnel.
---
## Repoint Production Federation Sources To Forgejo
```task
id: REUSE-WP-0020-T01
status: done
priority: high
```
**Completed 2026-08-20.** Updated 50 enabled hub registrations from
`gitea.coulomb.social/<repo>/raw/main/...` to
`forgejo.coulomb.social/<repo>/raw/branch/main/...` via
`reuse-surface hub update --repo <r> --url <new>`.
Notes for the record:
- All 50 new URLs were pre-verified `200` before any write.
- The canonical Forgejo raw form is `/raw/branch/main/`. `/raw/main/` answers
`303` and works only because `urllib.request.urlopen` follows redirects —
`registry/federation/sources.yaml` still uses the redirecting form. Harmless,
but prefer the canonical form in new writes.
- One registration (`inter-hub`, already `disabled`) still carries a Gitea URL.
Its Forgejo equivalent answers `307`, so it was left alone rather than
repointed to something unverified. It contributes nothing while disabled.
Verified: `GET /v1/federated` → 60 sources, all Forgejo, 62 capabilities.
## Harden Compose Against A Malformed Member Index
```task
id: REUSE-WP-0020-T02
status: done
priority: high
```
T01 briefly took `/v1/federated` to HTTP 500. Root cause: `evidence-binder`'s
live index has two capability entries with **no `id` field**, and
`compose_federated_index` does `cap_id = item["id"]` unguarded. Its Gitea copy
was a stale snapshot returning a non-mapping, so the entries had never been
parsed before.
**One bad member index must not take down the federated endpoint.** In
`reuse_surface/federation.py::compose_federated_index`, guard the per-source
body so a failure degrades to a warning and the remaining sources still
compose:
| Failure | Current | Wanted |
|---|---|---|
| `item["id"]` missing | `KeyError`, 500 | skip entry, warn, keep source |
| `yaml.safe_load` → non-mapping | `AttributeError`, 500 | skip source, warn |
| `yaml.safe_load` → `None` (empty file) | `AttributeError`, 500 | skip source, warn |
| YAML parse error | `YAMLError`, 500 | skip source, warn |
| `sorted(key=item["id"])` | `KeyError` | unreachable once entries are filtered |
Confirmed present at HEAD, not only in the deployed image — deploying HEAD
would **not** have fixed this. Land T02 before T04.
Pytest in `tests/test_federation.py`: member index with a missing `id`, an
empty file, a non-mapping body, and unparseable YAML — each composes the
other sources successfully and emits a warning naming the source.
**Done 2026-08-20.** Extracted `_read_index_entries()`; every read failure now
returns an empty row list plus a warning, and rows without an `id` are skipped
individually. A failed source stays in `sources` with `count: 0` rather than
vanishing, so operators can still see it. Six tests added. Verified against the
real `evidence-binder` index: composes clean with two skip warnings instead of
raising.
## Fix The evidence-binder Capability Index
```task
id: REUSE-WP-0020-T03
status: wait
priority: medium
```
Cross-repo, in `~/evidence-binder`. **Routed 2026-08-20**, not edited directly —
that repo is active (last commit 2026-07-30) and has its own identity and topic.
The defect is larger than the missing `id`. Both rows use an entirely
non-conforming shape — `type` / `title` / `description` / `keywords` instead of
`id` / `name` / `summary` / `vector` / `domain` / `status` / `owner` / `path` /
`tags` / `consumption_modes`. History shows `597b489` added the
REUSE-WP-0014-T04 scaffold and then `a10f080` ("Extract binder package from
citation-evidence") hand-wrote these two rows around it. `registry/capabilities/`
is empty, so both index rows are orphans with no backing entry Markdown.
Left to that repo's owner because the remaining judgment is theirs: reshaping
the rows is mechanical, but assigning D/A/C/R maturity vectors is an assessment
of their own delivery state, and invented values would be wrong in a way that is
hard to notice later. The message offers `capabilities: []` as an honest
alternative — ten other federation members sit at count 0 — and offers to do the
work here if they supply vectors or accept a conservative `D1/A0/C0/R0`
placeholder marked for review.
Suggested ids: `capability.evidence.binding`,
`capability.evidence.rect-registry`.
## Re-Enable The evidence-binder Source
```task
id: REUSE-WP-0020-T04
status: wait
priority: medium
```
Blocked on T02 and T03. `evidence-binder` was set `--no-enabled` on the
production hub to restore service. Once its index is valid and compose is
hardened, re-enable and confirm `/v1/federated` returns 61 sources.
## Repoint The Production Deployment To A Forgejo Image
```task
id: REUSE-WP-0020-T05
status: todo
priority: high
```
Deadline-bound: must land before **2026-08-31**.
1. ~~Verify which tags actually exist.~~ **Done 2026-08-20.** Authenticated
against the registry with the workstation's stored Forgejo credentials:
```
GET /v2/coulomb/reuse-surface/tags/list
→ {"tags": ["latest", "main-bca7165", "main-f9d957a"]}
```
**There is no `e3ae22e` tag.** The pinned manifest cannot pull today — this
is confirmed, not inferred. `main-f9d957a` (2026-07-08, REUSE-WP-0019-T06)
is the newest build; the 10 commits between it and HEAD were docs, workplans,
and licence changes that do not match the image workflow's path filters, so
no newer image exists yet.
2. Bump `image.tag` in `railiance-apps/helm/reuse-surface-values.yaml` from
`e3ae22e` to a verified Forgejo tag.
**Prefer a build of current HEAD over `main-f9d957a`.** T02's compose
hardening is not in `main-f9d957a`, and without it re-enabling
`evidence-binder` (T04) would take production to HTTP 500 again. Pushing
HEAD touches `reuse_surface/**`, so CI will produce `main-<short-sha>`;
deploy that tag. Not pushed yet — pushing to the shared remote needs an
explicit go-ahead.
Left deliberately unmodified for now: writing a tag that has not been built
is worse than leaving the current wrong one, since it converts a latent
failure into an immediate one on the next reconcile.
3. Correct `docs/deploy/reuse-kubernetes.md`, which still describes Gitea as
the live registry and its manual `docker build`/`push` commands still
target `gitea.coulomb.social`.
4. Apply from a host with cluster access; verify rollout, `/v1/federated`,
and that the PVC at `/data` survived.
Ships REUSE-WP-0019 **T04/T05/T06** as a side effect — telemetry store,
R-axis aggregation, and hub freshness monitoring were closed as finished but
landed after the deployed commit and have never run in production.
`GET /v1/reuse-events` returning `404` on production confirms this.
Review the six weeks of change between `e3ae22e` and HEAD before applying.
## Correct Hub Freshness And Health Routing
```task
id: REUSE-WP-0020-T06
status: todo
priority: low
```
Two smaller production inconsistencies found while verifying T01:
- `GET /health` returns nginx `404` through the ingress, though
`docs/deploy/reuse-kubernetes.md` documents it as the liveness path and the
landing page links it. Only `/v1/*` routes. Pod probes hit the container
directly so the service is unaffected, but the documented URL is wrong —
fix the ingress route or the docs, whichever matches intent.
- `composed_at` stayed at `2026-08-20T20:44:12` with `stale: false` across a
recompose that demonstrably changed output (61 → 62 capabilities). The
freshness timestamp is not tracking recomposes. Re-check after T05, since
REUSE-WP-0019-T06 touches exactly this and is not deployed.
## Refresh SCOPE.md Standard Sections
```task
id: REUSE-WP-0020-T07
status: todo
priority: low
```
Unrelated to the retirement, recorded so it is not lost. The hub's repo scope
check reports `C5b`/`C5c` warnings: `SCOPE.md` is missing the standard H2
sections *Relevant When*, *Not Relevant When*, *How It Fits*, *Terminology*,
*Related / Overlapping*, *Provided Capabilities*, and carries no fenced
capability block.
## Fix Wall-Clock Rot In The Plan-Check Test Suite
```task
id: REUSE-WP-0020-T08
status: done
priority: medium
```
Found while verifying T02: `tests/test_plan_check.py` was already failing at
clean HEAD, independent of any change here. Three tests pinned the federated
index compose date to the literal `"2026-07-06"`, and
`test_run_plan_check_reuse_verdict` asserts no staleness warning — so the test
passed when written and started failing once wall-clock time drifted past
`STALE_DAYS`. **CI is red today for this reason alone**, which matters because
T05 calls for verifying the image workflow is green.
**Done 2026-08-20.** Replaced the literals with a `_recent_date()` helper
derived from `STALE_DAYS`. Full suite: 179 passed.
---
## Verification
```bash
python3 -m pytest tests/test_federation.py -q
python3 -m reuse_surface.cli validate
python3 -m reuse_surface.cli federation compose --refresh
REUSE_SURFACE_URL=https://reuse.coulomb.social python3 -m reuse_surface.cli hub list
curl -s https://reuse.coulomb.social/v1/federated | python3 -m json.tool | head -20
```
No source may reference `gitea.coulomb.social` after this workplan closes.