리뷰 두 건을 반영했다. 계약 - 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>
143 lines
6.8 KiB
Markdown
143 lines
6.8 KiB
Markdown
---
|
|
name: deriving-tech-log-root-tree
|
|
description: Use when a completed or substantially completed docs project analysis must be decomposed into grounded Tech Log Topics and candidate Case, Concept, Reference, Open Question, and Decision records.
|
|
---
|
|
|
|
# Deriving Tech Log Root Tree
|
|
|
|
## Core rule
|
|
|
|
**Select what is worth publishing. Do not emit everything the analysis found.**
|
|
|
|
Recall is the objective function of an analysis-coverage audit. It is not the objective
|
|
function of an editorial decomposition. When the two are measured on one axis, every
|
|
analysis by-product becomes a record. A decomposition that excludes nothing has not
|
|
selected anything.
|
|
|
|
## SSOT hierarchy
|
|
|
|
Four layers, and only one of them is the input for finding candidates.
|
|
|
|
| Layer | Role |
|
|
|---|---|
|
|
| code · config · execution evidence | ground truth for facts |
|
|
| `final/document.md` | **SSOT for the candidate scope** — the only input for discovering candidates |
|
|
| `analysis/**/*.md` | supporting detail for a claim `final` already adopted — exists only while the analysis is running |
|
|
| `tech-log-tree.json` | the decomposition contract and the index at once, and the source of truth. Written by hand; a script refreshes only the fields it can read back from the record files |
|
|
|
|
Do not open `analysis/**` to discover candidates. Open it to check the detail of a claim
|
|
that is already in `final/document.md`. If the analysis holds material that `final` does
|
|
not, **fix `final/document.md` first**, then decompose. Otherwise 61 module documents
|
|
become 61 competing SSOTs and the tree grows to their combined section count.
|
|
|
|
## Candidate scope
|
|
|
|
A folded `final/document.md` is not uniformly candidate material. Part 1 is the integrated
|
|
analysis and it is where candidates come from. Part 2 holds the module analyses that were
|
|
folded in, and Part 3 holds the analysis material — both are supporting evidence, and
|
|
reading them as candidate material recreates the failure the fold was meant to end: one
|
|
candidate per module-analysis heading.
|
|
|
|
Declare the boundary in the contract so it is checkable rather than remembered.
|
|
|
|
```json
|
|
"candidateScope": {
|
|
"document": "final/document.md",
|
|
"sections": ["§3", "§4", "§5", "§6", "§7", "§8", "§9", "§10", "§11"],
|
|
"excluded": ["제2부 — 모듈 분석 전문", "제3부 — 분석 재료"]
|
|
}
|
|
```
|
|
|
|
An anchor outside that scope is a source anchor, not a candidate. Cite it from a node whose
|
|
candidate came from Part 1.
|
|
|
|
## Which files exist, and when
|
|
|
|
Decomposition happens after the analysis has been folded in, and the folded project has
|
|
fewer files than the one that was being analyzed. Read what is actually there.
|
|
|
|
| Phase | Files | Where candidates come from |
|
|
|---|---|---|
|
|
| analysis running | `state.json` · `source-index.md` · `analysis/**` · `final/document.md` | `final/document.md` |
|
|
| analysis folded in | `final/document.md` only | `final/document.md`, candidate scope |
|
|
| decomposition | `final/document.md` · `tech-log-tree.json` | candidate scope |
|
|
|
|
`state.json` and `source-index.md` say how far the analysis got and which code it covered.
|
|
They do not hold candidates, and in a folded project they are gone.
|
|
|
|
## Required sequence
|
|
|
|
1. Read the candidate scope of `final/document.md` end to end.
|
|
2. Pick representative **Cases** from the confirmed-problem and execution sections
|
|
(in the standard layout, §3–§8).
|
|
3. Pick **References** from the reusable-criteria section (§9).
|
|
4. Pick **Decisions** from the explicit-decision section (§10).
|
|
5. Pick **Questions** from the unresolved section (§11).
|
|
6. Only now add the **Concepts** those four need in order to be understood. Concept is
|
|
derived backwards from the records that require it, never by sweeping headings.
|
|
7. Give every candidate a disposition — `references/candidate-disposition.md` — and set
|
|
`dispositionReview` to `CONFIRMED` only for the ones a person actually re-read.
|
|
8. Group `PROMOTE` candidates into Topics. Write one reader question per Topic.
|
|
9. Write every promoted candidate into `tech-log-tree.json` as a node under its Topic,
|
|
with the fields its kind requires. There is no second tree to keep in step.
|
|
10. Run `references/decomposition-checklist.md`.
|
|
11. Record `candidateScope`, the source document hash, and the project revision.
|
|
12. `python3 scripts/verify-tech-log-tree.py <project>` — errors must be 0.
|
|
|
|
Use `.agents/skills/writing-tech-log-records/references/tech-log-tree-contract.md` as the
|
|
output contract.
|
|
|
|
## Topic boundary
|
|
|
|
**A Topic is one reader question.** Write it under the topic slug:
|
|
|
|
```text
|
|
TOPIC
|
|
OAuth 자격증명과 세션의 보관 경계
|
|
oauth-oidc-auth-boundary
|
|
독자 질문 — 자격증명과 세션을 누가 보관하고, 누가 API 요청을 만들며, 보호 자원은 무엇을 신뢰하는가?
|
|
```
|
|
|
|
A node that does not help answer that question belongs to another Topic. If a Topic needs
|
|
two questions, it is two Topics. If two Topics share one question, they are one Topic.
|
|
|
|
This is the test that catches both failures at once — splitting one problem space across
|
|
`state-machines-and-ownership`, `state-ownership-and-concurrency`, and
|
|
`owner-safe-state-machines`, and packing forwarded-header trust, fileserver mapping,
|
|
Redis key APIs, and permission normalization into one `admission-budget-and-backpressure`.
|
|
|
|
Do not create one Topic per source file or module. A directory is not a Topic.
|
|
|
|
## Classification discipline
|
|
|
|
- **Case** — one problem, an observation or reproduction, a diagnosis, a closed conclusion.
|
|
- **Concept** — structure or behavior that must be explained from the beginning before a
|
|
Case can be understood. Has a `basis-version`.
|
|
- **Reference** — a rule that applies to the next project, with scope and exceptions.
|
|
- **Open Question** — no answer yet, the design turns on the answer, and there is a next
|
|
verification and a closing criterion.
|
|
- **Decision** — the project actually chose a direction, with grounds and an accepted cost.
|
|
|
|
The independence test decides all five:
|
|
|
|
> Delete this record and fold it into a related Case or Concept as one section. If
|
|
> understanding, decisions, and reuse are unchanged, it is not an independent record.
|
|
|
|
Branches may be empty. Symmetry is not a quality goal. Neither is volume — a large
|
|
denominator justifies a long `final/document.md`, not a long tree.
|
|
|
|
## Refreshing the derived fields
|
|
|
|
There is one file. `tech-log-tree.json` is written by hand, and the build refreshes only
|
|
what it can read back from the record files — `file`, `publication`, `status`, `studioId`,
|
|
`assets`, `evidenceFiles` — plus `counts`, `ssotSha256`, and the `unlisted` list.
|
|
|
|
```bash
|
|
python3 scripts/build-tech-log-tree.py <project>
|
|
```
|
|
|
|
`readiness`, `source`, `classification`, `relations`, and the rest of each kind's fields
|
|
survive a rebuild untouched. The build never reads the directory listing for Topics: a
|
|
folder left behind after a node is dropped from the contract shows up in `unlisted`, and it
|
|
does not come back as a Topic.
|