Everything on this page also renders in pipeline A's text primitive
(format: "markdown"), except footnotes — those are pipeline-B only. See the
Overview for the full comparison table.
Tables
Source:
| Method | Process | Owner |
| :--- | :---: | ---: |
| Traditional | Code → Test → Docs | Platform |
| Modern | Test → Code → Docs | Product |Rendered:
| Method | Process | Owner |
|---|---|---|
| Traditional | Code → Test → Docs | Platform |
| Modern | Test → Code → Docs | Product |
A wide table (long cells, many columns) scrolls inside its own container instead of
forcing the whole page to scroll horizontally — the wrapper is
.aup-markdown-table-wrap / .docs-table-wrap.
Task lists
Source:
- [x] Write the content pipeline
- [x] Ship GFM support
- [ ] Document every extension in one placeRendered:
- Write the content pipeline
- Ship GFM support
- Document every extension in one place
Checkboxes render disabled — this is a static reference, not an interactive to-do
list; checked state reflects what the author wrote in the source, not a live toggle.
Strikethrough
Source:
~~This approach is deprecated.~~ Use the new one instead.Rendered:
This approach is deprecated. Use the new one instead.
Code highlighting
Source:
```typescript
export function greet(name: string): string {
return `Hello, ${name}!`;
}
```Rendered:
export function greet(name: string): string {
return `Hello, ${name}!`;
}Fenced code blocks get a language label and a copy-to-clipboard button
(.aup-code-toolbar); the <code> element carries a language-<lang> class for
syntax-highlighting CSS to hook into.
Footnotes (pipeline B only)
Source:
Every request carries a signed capability token.[^cap]
[^cap]: See `docs/architecture/capability-tokens.md` for the token format.Rendered:
Every request carries a signed capability token.[1]
Footnotes are collected once per document and rendered in a single end-of-document section, numbered by first-reference order (not definition order). An unreferenced definition produces no footnotes section at all — it's silently dropped, matching this pipeline's general degrade-not-break posture for authoring mistakes.
Pipeline A does not support footnotes at all — [^1] inside an AUP
format: "markdown" text field renders as literal text, not a numbered reference.
Raw HTML (sanitized in both pipelines, different allowlists)
Inline and block-level raw HTML in the markdown source is allowed through an
allowlist of safe tags/attributes (markdown-media.ts's sanitizeRawHtml) —
neither passed through verbatim (a storage-XSS vector) nor silently dropped.
Pipeline A also sanitizes raw HTML, but via a different mechanism. The AUP
text primitive's format: "markdown" renderer (renderMarkdown() in
providers/runtime/ui/src/web-page/core.ts) pipes marked.parse(raw) through
DOMPurify — a generic sanitizer with its own (broader, library-default)
allowlist, not the fixed, hand-curated tag list this pipeline's
sanitizeRawHtml uses. Both pipelines are safe against storage-XSS; the exact
set of surviving non-media tags/attributes differs between them, so don't
assume a page that renders here renders identically through the AUP text
primitive. The media tags are the exception: <img> / <video> /
<source> (their attributes, the https-or-relative URL policy on
src/poster/every srcset candidate, and the forced loading="lazy" /
preload="metadata") come from one shared allowlist
(@aigne/afs-ui/web-page/media-allowlist) in both pipelines.
Source:
<div class="callout"><strong>Note:</strong> this box is authored as raw HTML.</div>
<img src="https://example.com/diagram.png" onerror="alert(1)" alt="A diagram">
<script>alert("this never runs")</script>Rendered:

Only a fixed tag/attribute allowlist survives (div, details/summary,
headings, table, a/img with their safe attributes, and a handful of inline
tags): the <div>/<img> above render, but onerror is stripped from the
<img> (open the source of this page to confirm it's gone from the rendered
attribute list) and the whole <script> tag — including its body — disappears
without a trace. Each raw-HTML block converts independently: a multi-line
tag like <details>…<summary>…</summary>\n\nbody\n</details> with a blank line
inside it does NOT nest correctly — the blank line splits it into separate
paragraphs, breaking the <details>/<summary> structure. Keep raw HTML blocks
to a single line (or accept the broken nesting) rather than assuming multi-line
block HTML round-trips the way it would in the source. This is a genuinely new
capability (landed the same week as this reference, arc#2537/#2599) — check
markdown-media.ts's RAW_HTML_ALLOWED_TAGS (plus the shared media allowlist
it merges in) for the exact current allowlist rather than assuming it matches
this list forever.
- See
docs/architecture/capability-tokens.mdfor the token format. ↩