capture-evidence.py 는 명령을 subprocess 로 직접 돌리고 그 프로세스의 반환값을 그대로 메타에 적는다. 종료 코드를 인자로 받지 않으므로 손으로 적을 경로가 없다. raw 원문과 실행 메타가 같은 이름으로 함께 떨어져 「raw 는 있는데 meta 가 없다」가 구조적으로 안 생긴다. skill-versions.py 는 스킬 9개의 metadata.version 과 검사기 17개의 내용 해시를 한 장으로 낸다. 통과 판정을 검증기 버전에 묶으려면 묶을 값이 있어야 한다. 버전 칸이 없던 스킬 여덟에 1.0.0 을 붙였다. 산문은 한 줄도 안 바꿨다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4vKjQo9KKBBokzxqXLCfk
167 lines
7.9 KiB
Markdown
167 lines
7.9 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.
|
|
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.
|
|
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.
|
|
|
|
## 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.
|
|
|
|
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.
|