pipeline: make tech-log-tree.json the one decomposition contract and enforce it
리뷰 두 건을 반영했다. 계약 - 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>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
73026cada6
commit
9d2a3725c5
@@ -0,0 +1,152 @@
|
||||
# `tech-log-tree.json` 예시
|
||||
|
||||
**구조 예시다.** 실제 분석을 수행해 만든 결과가 아니므로 이 값을 그대로 옮겨 쓰지 않는다.
|
||||
실제 프로젝트에서는 같은 모양에 진짜 source anchor 와 evidence 를 채우고 readiness 를 판정한다.
|
||||
|
||||
트리는 이 파일 하나다. 사람이 읽는 트리와 Node Specification 을 따로 쓰고 대조하던 절차는 없다 —
|
||||
계약과 색인이 같은 파일이라 어긋날 자리가 없다.
|
||||
|
||||
```json
|
||||
{
|
||||
"schemaVersion": 4,
|
||||
"project": "n+1liner",
|
||||
"ssot": "final/document.md",
|
||||
"ssotSha256": "<sha256>",
|
||||
"sourceRevision": "<git-revision>",
|
||||
"generatedAt": "<YYYY-MM-DD>",
|
||||
"candidateScope": {
|
||||
"document": "final/document.md",
|
||||
"sections": ["§3", "§4", "§5", "§6", "§7", "§8", "§9", "§10", "§11"],
|
||||
"excluded": ["제2부 — 모듈 분석 전문", "제3부 — 분석 재료"]
|
||||
},
|
||||
"contract": {
|
||||
"readinessValues": ["READY", "OPEN", "NEEDS_EVIDENCE", "NEEDS_DECISION", "BLOCKED"],
|
||||
"dispositionValues": {
|
||||
"PROMOTE": "독립 Tech Log 로 쓴다",
|
||||
"MERGE_INTO": "다른 기록의 한 절로 흡수한다",
|
||||
"KEEP_IN_SSOT": "분석에는 남기고 독립 기록으로 만들지 않는다",
|
||||
"NEEDS_EVIDENCE": "주장에 아직 검증이 없다",
|
||||
"NEEDS_DECISION": "방향이 그럴듯하지만 프로젝트가 정하지 않았다",
|
||||
"BLOCKED": "원본이 불완전하거나 서로 어긋난다"
|
||||
}
|
||||
},
|
||||
"topics": {
|
||||
"jpa-feed-query-performance": {
|
||||
"topic": "jpa-feed-query-performance",
|
||||
"title": "JPA 피드 조회 성능",
|
||||
"readerQuestion": "피드 한 화면을 그리는 데 쿼리가 몇 번 나가고, 조회 전략을 바꿀 때 무엇이 함께 바뀌는가?",
|
||||
"kinds": {
|
||||
"case": [
|
||||
{
|
||||
"title": "필드 접근 없이 발생한 EAGER ToOne N+1",
|
||||
"kind": "case",
|
||||
"slug": "eager-to-one-n-plus-one",
|
||||
"readiness": "READY",
|
||||
"source": ["final/document.md#user-page-연관-숨은-추가-쿼리-정량화"],
|
||||
"code": ["FeedQueryRepository.java:loadFeed"],
|
||||
"evidence": ["evidence/raw/explain/highlights-child-plan-A.txt"],
|
||||
"classification": "조회 한 번에 나간 쿼리 수를 세어 재현했고 실행계획으로 확인했다",
|
||||
"missing-verification": "동시 트래픽에서는 재지 않았다",
|
||||
"relations": ["reference:fetch-type-vs-fetch-strategy"]
|
||||
}
|
||||
],
|
||||
"concept": [
|
||||
{
|
||||
"title": "Fetch Type 과 Fetch Strategy 가 갈라지는 자리",
|
||||
"kind": "concept",
|
||||
"slug": "fetch-type-and-fetch-strategy",
|
||||
"readiness": "READY",
|
||||
"source": ["final/document.md#fetch-type과-fetch-strategy"],
|
||||
"basis-version": "Hibernate 6.4 · Spring Data JPA 3.2",
|
||||
"classification": "이 구분을 먼저 알아야 위 Case 의 관측을 읽을 수 있다",
|
||||
"relations": ["case:eager-to-one-n-plus-one"]
|
||||
}
|
||||
],
|
||||
"reference": [
|
||||
{
|
||||
"title": "Fetch Type 과 Fetch Strategy 를 구분한다",
|
||||
"kind": "reference",
|
||||
"slug": "fetch-type-vs-fetch-strategy",
|
||||
"readiness": "READY",
|
||||
"source": ["final/document.md#fetch-type과-fetch-strategy"],
|
||||
"classification": "다음 프로젝트에도 적용할 조회 기준이다",
|
||||
"scope": "JPA 연관을 하나라도 조회하는 모듈",
|
||||
"exceptions": "단건 조회만 있는 경로에는 걸리지 않는다",
|
||||
"relations": ["case:eager-to-one-n-plus-one"]
|
||||
}
|
||||
],
|
||||
"question": [
|
||||
{
|
||||
"title": "ANALYZE 이후 Cardinality Estimate 는 어떻게 달라지는가",
|
||||
"kind": "question",
|
||||
"slug": "cardinality-estimate-after-analyze",
|
||||
"readiness": "OPEN",
|
||||
"source": ["final/document.md#query-plan-측정"],
|
||||
"known": "현재 통계에서 Plan B 의 추정 행 수는 실제의 1/8 이다",
|
||||
"unknown": "통계를 갱신하면 플래너가 같은 계획을 고르는지",
|
||||
"next-verification": "seed(1000) 뒤 ANALYZE highlights 를 돌리고 Plan B 를 다시 잰다",
|
||||
"decision-criterion": "추정치가 실제의 2배 안이면 닫고, 벗어나면 통계 갱신 주기를 정하는 Decision 으로 넘긴다",
|
||||
"relations": ["case:eager-to-one-n-plus-one"]
|
||||
}
|
||||
],
|
||||
"decision": [
|
||||
{
|
||||
"title": "Collection Fetch Join 과 Pagination 을 같이 쓰지 않는다",
|
||||
"kind": "decision",
|
||||
"slug": "no-collection-fetch-join-with-pagination",
|
||||
"readiness": "READY",
|
||||
"decision-status": "ADOPTED",
|
||||
"source": ["final/document.md#컬렉션-fetch-join-페이징"],
|
||||
"decision-evidence": ["case:eager-to-one-n-plus-one"],
|
||||
"grounds": "메모리 페이징으로 떨어지는 것을 실행계획에서 확인했다",
|
||||
"classification": "대안을 두고 프로젝트가 실제로 고른 방향이다",
|
||||
"relations": ["case:eager-to-one-n-plus-one"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"candidates": [
|
||||
{
|
||||
"id": "F012",
|
||||
"kindCandidate": "CASE",
|
||||
"sourceRefs": ["final/document.md#user-page-연관-숨은-추가-쿼리-정량화"],
|
||||
"summary": "필드 접근 없이 EAGER ToOne 이 추가 쿼리를 냈다",
|
||||
"disposition": "PROMOTE",
|
||||
"dispositionReview": "CONFIRMED",
|
||||
"target": "case:eager-to-one-n-plus-one",
|
||||
"reason": "재현·진단·결론이 한 사건 안에서 닫힌다"
|
||||
},
|
||||
{
|
||||
"id": "F013",
|
||||
"kindCandidate": "CASE",
|
||||
"sourceRefs": ["final/document.md#컬렉션-n1-정량화"],
|
||||
"summary": "같은 원인으로 컬렉션 쪽에서도 추가 쿼리가 났다",
|
||||
"disposition": "MERGE_INTO",
|
||||
"dispositionReview": "CONFIRMED",
|
||||
"target": "case:eager-to-one-n-plus-one",
|
||||
"reason": "같은 결함의 두 번째 증상이다. 그 Case 의 표에 행으로 들어간다"
|
||||
},
|
||||
{
|
||||
"id": "F014",
|
||||
"kindCandidate": "CONCEPT",
|
||||
"sourceRefs": ["final/document.md#분석-범위"],
|
||||
"summary": "이번 분석에서 읽은 리포지터리 메서드는 41개다",
|
||||
"disposition": "KEEP_IN_SSOT",
|
||||
"dispositionReview": "CONFIRMED",
|
||||
"target": null,
|
||||
"reason": "분석 범위 계수다. 분석에는 남아야 하고 공개 기록으로는 읽을 사람이 없다"
|
||||
}
|
||||
],
|
||||
"counts": { "topics": 1, "nodes": 5, "written": 0, "unwritten": 5, "unlisted": 0, "candidates": 3 },
|
||||
"unlisted": [],
|
||||
"history": {}
|
||||
}
|
||||
```
|
||||
|
||||
## 이 예시가 보여 주는 것
|
||||
|
||||
- 후보 셋 중 하나만 글감이 됐다. `MERGE_INTO` 와 `KEEP_IN_SSOT` 이 없는 분해는 선별하지 않은 분해다.
|
||||
- Concept 은 Case 를 먼저 고른 뒤에 그것을 읽는 데 필요해서 더했다.
|
||||
- Question 에 `decision-criterion` 이 있다. 무엇이 나오면 닫는지를 적지 않으면 검증을 마쳐도 열려 있다.
|
||||
- 다섯 종류를 억지로 채우지 않아도 된다. 여기서 다섯이 다 있는 것은 실제로 다섯이 있었기 때문이다.
|
||||
Reference in New Issue
Block a user