From 73880e138d8724f27bac02a0d2f5e54b0d1dff4b Mon Sep 17 00:00:00 2001 From: m3taversal Date: Sun, 10 May 2026 19:51:17 +0100 Subject: [PATCH] fix(claims): resolve long activity-feed slugs to canonical file stems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Activity feed emits slugs derived from PR description (the slugified claim title), which can be longer than the on-disk file stem (agents pick shorter hand-chosen filenames). Pure exact-stem lookup 404s on those. Three-tier resolution in handle_claim_detail: 1. Exact stem match (existing behavior) 2. Title fallback: normalize requested slug, look up via by_title index (already populated from frontmatter title during _build_indexes) 3. Prefix fallback: longest common prefix among stems, anchored at 32 chars to prevent spurious hits Response slug returns the canonical on-disk stem so frontend share-links and caches converge to one form. Repro: GET /api/claims/spacex-and-amazon-kuiper-non-endorsement-of-wef-debris- guidelines-demonstrates-systemic-voluntary-governance-failure-at-the-scale- where-it-matters-most was 404; now 200, returns shorter on-disk slug '...-governance-failure'. Negative case (nonsense slug) still 404s. Reported by Ship — Cory-facing demo path. Co-Authored-By: Claude Opus 4.7 (1M context) --- diagnostics/claims_api.py | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/diagnostics/claims_api.py b/diagnostics/claims_api.py index f18c5d5..8808e45 100644 --- a/diagnostics/claims_api.py +++ b/diagnostics/claims_api.py @@ -423,12 +423,43 @@ async def handle_claim_detail(request): One round-trip, all data resolved server-side. Wikilinks pre-resolved. """ - slug = request.match_info["slug"] + requested_slug = request.match_info["slug"] by_title, by_stem = _build_indexes() + # Resolution order: exact stem → title-normalized (handles description-derived + # slugs from /api/activity-feed that are longer than on-disk file stems) → + # stem-as-prefix (handles description-derived slugs that are shorter than the + # file stem because the description was truncated upstream). + slug = requested_slug rel_path = by_stem.get(slug) if not rel_path: - return web.json_response({"error": "claim not found", "slug": slug}, + # Title fallback: requested slug = slugified frontmatter title + norm = _normalize_for_match(requested_slug) + resolved_stem = by_title.get(norm) + if resolved_stem: + slug = resolved_stem + rel_path = by_stem.get(resolved_stem) + if not rel_path: + # Prefix fallback: walk stems sharing a common prefix with the request, + # pick longest match. Anchored at 32 chars to avoid spurious hits. + norm_req = _normalize_for_match(requested_slug) + best_stem = None + best_len = 0 + for stem in by_stem: + norm_stem = _normalize_for_match(stem) + common = 0 + for a, b in zip(norm_req, norm_stem): + if a != b: + break + common += 1 + if common >= 32 and common > best_len: + best_stem = stem + best_len = common + if best_stem: + slug = best_stem + rel_path = by_stem.get(best_stem) + if not rel_path: + return web.json_response({"error": "claim not found", "slug": requested_slug}, status=404, headers=CORS_HEADERS) filepath = CODEX_BASE / rel_path