--- 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//raw/main/...` to `forgejo.coulomb.social//raw/branch/main/...` via `reuse-surface hub update --repo --url `. 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: todo priority: medium ``` Cross-repo, in `~/evidence-binder`. Both entries in `registry/indexes/capabilities.yaml` ("Evidence-to-target binding", "Visual-guide rect registry") lack the required `id`. Add ids consistent with the registry naming convention, confirm against `schemas/capability.schema.yaml`, and check whether `registry/capabilities/*.md` entries exist behind them or whether the index rows are orphans. Route rather than edit directly if that repo has an owning agent. ## 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-`; 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.