genetics-mcp 2.17.1 → 2.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/bin/aarch64-apple-darwin/gcell +0 -0
  2. package/bin/manifest.json +2 -2
  3. package/bin/x86_64-apple-darwin/gcell +0 -0
  4. package/genetics.mjs +71 -16
  5. package/package.json +1 -1
  6. package/skills/genetics-report/SKILL.md +146 -131
  7. package/skills/genetics-report/assets/example/cases-briefing-designs.json +32 -0
  8. package/skills/genetics-report/assets/example/plate-pair-after.png +0 -0
  9. package/skills/genetics-report/assets/example/plate-pair-before.png +0 -0
  10. package/skills/genetics-report/assets/example/plate-pair.html +25 -0
  11. package/skills/genetics-report/assets/example/report-briefing-designs.json +736 -0
  12. package/skills/genetics-report/assets/example/report-briefing.json +27 -22
  13. package/skills/genetics-report/assets/example/report.json +5 -5
  14. package/skills/genetics-report/assets/figure.mjs +390 -63
  15. package/skills/genetics-report/assets/mockshot.mjs +3 -2
  16. package/skills/genetics-report/assets/pair.mjs +138 -0
  17. package/skills/genetics-report/assets/shot.mjs +87 -19
  18. package/skills/genetics-report/references/content-pipeline.md +34 -33
  19. package/skills/genetics-report/references/history.md +117 -0
  20. package/skills/genetics-report/references/illustrations.md +16 -12
  21. package/skills/genetics-report/references/lim-portal-format.md +175 -91
  22. package/skills/genetics-report/references/linear-read-test.workflow.js +47 -0
  23. package/skills/genetics-report/references/process.md +152 -90
  24. package/skills/genetics-report/references/readability.md +48 -24
  25. package/skills/genetics-report/references/reader-review.workflow.js +160 -27
  26. package/skills/genetics-report/references/visual-first.md +100 -35
@@ -11,20 +11,10 @@ SKILL.md 는 「무엇을 만드는가」를 정하고, 이 문서는 **각 단
11
11
 
12
12
  ---
13
13
 
14
- ## 0. 전체 흐름
15
-
16
- | 단계 | 하는 일 | 산출물 | 묻는가 | 자동 관문 |
17
- |---|---|---|---|---|
18
- | 0 | 독자 정의 | 독자 문장 1개 + 어휘 정책 | **반드시 묻는다** | — |
19
- | 1 | 자료 수집 | 원자료(티켓·로그·대화) | 소스·범위·「고객이 아는 참조」 | — |
20
- | 2 | 건별 정리 | `cases` 배열 | 안 묻는다. 단 「확인 필요」는 되돌린다 | 발행 검증(그룹 정합) |
21
- | 3 | 등급 부여 | `grades` + 건별 `grade` | 등급 이름·개수만 | 없음 — 사람이 본다 |
22
- | 4 | 요약·확인 요청 | `summary` · `open_items` · `cta` | 공통 원인의 실재 여부 | **요약 근거 게이트 — 발행 거절** |
23
- | 5 | **여백 주석 채우기** | 절마다 `marg{why,key,advice}` | 안 묻는다 | ⚠ 경고(거절 아님) |
24
- | 6 | 조립 | `parts` + `sections` + 골격 칸 | 원자료의 「확인 필요」 | 발행 검증 + 렌더러 assert 2종 |
25
- | 7 | 검수 | 통과/실패 판정 | 안 묻는다 | dry_run warnings + 검사기 8종 + **사람의 눈** |
26
- | 8 | 전달 | 열람 링크 또는 `.html` 하나 | **공개 직전 사람 승인** | 초안이 기본값 |
27
- | 9 | 발행 뒤 판정 | outline 지문 | 안 묻는다 | `get_report(outline: true)` |
14
+ ## 전체 흐름
15
+
16
+ **단계 번호와 밟는 순서의 정본은 SKILL.md 「작업 체크리스트」 한 장이다** — 단계 · 산출물 · 명령 · 통과 기준 · 집행자 · 묻는 것이 거기 있다.
17
+ 이 문서는 그 번호를 따라 각 단계의 내용만 푼다 — 0a 결정 장부는 0단계 안에, 0.5단계 시각 설계는 SKILL.md 0.5단계와 `references/visual-first.md` 에 있다. 흐름표를 여기 두 벌로 두지 않는다.
28
18
 
29
19
  새 프로젝트에서 손대는 것은 **발행 데이터 하나**다(로컬 렌더라면 `report.json` + `cases.json`).
30
20
  렌더러(`build.py`·`gazette.py`·`sentbreak.py`·`boundary.py`·`gazette.css`)는 레포에만 있고 프로젝트 고유 데이터를 하나도 안 갖는다.
@@ -39,16 +29,16 @@ python3 ~/.claude/skills/genetics-report/assets/render.py report.json
39
29
  **`report.json` 에 `css`·`render`·`palette` 를 적지 마라** — 서버와 갈릴 수 있고, 갈리면 로컬에서 본 것과 고객이 본 것이 다르다.
40
30
 
41
31
  **`build.py` 를 직접 부르지 마라.**
42
- `cfg.get('css', ['report.css'])`(`build.py:285-286`)로 떨어져 옛 서식 시트가 실린다.
32
+ `cfg.get('css', ['report.css'])`(`build.py styles()`)로 떨어져 옛 서식 시트가 실린다.
43
33
  스타일이 안 맞는 문서가 검사를 통과해 「거짓 초록」이 된다.
44
34
 
45
35
  실측(스킬 동봉 예시 · 절 5 · 건 2): 폰트 임베드 1면 409,044B → base64 545,392B · 산출물 659,164B · ⚠ 0건.
46
36
 
47
37
  ---
48
38
 
49
- ## 1. 0단계 — 독자를 먼저 못 박는다
39
+ ## 0단계 — 독자를 먼저 못 박는다
50
40
 
51
- **이 스킬에서 유일하게 반드시 사용자에게 확인해야 하는 단계다.**
41
+ **이 스킬에서 반드시 사용자에게 확인해야 하는 것은 이 단계와 공개 승인(10단계) 둘이다.**
52
42
  나머지는 스스로 판단해도 된다.
53
43
 
54
44
  ### 묻는 것
@@ -56,10 +46,11 @@ python3 ~/.claude/skills/genetics-report/assets/render.py report.json
56
46
  | 질문 | 왜 이걸 묻는가 | 예시 답 |
57
47
  |---|---|---|
58
48
  | 이 문서를 **읽고 판정하는 사람**은 누구인가 — 직함이 아니라 *하는 일*로 | 직함(「팀장」)에서는 어휘 정책이 안 나온다. 행위(「검증한다」)에서는 나온다 | 「고객사 검수 담당 — 우리 작업이 실제로 해결됐는지 확인하는 사람」 |
59
- | 읽고 나서 **해야 할 행동**은 무엇인가 | `cta`·`open_items`·건별 `how_to_check` 가 여기서 결정된다 | 「각 건을 한 번씩 열어 확인하고, 남은 판단 사항에 답을 준다」 |
49
+ | 읽고 나서 **해야 할 행동**은 무엇인가 — 할 일이 둘이면 둘 다 목록으로 | `cta`·`open_items`·건별 `how_to_check` 가 여기서 결정된다. 할 일 목록은 독자 검토 `args.reader` 에 그대로 싣는다 | 「① 결정 물음에 회신한다 ② 시험판에서 확인한다」 |
60
50
  | 문서에 실어도 되는 **참조 체계**는 무엇인가 | 독자가 확인할 수 없는 참조는 신뢰에 기여하지 않고 노이즈만 된다 | 「본인이 제보한 원본 링크만. 내부 티켓 번호는 안 됨」 |
61
51
  | 전달 경로 — 포털 링크인가, 파일 하나인가, 인쇄도 도는가 | 포털은 셸이 문서 둘레에 요약·댓글을 놓는다. 파일 전달은 그것이 없다 | 「포털 링크 + 인쇄 가능해야 함」 |
62
52
  | 읽고 나서 **무엇을 결정하는가** — 선택지·선택지마다 달라지는 것·기한. 우리 의견을 낼지는 따로 묻는다 | 답이 있으면 「결정한다」 모드다. `open_items` 가 고객이 고를 **물음**이 되고 기한 ⚠ 가 걸린다. 의견은 내면 한 절 한 자리(lim-portal-format §12.3b) | 「[방식 A] 를 들일지, 그대로 둘지 — 12월 31일까지」 |
53
+ | **이미 정해진 것**은 무엇이고 **누가·어디서** 정했는가(체크리스트 0a) | 정해진 것은 사실로 배경에 서고 결정 요청에서 빠진다 — 주체와 기록을 확인하지 않고 쓰면 열린 항이 닫힌 사실로 실리거나, FCG 가 대화에서 정한 것이 고객 결정처럼 읽힌다(SKILL 3b 「결정 주체」). 작업 폴더 `decisions.json`(발행 안 함): 항 · 내용 · 결정권(고객/FCG) · 정한 주체 · 기록(회신 원문·회의록·결정문의 이름과 날짜) · 사용자 확인 여부. 미확인 항은 원고에 쓰기 전에 한 번에 묻는다 | 「화면 문구 교체 — 결정권 고객 · 고객 회신 메일(날짜) · 확인함」 |
63
54
 
64
55
  ### 이 답에서 곧바로 정해지는 것
65
56
 
@@ -72,21 +63,19 @@ python3 ~/.claude/skills/genetics-report/assets/render.py report.json
72
63
  전 건 검수자에게는 `status` 절의 대조표가 핵심이지만, 결정권자 독자에게는 요약면 상태 막대만 남기고 대조표를 빼는 것이 맞다.
73
64
  - **말을 거는 자리의 종결** — 독자가 **사람**이면 그 사람에게 직접 하는 말은 하십시오체로 맺는다
74
65
  (`cta`·`open_items`·PART I 간지·판권지 회신 안내). 우리가 한 일을 적는 기록 자리만 개조식이다.
75
- ⚠ 이 줄이 오래 없어서 **말투 반려가 네 번 났다**(2026-09-02 · 09-03 ×2 · 09-11) —
76
66
  독자를 사람으로 정해 놓고 문체는 상수로 두면, 문서가 사람이 아니라 대장에게 말한다.
77
- 자세한 것은 SKILL 5·5a.
67
+ 자세한 것은 SKILL 5·5a(경위: `references/history.md` §5).
78
68
 
79
69
  | 규칙 | 근거 | 어기면 |
80
70
  |---|---|---|
81
71
  | 독자를 문장 하나로 확정하기 전에는 한 건도 쓰지 않는다 | 어휘 정책·PART·절 구성이 전부 여기서 파생된다 | 전 건 재집필. 18건이면 18건 전부 |
82
72
  | 독자를 「직함」이 아니라 「하는 일」로 받는다 | 하는 일이 정해져야 「이 사람이 확인할 수 있는가」를 매 문장에서 판정할 수 있다 | 매 건 즉흥 판단이 되고 문서 안에서 어휘가 흔들린다 |
83
73
 
84
- 독자를 안 정하고 만들면 **개발 로그에 조판만 입힌 물건**이 나온다.
85
- 실제로 한 번 그렇게 만들었고, 「개발팀 내부적인 작업 시나리오까지 섞어서 보고서로 보내고 있다」는 지적을 받았다.
74
+ 독자를 안 정하고 만들면 **개발 로그에 조판만 입힌 물건**이 나온다(경위: `references/history.md` §2).
86
75
 
87
76
  ---
88
77
 
89
- ## 2. 1단계 — 자료 수집
78
+ ## 1단계 — 자료 수집
90
79
 
91
80
  | 규칙 | 근거 | 어기면 |
92
81
  |---|---|---|
@@ -96,14 +85,14 @@ python3 ~/.claude/skills/genetics-report/assets/render.py report.json
96
85
  | 링크는 **독자가 아는 것만** 남긴다 | 독자가 조회할 수 없는 주소는 확인 수단이 아니라 노이즈다 | 문서가 독자가 아니라 자기 조직을 향해 말한다 |
97
86
 
98
87
  `source_url` 은 `http(s)` 로 시작해야 한다.
99
- 아니면 **발행이 거절된다**(`report_publish.php:194-197`).
88
+ 아니면 **발행이 거절된다**(`report_publish.php report_data_validate()`).
100
89
 
101
90
  **묻는다**: 원자료 소스와 범위(기간·상태 필터), 어떤 링크가 「고객이 아는 참조」인지.
102
91
  **판단한다**: 병렬 분할, 필드 추출, 어느 티켓이 한 건으로 합쳐지는지.
103
92
 
104
93
  ---
105
94
 
106
- ## 3. 2단계 — 건별 정리 (한 건 = 한 레코드)
95
+ ## 2단계 — 건별 정리 (한 건 = 한 레코드)
107
96
 
108
97
  `cases` 는 객체 배열이다.
109
98
 
@@ -147,9 +136,9 @@ source_url 제보 원본 링크 (없으면 생략. http(s) 필수)
147
136
 
148
137
  ### ⚠ `text_fix` 치환표는 관보에서 **안 돈다** (코드 확인 · 옛 문서와 어긋남)
149
138
 
150
- `text_fix` 는 `build.py:516` 에만 있고, 그 줄은 **옛 「수정 내역 보고」 경로 안**이다.
151
- 관보 경로 `build_gazette()`(`build.py:421-454`)는 그 코드를 지나지 않고, `gazette.py` 에는 `text_fix` 라는 낱말이 아예 없다.
152
- 서버는 그 키를 화이트리스트에 넣어 **받기만 한다**(`report_publish.php:45`) — 설정에 실어 두고 아무도 안 읽는다.
139
+ `text_fix` 는 `build.py build()` 안에만 있고, 그 자리는 **옛 「수정 내역 보고」 경로 안**이다.
140
+ 관보 경로 `build.py build_gazette()` 는 그 코드를 지나지 않고, `gazette.py` 에는 `text_fix` 라는 낱말이 아예 없다.
141
+ 서버는 그 키를 화이트리스트(`REPORT_CLIENT_KEYS`)에 넣어 **받기만 한다**(`report_publish.php report_client_fields()`) — 설정에 실어 두고 아무도 안 읽는다.
153
142
 
154
143
  실측(2026-08-24): `{"from":"레코드","to":"항목"}` 을 넣고 원고에 「레코드」를 심어 렌더했더니 **「레코드」가 그대로 산출물에 남았다.**
155
144
 
@@ -158,7 +147,7 @@ source_url 제보 원본 링크 (없으면 생략. http(s) 필수)
158
147
 
159
148
  ---
160
149
 
161
- ## 4. 3단계 — 등급 부여 (독립 단계로 분리한다)
150
+ ## 3단계 — 등급 부여 (독립 단계로 분리한다)
162
151
 
163
152
  검수 독자가 가장 먼저 보는 축이다.
164
153
  「이걸 너희는 어디까지 확인했는가」에 답하는 장치다.
@@ -167,7 +156,7 @@ source_url 제보 원본 링크 (없으면 생략. http(s) 필수)
167
156
 
168
157
  각 건의 `fix`·`how_to_check` 본문을 읽고, **본문에 실제로 적혀 있는 근거만으로** 분류한다.
169
158
 
170
- 예시(4단계, 강한 순):
159
+ 예시(네 등급, 강한 순):
171
160
 
172
161
  | 등급 | 뜻 (범례로 문서에 함께 실린다) | 붙는 조건 |
173
162
  |---|---|---|
@@ -189,7 +178,7 @@ source_url 제보 원본 링크 (없으면 생략. http(s) 필수)
189
178
 
190
179
  ### `grades` 는 **키 순서가 강도 순서**다
191
180
 
192
- `gazette.py:80-83` 이 주석으로 「스킬 2단계가 계약」이라고 물고 있다.
181
+ `gazette.py` 의 `RANK_LVL`·`RANK_TAG` 머리 주석이 「스킬 3단계가 계약」이라고 물고 있다(체크리스트 3단계 — 옛 문서의 「2단계」가 이것이다).
193
182
  자리로 세기를 정한다 — JSON 객체의 키 순서가 그대로 강한 순이다.
194
183
 
195
184
  ```
@@ -197,7 +186,7 @@ RANK_LVL = ['full', 'half', 'open', 'open'] # 대조표의 강도 표식
197
186
  RANK_TAG = ['live', 'data', 'done', 'soon'] # 배지 종류
198
187
  ```
199
188
 
200
- 실측으로 나오는 두 가지 한계(`gazette.py:751-762`).
189
+ 실측으로 나오는 두 가지 한계(`gazette.py Doc._grade()`).
201
190
 
202
191
  - **강도 표식은 3단까지만 갈린다.** 3번째와 4번째가 둘 다 `open` 이다.
203
192
  - **5번째 등급부터는 전부 최약 표식으로 눌린다**(`table[-1]`). `grades` 에 없는 이름을 건에 적어도 같다.
@@ -210,10 +199,18 @@ RANK_TAG = ['live', 'data', 'done', 'soon'] # 배지 종류
210
199
 
211
200
  ---
212
201
 
213
- ## 5. 4단계 — 요약·확인 요청 집필
202
+ ## 4단계 — 요약·확인 요청 집필
214
203
 
215
204
  건별 내용이 아니라 **문서의 판단 층**이다.
216
205
 
206
+ ### 코드 작업을 녹일 때 — 상태 표를 먼저 만든다
207
+
208
+ 원고를 쓰기 전에 핸드오프에 **상태 표**(무엇 · 코드 반영 · 시험판 전달 · 실기기 · 운영, 칸마다 근거)를 만들고, 문서의 동사는 이 표의 확인 수준을 넘지 않게 고른다.
209
+ 반영 단계 어휘는 `references/content-pipeline.md` 「4.2 치환 유형」 B 행을 따른다. 실기기 확인이 없으면 같은 문장에 그렇다고 적는다.
210
+ 판정 규칙은 이용자가 확인할 수 있는 동작으로만 적고, 구현 규칙(어느 키로 판정하는지)은 싣지 않는다.
211
+ **커밋·브랜치·파일·시험 도구·시험 건수는 이 표에만** 둔다 — 원고에 새면 `gazette.py` `text_scan()` 개발 식별자 ⚠(두 경로 — ⚠ 이 행은 운영 반영 확인 전이라 배포 전에는 로컬 렌더에만 있다, lim-portal-format §10-1 머리).
212
+ 확인 수준보다 앞선 완료형 동사는 기계가 못 가린다 — 독자 검토에 `args.state` 로 이 표를 주면 state 관점이 대조하고, 나머지는 사람의 눈(SKILL 반드시 지키는 것 3).
213
+
217
214
  ### 문체
218
215
 
219
216
  **자리가 정한다.** 요약·건·절 도입문처럼 **우리가 한 일을 적는 기록 자리**는 개조식
@@ -221,18 +218,20 @@ RANK_TAG = ['live', 'data', 'done', 'soon'] # 배지 종류
221
218
  **하십시오체**(`~하여 주시기 바랍니다` / `~부탁드립니다` / `~하겠습니다`).
222
219
  자리별 규율은 SKILL 5·5a, 대응표는 `references/lim-portal-format.md` §14-1·§14-2.
223
220
 
224
- > · 개발 용어(파일명·커밋해시·SQL·클래스명) 금지. **배포 단계 용어(스테이징/테스트환경) 금지.**
221
+ > · 개발 용어(파일명·커밋해시·SQL·클래스명) 금지. **배포 단계 용어(스테이징/테스트환경) 금지** — 반영 단계는 위 「코드 작업을 녹일 때」의 어휘로.
225
222
  > · 결론을 먼저 쓴다. 각 문단은 리드 문장 하나로 시작한다.
226
223
  > · 과장 금지. 확인한 것과 확인하지 못한 것을 구분해서 쓴다.
227
224
 
228
225
  기록 자리에서 문체를 안 바꾸면 낱말이 아니라 **조판의 문제**로 남는다 — 경어체 서술문은
229
226
  관보 문법 위에서 겉돈다. 반대로 요청 자리에서 개조식을 고집하면 **하대로 읽힌다** — 이쪽이
230
- 네 번 반려된 자리다(SKILL 5a). `tone.mjs` 는 판단 항목 본문(`p.ask__body`)과 「」 인용만 빼고 세므로(2026-09-12),
231
- `cta`·간지·판권지 회신 안내 같은 다른 요청 자리의 경어체 경고는 **사람이 걸러 읽고 그대로 둔다**.
227
+ 네 번 반려된 자리다(SKILL 5a). 서버 `report_tone.php report_tone_warnings()` 는 요청 자리(`open_items` 본문·`cta`·판권지 `note`·`asks` 절 여백 주석)와 「」 인용을 안 세고,
228
+ 로컬 `tone.mjs` 는 판단 항목 본문(`p.ask__body`)과 「」 인용만 뺀다. 그래서 PART I 간지의 요청 문장(두 경로)과 로컬의 `cta`·판권지 회신 안내에서 나는 경어체 경고는 **사람이 걸러 읽고 그대로 둔다**.
229
+ ⚠ 서버 쪽 자리 가림은 **운영 반영 확인 전**이다 — 배포 전의 운영 `dry_run` 은 요청 자리에서도 경어체를 세므로, 그 경고는 자리를 확인하고 넘긴다(lim-portal-format §10 통과 기준 표 셋째 행).
232
230
 
233
- ### `summary` — 4개의 `{lead, body, refs}`
231
+ ### `summary` — 검수·보고판 4개의 `{lead, body, refs}`
234
232
 
235
- `lead` 는 결론 한 문장, `body` 는 2~4문장.
233
+ 요지 칸 수의 정본은 SKILL.md 4~6단계 「요지」 한 곳이다 — 여기는 그 판마다 칸에 무엇을 쓰는지만 푼다.
234
+ 검수·보고판: `lead` 는 결론 한 문장, `body` 는 2~4문장.
236
235
  **건별 나열 금지** — 검수자가 요약면에서 판단을 끝낼 수 있어야 한다.
237
236
 
238
237
  1. 접수된 N건이 어떤 성격이었고 지금 어떤 상태인가
@@ -240,12 +239,22 @@ RANK_TAG = ['live', 'data', 'done', 'soon'] # 배지 종류
240
239
  3. 여러 건을 관통하는 **공통 원인**이 있다면 그것 — **실재할 때만 쓴다.** 없는데 만들어 내면 그게 이 문서에서 가장 나쁜 슬롭이다
241
240
  4. 검토 요청 — 무엇을 봐 주면 되는지
242
241
 
243
- 「결정한다」 모드에서는 4칸의 내용이 다르다 — 무엇을 언제까지 결정하는가 / 검토한 선택지와 편익·비용·불확실성 /
244
- 확인한 사실과 근거의 등급·성숙도(권고 없음 — 의견은 별도 절 한 자리) / 결정 뒤 이어지는 것(SKILL.md 4단계 브리핑판).
242
+ 「결정한다」판은 4~6문단이고 순서가 다르다 — 무엇을 언제까지 결정하는가 / 검토한 선택지와 편익·비용·불확실성 /
243
+ 확인한 사실과 근거의 등급·성숙도(권고 없음 — 의견은 별도 절 한 자리) / 결정 뒤 이어지는 것(SKILL.md 4~6단계 브리핑판).
244
+ 결정에 직접 닿는 문단만 `body`(세 문장 안)를 쓰고, 근거 문단은 `lead` 한 줄 + `refs` 로 두고 `body` 를 비운다 — 본문은 건이 가진다(readability R12g).
245
+ 집행자: 독자 검토 structure 관점 · 사람 눈 — 기계 없음.
246
+
247
+ ### 확인 프로필 표 — 시험판·검수에서 무엇을 넣어 보면 되는가
248
+
249
+ 결정 문서의 둘째 할 일(시험판 확인 요청)에는 확인 프로필 표를 곁에 둔다. 모양의 정본은 `lim-portal-format.md` 「12.3b 브리핑 서사 공식」이고, 여기는 싣기 전 확인만 적는다.
250
+ - 경계마다 포함 쪽·제외 쪽 두 행을 둔다(≤·< 가 행 이름에 드러나게). 날짜로 정해지는 경계는 확인일 기준 상대값과 계산 예 하나.
251
+ - 각 행을 **판정 코드나 정의로 확인한 뒤** 싣는다 — 문서의 나이·키 같은 정의가 앱의 정의(만 나이/세는 나이 등)와 같은지 먼저 본다.
252
+ - 앱에서 만들 수 없는 조건은 행을 지우지 않고 대안을 적는다.
253
+ 집행자: 사람의 눈(7단계 표 「확인 프로필 표」 행) · 독자 검토 facts 관점(`args.sources` 에 코드를 주면) — 기계 없음.
245
254
 
246
255
  ### 요약 근거 게이트 — 발행이 **거절**된다
247
256
 
248
- `report_publish.php:260-287` 이 짝을 이룬 두 검사를 건다.
257
+ `report_publish.php report_data_validate()` 가 짝을 이룬 두 검사를 건다.
249
258
 
250
259
  | 검사 | 무엇을 보는가 | 어기면 |
251
260
  |---|---|---|
@@ -263,6 +272,8 @@ RANK_TAG = ['live', 'data', 'done', 'soon'] # 배지 종류
263
272
 
264
273
  건별 `remaining` 을 **성격별로 묶는다.**
265
274
  건별로 다시 나열하지 않는다.
275
+ 요청 절이 없는 문서가 확인 못 한 것을 `note` 절에 모을 때도 같다 — 건의 `remaining` 을 문단마다 옮겨 적지 않고
276
+ 성격(작업자 미상 · 기록 없음 · 현장에서만 가려짐 같은)으로 묶고, 건은 번호로 가리킨다.
266
277
 
267
278
  > 이건 고객 숙제 목록이 아니라 **우리가 확정하지 못한 것을 밝히는** 절이다.
268
279
  > 주어를 우리로 써라.
@@ -271,6 +282,8 @@ RANK_TAG = ['live', 'data', 'done', 'soon'] # 배지 종류
271
282
  같은 데이터를 「고객이 해야 할 일」로 쓰면 책임 전가 문서가 되고, 「우리가 아직 못 닫은 것」으로 쓰면 신뢰 문서가 된다.
272
283
  그래서 검수·보고 문서의 `open_items[].title` 은 전부 1인칭이다 — `검증에 쓴 표본이 작음`, `같은 전화번호를 쓰는 계정의 주인은 우리가 판단할 수 없음`.
273
284
  결정 문서(브리핑)의 판단 항목은 다르다 — 고객이 고를 **물음**이다(SKILL 0.5단계 5행 · lim-portal-format §12.5b).
285
+ 독자의 할 일이 둘이면(결정 + 시험판 확인) 둘째는 `open_items` 두 번째 건 「… 확인 요청」으로 두고 본문 첫 문장에 상대 기한을 넣는다 —
286
+ 표지·현황 줄에는 사실문으로만 올리고, 읽는 법(`guide`)에 요청문을 두지 않는다(보고 자리, SKILL 5a). 집행자: `gazette.py` `brief_checks()` 기한 ⚠(「확인 요청」 문구로 갈림) · 독자 검토 decider 관점.
274
287
 
275
288
  | 규칙 | 어기면 |
276
289
  |---|---|
@@ -283,19 +296,19 @@ RANK_TAG = ['live', 'data', 'done', 'soon'] # 배지 종류
283
296
  절 참조는 원고에 `§3.1` 로 쓴다.
284
297
  렌더러의 `link()` 가 앵커로 바꾸며 `§` 기호를 버린다 — `§` 는 법령·논문 기호이지 보고서 활자가 아니다.
285
298
 
286
- `link()` 를 안 거친 자리에 `§n.n` 이 남으면 **렌더가 중단된다**(`build.py:390-393`).
299
+ `link()` 를 안 거친 자리에 `§n.n` 이 남으면 **렌더가 중단된다**(`build.py guard()`).
287
300
  제목·라벨·표지 메타는 `link()` 를 안 지난다 — 거기 절 참조를 쓰지 마라.
288
301
 
289
302
  ---
290
303
 
291
- ## 6. 5단계 — 여백 주석(`marg`)을 채운다
304
+ ## 5단계 — 여백 주석(`marg`)을 채운다
292
305
 
293
306
  **여기가 관보의 본체다.**
294
307
  옛 절차의 이 자리에는 팔레트 고르기가 있었다.
295
- 관보에는 팔레트가 없다(`build_gazette` 가 `styles(..., tokens=False)` 로 부른다 — `build.py:441`).
308
+ 관보에는 팔레트가 없다(`build.py build_gazette()` 가 `styles(..., tokens=False)` 로 부른다).
296
309
  고를 색이 없어진 자리를 **채울 문장**이 대신한다.
297
310
 
298
- `marg` 가 비면 렌더러가 경고한다(`gazette.py:400-402`).
311
+ `marg` 가 비면 렌더러가 경고한다(`gazette.py Doc.marg()`).
299
312
 
300
313
  ```
301
314
  절 03 「처리 현황」에 여백 주석(marg.why/key/advice)이 없습니다 — 두 단 본문이 한 단으로 섭니다
@@ -316,11 +329,12 @@ RANK_TAG = ['live', 'data', 'done', 'soon'] # 배지 종류
316
329
  셋 중 채운 것만 그려진다.
317
330
  하나도 없으면 `<aside class="marg">` 자체가 안 나오고 본문이 한 단이 된다.
318
331
 
319
- ### 나쁜 `marg` 의 모양 넷
332
+ ### 나쁜 `marg` 의 모양 다섯
320
333
 
321
334
  | 모양 | 왜 나쁜가 |
322
335
  |---|---|
323
336
  | 본문 첫 문장을 그대로 옮긴 `key` | 같은 것이 두 번 있게 된다. 여백 단은 요약란이 아니라 **다른 층위**다 |
337
+ | 다른 절 `key` 와 같은 명제인 `key` | 두 절에서 가져갈 것이 같다는 뜻이다. 한쪽이 자기 kind 의 몫(아래 표)을 안 적었거나 두 절이 사실은 한 절이다. 낱말이 달라도 명제가 같으면 같은 key 다 — 글자 비교로는 안 잡힌다(2026-09-12 실측: 같은 명제 두 key 의 글자 2-gram 유사도 0.09) |
324
338
  | 「이 절에서는 …을 설명합니다」류의 `why` | 독자의 질문이 아니라 저자의 목차다. 독자는 그 질문을 던지지 않는다 |
325
339
  | 어느 절에나 붙는 `advice` (「자세한 내용은 본문 참조」) | 가리키는 곳이 없으면 안 쓰느니만 못하다. **실제 절 번호**를 대라 |
326
340
  | 채우려고 넣은 세 줄 | 세 칸을 다 채우는 것이 목표가 아니다. 할 말이 둘이면 둘만 쓴다 |
@@ -346,11 +360,11 @@ RANK_TAG = ['live', 'data', 'done', 'soon'] # 배지 종류
346
360
 
347
361
  ---
348
362
 
349
- ## 7. 6단계 — 조립
363
+ ## 6단계 — 조립
350
364
 
351
365
  ### 골격 순서는 바꾸지 않는다
352
366
 
353
- `gazette.py:898-906` 이 순서를 고정한다.
367
+ `gazette.py Doc.build()` 가 순서를 고정한다.
354
368
 
355
369
  ```
356
370
  표지 → 발행 이력 → 차례 → 이 문서 읽는 법 → 요약
@@ -361,7 +375,7 @@ RANK_TAG = ['live', 'data', 'done', 'soon'] # 배지 종류
361
375
 
362
376
  ### PART 를 먼저 가른다
363
377
 
364
- `parts` 가 없으면 경고 뒤 절 전체가 한 묶음으로 선다(`gazette.py:843-847`).
378
+ `parts` 가 없으면 경고 뒤 절 전체가 한 묶음으로 선다(`gazette.py Doc.build()`).
365
379
  그러면 「먼저 읽을 것 / 경과 / 남은 것」이라는 **읽기 순서가 사라진다.**
366
380
 
367
381
  가르는 기준은 절의 성격이 아니라 **독자가 무엇을 해야 하는가**다.
@@ -372,16 +386,23 @@ RANK_TAG = ['live', 'data', 'done', 'soon'] # 배지 종류
372
386
  | 2 경과 | 읽고 확인할 것 | `summary` · `status` · `cases` |
373
387
  | 3 남은 것 | 알아 둘 것 | `note` |
374
388
 
389
+ **`summary` · `status` · `cases` 는 한 묶음이다.** 셋은 같은 건을 요지 · 대조표 · 건 카드 세 배율로 보이는 것이라
390
+ 서로 다른 PART 에 두지 않는다. 한 사건의 시간(경과 표)과 까닭(건)을 「경과 / 원인」 두 PART 로 가르면
391
+ 앞 PART 의 요지·대조표·여백 주석이 뒤 PART 간지의 물음에 먼저 답하고, 같은 사실이 PART 이름만 바꿔 두 번 선다.
392
+ 요청(`asks`)이 없는 문서(회신 없는 정황 공유)는 1 요청만 빼고 「경과 / 남은 것」으로 가른다 — 원인은 경과 PART 안의 건이 맡는다.
393
+ 예외는 결정한다 모드 하나다 — 요지를 결정 PART 에, 건을 근거 PART 에 두는 것은 `asks` 가 곁에 있어 요지가 결정의 배경이 될 때뿐이다.
394
+ 집행자: 독자 검토 「구조」 관점(`references/reader-review.workflow.js`) · 사람의 눈(7단계 표 「PART 간지」 행) — 렌더러 ⚠ 없음(경위: `references/history.md` §9).
395
+
375
396
  절이 하나뿐인 묶음도 간지를 둔다 — 간지는 장식이 아니라 층위 표시다.
376
397
 
377
398
  ### `kind` 는 다섯뿐이다
378
399
 
379
400
  `summary` · `status` · `cases` · `asks` · `note`.
380
- 그 밖의 값은 **서버가 거절한다**(`REPORT_RENDER_KINDS`, `report_publish.php:96·172-175`).
401
+ 그 밖의 값은 **서버가 거절한다**(`REPORT_RENDER_KINDS`, `report_publish.php report_data_validate()`).
381
402
  거절하는 이유는 완성도가 아니라 손실이다 — 모르는 `kind` 의 절은 렌더러가 아무 말 없이 통째로 빠뜨린다.
382
403
 
383
404
  `tone` 은 여섯이다(`you`·`deep`·`neutral`·`done`·`wait`·`hold`).
384
- 목록 밖의 값은 **조용히 `neutral` 로 강등된다**(`gazette.py:801-802`) — 기계가 안 잡으니 눈으로 본다.
405
+ 목록 밖의 값은 **조용히 `neutral` 로 강등된다**(`gazette.py Doc.build()`) — 기계가 안 잡으니 눈으로 본다.
385
406
  생략하면 `kind` 가 정한다.
386
407
 
387
408
  ### 렌더러가 자동으로 하는 것
@@ -393,13 +414,13 @@ RANK_TAG = ['live', 'data', 'done', 'soon'] # 배지 종류
393
414
  | 사례 번호 계산 | `groups[].no` 아래로 1부터 | 번호를 손으로 적지 않는다 |
394
415
 
395
416
  **긴 산문의 문단 분절은 자동으로 안 된다.**
396
- 옛 경로의 `chunk()` 는 관보 렌더러에 안 넘어간다(`build.py:440` 이 `{'e', 'link'}` 만 준다).
417
+ 옛 경로의 `chunk()` 는 관보 렌더러에 안 넘어간다(`build.py build_gazette()` 가 `e`·`link`·`mark` 만 준다).
397
418
  문단은 원고에서 끊는다.
398
419
 
399
420
  ### 관보에 자리가 없는 칸
400
421
 
401
- `chapters`(챕터 오프닝) · `figure`(요약 도형) · `principles` · `logic` · `assets` 중 `role != "logo"` 인 것.
402
- 전부 **세고 버린다**(`gazette.py:878-896`).
422
+ `chapters`(챕터 오프닝) · `figure`(요약 도형) · `principles` · `logic` · `assets` 중 `role` 이 `logo`·`figure` 밖인 것 · 어느 절 `plates` 에도 안 걸린 도판.
423
+ 전부 **세고 버린다**(`gazette.py Doc.build()`).
403
424
  보낸 사람이 실린 줄 알면 안 되므로 ⚠ 로 소리 내어 센다.
404
425
 
405
426
  `assets[].brief` 를 주면 **서버가 진짜로 그린다** — 크레딧이 타고 렌더러가 버린다.
@@ -411,45 +432,48 @@ RANK_TAG = ['live', 'data', 'done', 'soon'] # 배지 종류
411
432
  |---|---|---|
412
433
  | 서버 검증 | 모르는 `kind` · 없는 그룹 · 없는 PART · 요약 근거 · `source_url` 모양 · 목록/객체 모양 | **발행 거절** |
413
434
  | 렌더러 assert | `link()` 를 안 거친 `§n.n` · `forbid` 정규식 적중 | **렌더 중단(파일 안 씀)** |
414
- | 렌더러 ⚠ | `marg` 없음 · `parts` 없음 · 그룹 미배정 건 · 자리 없는 그림 | 경고만 |
435
+ | 렌더러 ⚠ | `marg` 없음 · `parts` 없음 · 그룹 미배정 건 · 자리 없는 그림 · 시각 우선·글 층·도판 출처 줄·그림 번호·개발 식별자(`lim-portal-format.md` 「10. 경고 조건」) | 경고만 |
415
436
 
416
437
  ### ⚠ `forbid` 는 **로컬 렌더에서만** 돈다 (코드 확인)
417
438
 
418
- `guard()`(`build.py:394-396`)가 `cfg['forbid']` 를 읽어 산출물을 훑고, 걸리면 assert 로 죽는다.
439
+ `build.py guard()` 가 `cfg['forbid']` 를 읽어 산출물을 훑고, 걸리면 assert 로 죽는다.
419
440
  실측(2026-08-24): 금지어 「티켓」을 원고에 심고 `render.py` 를 돌리면 `AssertionError: 금지 표현 유출: 티켓 → ['티켓']` 으로 멈추고 파일이 안 써진다.
420
441
 
421
442
  그런데 포털 발행에서는 이 그물이 없다.
422
- `forbid` 는 `REPORT_CLIENT_KEYS` 에 없어서 `report_client_fields()` 가 **깎아 낸다**(`report_publish.php:76` 이 「서버만 정한다」고 명시).
443
+ `forbid` 는 `REPORT_CLIENT_KEYS` 에 없어서 `report_publish.php report_client_fields()` 가 **깎아 낸다**(주석이 「서버만 정한다」고 명시).
423
444
  서버는 그 값을 따로 안 채운다 — 빈 채로 렌더러에 닿는다.
424
445
 
425
446
  포털 발행이 최종 경로라도 **발행 전에 같은 데이터를 로컬로 한 번 구워서 `forbid` 그물을 통과시켜라.**
426
- 그것이 지금 유일한 자동 어휘 관문이다.
447
+ 고객사·제품명처럼 문서마다 다른 금칙 값의 자동 관문은 지금 이것뿐이다 — 개발 식별자·과정 어휘는 두 경로 다 `gazette.py` `text_scan()` ⚠ 가 센다(⚠ 2026-09-14 에 더한 행은 운영 반영 확인 전 — 배포 전에는 로컬 렌더만, lim-portal-format §10-1 머리).
427
448
 
428
449
  ---
429
450
 
430
- ## 8. 7단계 — 검수
451
+ ## 7단계 — 검수
431
452
 
432
- 관문은 셋이고, 셋 다 다른 것을 본다.
453
+ 관문은 넷이고, 넷 다 다른 것을 본다. 순서는 SKILL.md 「작업 체크리스트」 7단계 행(로컬 렌더 → dry_run → 조판 검사 → 독자 검토 → 사람 눈)이고, 조판 검사 8종은 단일 HTML 로 전달할 때 돈다.
433
454
 
434
455
  | 층 | 무엇을 보는가 | 통과 기준 |
435
456
  |---|---|---|
436
- | `dry_run` warnings | 무엇이 **안 실렸는가** | ⚠ 0건 |
457
+ | `dry_run` warnings(로컬 렌더 ⚠ 포함) | 무엇이 **안 실렸는가** · 규칙 낱말·모양 | 작성자가 고칠 수 있는 ⚠ 0건 |
437
458
  | 검사기 8종 | 조판이 **성립했는가** | 각 검사기의 0건 |
459
+ | 독자 검토(`reader-review.workflow.js`) | 처음 읽는 사람·결정권자가 **읽히는가** · 도판을 열어 잰 틀 · 1차 자료와 맞는가 | Verify 를 통과한 발견 반영 · 「⚠ 도판 N장 미열람」 없음(SKILL 0.5단계 7행) |
438
460
  | 사람의 눈 | 이 문서가 **이 독자에게 맞는가** | 판단 |
439
461
 
440
462
  ### `dry_run` 이 실질적 유일한 품질 관문이다
441
463
 
442
464
  `publish_report(dry_run: true)` 는 렌더까지 돌리고 아무것도 저장하지 않는다.
443
- 돌아오는 `warnings` 는 세 곳에서 모인다(`report_publish.php:1100-1108`).
465
+ 돌아오는 `warnings` 는 네 곳에서 모인다(`report_publish.php report_build_checked()`).
444
466
 
445
- 1. **렌더러가 센 것** — `marg` 없음 · `parts` 없음 · 그룹 미배정 건 · 자리 없는 그림. 이것이 먼저 온다.
446
- 2. **입고 검사** — 렌더 산출물이 발행 규격을 지켰는가.
467
+ 1. **렌더러가 센 것** — `marg` 없음 · `parts` 없음 · 그룹 미배정 건 · 자리 없는 그림 · `text_scan()`·`brief_checks()`·`tally_scan()` 경고. 이것이 먼저 온다.
468
+ 2. **입고 검사** — 렌더 산출물이 발행 규격을 지켰는가(본문 링크의 바깥 호스트도 여기서 센다).
447
469
  3. **그림 관문** — 자산 재사용 등.
470
+ 4. **문체 관문** — `report_tone.php report_tone_warnings()`: 기록 자리 경어체 · 상대 행위의 명령형 · 보고 칸의 요청문. 맨 뒤에 온다.
448
471
 
449
472
  2026-08-18 이전에는 이 값이 **한 번도 도구에 도달하지 않았다**(서버가 `error_log` 로만 흘렸다) — 에이전트는 「경고 없음」으로 읽었고 확인할 방법이 없었다.
450
473
  지금은 dry_run 과 실제 발행 응답 **양쪽에** 실린다.
451
474
 
452
- ⚠ 가 하나라도 있으면 그것부터 고친다.
475
+ ⚠ 가 하나라도 있으면 그것부터 고친다. 작성자가 없앨 수 없는 경고(소속처 로고 파생본·본문에 실은 원본 링크의 바깥 호스트·SKILL 「Lim 발행 규칙」이 의도라 적은 요청 자리 경어체)는
476
+ 줄마다 까닭을 핸드오프에 적는다 — 경고 기준선을 기계로 대조하는 도구는 아직 없어, 새 경고가 그 속에 묻히지 않게 사람이 판마다 대조한다.
453
477
  경고를 안고 고객에게 링크를 보내지 마라.
454
478
 
455
479
  ### 검사기 8종
@@ -461,7 +485,7 @@ RANK_TAG = ['live', 'data', 'done', 'soon'] # 배지 종류
461
485
  |---|---|
462
486
  | `kcheck.mjs` | 대비(알파·조상 opacity 합성) · 제목 레벨 · 표 접근명 · 앵커 · 7폭 넘침 |
463
487
  | `read.mjs` | 한글 자폭 실측 글자/줄 · 행간 · 본문 대비 |
464
- | `tone.mjs` | 경어체 잔존 · 늘어짐 지표 · 한 문장 한 줄 |
488
+ | `tone.mjs` | 경어체 잔존 · 늘어짐 지표 · **한 문장 한 줄** · 문체 유형 4종(학생 보고서투·완충어·근거 없는 총평·단독 부정 종결) |
465
489
  | `fontchk.mjs` | 렌더된 활자 크기·굵기 분포 |
466
490
  | `flexcheck.mjs` | flex/grid 안에서 문장이 토막 나는 곳 |
467
491
  | `cssaudit.sh` | 활자 스케일 · 간격 격자 · 표 마감 · 미봉책 |
@@ -500,8 +524,8 @@ RANK_TAG = ['live', 'data', 'done', 'soon'] # 배지 종류
500
524
  | 표지 가운데 | 빈 마당이 그대로인가. **채우지 마라** — 그것이 문법이다 |
501
525
  | 차례 | 항목 수가 절 수와 맞는가. 죽은 링크가 없는가 |
502
526
  | 이 문서 읽는 법 | 질문형이 `~합니까?` 로 새지 않았는가 |
503
- | 요약면 | `notice.state` 가 실제 상태와 맞는가. 상태 막대 라벨이 무엇을 세는지 `bar_note` 가 밝히는가 |
504
- | PART 간지 | 인쇄에서 각각 1쪽 단독으로 서는가 |
527
+ | 요약면 | `notice.state` 가 실제 상태와 맞는가. 상태 막대 라벨이 무엇을 세는지 `bar_note` 가 밝히는가. 수치 칸(`stats`)끼리 겹치는 집합이 없는가 — 겹치면 하나를 빼거나 라벨에 관계를 드러낸다. 막대 합·머리띠 잔여와 문서의 셈 대조는 `gazette.py` `tally_scan()` ⚠, 수치 칸 겹침은 사람의 눈(독자 검토 decider 관점) |
528
+ | PART 간지 | 인쇄에서 각각 1쪽 단독으로 서는가. **간지 물음(`questions`)만 이어 읽어 PART 마다 다른 물음인가**, 앞 PART 의 요지 lead·여백 주석 key·표 행이 뒤 PART 물음에 먼저 답하지 않는가. 같은 시각·금액·건 제목이 두 PART 에 서면 한쪽은 건 번호로 가리키기만 한다(readability R12g) — 기계는 세지 않는다 |
505
529
  | 절 첫 화면 | 두 단으로 서는가. 한 단이면 `marg` 가 빈 것이다 |
506
530
  | 여백 주석 | 본문 첫 문장의 복사본이 아닌가. 셋을 채우려고 넣은 줄이 없는가 |
507
531
  | 건 필드표 | 증상→원인→조치→확인 방법 순서가 전 건 같은가 |
@@ -509,22 +533,28 @@ RANK_TAG = ['live', 'data', 'done', 'soon'] # 배지 종류
509
533
  | 기록 자리 | 경어체 잔존 0건인가(`습니다|십시오|합니다|입니다|됩니다|주세요`) |
510
534
  | 요청 자리 | 반대다 — `cta`·`open_items`·PART I 간지·판권지 회신 안내가 **하십시오체로 맺는가**. 명사형 종결(`부탁드림`·`바람`·`하시면 됨`)이 남아 있지 않은가 |
511
535
  | 본문 전체 | 낱말 중간에서 줄이 바뀐 곳이 없는가 |
512
- | 본문 전체 | 독자가 조회할 수 없는 식별자가 남아 있지 않은가(포털 발행이면 이 그물이 없다 — 여기가 마지막 관문이다) |
536
+ | 본문 전체 | 독자가 조회할 수 없는 식별자가 남아 있지 않은가 — 해시·코드 파일·개발 도구·커밋·브랜치 같은 개발 식별자는 `gazette.py` `text_scan()` ⚠(두 경로), 고객사·제품명 같은 문서마다 다른 금칙 값은 포털 발행에 그물이 없어 **여기가 마지막 관문이다** |
537
+ | 본문 전체 | (코드 작업을 녹인 문서) 「반영함·보이지 않음·빠짐」 같은 완료형 동사가 원고 밖 상태 표의 확인 수준(코드·시험판·실기기·운영)을 넘지 않는가 — 기계 없음, 독자 검토 state 관점(`args.state`) · 사람의 눈(4단계 「코드 작업을 녹일 때」) |
538
+ | 배경 · 결정 요청 | 「정해진 것」으로 쓴 항마다 결정 장부에 결정권·정한 주체·기록·확인이 있는가. 결정권이 고객인 항을 기록 없이 사실로, FCG 가 정한 항을 고객 결정처럼 쓰지 않았는가 — 주체 없는 결정 명사만 `gazette.py` `text_scan()` 과정 어휘 ⚠, 그 밖은 사람의 눈 · 독자 검토 facts 관점(SKILL 3b 「결정 주체」) |
539
+ | 표지 · 판권지 · 발행 이력 | 작성일 세 자리(`meta` 작성 · 판권지 작성 · `history` 마지막 `date`)가 한 값이고 공개하는 날인가. 공개 전 판을 `history` 에 쌓지 않았는가 — 세 자리 대조는 `gazette.py` `date_scan()` ⚠(두 경로), 공개하는 날인지와 `history` 행 수는 **사람의 눈**(SKILL 3c · 10단계) |
513
540
  | 본문 전체 | 「A에서 B로」 전환 공식 헤드라인이 없는가 — **기계가 안 잡는다**(SKILL.md 카피 게이트 표) |
514
541
  | 본문 전체 | 부정 서술의 주어가 사람·조직·국가가 아닌가 — 기계는 「이유 없는 부정 종결」까지만 잡는다(content-pipeline §6.4) |
515
542
  | 본문 전체 | (결정권이 고객에게 있는 문서) 권고 낱말이 제목·표 열·절 이름·여백 주석·요약면에 배어 **동의만 받는 틀**인가. 우리 의견은 한 절 한 자리에만 있는가 — 기계는 **낱말까지**(`gazette.py` ⚠ W1). 동의만 받는 틀인지 · 의견 한 자리 여부는 **사람의 눈**(SKILL.md 카피 게이트 표 · lim-portal-format §12.6b) |
516
- | 도판 출처 줄 · 인용 | 도판·인용마다 **원본 위치**(문서 이름 + 쪽·행·주소, 또는 건 §n.n)가 있는가. 「흐리다」「못 연다」는 말은 원본 해상도로 **잰 뒤** 쓴 것인가 — 고객·제3자 자료에서 낸 도판(`user-provided`)의 원본 위치 누락은 `gazette.py` ⚠ W2, 재고 썼는지와 인용이 원문 그대로인지는 **사람의 눈**(독자 검토 「원본 대조」 관점) |
543
+ | 도판 출처 줄 · 인용 | 도판 출처 줄이 **위치 한 구절**인가(readability R12e — 쪽·행·주소·건 § 가운데 하나, 우리 도안은 비움). 원본 이름·찾을 낱말은 건이나 부록 출처 목록에 있는가. 「흐리다」「못 연다」는 말은 원본 해상도로 **잰 뒤** 쓴 것인가 — 고객·제3자 자료에서 낸 도판(`user-provided`)의 원본 위치 누락은 `gazette.py` ⚠ W2, 재고 썼는지와 인용이 원문 그대로인지는 **사람의 눈**(독자 검토 「원본 대조」 관점) |
544
+ | 보이는 자리의 제작 메타 | 출처 줄·캡션·부록·요청 본문에 독자의 할 일에 닿지 않는 제작 설명(합성이며·재현임·배율·잘라냄·촬영 환경·결정 경로)이 있는가. 제작 기록은 `origin` 과 도판 HTML 머리 주석에만, 정직성은 캡션의 한 낱말(도안·재현·합성)이면 된다 — 출처 줄은 `gazette.py` `text_scan()` 제작 메타 ⚠, 캡션·부록·요청 본문은 **사람의 눈** · 독자 검토 Verify 의 노이즈 거름 |
517
545
  | 결정 요청 | 결정에 **이용자 화면의 변화**(팝업·안내·칸의 유무)가 따르는데 고객 디자인 시스템 규격의 **도안**이 없는가 — **없음 — 사람의 눈**(절차는 visual-first 「화면 도안」) |
546
+ | 결정 요청 · 선택지 | 선택지마다 **같은 틀·같은 잣대**인가(이름·수·순서가 한눈표·선택지 비교 행렬·의견 표·도판 칩·조건표에서 같은가). 한 선택지만 합성·실사·부연·크게·먼저가 아닌가. 조건표의 각 행이 **모든 도안 그림과 앱 코드**에 맞는가(대조 없이 「공통」이라 적지 않았는가) — 기계는 `asks` 절 표의 모든 행이 같은 열(`gazette.py` `brief_checks()` ⚠)과 도판 틀(`figure.mjs` R13)까지, 그 밖은 독자 검토 figures 관점 · **사람의 눈**(lim-portal-format 「12.3b 브리핑 서사 공식」) |
547
+ | 확인 프로필 표 | 경계마다 포함·제외 두 행인가. 각 행을 판정 코드나 정의로 확인했는가(문서의 나이·키 정의가 앱의 정의와 같은가). 날짜 경계는 확인일 기준 상대값인가 — **없음 — 사람의 눈**(4단계 「확인 프로필 표」) |
518
548
  | 본문 전체 | 진행 상태 어휘(정해짐·회신 대기·N판)·개수로 상태를 세는 캡션·직전 판과의 비교(「~ 그대로」)가 남았는가. 정해진 것은 사실로 배경에, 결정 요청에는 지금 열린 것만 — 기계는 **낱말과 개수로 상태를 세는 표 제목**까지 잡는다(`gazette.py` 과정 어휘 ⚠ · readability R11h). 표 제목 밖의 개수 표현·「~ 그대로」 비교는 사람의 눈 |
519
549
  | 부록 | 본문에서 실제로 참조되는 것만 있는가 |
520
550
  | 판권지 | 문서가 여기서 닫히는가. 꼬리 카드가 붙어 있으면 구버전이다 |
521
551
  | 인쇄 미리보기 | 절마다 새 쪽에서 시작하는가. 표가 쪽 경계에서 잘리지 않는가 |
522
552
 
523
- 까닭(권고 낱말 · 원본 위치 · 도안 · 진행 상태 어휘 네 행): 실사례(2026-09-11, 고객사 결정 브리핑) 반려에서 왔다 — 그날엔 넷 다 기계 집행자가 없었다. 지금 기계는 낱말(과정 어휘 · W1)과 원본 위치 누락(W2)까지 잡는다(운영 dry_run 은 렌더러 배포 뒤 — lim-portal-format §11-6 머리). 틀 · 의견 자리 · 도안 유무 · 원본을 쟀는지는 여전히 이 표가 그물이다.
553
+ 이 표의 행 가운데 기계가 일부만 잡는 것은 행 끝에 그 한계를 적었다. 틀 · 의견 자리 · 도안 유무 · 원본을 쟀는지 · 선택지 사이 대칭 · 날짜는 여전히 이 표가 그물이다(경위: `references/history.md` §4·§7·§8·§12).
524
554
 
525
555
  ---
526
556
 
527
- ## 9. 8단계 — 전달
557
+ ## 8단계 — 전달
528
558
 
529
559
  서식은 서버가 정하므로 고를 것이 없다.
530
560
  경로만 고른다.
@@ -535,11 +565,11 @@ RANK_TAG = ['live', 'data', 'done', 'soon'] # 배지 종류
535
565
  서버가 렌더하므로 **HTML 을 만들지 않는다.**
536
566
 
537
567
  **기본은 초안이다.**
538
- 도구가 `publish` 를 명시적으로 참으로 주지 않는 한 고객에게 안 보인다.
539
- 확인한 뒤 `release_report(id)` 로 연다 — 그것이 사람의 승인 행위다.
568
+ 도구가 `publish` 를 명시적으로 참으로 주지 않는 한 고객에게 안 보인다. 여는 것은 10단계다.
540
569
 
541
- 수정은 `get_report` → 데이터 수정 → `update_report`.
542
- **같은 링크가 새 내용을 가리킨다.**
570
+ 수정은 SKILL.md 8단계 「수정 절차」(`get_report` 머리줄 확인 → 백업 → 고침 → 로컬 렌더·dry_run·독자 검토 → `update_report` → 9단계).
571
+ **같은 링크가 새 내용을 가리킨다.** 초안 `update_report` 도 **본문 판(`body_rev`)** 을 올리고 사용자가 그 판을 읽으므로 독자 검토를 통과한 본문 판만 올린다 — 발행 이력 행(`history`)은 공개 뒤에만 는다(SKILL 3c).
572
+ 사람이 고친 초안이면 덮기 전에 묻는다 — `update_report` 에 `body_rev` 잠금 칸이 아직 없어 기계가 막지 않는다.
543
573
 
544
574
  포털 셸이 문서 **둘레**에 놓는 것(검토 회신·핵심 요약·진척률 바)은 저작 대상이 아니다.
545
575
  문서 안에 「댓글로 알려 주세요」를 쓰면 같은 것이 두 번 있게 된다.
@@ -553,15 +583,17 @@ python3 ~/.claude/skills/genetics-report/assets/render.py report.json
553
583
  파일 하나를 그대로 보낸다.
554
584
  상대는 더블클릭만 하면 된다.
555
585
 
556
- 관보 문서에는 **문서 안 편집 층이 없다**(`cfg['editor'] = False`, `build.py:439`).
586
+ 관보 문서에는 **문서 안 편집 층이 없다**(`cfg['editor'] = False`, `build.py build_gazette()`).
557
587
  편집기의 블록 모델이 옛 마크업에 묶여 있어 관보 위에서는 차례를 빈 값으로 덮는다 — 고장이 아니라 파괴다.
558
588
  문구 수정은 데이터를 고쳐 다시 굽는다.
559
589
 
560
- **묻는다**: 최종 승인, 파일명 규칙.
590
+ **묻는다**: 파일명 규칙. 보내는 것은 10단계의 승인 뒤다.
561
591
 
562
592
  ---
563
593
 
564
- ## 10. 9단계 — 발행 뒤 판정
594
+ ## 9단계 — 발행 뒤 판정
595
+
596
+ 초안 발행·수정마다 돈다(공개 전에도). 이 블록 목록 대조와 열람 주소 열기가 SKILL.md 「작업 체크리스트」 9단계 두 행이다 — 둘 다 대조 기계는 아직 없고 사람이 한다.
565
597
 
566
598
  브라우저를 안 열고도 「무엇을 냈는가」를 확인하는 창구다.
567
599
 
@@ -582,13 +614,13 @@ get_report(id: N, outline: true)
582
614
  | `div.colophon` | 판권지 |
583
615
 
584
616
  `figure.eof-endcard` 가 보이면 **구버전**이다.
585
- 관보는 꼬리 카드를 안 붙인다(`REPORT_EOF_TEMPLATES = []`, `report_publish.php:1249`).
617
+ 관보는 꼬리 카드를 안 붙인다(`REPORT_EOF_TEMPLATES = []`, `report_publish.php report_with_eof()`).
586
618
 
587
619
  `aside.marg` 개수가 절 수보다 적으면 그만큼의 절이 한 단으로 서 있다는 뜻이다.
588
620
 
589
621
  ### ⚠ 머리줄의 `절 0 · 건 0 · 장 0 · 그림 0` 은 **정상값**이다
590
622
 
591
- MCP 가 찍는 요약 줄은 **옛 서식의 셈**만 읽는다(`mcp/genetics.mjs:2718-2726`).
623
+ MCP 가 찍는 요약 줄은 **옛 서식의 셈**만 읽는다(`genetics.mjs outlineText()`).
592
624
  그 값들은 `h2.sec` · `.case` · `.ch-open` · `<img>` 를 세는데, 관보는 그중 무엇도 안 쓴다.
593
625
  실측(예시 문서): 절 0 · 건 0 · 장 0 · 그림 0 이고 실제로는 `ask` 1 · `sheet` 2 · `board` 1 이었다.
594
626
 
@@ -603,9 +635,34 @@ MCP 가 찍는 요약 줄은 **옛 서식의 셈**만 읽는다(`mcp/genetics.mj
603
635
  응답 끝의 `⚠ 빈 블록 N건` 은 **렌더러가 조각을 빠뜨렸다는 뜻**이다.
604
636
  데이터 문제가 아니라 렌더러 문제이므로, 그대로 두지 말고 원인을 찾는다.
605
637
 
638
+ ### 열람 주소를 연다
639
+
640
+ 블록 목록은 「무엇이 섰는가」만 말한다. 칩·굵게·링크가 원문 글자(`**`·`{{`·`](`)로 찍혔는지, 도판 쌍·판단 절이 제 폭으로 섰는지는
641
+ 열람 주소를 열어야 보인다. 못 열었으면(로그인·브라우저 점유) 까닭을 핸드오프에 적는다 — 「안 봄」을 「이상 없음」으로 적지 않는다.
642
+
606
643
  ---
607
644
 
608
- ## 11. 같은 사실이 두 문서로 갈리는 지점
645
+ ## 10단계 — 공개
646
+
647
+ 공개 전 수정은 판이 아니다(SKILL 3c). 공개하기 직전에 한 번만 날짜를 맞춘다.
648
+
649
+ 1. 작성일 세 자리 — `meta` 의 작성 · 판권지(`colophon`) 작성 · `history` 마지막 `date` — 를 공개하는 날 한 값으로 고친다. `history` 는 공개할 판 한 행(「최초 발행」)만 둔다.
650
+ `cover_date` 는 요약면 「기준」 날짜(수치의 기준일)라 이 셋에 묶지 않는다.
651
+ 2. `update_report` 한 번 → 9단계 두 행.
652
+ 3. 사용자 승인을 받고 `release_report(id)` 로 연다 — 그것이 사람의 승인 행위다.
653
+
654
+ 공개 뒤 내용이 바뀐 update 에만 `history` 행을 더한다. 문서 밖 결정의 날짜는 본문에 적지 않는다(결정 장부).
655
+
656
+ | 규칙 | 집행자 |
657
+ |---|---|
658
+ | 작성일 세 자리가 한 값이고 공개일과 같다 · 공개 전 판을 `history` 에 쌓지 않는다 | 세 자리 대조는 `gazette.py` `date_scan()` ⚠ · 공개일과 `history` 행 수는 사람(7단계 표 「표지 · 판권지 · 발행 이력」 행) — 공개 때 날짜 안내(`release_report`)는 기계가 아직 없다 |
659
+ | 공개는 사람이 한다 | `release_report` — 도구가 `publish` 를 참으로 주지 않는 한 초안이 기본값 |
660
+
661
+ **묻는다**: 공개일, 공개 승인(반드시).
662
+
663
+ ---
664
+
665
+ ## 같은 사실이 두 문서로 갈리는 지점
609
666
 
610
667
  내부 기록 ↔ 고객 문서 대조표는 SKILL.md 0단계에 있다.
611
668
  여기서는 그 표를 만든 **원리 셋**만 적는다.
@@ -631,33 +688,38 @@ MCP 가 찍는 요약 줄은 **옛 서식의 셈**만 읽는다(`mcp/genetics.mj
631
688
 
632
689
  ---
633
690
 
634
- ## 12. 새 프로젝트 체크리스트
691
+ ## 새 프로젝트 체크리스트 — 채울 칸
692
+
693
+ 밟는 순서·명령·통과 기준은 SKILL.md 「작업 체크리스트」 한 장이다. 여기는 **어느 칸을 채우는가**만 적는다(「단계」는 그 표의 번호).
635
694
 
636
695
  | # | 채울 것 | 어디에 | 단계 |
637
696
  |---|---|---|---|
638
- | 1 | 독자 문장 1개 (하는 일로) | (문서 아님 — 작업 전제) | 0 |
697
+ | 1 | 독자 문장 1개 (하는 일로) + 할 일 목록 | (문서 아님 — 작업 전제) | 0 |
698
+ | 1a | 정해진 것의 결정권·정한 주체·기록·확인 여부 | 작업 폴더 `decisions.json`(발행 안 함) | 0a |
639
699
  | 2 | 금지 어휘·식별자 정규식 | `forbid` (로컬 렌더에서만 돈다) | 0·6 |
640
700
  | 3 | PART 구성 | `parts` | 0·6 |
641
701
  | 4 | 절 구성과 순서 | `sections` + `groups` | 0·6 |
642
702
  | 5 | 건별 필드 | `cases` | 1·2 |
643
703
  | 6 | 보고서 표제로 재작성한 전 건 제목 | `cases[].title` | 2 |
644
704
  | 7 | 등급 이름·정의문 (**4단 이하** · 최약 등급에 한계 명시) | `grades` | 3 |
645
- | 8 | 요약 4문단 + **문단마다 근거** | `summary` (+ `refs`) | 4 |
705
+ | 8 | 요지(칸 수는 SKILL 4~6단계) + **문단마다 근거** | `summary` (+ `refs`) | 4 |
646
706
  | 9 | 확인 요청 3~6개 (주어는 우리) | `open_items` | 4 |
647
707
  | 10 | **절마다 여백 주석** | `sections[].marg` | 5 |
648
708
  | 11 | 표지·발행 이력·활용법 | `doc_no`·`client`·`title`·`title_sub`·`subtitle`·`meta`·`issuer`·`history`·`guide` | 6 |
649
709
  | 12 | 요약면 | `notice`·`bar`·`bar_note`·`stats` | 6 |
650
710
  | 13 | 부록·판권지 | `appendix`·`colophon` | 6 |
651
- | 14 | 고객사 로고 (있으면) | `assets` — `role:"logo"` 하나만 실린다 | 6 |
652
- | 15 | (브리핑) 판단 항목마다 물음 제목과 기한 · 두 길 대칭 한눈표 · 선택지 전치 행렬 · 의견 절 하나 · 바꾸는 길의 위험 · 회신 뒤 일정 | `open_items` · `sections[].tables` · `note` 절 넷 | 4·6 |
653
- | 16 | (브리핑) `doc_type` 에 「브리핑」 — 렌더러 ⚠ 셋이 켜진다 | `doc_type` | 0 |
711
+ | 14 | 고객사 로고 (있으면) | `assets` — `role:"logo"` | 6 |
712
+ | 14a | 도판 — 도판 HTML·PNG · 일괄 판정 매니페스트 | `assets` 의 `role:"figure"` + `sections[].plates` · 작업 폴더 `plates.json` | 6 |
713
+ | 15 | (브리핑) 판단 항목마다 물음 제목과 기한 · 둘째 할 일이면 「… 확인 요청」 · 두 길 대칭 한눈표 · 선택지 전치 행렬 · 의견 절 하나 · 바꾸는 길의 위험 · 회신 뒤 일정 | `open_items` · `sections[].tables` · `note` 절 넷 | 4·6 |
714
+ | 16 | (브리핑) `doc_type` 에 「브리핑」 — `brief_checks()` 경고가 켜진다 | `doc_type` | 0 |
715
+ | 17 | (코드 작업을 녹이면) 원고 밖 상태 표 | 작업 폴더(발행 안 함) · 독자 검토 `args.state` | 4 |
654
716
 
655
717
  건드리지 않는 것: `build.py`·`gazette.py`·`gazette.css`·`sentbreak.py`·`boundary.py`.
656
- `render.py` 가 사본이 생기면 멈춘다(`FORKABLE`) — 2026-08-18 에 나흘 만에 887줄이 갈라졌고 그동안 고객 문서가 서식 없이 나갔기 때문이다.
718
+ `render.py` 가 사본이 생기면 멈춘다(`FORKABLE`, 경위: `references/history.md` §1).
657
719
 
658
720
  ---
659
721
 
660
- ## 13. 미확정
722
+ ## 미확정
661
723
 
662
724
  1. **`open_items` 개수 3~6 의 근거.**
663
725
  상한·하한을 정한 실측 근거는 없다.