Routing / status-code fixes — S-1, S-2, B-1, B-2

← All Specs

Routing / status-code fixes — S-1, S-2, B-1, B-2

Task ID: audit-routing-fixes-2026-05-14 Layer: Atlas (artifact / notebook / cite routes) Date: 2026-05-14 v1 carve-out: user-authorized; v1 still serves scidex.ai

Four small route-level fixes uncovered by the 2026-05-13 audit
(/tmp/audit-2026-05-13).

S-1 — /artifact/<missing-id> returned HTTP 200 with a not-found body

artifact_detail rendered the "Artifact Not Found" content via page_template(...) but FastAPI wrapped the string in a default 200
HTMLResponse. Bots, monitoring, and the audit harness all saw 200 →
unable to distinguish "real page" from "missing artifact." Wrap in HTMLResponse(..., status_code=404) so the status code matches the
body.

S-2 — /idea-collider returned 404

The Forge idea-collider lives at /forge/idea-collider. A small
number of homepage / nav emitters use the short /idea-collider
path. Rather than chase them all (the audit identified some at line
33919, 49289, 54311 but there may be more elsewhere), add a 301
alias route that preserves the ids and seed query parameters.

B-1 — Notebook detail 500 on empty file_path

_resolve_notebook_paths returns Path("") for notebooks with a
NULL or empty file_path. Path("").exists() is True (it
resolves to CWD), so the existing guard at api.py:74826
(if ipynb_local_path and ipynb_local_path.exists()) lets the
empty path through. The next line called ipynb_local_path.with_suffix(".html")ValueError: PosixPath('.')
has an empty name
, surfacing as HTTP 500 on 3 sampled notebooks.

Add ipynb_local_path.name to the guard so empty / dot paths
fall through to the existing associated_analysis_id fallback.

B-2 — /cite?fmt=csl returned 400

artifact_cite validates fmt in {"bibtex", "ris", "endnote",
"csl_json"}
. External citation managers (Zotero, Better BibTeX)
sometimes probe with the short alias csl. Accept csl as a synonym
for csl_json by coercing it before the validator runs.

Verification

  • python3 -c "import ast; ast.parse(open('api.py').read())" → OK
  • Post-deploy, re-run /tmp/audit-2026-05-13/reverify.py. The four
failures for S-1, S-2, B-1×3, B-2 should flip to PASS without any
previously-PASS check regressing.
  • Spot-check live URLs after scidex-api restart.

Risk

Low.

  • S-1: changes the status code only; body identical to before, so
any existing UI that read the page won't notice. SEO / monitoring
now sees the correct 404.
  • S-2: alias route below the canonical route definition; trivially
reversible.
  • B-1: tightens an existing guard with one additional .name check.
Previously-passing paths still pass.
  • B-2: pre-validator coercion. The csl_json branch is unchanged.

File: audit-routing-fixes-2026-05-14_spec.md
Modified: 2026-05-18 04:17
Size: 2.7 KB