리뷰 두 건을 반영했다. 계약 - tech-log-tree.json 하나가 분해 계약이자 색인이다. 사람이 읽는 트리·Node Specification· 후보 대장은 없어졌고, 문서에 남아 있던 그 개념을 걷어냈다 - candidateScope — 후보를 찾는 SSOT 범위. 접어 넣은 제2부·제3부는 근거이지 후보가 아니다 - sourceRepository — 분석한 저장소의 경로·리비전·판단 근거. 리비전을 모르면 null 로 두고 지어내지 않는다. 갈래가 여럿이면 revisions - 검사기: 계약 미채택·PENDING·PROMOTE↔글감 양방향·candidateScope·sourceRepository 를 error/warn 으로 센다. 옛 스키마도 검사를 피하지 못한다. 테스트 22 → 31 기록 쓰기 - 템플릿 5종에 source·sourceRevision·topicName, Question 에 닫는 조건, 본문 없는 종류에서 assets 제거. 고정 절 개수 삭제 - check_evidence.mjs — 인용한 코드가 SSOT 에 있는지, 앵커가 SSOT 를 가리키는지, 제목이 계약과 같은지, 리비전이 저장소에 있는지. 게시된 기록에서 SSOT 와 다른 URL 을 잡았다 문체 - 문체 규칙의 정본을 ai-tells.md 로. explaining.md 의 질문체 제목·절 끝 대조 반복·그림 예고 규칙을 삭제해 충돌을 없앴다. 첫 절 「설명 뒤에 평가를 붙이지 않는다」에 지우는 사례 네 유형 - voice 스킬의 「독자 쪽을 본다」를 자료에 오독 기록이 있을 때로 좁히고, 평가만 더한 예시를 교체 - check_prose: 안내 문장을 요구하던 경고 제거, 문장이 끝나지 않은 채 문단이 끝나는 조각 검사 추가 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
8.0 KiB
SSOT에서 글감을 뽑는 기준
final/document.md 하나가 정본(SSOT)이다. 거기서 Studio에 올릴 기록을 뽑아낸다. 이 문서는
무엇을 몇 건으로 나눌지를 정하는 기준이다. 나눈 결과는 tech-log-tree.json 하나에 적는다 —
분해 계약과 색인이 같은 파일이라 둘이 어긋날 자리가 없다.
어느 파일에서 뽑나
| 층 | 하는 일 |
|---|---|
| 코드·설정·실행 증거 | 사실의 근거 |
final/document.md |
글감 범위의 SSOT — 후보를 발견하는 유일한 입력 |
analysis/**/*.md |
final이 이미 채택한 주장을 상세히 확인하는 보조 근거 |
tech-log-tree.json |
사람이 고른 글감. 분해 계약이자 색인이고 이 파일이 정본이다 |
analysis/**를 글감을 찾으려고 열지 않는다. 이미 final에 있는 주장의 세부를 확인할 때만
연다. 분석에만 있고 final에는 없는 자료를 발견하면 트리에 바로 넣지 말고 final/document.md를
먼저 보강한다. 그러지 않으면 모듈 문서 61편이 각각 정본 노릇을 하고, 트리는 그 절 수의 합만큼
자란다.
후보를 찾는 범위
접어 넣은 final/document.md가 전부 후보 자리는 아니다. 제1부(통합 분석)가 후보를 찾는
범위이고, 제2부(모듈 분석 전문)와 제3부(분석 재료)는 근거다. 제2부의 절 제목을 후보로 읽으면
모듈 분석 편수만큼 글감이 늘어난다 — 접어 넣기 전에 있던 문제가 그대로 돌아온다.
범위는 기억하지 말고 계약에 적는다.
"candidateScope": {
"document": "final/document.md",
"sections": ["§3", "§4", "§5", "§6", "§7", "§8", "§9", "§10", "§11"],
"excluded": ["제2부 — 모듈 분석 전문", "제3부 — 분석 재료"]
}
범위 밖의 앵커는 후보가 아니라 근거다. 제1부에서 나온 글감의 source로 건다.
왜 먼저 나누는가
긴 글을 앞에서부터 잘라 기록으로 만들면 절 하나가 기록 하나가 된다. 그러면 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 |
종류를 정하는 물음
순서대로 묻는다. 먼저 걸리는 것이 그 글감의 종류다.
- 내가 돌려서 얻은 결과인가 → Case. 수치·실행계획·로그가 SSOT에 있어야 한다. 없으면 Case가 아니다
- 남의 것이 어떻게 동작하는지인가 → Concept. 무엇을 보고 썼는지(버전)가 있어야 한다
- 다음에도 같게 하기로 한 규칙인가 → Reference
- 대안을 두고 고른 것인가 → Decision. 근거로 걸 기록이 최소 하나 필요하다
- 아직 모르는 것인가 → 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에 올렸는지다 — 둘은 다른 것이라 섞지 않는다.
{
"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에
적힌다.
python3 scripts/build-tech-log-tree.py [프로젝트]
python3 scripts/verify-tech-log-tree.py [프로젝트] # error 0 이어야 한다
순서
- SSOT를 끝까지 읽는다. 절 제목만 훑지 않는다 — 수치와 근거가 어디 있는지 알아야 종류를 정한다
- §3~§8에서 Case, §9에서 Reference, §10에서 Decision, §11에서 Question을 고른다
- 그 넷을 이해하는 데 필요한 Concept만 거꾸로 더한다
- 후보마다 처분을 적고, 사람이 다시 읽은 것만
dispositionReview: CONFIRMED로 둔다.PROMOTE이면서CONFIRMED인 것만 글감이 된다 - 주제를 묶고 주제마다 독자 질문을 한 줄 적는다
- 글감 하나를 골라
<주제>/<종류>/에 기록을 쓴다. 형식은writing-each-kind.md - 색인을 다시 만들고 검사기를 돌린다
- Studio에 넣고 저장한다