--- 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 ` — 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 ``` `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.