Heuristic Mermaid 자동 생성 설계
Heuristic Mermaid 자동 생성 설계
Status: implemented in repository. Current code reflects the v1 scope described here.
문제
현재 Docs Viewer는 markdown 안에 직접 작성된 ` ```mermaid ` 블록만 추출한다.
vibelign/core/docs_visualizer.py는 extraction-only 구조다.- README / guide / spec / plan 문서처럼 구조는 풍부하지만 Mermaid가 없는 문서는
diagram_blocks=[]가 된다. - 사용자는 “문서를 읽으면 구조 다이어그램이 자동으로 나오길” 기대하지만, 현재 계약은 그 기대를 충족하지 못한다.
목표
문서에 authored Mermaid가 없어도 markdown 구조를 바탕으로 신뢰 가능한 범위에서만 Mermaid 다이어그램을 자동 생성한다.
핵심 원칙:
- markdown가 source of truth다.
- authored Mermaid가 있으면 그것을 항상 우선한다.
- heuristic은 deterministic해야 한다.
- confidence가 낮으면 생성하지 않는다.
- autogenerated diagram은 authored처럼 보이면 안 된다.
핵심 변경
1. diagram provenance 확장
diagram_blocks에 출처와 신뢰 메타를 추가한다.
{
"id": "diagram-heuristic-1",
"kind": "mermaid",
"title": "설치 흐름",
"source": "flowchart TD\nA[설치] --> B[설정]",
"provenance": "heuristic",
"generator": "step-flow-v1",
"confidence": "high",
"warnings": []
}
provenance:authored | heuristic | ai_draftgenerator: 생성 규칙 이름confidence: persisted diagram에서는high | medium만 사용warnings: diagram 자체에 붙는 local warning 메모
score < 4처럼 diagram이 아예 생성되지 않는 경우의 warning은diagram_blocks[].warnings가 아니라 artifact rootwarnings에 기록한다.
2. authored-first + heuristic fallback
현재:
diagram_blocks = _extract_mermaid_blocks(normalized, sections)
제안:
diagram_blocks = _extract_mermaid_blocks(normalized, sections)
if not _has_usable_authored_diagram(diagram_blocks):
diagram_blocks = _generate_heuristic_diagrams(
lines=lines,
title=title,
summary=summary,
sections=sections,
action_items=action_items,
warnings=warnings,
)
_has_usable_authored_diagram()은 단순히len(blocks) > 0을 보지 않는다. 아래 조건을 모두 만족하는 authored block이 1개 이상 존재해야 authored로 인정한다:
source가 공백만이 아닌 non-empty 문자열- Mermaid 선두 keyword(
flowchart,graph,mindmap,sequenceDiagram,classDiagram,stateDiagram,erDiagram,journey,pie,gantt,gitGraph,timeline) 중 하나로 시작위 조건을 만족하는 authored block이 없으면 heuristic fallback이 실행되며, 빈 authored block은
auto_diagram_note: empty authored mermaid block ignoredwarning 과 함께 drop한다. 초기 계약에서 authored block의 invalid 판단은 빈 source 또는 선두 keyword 미매칭을 의미한다. 즉, 여기서 말하는 invalid authored block은 Mermaid 엔진의 전체 parse validation을 뜻하지 않는다. v1에서는 cheap gate만 적용한다.
2.1 cross-platform inheritance
이 설계는 기존 docs viewer의 cross-platform 규칙을 그대로 상속한다.
- path separator / relative path key /
source_hash정책을 새로 정의하지 않는다. - heuristic generator는 기존 visualizer가 이미 정규화한 markdown 텍스트만 입력으로 사용한다.
- 즉 BOM strip, newline normalize, UTF-8 정책은 기존 helper가 authoritative하다.
- heuristic logic은 raw bytes, OS-native line ending, OS-native path separator를 직접 해석하지 않는다.
이 기능은 새 platform layer가 아니라, 기존 hash-bound derived artifact 파이프라인 위에 얹는 parser/rendering 확장이다.
3. heuristic generator는 4종을 지원 (단, 초기 릴리즈는 3종)
스펙은 아래 4종을 정의한다.
step_flowdecision_flowheading_mindmapcomponent_flow
초기 릴리즈 범위: step_flow, heading_mindmap, component_flow 3종만 구현한다.
decision_flow는 자유 서술체 마크다운에서 {question?} / yes-branch / no-branch 노드를
deterministic하게 추출하는 알고리즘이 본 스펙에서 아직 확정되지 않았으므로 후속 릴리즈로 유예한다.
score guide와 tie-break 순서에는 계속 존재하되, candidate 생성기가 None을 반환하도록 구현하여
선택되더라도 바로 다음 후보로 fallback한다.
즉 v1에서는 decision signal이 탐지되고
decision_flow가 선택 단계까지 올라갈 수는 있어도, 실제 emitted diagram은 항상 다음 fallback 후보의 결과이거나 최종적으로 no-diagram이다.
애매한 문서는 생성하지 않는다.
signal 추출 규칙
ordered step
다음 패턴을 순서형 step으로 본다.
1. foo2) foo① fooStep 1: foo1단계 — foo
ORDERED_STEP_RE = re.compile(
r"^\s*(?:\d+[.)]|[①②③④⑤⑥⑦⑧⑨⑩]|step\s*\d+[:.)]?|\d+단계)\s*(?:[-—:]\s*)?(?P<text>.+)$",
re.IGNORECASE,
)
주의: markdown ordered list (
1. foo,2. bar)는 changelog / TOC / nested list 에서도 흔하게 나온다. 따라서 정규식 매칭만으로 step으로 판정하지 않고, 연속 증가 번호 3개 이상(1,2,3…)을 요구한다. 번호가 리셋되거나 건너뛰면 step sequence로 보지 않는다.“연속”의 정의는 다음과 같다:
- 동일 list block scope 내에서만 연속성을 판단한다. (빈 줄 2개 이상 / 다른 heading / fenced code block / 다른 block element 가 나타나면 list scope 가 끊긴 것으로 본다.)
- scope 내에서
n, n+1, n+2, …형태로 1씩 증가해야 한다 (markdown renderer가 auto-renumber 하더라도 source 번호 기준).- nested list (indent 가 상위와 다른 항목) 는 최상위 sequence로 카운트하지 않는다.
Step N/N단계/①②③표기도 마찬가지로 같은 block 범위 내에서 1씩 증가해야 한다.- cross-section / cross-heading step은 인정하지 않는다 (heading 사이에 끼어있으면 끊긴 것).
action_items / checklist
- [ ] foo, - [x] foo 패턴을 checklist로 본다.
CHECKLIST_RE = re.compile(r"^\s*[-*]\s*\[[ xX]\]\s+(?P<text>.+)$")
action_items는 이 helper가 signal dataclass에 채운다.
별도 저장소나 외부 파이프라인에 의존하지 않는다.
decision line
다음 표현을 decision 신호로 본다.
if,when,fallback,yes,no존재?,일치?,없으면,실패 시,성공 시
DECISION_HINT_RE = re.compile(
r"\b(if|when|else|fallback|exists?|missing|match|mismatch|yes|no|success|fail)\b|존재\?|일치\?|없으면|실패\s*시|성공\s*시",
re.IGNORECASE,
)
file / component signal
다음 패턴을 component 후보로 본다.
`configure.js`README.mdfoo.py
# forward-slash, backslash, drive-letter 를 모두 허용한다.
FILE_LIKE_RE = re.compile(
r"(?:[A-Za-z]:[\\/])?" # optional drive letter (Windows)
r"(?:[\w.\-]+[\\/])*" # path segments with / or \
r"[\w.\-]+\.(?:js|ts|tsx|py|md|json|yaml|yml|rs)\b"
)
위 regex는
configure.js,docs/guide/README.md,docs\guide\README.md,C:\foo\bar.py를 모두 매칭한다. path-like text는 component 후보를 찾기 위한 display signal일 뿐이며, cache key / artifact identity / node id source로 사용하면 안 된다. 또한 fenced code block 내부, markdown link destination ([text](path)의 target), shell command example 안의 path는 기본적으로 component signal에서 제외한다. signal 대상은 prose / list / table 셀의 path-like text를 우선으로 한다.
table signal
markdown table에서 file / role / module / component 류 컬럼이 보이면 component flow 후보 점수를 올린다.
doc kind hint
문서 제목 / 섹션 제목 / 본문에서 다음 힌트를 점수화한다.
이 키워드 리스트는 단일 source 로 관리하며, signal 추출과 candidate scoring이 같은 dict를 참조해야 한다. 두 곳에서 독립적으로 하드코딩하지 않는다.
DOC_KIND_HINTS: dict[str, tuple[str, ...]] = {
"step": ("설치", "사용법", "가이드", "step", "phase", "workflow", "절차", "실행"),
"decision": ("faq", "판정", "결정", "trust", "fallback", "검증", "decision"),
"component": ("구성", "파일", "역할", "architecture", "module", "모듈", "컴포넌트"),
"overview": ("readme", "소개", "개요", "overview", "about"),
}
매칭 규칙:
- 모두 소문자로 비교한다 (대소문자 무관).
- 본문은 단어 단위가 아닌 substring 매칭을 허용한다 (한국어 때문에).
overviewhint는 파일 수준 override와 별도다 — 본문 매칭은 score만 올리고 override는 발동하지 않는다.- locale 확장 시 위 dict 한 곳만 수정한다.
diagram type 별 규칙
1. step_flow
선택 조건
- ordered steps >= 3
- 또는 checklist/action_items >= 3
- 단계형 문서 / 설치 가이드 / 실행 순서 문서
confidence
high: ordered steps >= 4medium: checklist 위주low: step 수가 적거나 텍스트가 장황
여기서
low는 persistedconfidence값이 아니라 internal scoring outcome이다. 실제 artifact에는high | medium만 기록되고,low는 diagram skip + artifact root warning으로 처리된다.
Mermaid 템플릿
flowchart TD
S1["유저 스크립트 폴더에 복사"]
S2["API 키 입력"]
S3["Harmony 재시작"]
S1 --> S2
S2 --> S3
def _render_step_flowchart(title: str, steps: list[str]) -> str:
nodes = []
edges = []
for i, step in enumerate(steps[:8], start=1):
node_id = f"S{i}"
label = _safe_mermaid_label(step, 36)
nodes.append(f'{node_id}["{label}"]')
if i > 1:
edges.append(f"S{i-1} --> {node_id}")
return "\n".join(["flowchart TD", *nodes, *edges])
2. decision_flow
선택 조건
- decision lines >= 2
- fallback / validation / trust / error path 문서
confidence
high:존재?,일치?,없으면같은 분기 조건 명확medium: 조건 문장만 있음low: 키워드만 산발적
여기서
low는 persistedconfidence값이 아니라 internal scoring outcome이다. 실제 artifact에는high | medium만 기록되고,low는 diagram skip + artifact root warning으로 처리된다.
Mermaid 템플릿
flowchart TD
START["문서 열기"]
D1{"artifact 존재?"}
Y1["계속 진행"]
N1["markdown-only"]
START --> D1
D1 -->|yes| Y1
D1 -->|no| N1
3. heading_mindmap
선택 조건
- top headings >= 3
- ordered step는 약함
- README / overview / FAQ / concept 문서
README / overview override는 파일 수준 hint가 있을 때만 적용한다. 아래 조건 중 하나를 만족해야 override가 발동한다:
- 파일명이
README.md/readme.md(대소문자 무관)- 문서의 H1 이
README,Overview,소개,개요중 하나와 정확히 일치 (partial match 금지)본문 / H2 이하 점수용 hint(도입부에 “overview”가 섞인 가이드 문서 등)는 override를 트리거하지 않고, 기존 score 규칙만 적용한다. 이유: “설치 가이드 (README 참고)” 같은 제목에 README 단어가 섞였다는 이유로 mindmap이 강제되는 오탐을 막는다.
confidence
high: H2/H3 구조 선명medium: heading 수는 충분하나 질이 약함low: heading 거의 없음
여기서
low는 persistedconfidence값이 아니라 internal scoring outcome이다. 실제 artifact에는high | medium만 기록되고,low는 diagram skip + artifact root warning으로 처리된다.
Mermaid 템플릿
mindmap
root(("Annotation Translator v1.0"))
"설치 가이드"
"사용법"
"설정 항목"
"FAQ"
"파일 구성"
"참고 링크"
def _render_heading_mindmap(title: str, top_headings: list[VisualSection]) -> str:
lines = ["mindmap", f' root(("{_safe_mermaid_label(title, 28)}"))']
for heading in top_headings[:8]:
lines.append(f' "{_safe_mermaid_label(heading.title, 28)}"')
return "\n".join(lines)
4. component_flow
선택 조건
- file-like item >= 3
파일 구성,구성 요소,역할섹션 존재- role table 또는 module list 존재
confidence
high: 파일명 + 역할 표 명확medium: bullet 위주 파일 목록low: component 신호 부족
여기서
low는 persistedconfidence값이 아니라 internal scoring outcome이다. 실제 artifact에는high | medium만 기록되고,low는 diagram skip + artifact root warning으로 처리된다.
Mermaid 템플릿
graph LR
subgraph components ["파일 구성"]
C1["configure.js"]
C2["AT_Config.js"]
C3["annotationTranslator.js"]
end
이 타입은 실제 dependency graph가 아니라 구조 요약이다. 화살표(
-->)는 dependency/호출 관계로 오해되므로 사용하지 않는다. node를subgraph로 그룹핑만 하고, edge는 생략한다. 렌더 시 banner로 “structural summary, not a dependency graph” 를 반드시 표시한다.
candidate 선택 전략
후보를 모두 만들고 점수 기반으로 하나만 선택한다.
score guide
step_flow
- ordered steps >= 4: +4
- ordered steps >= 3: +3
- action_items >= 3: +2
- process keyword present: +2
decision_flow
- decision lines >= 3: +4
- fallback / warning keyword: +2
- yes/no 표현: +2
heading_mindmap
- top headings >= 4: +4
- overview/readme hint: +2
- ordered steps < 3: +1
component_flow
- file-like items >= 3: +3
- component section title present: +2
- role-like table row 존재: +2
selection rule
- 최고 점수 후보 1개 선택
- 단, 파일 수준 README/Overview hint (파일명 ==
README.md또는 H1이README/Overview/소개/개요중 하나와 정확 일치) 가 있으면heading_mindmap을 기본 선택으로 고정하고 다른 후보는 override하지 않는다. 예외는 authored Mermaid뿐이다. - 동점이면 우선순위:
step_flowdecision_flowcomponent_flowheading_mindmap
score < 4이면 생성 안 함
confidence rule
high:score >= 6medium:score == 4 or 5score < 4→ 생성 skip (confidence 값이 아니라 “skip 사건”으로 처리)
즉 artifact에 실제로 기록되는 confidence 값은 high | medium 두 가지뿐이다.
low는 상태가 아니라 skip reason이며, 대신 auto_diagram_skipped: low confidence warning이 남는다.
(이는 plan.md의 “confidence=low이면 생성하지 않는다” 규칙과 동일한 의미의 단일 정의다.)
안전한 label 정규화
Mermaid label은 길이와 특수문자를 제한한다.
# Mermaid syntax-sensitive characters: " { } [ ] ( ) < > ` | # ; :
# | 은 edge label 구분자, # 은 style directive, ; 은 statement 구분자.
_MERMAID_UNSAFE_RE = re.compile(r'["{}\[\]()<>`|#;:]')
def _safe_mermaid_label(text: str, max_len: int = 32) -> str:
# Windows backslash path(`docs\guide\README.md`)는 display 의도를 지키기 위해
# 파괴하지 않고 forward slash 로 **치환**한다.
value = text.replace("\\", "/")
# CRLF 잔여 제거
value = value.replace("\r", "")
value = _MERMAID_UNSAFE_RE.sub(" ", value)
value = re.sub(r"\s+", " ", value).strip()
if len(value) <= max_len:
return value
return value[: max_len - 1].rstrip() + "…"
backslash 자체를 제거하면
C:\foo\bar.py같은 라벨이C foo bar py로 깨진다. 따라서 backslash는 unsafe 문자로 취급하지 않고 forward slash 치환으로 정보 보존한다. Mermaid는 forward slash를 라벨 텍스트 안에서 안전하게 처리한다.
추가 특수문자가 문제되면 whitelist 방식(
[\w\s가-힣.,\-]+)으로 전환을 검토한다. 현재는 Mermaid syntax를 깨는 문자만 공백 치환하는 blacklist 방식으로 시작한다.
warnings 정책
다음 상황은 warning으로 남긴다.
- 문서가 너무 길어서 노드 수를 축약함
- 후보가 2개 이상이지만 1개만 선택함
score < 4라 생성 안 함- huge doc partial-disable 정책으로 heuristic도 skip됨
- component flow가 실제 dependency가 아니라 summary임
warning scope는 아래처럼 분리한다.
- artifact root
warnings- diagram skip
- huge doc partial-disable
- candidate truncation / selection note
diagram_blocks[].warnings- 특정 diagram에만 해당하는 local note
- 예:
component_flow의 structural summary 경고
예:
auto_diagram_truncated: top headings capped at 8auto_diagram_skipped: low confidence (score<4)auto_diagram_skipped_huge_doc: partial-disable policyauto_diagram_note: component flow is a structural summary
huge doc partial-disable 통합
기존 visualizer는 very long doc에서 일부 기능을 partial-disable한다.
heuristic fallback은 그 체크 이후에만 실행되며, partial-disable 조건이 걸리면 heuristic도 즉시 skip하고
auto_diagram_skipped_huge_doc warning을 남긴다.
이 규칙은 authored Mermaid extraction 경로에는 영향을 주지 않는다.
normalized input 보장
heuristic signal 추출은 반드시 기존 visualizer의 normalized text 기준으로만 동작한다.
- BOM 포함 파일도 normalize 이후 동일하게 처리한다.
- CRLF / LF 차이로 heuristic score나 Mermaid source가 달라지면 안 된다.
- 같은 markdown 내용이면 macOS / Windows에서 동일한 heuristic diagram source가 나와야 한다.
generator versioning
generator 필드는 step-flow-v1 처럼 이름 + 버전으로 기록된다.
버전이 올라가면 (예: step-flow-v2) 기존 artifact의 generator 값이 현재 코드와 mismatch되므로
generator mismatch 자체가 stale 조건이며 재생성 대상이 된다.
구현상 schema/version 체크와 같은 stale 판정 경로를 재사용해도 되지만, 의미적으로는 별도의 stale 원인으로 본다.
즉 heuristic 알고리즘 변경은 반드시 버전을 bump하여 cache가 자연스럽게 갱신되도록 한다.
viewer UX 규칙
Viewer는 provenance를 항상 보여야 한다.
Authored diagramAuto-generated from document structureAI-generated draft
heuristic / ai_draft는 설명 배너를 표시한다.
예:
이 다이어그램은 문서 구조를 바탕으로 자동 생성되었습니다. 원문 markdown가 기준입니다.
trust-state 규칙
trust state는 기존 기준을 유지한다.
- freshness는
source_hash - schema mismatch / generator mismatch는 stale
- heuristic diagram도 hash-bound derived artifact다.
단, trust state와 provenance는 다르다.
- trust state: 최신성 / artifact 유효성
- provenance: authored / heuristic / ai_draft 출처
예:
enhanced-synced + heuristicenhanced-stale + authoredmarkdown-only + none
수정 대상 파일
| 파일 | 변경 내용 |
|---|---|
vibelign/core/docs_visualizer.py |
heuristic signal 추출 + candidate 선택 + Mermaid source 생성 |
vibelign/core/docs_cache.py |
diagram schema field 확장 |
vibelign-gui/src/lib/vib.ts |
TypeScript artifact type 확장 |
vibelign-gui/src/components/docs/VisualSummaryPane.tsx |
provenance / confidence label 표시 |
tests/test_docs_visualizer.py |
README / step / decision / component heuristic 테스트 추가 |
테스트
- authored Mermaid가 있으면 heuristic 생성 안 함
- README형 문서에서 heading mindmap 생성
- step형 문서에서 flowchart 생성
- decision형 문서에서 decision flow 생성
- component 문서에서 component flow 생성
- 구조 신호가 약한 문서는 diagram 없음
- same input => same output 유지
예시 결과
README류 문서는 기본적으로 아래 결과를 기대한다.
mindmap
root(("Annotation Translator v1.0"))
"설치 가이드"
"사용법"
"AT_Config.js 설정 항목"
"API 키 탐색 순서"
"자주 묻는 질문"
"파일 구성"
"참고 링크"
최종 권장안
초기 버전은 아래만 구현한다.
- authored Mermaid 우선
- heuristic은 최대 1개만 생성
- README류는
heading_mindmap - 가이드류는
step_flow - 조건 문서는
decision_flow - 애매하면 생성하지 않음
- provenance와 confidence를 항상 표시
이 설계는 문서 커버리지를 크게 늘리면서도 markdown-first와 trust semantics를 유지하는 가장 안전한 확장 경로다.