ADR md → Human-Friendly HTML Dual Output Automation (blog integration + KR/EN UI + content i18n infra)
Sprint 157 — ADR md → Human-Friendly HTML Dual Output Automation
Goals
- Keep ADR md files as LLM-friendly SSOT while automatically generating an HTML site that lets humans quickly grasp decision flow, impact, and context
- 105 ADRs (8 permanent + 1 topic + 96 sprint) authored once → automatically converted via blog
(adr)routing group with visualization + search + Related ADR graph - Establish Sprint 152 seed #19 (KR/EN dual-language plan mandate) — UI i18n routing + content i18n infrastructure +
/stopworkflow obligation
Decisions
- Build model: Integrate into blog/ Next.js 15 + MDX infrastructure as
(adr)routing group (user-confirmed). Reuse single Docker image + nginx + k3s GitOps cycle — 0 new workflow files.ci.yml:132-134'sblogpaths filter already includesdocs/adr/** - Scope: Extended (conversion + visualization + search + categories full package) — user-confirmed
- PR bundling: 4 PRs integrated in a single sprint (P1 / P2~P8 / P9 / P10) — inheriting Sprint 150/153/154 bundle pattern. Avoids inefficiency of 8 sequential PR merges with CI wait
- i18n dimension 1 (UI): User direct feedback ("ADR doesn't seem to work for both English and Korean") → P9 hotfix added immediately. Apply existing blog
(ko)/+en/pattern to ADR./adr/↔/en/adr/routing + LocaleToggle + 50 dictionary keys - i18n dimension 2 (Content): Explicit user requirement → P10 added.
docs/adr-en/separate directory (blogcontent/posts-en/pattern) + loader locale extension + Claude API auto-translator +/stopworkflow EN mandate
Implementation (10 PRs squash merge, origin/main 9f1217a → 1ba57d6 + PR #262)
| PR | Phase | Owner | Changes | Lines |
|---|---|---|---|---|
| #253 | P1 | architect | blog/src/lib/adr/{types,loader,parser,section-aliases,index-builder,markdown,fixtures}.ts 7 files + minisearch dependency | +1,139 |
| #254 | P2~P8 | architect | scripts/check-adr-{conversion,links}.mjs + (adr)/ 6 routing pages + 12 visualization components + minisearch search + mermaid Related ADR graph + ci.yml 2 steps | +3,620 |
| #255 | P9 | architect | UI i18n: /en/adr/ 6 routes + LocaleToggle integrated header + i18n dictionary ~50 keys + 12 components locale prop propagation + KoreanOnlyBanner | +926 −203 |
| #256 | P10 | architect | Content i18n infra: docs/adr-en/ + getAllAdrs(locale) extension + hasEnTranslation flag + scripts/translate-adr.mjs Claude API auto-translator + /stop workflow + Scribe persona EN mandate | +771 −44 |
| #257 | ADR + hotfix #1 | scribe+architect | sprint-157 KR+EN ADR + next.config.ts outputFileTracingIncludes removal (out skip hypothesis) | +215 −3 |
| #258 | hotfix #2 | architect | outputFileTracingRoot: __dirname explicit (workspace root warning silenced) + EN ADR broken refs (sprint-152/156 → KR SSOT links) | +11 −5 |
| #259 | hotfix probe | architect | ci.yml Build Blog step out/ location probe + fallback (debug stage). Probe log became the decisive diagnostic | +20 −2 |
| #260 | chore + trigger | scribe | docs/adr/README.md sprint count fix (89→97) + paths filter triggered build-blog → probe results revealed | +3 −3 |
| #261 | hotfix #3 real fix | architect | ci.yml node ../scripts/check-adr-links.mjs out/adr → node scripts/check-adr-links.mjs blog/out/adr absolute segment. check-adr-links.mjs:33 ROOT=repo root dependency was the real cause | +5 −21 |
| #262 | UX addition | architect | Blog → ADR entry navigation: header.tsx right nav 'ADR' link + home-page.tsx CTA card + i18n dictionary 4 keys (user feedback "no button to navigate from existing blog to ADR" addressed immediately) | +51 −3 |
Verification
- All 4 PRs CI fail 0, mergeStateStatus CLEAN ✅ (auto-merge flow)
npx tsc --noEmit— 0 errorsnpm run build— 244 static pages (KR 122 + EN 122, prior 31 → 244)node scripts/check-adr-conversion.mjs— 10 fixture pass + 105 ADRs parsed successfullynode scripts/check-adr-links.mjs blog/out/adr— 1,109 links, 0 brokennode scripts/check-adr-links.mjs blog/out/en/adr— 1,213 links, 0 brokennode scripts/check-doc-refs.mjs --include-untracked— 172 files, 0 broken refsnode scripts/check-adr-en-coverage.mjs --lint— 105 WARN (strict activation deferred to Sprint 158+)- Browser visual verification:
/adr/(index + timeline + cards) +/adr/sprints/156/(3-column TOC + body + meta sidebar + Related ADR mini-graph) +/en/adr/(English UI) +/en/adr/sprints/156/(KoreanOnlyBanner + Korean body) - Sprint 155 3-layer safety net (plan + pre-push + CI lint) effective on all sprint commits — 0 violations
Branch discipline ✅ 25 sprints consecutive compliance
All 4 PRs used new branches + Squash merge, 0 direct commits to main (since Sprint 134 violation).
New patterns
- User direct feedback → immediate hotfix cycle (inheriting Sprint 150~152 pattern) — UI i18n gap (P9), content i18n omission (P10) both addressed via separate PRs immediately. Plan-stage omissions recovered in real-time through user verification cycle
- Single sprint 4-PR bundle + auto-merge flow —
gh pr merge --squash --autoauto-merges on CI green. Avoids inefficiency of 8 sequential PR CI wait. Evolution of Sprint 150/153/154 bundle pattern - External directory static import (blog → docs/adr) —
path.resolve(process.cwd(), '..', 'docs', 'adr')+ explicitoutputFileTracingIncludes. Verified pattern for safely referencing external SSOT inoutput: 'export'static export environment - Fallback chain — no frontmatter + English section names — gray-matter failure gracefully degrades to body H1/dash-list/H2 pattern. sprint-62~87 English section alias mapping handles 90+ sprint ADRs + 8 permanent ADRs in single pipeline
hasEnTranslationflag + conditional KoreanOnlyBanner — natural English page when EN translation exists, Korean body + banner + original link when absent. Seamlessly supports gradual translation migration- Auto-translator infrastructure first → actual translation gradual —
translate-adr.mjsinfrastructure complete +/stopmandate + lint (advisory) in this sprint. 95-file batch translation immediately possible upon user API key availability. Infrastructure-first separation /stopworkflow KR+EN dual-write mandate self-bootstrap — Rule introduced this sprint (P10) immediately self-applied to this sprint's ADR creation. Meta-self-verification cycle (inheriting Sprint 154~155 pattern)
Lessons
- Plan-stage i18n omissions are only recovered via user verification cycle — Sprint 152 seed #19 (KR/EN dual mandate) again omitted in this sprint's plan stage. User direct feedback ("Ko/En buttons exist") triggered P9/P10 hotfix. Need to automate seed #19 as plan template checklist item (Sprint 158 candidate)
- UI i18n and content i18n are separate efforts — P9 alone shows English pages, but Korean body limits utility. Must include content infrastructure (P10) for completeness. Both dimensions must be addressed simultaneously in plan stage
- Don't create
(adr)/page.tsxinside(adr)route group — conflicts with existing(ko)'s/route. Route groups don't affect URL, so registering same root path causes build failure. Only/adr/...subroutes are safe - Mermaid code fences require pre-renderMdx transformation — ADR body
```mermaid ... ```must be pre-replaced with<Mermaid chart={String.raw\...`} />JSX. To work with compileMDX(format: 'md'`), mermaid code blocks render as code blocks and graphs are separated into components - Forbid placeholder API key when ANTHROPIC_API_KEY missing — Verify env var → exit 2 + guidance only if missing. Pilot translation separated to user direct execution. Security + cost transparency achieved together
- CI paths filter reuse → 0 new workflow files —
ci.yml:132-134'sblogfilter already includesdocs/adr/**, so this sprint created 0 new workflow files. Existing infrastructure leveraged - Browser visual verification is the last safety net — tsc/lint/link integrity all passing doesn't catch visual regressions. Only after directly verifying index/detail/graph/English pages in browser was safety confirmed
outputFileTracingIncludes+output: 'export'combo silently skipsout/generation — Sprint 157 P10 addedoutputFileTracingIncludes, but despitenext buildreturning exit 0 + 239-page build log output,out/directory was generated 0 times. Surfaced asADR link integritystep failure in CI only. After 4-PR auto-merge, user direct feedback "CI all failed" triggered post-merge hotfix (removed option fromnext.config.ts). Static export has no runtime file access, so trace include itself is unnecessary. Even on CI green, mandatory build artifact existence check (ls out/) required when adding new configuration — Sprint 158 candidate
Sprint 158 carryover
- User direct execution (after API key acquisition):
- Pilot translate 10 ADRs — 8 permanent + 1 topic + sprint-156 (
node scripts/translate-adr.mjs --target <path>) - After validation, batch translate remaining 95 via
--all
- Pilot translate 10 ADRs — 8 permanent + 1 topic + sprint-156 (
- New automation candidates:
- Seed #24: Auto-insert i18n dual-language mandate checklist into plan template (automate Sprint 152 seed #19 at plan stage)
- Seed #25: Activate
check-adr-en-coverage.mjs --strictas CI hard gate (currently advisory) - Seed #26:
docs/adr/README.mdpaths filter negation (block unnecessary blog builds)
- UAT user direct (14 sprints accumulated):
- Seed #5: Programmers re-submission grading verification
- Seed #9: English environment + production Grafana CB dashboard ai-analysis visual consistency
- Carryover maintained:
- Seed #18: Pre-merge domain fact cross-check automation for blog posts
- Seed #23: Plan template "rebase post cumulative count fix" checklist (Sprint 156)
- Follow-up (optional):
- create/edit page.tsx category UI
- Programmers URL auto-category inference
- Backfill existing SQL problem data
- Remove
skippedallowance from coverage-gate (Sprint 156 Phase B option B) - Post-merge pre-deploy gate (Sprint 156 Phase B option C)
- prom-client Case B~D check automation
.claude-tools/Phase 2 actual deletion (after trigger path verification)
Related documents
- docs/adr/README.md — English directory guidance + auto-translator usage added
- docs/adr-en/README.md — English translation policy SSOT
- scripts/translate-adr.mjs — Claude API auto-translator
- scripts/check-adr-conversion.mjs — 10 fixture self-test
- scripts/check-adr-links.mjs — Build output link integrity
- scripts/check-adr-en-coverage.mjs — EN translation coverage lint
- sprint-152.md (KR) — Seed #19 (KR/EN dual mandate) established this sprint (EN version pending translation in Sprint 158+)
- sprint-156.md (KR) — Previous sprint, this sprint's starting point (
9f1217a) (EN version pending translation in Sprint 158+)