Daeseon Yoo
Back to project
·Decision·2 min

Per-project README pages: stored + sanitized, not live-fetched

Each project can now show its full README as its own page (/projects/<slug>/readme), and the README also feeds a richer project intro. Chose store-in-blog + sanitize-on-render over live-fetch from the (private) satellite, so the render is verifiable and dead links are neutralized.

Context

Project intros read thin, and there was no way to see a project's full README without its (now-private) repo. Goal: a richer intro (summarized from the README) plus the full README viewable as its own page.

Options considered

Chosen + Why

Store + sanitize. The README is mirrored to content/projects/readme/<slug>.md and rendered at /projects/<slug>/readme (en+ko) through the existing compileMDX. lib/readme.ts sanitizes first: strips the HTML chrome MDX can't parse, and neutralizes links that would 404 (relative + private-repo → plain text) while keeping in-page anchors, daeseon.ai links, and shield badges. A "Full README" button appears on the project page only when a README exists. The same README content also drove the richer Mimi intro.

Trade-off accepted

The stored README is a copy that can drift from the satellite's canonical README (acceptable — it's mirrored on update). Relative repo-doc links (ARCHITECTURE.md, etc.) become plain text rather than links, because the repo is private and they'd 404 — losing click-through but avoiding the dead links the recruiter audit flagged as the worst offense.

Reversibility

two-way — a route + lib/readme.ts + stored markdown; revert is a commit.

Verified by

next build prerenders /projects/shadow-ai/readme; the artifact has the content, zero leaked private-repo links from the README (the 2 remaining GitHub hrefs are the site footer's public org link), in-page anchors and shield badges intact, and the "Full README" button on the project page.

Commit

49994b8.