Invalidate the composed index when a registration changes (REUSE-WP-0020-T09)
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
ci / validate-registry (push) Successful in 1m18s
Build and Publish Container Image / build-and-push (push) Successful in 23s

Enabling a federation source left /v1/federated serving its cached compose and
reporting stale: false while doing it, so a repo could be correctly registered
and silently invisible for as long as its cached index survived. That is how
evidence-binder stayed missing after re-enabling until a manual
POST /v1/federated/compose was issued.

Registration writes now mark the composed index stale, and a plain GET
recomposes when the flag is set. Clearing it there is not a silent clear: that
pass really did refetch. A PATCH touching only a description does not
invalidate anything.

This changes a contract documented in specs/FederationHubAPI.md, so the
staleness section is rewritten rather than left to drift, including the two
triggers that now set the flag.

The first two tests written for this were worthless -- they passed with the fix
removed, because a newly registered repo has no cache entry and gets fetched
regardless. The real failure needs a populated cache holding stale content
inside its 24h TTL. test_re_enabled_source_refetches_a_stale_cache models that
and fails on pre-fix code; verified by reverting the mark_stale calls.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-08-21 13:51:58 +02:00
parent d1de320743
commit 6cbc862371
5 changed files with 235 additions and 26 deletions

View file

@ -199,14 +199,28 @@ composed_at: "2026-07-07T16:22:09+00:00" # REUSE-WP-0019-T02
stale: false # REUSE-WP-0019-T02
```
`composed_at`/`stale` (added REUSE-WP-0019-T02) track *forced* recomposes
only (`refresh=true`, the webhook, or the scheduled fallback) — a plain
`GET` still serves current best-effort data (per-source `cache_ttl_seconds`
still applies) but never silently clears a staleness signal nothing
actually refreshed. `composed_at` is `null` until the first forced
recompose since the hub process's SQLite DB was created. `stale: true`
means a `registry/indexes/` change was pushed (via webhook) since the last
forced recompose completed.
`composed_at`/`stale` (added REUSE-WP-0019-T02) track recomposes that
actually refetched — `refresh=true`, the webhook, the scheduled fallback, or
a plain `GET` that found the index marked stale. A plain `GET` on a
non-stale index still serves current best-effort data (per-source
`cache_ttl_seconds` still applies) and never clears a staleness signal
nothing acted on. `composed_at` is `null` until the first such recompose
since the hub process's SQLite DB was created.
`stale: true` means the composed index no longer reflects its inputs, set by
either of two triggers:
- a `registry/indexes/` change pushed by a member repo (via webhook), or
- a **registration change through this API** (REUSE-WP-0020-T09) — `POST
/v1/repos`, `DELETE /v1/repos/{repo}`, or a `PATCH /v1/repos/{repo}` that
touches `enabled`, `url`, `index`, `required`, or `domain`.
The second trigger closes a silent failure: before it, enabling a source left
the composed index untouched and still reporting `stale: false`, so a repo
could be correctly registered and yet invisible in `/v1/federated` for as long
as its cached index survived. The next `GET` now recomposes and clears the
flag — which is not a silent clear, because that pass really did refetch. A
recompose that *fails* leaves `stale: true` set.
Query parameters: