Files
document-haness/.agents/skills/deriving-tech-log-root-tree/SKILL.md
T
DongHyeonkaandClaude Opus 5 ab59130196 chore: 이전 세션이 남긴 변경을 커밋한다
이번 파이프라인 작업과 무관하게 작업 트리에 남아 있던 것을 그대로 올린다.
사용자가 「전부 커밋」으로 정했고, 이번 작업과 섞이지 않게 커밋만 나눴다.

대부분은 clean-architecture-backend-template 의 그림 정본 재배치다 —
final/assets/diagrams/<이름>/ 에 있던 것이 CLAUDE.md 가 적은 배치인
final/assets/<이름>/ 로 옮겨졌고 .techviz/<이름>/ 이 함께 들어왔다.
삽입 줄의 대부분(3.15M)이 그 .techviz context.json 이다.

그 밖에 ca-tmpl·document-haness 의 정리, .claude/agents/ 열한 개,
writing-practitioner-guides 스킬, .playwright-mcp 세션 산출물,
scripts/check-ssot-facts.py 와 그 시험이 들어 있다.

이 커밋의 내용은 내가 만든 것이 아니라 이전 세션이 남긴 것이고 검증하지 않았다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 11:02:02 +09:00

190 lines
9.2 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, Setup, Reference, Open Question, and Decision records.
metadata:
version: "1.0.0"
language: "ko-KR"
---
# 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. Add the
**Setups** the analysis actually supports in the same pass — the section below says which
ones those are.
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/build-tech-log-tree.py <project>` first, then
`python3 scripts/verify-tech-log-tree.py <project>` — errors must be 0. Build fills
`ssotSha256`; verify errors when it is absent, so verifying before building always fails.
## When a Setup belongs in the tree
Add a **Setup** where the analysis records the commands and configuration values that stand
an environment up and someone other than the author has to run them.
Its test is not the one the other five take. They ask whether a claim is worth publishing on
its own; this one asks whether a reader would type these lines. A reproduction that only
re-obtains one measurement stays inside that Case's `재현 조건` field, and a procedure nobody
but the author would run is that Case's environment section.
Setup nodes need a project. A Topic is optional, and a Setup without one reads as that
project's shared configuration. Instead of a verification date it carries `pinned-versions`
— the versions the procedure was established on.
## Three fields the verifier requires and this procedure does not otherwise name
Write them by hand. `verify-tech-log-tree.py` counts each as an error when missing.
| Field | What goes in it |
|---|---|
| `candidateScope` | the range candidates were found in — above |
| `sourceRepository` | `path` of the analyzed repository, its `revision`, and how that was established. Leave `revision` `null` rather than inventing one; when the work is split across branches, pair names and commits under `revisions` |
| `ssotSha256` | filled by `build-tech-log-tree.py`. Its job is to catch a tree whose SSOT changed after the candidates were chosen |
```json
"sourceRepository": {
"path": "/absolute/path/to/analyzed-repo",
"revision": null,
"revisions": {"AP1 develop-pattern1": "64175266"},
"verified": "how the revision was established, or why it is null"
}
```
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.
- **Setup** — a procedure the reader runs to stand the environment up. Carries
`pinned-versions` instead of a verification date, and always belongs to a project.
The independence test decides the first 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.
Setup does not answer that question, because folding a procedure into a Case is exactly what
this kind exists to stop — the commands end up in a plain-text `검증 환경` field where they
cannot be copied. Ask instead who runs it. If only the author ever will, it is that Case's
environment, not a 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.