Ideas were spread across six places with real duplication and one list that was about to be lost. Consolidated by merging the duplication, not the categories. The 130-item feature-list triage lived in .scratch/, which is gitignored — 130 verified verdicts a scratchpad cleanup would have deleted. It is now ideas/feature-list-triage.md with the header the ideas gates require, indexed, and out of agent context by default like everything there. exploration.md's Verdicts table was empty and designed for exactly this; it now carries one row per body of work with a pointer, rather than 130 rows nobody could skim. Deferred triggers had no single owner: continue.md restated them and drifted, still listing the extension registry as pending after ADR-0081 built it. deferred-decisions.md now holds the only trigger table, covering the two it already described plus math, corrections, write routes and the registry's remaining fields. continue.md links to it. Two rows record couplings worth not rediscovering: the View layer may pull a minimal settings cascade in with it, because the Views counter says selection resolves *through* the cascade; and ADR-0081's seam mounts GET only, so Arc 3's first write endpoint extends it. Corrections per post is dropped in its cheap form and parked in its real one. A hand-maintained frontmatter list was rejected for an authoring reason rather than a technical one — a half-remembered list of corrections misleads where none would not, because two entries imply those were the only two. Deriving it from repository history is the shape worth waiting for, and the file records what must be settled first: whether the engine may read local git history at all, that it needs a dependency or a subprocess, and that not every commit is a correction. Not merged, on purpose: latent items stay in state.md because they describe shipped code and are gated against .go commits; arcs stay in roadmap.md because they are sequencing decisions. One file would have flattened four levels of authority and made pending work cheap to load, which is the opposite of why ideas/ sits outside harness/ at all. harness/README.md now says which list owns what, in both the topic table and the single-source table. 7 files. No rule or threshold moved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
33 KiB
Feature-list triage
Status: catalog — one verdict per item, overridden by the answers at the top Raised: 2026-08-02
Every item from the human's feature list, with a verdict. Not a plan and not a queue — nothing here has
authority over the code, and being listed earns nothing. It lives in ideas/ because that is where work
nobody has committed to belongs, and it is out of agent context by default like everything else here.
Summary counts, which ideas/exploration.md also carries: 130 rows — 42 BUILT, 24 THEME, 29 LEAF, 2 TRUNK,
4 PARK, 4 CONFLICT (all four now resolved). Three of the LEAF items have since shipped.
Verified against the code on 2026-08-02, not against state.md. Where a verdict says BUILT it means the
code does it, and the citation is where to look.
Detail volume was normalised deliberately. The longest section (Content-specific) is mostly frontmatter that already works plus one genuine primitive; the shortest line in the whole list ("Search") is the largest single piece of work in it.
Answered 2026-08-02 — these override the verdicts below
-
Categories: sections are categories. No second taxonomy. Every "categories" row below resolves to BUILT, and the tag axis stays the only non-structural one.
-
Private: dropped entirely. No secret links, no capability URLs — ADR-0024 stands untouched.
draftplus unlisted covers the ground, so unlisted survives as a small LEAF andprivate/archivedleave the list. -
Task lists: enabled, superseding ADR-0058's exclusion. The
TaskListextension only. Linkify and the rest of the GFM bundle stay out — linkify rewrites an author's plain text into markup, which is the line ADR-0034 draws and is a separate decision from this one. -
JS: a small page-scoped exception is allowed. Opt-in per page via frontmatter, never site-wide, which requires the
styles/scriptsfields to be actually lifted. The antifeature list must be written down with the exception in it — an exception nobody recorded is how the whole list erodes. -
Git sync stays out of the engine.
content-model.md's position is untouched: something else pulls, the engine notices the changed files and rebuilds, which already works. No credentials in the engine and no write path to the content repo. The "incoming webhook" half remains an Arc 3 question, not a git one. -
Shared parts between bundles: left blocked, deliberately. Not worth a permanent disk-contract decision yet. So cross-document jargon tooltips and the worldbuilding glossary are PARK, and abbreviations stay per-document as ADR-0062 has them. Revisit when something being written needs it.
-
The Extras section is live scope, not a remember-these-exist list — the human overrode their own header. Its verdicts below stand as real candidates.
The decided small batch is done (69f43e5, 64e53f2): task lists enabled superseding ADR-0058, the
antifeature list recorded as ADR-0080 with its one exception, and page assets shipped as ADR-0079 — theme
assets:<name> fragments pulled in by the shortcodes that need them or by use:, plus the bundle's own
styles/scripts, lifted at last. Unlisted was dropped. So every verdict below tagged THEME for a
shortcode needing CSS or JS is now genuinely reachable without engine work.
Unstarted, in no fixed order: the View layer (closes Arc 2), raw passthrough routes, corrections/updates per post.
Still open: whether to fix SKILL.md's stale three-question cap (CLAUDE.md §4 uncapped it in 25d7045);
declared content types (queue 7), which is what "alt text and content warnings first-class per type" needs.
Verdict key
| Tag | Means |
|---|---|
| BUILT | Ships today. Cited. |
| THEME | A theme fragment, template or CSS. No engine code, no rebuild — proven below. |
| LEAF | Engine work that reduces to an existing primitive. Buildable when wanted. |
| TRUNK | Wants a permanent service or a core-model change. Needs a deliberate decision in an ADR. |
| PARK | Real but not now. ideas/exploration.md. |
| CONFLICT | Contradicts a recorded decision. Surfaced, never resolved silently (CLAUDE.md rule 9). |
| ? | Needs an answer before it can be classified. |
The one verification that reframes the list
A shortcode name the engine has never heard of renders from a theme fragment alone, and the fragment hot-swaps while the server runs:
::spoiler{summary="Chapter 3 twist" body="She was the lighthouse."}
+ templates/shortcodes/spoiler.html → renders, no engine code, no rebuild
edit the fragment with the server up → new output in one poll interval
The parser's switch on name only enriches three names (gallery reads the directory, figure
inspects a picture, include marks content). Every other name becomes a node and renders through a
fragment of that name; a name the theme does not define renders nothing and logs it
(internal/ext/shortcodes/shortcodes.go, content-model.md Shortcodes).
So "lightweight shortcode mechanism, loaded as data/templates rather than hardcoded, so I can add one without a rebuild" is already built exactly as specified — and any item that is "render this markup from these arguments" is a fragment you write, not a feature I build.
Content
| Item | Verdict | Justification |
|---|---|---|
| post / page / note / project as separate types | BUILT as convention | Sections are the type: content-model.md Post types already names post/comic/art/writing/page/status, defaulting from the top-level directory. note/ and project/ are two mkdirs. A declared type file with per-type defaults is deferred (queue 7, ideas/deferred-decisions.md) and waits for a dated bundle needing to stay out of a feed |
| Tags | BUILT | Flat, case- and script-preserved, slugged for URLs (ADR-0018); listings global and section-narrowed, paginated, with per-tag Atom feeds (ADR-0043) |
| Categories | ? | See question 1. My read: sections already are categories — one per post, structural, in the URL. A second single-select axis buys a routing case and a listing shape you already have |
| Series with its own ordered index | BUILT | Sequence: structural membership by nesting (ADR-0033), order ascending sparse, prev/next/first/last/count, series archive on any nested bundle, resolving through language fallback (ADR-0016) |
| Page status: draft | BUILT | Lifted to Bundle.Draft; 404 for the bundle and every file inside it (ADR-0024); -dev on reveals. Future-dating works the same way and expires per request |
| Page status: unlisted | LEAF, small | Served at its URL, absent from listings, feeds and sitemap. No conflict — it is a query-membership flag, and Site.Run is already the one place membership is decided |
| Page status: private via secret link | CONFLICT | ADR-0024 chose 404 over 403 for unpublished work specifically so existence is not confirmed. A secret link serves unpublished content to whoever holds the URL, which is a capability-URL design (unguessable token, no listing, no sitemap, noindex) and a different security model. Question 2 |
| Page status: archived | THEME | A frontmatter flag the template reads. Nothing engine-side unless it changes query membership, in which case it is unlisted above |
| Page inclusion — embed one fragment in another | BUILT | ::include{file=…}; merging is the default so the page is one document (ADR-0066, ADR-0076), include: embed for containment. One level, no .., missing file logs and still serves |
| Per-page custom CSS/JS for demos | THEME today, LEAF to formalise | styles/scripts are in content-model.md's table but not lifted — verified: nothing in internal/ reads them, so they sit in Extra, which the theme can already read (ADR-0002 open page object). Lifting them properly is a small LEAF. Consistent with your antifeature: JS for demos only |
| Folder-per-post browsable extras | BUILT, precisely as described | extras/ (ADR-0047): scanner skips it, enumerated as a sorted tree, classified markdown/text/image/pdf/audio/video/other/dir, markdown and text rendered, everything else offered as bytes, ?raw for the file itself, excluded from queries/feeds/sitemaps. Your "old drafts, notes, worldbuilding material, reference lists, code samples" is the exact use it was built for |
| Inline image alignment / sizing | THEME | The figure fragment plus CSS classes. left/right/center/full-width is layout, and ADR-0045 is the precedent for not doing layout in the engine |
| — cropped | LEAF | The one part that is not CSS: derivatives are width-only today (ADR-0042). A crop is a new operation on the existing pass |
| EPUB export | LEAF | Genuinely new. An Effect writing an artifact: archive/zip and XHTML are stdlib, so no dependency. Wants the Effect runner (counter at 1, due at 2) |
| Server-rendered QR code | LEAF | Self-contained, no client JS, fits the values. ~200 lines hand-rolled, or a dependency needing an ADR. Nice small win |
Authoring
| Item | Verdict | Justification |
|---|---|---|
| Markdown + footnotes, tables | BUILT | CommonMark plus tables, footnotes, definition lists, heading ids declared or derived (ADR-0058). Footnote ids namespace per included file |
| — task lists | CONFLICT | ADR-0058 excluded them deliberately: "a note-taking affordance, not a publishing one", and the GFM bundle was refused because it drags task lists and linkify in with the tables. Question 3 |
| Frontmatter fields | BUILT | Unknown keys land in Extra and templates read them (ADR-0002). Most of the Content-specific section below needs nothing more than this |
| TOC generation | BUILT | ::toc, ::toc{depth=2}; entries carry level, text without markup, and the id the engine assigned, so indentation is CSS (ADR-0065, ADR-0066) |
| Reading time, word count | LEAF, tiny — and notable | These are the first page-level transforms, the same slot as OpenGraph/JSON-LD (queue 15). The transform counter is at 0 of 3, so this is one of the three things that would earn the Stage pipeline |
| Image galleries | BUILT | ::gallery lists pictures beside the bundle in filename order; subdirectories and unshowable files excluded (ADR-0016) |
| oEmbed-style embeds | TRUNK, and your own list answers it | The engine never fetches — content-model.md: "It never fetches them. Pulling a git repository is the operator's business". Also collides with your antifeature about third-party embeds. Your "static self-rendered copies" is the right answer; see below |
| Typography: smart quotes, dashes | BUILT | goldmark typographer on the parsed tree, so code spans keep straight quotes and -- (ADR-0034) |
| — widow/orphan control | CSS, and settled | You already say "mostly CSS", and you are right for a recorded reason: ADR-0045 deleted a shipped engine feature for this. text-wrap: pretty/balance in theme.css does it |
| Lightweight shortcode mechanism, no rebuild | BUILT | Verified this session, see above. This is the highest-value already-built finding in the list |
| Timeline within a single page | THEME | A container shortcode over authored entries. Zero engine code |
| Timeline across pages / series | LEAF | A Query (already earned) plus a listing shape. The genuinely new half of that bullet — worth splitting from the other |
| admonitions, info boxes, tabs | BUILT | :::note, :::warn, :::tip, :::details — and siblings sharing a group render as tabs — plus :::aside for margin notes (ADR-0064). A kind the theme does not define renders its body unwrapped rather than losing it |
| Static self-rendered copies of external content | Reframe → THEME | As an engine fetch it is a TRUNK (see oEmbed). As content it is free: an external tool snapshots the tweet into the bundle, and a fragment renders it. Same reader benefit, no outbound capability, no trust boundary. Strong recommendation |
| Inline code highlighting via a marker | LEAF, small | Block highlighting is built (chroma, ADR-0075). Caution: your proposed `{lang=python}…` is exactly the "what prose does this eat?" failure mode — five defects this project has already shipped were new syntax swallowing ordinary text |
| Ruby annotations | LEAF, small | An inline mark in internal/ext/notation/, which owns exactly this (ADR-0061), or a fragment if you accept a shortcode call instead of terse syntax |
| Strikeout and highlight | BUILT | ~~struck~~ and ==marked==, khosra's own so a single tilde can mean subscript (ADR-0061) |
| Columnar layout | THEME | CSS columns |
| Archive.org link beside external links | split | Rendering the link: THEME. Auto-submitting on publish: LEAF after the Effect runner, and outbound to a third party |
| Readability score EN/BN | PARK | English is easy. Bengali is not, and roadmap.md flags Bengali tokenisation as the genuinely novel problem in this project — do not solve it as a side effect of a stats page |
Multilingual
| Item | Verdict | Justification |
|---|---|---|
| EN/BN pairing between translations | BUILT | Language variants per bundle key, fallback requested → default → any → 404 (ADR-0004), English at root and /bn/ prefix with /en/… redirecting (ADR-0009) |
| Script-aware typography per language | BUILT (chrome) + CSS (body) | Chrome, dates and digits localise from an engine table — পৃষ্ঠা ২ / ২, ৮ মার্চ ২০২৬ (ADR-0034). Authored text is deliberately never localised. Per-script typography is CSS, and the lang the theme needs is already on the fragment (ADR-0067) |
hreflang |
BUILT | Emitted from the variants that exist, absolute once base is declared (ADR-0039) |
Theming
| Item | Verdict | Justification |
|---|---|---|
| Partials / includes | BUILT | Fragments and Partial in the theme contract; shortcode fragments may be one file or a directory (ADR-0071) |
Dark / light via prefers-color-scheme |
BUILT | Present in theme.css — verified |
| Design tokens instead of hardcoded CSS | THEME | Custom properties. theme.css is yours to restructure; the engine has no opinion |
| Print stylesheet | THEME | Not written. Pure CSS |
Discovery
| Item | Verdict | Justification |
|---|---|---|
| Archive pages for tags | BUILT | Global and section-narrowed tag listings, paginated at /{section}/page/N/ (ADR-0028) |
| Search | LEAF — and the largest item in this list | One word, arc-sized. roadmap.md: "Bengali tokenisation for search is the genuinely novel problem here — solve it well and deliberately, not as a side effect of adding search." Needs an index (an Effect), a query path, and a real decision about conjunct normalisation beyond NFC |
| RSS / Atom feeds | BUILT | Atom per site, section and tag, from dated bundles via one Query (ADR-0043) |
| JSON Feed | LEAF, tiny | One more marshaller. Note state.md: output formats deliberately do not count toward the Views counter — three functions with nothing to share |
| Sitemap | BUILT | Every bundle in every language it exists in, lastmod where dated, 404 without base (ADR-0039) |
SEO
| Item | Verdict | Justification |
|---|---|---|
| OG tags | BUILT | Emitted from fields already on the page, absolute with base |
| Meta descriptions | THEME | From summary or derived. No new disk field — content-model.md is explicit that SEO adds no frontmatter |
| Twitter cards, JSON-LD | LEAF — the Arc 2 mover | content-model.md: "no consumer yet". These are queue 15, the first page-level transforms, and they take the transform counter off 0 toward 3. Also the trigger for the latent item that the counters have no mechanical check |
| Canonical URLs | BUILT | Canonical plus trailing-slash canonicalisation, one rule applied once |
| Redirect handling | BUILT | aliases → permanent redirects; ambiguity logged and dropped rather than fatal (ADR-0008, ADR-0029) |
| Content as data — text / JSON / YAML | LEAF | Output formats. Same feature as "Personal JSON API" in Extras — one build, two list entries |
Performance
| Item | Verdict | Justification |
|---|---|---|
| Cache | Deliberately not built | "No cache until requests feel slow." A whole page renders in ~63µs (ADR-0044), and a latent item explicitly declines to memoise sequence resolution for want of a measurement. Adding one now would be a cache with no number behind it |
| Static export | LEAF | Useful independently of caching — it is a deploy artifact and an offline copy. Reduces to Query + Effect |
| Responsive images | BUILT | Three widths, never upscaled, content-hash names, srcset closed by the original, width/height from the source so the box is reserved (ADR-0040, ADR-0042) |
| Native lazy loading | THEME | One attribute in the figure/gallery fragment |
| Incremental rebuilds | PARK with a trigger | The rebuild is a whole-index swap and is not what costs anything. Revisit when a rebuild is felt — the same trigger that would revive the parked cache |
Routing
| Item | Verdict | Justification |
|---|---|---|
Raw passthrough routes, incl. .well-known/* |
LEAF — highest leverage small item in the list | static/ only answers under /static/; nothing can be served at /pubkey or /.well-known/security.txt. This one feature satisfies four separate entries: /pubkey, humans.txt, security.txt, and the PGP page. Needs care: the root is engine-owned (ADR-0008, ADR-0050), so it wants an explicit reserved-path story rather than a catch-all |
| Short / alias URLs with redirects | BUILT | aliases, any number, permanent redirects, ambiguity logged and dropped |
Events
| Item | Verdict | Justification |
|---|---|---|
| Hook points (on publish, on webmention) | LEAF | The Effect primitive. Counter at 1 (the derivative pass), runner due at 2 — so the second Effect earns the trigger wiring rather than inventing it |
| Outgoing webhooks (Slack, Telegram, custom) | LEAF after the runner | Outbound HTTP with per-target rules. Sequence it after the runner exists or it becomes the runner |
| Incoming webhooks (git push → rebuild) | Arc 3 | A write endpoint, therefore the untrusted boundary (ADR-0003) and rate limiting. Not a small addition to a read-only server |
| Auto-submit external links to archive.org | LEAF after the runner | Outbound Effect, third-party dependency at runtime |
| Git pull / push sync | CONFLICT | content-model.md: "The engine notices changes. It never fetches them — pulling a git repository is the operator's business, not the engine's." A deliberate position, and reversing it means the engine holds credentials and can rewrite the author's data. Question in chat |
| Auto-flag old pages | THEME — free today | And it is roadmap.md's own worked example: "An 'old article' banner is a Stage; it needs no job and can never be stale." Computable from the page plus the clock, so the template does it with the date it already has |
Interactivity
| Item | Verdict | Justification |
|---|---|---|
| Server-processed forms | Arc 3 | The Interaction primitive, with ADR-0003's boundary. Fields, validation, action target — plus rate limiting and spam handling, which is why they are one arc and not one feature |
| Webmention replies / reactions | Arc 3 | The canonical first Interaction; roadmap.md already sequences it |
| Client JS for demos only | Already the position | The reference theme ships no assets and no JS: icons are Unicode (ADR-0063), highlighting is server-side (ADR-0075), the TOC and the contents list are server-rendered, tabs and reveal panels are details with no script |
IndieWeb
| Item | Verdict | Justification |
|---|---|---|
| Webmention send/receive, Micropub, POSSE, mf2 | Arc 3 / 4 | roadmap.md puts Micropub first on purpose: it buys an existing editor ecosystem and needs no UI, which may remove the admin panel entirely |
| ActivityPub | TRUNK — already recorded as one | ideas/exploration.md lists it among known trunks. Your "only if it sparks joy" and the sovereignty test agree. Your instinct that it needs its own actor/inbox/signing mechanism rather than the generic webhook path is correct and is why it is a trunk |
| Gemtext | LEAF | An output format, and ideas/exploration.md stars Gemini as high fit-to-effort for this project's values |
Admin
| Item | Verdict | Justification |
|---|---|---|
| Dev server | BUILT | -dev on reveals drafts and future-dated bundles and polls 4×/s; content and templates swap atomically as one snapshot, so a template typo keeps the last working theme instead of taking the site down (ADR-0055, ADR-0056, ADR-0077) |
| Live reload (browser auto-refresh) | Tension | Needs JS or SSE on every page, against your own antifeature. The rebuild already happens in one poll; a manual refresh costs nothing. Recommend dropping it |
| Git as source of truth | BUILT by design | The site root is its own repository, passed with -site; nothing in the engine repo is content (ADR-0011) |
| Link / frontmatter checks | BUILT | khosra check: engine workarounds, broken internal links, missing titles, missing alt text, mixed series ordering, retired shortcode calls. Non-zero exit, and verify.sh runs it over the demo site |
Security & Ops
| Item | Verdict | Justification |
|---|---|---|
| Rate limiting on write endpoints | Arc 3 | Arrives with the first write endpoint, not before — there is nothing to limit on a read-only server |
| Spam filtering on webmentions | Arc 3 | With receive. Your honeypot-and-rate-limit stance beats CAPTCHA and is already in the antifeatures |
| Backups | Operator, not engine | ADR-0010's sovereignty line. The content is a git repo; the derivative cache is disposable by construction (content-hash names) |
Analytics
| Item | Verdict | Justification |
|---|---|---|
| Self-hosted / simple counting | LEAF, with a cheaper alternative | A counter is a write on the read path — state, IO, and a concurrency question the engine has deliberately avoided. Parsing access logs offline costs zero engine code and answers the same question. Recommend the log route first |
Antifeatures
Every one is consistent with a decision already recorded, and several are enforced rather than intended: no JS or assets in the reference theme (ADR-0063), server-side highlighting (ADR-0075), server-side TOC (ADR-0065), no trackers therefore no cookie banner, server-side over client-side as the standing preference.
Recommendation: record the list as an ADR. An antifeature that is not written down does not bind a future decision, and this list is exactly the kind of thing an agent will erode one reasonable-looking request at a time. It is also the cheapest ADR in the backlog — a decision you have already made.
One internal tension: "no hover-preview popups (the gwern.net thing)" versus Extras' "hover-preview
footnotes/sidenotes". Adjacent but arguably different — a footnote is your own text, a link preview is
someone else's page. Worth resolving explicitly in the ADR, and note :::aside already gives you
server-rendered margin notes with no JS at all.
Extras
| Item | Verdict | Justification |
|---|---|---|
| Now / Uses / Colophon pages | BUILT | Ordinary bundles under pages/. The demo site already has pages/colophon |
| Guestbook | Arc 3 | Forms or webmention; piggybacking on that infrastructure is right |
| Blogroll, OPML export | THEME + tiny LEAF | The list is content; OPML is one more output format |
| Link blog | BUILT as convention | A section plus a type. Nothing to build |
| TIL | BUILT as convention | Same |
| Digital-garden backlinks | Arc 4 | roadmap.md names backlinks and transclusion there. Needs a link graph over the index — a real feature, arc-gated, and transclusion needs a cycle guard |
| Random post | LEAF, tiny | One route. Note the discipline: time.Now() is confined to clock.go and verify.sh enforces it by filename; randomness deserves the same containment |
| Yearly wrap-up | LEAF, small | A Query by date range plus a listing. The Query primitive is already earned |
| Site changelog | THEME / content | A page you write, or git log of the content repo rendered — the latter shares the git-reading question with per-post edit history |
| Personal JSON API | LEAF | Same feature as "content as data" under SEO |
| Reading / watching / listening log | BUILT as convention | A section plus frontmatter |
| Writing stats page | LEAF, small | Query plus arithmetic. Pairs with word count |
| Command-palette search (Cmd+K) | Tension, and gated | Needs JS on every page, against the antifeature — and needs Search to exist first |
| Sticky / floating TOC | THEME | position: sticky. The TOC data already exists server-side |
| Hover-preview footnotes / sidenotes | THEME | Achievable with CSS alone, and :::aside already does margin notes. See the antifeature tension above |
| Copy link / share button | THEME | A plain link needs no JS; the clipboard does |
| 404 with personality | THEME | The 404 path exists and is tested; the words are the theme's |
humans.txt / security.txt |
LEAF | The raw-passthrough route. Same one feature |
| IndexNow-style ping on publish | LEAF after the Effect runner | Outbound |
| PGP / identity proof page | LEAF | The raw-passthrough route again |
| EXIF display on photo posts | LEAF — cheaper than it looks | The engine already opens and inspects every picture for width/height, memoised and LRU-bounded (ADR-0042, ADR-0044, memo.go). EXIF extends an existing pass rather than adding one |
Content-specific
Tech writing
| Item | Verdict | Justification |
|---|---|---|
| Copy-to-clipboard on code blocks | Tension | Needs JS. ~10 lines, but it is JS on ordinary pages, which your antifeatures rule out. Your call, not mine to soften |
| Line highlighting in code blocks | BUILT | hl=3,7-9, in the numbering the reader sees, plus title=, numbers=, start=, file=, lines= (ADR-0075) |
| "View as Markdown" / raw source per post | LEAF, small | The pattern exists: extras already serve ?raw. Extending it to a bundle body is a route, not a redesign |
| Corrections / updates section per post | THEME today | A frontmatter list plus a fragment. No engine code. And the right call for credibility — I would build this before most of the list |
| Per-post edit history | LEAF / ? | Means the engine reads git, which it does not do today. Either an Effect writes a history file, or this stays the operator's. Shares the question with git sync |
| Inline diagrams as a figure template | BUILT | ::figure{src alt caption} is exactly image + caption + alt |
| Jargon / acronym tooltips | BUILT for one document, gap across documents | *[TERM]: definition expands every whole-word use and renders <abbr> (ADR-0062) — tooltip is the browser's title. But a definition applies to that document only, and content-model.md names sharing a fragment between bundles as deliberately not possible yet: it needs somewhere to keep shared parts, which is a disk-contract decision. That gap is the real feature behind both this and the worldbuilding glossary |
| Sandboxed runnable demos | THEME + the styles/scripts lift |
An iframe is markup. Isolation is the iframe's, not the engine's |
Comics
| Item | Verdict | Justification |
|---|---|---|
| Numbered index, newest first, numbering independent of date | BUILT | order is position and never appears in a URL (ADR-0016); listings sort by date, sequences ascend by order. The two are already separate |
| One per page, next / prev | BUILT | Sequence navigation on any nested bundle, resolving through language fallback |
| — random | LEAF, tiny | One route |
| — grid when more than one per page | THEME | Layout |
| Panels, some hidden until revealed | BUILT | :::details — an expandable panel with no JavaScript (ADR-0064) |
| Description and alt text as separate fields | THEME today | Both live in Extra; the template decides which goes to alt and which to prose. khosra check already fails on missing alt text |
| Serialized arcs alongside one-offs | Agreed open — and a content question | You call it open and I think you are right to. Worth noting why it is cheap to defer: membership is structural (ADR-0033), so an arc is a nested directory and a one-off is not. The archive's reading order is a theme decision on top of data that already exists. Decide it with real content in front of you |
Artwork
| Item | Verdict | Justification |
|---|---|---|
| Gallery / grid view | BUILT + THEME | ::gallery gathers; grid is CSS |
| Lightbox | THEME | An <a> to the original is the no-JS version and also gives you "view full resolution" |
| Per-piece metadata (medium, tools, dimensions) | BUILT | The Extra bag, by design (ADR-0002) |
| Before / after slider | THEME | CSS-only versions exist; the draggable one is JS. Useful beyond art, as you say |
| "View full resolution" link | BUILT-able, one line | The original is already src and the derivatives are the srcset — the full-res file is already addressable |
| Attribution / license per piece | THEME | Extra plus a fragment |
| Alt text as a real description, required for this type | BUILT (checked) + ? (per-type requirement) | khosra check already fails on missing alt text everywhere. Making it required for art specifically needs declared types (queue 7) |
Creative writing
| Item | Verdict | Justification |
|---|---|---|
| Per-piece template variants — letter, terminal log, clipping | LEAF — and the most significant item in the list | This is the View layer: view: in frontmatter choosing a presentation, architecture.md's named primitive, counter at 0 of 2. Your Cross-cutting bullet restates it, which makes it the second concrete use — the thing that earns the primitive. Arc 2 closes when the View layer lands, and that is where the core stops growing (roadmap.md). You independently asked for the exact feature the roadmap was waiting for, and your framing — one content model, variants layered on top, not a content type per variant — matches the ADR-0023 contract already frozen for it |
| Story status: ongoing / complete / hiatus | THEME | Extra plus the series index template |
| Worldbuilding glossary with inline definitions | BUILT for one document, same cross-document gap as jargon tooltips | See above — the shared-fragment decision is the real blocker for both |
| Content warnings | THEME today, ? for first-class | A frontmatter field works now. "First-class across all four types" means enforced, which needs declared types (queue 7) |
| Epigraphs / dedications | THEME | Extra plus a fragment |
Cross-cutting
| Item | Verdict | Justification |
|---|---|---|
| Per-piece template variant across all types | = the View layer above | One feature, stated twice. Counts as its second concrete use |
| Alt text and content warnings first-class across types | ? | "First-class" = enforced per type = declared content types, queue 7, whose trigger is currently "a dated bundle needing to stay out of a feed". Your requirement is a better trigger for it than the recorded one |
What this adds up to
Already shipping, or shipping with a theme fragment you write: the majority. Notably the whole admonitions/tabs/panels family, extras folders, includes, series, tags, feeds, sitemap, canonical and redirects, responsive images, code highlighting with line ranges, abbreviations, TOC, dev server, checks, and — verified this session — user-defined shortcodes with no rebuild.
Three items are disproportionately valuable:
- Per-piece template variants — earns the View layer and closes Arc 2. Architecturally the most important thing in the list, and you asked for it twice without naming it.
- Raw passthrough routes — one small feature, four list entries, and a real gap today.
- Corrections/updates per post — no engine code at all, and the one item here that changes whether the writing can be trusted.
Four collide with recorded decisions and are surfaced rather than settled: task lists (ADR-0058),
private/secret-link (ADR-0024), git sync (content-model.md), external fetching for oEmbed and static
copies (same). One reframe removes the fourth entirely.
Five need JS on ordinary pages, against your own antifeature: copy-to-clipboard, Cmd+K, draggable before/after, lightbox, live reload. Each has a no-JS equivalent except the clipboard.
The shortest line is the biggest job. "Search" is one word and is arc-sized, because Bengali tokenisation is the novel problem this project has been pointing at from the start.