# SSOT에서 글감을 뽑는 기준 `final/document.md` 하나가 정본(SSOT)이다. 거기서 Studio에 올릴 기록을 뽑아낸다. 이 문서는 **무엇을 몇 건으로 나눌지**를 정하는 기준이다. 나눈 결과는 `tech-log-tree.json` 하나에 적는다 — 분해 계약과 색인이 같은 파일이라 둘이 어긋날 자리가 없다. ## 어느 파일에서 뽑나 | 층 | 하는 일 | |---|---| | 코드·설정·실행 증거 | 사실의 근거 | | `final/document.md` | **글감 범위의 SSOT** — 후보를 발견하는 유일한 입력 | | `final/assets/` · `final/.techviz/` | 이미 그린 그림과 그 정본. 새 후보를 내지 않고 글감에 배정된다 | | `final/evidence/` | 이미 실행한 측정의 원문. 마찬가지로 배정된다 | | `analysis/**/*.md` | final이 이미 채택한 주장을 상세히 확인하는 보조 근거 | | `tech-log-tree.json` | 사람이 고른 글감. 분해 계약이자 색인이고 이 파일이 정본이다 | **`analysis/**`를 글감을 찾으려고 열지 않는다.** 이미 final에 있는 주장의 세부를 확인할 때만 연다. 분석에만 있고 final에는 없는 자료를 발견하면 트리에 바로 넣지 말고 `final/document.md`를 먼저 보강한다. 그러지 않으면 모듈 문서 61편이 각각 정본 노릇을 하고, 트리는 그 절 수의 합만큼 자란다. ## 후보를 찾는 범위 접어 넣은 `final/document.md`가 전부 후보 자리는 아니다. **제1부(통합 분석)가 후보를 찾는 범위**이고, 제2부(모듈 분석 전문)와 제3부(분석 재료)는 근거다. 제2부의 절 제목을 후보로 읽으면 모듈 분석 편수만큼 글감이 늘어난다 — 접어 넣기 전에 있던 문제가 그대로 돌아온다. 범위는 기억하지 말고 계약에 적는다. ```json "candidateScope": { "document": "final/document.md", "sections": ["§3", "§4", "§5", "§6", "§7", "§8", "§9", "§10", "§11"], "excluded": ["제2부 — 모듈 분석 전문", "제3부 — 분석 재료"] } ``` 범위 밖의 앵커는 후보가 아니라 근거다. 제1부에서 나온 글감의 `source`로 건다. ## 그림과 증거는 후보가 아니라 배정 대상이다 `final/assets/`의 그림과 `final/evidence/`의 측정은 글감을 새로 만들지 않는다. 이미 정해진 글감에 붙는다. 그래서 처분을 매기는 자리가 아니라 **배정하는 자리**이고, 계약의 `ssot-assets`·`ssot-evidence`가 그 자리다. 글감을 다 고른 뒤 두 폴더를 한 번 훑는다. 물음은 하나다. > **이 그림이나 이 측정은 어느 글감의 것인가. 붙을 글감이 없으면 왜 없는가.** ```json "ssot-assets": ["ap3-bff-session-flow"], "ssot-evidence": ["raw/explain/l3-cartesian-join-plan.txt"] ``` 배정한 것은 기록의 `assets`·`evidence`가 실제로 가리켜야 한다. 배정해 놓고 쓰지 않으면 `verify-tech-log-tree.py`가 error로 센다. **배정하지 않으면 글을 쓸 때 같은 그림을 새로 그린다.** keycloak이 그렇게 됐다. SSOT에 `ap3-bff-session-flow`, `ap4-edge-forward-auth-flow`를 포함한 그림 13장이 `.techviz` 정본까지 갖춘 채 있었는데, 기록 24편은 그중 한 장도 가리키지 않고 이름이 다른 그림 5장을 새로 만들어 썼다. 새로 만든 5장에는 정본이 없어서 고칠 수도 없다. 붙을 글감이 없는 그림도 있다. 패턴 넷을 나란히 놓고 비교하는 그림은 Reference에 붙어야 맞는데 Reference에는 본문이 없다. 그런 그림은 그대로 두고, 왜 두는지 계약에 적는다 — 「Reference에만 쓸 자리가 있어 본문 있는 종류에 담지 못한다」처럼. `verify-project-layout.py`가 「기록이 쓰지 않는 SSOT 그림」으로 세므로, 센 숫자가 설명되지 않은 채 남지 않게 한다. 증거도 같다. 재료로만 쓰고 인용하지 않기로 한 측정은 정상이다. 「기록이 인용하지 않는 raw 증거」가 전부 설명되는지만 본다. ## 왜 먼저 나누는가 긴 글을 앞에서부터 잘라 기록으로 만들면 절 하나가 기록 하나가 된다. 그러면 Case의 칸도 Reference의 칸도 반쯤만 맞는 기록이 나온다. 종류를 먼저 정하고 그 종류가 요구하는 것이 SSOT에 있는지 확인해야 한다. ## 고르는 것이지 남김없이 내는 것이 아니다 분석에 빠진 것이 없는지 볼 때는 recall 100%가 맞다. 공개할 글을 정할 때는 아니다. 「분석에서 보존할 가치」와 「독립된 글로 읽을 가치」는 다른 물음이고, 둘을 한 축으로 재면 분석 부산물이 전부 글이 된다. 물음은 하나다. > **이 기록을 없애고 관련 Case나 Concept의 한 절로 넣어도 이해·결정·재사용성이 그대로라면 > 독립 기록으로 만들지 않는다.** 후보마다 처분을 적는다 — `PROMOTE` · `MERGE_INTO` · `KEEP_IN_SSOT` · `NEEDS_EVIDENCE` · `NEEDS_DECISION` · `BLOCKED`. `KEEP_IN_SSOT`은 버린 것이 아니라 분석에 남기고 글로 만들지 않기로 한 것이고, 그것도 정상적인 결과다. 자세한 것은 `.agents/skills/deriving-tech-log-root-tree/references/candidate-disposition.md`. ## 한 건으로 자르는 단위 **절이 아니라 주장이다.** SSOT의 `##` 하나가 기록 하나가 아니다. 다음 넷 중 하나가 한 건이다. | 단위 | 무엇 | 종류 | |---|---|---| | 재현한 관측 하나 | 조건을 갖추고 돌려서 얻은 수치·실행계획·로그 | Case | | 남의 것의 동작 하나 | 문서와 코드를 읽어 정리한 규칙·흐름 | Concept | | 반복 적용할 기준 하나 | 다음에도 같게 하기로 한 규칙 | Reference | | 프로젝트가 고른 방향 하나 | 대안을 두고 정한 것 | Decision | | 닫히지 않은 판단 하나 | 아직 모르는 것과 무엇을 하면 닫히는지 | Question | ## 종류를 정하는 물음 순서대로 묻는다. 먼저 걸리는 것이 그 글감의 종류다. 1. **내가 돌려서 얻은 결과인가** → Case. 수치·실행계획·로그가 SSOT에 있어야 한다. 없으면 Case가 아니다 2. **남의 것이 어떻게 동작하는지인가** → Concept. 무엇을 보고 썼는지(버전)가 있어야 한다 3. **다음에도 같게 하기로 한 규칙인가** → Reference 4. **대안을 두고 고른 것인가** → Decision. 근거로 걸 기록이 최소 하나 필요하다 5. **아직 모르는 것인가** → Question. 미지수가 최소 하나 필요하다 ## 나눌 때 지키는 것 **한 건에 종류를 섞지 않는다.** N+1을 재현해 고쳤고 그 과정에서 조회 기준을 굳혔다면 Case 하나와 Reference 하나로 나누고 `관계`로 잇는다. **증거가 없는 Case는 만들지 않는다.** SSOT에 그 수치가 없으면 글감 목록에는 남기되 `file` 없이 두고, 측정을 먼저 한다. 없는 수치를 쓰지 않는다. **Decision은 근거 없이 만들지 않는다.** 게시가 거절된다. 근거로 걸 Case나 Concept이 먼저 있어야 한다. 그래서 Decision은 대개 마지막에 뽑는다. **같은 관측을 두 건으로 쪼개지 않는다.** 「N+1이 났다」와 「그래서 몇 개가 나갔다」는 한 건이다. 쪼개면 둘 다 반쪽이 된다. **Concept은 거꾸로 뽑는다.** Case·Reference·Decision·Question을 먼저 고르고, 그것을 읽는 사람이 미리 알아야 하는 구조가 있을 때만 Concept을 만든다. 메커니즘처럼 보이는 절을 훑어 채우면 어느 기록도 필요로 하지 않는 개념이 쌓인다. **주제 하나에 독자 질문 하나.** Studio는 주제 아래에 다섯 종류를 나눠 보여 준다. 그 주제의 기록들이 함께 답하는 물음을 한 줄로 적고, 그 물음에 답하지 않는 글감은 다른 주제로 옮긴다. 주제 slug는 Studio의 것을 그대로 쓴다. ## `tech-log-tree.json` **주제·글감·`readiness`·`source`·`classification`·`relations`는 사람이 적는다.** 스크립트가 채우는 것은 기록 파일에서 읽을 수 있는 넷뿐이다 — `file`·`publication`·`status`·`studioId`. 주제 → 종류 → 글감 순서로 담고, 아직 쓰지 않은 글감은 `file` 없이 남는다. `readiness`는 증거가 갖춰진 정도이고 `publication`은 Studio에 올렸는지다 — 둘은 다른 것이라 섞지 않는다. ```json { "project": "n+1liner", "ssot": "final/document.md", "topics": { "jpa-feed-query-performance": { "topic": "jpa-feed-query-performance", "kinds": { "case": [ { "title": "Fetch 타입이 아니라 조회 방식이 만든 ToOne N+1", "slug": "eager-toone-nplus1-without-access", "file": "jpa-feed-query-performance/case/case-eager-toone-nplus1-without-access.md", "status": "게시 전", "studioId": "32d0be7d-…", "assets": 1, "evidence": 3 }, { "title": "아직 쓰지 않은 글감" } ], "concept": [], "reference": [], "question": [], "decision": [] } } } } ``` 기록을 쓰거나 지운 뒤에는 파생 칸을 다시 채운다. 사람이 적은 칸은 그대로 남는다. 디렉터리를 훑어 주제를 만들지 않으므로, 계약에서 뺀 주제의 폴더가 남아 있어도 되살아나지 않고 `unlisted`에 적힌다. ```bash python3 scripts/build-tech-log-tree.py [프로젝트] python3 scripts/verify-tech-log-tree.py [프로젝트] # error 0 이어야 한다 ``` ## 순서 1. SSOT를 끝까지 읽는다. 절 제목만 훑지 않는다 — 수치와 근거가 어디 있는지 알아야 종류를 정한다 2. §3~§8에서 Case, §9에서 Reference, §10에서 Decision, §11에서 Question을 고른다 3. 그 넷을 이해하는 데 필요한 Concept만 거꾸로 더한다 4. 후보마다 처분을 적고, 사람이 다시 읽은 것만 `dispositionReview: CONFIRMED`로 둔다. `PROMOTE`이면서 `CONFIRMED`인 것만 글감이 된다 5. 주제를 묶고 주제마다 독자 질문을 한 줄 적는다 6. 글감 하나를 골라 `<주제>/<종류>/`에 기록을 쓴다. 형식은 `writing-each-kind.md` 7. 색인을 다시 만들고 검사기를 돌린다 8. Studio에 넣고 저장한다