fix(claims): resolve long activity-feed slugs to canonical file stems
Some checks are pending
CI / lint-and-test (push) Waiting to run
Some checks are pending
CI / lint-and-test (push) Waiting to run
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) <noreply@anthropic.com>
This commit is contained in:
parent
1bc541ac93
commit
73880e138d
1 changed files with 33 additions and 2 deletions
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue