system
400.wtf

400.wtf / system / docs · docs/ADR/0003-artifact-content-collection-registry.md

ADR-0003 — Artifacts as an MDX content collection

Status: Accepted (documents the existing decision)

2026-10 (DS-5): The collection is now stations, one folder per station with pt.mdx / en.mdx (write-up only) and _stage.astro (the island). Entry ids are paths (weaver/pt); the slug frontmatter field is gone. See apps/site/src/content.config.ts.

Context

Artifacts are the site’s content type: each needs a URL, typed metadata (status, category, presentation mode…), an interactive implementation, and — per the roadmap — prose. New artifacts should be addable without touching routing or index code.

Decision

One Astro content collection (src/content.config.ts) globbing src/artifacts/**/*.mdx, with a Zod schema over frontmatter. Implementation code lives beside the MDX in _-prefixed folders (excluded from the glob), imported and mounted by the MDX body as a Vue island. Routes (/artifact/[...slug]) and the index derive entirely from the collection.

The collection is the registry; the filesystem is the database; git is the CMS.

Alternatives considered

  • Hardcoded pages per artifact: no schema, no index generation, metadata drifts — rejected.
  • Separate data file (JSON/TS array) + component map: typed and explicit, but splits one artifact across two locations and needs a manual registry — the glob does this work for free.
  • Colocate prose elsewhere (per-artifact about.md): MDX already merges prose and mount point in one file; splitting would be structure for its own sake.
  • A real CMS: absurd at this scale; noted only to mark the boundary.

Tradeoffs

  • (+) Adding an artifact = adding a folder. Zod validates at build. render(post) gives prose for free when write-ups land.
  • (+) _ privacy convention keeps implementation out of the collection cleanly.
  • (−) The id/slug trap: routes use loader id while links use data.slug; they agree only because both artifacts set explicit slug frontmatter (the loader adopts it). The schema’s slugify(title) fallback produces a value routing ignores — a 404 factory for any future artifact that omits slug. Fix per architecture.md R1 (make slug required, delete the fallback).
  • (−) MDX compile is part of the build; heavy artifact counts could slow builds (not a concern below ~100 entries).

Consequences

  • The frontmatter schema is the artifact API: index UI, artifact shell, OG images, and the future scaffold all read from it. Schema changes are project-wide changes — version them consciously (roadmap adds: hint, code for HTTP numbering, OG copy).
  • Artifact implementations must stay self-contained (no cross-artifact imports); shared needs graduate into utils//composables/ by the rule of three.

Future evolution

  • Scaffold (pnpm new:artifact) stamps the contract (artifacts.md R2).
  • Possible second collection for lab notes (content.md R7) — same pattern, separate concerns.
  • featured/category become curation inputs for the index redesign.