ADR md → Human-Friendly HTML Dual Output Automation (blog integration + KR/EN UI + content i18n infra)

SummaryKeep ADR md files as LLM-friendly SSOT while automatically generating an HTML site that lets humans quickly grasp decision flow, impact, and context

Date
ImpactCritical
PRs10
Lines changed+6761 -284

Implementation Phases

P1
#253

P1

architect+1,139
P2~P8
#254

P2~P8

architect+3,620
P9
#255

P9

architect+926 −203
P10
#256

P10

architect+771 −44
ADR + hotfix #1
#257

ADR + hotfix #1

scribe+architect+215 −3
hotfix #2
#258

hotfix #2

architect+11 −5
hotfix probe
#259

hotfix probe

architect+20 −2
chore + trigger
#260

chore + trigger

scribe+3 −3
hotfix #3 **real fix**
#261

hotfix #3 real fix

architect+5 −21
UX addition
#262

UX addition

architect+51 −3

Key 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's blog paths filter already includes docs/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 (blog content/posts-en/ pattern) + loader locale extension + Claude API auto-translator + /stop workflow EN mandate

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 + /stop workflow 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's blog paths filter already includes docs/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 (blog content/posts-en/ pattern) + loader locale extension + Claude API auto-translator + /stop workflow EN mandate

Implementation (10 PRs squash merge, origin/main 9f1217a1ba57d6 + PR #262)

Workflow

Verification

  • All 4 PRs CI fail 0, mergeStateStatus CLEAN ✅ (auto-merge flow)
  • npx tsc --noEmit — 0 errors
  • npm run build244 static pages (KR 122 + EN 122, prior 31 → 244)
  • node scripts/check-adr-conversion.mjs — 10 fixture pass + 105 ADRs parsed successfully
  • node scripts/check-adr-links.mjs blog/out/adr — 1,109 links, 0 broken
  • node scripts/check-adr-links.mjs blog/out/en/adr — 1,213 links, 0 broken
  • node scripts/check-doc-refs.mjs --include-untracked — 172 files, 0 broken refs
  • node 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

  1. 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
  2. Single sprint 4-PR bundle + auto-merge flowgh pr merge --squash --auto auto-merges on CI green. Avoids inefficiency of 8 sequential PR CI wait. Evolution of Sprint 150/153/154 bundle pattern
  3. External directory static import (blog → docs/adr)path.resolve(process.cwd(), '..', 'docs', 'adr') + explicit outputFileTracingIncludes. Verified pattern for safely referencing external SSOT in output: 'export' static export environment
  4. 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
  5. hasEnTranslation flag + conditional KoreanOnlyBanner — natural English page when EN translation exists, Korean body + banner + original link when absent. Seamlessly supports gradual translation migration
  6. Auto-translator infrastructure first → actual translation gradualtranslate-adr.mjs infrastructure complete + /stop mandate + lint (advisory) in this sprint. 95-file batch translation immediately possible upon user API key availability. Infrastructure-first separation
  7. /stop workflow 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)