Skip to content

feat(architecture): 근거 달린 IR로 그리는 아키텍처 그림 스킬과 ges_architecture 도구 - #53

Merged
tienne merged 154 commits into
mainfrom
feat-architecture-view
Oct 5, 2026
Merged

tienne merged 154 commits into
mainfrom
feat-architecture-view

Conversation

@tienne

@tienne tienne commented Oct 5, 2026

Copy link
Copy Markdown
Owner

Summary

세션이 코드와 맥락 소스를 탐색해 근거 달린 IR(중간 표현 JSON)을 쓰면 서버가 그 IR을 검증해 단일 HTML 그림으로 그리는 /architecture 스킬과 ges_architecture MCP 도구를 추가했습니다. 핵심 약속은 "실선은 코드나 스펙에서 확인한 연결"이라는 것이고 이걸 서버 검증으로 강제합니다. 근거를 못 찾은 연결은 그리지 않고 미해결 질문으로 돌립니다.

  • 서버는 LLM을 부르지 않습니다. 탐색과 IR 작성은 세션이 하고 서버는 스키마와 근거 검증, 결정적 렌더만 합니다.
  • 같은 입력이면 같은 바이트의 HTML이 나옵니다(elkjs randomSeed 고정, id 정렬, 외부 스크립트 없음).
  • 106개 파일, +29,767 / -4입니다. 기존 파일 수정은 CLAUDE.md, README, 레퍼런스 문서, 라우팅 표, MCP 서버 등록 정도이고 나머지는 새 파일입니다.

Changes

커밋이 154개라 큰 묶음 여덟 개로 나눠 적습니다. 묶음 경계는 커밋 메시지와 파일 목록 기준이라 일부 겹칩니다.

① 기본 골격

  • IR 스키마(src/architecture/ir-schema.ts, schemas/architecture-ir.schema.json)와 뷰 두 개: screen-chain(화면 → 엔드포인트 → 앱 모듈 → 외부 서비스, DB 테이블), deploy-path(트리거 → 빌드 → 산출물 → 배포 대상)
  • validator.ts가 근거 파일과 줄을 실제로 확인합니다. 근거 없는 실선은 SOLID_EDGE_WITHOUT_EVIDENCE 등으로 IR 전체를 거부합니다.
  • 재실행 병합: 노드 안정 키(kind, repo, 정규화한 label)로 이전 id와 사람이 단 답을 물려받습니다.
  • elkjs layered 레이아웃, 드릴다운 레벨(전체 → 서비스 → 기능영역 → 화면, 서버), 포커스(노드 하나와 위아래로 이어진 카드만 남기기), 주소 해시, 검색, 밝은 화면과 어두운 화면

② 서빙 인프라와 live 근거

  • domain, cdn, bucket, cloud_account 노드와 environment, account, platforms 필드. 서비스 카드에 웹, 웹(SSR), AOS, iOS 칩
  • 실제 클라우드 조회 결과는 live 근거(command, observedAt 필수)로 싣고 늘 점선입니다.
  • 읽기 전용 판정: 도구 이름은 filter_tools, 클라우드 CLI 명령은 classifyCliCommand(src/utils/read-only-tools.ts)
  • 계정 ID와 CDN 배포 ID는 노드 id에 못 넣습니다(CLOUD_ID_IN_ID).

③ 분석 합치기와 저장소

  • merge 액션: 따로 돌린 분석 IR 여럿을 git remote 기준으로 합칩니다. 충돌은 고르지 않고 merge:<필드>:<노드 id> 질문으로 남깁니다.
  • 합친 그림은 "같이 쓰는 카드" 띠와 제품 전용 띠로 나뉩니다.
  • datastore 노드(테이블의 parent). 전체보기는 prod 기준입니다.

④ 마이크로 프론트엔드

  • micro_app 노드와 loads 엣지(federation 설정의 remotes 줄). 앱마다 서빙 사슬을 따로 그립니다.

⑤ 도메인 흐름 (flows)

  • 행위자 가로줄, 정상 흐름과 옆 흐름, 상태 값 구간. 배치는 flow-layout.ts의 격자입니다. 단계 카드에서 기술 그림의 화면과 API로 건너갑니다.

⑥ 하네스와 MCP 레포

  • client, skill, agent kind. MCP 도구는 endpoint에 protocol: "mcp", mcpServer, actions를 답니다.
  • SKILL.md와 AGENT.md 줄은 skill, agent 노드와 거기서 나가는 엣지에서만 code 근거로 인정합니다(MD_CODE_EVIDENCE).

⑦ 어휘 팩과 질문별 그림

  • 어휘를 src/architecture/packs/의 팩 일곱 개(web-product, harness, generic, infra, data, process, knowledge)로 묶었습니다. packs를 안 적은 옛 IR은 web-product와 harness로 읽습니다.
  • 범용 component(displayKind, renderClass)로 처음 보는 개념도 그립니다.
  • 질문별 그림(projections): sequence, dataflow, compare. 지도의 노드와 엣지를 id로 가리켜 같은 HTML 안에 그립니다.

⑧ 지식 문서

  • scan_docs: 문서 레포의 근거 표시, 구멍 표시, 질문 안내 표, 화면 색인, 수정일을 결정적으로 뽑습니다. 형식은 입력 정규식으로 받습니다.
  • link_docs: 기술 그림에 describes 점선으로 문서를 엮고 빈 곳, 오래된 곳, 어긋난 곳, CODEOWNERS 담당을 냅니다.
  • stale_docs: 바뀐 파일로 손봐야 할 문서를 고릅니다.

흐름 변화 (AS-IS → TO-BE)

AS-IS
아키텍처 그림 요청 → 세션이 코드를 읽고 글로 설명하거나 임의 형식으로 그림 → 근거 확인 장치 없음 → 재실행하면 처음부터 다시

TO-BE
/architecture → start(이전 실행 요약, 맥락 소스 후보) → filter_tools(읽기 전용 도구만) → 코드와 맥락 소스 탐색 → match_endpoints(FE 호출과 BE 라우트 매칭) → 세션이 IR 작성 → validate → 미해결 질문을 사용자에게 묻고 답을 user 근거로 기록 → render(이전 실행과 병합, 좌표 계산, HTML 두 개와 IR 저장)

구분 AS-IS TO-BE
도구 아키텍처 그림 전용 도구 없음 ges_architecture 액션 10개(start, filter_tools, match_endpoints, validate, render, status, merge, scan_docs, link_docs, stale_docs), /architecture 스킬
실선의 의미 정해진 규칙 없음 code나 spec 근거가 있을 때만 실선. doc, user, live는 점선
근거 없는 연결 그냥 그려지거나 빠짐 그리지 않고 auto:* 미해결 질문으로 남김
재실행 매번 새로 그림 이전 IR과 병합해 id와 답을 유지
공유 해당 없음 .html은 본인용, .shared.html은 private 근거 인용과 계정 ID를 가린 공유용. 문서 노드의 제목과 경로는 공유본에도 보임
의존성 - elkjs ^0.12.0

Test plan

  • pnpm gate 통과 (typecheck, verify:rules, lint, format:check, build, test). 테스트 파일 251개, 테스트 4299개 통과, 1개 스킵
  • 기존 그림 유지: tests/unit/architecture/legacy-snapshot.test.ts가 서비스 구조, 흐름, 하네스, 배포 경로, 합친 그림 다섯 종의 private과 shared HTML을 SHA-256 해시로 비교합니다. 팩과 새 모양을 더한 뒤에도 바이트까지 같습니다.
  • 단위 테스트 tests/unit/architecture/ 32개 파일, tests/unit/mcp/architecture-passthrough.test.ts, 통합 테스트 tests/integration/architecture-e2e.test.ts. fixture는 가짜 레포(acme 등)만 씁니다.
  • 실제 레포 검증은 레포 밖에서 했습니다. 산출물과 수치는 이 레포에 넣지 않았습니다. gestalt 자신과 modelcontextprotocol/servers로 하네스 그림을 돌려봤습니다.
  • 커밋과 diff에 사내 이름, 로컬 경로가 없는지 grep으로 확인했습니다.

리뷰는 아래부터 보시면 좋을 것 같습니다.

  • src/architecture/validator.ts: 근거 규칙과 에러 코드. 실선에 근거가 있다는 약속이 여기서 지켜집니다.
  • src/mcp/tools/architecture-passthrough.ts: 액션 열 개의 입력을 검증하고 응답합니다.
  • src/architecture/store.ts: 저장할 때 private 인용을 제거하고 공유본을 가립니다.
  • src/utils/read-only-tools.ts: 읽기 전용 판정이 너무 느슨하거나 빡빡하지 않은지 봐주시면 좋겠습니다.

남긴 것도 적어둡니다.

  • 공유본 가리기에서 공개 코드 근거의 인용(excerpt)은 그대로 실립니다. 설정 문자열 같은 인용도 가릴지는 후속으로 봅니다.
  • 신선도 히트맵, 팀 색 토글, 기간 비교 그림은 데이터만 있고 그림은 아직 없습니다.

Related issues

없음

tienne added 30 commits October 5, 2026 11:24
도구 이름에 조회 동사가 들고 쓰기 동사가 없는 것만 맥락 소스 후보로 남긴다. ~/.claude/projects 메모리는 경로가 아니라 git remote로 묶어 워크트리와 다른 클론의 메모리까지 모은다.
노드와 엣지마다 code, spec, doc, user 근거를 달고 포함 관계(parent)와 표시 이름을 싣는다. zod 스키마와 같은 내용의 JSON Schema 파일을 함께 둔다.
근거 없는 실선과 잘못된 포함 관계는 거부하고, 근거 없는 노드와 연결은 미해결 질문으로 돌린다. 저장한 IR을 다음 실행의 출발점으로 써서 노드 id를 유지한다. FE와 BE 엔드포인트는 method와 정규화한 경로 템플릿으로 맞춘다.
서비스 노드가 있으면 전체, 서비스, 기능영역, 서버 레벨로 나누고 세부 연결을 묶음 엣지로 모은다. 레벨마다 레인을 배정하고, 포커스할 때 남길 노드와 다시 쌓을 좌표를 결정적으로 계산한다.
카드마다 종류 칩을 붙이고 레인을 레이어별로 나눈다. 상세보기, 두 항목 사이 경로, 포커스를 해시 경로로 다뤄 뒤로가기가 된다. 테마는 시스템을 따르고 토글로 바꾼다. 공유본은 private 근거의 링크와 인용을 뺀다.
start, filter_tools, match_endpoints, validate, render, status를 Passthrough로 제공한다. 서버는 LLM을 부르지 않고 스키마와 근거 검증, 결정적 렌더만 한다. 레이아웃용으로 elkjs 의존성을 추가했다.
세션이 코드와 맥락 소스를 읽기 전용 도구로 탐색해 근거 달린 IR을 쓰고 서버에 검증과 렌더를 맡기는 절차를 정한다. 게이트웨이 탐색 순서, 로컬 클론 받기, 표시 이름 출처를 담았다.
IR 구조, 근거와 선 모양, 검증 규칙, 공개 범위, 드릴다운과 포커스 조작을 문서로 정리했다.
기능영역은 앱 메뉴나 네비게이션 정의, 라우트 중첩, 실제로 씌워진 레이아웃 순으로 근거를 본다. 사용자가 답한 경계는 user 근거로 남겨 재실행 때 같은 질문을 다시 만들지 않는다.
classifyCliCommand가 셸 명령 한 줄을 allow, deny, ambiguous로 나눈다. aws 하위 명령은 list, get, describe로 시작할 때만 allow다. 비밀값이나 자격증명을 내주는 동작, 파일을 내려받는 get-object, 셸 메타문자로 명령을 이어 붙인 경우는 이름과 무관하게 deny다. aws-vault는 list만 허용하고 aws 밖의 CLI는 판정하지 않는다.
도메인, CDN, 버킷, 클라우드 계정 노드와 resolves_to, origin, serves 엣지를 IR에 넣었다. 인프라 노드는 environment와 account를 가지고, 서비스 노드는 platforms와 플랫폼별 근거를 가진다.

live 근거는 실행한 명령과 조회 시각이 필수다. validate가 명령을 읽기 전용 판정으로 다시 보고, private live 근거에 응답 원문이 들어 있으면 거부한다. live 근거만 있는 선은 점선이다.

계정 ID나 CDN 배포 ID가 든 id는 공유본에서도 가릴 수 없어 CLOUD_ID_IN_ID로 거부한다. 이전 실행 병합도 그런 id를 물려받지 않는다. 공유본은 live 근거를 조회 시각만 남기고 노드 이름과 질문의 계정 ID를 가린다. 근거 없는 Android, iOS 플랫폼은 미해결 질문으로 돌린다.
서비스 레벨에 서빙 인프라가 있으면 도메인, CDN, 버킷 레인을 기능 영역 앞에 둔다. 서비스 카드는 기능 영역 레인 맨 위에 서고 직속 기능영역과 화면으로 포함 선을 긋는다. 인프라 카드는 레인 안에서 prod, stage, qa, dev 순으로 놓인다.

deploy-path는 버킷, CDN, 도메인 순의 인프라 레인을 오른쪽에 붙인다. 요청 방향인 origin과 resolves_to는 오른쪽에서 왼쪽으로 그린다. 계정이 다른 두 노드를 잇는 선은 다른 색으로 그리고 범례에 싣는다.

서비스 카드에는 웹, AOS, iOS 칩과 prod 도메인을 싣는다. 아이콘은 회사 로고 대신 중립 모양을 쓴다. 상세 패널에 환경, 계정, 환경별 도메인, 플랫폼 근거, live 근거의 명령과 조회 시각을 보인다.
start 응답에 readOnlyCliRule을 실어 세션이 클라우드 조회 전에 쓸 수 있는 하위 명령 동사를 알게 했다. 지시문에 live 근거 작성법 한 줄을 더했다.
가짜 계정 ID와 example.com 도메인으로 만든 aws 응답 fixture와 가짜 인프라 코드를 쓴다. 스키마와 검증기, 공유본 가리기, 서비스 사실 계산, 레인 순서, 환경 순서, 역방향 선, 병합을 단위 테스트로 본다. e2e는 레인과 카드, 선 모양, 계정 넘는 선, 결정적 렌더, 읽기 전용이 아닌 명령 거부를 본다.
Step 4.5를 넣었다. 인프라 코드에서 먼저 찾고, 프로필과 만료를 읽고, 실제로 맡은 역할은 자격증명 파일의 역할 ARN 필드에서만 읽는다. 세션이 만료됐으면 로그인 명령을 만들어 보여주고 멈춘다. 조회는 list, get, describe 하위 명령만 쓰고 코드와 실제가 다르면 미해결 질문으로 돌린다. 도구별 로그인 명령 예시는 가짜 값으로 적었다.
새 노드와 엣지 종류, 인프라 필드, live 근거와 선 모양, 새 에러 코드, 공유본 가리기, 서비스 레벨과 deploy-path 레인, 플랫폼 칩, CLI 명령 판정을 문서에 맞췄다.
레포마다 따로 돌린 아키텍처 IR을 하나로 합치는 mergeArchitectureIrs를 추가했다.
레포는 별칭이 아니라 git remote를 정규화해 알아보고, 노드는 nodeKey로 알아본다.
같은 노드는 근거를 합집합으로 남기고 id가 부딪히면 결정적으로 다시 매긴다.

표시 이름이나 parent처럼 값이 갈리는 필드는 한쪽을 고르지 않고 비운 뒤 미해결
질문으로 돌린다. user 근거가 있는 쪽 값이 하나뿐이면 그 값을 그대로 둔다.
핸들러를 못 찾은 엔드포인트는 다른 분석의 라우트와 method, 경로로 맞춰 레포를
넘는 handles 연결을 단다. 입력 순서와 상관없이 같은 바이트가 나오도록 입력을
해시 순으로 정렬하고 끝에서 스키마를 다시 통과시킨다.
ges_architecture에 merge 액션을 추가했다. irs나 irPaths로 IR을 받아 합치고
outPath가 있으면 파일로 쓴 뒤 경로와 보고를 돌려준다. 합친 IR은 크기가 커서
validate와 render도 ir 대신 irPath로 받을 수 있게 했다.
겹치는 레포 둘, 안 겹치는 레포 둘, 값이 갈리는 경우를 가짜 레포로 검증한다.
레포를 넘는 handles 연결과 결정성, private 근거가 공유 HTML에 안 새는지도 본다.
architecture-view 문서에 merge 액션 파라미터와 응답, 분석 합치기 규칙을 적었다.
CLAUDE.md의 액션 목록과 모듈 설명도 함께 고쳤다.
두 제품을 따로 분석한 뒤 merge로 합치고 렌더하는 절차와 보고할 내용을 스킬에 넣었다.
DB 클러스터와 캐시 클러스터를 datastore 노드로 두고 db_table의 parent로 삼게 했다. 테이블 단위로만 있던 DB를 클러스터 단위로 묶어 전체 그림에 올리기 위해서다.

합친 IR에 입력마다 groups를 붙인다. members는 그 입력에 있던 노드를 합친 뒤의 id다. 엣지를 따라 소속을 정하면 같이 쓰는 게이트웨이의 routes를 타고 한 제품이 다른 제품 영역까지 삼켜서 입력에 있었는지로만 정했다. 이미 합친 IR을 다시 합치면 그 그룹을 물려받고, 재실행 병합에서는 id를 따라 바꾼다. 검증에 DUPLICATE_GROUP_ID와 GROUP_MEMBER_NOT_FOUND를 더했다.
그룹이 둘인 그림은 레인을 그대로 두고 열마다 첫째 전용, 같이 쓰는 영역, 둘째 전용 순으로 카드를 다시 쌓는다. 두 제품 영역을 색 박스로 감싸고 겹치는 가운데 띠에 점선 테두리와 라벨을 단다. 띠 높이는 모든 열에서 같게 맞춰 가로 한 줄이 같은 영역으로 읽히게 했다. 한쪽 전용 카드가 없는 레벨은 박스가 공용 띠와 겹쳐 라벨이 부딪혀서 띠를 나누지 않는다. 포커스도 같은 규칙으로 다시 쌓는다.

전체보기는 prod 기준으로 묶는다. environment가 있고 prod가 아닌 노드는 빼고, 서버에서 저장소로 가는 묶음 선을 올린다. 테이블은 서버 레벨에만 남긴다.
합친 IR의 제품 그룹 이름을 호출하는 쪽이 정할 수 있게 했다. 안 주면 입력의 서비스 이름을 쓴다.
합치기의 그룹 생성과 물려받기, 그룹 검증, 띠 쌓기, 전체보기 prod 걸러내기와 저장소 묶음, 포커스 띠, 영역 렌더를 검증한다.
datastore kind와 테이블의 parent, 전체보기 prod 기준, 합친 그림의 띠 레이아웃, merge의 groupNames를 문서에 더했다.
저장소 클러스터를 찾아 테이블의 parent로 다는 절차와 id 별칭 규칙, 합칠 때 제품 이름을 넘기는 법을 적었다.
플랫폼 칩이 이름 뒤 첫 줄에 붙어 있어서 칩이 셋이고 추정 표시까지 붙으면 카드 최대 폭에 걸려 이름이 먼저 잘렸다. 칩을 도메인이나 기술 이름이 있는 둘째 줄 끝으로 옮겨 이름이 끝까지 보이게 했다. 칩만 있는 카드도 두 줄 높이로 잰다.
칩이 이름 줄에 없고 도메인 줄에 붙는지, 칩만 있어도 두 줄 높이인지, 둘째 줄이 짧으면 칩이 폭을 늘리지 않는지 검증한다.
배포 대상(deploy_target)도 serves로 서비스를 서빙할 수 있게 했다. CDN의 origin과 도메인의 resolves_to도 배포 대상을 가리킬 수 있다.
prod 서빙 노드에 배포 대상이 있으면 SSR, 버킷만 있으면 정적으로 판정한다. 서비스 카드의 웹 칩은 "웹(정적)"과 "웹(SSR)"으로 나뉘고 패널에는 서빙 서버 목록이 나온다.
서비스 레벨 인프라 레인에도 배포 대상이 버킷 자리에 선다.
tienne added 28 commits October 5, 2026 11:24
MCP 도구를 받는 모듈을 핸들러로, 핸들러에서 uses로 닿는 모듈을 엔진으로 보고 엔진을 핸들러 오른쪽 열에 따로 세웠다. 외부 서비스와 저장소 열은 한 칸씩 뒤로 밀린다.

elk는 같은 partition 안에서 나가는 선이 없는 카드를 뒤 층으로 민다. 엔진을 안 부르는 핸들러가 그래서 엔진 열에 서 있었다.
묶음 선 모양 규칙을 코드 근거 우선으로 고치고 섞인 묶음 표시를 적었다. 옆 흐름 구간을 끼우는 조건과 tree 드릴다운 바깥 카드 열 규칙을 더했다. match_endpoints 절에 nameRefs 파라미터와 refs 응답, matcher별 비교 규칙을 넣었다.
loads를 app_module에서 agent로도 그을 수 있게 했다. 서버 레벨에서 고른 모듈이 loads로 읽어 들이는 에이전트를 받는 쪽 열에 세운다.

어떤 스킬도 띄우지 않는 에이전트는 서비스 레벨과 기능영역 레벨에 안 나와서 드릴다운 어디에서도 안 보였다. 스킬에서 에이전트로 가는 loads는 spawns 자리라 계속 거부한다.
infra, data, process, generic 팩에서 레포의 참조와 선언을 모아 match_endpoints의 nameRefs로 넘기는 단계를 더했다. 팩마다 무엇을 모으면 되는지와 예시 하나, 결과 처리 규칙을 적었다. 묶음 레벨 설명에 바깥 카드 열 넘김을 한 구절 붙였다.
하네스 IR에서 레지스트리 모듈이 에이전트를 loads로 읽어 들이게 긋는 법과 그 에이전트가 서는 서버 레벨 자리를 적었다. 전체 레벨에서 엔진이 핸들러 오른쪽 열에 따로 서는 이유도 적었다.
feat-architecture-categories의 어휘 팩, 질문별 투영 그림, 약점 수정 넷을 하네스 브랜치에 합쳤다.

충돌은 오류 코드 표 두 곳에서만 났다. INVALID_LOADS_ENDS 행은 app_module에서 agent로 가는 loads를 받는 이쪽 설명을, INVALID_HARNESS_EDGE_ENDS 행은 다른 팩 엣지 규칙을 덧붙인 저쪽 설명을 살렸다.
문서가 적은 파일 경로와 화면 색인의 라우트를 기술 노드에 잇도록 name-ref matcher 둘을 더했다.
doc-path는 앞의 ./와 줄 번호, 앵커, 브랜치 표기를 떼고 같은 파일로 본다.
screen-route는 엔드포인트 경로처럼 경로 변수 표기(:id, [id], {id})를 같은 꼴로 편다.
문서 묶음, 문서, 화면 문서를 tree 드릴다운으로 그리는 knowledge 팩을 더했다.
노드에는 doc(경로, 수정일, 열린 구멍, 근거 구성, 링크 상태, 질문 안내 표, 화면 정보)과 owners를, describes 엣지에는 docLink를 싣는다.
describes는 문서 쪽에서만 나가고 늘 점선이어야 해서 검증기가 따로 본다.
공유본은 제목과 경로만 남기고 본문 글자와 담당, 연결 대상 경로를 가린다.
카드 배지와 서랍의 문서 정보, 스타일은 지식 팩을 쓰는 그림에만 실어 기존 그림의 바이트가 그대로다.
문서 레포의 md를 LLM 없이 훑어 근거 표시, 빈 곳과 확인 안 된 사실 표시, 최종 수정 머리줄, 질문 안내 표, 화면 색인 표를 뽑는다.
근거 종류는 code, wiki, ticket 같은 묶음으로 접어 레포 이름이 막대에 그대로 실리지 않게 했다.
레포마다 표시 형식이 달라 정규식을 docPatterns로 바꿀 수 있다.
결과와 지식 문서 지도 초안 IR은 .gestalt/architecture/ 아래 파일로 남기고 응답에는 요약과 경로만 싣는다.
초안은 레포 묶음을 맨 위에 두고 폴더를 따라 tree로 내려가며, 질문 안내 표와 화면 색인을 indexes 선으로 잇는다.
종류 없이 레포/경로:줄로 적은 근거와 줄 범위, 앵커가 붙은 경로를 코드 근거로 읽는다.
자리표시(<repo>/<path>)는 버린다. 설명 없는 [GAP]은 그 줄 나머지를 설명으로 쓴다.
화면 색인은 프레임이나 타입 열이 있는 표만 본다. 빈 칸 표기(—, -)는 값이 없는 것으로 친다.
심링크 폴더로 같은 파일이 두 번 잡히면 숨김 폴더 밖의 짧은 경로 하나만 남긴다.
문서 본문과 코드 블록에서 `GET /path` 꼴의 API 언급을 줄 번호와 함께 뽑는다. 문서마다 같은 언급은 한 번만 남기고 요약에 개수를 단다. 문서가 말한 API가 기술 그림에 있는지 맞춰보는 데 쓴다.
scan_docs 결과와 기술 IR을 받아 지식과 아키텍처 그림(knowledge-link)을 만든다. 문서가 가리킨 파일이 같은 노드, 그 파일을 품은 패키지 루트 모듈, 폴더 아래 모듈, 같은 API, 화면 색인의 라우트 순서로 잇고 선은 점선으로 그린다. 라우트는 생략할 수 있는 끝 파라미터와 색인에 값을 박아 적은 자리도 같은 화면으로 본다.

codeRoots를 주면 가리킨 파일이 있는지와 마지막 커밋 날짜를 git으로 확인한다. 문서 수정일보다 코드가 늦게 바뀌었으면 낡은 문서로 표시하고 링크 상태를 다시 세어 문서 지도에도 돌려 쓴다. 신호(덮인 비율, 낡음, 어긋난 API, 한쪽에만 있는 화면)는 doc-link.json에 남긴다.

문서 노드는 카드로 그리지 않는다. 기술 카드에 "문서 N", "문서 없음", "낡음 N" 배지를 달고 서랍에 설명하는 문서 목록을 보인다. 머리줄에는 문서가 붙은 비율을 단다. knowledge 팩이 없는 그림은 바이트가 그대로다.
구멍 설명에 `진술 @2026-08-11`처럼 날짜 앞에도 @를 붙여 쓴 문서가 있어 날짜가 담당으로 잡혔다. @핸들은 글자로 시작할 때만 담당으로 받는다.
link_docs가 문서 레포와 코드 레포의 CODEOWNERS를 읽어 문서와 기술 노드의 owners를 채운다. 규칙은 GitHub 문법대로 뒤에 나온 것이 이기고, 담당이 빈 규칙은 담당을 지운다. 레포 전체에 거는 `*` 규칙에만 걸린 자리는 기본 담당으로 따로 센다. 근거 파일이 여럿이면 실제 담당이 있는 쪽만 모은다.

신호에는 담당이 있는 비율과 담당 없는 기술 노드 목록을 싣는다. 담당자별 구멍 수는 그림에 이어졌는지와 상관없이 문서 전체에서 세고, 구멍에 담당이 안 적혀 있으면 문서의 CODEOWNERS 담당으로 센다. 문서 레포 위치는 scan_docs가 doc-scan.json에 남긴다.

담당이 실린 그림에서만 문서가 붙어야 할 기술 카드에 "담당 없음" 배지를 단다. owners는 공유본에서 빠지므로 공유본에는 이 배지가 없다.
scan_docs가 README, CLAUDE, AGENTS, SKILL, KNOWLEDGE_INDEX 같은 진입 문서에서 링크와 질문 안내 표를 따라가 어느 문서에 닿는지 센다. 진입 문서가 아닌 INDEX는 들어오는 링크가 없을 때만 진입점으로 본다. 결과는 doc-routes.json에 남기고 응답 요약에는 고립 문서, 막다른 안내, 같은 말이 다른 문서로 가는 자리의 수를 싣는다.

링크 문법 없이 백틱이나 표 칸에 적은 md 경로도 길로 센다. 이런 경로는 어디를 기준으로 적었는지 모르니 문서 폴더부터 위 폴더로 한 단씩 올라가며 붙여 보고, 그래도 없으면 같은 레포에서 뒷부분이 맞는 문서가 하나뿐일 때만 그 문서로 본다. 질문 안내 표도 표에 적힌 링크를 남겨 같은 방식으로 풀기 때문에 옆 스킬 기준으로 적은 안내가 더는 끊긴 것으로 잡히지 않는다.

지식 지도 초안의 문서 노드에는 고립 여부와 안내 표가 그 문서로 보내는 말을 싣는다. 고립 문서 카드에 "고립" 배지를 달고, 지식 팩 그림에서는 찾는 말도 검색에 걸린다. 찾는 말은 본문에서 온 글자라 공유본에서 뺀다.
PR이나 브랜치에서 바뀐 파일을 받아 손봐야 할 문서를 고른다. 문서가 근거로 직접 가리킨 파일이 바뀌었거나, 근거가 가리킨 폴더 아래 파일이 바뀐 문서를 먼저 잡는다. link_docs를 돌려 둔 상태면 문서가 설명하는 기술 노드의 근거 파일이 바뀐 문서도 함께 잡는다. 파일 경로 대신 API나 화면 라우트로만 이어진 문서가 여기서 걸린다.

바뀐 파일은 `<레포 이름>:<경로>` 목록으로 받거나 diffBase와 changedRepo를 주면 codeRoots의 체크아웃에서 git diff로 찾는다. 큰 레포는 몇백 커밋만 넘어가도 파일 목록이 git 출력 기본 버퍼를 넘어서 버퍼를 키워 둔다. 결과는 stale-docs.json에 남기고 응답에는 수와 앞쪽 문서 몇 개만 싣는다.
검색이 지금 보이는 레벨의 카드만 보다 보니 지식 지도 전체보기에서는 묶음 카드 두세 개만 비교해 찾는 말이 하나도 걸리지 않았다.
문서 그림일 때만 묶음 카드가 들어갈 레벨의 문서 이름과 찾는 말을 모아 품게 했다. 레벨을 내려갈 때마다 검색을 다시 돌리므로 검색어가 든 묶음을 따라 문서까지 길이 켜진다.
기술 그림은 이 코드를 싣지 않아 legacy 바이트는 그대로다.
머리줄에 수정일을 안 적는 문서가 많아 신선도를 머리줄만으로 볼 수 없었다. scan_docs가 문서 루트마다 git log를 한 번 돌려 파일별 마지막 커밋 날짜를 doc.committedAt에 싣는다. 한글 파일 이름이 이스케이프되지 않게 quotepath를 끄고, blob 없이 받은 레포에서 이름 바꾸기 감지가 blob을 하나씩 받아오지 않게 --no-renames를 준다.

마지막 손댄 날은 머리줄 수정일과 커밋 날짜 중 늦은 쪽이다. 그림을 만든 날보다 180일 넘게 앞서면 doc.aged를 달고 카드에 오래됨 배지를, 서랍에 신선도 줄을 보인다. scan_docs 요약에는 달별 문서 수와 오래된 순 목록, 머리줄이 커밋보다 30일 넘게 뒤처진 문서 수를 싣는다.
ges_architecture에 들어간 지식 문서 액션 세 개를 도구 레퍼런스에 적었다. 액션마다 파라미터와 쓰는 파일, 응답 요약, 카드 배지를 두고 knowledge 팩 어휘와 지식 문서 공유본에서 남기는 것과 가리는 것을 더했다. CLAUDE.md의 action 목록도 맞췄다.
문서 레포를 알아보는 신호와 scan_docs, link_docs, render로 이어지는 순서, 바뀐 코드로 손볼 문서를 찾는 stale_docs 쓰는 때를 스킬에 더했다. 세션이 IR을 손으로 쓰지 않는 흐름이라 분석 합치기처럼 별도 절로 두었다.
질문별 그림이 몇 개인지 위쪽 바 단추를 눌러야만 보였다. 투영이 있으면 전체 레벨 지도 위에 그림마다 카드를 한 줄로 깔고, 카드에 모양과 제목, 질문, 단계 수, 참여 수를 적는다. 누르면 그 그림 레벨로 간다.

카드 줄은 섹션 위 여백에 두고 섹션을 그만큼 내린다. 화면 맞추기가 줄까지 넣도록 data-w와 data-h를 늘려 적는다. 섹션이 내려간 만큼 카드 중심 계산이 어긋나서 투영 전용 스크립트에서 섹션 위치를 더하는 쪽으로 다시 선언한다. 투영이 없는 그림은 스타일과 스크립트 모두 그대로라 바이트가 바뀌지 않는다.
들어가기 절에 전체 지도 위 카드 줄이 무엇을 보여주고 어디로 가는지 적었다. 하네스 레포 절에는 호출 순서를 진입 경로마다 순서도로 보여준다고 적었다.
하네스 흐름을 flows로 그리고 시퀀스 다이어그램은 따로 안 그린다던 안내를 뒤집었다. 지도와 stages, 서비스 없는 흐름으로는 호출 순서가 안 읽혀서, 진입 경로마다 sequence 질문별 그림 하나를 얹는 걸 기본으로 둔다. flows는 사람 쪽 업무 절차가 따로 있을 때만 쓴다.
레포 여럿을 합친 하네스 순서도에서 스킬이 어느 레포에 있는지 안 보였다. repos가 둘 이상이면 머리 카드 둘째 줄에 노드의 레포 이름을 적고, MCP 도구는 레포 대신 어느 서버의 도구인지 적는다. 둘째 줄이 생기는 만큼 순서도 배치가 카드 높이를 그에 맞춰 잰다. 레포가 하나인 그림은 그대로다.
sequence 모양 설명에 레포가 여럿일 때 머리 카드 둘째 줄에 무엇이 적히는지 더했다.
단계가 많은 순서도를 확대해 내려가면 머리 카드가 화면 밖으로 나가 이 선이 누구 건지 다시 올려봐야 했다. 확대와 이동이 viewport transform 하나라 CSS sticky가 안 먹어서, viewport 스타일이 바뀔 때마다 머리 카드를 화면 위까지 내리고 그림 끝에서 멈춘다. 머리 카드 뒤에는 바탕색 띠를 깔아 그 밑을 지나는 선과 글자를 가린다. 투영이 있는 그림에만 실린다.
sequence 모양 설명에 스크롤해도 머리 카드가 화면 위에 붙어 따라오는 동작과 그 이유를 더했다.
레포가 여럿이면 순서도 머리 카드에 노드의 레포 이름이 찍힌다. participants에 넣은 노드의 repo가 실제 레포와 맞는지 한 번 더 보고, MCP 도구에는 mcpServer를 빠뜨리지 말라고 적었다. 레포 위치를 지도만 보여준다고 읽히던 문장도 플러그인 위치로 좁혔다.
@tienne tienne self-assigned this Oct 5, 2026
@tienne
tienne merged commit e5025a1 into main Oct 5, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant