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,172 @@
|
||||
# Tech Log Tree Contract
|
||||
|
||||
`tech-log-tree.json` is the explicit boundary between deep project analysis and Tech Log
|
||||
record generation. **It is the decomposition contract and the index at once, and it is the
|
||||
source of truth.** There is one file, so nothing can disagree with it.
|
||||
|
||||
A finished `tech-log-studio/` holds `tech-log-tree.json` and the record folders. Nothing
|
||||
else.
|
||||
|
||||
A project whose index predates this contract fails verification with one error until it is
|
||||
migrated. The per-field checks stay off for such a project — "not written yet" must not read
|
||||
as "written wrong" — but non-adoption itself is counted, because a warning lets an old index
|
||||
avoid every check indefinitely.
|
||||
|
||||
## Required top level
|
||||
|
||||
- `schemaVersion`
|
||||
- `project`
|
||||
- `ssot` and `ssotSha256` — the hash prevents treating a tree derived from old material
|
||||
as current
|
||||
- `sourceRevision`
|
||||
- `generatedAt`
|
||||
- `sourceRepository` — `path`, `revision`, and `verified`: which checkout the analysis read,
|
||||
which commit the document describes, and how that was confirmed. Leave `revision` null rather
|
||||
than inventing one; the verifier warns instead of accepting a made-up label. When the work
|
||||
is spread over branches rather than one line of commits, use `revisions` — a label to commit
|
||||
map — and pin every tip the document describes
|
||||
- `candidateScope` — which part of the SSOT candidates may come from
|
||||
- `contract` — the decomposition rules, `readinessValues`, `dispositionValues`
|
||||
- `topics`, `candidates`, `counts`, `unlisted`
|
||||
|
||||
## Candidate scope
|
||||
|
||||
A folded `final/document.md` carries the integrated analysis, the module analyses, and the
|
||||
analysis material in one file. Only the first is candidate material.
|
||||
|
||||
```json
|
||||
"candidateScope": {
|
||||
"document": "final/document.md",
|
||||
"sections": ["§3", "§4", "§5", "§6", "§7", "§8", "§9", "§10", "§11"],
|
||||
"excluded": ["제2부 — 모듈 분석 전문", "제3부 — 분석 재료"]
|
||||
}
|
||||
```
|
||||
|
||||
`document` names the SSOT and must match `ssot`. `sections` names the candidate scope, and
|
||||
`excluded` names the parts that are evidence rather than candidates. A node may cite an
|
||||
anchor from an excluded part in `source`; it may not exist because of one.
|
||||
|
||||
## Topics
|
||||
|
||||
Each Topic has `topic` (its key), `title`, **`readerQuestion`**, and `kinds` with the five
|
||||
record kinds.
|
||||
|
||||
```json
|
||||
"oauth-oidc-auth-boundary": {
|
||||
"topic": "oauth-oidc-auth-boundary",
|
||||
"title": "OAuth 자격증명과 세션의 보관 경계",
|
||||
"readerQuestion": "자격증명과 세션을 누가 보관하고, 누가 API 요청을 만들며, 보호 자원은 무엇을 신뢰하는가?",
|
||||
"kinds": { "case": [], "concept": [], "reference": [], "question": [], "decision": [] }
|
||||
}
|
||||
```
|
||||
|
||||
Every node in the Topic must help answer the reader question. Two Topics do not share a
|
||||
question; one Topic does not need two. Empty kinds are allowed — do not manufacture nodes
|
||||
to fill all five.
|
||||
|
||||
## Candidates
|
||||
|
||||
Everything the analysis found lives in `candidates` with its disposition —
|
||||
`.agents/skills/deriving-tech-log-root-tree/references/candidate-disposition.md`. Only
|
||||
`PROMOTE` candidates become nodes under `topics`, and the relation runs both ways: a
|
||||
`PROMOTE` candidate whose target is not a node, and a node no `PROMOTE` candidate points
|
||||
at, are both contract errors.
|
||||
|
||||
`dispositionReview` records whether a person re-read the candidate under the independence
|
||||
test. `PENDING` means it reached the tree by recall alone, and a `PENDING` candidate is an
|
||||
error, not a warning — a record written over an unreviewed tree inherits the
|
||||
over-classification the disposition step exists to catch. Write records only for nodes
|
||||
whose candidate is `PROMOTE` and `CONFIRMED`.
|
||||
|
||||
## Written by hand, refreshed by script
|
||||
|
||||
`readiness` · `source` · `code` · `evidence` · `classification` · `relations` and the rest
|
||||
of each kind's fields are written by a person. `build-tech-log-tree.py` never touches them.
|
||||
It refreshes only what it can read from the record files — `file`, `publication`, `status`,
|
||||
`studioId`, `assets`, `evidenceFiles` — and lists records that have no node in `unlisted`.
|
||||
|
||||
### Case
|
||||
|
||||
`slug` · `readiness` · `source` · `classification` · `missing-verification` · `relations`,
|
||||
plus `code`/`evidence` when the conclusion depends on them.
|
||||
|
||||
One problem, an observation or reproduction, a diagnosis, a conclusion that closes. Several
|
||||
observations that answer the same question with the same conclusion are one Case with a
|
||||
table or sub-sections, not several partial Cases.
|
||||
|
||||
### Concept
|
||||
|
||||
`slug` · `readiness` · `source` · `basis-version` · `classification` · `relations`.
|
||||
|
||||
`basis-version` names what the explanation was written against — `Keycloak 26.7.0 identity
|
||||
brokering`, `Spring Boot 3.3 auto-configuration`. A Concept without it cannot be known to
|
||||
be stale.
|
||||
|
||||
A Concept exists because a Case, Decision, or Question needs it to be understood. Absence,
|
||||
call-counts, unwired subsystems, analysis scope, and coverage ledgers are not Concepts.
|
||||
|
||||
### Reference
|
||||
|
||||
`slug` · `readiness` · `source` · `classification` · `scope` · `exceptions` · `relations`.
|
||||
|
||||
A Reference must be useful beyond retelling one Case. If removing the originating
|
||||
project's names leaves no rule, it is still a Case.
|
||||
|
||||
### Open Question
|
||||
|
||||
`slug` · `readiness: OPEN` · `source` · `known` · `unknown` · `next-verification` ·
|
||||
`decision-criterion` · `relations`.
|
||||
|
||||
Do not create a Question when the analysis already contains a verified answer. Move the
|
||||
material to Case/Reference/Decision and update the tree first.
|
||||
|
||||
### Decision
|
||||
|
||||
`slug` · `readiness` · `decision-status` · `source` · `decision-evidence` · `grounds` ·
|
||||
`classification` · `relations`.
|
||||
|
||||
`decision-status` is `PROPOSED`, `ADOPTED`, `SUPERSEDED`, or `NOT_DECIDED`. A
|
||||
`NOT_DECIDED` candidate uses `NEEDS_DECISION` and is not written as a Decision.
|
||||
|
||||
## Readiness semantics
|
||||
|
||||
**`readiness` is about evidence, not about publication.** Whether a record has been
|
||||
written, and whether it has been saved into Studio, are separate facts that the generated
|
||||
index carries as `file` and `publication`. A published record with thin evidence is still
|
||||
`NEEDS_EVIDENCE`.
|
||||
|
||||
| readiness | meaning | generation |
|
||||
|---|---|---|
|
||||
| `READY` | grounded enough for the kind | allowed |
|
||||
| `OPEN` | legitimate unresolved Question | allowed as Open Question |
|
||||
| `NEEDS_EVIDENCE` | material assertion still lacks verification | blocked |
|
||||
| `NEEDS_DECISION` | direction sounds plausible but the project has not decided | blocked |
|
||||
| `BLOCKED` | sources are incomplete or contradictory | blocked |
|
||||
|
||||
`REJECTED` is not a readiness. Whether a candidate becomes a record at all is a
|
||||
disposition, and it lives in `candidates`, not on the node.
|
||||
|
||||
## Derivation rules
|
||||
|
||||
1. Discover candidates from `final/document.md` only. It is the whole analysis, folded in —
|
||||
there is no `analysis/` folder to search in a finished project.
|
||||
2. Give every candidate a disposition before writing any node. `KEEP_IN_SSOT` is a normal
|
||||
outcome, and a decomposition that excludes nothing has not selected anything.
|
||||
3. Apply the independence test: if folding the record into a related Case or Concept as
|
||||
one section changes nothing, it is not an independent record.
|
||||
4. Take Cases, References, Decisions, and Questions first; add Concepts backwards from
|
||||
what those four require.
|
||||
5. Prefer several narrowly grounded Cases over one broad Case combining unrelated
|
||||
incidents — but merge observations that share a question and a conclusion.
|
||||
6. Extract Decisions only from explicit choice evidence: ADR, commit/history, configuration
|
||||
plus recorded rationale, issue/PR decision, or a user-supplied decision record.
|
||||
7. A node may relate to several siblings, but each record has one primary purpose.
|
||||
8. If new runtime evidence changes the answer, update the analysis and revise the tree
|
||||
before editing downstream records.
|
||||
|
||||
## Generation and verification
|
||||
|
||||
```bash
|
||||
python3 scripts/build-tech-log-tree.py <project> # 파생 칸을 다시 채운다
|
||||
python3 scripts/verify-tech-log-tree.py <project> # error 0 이어야 한다
|
||||
```
|
||||
Reference in New Issue
Block a user