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>
This commit is contained in:
DongHyeonka
2026-09-17 11:02:02 +09:00
co-authored by Claude Opus 5
parent 2109f726fe
commit ab59130196
1524 changed files with 3160026 additions and 8369 deletions
@@ -36,19 +36,50 @@ metadata:
## 일곱 단계
| # | 단계 | 스킬 | 산출물 |
|---|---|---|---|
| S1 | 코드베이스 → SSOT | `analyzing-codebase-for-tech-log` | `docs/<프로젝트>/final/document.md` |
| S2 | SSOT → 분해 계약 | `deriving-tech-log-root-tree` | `docs/<프로젝트>/tech-log-studio/tech-log-tree.json` |
| S3 | 글감 → 기록 | `writing-tech-log-records` | `.../<주제>/<종류>/<기록>.md` |
| S4 | 기록 → 그림 | `technical-visualizer` | `final/assets/<이름>/` · `final/.techviz/<이름>/` |
| S5 | AI 티 제거 | `rewriting-technical-prose-naturally` | 같은 기록 파일 (제자리 수정) |
| S6 | 일한 사람의 목소리 | `writing-as-the-person-who-did-it` | 같은 기록 파일 (제자리 수정) |
| S7 | Studio 저장 | `publishing-tech-log-to-studio` | Studio 작업본 + `studio:` URL. **게시하지 않는다** |
| # | 단계 | 스킬 | 에이전트 | 산출물 |
|---|---|---|---|---|
| S1 | 코드베이스 → SSOT | `analyzing-codebase-for-tech-log` | `ssot-analyst` | `docs/<프로젝트>/final/document.md` |
| S2 | SSOT → 분해 계약 | `deriving-tech-log-root-tree` | `tree-deriver` | `docs/<프로젝트>/tech-log-studio/tech-log-tree.json` |
| S3 | 글감 → 기록 | `writing-tech-log-records` | `record-writer` | `.../<주제>/<종류>/<기록>.md` |
| S4 | 기록 → 그림 | `technical-visualizer` | `diagram-maker` | `final/assets/<이름>/` · `final/.techviz/<이름>/` |
| S5 | AI 티 제거 | `rewriting-technical-prose-naturally` | `prose-rewriter` | 같은 기록 파일 (제자리 수정) |
| S6 | 일한 사람의 목소리 | `writing-as-the-person-who-did-it` | `voice-writer` | 같은 기록 파일 (제자리 수정) |
| S7 | Studio 저장 | `publishing-tech-log-to-studio` | `studio-validator` | Studio 작업본 + `studio:` URL. **게시하지 않는다** |
단계마다의 입력·관문·원장 칸은 [references/stage-contracts.md](references/stage-contracts.md).
서브에이전트에 그대로 넣는 프롬프트는 [references/subagent-prompts.md](references/subagent-prompts.md).
## 에이전트를 그때그때 만들지 않는다
단계마다 맡을 에이전트가 `.claude/agents/` 에 있다. `Agent` 도구의 `subagent_type` 에 위 표의
이름을 준다.
```
Agent(subagent_type="record-writer", prompt=<references/subagent-prompts.md 의 S3>)
```
프롬프트만 새로 써서 일반 에이전트를 띄우면 **어떤 규칙으로 일했는지가 어디에도 안 남는다.**
`skillEcho` 는 「스킬을 열었다」를 증명하지만 **누가 열었는지는 증명하지 않는다** — 매번 새로
띄운 에이전트도 SKILL.md 를 읽고 한 줄을 옮겨 적을 수 있다. 그래서 원장의 `runBy` 가 이름을
담고, 검사기가 계약과 대조한 뒤 그 `.md` 가 실재하는지까지 본다.
**배정의 정본은 `scripts/verify-pipeline-run.py` 의 `STAGES` 다.** 틀(`templates/run.json`)에도
같은 값이 적혀 있고, 둘이 갈리면 시험이 잡는다.
에이전트 정의는 그 단계의 「하는 일 · **안 하는 일** · 관문 · 보고」를 적는다. 프롬프트가
그것을 되풀이하지 않아도 되는 것이 이 방식의 값이다 — 프롬프트는 **이번 런의 입력 경로와
글감**만 준다.
**단계가 아닌 에이전트가 넷 더 있다.** 기록 한 편을 놓고 역할을 가른 것이라 아무 단계에도
붙지 않는다. 부를지는 사람이 정하고, 원장의 단계 칸에는 안 들어간다.
| 에이전트 | 언제 | 안 하는 일 |
|---|---|---|
| `source-auditor` | S3 앞. 원본의 주장을 `관측`·`추론`·`미검증`으로 가른 표를 만든다 | 기록을 안 쓴다 |
| `fact-reviewer` | 쓴 뒤. 결과 문장을 원문과 한 글자씩 역대조한다 | 파일을 안 고친다 |
| `reader-reviewer` | 쓴 뒤. 제목·요약·목차만 보고 30초 안에 읽히는지 본다 | 본문을 안 연다 |
| `setup-runner` | Setup 을 쓴 뒤. 손으로 끝까지 칠 수 있는지 읽는다 | 실제로 치지는 않는다 |
## 순서가 고정된 곳
세 자리는 바꾸면 결과가 틀어진다.
@@ -108,8 +139,15 @@ python3 scripts/verify-pipeline-run.py --init runs/<프로젝트>/<runId>/run.js
`runId``YYYY-MM-DD-HHMM` 이다. 원장의 틀은
[templates/run.json](templates/run.json) 이고, `--init` 이 그 틀을 채워 놓는다.
`--init` 은 단계마다 `skillRevision` 도 적는다 — 그 시점 그 스킬의 커밋이다. 스킬은
나중에 고쳐지고, 그러면 이 런의 영수증(`skillEcho`)이 현재 SKILL.md 에서 사라진다.
그 커밋이 적혀 있으면 검사기가 이력을 훑지 않고 그것 하나로 대조한다. 작업 트리가 그
커밋과 다르면 `null` 이다 — 모르는 리비전을 지어내지 않는다. 칸이 없는 옛 원장은
이력 훑기로 떨어지고, 그것도 정상이다.
### 1. 단계마다 서브에이전트를 띄운다
에이전트는 위 표의 것을 쓴다 — `Agent(subagent_type="<이름>", ...)`. 새로 만들지 않는다.
프롬프트는 [references/subagent-prompts.md](references/subagent-prompts.md) 의 것을 쓴다.
프롬프트에 **반드시** 들어가야 하는 넷이 있다.
@@ -135,6 +173,12 @@ error 0 이어야 런이 끝난 것이다. 이 검사기가 보는 것은 결과
준수**다 — 단계가 빠졌는지, 스킬 영수증이 그 스킬의 실제 문장인지, 관문이 돌았고 종료 코드가
0 이었는지, 적어 낸 산출물이 디스크에 있는지.
영수증은 셋이 아니라 **넷으로 갈린다.** 지금 SKILL.md 에 있으면 통과, 그 스킬의 과거
커밋에만 있으면 warn(그 뒤에 스킬이 고쳐졌다 — 어느 커밋에 있었는지 함께 적는다), 어느
판에도 없으면 error, 과거를 볼 수 없었으면(git 이 없다 · 이력 상한에 걸렸다) 또 다른
warn 이다. **warn 은 통과가 아니다** — 요약 줄이 「대조 못 한 영수증」을 따로 센다.
지난 런의 영수증이 warn 으로 바뀌었다고 원장을 고쳐 쓰지 않는다. 그것은 영수증이다.
### 3. 프로젝트 검사기를 돌린다
```bash
@@ -161,3 +205,4 @@ python3 scripts/check-figure-text.py <프로젝트>
| SSOT 에 없는 인용이 있다 | S3 이 앞 기록에서 코드를 옮겨 적었다 | `check_evidence` 종료 코드 ≠ 0 |
| 기록이 색인에 없다 | S2 를 건너뛰고 S3 을 했다 | S2 가 `SKIPPED` 인데 사유가 없다 |
| Studio 에서 그림이 안 보인다 | S7 이 Asset 을 올리기 전에 본문을 넣었다 | S7 관문에 미리보기 확인이 없다 |
| 스킬은 열었는데 결과가 그 역할 같지 않다 | 일반 에이전트를 띄웠다 | `runBy` 가 계약 이름이 아니다 |
@@ -1,6 +1,12 @@
# 단계 계약
단계마다 을 정한다 — **입력 · 스킬 · 관문 · 산출물.** 원장에 적히는 것도 이 넷이다.
단계마다 다섯을 정한다 — **입력 · 스킬 · 에이전트 · 관문 · 산출물.** 원장에 적히는 것도
이 다섯이다.
**에이전트는 그때그때 만들지 않는다.** `.claude/agents/<이름>.md` 가 그 단계의 「하는 일 ·
안 하는 일 · 관문 · 보고」를 적고 있고, `Agent` 도구의 `subagent_type` 에 그 이름을 준다.
배정의 정본은 `scripts/verify-pipeline-run.py``STAGES` 이고, 원장의 `runBy` 가 그 이름을
담는다 — 검사기가 계약과 대조한 뒤 그 `.md` 가 실재하는지까지 본다.
관문은 종료 코드가 0 이어야 지난 것이다. 0 이 아니면 그 단계는 `FAILED` 이고 다음 단계로
넘어가지 않는다.
@@ -12,6 +18,7 @@
| | |
|---|---|
| 스킬 | `analyzing-codebase-for-tech-log` |
| 에이전트 | `ssot-analyst``.claude/agents/ssot-analyst.md` |
| 입력 | 분석 대상 저장소 경로(사용자가 준다) · 그 저장소의 `AGENTS.md`(있으면) |
| 산출물 | `docs/<프로젝트>/final/document.md` |
| 관문 | `python3 scripts/verify-project-layout.py <프로젝트>` |
@@ -62,6 +69,7 @@ SSOT 가 코드와 **어긋나는** 것을 찾으면 그것은 보강 후보가
| | |
|---|---|
| 스킬 | `deriving-tech-log-root-tree` |
| 에이전트 | `tree-deriver``.claude/agents/tree-deriver.md` |
| 입력 | `docs/<프로젝트>/final/document.md` **하나** |
| 산출물 | `docs/<프로젝트>/tech-log-studio/tech-log-tree.json` |
| 관문 | `python3 scripts/build-tech-log-tree.py <프로젝트>``python3 scripts/verify-tech-log-tree.py <프로젝트>` (error 0) |
@@ -100,6 +108,7 @@ SSOT 가 코드와 **어긋나는** 것을 찾으면 그것은 보강 후보가
| | |
|---|---|
| 스킬 | `writing-tech-log-records` |
| 에이전트 | `record-writer``.claude/agents/record-writer.md` |
| 입력 | `tech-log-tree.json` 의 노드 하나 · 그 노드의 `source` 앵커가 가리키는 SSOT 절 |
| 산출물 | `docs/<프로젝트>/tech-log-studio/<주제>/<종류>/<기록>.md` |
| 관문 | 아래 셋 |
@@ -127,6 +136,7 @@ node .agents/skills/writing-tech-log-records/scripts/check_evidence.mjs <프로
| | |
|---|---|
| 스킬 | `technical-visualizer` |
| 에이전트 | `diagram-maker``.claude/agents/diagram-maker.md` |
| 입력 | S3 이 쓴 기록 `.md`(무엇을 그릴지) · 그 기록의 `source` 앵커가 가리키는 SSOT 절(그림의 사실) |
| 산출물 | `final/.techviz/<이름>/{context.json,prompt.md,spec.json}` · `final/assets/<이름>/` |
| 관문 | `techviz lint` · `check-figure-text.py` · `check-figure-overlap.py` · `preview-figure.py` 로 눈 확인 |
@@ -173,6 +183,7 @@ python3 scripts/check-figure-overlap.py --file 그림.svg
| | |
|---|---|
| 스킬 | `rewriting-technical-prose-naturally` |
| 에이전트 | `prose-rewriter``.claude/agents/prose-rewriter.md` |
| 입력 | S3·S4 를 지난 기록 `.md` (제자리 수정) |
| 산출물 | 같은 파일 |
| 관문 | `check_prose.mjs` error 0 · `style_profile.mjs` · S3 관문 재실행 |
@@ -202,6 +213,7 @@ node $S/style_profile.mjs <기록.md>
| | |
|---|---|
| 스킬 | `writing-as-the-person-who-did-it` |
| 에이전트 | `voice-writer``.claude/agents/voice-writer.md` |
| 입력 | S5 를 지난 기록 `.md` · **그리고 그 기록의 상류 자료** (SSOT · 커밋 메시지 · 주석 · `확인하지 못한 것` 칸) |
| 산출물 | 같은 파일 |
| 관문 | `check_voice.mjs` · `check_prose.mjs` 재실행 · S3 관문 재실행 |
@@ -228,6 +240,7 @@ node .agents/skills/rewriting-technical-prose-naturally/scripts/check_prose.mjs
| | |
|---|---|
| 스킬 | `publishing-tech-log-to-studio` |
| 에이전트 | `studio-validator``.claude/agents/studio-validator.md` |
| 입력 | S6 을 지난 기록 `.md` · frontmatter `assets:` 가 가리키는 SVG |
| 산출물 | Studio 작업본. 기록 frontmatter 의 `id`·`studio:` |
| 관문 | 상태 레일이 `저장됨` · `python3 scripts/build-tech-log-tree.py``verify-tech-log-tree.py` |
@@ -243,12 +256,12 @@ Asset 을 본문보다 먼저 올린다. 순서를 뒤집으면 미리보기가
## 관문 요약
| 단계 | 명령 |
|---|---|
| S1 | `verify-project-layout.py <프로젝트>` |
| S2 | `build-tech-log-tree.py``verify-tech-log-tree.py` (error 0) |
| S3 | `studio-body.py``check_body.mjs` · `check_prose.mjs` · `check_evidence.mjs --repo` |
| S4 | `techviz lint` · `check-figure-text.py` · `check-figure-overlap.py` · `preview-figure.py` (눈 확인) |
| S5 | `check_prose.mjs` (error 0) · `style_profile.mjs` · S3 관문 |
| S6 | `check_voice.mjs` · `check_prose.mjs` · S3 관문 |
| S7 | `저장됨` 확인 · `build-tech-log-tree.py``verify-tech-log-tree.py` |
| 단계 | 에이전트 | 명령 |
|---|---|---|
| S1 | `ssot-analyst` | `verify-project-layout.py <프로젝트>` |
| S2 | `tree-deriver` | `build-tech-log-tree.py``verify-tech-log-tree.py` (error 0) |
| S3 | `record-writer` | `studio-body.py``check_body.mjs` · `check_prose.mjs` · `check_evidence.mjs --repo` |
| S4 | `diagram-maker` | `techviz lint` · `check-figure-text.py` · `check-figure-overlap.py` · `preview-figure.py` (눈 확인) |
| S5 | `prose-rewriter` | `check_prose.mjs` (error 0) · `style_profile.mjs` · S3 관문 |
| S6 | `voice-writer` | `check_voice.mjs` · `check_prose.mjs` · S3 관문 |
| S7 | `studio-validator` | `저장됨` 확인 · `build-tech-log-tree.py``verify-tech-log-tree.py` |
@@ -2,6 +2,17 @@
단계마다 에이전트를 하나 띄운다. 아래를 그대로 쓰고 `<...>` 만 바꾼다.
**에이전트를 새로 만들지 않는다.** 단계마다 맡을 에이전트가 `.claude/agents/` 에 있고,
`Agent` 도구의 `subagent_type` 에 그 이름을 준다. 절마다 첫 줄에 적혀 있다.
```
Agent(subagent_type="record-writer", prompt=<아래 S3 프롬프트>)
```
에이전트 정의가 그 단계의 「하는 일 · 안 하는 일 · 관문 · 보고」를 이미 적고 있다. 그래서
프롬프트는 **이번 런의 입력 경로와 글감**을 준다 — 아래 것을 그대로 쓰되, 정의와 어긋나는
지시를 프롬프트로 덮어쓰지 않는다.
## 모든 프롬프트에 들어가는 넷
1. **스킬 이름과 「SKILL.md 를 끝까지 먼저 읽어라」.** 요약을 주지 않는다. 요약을 주면
@@ -42,6 +53,8 @@ S1 처럼 산출물이 `.md` 인 단계는 그래서 한 줄을 더한다.
## S1 — 코드베이스 → SSOT
**에이전트:** `ssot-analyst``subagent_type="ssot-analyst"`
```
너는 Tech Log 파이프라인의 1단계를 맡는다.
@@ -72,6 +85,8 @@ skillEcho 는 방금 읽은 SKILL.md 에서 네 작업에 해당하는 규칙
## S2 — SSOT → 분해 계약
**에이전트:** `tree-deriver``subagent_type="tree-deriver"`
```
너는 Tech Log 파이프라인의 2단계를 맡는다.
@@ -104,6 +119,8 @@ error 0 까지 고쳐라.
## S3 — 글감 → 기록
**에이전트:** `record-writer``subagent_type="record-writer"`
```
너는 Tech Log 파이프라인의 3단계를 맡는다.
@@ -112,6 +129,11 @@ error 0 까지 고쳐라.
record-kinds.md · writing-each-kind.md · body-syntax.md · code-tables-diagrams.md ·
explaining.md · ai-tells.md · choosing-a-diagram.md.
**종류가 Setup 이면 본문을 쓰기 전에 `Skill` 도구로 `writing-practitioner-guides` 를 연다.**
명령을 어떤 형태로 쓸지는 그 스킬이 정한다 — 사람이 직접 치는 실습 가이드이지 에이전트가
실행하기 편한 명령이 아니다. **그 스킬을 못 열면 Setup 을 쓰지 말고 그 사실을 돌려줘라.**
안 열고 쓴 Setup 은 검사기를 다 지나면서도 실행이 중간에 끊긴다 — 실제로 그렇게 나갔다.
글감: docs/<프로젝트>/tech-log-studio/tech-log-tree.json 의 <주제> / <종류> / "<제목>"
그 노드가 PROMOTE 이고 dispositionReview 가 CONFIRMED 인지 먼저 확인해라. 아니면 쓰지 마라.
@@ -138,6 +160,8 @@ error 0 까지 고쳐라.
## S4 — 기록 → 그림
**에이전트:** `diagram-maker``subagent_type="diagram-maker"`
```
너는 Tech Log 파이프라인의 4단계를 맡는다.
@@ -178,6 +202,8 @@ error 0 까지 고쳐라.
## S5 — AI 티 제거
**에이전트:** `prose-rewriter``subagent_type="prose-rewriter"`
```
너는 Tech Log 파이프라인의 5단계를 맡는다.
@@ -212,6 +238,8 @@ document-skeleton.md · article-shape.md · korean-tech-blog-register.md.
## S6 — 일한 사람의 목소리
**에이전트:** `voice-writer``subagent_type="voice-writer"`
```
너는 Tech Log 파이프라인의 6단계를 맡는다.
@@ -246,6 +274,8 @@ check_voice.mjs 는 목소리가 모자란지 재지 않는다. 지어낸 목소
## S7 — Studio 저장
**에이전트:** `studio-validator``subagent_type="studio-validator"`
```
너는 Tech Log 파이프라인의 7단계를 맡는다.
@@ -1,5 +1,5 @@
{
"schemaVersion": 1,
"schemaVersion": 2,
"runId": "<YYYY-MM-DD-HHMM>",
"project": "<프로젝트>",
"record": "<docs/<프로젝트>/tech-log-studio/<주제>/<종류>/<기록>.md — 이 런이 만드는 기록>",
@@ -10,10 +10,11 @@
"id": "S1",
"name": "코드베이스 → SSOT",
"skill": "analyzing-codebase-for-tech-log",
"runBy": "subagent",
"runBy": "ssot-analyst",
"status": "PENDING",
"skipReason": "",
"skillEcho": "",
"skillRevision": null,
"inputs": [],
"outputs": [],
"gates": [],
@@ -23,10 +24,11 @@
"id": "S2",
"name": "SSOT → 분해 계약",
"skill": "deriving-tech-log-root-tree",
"runBy": "subagent",
"runBy": "tree-deriver",
"status": "PENDING",
"skipReason": "",
"skillEcho": "",
"skillRevision": null,
"inputs": [],
"outputs": [],
"gates": [],
@@ -36,10 +38,11 @@
"id": "S3",
"name": "글감 → 기록",
"skill": "writing-tech-log-records",
"runBy": "subagent",
"runBy": "record-writer",
"status": "PENDING",
"skipReason": "",
"skillEcho": "",
"skillRevision": null,
"inputs": [],
"outputs": [],
"gates": [],
@@ -49,10 +52,11 @@
"id": "S4",
"name": "기록 → 그림",
"skill": "technical-visualizer",
"runBy": "subagent",
"runBy": "diagram-maker",
"status": "PENDING",
"skipReason": "",
"skillEcho": "",
"skillRevision": null,
"inputs": [],
"outputs": [],
"gates": [],
@@ -62,10 +66,11 @@
"id": "S5",
"name": "AI 티 제거",
"skill": "rewriting-technical-prose-naturally",
"runBy": "subagent",
"runBy": "prose-rewriter",
"status": "PENDING",
"skipReason": "",
"skillEcho": "",
"skillRevision": null,
"inputs": [],
"outputs": [],
"gates": [],
@@ -75,10 +80,11 @@
"id": "S6",
"name": "일한 사람의 목소리",
"skill": "writing-as-the-person-who-did-it",
"runBy": "subagent",
"runBy": "voice-writer",
"status": "PENDING",
"skipReason": "",
"skillEcho": "",
"skillRevision": null,
"inputs": [],
"outputs": [],
"gates": [],
@@ -88,10 +94,11 @@
"id": "S7",
"name": "Studio 저장",
"skill": "publishing-tech-log-to-studio",
"runBy": "subagent",
"runBy": "studio-validator",
"status": "PENDING",
"skipReason": "",
"skillEcho": "",
"skillRevision": null,
"inputs": [],
"outputs": [],
"gates": [],