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>
An index row with no id now says whether it looks like a SCOPE.md capability
block, instead of leaving the author to work out why their repo contributes
nothing to the federated index. That confusion is invisible without a
diagnostic: the row is valid YAML, the file parses, and the member just
silently disappears.
Swept all 61 federation sources with --refresh: evidence-binder is the only
affected member. The other ten zero-count repos are genuine empty scaffolds.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Repointing the production hub's 50 Gitea-hosted federation sources to Forgejo
ahead of the 2026-08-31 CoulombCore retirement took /v1/federated to HTTP 500.
One member index (evidence-binder) has capability rows with no `id`, and
compose_federated_index dereferenced item["id"] unguarded. Its Gitea copy was a
stale snapshot returning a non-mapping, so those rows had never been parsed.
A single malformed member index must not take down the whole endpoint. Extract
_read_index_entries(): unparseable YAML, a non-mapping body, an empty file, and
a non-list `capabilities` each degrade to a warning and an empty row list, and
rows without an `id` are skipped individually. A failed source stays listed with
count 0 so it remains visible to operators rather than silently disappearing.
Also fix wall-clock rot in tests/test_plan_check.py, which was already failing
at clean HEAD: three tests pinned the compose date to a literal that has now
aged past STALE_DAYS.
Add workplan REUSE-WP-0020 covering the full cutover.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
reuse_surface/stats.py: _hub_summary() now reports composed_at, stale,
age_days, freshness_threshold_days (REUSE_SURFACE_FRESHNESS_DAYS env,
default 7), and a computed stale_warning. New hub_client.hub_federated()
backs it. format_stats_markdown surfaces a STALE marker when triggered.
.forgejo/workflows/ci.yml: new informational (non-failing) hub freshness
check against the live production hub on every push -- prints a
:⚠️: annotation when stale, never fails the build.
docs/RegistryFederation.md: new section tying together the webhook (T02),
scheduled fallback (T03), and freshness visibility (T06) into one
explanation. docs/deploy/reuse-kubernetes.md: updated for the T03 Forgejo
migration and the now-automated image.yaml build; image promotion
checklist updated for the known /health ingress bug (verify via
/v1/repos or /v1/federated instead).
14 new pytest cases, 173 total pass. Live-verified against production:
reuse-surface stats correctly showed composed_at/age_days for the real
federated index. Separately discovered and confirmed (via a live signed
webhook test) that reuse-surface-env moving to ExternalSecret/OpenBao
custody (railiance-apps commit 706f6c7, found while updating these docs)
did not break the T02/T03 webhook -- the synced value still matches what
the hub actually uses.
REUSE-WP-0019 is now fully complete (T01-T06). SCOPE.md and
docs/IntentScopeGapAnalysis.md updated to reflect closure.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
reuse_surface/reports.py: collect_reuse_events() merges the hub's
GET /v1/reuse-events (if reachable) with this repo's local JSONL fallback,
deduped. collect_reuse_report() aggregates per-capability consumer counts,
outcome breakdown, and last-used. collect_reused_by_suggestions() proposes
evidence-gated relation_add patches -- only for capabilities this repo
owns, only for consumer repos not already listed -- reusing the existing
patches.py:apply_patches mechanism (relation_add already isn't in
SAFE_DETERMINISTIC_KINDS, so it was already never auto-applied by
maintain --auto).
New CLI: reuse-surface report reuse [--capability-id] [--format]
[--suggest-relations] [--apply]. --apply requires --suggest-relations and
is the only thing that writes -- nothing happens automatically from
telemetry alone.
schemas/capability.schema.yaml: added relations.reused_by as a new
repoSlugList type, distinct from the existing capability-id relations,
since reused-by targets are consumer repo slugs.
specs/CapabilityMaturityStandard.md Sec8.9: what observed-reuse evidence
counts toward R2->R3 (single corroborating consumer) vs R3->R4+ (multiple
independent consumers) and what it never substitutes for.
19 new pytest cases, 162 total pass. Live-verified with synthetic local
events against a real capability entry: --suggest-relations --apply
correctly wrote relations.reused_by via the real apply_patches path
(reverted after, since it was a smoke test).
Deliberately deferred: surfacing consumer counts in the catalog/graph --
graph.py's relation model is capability-to-capability edges, a different
namespace than repo-slug reused_by targets; catalog.py doesn't currently
parse full front matter per entry. Left for a follow-up.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Implements the hub side of the shared reuse-event schema (already drafted
in WP-0018-T01, schemas/reuse-event.schema.json): a SQLite reuse_events
table, POST /v1/reuse-events (token-auth), GET /v1/reuse-events?capability_id=
(read-only).
reuse_surface/plan_check.py: refactored record_outcome around a new
shared post_or_fallback_reuse_event() helper -- tries the hub first, falls
back to the local JSONL only on failure/unreachability, never both. New
record_manual_reuse_event() backs a new CLI command, reuse-surface
record-reuse, for retroactive facts recorded outside plan-check.
Privacy/scope (repo slugs and capability ids only, no code, no secrets) is
enforced structurally via the schema's additionalProperties: false, not
just by convention.
21 new pytest cases, 145 total pass. Live-verified against a real running
hub instance: POST/GET /v1/reuse-events directly, record-reuse and
plan-check --record-outcome both posting successfully to the hub, and --
after actually killing the hub process -- confirmed the fallback path
writes correctly to the local JSONL instead of erroring.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
reuse_surface/hub/store.py: compose_state table tracking composed_at/stale,
updated only on a *forced* recompose (refresh=true, webhook, future
scheduled fallback) -- a plain GET still serves current best-effort data
but never silently reports itself as freshly composed.
reuse_surface/hub/webhooks.py: constant-time HMAC-SHA256 signature
verification (fails closed on an empty/unconfigured secret) and
path-only push-payload inspection (never parses file content, per design
principle 2 -- webhook only decides whether to trigger a pull-based
recompose).
New endpoint POST /v1/webhooks/forgejo, accepting both
X-Forgejo-Signature and X-Gitea-Signature headers since sibling repos
migrate independently. GET /v1/federated and POST /v1/federated/compose
now share an asyncio.Lock with the webhook so concurrent recompose
triggers coalesce instead of overlapping.
No separate /v1/recompose route was added -- POST /v1/federated/compose
already did that job from earlier hub work; specs/FederationHubAPI.md now
documents this explicitly instead of duplicating it.
28 new pytest cases (128 total pass). Live-verified: ran the actual hub
service locally and sent a real HMAC-signed webhook over HTTP, confirming
the full webhook-to-recompose path, signature rejection, and the
irrelevant-path no-op.
Does NOT deploy to the live reuse.coulomb.social hub -- that needs a
container rebuild/push/k8s rollout, a separate production-deployment
action out of scope here without explicit sign-off.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
reuse_surface/forge_host.py: parse/derive/rewrite raw index URLs across
Gitea and Forgejo (handles both the legacy /raw/<branch>/... form and the
canonical /raw/branch/<branch>/... form both forges serve without a 303
redirect). migrate_source_host() verifies the new URL resolves via HTTP
HEAD before writing -- refuses to point a repo at a host it hasn't
actually migrated to.
New CLI: reuse-surface federation migrate-host --repo <slug> --to
<base-url> [--from <check>] [--dry-run] [--no-verify] [--update-hub].
Inventory: cross-referenced sources.yaml against each repo's actual git
origin. 11/61 repos already on Forgejo; found 2 with stale sources.yaml
entries (activity-core, state-hub) despite having migrated. Fixed for
real: local sources.yaml + production hub registration (hub update),
verified against GET /v1/federated post-migration. config-atlas's
WP-0017-T06 303 confirmed NOT a host-transition symptom (already diagnosed
there as something else).
Also fixed two host-agnostic gaps found while inventorying:
registry_update.py and maintain_llm.py only recognized .gitea/workflows/,
missing repos already on .forgejo/workflows/. Fixed a stale copy-paste
example (state-hub's now-wrong old URL) in docs/RegistryFederation.md, and
a pre-existing unrelated port typo (8088 vs the real llm-connect default
8080) in tools/README.md and registry/README.md.
17 new pytest cases (tests/test_forge_host.py), 106 total pass.
Recomposed federated.yaml post-migration: still 61 capabilities.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
llm-connect came up locally (mock provider, 127.0.0.1:8080), unblocking
this task.
reuse_surface/plan_check.py: build_rerank_prompt/request_rerank/apply_rerank,
reusing llm_bridge.execute_prompt/extract_json_object (same pattern as
maintain_llm.py's request_maintain_patches). New schema
schemas/plan-check-rerank.schema.json rejects malformed responses (missing
fields, non-JSON, invented candidate ids outside the input set) rather than
guessing.
Per design principle 3 (deterministic matches always rank first),
apply_rerank appends LLM-scored entries after the deterministic list
(kind: 'llm') instead of reordering it -- the trusted base result is
identical with or without the rerank pass. Graceful skip via a 'notes'
field when LLM_CONNECT_URL is unset or the response is malformed, mirroring
maintain.py's no_llm/skip pattern. New --llm-url/--no-llm CLI flags.
Also extended plan-check-result.schema.json for 'notes' and the
(pre-existing, previously unschema'd) 'filed_capability_request' field.
9 new pytest cases; 89 total pass. Live-verified against the running
llm-connect instance: it correctly rejected the mock provider's non-JSON
response and surfaced the skip note, with the deterministic verdict and
match order completely unaffected.
REUSE-WP-0018 is now fully done (T01-T06). Updated
docs/IntentScopeGapAnalysis.md priority 29 to Closed and the workplan's
own frontmatter status to finished.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
T01: specs/PlanCheck.md design doc, plan-check-result.schema.json and
reuse-event.schema.json (the latter shared with WP-0019's reuse telemetry).
T02: reuse_surface/plan_check.py + 'reuse-surface plan-check' CLI command.
Deterministic token-Jaccard matching against registry/indexes/federated.yaml
(reuses overlaps.py's TOKEN_RE rather than a second scoring method), with
reuse/extend/new verdicts, markdown and --format json output, staleness
warning, and --record-outcome JSONL telemetry.
T04: reuse_surface/statehub_bridge.py bridges plan-check 'new' verdicts to
State Hub capability requests (--file-request) and surfaces open requests
with no matching capability (report gaps --check-capability-requests, opt-in
to stay offline-safe). Verified against the live local State Hub API;
status field (not catalog_entry_id presence) is the correct open/closed
signal, and the list endpoint needs a longer timeout than the health check
(~7s observed with 5 rows).
T06: docs (tools/README.md, RegistryFederation.md, SCOPE.md,
IntentScopeGapAnalysis.md priority 29) and an informational CI smoke step.
T03 (LLM rerank) not started -- llm-connect isn't running on this
workstation. T05 (ecosystem rollout) remains blocked: WP-0017 has drafted
entries for all 61 repos but they're still local-only pending its own T05
push/publish pass, so the federated index isn't yet worth rolling out
plan-check as ecosystem convention.
16 new tests, all mocked -- no network calls in the default test run.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds capability_status: has|none|pending to every roster row (10 has /
51 pending currently), a coverage_ratio headline, and splits the gap
report's empty-scaffold section into unclassified vs explicitly-none.
Adds templates/NO_CAPABILITIES.template.md for the no-capability marker.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add hub sync and report cohorts CLI commands with pytest coverage, document
sibling index publish contract and hub hardening path, align INTENT layout,
raise external evidence on three registry entries, and close gap priorities
19-23 (priority 18 deferred on sibling index blocks).
Load the schema from reuse_surface/hub/ next to store.py and declare
package-data so pip install includes the YAML in site-packages. Fixes
hub register 500 in the container image.
Extend federation manifest schema for url sources with auth and TTL metadata.
Fetch remote capability indexes over HTTP(S), cache under
registry/federation/cache/, and fall back to stale cache on fetch failure.
Add --refresh flag, seven federation tests, and updated federation docs.
Register six new capabilities (12 total), add searchable catalog UI and graph
explorer, introduce pytest suite with CI fail-on-warnings, and close gap
analysis priorities 13 and 16. WP-0010 remains backlog for network federation.
Add federation manifest and schema, federation compose and graph CLI commands,
relation cycle/reference checks, federated index and Mermaid graph artifacts,
RegistryFederation guide, and CI validation updates.
Add Gitea CI workflow for registry validation, reuse-surface overlaps and
catalog commands, generated catalog artifacts, and documentation updates
closing gap analysis priorities 9-11.
Align INTENT.md with delivered layout, add CapabilityRegistryConcept guide,
extend schema with promotion_history, ship reuse-surface validate/query/export
CLI, register three more helix_forge capabilities, and refresh SCOPE and gap
analysis to reflect A3 tooling and D5/A3/C4/R2 self-assessment.