Name the SCOPE-block mistake in compose warnings (REUSE-WP-0020-T03)
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
ci / validate-registry (push) Successful in 1m20s
Build and Publish Container Image / build-and-push (push) Successful in 34s

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>
This commit is contained in:
tegwick 2026-08-21 03:23:29 +02:00
parent 9d015d4604
commit 0300c5b142
3 changed files with 67 additions and 4 deletions

View file

@ -183,6 +183,26 @@ def resolve_source_index_path(
return _write_remote_cache(source["repo"], url, content, cache_dir), warnings
SCOPE_BLOCK_KEYS = {"type", "title", "description", "keywords"}
def _shape_hint(item: dict[str, Any]) -> str:
"""Name the likely mistake when an index row has no id.
The fenced `capability` blocks in a repo's SCOPE.md use
type/title/description/keywords, which is close enough to an index row to
be copied into one by mistake — and then the member silently contributes
nothing. Say so instead of leaving the author to guess.
"""
if SCOPE_BLOCK_KEYS & set(item):
return (
" — looks like a SCOPE.md capability block"
" (type/title/description/keywords); an index row needs"
" id/name/summary/vector/domain/status/owner/path"
)
return ""
def _read_index_entries(
index_path: Path, repo: str
) -> tuple[list[Any], list[str]]:
@ -234,7 +254,10 @@ def compose_federated_index(
continue
cap_id = item.get("id")
if not cap_id:
warnings.append(f"{source['repo']}: capability #{position} has no id, skipped")
warnings.append(
f"{source['repo']}: capability #{position} has no id, skipped"
f"{_shape_hint(item)}"
)
continue
if cap_id in seen_ids:
warnings.append(

View file

@ -283,3 +283,33 @@ def test_malformed_source_still_listed_with_zero_count(tmp_path, monkeypatch):
)
remote = next(s for s in federated["sources"] if s["repo"] == "remote-repo")
assert remote["count"] == 0
def test_scope_shaped_row_names_the_mistake(tmp_path, monkeypatch):
"""A SCOPE.md capability block copied into an index should say so."""
body = """
version: 1
domain: helix_forge
capabilities:
- type: library
title: Evidence-to-target binding
description: Links evidence items to structured targets.
keywords: [evidence, binding]
"""
_, warnings = _compose_with_remote_body(body, tmp_path, monkeypatch)
hint = next(w for w in warnings if "remote-repo" in w)
assert "SCOPE.md capability block" in hint
assert "id/name/summary" in hint
def test_plain_missing_id_gets_no_scope_hint(tmp_path, monkeypatch):
body = """
version: 1
capabilities:
- name: Has a name but no id
summary: Something
"""
_, warnings = _compose_with_remote_body(body, tmp_path, monkeypatch)
hint = next(w for w in warnings if "remote-repo" in w)
assert "has no id" in hint
assert "SCOPE.md" not in hint

View file

@ -312,9 +312,19 @@ blocks that belong in `SCOPE.md` use `type` / `title` / `description` /
one from the wrong place.
That makes it a systemic trap rather than a one-off mistake, so the Terminology
section now documents the distinction explicitly. Worth a follow-up: `validate`
could detect SCOPE-shaped rows in an index and say so by name, and `report gaps`
could sweep the federation for other members with the same confusion.
section now documents the distinction explicitly, and the compose warning now
names it:
```
evidence-binder: capability #0 has no id, skipped — looks like a SCOPE.md
capability block (type/title/description/keywords); an index row needs
id/name/summary/vector/domain/status/owner/path
```
**Federation swept 2026-08-21.** Composed all 61 sources with `--refresh`:
`evidence-binder` is the **only** affected member. The other ten zero-count
repos are genuine empty scaffolds and produced no warnings, so the confusion has
not spread.
## Fix Wall-Clock Rot In The Plan-Check Test Suite