[Artifacts] Artifact embed rendering: resolve {{artifact:ID}} markers in documents done

← Artifacts
Already resolved: api.py:23686-23757 implements full embed rendering engine with `_ARTIFACT_EMBED_PATTERN`, `_resolve_authored_paper_embeds`, and `_render_artifact_embed_html` supporting view parameter and all artifact types. Integration verified in paper_detail and dashboard_standalone routes. No new code needed.

Last Error

cli-reopen-manual: reopened — task was marked 'done' but has no task_runs row in (done/completed/success)

Git Commits (1)

[Verify] Artifact embed rendering already resolved [task:a17-29-EMBD0001]2026-04-25
Spec File

[Artifacts] Artifact embed rendering engine

Goal

Authored papers and dashboards reference other artifacts via {{artifact:ID}} markers in their content. This task builds the rendering engine that resolves these markers into inline HTML, making documents truly dynamic — a figure updates when re-generated, a hypothesis score changes when re-evaluated, a dataset grows as new data arrives.

Embed Marker Syntax

{{artifact:ARTIFACT_ID}}           → default view for the artifact type
{{artifact:ARTIFACT_ID:figure}}    → render as figure with caption
{{artifact:ARTIFACT_ID:table}}     → render as data table
{{artifact:ARTIFACT_ID:summary}}   → render as compact card
{{artifact:ARTIFACT_ID:chart}}     → render as interactive chart
{{artifact:ARTIFACT_ID:link}}      → render as hyperlink only
{{artifact:ARTIFACT_ID:full}}      → render full detail (expanded)

Rendering Rules by Artifact Type

figure

  • Default view: <figure><img src="..."><figcaption>...</figcaption></figure>
  • summary: thumbnail with title
  • link: [Figure: title](url)

hypothesis

  • Default view: scored card with title, composite_score gauge, target_gene, evidence_for/against counts
  • summary: one-line score badge Hypothesis: title (score: 0.82)
  • full: complete hypothesis with evidence matrix

model

  • Default view: metric card with model_family, key evaluation metrics, parameter count
  • summary: Model: title (RMSE: 0.023)
  • table: parameters table with values and sources

dataset / tabular_dataset

  • Default view: summary card with row_count, column_count, source
  • table: first 10 rows as HTML table with column headers
  • summary: one-line Dataset: title (50,000 rows)

paper

  • Default view: citation card with title, authors, journal, year
  • link: formatted citation [Authors et al., Year]

protein_design

  • Default view: card with sequence length, mutations, stability, binding affinity
  • summary: Protein: title (v3, Kd=120nM)

dashboard

  • Default view: iframe embed of the live dashboard
  • summary: snapshot card with last_rendered_at

notebook

  • Default view: rendered notebook HTML (scrollable embed)
  • summary: card with title and associated analysis

Implementation

resolve_embeds(content_html: str) -> str

import re

EMBED_PATTERN = re.compile(r'\{\{artifact:([^:}]+)(?::([^}]+))?\}\}')

def resolve_embeds(content_html: str) -> str:
    """Replace all {{artifact:ID[:view]}} markers with rendered HTML."""
    def replace_embed(match):
        artifact_id = match.group(1)
        view = match.group(2) or 'default'
        return render_artifact_embed(artifact_id, view)
    return EMBED_PATTERN.sub(replace_embed, content_html)

def render_artifact_embed(artifact_id: str, view: str = 'default') -> str:
    """Render a single artifact as inline HTML based on its type and requested view."""
    artifact = get_artifact(artifact_id)
    if not artifact:
        return f'<div class="embed-error">Artifact not found: {artifact_id}</div>'
    
    artifact_type = artifact['artifact']['artifact_type']
    renderer = EMBED_RENDERERS.get(artifact_type, render_generic_embed)
    return renderer(artifact, view)

EMBED_RENDERERS = {
    'figure': render_figure_embed,
    'hypothesis': render_hypothesis_embed,
    'model': render_model_embed,
    'dataset': render_dataset_embed,
    'tabular_dataset': render_dataset_embed,
    'paper': render_paper_embed,
    'protein_design': render_protein_embed,
    'dashboard': render_dashboard_embed,
    'notebook': render_notebook_embed,
    'authored_paper': render_authored_paper_embed,
}

Integration Points

  • GET /paper/{id} calls resolve_embeds() before rendering
  • GET /dashboard/{id} calls resolve_embeds() after data source injection
  • GET /api/papers/authored/{id}?rendered=true returns HTML with resolved embeds

Acceptance Criteria

resolve_embeds() finds and replaces all {{artifact:ID}} markers
☐ View parameter (figure, table, summary, chart, link, full) respected
☐ Each artifact type has a dedicated renderer
☐ Missing artifacts show error placeholder (not crash)
☐ Default view auto-selected based on artifact type
☐ Renderers produce valid HTML matching SciDEX dark theme
☐ Integration with authored paper rendering
☐ Integration with dashboard rendering
☐ Work log updated with timestamped entry

Dependencies

  • a17-27-APAP0001 (authored papers use embeds)
  • a17-28-DASH0001 (dashboards use embeds)

Dependents

  • d16-25-DPAP0001 (demo: dynamic paper with embedded artifacts)

Work Log

Payload JSON
{
  "completion_shas": [
    "def767be5"
  ],
  "completion_shas_checked_at": ""
}

Sibling Tasks in Quest (Artifacts) ↗

Task Dependencies

↓ Referenced by (downstream)