Quest: Artifact Page Unification

← All Specs

Quest: Artifact Page Unification

Status: in-progress Layer: Atlas Owner: SciDEX Agent Created: 2026-05-10

Problem

/artifact/{id} is the canonical browse URL for every artifact, but it does
not yet behave like the rich, type-aware page it needs to be:

  • Content tab reads from the wrong source. Each per-type branch in
  • artifact_detail (api.py:34602+) reads its content from
    artifact.metadata. For backfilled rows whose metadata is just
    {"source_table": "experiments"}, the page renders bare scaffolding.
    The real content lives in the source table (experiments, hypotheses,
    analyses, …) keyed by the same ID.

  • Cross-cutting tabs are hypothesis-only. Placeholder slots
  • (_hyp_extra_provenance, _hyp_extra_versions, _hyp_extra_links,
    _hyp_sidebar_html) are wired only for atype == "hypothesis".
    Every other type renders empty tabs.

  • No Discussion or Embed tabs. Debates / comments / reviews are not
  • surfaced on the artifact page. No way to embed an artifact elsewhere.

  • Links to alternate URLs are not discoverable. The type-prefix
  • aliases (/experiment/{id}, /experiments/{id}) are convenient for
    sharing but the artifact page doesn't show them. Users hit 404s when
    guessing plural forms (the bug that motivated this quest).

  • URL canonicalization needs alignment with REST. Plural item URLs
  • like /experiments/{id} returned 404 (fixed in PR #1385).
    /artifacts/{id} also lacks a 301 alias today.

    Goal

    Make /artifact/{id} the single, canonical, rich page for every artifact.
    Tab strip is universal; Content tab is type-aware. Render inline when
    content is structured; iframe only when the asset needs CSS/JS isolation
    (notebook, ai_image, 3D structure).

    Final architecture

    /artifact/{id}    ← canonical for every artifact type
    ├── Header (universal)
    │   title • type badge • lifecycle • quality • funding • supersede banner
    │
    ├── Tab strip (universal)
    │   Content │ Discussion │ Provenance │ Versions │ Links │ Embed
    │
    ├── Content tab (type-aware)
    │   experiment    → query experiments + linked hypotheses INLINE
    │   hypothesis    → query hypotheses, render scores + debates INLINE
    │   analysis      → query analyses, render figures + methodology INLINE
    │   paper         → render abstract + figures + full-text link INLINE
    │   figure        → image + caption + evidence audit INLINE
    │   model         → model card INLINE
    │   dataset       → schema + sample INLINE
    │   notebook      → iframe /notebook/{id}/preview (isolation needed)
    │   ai_image      → iframe (full-bleed)
    │   3d_structure  → iframe (mol viewer)
    │
    ├── Discussion tab (universal)
    │   debate_sessions WHERE target_artifact_id=id + comments + reviews
    │
    ├── Provenance tab (universal)
    │   created_by • commit_sha • origin_url • source PMID
    │   parent links (metadata) • derivation chain • dependencies
    │
    ├── Versions tab (universal)
    │   version_number • parent_version_id • supersede chain (pred → succ)
    │
    ├── Links tab (universal)
    │   alias paths (singular + plural) • API endpoints • citation export
    │   external: origin_url, source PMID, wiki page if exists
    │
    └── Embed tab (universal)
        iframe snippet • markdown link • social share
        /artifact/{id}?embed=1 renders chrome-stripped Content only

    URL canonicalization (cumulative across PRs)

    URLBehavior
    /artifact/{id}canonical render
    /artifacts/{id}301 → /artifact/{id} (PR 1)
    /experiment/{id}, /hypothesis/{id}, …301 → /artifact/{id} (existing)
    /experiments/{id}, /hypotheses/{id}, …301 → /artifact/{id} (PR #1385)
    /notebook/{id}dedicated renderer (notebook-specific page)
    /figures/{analysis_id}dedicated figure-directory browser (unchanged)
    /notebooks/{filename}file serving (unchanged)
    /artifact/{id}?embed=1chrome-stripped Content only (PR 5)

    PR plan

    PRScopeStatus
    1Fix experiment renderer to query experiments table. Add /artifacts/{id} 301 alias. Bootstrap quest spec.in_progress
    2Extract per-type renderers to scidex/atlas/viewers/{type}.py. Each branch reads from its source table. Audit hypothesis/analysis/paper/model/dataset/figure.blocked-by 1
    3Promote _hyp_extra_provenance + _hyp_extra_versions to universal renderers. All types get populated tabs.blocked-by 2
    4Discussion tab: debates + comments + reviews keyed on target_artifact_id.blocked-by 2
    5Links tab + Embed tab + ?embed=1 chrome-strip mode.blocked-by 2
    6Type-specific sidebar slot (scores radar / market / metrics).experiment slice shipped (PR #1413, _render_experiment_extras_html); other types pending

    Decisions

    Inline vs iframe. Inline by default. Iframe only for content with
    hostile/heterogeneous styling (jupyter HTML, AI images, 3D viewers).
    Rationale: SEO, print/PDF export, deep linking into sub-sections,
    single round trip, simpler styling.

    Canonical URL form. Singular /artifact/{id} stays canonical (years of
    internal links + KG edges reference it). Plural /artifacts/{id} is a
    301 alias, like the type-prefix plural forms shipped in PR #1385.

    Source table joins by ID. Backfilled artifact shells share their ID
    with the source-table row (e.g. exp-c3c760f8-… appears in both artifacts.id and experiments.id). Renderers join WHERE
    source_table.id = artifact.id
    . No metadata duplication required.

    Existing dedicated handlers. /notebook/{id} stays as a dedicated
    renderer (it predates this quest and already works well). Legacy
    type-specific handlers (/hypothesis/{hyp_id}, /analysis/{analysis_id})
    are shadowed by the type-prefix 301 aliases and only reachable via the
    redirect into artifact_detail. Their rendering logic is the reference
    for what the new inline renderers should match or exceed — line numbers
    intentionally omitted here because they drift; grep the function name.

    The /experiment/{exp_id} (experiment_detail) handler has been deleted (PR #1413, task experiment-artifact-extras-2026-05-13). Its
    rich sections were ported into _render_experiment_extras_html
    (api.py:31062), which artifact_detail calls inline in the atype == "experiment" branch. The experiment renderer is now the
    worked example of the PR-6 type-specific slot; treat it as the pattern
    for the remaining types. Four old-handler capabilities are deferred —
    see experiment-wiki-crosslinks-restore-2026-05-18_spec.md and experiment-extras-followups-2026-05-18_spec.md.

    File: quest_artifact_page_unification_spec.md
    Modified: 2026-05-18 19:31
    Size: 6.6 KB