genetics-mcp 2.18.1 → 2.18.3
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.
- package/bin/aarch64-apple-darwin/gcell +0 -0
- package/bin/manifest.json +2 -2
- package/bin/x86_64-apple-darwin/gcell +0 -0
- package/package.json +1 -1
- package/skills/genetics-report/SKILL.md +10 -6
- package/skills/genetics-report/assets/diagram.mjs +195 -0
- package/skills/genetics-report/assets/example/cases-briefing-designs.json +4 -2
- package/skills/genetics-report/assets/example/cases-briefing.json +4 -2
- package/skills/genetics-report/assets/example/report-briefing-designs.json +17 -9
- package/skills/genetics-report/assets/example/report-briefing.json +4 -3
- package/skills/genetics-report/assets/example/report.json +1 -1
- package/skills/genetics-report/references/diagrams.md +88 -0
- package/skills/genetics-report/references/edition-review.workflow.js +49 -0
- package/skills/genetics-report/references/history.md +6 -0
- package/skills/genetics-report/references/lim-portal-format.md +105 -12
- package/skills/genetics-report/references/process.md +3 -3
- package/skills/genetics-report/references/readability.md +1 -1
- package/skills/genetics-report/references/visual-first.md +1 -0
|
Binary file
|
package/bin/manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"aarch64-apple-darwin": "
|
|
2
|
+
"aarch64-apple-darwin": "9d666f91accd6183e93fc4e7b1e17f831c9caa2d0237cf5f009b7e2e87544249",
|
|
3
3
|
"aarch64-unknown-linux-musl": "aa25f20daefda931d8497d346bd47fb9b8a2acc3136ec2b9eeff18773a789e8c",
|
|
4
|
-
"x86_64-apple-darwin": "
|
|
4
|
+
"x86_64-apple-darwin": "11b3060990432a45f716e082823bdb972f8ab531a4ce85ea6b4a34c9df305df8",
|
|
5
5
|
"x86_64-unknown-linux-musl": "c97bcded75ec34cdb5a448159e7f057a756deb6a45fa7793b299d0040c19be93"
|
|
6
6
|
}
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -44,7 +44,7 @@ version: "4.0.0"
|
|
|
44
44
|
| 6 도판 굽기 | 도판 HTML → PNG · `plates.json` | `node assets/figure.mjs --batch plates.json --report report.json` · 두 칸 비교는 `shot.mjs --after` → `pair.mjs` | exit 0(✗0 · 합계 400,000B 안) | `figure.mjs` R0~R10·R13 · `pair.mjs` · 칩 문구의 뜻은 사람 눈 | — |
|
|
45
45
|
| 7 로컬 렌더 | 단일 HTML | `python3 assets/render.py report.json` | ⚠0 · `forbid` 적중 0 | `gazette.py` `text_scan()`·`brief_checks()`·`tally_scan()` · `build.py guard()` | — |
|
|
46
46
|
| 7 dry_run | 경고 목록 | `publish_report`/`update_report` 에 `dry_run: true` | 작성자가 고칠 수 있는 ⚠0 — 못 없애는 경고는 까닭을 핸드오프에 | `report_publish.php report_build_checked()`(렌더러·입고·그림·문체) · 경고 기준선 대조는 사람 | — |
|
|
47
|
-
| 7 독자 검토 | 워크플로 결과(`merged`·`questions`·`dropped`) | `references/reader-review.workflow.js`(0.5단계 7행) | 사실 틀림 반영 · 「⚠ 도판 N장 미열람」 없음 · 반영 뒤 로컬 렌더·dry_run 다시 | 워크플로 관점 + JS 거름 · `questions`·`dropped` 는 사람 | `questions[]` |
|
|
47
|
+
| 7 독자 검토 | 워크플로 결과(`merged`·`questions`·`dropped`) | `references/reader-review.workflow.js`(0.5단계 7행) · 구조를 안 바꾼 낱말 손질 판은 `references/edition-review.workflow.js`(결과 `kept`·`weak`) | 사실 틀림 반영 · 「⚠ 도판 N장 미열람」 없음 · 반영 뒤 로컬 렌더·dry_run 다시 | 워크플로 관점 + JS 거름 · `questions`·`dropped` 는 사람 | `questions[]` |
|
|
48
48
|
| 7 조판 검사 | 검사기 출력 | `gazette-report` 검사기 8종(단일 HTML 전달일 때) | 각 0건 · **표본 수를 함께 본다**(0표본 통과는 통과가 아니다) | 검사기 — 실행법은 `gazette-report` 스킬 | — |
|
|
49
49
|
| 7 사람 눈 | — | 인쇄 미리보기까지 연다 | `references/process.md` 「7단계 — 검수」 사람의 눈 표 | 사람 | — |
|
|
50
50
|
| 8 초안 발행·수정 | 초안 링크 | 새 문서 `publish_report` · 고치면 8단계 「수정 절차」 | 같은 링크 · 사람이 고친 초안을 모르고 덮지 않음 | 사람 — `update_report` 에 `body_rev` 잠금 칸이 아직 없음 | 사람이 고친 초안이면 덮어도 되는가 |
|
|
@@ -105,7 +105,8 @@ version: "4.0.0"
|
|
|
105
105
|
- `cases[].grade` 는 `grades` 에 정의된 이름이어야 한다.
|
|
106
106
|
- **`update_report` 는 같은 주소를 덮어쓴다** — 이미 고객에게 나간 링크가 새 내용을 가리키므로, 발행 후 수정은 재발행이 아니라 update 로 한다. 기존 문서는 `list_reports`/`get_report` 로 받아 고쳐 되돌려 보낸다.
|
|
107
107
|
- **그림 계약**: `update_report` 에서 `assets` 를 **빼고** 보내면 서버가 저장된 그림을 그대로 유지한다. 전부 지우려면 `assets: []` 를 명시하고 `sections[].plates`·`chapters[].asset` 에서도 그 id 를 빼야 한다 — 가리키는 곳이 없는 id 는 422. 한 장만 바꾸려면 그 id 에 새 `source`/`file`, 나머지는 `get_report` 가 준 표식 그대로.
|
|
108
|
-
- 초안 상태로 올려 두고 다듬어도 발행은 막히지 않는다. 단 초안 `update_report` 도 **본문 판(`body_rev`)** 을 올리고 사용자가 그 판을 읽으므로 **독자 검토를 통과한 본문 판만 올린다** — 다듬는 중간 판은 로컬 렌더·dry_run 으로 본다.
|
|
108
|
+
- 초안 상태로 올려 두고 다듬어도 발행은 막히지 않는다. 단 초안 `update_report` 도 **본문 판(`body_rev`)** 을 올리고 사용자가 그 판을 읽으므로 **독자 검토를 통과한 본문 판만 올린다** — 다듬는 중간 판은 로컬 렌더·dry_run 으로 본다. 사용자가 「바로 반영」을 지시하면 먼저 올리고 검토는 곧바로 뒤따라 돌려 다음 판에 반영한다(2026-09-15 사용자 결정).
|
|
109
|
+
⚠ **운영 반영 확인 전 — 운영 서버는 초안에 `update_report` 를 보내면 그 순간 공개로 바꾼다**(저장된 공개일이 비었으면 오늘로 채운다, 수리는 레포 main 에만 있다). 배포 전까지 초안은 `dry_run` 으로만 다듬고, 굳은 뒤 `publish_report` 한 번 → 사람 확인 → `release_report`. 이미 썼으면 `list_reports` 대괄호가 「데이터로 발행된 문서」인지 보고 `unpublish_report(id)` 로 되돌린다 — 교체 응답 문구(「이미 나간 열람 링크가 그대로…」)는 초안에도 같아 그것만으로는 못 가른다. 결정 브리핑 한 편이 첫 교체로 공개돼 여러 판을 고치는 엿새 동안 열려 있었다(2026-09-09~15).
|
|
109
110
|
**발행 이력 행(`history`)은 다른 것이다** — 공개 전 수정은 이력 행을 만들지 않는다(3c).
|
|
110
111
|
- 내용 설계 규칙(아래 0~3단계, 고객 언어 치환, 카피 게이트)은 **전달 경로와 무관하게 동일하게** 적용된다 — 서버가 렌더한다고 해서 원고 품질 단계를 건너뛰지 않는다.
|
|
111
112
|
|
|
@@ -206,11 +207,11 @@ Lim 보고서, Lim 에 올려줘, 고객 보고서, 수정 내역, 검수 요청
|
|
|
206
207
|
| 1 | **한 장 이야기** | 문제 → 원인 → 해법(지금 상태 포함) → 정할 것. 칸 수가 아니라 이야기 순서다 — 요지 칸 수는 4~6단계 「요지」 한 곳이 정본. 다루는 일의 **사건 경과** 표 한 장을 1절에 둔다(이 문서의 판 이력은 `history` — 3c) |
|
|
207
208
|
| 2 | **예시 한 명** | 실존 이용자 하나의 실제 숫자로 문서 전체를 관통시킨다(같은 사람이 그림·표·건에 반복) |
|
|
208
209
|
| 3 | **용어 잠금 표** | 같은 낱말이 두 값을 가리키면 그 자리에서 문서가 죽는다. 뜻이 갈리는 낱말은 표 한 장으로 값·정의·보이는 자리를 박는다. 장치 이름(건 행 `labels` 값)은 절 이름·부제·표 열·캡션에 다시 쓰지 않고, 선택지 기호(A·B·C)는 한 목록에만 붙인다. 집행자: `gazette.py` `text_scan()` ⚠(건 행 이름 충돌·선택지 기호 충돌) · 독자 검토 first-time 관점(한 낱말 두 값) · 뜻이 같은지는 사람 눈 |
|
|
209
|
-
| 4 | **절마다 시각 장치 먼저** | 개념은 그림, 실제 화면은 번호 표식 캡처, 견주기는 표, 「A 면 B」는
|
|
210
|
+
| 4 | **절마다 시각 장치 먼저** | 개념은 그림, 실제 화면은 번호 표식 캡처, 견주기는 표, 「A 면 B」는 흐름도(원인 사슬은 `flow.mode:"cause"`), 분포는 막대 차트, **시간에 따른 값은 시계열**(`charts[].kind:"series"`), **공식 문서·회신 문구는 원문 인용**(`quotes`), **구조·흐름·순서·상태는 Archify 도해**(`diagrams`, `references/diagrams.md`). 글은 그 옆 3줄. 도판 층 R0~R10·R13 은 `figure.mjs`(비교 컷은 `shot.mjs --after` → `pair.mjs`), 글 층 R11 은 렌더러 ⚠ — 값은 `references/readability.md` 「1. 규칙표」 한 곳(경위: `references/history.md` §3) |
|
|
210
211
|
| 5 | **판단 요청** | 요청은 **고객이 고를 물음**이다 — 제목은 물음, 본문 첫 문장은 기한과 두 길의 이름, 결과는 한눈표 **한 곳에만** 대칭으로. 모양·선택지 대칭 불변식·화면 도안 절차는 `references/visual-first.md` §5 와 `lim-portal-format.md` §12.3b. 집행자: `gazette.py` `brief_checks()` ⚠(기한·동일값 열) · ⚠ W1(권고 낱말) · 선택지 사이 대칭은 독자 검토 figures 관점 · 도안 유무는 **없음 — 사람의 눈**(`references/process.md` 7단계 표) |
|
|
211
212
|
| 5a | **원본 확인** | 주장·그림마다 원본을 **댈 수 있어야** 한다. 출처 줄은 위치 한 구절(`references/readability.md` R12e — 우리 도안은 비움), 원본 이름·찾을 낱말은 건(`source_url`·`how_to_check`)이나 부록에, 제작 메타는 `origin` 으로(11번). 집행자: 독자 검토 source 관점(누를 수 있는 원본) · facts 관점(앱 동작·내부 수치·결정 주체) · `gazette.py` ⚠ W2 · `text_scan()` 제작 메타 ⚠ — 그 밖은 사람 눈 |
|
|
212
213
|
| 6 | **표기** | 인라인 표기(`**굵게**`·`==강조==`·`{{ok|칩}}`·`[글자](주소)`·**맨 주소**)와 절·PART 의 `tone`, 차트 행 `tone`, 건의 `source_url`, 요약면 `notice.state`. 그림 가리킴은 손으로 번호를 치지 않고 `{{그림:<assets id>}}` 토큰으로(번호·조사를 렌더러가 셈) — ⚠ **운영 반영 확인 전**이라 배포 뒤 열람 DOM 에서 `a.ref[href^="#fig-"]` 를 세어 확인하기 전에는 쓰지 않는다(`lim-portal-format.md` §11-6). 색은 뜻으로(`lim-portal-format.md` 「11-6. 인라인 표기」) |
|
|
213
|
-
| 7 | **독자 검토** | 발행 전 `references/reader-review.workflow.js` — 관점 여덟(상태 표 `args.state` 를 주면 아홉). **Verify 를 통과한 발견만 반영한다 — 검토자 주장도 틀린다.** 반영 순서는 사실 틀림 → 구조(합치기·빼기) → 나머지. **빼기가 포인터를 남기면 안 된다** — 「N절을 보라」「건 §n.n」「부록에 있다」로 대신하면 독자가 장을 오가는 시험지가 된다. 낱말·규칙·예시 선언은 **첫 등장 자리에서 한 구절**, 뒤 절의 번호·표 이름·§ 는 앞 절에서 쓰지 않는다(요약 근거는 `refs[]` 로). 선형 읽기 관점의 **뜀 지점(`jumps`) 0 이 목표**(실측은 `references/linear-read-test.workflow.js`, 독자 셋+결정권자). 관점·인자·JS 거름 규칙은 `references/visual-first.md` §7. 집행자: 워크플로 관점 + JS 거름 · 뒤 절 번호는 렌더러 ⚠ 「앞 절이 뒤 절을 가리키는 곳」(`gazette.py text_scan` W5 · readability R12h) · 정의 위치와 `questions`·`dropped` 는 사람 눈(경위: `references/history.md` §9·§12) |
|
|
214
|
+
| 7 | **독자 검토** | 발행 전 `references/reader-review.workflow.js` — 관점 여덟(상태 표 `args.state` 를 주면 아홉). **Verify 를 통과한 발견만 반영한다 — 검토자 주장도 틀린다.** 반영 순서는 사실 틀림 → 구조(합치기·빼기) → 나머지. **빼기가 포인터를 남기면 안 된다** — 「N절을 보라」「건 §n.n」「부록에 있다」로 대신하면 독자가 장을 오가는 시험지가 된다. 낱말·규칙·예시 선언은 **첫 등장 자리에서 한 구절**, 뒤 절의 번호·표 이름·§ 는 앞 절에서 쓰지 않는다(요약 근거는 `refs[]` 로). 선형 읽기 관점의 **뜀 지점(`jumps`) 0 이 목표**(실측은 `references/linear-read-test.workflow.js`, 독자 셋+결정권자). 관점·인자·JS 거름 규칙은 `references/visual-first.md` §7. 집행자: 워크플로 관점 + JS 거름 · 뒤 절 번호는 렌더러 ⚠ 「앞 절이 뒤 절을 가리키는 곳」(`gazette.py text_scan` W5 · readability R12h) · 정의 위치와 `questions`·`dropped` 는 사람 눈(경위: `references/history.md` §9·§12). **낱말만 고친 판도 검토 없이 내지 않는다** — 변경 자리만 보는 `references/edition-review.workflow.js`(세 관점 + 반박)로 돌린다. 렌더러 경고 0 만 보고 낸 15판이 다음 판 8자리 손질을 낳았다(2026-09-15). 사용자가 「바로 반영」을 지시하면 먼저 내고 이 검토는 곧바로 뒤따라 돌려 다음 판에 반영한다(같은 날 사용자 결정) |
|
|
214
215
|
| 8 | **실물 확인** | 발행할 때마다 체크리스트 9단계 두 행 — 블록 목록 대조와 열람 주소 열기. 집행자: 사람 — 발행 뒤 대조 기계는 아직 없다 |
|
|
215
216
|
|
|
216
217
|
상세와 도판 예산(장당 80KB · 합계 400KB), 그림 만드는 법, 반려 사례는 `references/visual-first.md`.
|
|
@@ -311,7 +312,7 @@ report.json + cases.json 을 하나로 합쳐 `publish_report` 에 준다
|
|
|
311
312
|
서버는 「빈 칸」을 안 막는다 — 막는 것은 **조용히 사라지는 것**뿐이고, 관보의 본체가
|
|
312
313
|
빠지면 ⚠ 로 소리 내어 센다. `marg` 가 비었다는 경고를 못 보고 넘기면 두 단이 한 단으로 선다.
|
|
313
314
|
|
|
314
|
-
**기본은 초안이다.** 도구가 `publish` 를 참으로 주지 않는 한 고객에게 안 보인다.
|
|
315
|
+
**기본은 초안이다.** 도구가 `publish` 를 참으로 주지 않는 한 고객에게 안 보인다. ⚠ 단 운영 반영 확인 전에는 초안에 보낸 `update_report` 가 공개로 바꾼다(위 「Lim 발행 규칙」).
|
|
315
316
|
공개 직전 날짜를 맞춘 뒤(3c) `release_report(id)` 로 연다 — 그것이 사람의 승인 행위다(체크리스트 10단계).
|
|
316
317
|
|
|
317
318
|
**수정 절차** — `get_report` 머리줄에서 판번호(`body_rev`)와 사람 편집 초안 여부를 본다 → 받은 JSON 을 백업 → 자리마다 지금 값이
|
|
@@ -368,8 +369,10 @@ render·css·폰트를 함께 얹는다. 직접 부르면 옛 서식 시트가
|
|
|
368
369
|
| `assets/render.py` | `report.json` → 단일 HTML. **서버 등록부에서 서식을 읽어** 레포 `private/builder/build.py` 를 부른다 — 여기 사본을 두지 마라 |
|
|
369
370
|
| `assets/subset_font.py` | 폰트 서브셋 생성 (보통 실행 불필요) |
|
|
370
371
|
| `assets/example/report.json` · `cases.json` | 관보 필드를 전부 채운 본보기. 새 문서는 여기서 베낀다 |
|
|
372
|
+
| `assets/diagram.mjs` | Archify 도해를 관보 `diagrams[].svg` 로 굳힌다 — `render <type> <ir.json> <out.svg> --wide --min-text 16 --tight`(Archify deliver → 계산된 스타일을 속성으로 → 클래스·스크립트·애니메이션 걷음 → 작은 글자 키움·빈 띠 조임·겹침 측정) · `export <html> <out.svg>` · `inspect <svg>`. 렌더러와 같은 잣대(바이트 250KB · viewBox · 표시 글자 15px)로 재고 ✗ 면 exit 3. Archify 자체는 동봉하지 않는다(`references/diagrams.md` §1) |
|
|
371
373
|
| `assets/figure.mjs` · `shot.mjs` · `mockshot.mjs` · `pair.mjs` | 도판 도구 — 도판 HTML 을 굽고 가독성·틀 문법 관문(R0~R10·R13)을 잰다, `--batch plates.json` 은 일괄 판정과 예산 합계, `--spec` 은 계약 상수(`figure.mjs`) · 웹 화면을 검정 원 번호 표식과 함께 찍고 `--after` 로 블록을 숨긴 전후 두 장을 같은 구간으로 찍는다(`shot.mjs`) · 고객 앱 화면 도안을 2배 밀도로 찍는다(`mockshot.mjs`, `references/visual-first.md` 「화면 도안」) · 전후 두 컷을 좌우 같은 틀·같은 배율로 조립하고 판정까지 찍는다(`pair.mjs`, 본보기 `assets/example/plate-pair.html`) |
|
|
372
374
|
| `references/linear-read-test.workflow.js` | 선형 읽기 **실측** — 처음 읽는 사람 셋 + 결정권자가 앞에서 뒤로 한 번 읽으며 뛰어야 하는 자리를 전부 센다(`args: {file, reader, round}` — 재측정은 `round` 를 바꿔야 캐시를 안 탄다). 뜀 0 이 목표. 독자 검토(`reader-review.workflow.js`)의 linear 관점이 같은 읽기를 하나로 한다 |
|
|
375
|
+
| `references/edition-review.workflow.js` | **판 갱신 검토** — 구조는 그대로이고 낱말·출처 줄·여백 주석만 고친 판의 **변경 자리**(edit-N.py 의 put/sub 호출)만 선형 독자·규칙 대조·사실 대조 세 관점으로 읽고 심각도 3 이상은 반박한다(`args: {file, backup, edit_script, doc}`). 사실 관점이 백업↔새 판 JSON diff 에 스크립트가 바꾼 자리가 빠짐없이 있는지(삽입·삭제가 섞이면 줄 수가 아니라 자리로)·수치 무변경을 검산한다. 구조가 바뀐 판은 `reader-review.workflow.js`. 낱말 손질 판을 검토 없이 발행하자 다음 판 8자리 손질이 생겼다(2026-09-15) |
|
|
373
376
|
| `assets/example/report-briefing.json` · `cases-briefing.json` | 「결정한다(R&D 브리핑)」 모드 본보기 — 물음 제목 · 두 길 대칭 한눈표 · 선택지 전치 행렬 · FCG 의견 표 한 장 · 바꾸는 길의 위험 · 고른 길별 일정 · `cta` 기본 `[]` |
|
|
374
377
|
| `assets/example/report-briefing-designs.json` · `cases-briefing-designs.json` | 「결정한다」 도안 결재형 본보기 — 세 길 · 전폭 선택지 도판(자리 표시) · 조건표 · 선택지마다 한 행인 의견 표 · 판단 항목 둘(물음 + 「… 확인 요청」) · 확인 프로필 표(`references/lim-portal-format.md` §12.3b) |
|
|
375
378
|
| 폰트 | **레포** `private/builder/fonts/pretendard-gazette.woff2` — 가변 1면 |
|
|
@@ -412,7 +415,7 @@ render·css·폰트를 함께 얹는다. 직접 부르면 옛 서식 시트가
|
|
|
412
415
|
`summary`, 절 도입문·여백 주석, `status` 대조표, 부록, 판권지의 사실 서술.
|
|
413
416
|
**여기서만** `습니다|십시오|합니다|입니다|됩니다|주세요` 가 0건이어야 한다.
|
|
414
417
|
5a. **상대에게 말 거는 자리는 하십시오체로 맺는다.** 종결까지 존댓말이어야 한다.
|
|
415
|
-
그 자리는 요청 절(`cta`·`open_items`)·PART I 간지·판권지의 회신
|
|
418
|
+
그 자리는 요청 절(`cta`·`open_items`)·PART I 간지·판권지의 회신 안내, 그리고 요청 절의 확인 요청을 가리키는 읽는 법(`guide`) 한 줄이다(2026-09-15 사용자 결정).
|
|
416
419
|
|
|
417
420
|
| 상대가 할 일 | 쓰지 않는다 | 쓴다 |
|
|
418
421
|
|---|---|---|
|
|
@@ -582,6 +585,7 @@ KS X 1001 완성형 2,350자. 관보 서식이 `--w-light: 300` 을 쓰므로 40
|
|
|
582
585
|
| `references/history.md` | 규칙이 생긴 반려 경위·원문·실측 — 규칙이 아니다. 규칙 자리의 「(경위: §n)」이 가리킨다 |
|
|
583
586
|
| `references/lim-portal-format.md` | **포털 발행할 때** — 관보 골격·여백 주석·PART·톤·요약 근거 게이트·운영 제약 |
|
|
584
587
|
| `references/content-pipeline.md` | 데이터 모델 전수 — 절 `kind` 5종, 등급, 한국어 조판 규칙, 고객 언어 치환 |
|
|
588
|
+
| `references/diagrams.md` | **도해가 필요할 때** — Archify 설치 위치·IR 작성·굳히기(`diagram.mjs`)·가독성 잣대·관보에 앉히는 법 |
|
|
585
589
|
| `references/font-embedding.md` | 폰트를 바꾸거나 커버리지를 조정할 때 |
|
|
586
590
|
| `gazette-report` 스킬 (별도) | 조판 규칙의 원전 — 활자 스케일·괘선·인쇄 페이지 문법·검사기 8종 |
|
|
587
591
|
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// diagram.mjs — Archify 도해를 관보 `sections[].diagrams[]` 에 앉힐 **독립 SVG** 로 굳힌다.
|
|
3
|
+
//
|
|
4
|
+
// node diagram.mjs render <type> <ir.json> <out.svg> [--archify <dir>] [--wide] [--json]
|
|
5
|
+
// Archify(`bin/archify.mjs deliver … --quality showcase`)로 HTML 을 만든 뒤 export 한다.
|
|
6
|
+
// node diagram.mjs export <archify.html> <out.svg> [--wide] [--json]
|
|
7
|
+
// 이미 만든 Archify HTML(또는 아무 HTML 의 첫 <svg role="img">)에서 SVG 하나를 뽑는다.
|
|
8
|
+
// node diagram.mjs inspect <in.svg> [--wide] [--json]
|
|
9
|
+
// 렌더러(gazette.py _svg_clean)와 같은 잣대로 잰다 — 바이트 · viewBox · 가장 작은 글자 · 표시 글자.
|
|
10
|
+
//
|
|
11
|
+
// 왜 export 가 필요한가: Archify 의 HTML 은 SVG 가 페이지 CSS(28만 자)의 클래스에 기대고, 뷰어 스크립트가
|
|
12
|
+
// 애니메이션·상세 단계를 만든다. 관보 열람은 스크립트 0·문서 활자 스케일이라, **계산된 스타일을 속성으로 굳히고**
|
|
13
|
+
// 클래스·스크립트·스타일·애니메이션을 걷은 SVG 만 앉힐 수 있다(gazette.py _svg_clean 이 한 번 더 걷는다).
|
|
14
|
+
// 글자 크기는 굳히되 font-family 는 **굳히지 않는다** — 문서의 Pretendard 를 물려받아야 본문과 같은 활자로 선다.
|
|
15
|
+
//
|
|
16
|
+
// 가독성 잣대(스킬 references/readability.md R1): 표시 글자 ≥ 15px. 표시 폭은 본문 단 760 · 전폭(--wide) 1030 이고
|
|
17
|
+
// 표시 글자 = 원본 font-size × 표시 폭 ÷ viewBox 폭. 15 미만이면 이 도구가 ✗ 로 멈추고(exit 3), 렌더러도 ⚠ 로 센다.
|
|
18
|
+
import fs from 'node:fs';
|
|
19
|
+
import os from 'node:os';
|
|
20
|
+
import path from 'node:path';
|
|
21
|
+
import { spawnSync } from 'node:child_process';
|
|
22
|
+
import { pathToFileURL } from 'node:url';
|
|
23
|
+
|
|
24
|
+
const COLUMN = 760, WIDE = 1030, MIN_FONT = 15, MAX_BYTES = 250000; // gazette.py 상수와 같은 값
|
|
25
|
+
const argv = process.argv.slice(2);
|
|
26
|
+
const flags = new Set(argv.filter(a => a.startsWith('--')));
|
|
27
|
+
const opt = (k) => { const i = argv.indexOf(k); return i >= 0 ? argv[i + 1] : undefined; };
|
|
28
|
+
const pos = argv.filter((a, i) => !a.startsWith('--') && (i === 0 || argv[i - 1] !== '--archify'));
|
|
29
|
+
const cmd = pos[0];
|
|
30
|
+
const JSON_OUT = flags.has('--json');
|
|
31
|
+
const wide = flags.has('--wide');
|
|
32
|
+
const usage = 'usage: node diagram.mjs render <type> <ir.json> <out.svg> [--archify <dir>] [--wide] [--json]\n'
|
|
33
|
+
+ ' node diagram.mjs export <archify.html> <out.svg> [--wide] [--json]\n'
|
|
34
|
+
+ ' node diagram.mjs inspect <in.svg> [--wide] [--json]';
|
|
35
|
+
|
|
36
|
+
function say(obj, text) { if (JSON_OUT) console.log(JSON.stringify(obj)); else console.log(text); }
|
|
37
|
+
function die(msg, code = 2) { console.error(msg); process.exit(code); }
|
|
38
|
+
|
|
39
|
+
// ── Archify 찾기: --archify > ARCHIFY_DIR > 에이전트 스킬 폴더 넷 ──
|
|
40
|
+
function findArchify(explicit) {
|
|
41
|
+
const home = os.homedir();
|
|
42
|
+
const cands = [explicit, process.env.ARCHIFY_DIR,
|
|
43
|
+
path.join(home, '.claude/skills/archify'), path.join(home, '.agents/skills/archify'),
|
|
44
|
+
path.join(home, '.cursor/skills/archify'), path.join(home, '.codex/skills/archify')].filter(Boolean);
|
|
45
|
+
for (const d of cands) if (fs.existsSync(path.join(d, 'bin/archify.mjs'))) return d;
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ── playwright-core 와 크롬: figure.mjs·shot.mjs 와 같은 자리를 찾는다 ──
|
|
50
|
+
async function browser() {
|
|
51
|
+
const home = os.homedir();
|
|
52
|
+
const cands = [process.env.PLAYWRIGHT_CORE,
|
|
53
|
+
path.join(home, '.claude/skills/gazette-report/scripts/node_modules/playwright-core/index.mjs'),
|
|
54
|
+
path.join(process.cwd(), 'node_modules/playwright-core/index.mjs'),
|
|
55
|
+
path.join(process.cwd(), 'node_modules/playwright/index.mjs')].filter(Boolean);
|
|
56
|
+
let chromium = null;
|
|
57
|
+
for (const c of cands) if (fs.existsSync(c)) { ({ chromium } = await import(pathToFileURL(c).href)); break; }
|
|
58
|
+
if (!chromium) { try { ({ chromium } = await import('playwright')); } catch { /* 아래에서 멈춘다 */ } }
|
|
59
|
+
if (!chromium) die('playwright(-core) 를 못 찾음 — PLAYWRIGHT_CORE=<index.mjs 절대경로> 로 알려 주거나 gazette-report 스킬의 scripts/node_modules 를 두십시오');
|
|
60
|
+
const exe = process.env.CHROME || process.env.PLAYWRIGHT_CHROMIUM || findChromium();
|
|
61
|
+
return chromium.launch(exe ? { executablePath: exe } : {});
|
|
62
|
+
}
|
|
63
|
+
function findChromium() {
|
|
64
|
+
const mac = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
|
|
65
|
+
if (fs.existsSync(mac)) return mac;
|
|
66
|
+
const home = os.homedir();
|
|
67
|
+
for (const r of [path.join(home, 'Library/Caches/ms-playwright'), path.join(home, '.cache/ms-playwright')]) {
|
|
68
|
+
if (!fs.existsSync(r)) continue;
|
|
69
|
+
const dirs = fs.readdirSync(r).filter(d => /^chromium(_headless_shell)?-\d+$/.test(d)).sort().reverse();
|
|
70
|
+
for (const d of dirs) {
|
|
71
|
+
for (const rel of ['chrome-mac-arm64/Chromium.app/Contents/MacOS/Chromium', 'chrome-mac/Chromium.app/Contents/MacOS/Chromium',
|
|
72
|
+
'chrome-headless-shell-mac-arm64/chrome-headless-shell', 'chrome-linux/chrome', 'chrome-headless-shell-linux64/chrome-headless-shell']) {
|
|
73
|
+
const p = path.join(r, d, rel); if (fs.existsSync(p)) return p;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ── 페이지 안에서 돌 코드: 계산된 스타일을 속성으로, 런타임 흔적은 걷는다 ──
|
|
81
|
+
const INLINE = `(() => {
|
|
82
|
+
const PROPS = ['fill','fill-opacity','fill-rule','stroke','stroke-width','stroke-dasharray','stroke-dashoffset','stroke-linecap',
|
|
83
|
+
'stroke-linejoin','stroke-miterlimit','stroke-opacity','opacity','font-size','font-weight','font-style','text-anchor',
|
|
84
|
+
'dominant-baseline','letter-spacing','paint-order','clip-path','mask','filter','marker-start','marker-mid','marker-end',
|
|
85
|
+
'text-decoration','transform','transform-origin','mix-blend-mode'];
|
|
86
|
+
const svg = document.querySelector('svg[role="img"]') || document.querySelector('svg');
|
|
87
|
+
if (!svg) return { error: 'svg 가 없다' };
|
|
88
|
+
// 상세 단계·애니메이션은 뷰어의 것 — 가장 자세한 단계로, 움직임 없이.
|
|
89
|
+
const css = [...document.querySelectorAll('style')].map(s => s.textContent).join('\\n');
|
|
90
|
+
const full = /data-detail-level="full"/.test(css) ? 'full' : (/data-detail-level="read"/.test(css) ? 'read' : null);
|
|
91
|
+
if (full) for (const el of document.querySelectorAll('[data-detail-level]')) el.setAttribute('data-detail-level', full);
|
|
92
|
+
const st = document.createElement('style');
|
|
93
|
+
st.textContent = '*{animation:none!important;transition:none!important}';
|
|
94
|
+
document.head.appendChild(st);
|
|
95
|
+
void document.body.offsetHeight;
|
|
96
|
+
const clone = svg.cloneNode(true);
|
|
97
|
+
const live = [svg, ...svg.querySelectorAll('*')];
|
|
98
|
+
const copy = [clone, ...clone.querySelectorAll('*')];
|
|
99
|
+
const DROP = new Set(['script','style','foreignObject','animate','animateTransform','animateMotion','set','iframe','object','embed','video','audio']);
|
|
100
|
+
let minFont = null, texts = 0;
|
|
101
|
+
for (let i = live.length - 1; i >= 0; i--) {
|
|
102
|
+
const a = live[i], b = copy[i];
|
|
103
|
+
if (!b) continue;
|
|
104
|
+
if (DROP.has(a.localName)) { b.remove(); continue; }
|
|
105
|
+
const cs = getComputedStyle(a);
|
|
106
|
+
if (cs.display === 'none' || cs.visibility === 'hidden' || (cs.opacity === '0' && a !== svg)) { b.remove(); continue; }
|
|
107
|
+
const par = (a.parentElement && a !== svg) ? getComputedStyle(a.parentElement) : null;
|
|
108
|
+
let style = '';
|
|
109
|
+
for (const p of PROPS) {
|
|
110
|
+
const v = cs.getPropertyValue(p);
|
|
111
|
+
if (!v) continue;
|
|
112
|
+
if (v === 'none' && !['fill', 'stroke'].includes(p)) continue;
|
|
113
|
+
if (par && par.getPropertyValue(p) === v && !['fill', 'stroke', 'font-size', 'font-weight', 'opacity'].includes(p)) continue;
|
|
114
|
+
style += p + ':' + v + ';';
|
|
115
|
+
}
|
|
116
|
+
if (a.localName === 'text' || a.localName === 'tspan') {
|
|
117
|
+
const fs = parseFloat(cs.fontSize);
|
|
118
|
+
if (Number.isFinite(fs) && (a.textContent || '').trim()) { texts++; minFont = minFont === null ? fs : Math.min(minFont, fs); }
|
|
119
|
+
}
|
|
120
|
+
for (const at of Array.from(b.attributes)) {
|
|
121
|
+
if (at.name === 'class' || at.name.startsWith('data-') || at.name.startsWith('on') || at.name === 'style' || at.name === 'tabindex') b.removeAttribute(at.name);
|
|
122
|
+
if ((at.name === 'href' || at.name === 'xlink:href') && !(at.value.startsWith('#') || at.value.startsWith('data:image/'))) b.removeAttribute(at.name);
|
|
123
|
+
}
|
|
124
|
+
if (style) b.setAttribute('style', style);
|
|
125
|
+
}
|
|
126
|
+
clone.removeAttribute('width'); clone.removeAttribute('height');
|
|
127
|
+
clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
|
|
128
|
+
const vb = (clone.getAttribute('viewBox') || '').trim().split(/[\\s,]+/).map(Number);
|
|
129
|
+
return { svg: new XMLSerializer().serializeToString(clone), minFont, texts, vw: vb[2] || 0, vh: vb[3] || 0, level: full };
|
|
130
|
+
})()`;
|
|
131
|
+
|
|
132
|
+
function measure(svg, wideFlag, extra = {}) {
|
|
133
|
+
const bytes = Buffer.byteLength(svg, 'utf8');
|
|
134
|
+
const vb = /viewBox\s*=\s*"\s*([-\d.]+)[\s,]+([-\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)\s*"/i.exec(svg);
|
|
135
|
+
const vw = vb ? parseFloat(vb[3]) : 0, vh = vb ? parseFloat(vb[4]) : 0;
|
|
136
|
+
const sizes = [...svg.matchAll(/font-size\s*[:=]\s*"?\s*([\d.]+)\s*(px)?/gi)].map(m => parseFloat(m[1])).filter(Number.isFinite);
|
|
137
|
+
const minFont = extra.minFont ?? (sizes.length ? Math.min(...sizes) : null);
|
|
138
|
+
const display = wideFlag ? WIDE : COLUMN;
|
|
139
|
+
const shown = (vw && minFont !== null) ? minFont * display / vw : null;
|
|
140
|
+
const texts = extra.texts ?? (svg.match(/<text\b/gi) || []).length;
|
|
141
|
+
const verdicts = [];
|
|
142
|
+
if (!vw) verdicts.push(['R0', 'fail', 'viewBox 가 없다 — 폭을 알 수 없어 렌더러가 싣지 않는다']);
|
|
143
|
+
if (bytes > MAX_BYTES) verdicts.push(['R0', 'fail', bytes + 'B > 상한 ' + MAX_BYTES + 'B — 노드를 줄이거나 PNG 도판으로']);
|
|
144
|
+
if (shown !== null && shown < MIN_FONT) verdicts.push(['R1', 'fail', '가장 작은 글자 ' + minFont + 'px × (' + display + '/' + vw + ') = 표시 ' + shown.toFixed(1) + 'px < ' + MIN_FONT + 'px — ' + (wideFlag ? '노드·라벨을 줄인다' : '--wide 로 전폭에 세우거나 노드를 줄인다')]);
|
|
145
|
+
if (vw && vh / vw > 1.8) verdicts.push(['R9', 'warn', '세로가 폭의 ' + (vh / vw).toFixed(2) + '배 — 포털이 폭을 깎는다(1.8배 안으로)']);
|
|
146
|
+
if (!texts) verdicts.push(['R4', 'warn', '글자가 0 — alt 만으로 읽히는 도해인지 확인']);
|
|
147
|
+
return { bytes, vw, vh, minFont, display, shownMinFont: shown === null ? null : Math.round(shown * 10) / 10, texts, verdicts };
|
|
148
|
+
}
|
|
149
|
+
function report(out, m, extra) {
|
|
150
|
+
const bad = m.verdicts.some(v => v[1] === 'fail');
|
|
151
|
+
const lines = [out + ' — ' + m.bytes + 'B · viewBox ' + m.vw + '×' + m.vh + ' · 글자 ' + m.texts + '개 · 가장 작은 글자 ' + (m.minFont ?? '?') + 'px → 표시 ' + (m.shownMinFont ?? '?') + 'px(폭 ' + m.display + ')',
|
|
152
|
+
...m.verdicts.map(v => ' ' + (v[1] === 'fail' ? '✗' : '⚠') + ' ' + v[0] + ' ' + v[2]),
|
|
153
|
+
bad ? ' → 렌더러도 같은 ⚠ 를 낸다. 고친 뒤 다시.' : ' ✓ 관보 diagrams[].svg 에 넣을 수 있다'];
|
|
154
|
+
say({ ok: !bad, out, ...m, ...extra }, lines.join('\n'));
|
|
155
|
+
process.exit(bad ? 3 : 0);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function exportSvg(html, out) {
|
|
159
|
+
const b = await browser();
|
|
160
|
+
try {
|
|
161
|
+
const ctx = await b.newContext({ viewport: { width: 1600, height: 1000 }, reducedMotion: 'reduce', colorScheme: 'light' });
|
|
162
|
+
const page = await ctx.newPage();
|
|
163
|
+
await page.goto(pathToFileURL(path.resolve(html)).href, { waitUntil: 'load' });
|
|
164
|
+
await page.waitForTimeout(700);
|
|
165
|
+
const r = await page.evaluate(INLINE);
|
|
166
|
+
if (!r || r.error) die('export 실패: ' + (r && r.error));
|
|
167
|
+
fs.writeFileSync(out, r.svg, 'utf8');
|
|
168
|
+
return r;
|
|
169
|
+
} finally { await b.close(); }
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
(async () => {
|
|
173
|
+
if (cmd === 'inspect') {
|
|
174
|
+
const [, inp] = pos; if (!inp) die(usage);
|
|
175
|
+
return report(inp, measure(fs.readFileSync(inp, 'utf8'), wide));
|
|
176
|
+
}
|
|
177
|
+
if (cmd === 'export') {
|
|
178
|
+
const [, html, out] = pos; if (!html || !out) die(usage);
|
|
179
|
+
const r = await exportSvg(html, out);
|
|
180
|
+
return report(out, measure(r.svg, wide, { minFont: r.minFont, texts: r.texts }), { source: html, level: r.level });
|
|
181
|
+
}
|
|
182
|
+
if (cmd === 'render') {
|
|
183
|
+
const [, type, ir, out] = pos; if (!type || !ir || !out) die(usage);
|
|
184
|
+
const dir = findArchify(opt('--archify'));
|
|
185
|
+
if (!dir) die('Archify 를 못 찾음 — `npx skills add tt-a1i/archify -g` 로 설치하거나 --archify <dir> / ARCHIFY_DIR 로 알려 주십시오');
|
|
186
|
+
const tmp = path.join(os.tmpdir(), 'diagram-' + process.pid + '.html');
|
|
187
|
+
const d = spawnSync(process.execPath, [path.join(dir, 'bin/archify.mjs'), 'deliver', type, path.resolve(ir), tmp, '--quality', 'showcase', '--json'], { encoding: 'utf8' });
|
|
188
|
+
let receipt = null; try { receipt = JSON.parse(d.stdout); } catch { /* 아래에서 원문을 보인다 */ }
|
|
189
|
+
if (d.status !== 0 || !receipt || !receipt.ok) die('archify deliver 실패(exit ' + d.status + ')\n' + (d.stdout || '') + (d.stderr || ''), 4);
|
|
190
|
+
const r = await exportSvg(tmp, out);
|
|
191
|
+
fs.rmSync(tmp, { force: true });
|
|
192
|
+
return report(out, measure(r.svg, wide, { minFont: r.minFont, texts: r.texts }), { archify: dir, validation: receipt.validation, ir: path.resolve(ir), level: r.level });
|
|
193
|
+
}
|
|
194
|
+
die(usage);
|
|
195
|
+
})();
|
|
@@ -26,7 +26,9 @@
|
|
|
26
26
|
"symptom": "도안 C 의 한 줄 띠 글자로 안내를 놓치지 않는가?",
|
|
27
27
|
"cause": "공개된 타사 사례 넷을 대조함. 한 줄 띠 안내가 창 안내보다 놓쳤다는 문의가 많았다는 사례가 셋.",
|
|
28
28
|
"fix": "세 도안 가운데 도안 C 가 놓칠 위험이 가장 큼. 이 앱에서 직접 잰 값은 아직 없음.",
|
|
29
|
-
"how_to_check": "
|
|
30
|
-
"remaining": "타사 사례라 이 앱 이용자와의 차이는 남음. 근거 등급이 한 단 낮은 이유임."
|
|
29
|
+
"how_to_check": "건 끝의 원본에서 사례 넷의 공개 원문을 열면 같은 표로 대조할 수 있음.",
|
|
30
|
+
"remaining": "타사 사례라 이 앱 이용자와의 차이는 남음. 근거 등급이 한 단 낮은 이유임.",
|
|
31
|
+
"source_url": "https://example.com/notice-banner-cases",
|
|
32
|
+
"source_text": "타사 사례 넷의 공개 원문 목록"
|
|
31
33
|
}
|
|
32
34
|
]
|
|
@@ -26,7 +26,9 @@
|
|
|
26
26
|
"symptom": "더 싼 방식 B 로도 같은 이득을 얻을 수 있지 않은가?",
|
|
27
27
|
"cause": "타사 공개 사례 6건을 대조함. 방식 B 는 자료가 백만 건을 넘는 곳에서 정확도가 급락한 사례가 4건.",
|
|
28
28
|
"fix": "우리 자료 규모는 그 문턱을 이미 넘었음. 규모가 절반 아래로 줄면 사례의 조건과 달라짐.",
|
|
29
|
-
"how_to_check": "
|
|
30
|
-
"remaining": "타사 사례라 우리 자료 특성과의 차이는 남음. 직접 실험은 하지 않음(근거 등급이 한 단 낮은 이유임)."
|
|
29
|
+
"how_to_check": "건 끝의 원본에서 사례 6건의 공개 원문을 열 수 있고, 규모·정확도 수치는 부록 4에 모아 둠.",
|
|
30
|
+
"remaining": "타사 사례라 우리 자료 특성과의 차이는 남음. 직접 실험은 하지 않음(근거 등급이 한 단 낮은 이유임).",
|
|
31
|
+
"source_url": "https://example.com/model-b-scale-cases",
|
|
32
|
+
"source_text": "타사 사례 6건의 공개 원문 목록"
|
|
31
33
|
}
|
|
32
34
|
]
|
|
@@ -66,14 +66,21 @@
|
|
|
66
66
|
"q": "회신 뒤에 할 일이 또 있는가?",
|
|
67
67
|
"a": [
|
|
68
68
|
"시험판에서 고른 도안의 화면을 확인하는 일 하나임.",
|
|
69
|
-
"기한은 01절 둘째 항목, 볼 경우는 07절
|
|
69
|
+
"기한은 01절 둘째 항목, 볼 경우는 07절 표 7-2."
|
|
70
70
|
]
|
|
71
71
|
},
|
|
72
72
|
{
|
|
73
|
-
"q": "
|
|
73
|
+
"q": "원본은 어디서 직접 보는가?",
|
|
74
74
|
"a": [
|
|
75
|
-
"
|
|
76
|
-
"
|
|
75
|
+
"타사 사례 넷의 공개 원문은 건 §4.3 끝의 원본에서 열림.",
|
|
76
|
+
"앱 코드·화면으로 확인한 두 건은 재현 방법 행대로 다시 볼 수 있음."
|
|
77
|
+
]
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"q": "시간이 없다면 어디를 봐야 하는가?",
|
|
81
|
+
"a": [
|
|
82
|
+
"정할 것과 세 도안은 01절, 확인한 사실의 요지는 02절에 있음.",
|
|
83
|
+
"우리회사 의견은 05절 한 곳에만 따로 둠."
|
|
77
84
|
]
|
|
78
85
|
}
|
|
79
86
|
],
|
|
@@ -129,7 +136,8 @@
|
|
|
129
136
|
"fix": "판단",
|
|
130
137
|
"check": "재현 방법",
|
|
131
138
|
"ask": "남은 불확실성",
|
|
132
|
-
"col_evidence": "근거 등급"
|
|
139
|
+
"col_evidence": "근거 등급",
|
|
140
|
+
"refs": "근거 건"
|
|
133
141
|
},
|
|
134
142
|
"parts": [
|
|
135
143
|
{
|
|
@@ -274,7 +282,7 @@
|
|
|
274
282
|
"marg": {
|
|
275
283
|
"why": "견줘서 무엇을 확인했는가?",
|
|
276
284
|
"key": "세 도안 모두 앱의 선례가 있고, 읽힘이 갈림.",
|
|
277
|
-
"advice": "
|
|
285
|
+
"advice": "문단 끝의 「근거 건」 번호는 그 문단을 받치는 건의 번호 — 화면에서는 누르면 열림."
|
|
278
286
|
}
|
|
279
287
|
},
|
|
280
288
|
{
|
|
@@ -423,7 +431,7 @@
|
|
|
423
431
|
"marg": {
|
|
424
432
|
"why": "고른 도안에서 무엇이 잘못될 수 있는가?",
|
|
425
433
|
"key": "첫 화면을 막는 도안 A 는 닫기 단추 크기로 줄임.",
|
|
426
|
-
"advice": "
|
|
434
|
+
"advice": "근거 등급이 「추정」인 행은 시험판에서 갱신."
|
|
427
435
|
},
|
|
428
436
|
"tables": [
|
|
429
437
|
{
|
|
@@ -575,7 +583,7 @@
|
|
|
575
583
|
{
|
|
576
584
|
"label": "정할 것",
|
|
577
585
|
"lead": "정할 것은 도안 하나이고, 고른 도안은 시험판에서 한 번 더 확인함.",
|
|
578
|
-
"body": "회신 후 14일 안에 시험판을 전달함(도안 C 는 21일). 확인할 네 경우는 07절
|
|
586
|
+
"body": "회신 후 14일 안에 시험판을 전달함(도안 C 는 21일). 확인할 네 경우는 07절 표 7-2 에 있음(§4.2).",
|
|
579
587
|
"refs": [
|
|
580
588
|
"4.2"
|
|
581
589
|
]
|
|
@@ -724,7 +732,7 @@
|
|
|
724
732
|
"mark": "이 브리핑의 사실은 2026. 9. 1. 기준임.",
|
|
725
733
|
"badge": "고객 공유용",
|
|
726
734
|
"note": "※ 이 문서에는 입력·저장 기능이 없음. 회신은 이 문서를 보낸 담당자에게 보내 주시기 바랍니다.",
|
|
727
|
-
"tail": "회신 뒤 일정은
|
|
735
|
+
"tail": "회신 뒤 일정은 표 7-1 대로 진행하고, 시험판 확인으로 이어짐."
|
|
728
736
|
},
|
|
729
737
|
"forbid": [
|
|
730
738
|
"(은|는|을|를) 제외(함|됨|했음|했다|한다|하였음|하였다)",
|
|
@@ -129,7 +129,8 @@
|
|
|
129
129
|
"fix": "판단",
|
|
130
130
|
"check": "재현 방법",
|
|
131
131
|
"ask": "남은 불확실성",
|
|
132
|
-
"col_evidence": "근거 등급"
|
|
132
|
+
"col_evidence": "근거 등급",
|
|
133
|
+
"refs": "근거 건"
|
|
133
134
|
},
|
|
134
135
|
"parts": [
|
|
135
136
|
{
|
|
@@ -238,7 +239,7 @@
|
|
|
238
239
|
"marg": {
|
|
239
240
|
"why": "두 달 검토로 무엇을 확인했는가?",
|
|
240
241
|
"key": "정확도 이득은 실재하고, 비용은 약 1.4배임.",
|
|
241
|
-
"advice": "
|
|
242
|
+
"advice": "문단 끝의 「근거 건」 번호는 그 문단을 받치는 건의 번호 — 화면에서는 누르면 열림."
|
|
242
243
|
}
|
|
243
244
|
},
|
|
244
245
|
{
|
|
@@ -399,7 +400,7 @@
|
|
|
399
400
|
"marg": {
|
|
400
401
|
"why": "바꾸면 무엇이 잘못될 수 있는가?",
|
|
401
402
|
"key": "비용 격차는 계약의 사용량 상한 조항으로 줄임.",
|
|
402
|
-
"advice": "
|
|
403
|
+
"advice": "근거 등급이 「추정」인 행은 첫 분기 실측에서 갱신."
|
|
403
404
|
},
|
|
404
405
|
"tables": [
|
|
405
406
|
{
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"marg": {
|
|
83
83
|
"why": "무엇을 확인해야 하는가?",
|
|
84
84
|
"key": "이 절이 조치 사항의 전부임. 나머지 절은 근거.",
|
|
85
|
-
"advice": "
|
|
85
|
+
"advice": "제목 옆 「근거」 번호는 처리 상세가 실린 건임. 화면에서는 누르면 열림."
|
|
86
86
|
} },
|
|
87
87
|
|
|
88
88
|
{ "no": "02", "id": "sec-2", "label": "02", "name": "요지", "kind": "summary", "part": "2",
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# 도해 — Archify 를 관보에 흡수한다 (2026-09-16)
|
|
2
|
+
|
|
3
|
+
> 「관보에 불가능한 로직이 있다면 가능하게 고도화하고, Archify 까지 호환되도록」(사용자 지시).
|
|
4
|
+
> 이 문서는 **Archify 로 도해를 그려 관보 `sections[].diagrams[]` 에 앉히는 절차**의 정본이다.
|
|
5
|
+
> 렌더러 쪽 계약(칸·경고)은 `lim-portal-format.md` §11-9, 가독성 잣대는 `readability.md` R1.
|
|
6
|
+
|
|
7
|
+
## 0. 왜 도판(PNG)이 아니라 도해(SVG)인가
|
|
8
|
+
|
|
9
|
+
| | 도판 `plates`(PNG) | 도해 `diagrams`(인라인 SVG) |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| 글자 | 비트맵 — 확대하면 뭉개지고 검사기가 못 읽는다 | 활자 — 인쇄·확대에서 선명하고 문서의 Pretendard 를 물려받는다 |
|
|
12
|
+
| 예산 | 장당 80KB · 합계 400KB | 장당 250KB(문서 크기에 직접 더해진다) |
|
|
13
|
+
| 만드는 법 | `figure.mjs` 로 굽는다 | `diagram.mjs render/export` 로 굳힌다 |
|
|
14
|
+
| 언제 | 실제 화면 캡처 · 비트맵 원본 | 구조·흐름·순서·데이터 흐름·상태 전이 |
|
|
15
|
+
|
|
16
|
+
열람 CSP 는 인라인 SVG 를 허용한다(2026-08-24 실측). 스크립트는 여전히 0 이다 — 그래서 Archify 의
|
|
17
|
+
**뷰어(팬·줌·애니메이션·검색)는 관보에 실리지 않고 정지 화면만 실린다.** 그것이 문서의 뜻이다.
|
|
18
|
+
|
|
19
|
+
## 1. Archify 는 어디에 있나
|
|
20
|
+
|
|
21
|
+
Archify 는 에이전트 스킬이다(MIT, `tt-a1i/archify`). **이 스킬 패키지에 동봉하지 않는다** — 8MB 이고 자체
|
|
22
|
+
갱신 절차를 가진다. 사용자 기계의 스킬 폴더에 설치돼 있어야 한다.
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx skills add tt-a1i/archify -g # Claude Code · Cursor · Codex 공용 설치기
|
|
26
|
+
node ~/.claude/skills/archify/bin/archify.mjs doctor
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`diagram.mjs` 는 `--archify <dir>` → `ARCHIFY_DIR` → `~/.claude/skills/archify` → `~/.agents/skills/archify`
|
|
30
|
+
→ `~/.cursor/skills/archify` → `~/.codex/skills/archify` 순으로 찾는다. 없으면 설치 안내와 함께 멈춘다(exit 2).
|
|
31
|
+
|
|
32
|
+
## 2. 절차 — IR 을 쓰고, 굳히고, 재고, 앉힌다
|
|
33
|
+
|
|
34
|
+
1. **종류를 고른다** — Archify `SKILL.md` 의 Type router: `architecture`(구성 요소·경계) · `workflow`(절차·관문·runbook)
|
|
35
|
+
· `sequence`(호출 순서) · `dataflow`(파이프라인·계보) · `lifecycle`(상태 전이). 모르면
|
|
36
|
+
`node bin/archify.mjs guide "<상황>" --json`.
|
|
37
|
+
2. **IR 을 쓴다** — Archify 의 `schemas/<type>.schema.json` 과 `examples/` 를 읽고 새로 쓴다(사실은 그 문서의 데이터에서).
|
|
38
|
+
한국어 라벨을 쓴다. 노드 12개 이하, 주 경로 하나. `meta.quality_profile: "showcase"`. `meta.animation` 은 적지 않는다.
|
|
39
|
+
**`meta.legend: {"mode": "hidden"}` 을 적는다** — Archify 의 자동 범례는 영문 7.5px 라 관보 관문(15px)에 걸리고 문서에서는
|
|
40
|
+
캡션이 그 몫을 한다(실측 2026-09-16). **열은 4개 이하**(viewBox 폭 ≈ 1,000 이하) — 전폭 1030px 에 1:1 로 앉아야 글자가 산다.
|
|
41
|
+
부제(`sublabel`)는 9자 안, 간선 라벨은 5자 안 — 글자를 키워도(아래 4단계) 노드 폭 168 안에 서는 길이다.
|
|
42
|
+
⚠ Archify 의 라벨 폭 셈은 라틴 글자 기준이라 **한글 라벨은 예측보다 넓게 선다** — 라벨 겹침 진단이 나면 낱말을 줄이거나
|
|
43
|
+
노드 `width` 를 준다.
|
|
44
|
+
3. **검증한다** — `node bin/archify.mjs validate <type> <ir.json> --quality showcase --json` 이 9/9 · 오류 0 · 경고 0.
|
|
45
|
+
4. **굳힌다** —
|
|
46
|
+
```bash
|
|
47
|
+
node ~/.claude/skills/genetics-report/assets/diagram.mjs render <type> <ir.json> <out.svg> --wide --min-text 16 --tight --json
|
|
48
|
+
```
|
|
49
|
+
deliver → 브라우저에서 계산된 스타일을 속성으로 굳힘 → 클래스·스크립트·스타일·애니메이션·원래 표현 속성 걷음 → SVG 하나.
|
|
50
|
+
font-family 는 굳히지 않는다(문서 활자를 물려받게).
|
|
51
|
+
- `--min-text 16`: Archify 는 글자 크기를 컴파일러 상수로 박아(제목 16 · 부제 11 · 간선 8 · 구역 7~10) IR 로는 못 키운다.
|
|
52
|
+
문턱 아래 글자를 뷰박스 단위 16 으로 키우되 배치는 그대로 두고, **글자 상자 겹침·노드 넘침을 브라우저에서 재서** 영수증에 낸다(R5).
|
|
53
|
+
노드 부제는 제목과 16px 간격이라 `--min-sub`(기본 15) 까지만 키운다 — 그 위는 제목과 겹친다.
|
|
54
|
+
- `--tight`: Archify 는 화면 비율에 맞춰 아래를 비워 두는데 문서에서는 빈 띠다. 배경 격자를 뺀 내용 상자 + 12 로 viewBox 를 조인다.
|
|
55
|
+
- 영수증의 「작은 글자」 다섯 개가 무엇인지 보고 IR 에서 부제·태그를 줄인다 — 글자를 더 키우는 것은 답이 아니다. 상세 단계는 가장 자세한 단계로 고정한다.
|
|
56
|
+
5. **잰다** — 위 명령이 함께 잰다: 바이트(≤ 250,000) · viewBox · 가장 작은 글자 × (표시 폭 ÷ viewBox 폭) ≥ 15px(R1) ·
|
|
57
|
+
세로/폭 ≤ 1.8(R9). ✗ 면 exit 3 — 렌더러도 같은 ⚠ 를 내므로 **여기서 고친다**: 넓은 도해는 `--wide`(표시 1030px),
|
|
58
|
+
그래도 모자라면 노드·라벨을 줄인다. 글자를 키우는 것은 Archify 의 몫이 아니다 — 도해를 쪼갠다.
|
|
59
|
+
6. **앉힌다** — `report.json`:
|
|
60
|
+
```json
|
|
61
|
+
"diagrams": [{
|
|
62
|
+
"id": "upgrade-path", "caption": "업그레이드가 지나는 세 관문",
|
|
63
|
+
"svg": "<svg …>…</svg>", "alt": "스냅샷 복원 → 시험 → 운영 적용",
|
|
64
|
+
"verdict": "직접 확인", "wide": true,
|
|
65
|
+
"origin": "archify workflow v2 · ir/upgrade-path.workflow.json"
|
|
66
|
+
}]
|
|
67
|
+
```
|
|
68
|
+
`svg` 는 파일 내용을 문자열로 넣는다(JSON 이스케이프). `origin` 은 렌더 안 됨 — IR 경로를 남긴다.
|
|
69
|
+
그림 번호는 차트 → 흐름도 → 도해 → 도판 순으로 자리가 매긴다. 본문에서 `{{그림:upgrade-path}}` 로 가리킨다.
|
|
70
|
+
7. **로컬 렌더 → dry_run** — 다른 장치와 같다. 렌더러 ⚠ 문구는 `lim-portal-format.md` §10-1.
|
|
71
|
+
|
|
72
|
+
## 3. 무엇이 관보에 들어가고 무엇이 빠지나
|
|
73
|
+
|
|
74
|
+
| Archify 가 주는 것 | 관보에서 |
|
|
75
|
+
|---|---|
|
|
76
|
+
| 정지 SVG(노드·간선·라벨·범례) | 그대로 — 활자로 |
|
|
77
|
+
| 팬·줌·검색·초점·경로 추적·프레젠테이션 | 빠진다(스크립트 0). 필요하면 문서 밖 링크로 Archify HTML 을 따로 준다 |
|
|
78
|
+
| 다크/라이트 | 라이트만 굳힌다(`colorScheme: light`) |
|
|
79
|
+
| 애니메이션(`trace`) | 걷는다 — `reducedMotion` + `animation:none` 으로 정지 상태를 굳힌다 |
|
|
80
|
+
| 브랜드 마크(`brand`) | `data:image/` 로 박힌 것만 남고 외부 주소는 걷힌다 |
|
|
81
|
+
| 뷰어 UI 언어(`meta.locale`) | 무관 — 뷰어가 안 실린다 |
|
|
82
|
+
|
|
83
|
+
## 4. 하지 않는 것
|
|
84
|
+
|
|
85
|
+
- Mermaid 를 관보에 직접 싣지 않는다. Mermaid 원문은 Archify 의 입력이다(Archify `SKILL.md` 「Mermaid input」) — Archify IR 로
|
|
86
|
+
다시 쓰고 위 절차를 밟는다.
|
|
87
|
+
- SVG 를 손으로 고치지 않는다. 고칠 것은 IR 이고, 굳히기는 다시 돌린다(재현성).
|
|
88
|
+
- 도해 하나에 문장을 넣지 않는다(R4). 설명은 캡션 한 문장과 본문.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// 판 갱신 검토 — 한 판의 **변경 자리만** 세 관점으로 읽고 심각도 3 이상은 반박한다.
|
|
2
|
+
// 실행: Workflow 도구에 이 파일을 scriptPath 로 주고 args 를 넘긴다(배열·객체는 JSON 값 그대로).
|
|
3
|
+
// args = { file: "<새 판 report.json 절대경로>",
|
|
4
|
+
// backup: "<직전 판 백업 절대경로>", // 새 판을 만든 스크립트의 출발점
|
|
5
|
+
// edit_script: "<edit-N.py 절대경로>", // put/sub 호출로 변경 자리가 전부 적힌 스크립트(멱등)
|
|
6
|
+
// doc: "<문서 이름 한 줄>", // 예: "고객사 결정 브리핑 16판"
|
|
7
|
+
// renderer: "<gazette.py 절대경로>", // 기본 report-portal private/builder/gazette.py
|
|
8
|
+
// handoff: "<HANDOFF.md 절대경로>" } // 있으면 그 판의 절을 배경으로 준다
|
|
9
|
+
//
|
|
10
|
+
// 언제 쓰나 — 구조(PART·절·표)가 그대로이고 낱말·출처 줄·여백 주석만 고친 판. 구조가 바뀐 판은
|
|
11
|
+
// `reader-review.workflow.js`(여덟 관점)다. 낱말 손질 판을 검토 없이 발행하자 다음 판 8자리 손질이
|
|
12
|
+
// 생겼다(2026-09-15 고객사 결정 브리핑 15판 → 16판): 되풀이된 전제, 주어가 빠진 출처 줄, 인쇄에서 죽는 「누르면」.
|
|
13
|
+
//
|
|
14
|
+
// 세 관점 — 선형 독자(그 자리에서 뜻이 닫히나·뒤 절 번호를 가리키나·조사·중복) · 규칙 대조(렌더러 text_scan 의
|
|
15
|
+
// W2·R12h·RX_PROC·장치 이름·SOURCE_MAX·그림 번호를 코드 줄로 대조) · 사실 대조(옛 문구의 사실이 바뀌지 않았나,
|
|
16
|
+
// 백업↔새 판 JSON diff 에 스크립트가 바꾼 자리가 빠짐없이 있고 기록 안 된 변경이 없는지 — 삽입·삭제가 섞이면 줄 수는 호출 수와
|
|
17
|
+
// 다르니 자리로 센다, 수치 무변경). 심각도 3 이상만 반박 단계(최대 6).
|
|
18
|
+
// 결과 — { kept(반박을 넘긴 발견), refuted, weak(심각도 2 이하 — 사람이 훑어 고를 것), passed }.
|
|
19
|
+
export const meta = {
|
|
20
|
+
name: 'report-edition-review',
|
|
21
|
+
description: '보고서 한 판의 변경 자리(edit-N.py)를 선형 독자·규칙 대조·사실 대조 세 관점으로 읽고, 심각도 3 이상은 반박 검증한다',
|
|
22
|
+
phases: [
|
|
23
|
+
{ title: 'Review', detail: '세 관점 병렬 — 선형 독자 · 렌더러 규칙 대조 · 사실 대조' },
|
|
24
|
+
{ title: 'Verify', detail: '심각도 3 이상 발견을 반박(최대 6)' },
|
|
25
|
+
],
|
|
26
|
+
}
|
|
27
|
+
const A = args || {}
|
|
28
|
+
if (!A.file || !A.backup || !A.edit_script) throw new Error('args.file · args.backup · args.edit_script 가 필요하다')
|
|
29
|
+
const RENDERER = A.renderer || '레포 report-portal 의 private/builder/gazette.py(args.renderer 가 없으면 이 기계에서 경로를 찾아 쓴다 — 못 찾으면 규칙 관점 결과에 「렌더러 못 읽음」으로 적는다)'
|
|
30
|
+
const DOC = A.doc || A.file
|
|
31
|
+
const CTX = `대상: ${DOC}. 직전 판 → 이 판의 변경은 ${A.edit_script} 에 put/sub 호출로 전부 적혀 있다(옛 문구 → 새 문구). 새 판 원고는 ${A.file}, 직전 판은 ${A.backup}.${A.handoff ? ` 배경은 ${A.handoff} 의 이 판 절.` : ''} 한글로 답한다. 파일은 읽기만 한다 — 고치지 않는다.`
|
|
32
|
+
const FINDINGS = { type: 'object', properties: { findings: { type: 'array', items: { type: 'object', properties: { where: { type: 'string' }, severity: { type: 'integer' }, claim: { type: 'string' }, evidence: { type: 'string' }, fix: { type: 'string' } }, required: ['where', 'severity', 'claim', 'evidence'] } }, passed: { type: 'array', items: { type: 'string' } } }, required: ['findings', 'passed'] }
|
|
33
|
+
const VERDICT = { type: 'object', properties: { refuted: { type: 'boolean' }, reason: { type: 'string' } }, required: ['refuted', 'reason'] }
|
|
34
|
+
const LENSES = [
|
|
35
|
+
{ key: 'linear', prompt: `${CTX}\n[선형 독자] 새 판을 처음 읽는 고객 담당이 되어 1절부터 부록까지 순서대로 읽는다. 특히 변경 자리마다 (a) 그 자리에서 뜻이 닫히는가 (b) 앞에서 정의되지 않은 낱말·번호를 가리키는가 — 뒤 절 번호 가리킴은 금지 규칙이고 표 번호·「다음 절」로 바꿔 놓은 것도 독자를 뒤로 보내면 적는다 (c) 어색한 문장·조사·중복(절 이름과 부제, 같은 답 묶음 안의 되풀이)이 있는가 (d) 인쇄본에서 죽는 화면 어휘(「누르면」)가 있는가를 본다. 문제가 없는 자리도 passed 에 적는다. severity 1~5(5=독자가 뜻을 잃음).` },
|
|
36
|
+
{ key: 'rules', prompt: `${CTX}\n[규칙 대조] 렌더러 ${RENDERER} 의 text_scan 규칙 — W2 출처 줄(rx_loc: 쪽·행·https·건 §·§ n·부록 N, user-provided 만; rx_meta 제작 설명·그림 번호), R12h 앞 절→뒤 절 가리킴(rx_fwd_case/rx_fwd_sec/rx_other_doc, fwd_spot 이 훑는 칸), RX_PROC 과정 어휘, 장치 이름 충돌(건 행 이름 labels 낱말이 다른 절 표 열·부제에), SOURCE_MAX, 그림·표 번호 가리킴, 문장 60자·괄호 부연·표 주석 문장 수 — 를 코드에서 직접 읽고, 변경 자리의 새 문구가 각 규칙을 만족하는지, 새 문구가 다른 규칙을 새로 어기지 않는지 하나씩 대조한다. 판정 근거는 렌더러 파일의 줄 번호로 적는다.` },
|
|
37
|
+
{ key: 'facts', prompt: `${CTX}\n[사실 대조] ① 직전 판과 새 판의 JSON 을 diff 해 스크립트의 put/sub·삽입·삭제가 바꾼 자리가 빠짐없이 보이는지, 기록되지 않은 변경이 없는지 자리로 센다(삽입·삭제가 섞이면 diff 줄 수는 호출 수와 다르다). 수치(인원·kg·cm·날짜)가 한 곳도 안 바뀌었는지도 센다. ② 변경 자리마다 새 문구가 옛 문구의 사실을 바꾸지 않았는지 원고 안에서 대조한다 — 가리키는 부록·표·용어가 실제로 있는가, 이름으로 바꾼 위치가 같은 대상인가, 사실문으로 고친 문장이 요지·다른 절과 같은 사실인가. 다른 문서를 인용한 자리는 그 문서를 열 수 있으면 열어 대조하고 못 열면 「확인 불가」로 적는다. 바뀐 사실이 있으면 severity 4 이상.` },
|
|
38
|
+
]
|
|
39
|
+
const reads = await pipeline(LENSES,
|
|
40
|
+
(l) => agent(l.prompt, { label: `review:${l.key}`, phase: 'Review', schema: FINDINGS }).then((r) => r && { lens: l.key, ...r }))
|
|
41
|
+
const all = reads.filter(Boolean)
|
|
42
|
+
const strong = all.flatMap((r) => r.findings.filter((f) => f.severity >= 3).map((f) => ({ lens: r.lens, ...f }))).slice(0, 6)
|
|
43
|
+
log(`발견 ${all.reduce((n, r) => n + r.findings.length, 0)} · 심각도≥3 ${strong.length} · passed ${all.reduce((n, r) => n + r.passed.length, 0)}`)
|
|
44
|
+
const verdicts = await parallel(strong.map((f) => () =>
|
|
45
|
+
agent(`${CTX}\n[반박] 다음 발견을 원고·스크립트·렌더러를 직접 읽어 반박해 본다. 발견: (${f.lens}) ${f.where} — ${f.claim}. 근거: ${f.evidence}. 처방: ${f.fix || '(없음)'}. 발견이 틀렸거나(사실이 아님), 규칙상 문제가 아니거나, 처방이 다른 규칙을 어기면 refuted=true. 확신이 없으면 refuted=true. reason 은 파일·줄·문구 인용으로.`, { label: `verify:${f.lens}`, phase: 'Verify', schema: VERDICT }).then((v) => ({ ...f, verdict: v }))))
|
|
46
|
+
const kept = verdicts.filter(Boolean).filter((v) => v.verdict && !v.verdict.refuted)
|
|
47
|
+
const weak = all.flatMap((r) => r.findings.filter((f) => f.severity < 3).map((f) => ({ lens: r.lens, ...f })))
|
|
48
|
+
if (!all.length || all.length < LENSES.length) log(`⚠ 관점 ${LENSES.length - all.length}개가 결과를 못 냈다(API 과부하 등) — resumeFromRunId 로 다시 돌리면 끝난 관점은 캐시를 탄다`)
|
|
49
|
+
return { kept, refuted: verdicts.filter(Boolean).filter((v) => v.verdict && v.verdict.refuted), weak, passed: all.map((r) => ({ lens: r.lens, passed: r.passed })) }
|
|
@@ -115,3 +115,9 @@
|
|
|
115
115
|
미커밋·실기기 미확인 상태가 완료형으로 실렸다. 앞선 9/3 반려 「커밋이라던가 개발자 대상 브리핑은 불필요한데?」와 같은 뿌리다. → 반드시 지키는 것 3 「코드 작업을 녹일 때」 · `text_scan()` 개발 식별자 ⚠ · state 관점.
|
|
116
116
|
- **그림 번호** — 판을 고칠 때 그림 번호를 손으로 동시 치환하다 뜻이 뒤바뀐 번호와 틀린 조사가 발행됐다. → `{{그림:id}}` 토큰 · `fig_order()` · 번호 가리킴 ⚠.
|
|
117
117
|
- **밟는 순서** — 규칙은 반려마다 늘었는데 흐름표에 시각 설계·독자 검토 행이 없어 검토가 관문이 아니라 선택 단계로 읽혔다. 단계 번호는 세 체계였다. → SKILL 「작업 체크리스트」.
|
|
118
|
+
- **낱말 손질 판을 검토 없이 냄** — 개발계 새 렌더러의 경고 넷을 고친 15판을 「낱말 14자리 치환이라 사실·구조 변화 0」이라며 검토 없이 냈다.
|
|
119
|
+
뒤늦게 돌린 세 관점 검토(선형 독자·규칙 대조·사실 대조 + 반박)가 낮은 지적 14를 냈다 — 같은 답 묶음 안의 전제 되풀이, 주어가 빠져 「4차가 접수한 것」으로 읽히는 출처 줄, 인쇄에서 죽는 「누르면」, 절 이름과 부제의 되풀이. → 16판 8자리 · `edition-review.workflow.js` · 0.5단계 표 7행 · 체크리스트 7단계 독자 검토 행.
|
|
120
|
+
- **검사기가 남의 문서의 절 번호를 뒤 절로 오인** — R12h 정규식이 「검수 보고 4차 제5절」·「제10절」·「제11절」을 이 문서(7절까지)의 뒤 절로 세어, 15판이 세 자리의 위치 구절을 억지로 지웠다(3.17kg 의 자리가 사라짐).
|
|
121
|
+
→ `text_scan` W5 「N차 … 제N절」 예외 · 16판이 표 이름(「두 계산식의 차이 실측」)으로 위치를 되살림. 표 번호 선참조(「표 7-1」)를 셀지는 미결.
|
|
122
|
+
- **사용자 결정 둘(2026-09-15)** — 두 물음 「읽는 법에 확인 요청 한 줄을 둬도 되나」·「「바로 반영」 지시가 판 갱신 검토보다 앞서도 되나」에 둘 다 「응」.
|
|
123
|
+
→ SKILL 5a 요청 자리 목록·108행·0.5단계 7행 · process.md · lim-portal-format §10 경어체 행·§12.5b · genetics lim 결정 두 장.
|
|
@@ -236,7 +236,7 @@ python3 assets/render.py <report.json>
|
|
|
236
236
|
"marg": {
|
|
237
237
|
"why": "무엇을 확인해야 하는가?",
|
|
238
238
|
"key": "이 절이 조치 사항의 전부임. 나머지 절은 근거.",
|
|
239
|
-
"advice": "
|
|
239
|
+
"advice": "제목 옆 「근거」 번호는 그 항목을 받치는 건임. 화면에서는 누르면 열림."
|
|
240
240
|
}
|
|
241
241
|
}
|
|
242
242
|
```
|
|
@@ -262,6 +262,8 @@ python3 assets/render.py <report.json>
|
|
|
262
262
|
| `note` | `kind:"status"` 에서 표 아래 한 줄 | 그 줄이 빠진다 |
|
|
263
263
|
| `flow` | 의존 흐름도 한 개(11-2절). `kind` 와 무관하다 | 도해가 빠진다 |
|
|
264
264
|
| `plates` | 절 본문 도판의 `assets[].id` 목록(11-1절) | 도판이 빠진다 |
|
|
265
|
+
| `quotes` | 원문 인용 목록(11-8절). `kind` 와 무관하다 | 인용이 빠진다 |
|
|
266
|
+
| `diagrams` | 도해(인라인 SVG) 목록(11-9절). `kind` 와 무관하다 | 도해가 빠진다 |
|
|
265
267
|
|
|
266
268
|
### ⚠ `no` 와 `id` 는 **안 읽힌다**
|
|
267
269
|
|
|
@@ -396,7 +398,7 @@ python3 assets/render.py <report.json>
|
|
|
396
398
|
| 그림 N장은 놓을 자리가 없어 | `logo`·`figure` 밖의 `role`(`hero`·`chapter`·`eof`) |
|
|
397
399
|
| 도판 N장이 어느 절에도 걸리지 않아 | `sections[].plates` 에 그 id 를 안 적었다 — 11-1절 |
|
|
398
400
|
| 절 …의 도판 「…」를 assets 에서 못 찾았습니다 | `plates` 가 없는 `assets[].id` 를 가리킨다 |
|
|
399
|
-
| 도판 「…」에 그림 바이트가 없습니다 | `file`·`source` 없이 이름만
|
|
401
|
+
| 도판 「…」에 그림 바이트가 없습니다 | `file`·`source` 없이 이름만 왔다. 로컬 `render.py` 는 `file` 을 바이트로 굳히지 않아 `file` 이 있어도 뜬다 — 로컬에서는 이 줄을 빼고 나머지 ⚠ 를 읽는다 |
|
|
400
402
|
| `chapters`·`figure`(**최상위 칸**) 는 자리가 없어 | 옛 서식 전용 칸. `assets[].role:"figure"` 와 다른 것이다 |
|
|
401
403
|
| 절 …의 `principles`/`logic` 은 자리가 없어 | 같음 |
|
|
402
404
|
| `wide`/`marg_width` 값이 길이 단위가 아니라 | CSS 값 검사 |
|
|
@@ -417,19 +419,20 @@ python3 assets/render.py <report.json>
|
|
|
417
419
|
경고가 하나라도 있으면 **그것부터 고친다.**
|
|
418
420
|
경고를 남긴 채 고객에게 링크를 보내지 마라.
|
|
419
421
|
|
|
420
|
-
**통과 기준은 이 한 곳에 둔다 — 통과 = 작성자 몫 ⚠0.** 원고로 고칠 수 없는 경고는 아래
|
|
421
|
-
나머지는 전부 0 으로 만든다.
|
|
422
|
+
**통과 기준은 이 한 곳에 둔다 — 통과 = 작성자 몫 ⚠0.** 원고로 고칠 수 없는 경고는 아래 넷뿐이고,
|
|
423
|
+
나머지는 전부 0 으로 만든다. 넷은 핸드오프에 사유 한 줄을 남기고 넘긴다.
|
|
422
424
|
|
|
423
425
|
| 작성자 몫이 아닌 경고 | 왜 | 누구 몫 |
|
|
424
426
|
|---|---|---|
|
|
425
427
|
| 「고객사 로고가 SVG 로 등록돼 있어 표지에 못 싣습니다」·「표지용 파생본이 아직 없습니다」 | 소속처 로고 설정의 상태다 | 소속처 관리 화면(사람) |
|
|
426
428
|
| 「바깥에서 받아 오는 것이 있습니다」 — 그 호스트가 **본문 링크**(`[글자](주소)`·맨 주소·건 `source_url`)뿐일 때 | 입고 검사가 `<a href>` 호스트까지 센다(§11-6). 누르는 링크는 받아 오는 자원이 아니다. `<img src>` 등으로 뜨면 작성자 몫 | 서버 검사 수리 전까지 사람이 호스트 목록을 대조 |
|
|
427
|
-
| 경어체 경고 — PART I 간지(`parts[]`)의 요청
|
|
429
|
+
| 경어체 경고 — PART I 간지(`parts[]`)의 요청 문장이나 읽는 법(`guide`)의 확인 요청 한 줄에서만 날 때 | 둘 다 요청 자리(§14-2 · §12.5b)인데 서버 문체 관문이 이 자리는 아직 센다 | 사람이 자리를 확인 |
|
|
430
|
+
| 레포의 렌더러·서버 관문이 운영보다 앞선 동안, 새 규칙대로 고친 자리에 운영 `dry_run` 이 옛 규칙으로 내는 경고 | 운영은 배포된 판으로 센다 — 되돌리면 새 규칙에 걸리고, 배포되면 사라진다 | 새 판에서 0 인지 먼저 잰다 — 렌더러 경고는 레포 작업본을 부르는 로컬 `render.py`, 서버 문체 관문·입고 검사·서식 등록부 칸은 레포 개발 스택의 `dry_run`(절차: genetics lim `cases/dev-스택이-OrbStack-덧판https-base_url으로-떠-있으면-…`). 0 이면 핸드오프에 「배포 뒤 해소」 한 줄 |
|
|
428
431
|
|
|
429
432
|
요청 절의 경어체는 이제 예외가 아니다. 서버 문체 관문(`report_tone.php report_tone_warnings()`)은 요청 자리(`open_items[].body`·`cta`·
|
|
430
433
|
`colophon.note`·`asks` 절 `marg`)와 「」 인용을 빼고 세고, 로컬 `tone.mjs` 는 판단 항목 본문과 「」 인용을 뺀다.
|
|
431
434
|
⚠ 서버 쪽 자리 가림은 운영 반영 확인 전(2026-09-14 갈래)이다 — 배포 전의 운영 `dry_run` 이 요청 자리에서 내는 경어체 경고는 이 표의 셋째 행처럼 자리를 확인하고 넘긴다. 그래도 경어체 경고가
|
|
432
|
-
뜨면 기록 자리(`open_items[].title` 포함)나 `guide` 에 요청 문장이 샌 것이다 — 요청 문장은 요청 절로 옮긴다(SKILL 5a 「보고 칸에 요청문을 두지 마라」).
|
|
435
|
+
뜨면 기록 자리(`open_items[].title` 포함)나 `guide` 에 요청 문장이 샌 것이다 — 요청 문장은 요청 절로 옮긴다(SKILL 5a 「보고 칸에 요청문을 두지 마라」). 단 `guide` 에서 요청 절의 확인 요청을 가리키는 한 줄은 허용한다(2026-09-15 사용자 결정) — 그 한 줄에서만 나는 경어체 경고는 §10 통과 기준 표의 행으로 넘긴다.
|
|
433
436
|
건의 `how_to_check` 에서 걸린 명령형은 「보고 칸에 요청문이 N곳 있습니다 — 요청 절(open_items)로 옮깁니다」로 따로 뜬다.
|
|
434
437
|
톤 게이트는 경고이지 거절이 아니다(실측 2026-09-11: 경어체 경고를 안은 `update_report` 2회 모두 발행 성공).
|
|
435
438
|
「경고 0」을 요청 자리를 개조식으로 내려서 맞추려다 **말투 반려가 네 번 났다**(genetics `decisions/고객-문서-요청-자리는-하십시오체로-맺는다-…`).
|
|
@@ -446,7 +449,12 @@ python3 assets/render.py <report.json>
|
|
|
446
449
|
|
|
447
450
|
| ⚠ | 문턱 | 처방 |
|
|
448
451
|
|---|---|---|
|
|
449
|
-
| 절 N 「이름」이 글로만 서 있습니다(M자) | 산문 400자 이상인데
|
|
452
|
+
| 절 N 「이름」이 글로만 서 있습니다(M자) | 산문 400자 이상인데 표·흐름도·차트·도판·인용·도해 0 | 그 절의 요지를 표·흐름도·차트·도판·인용·도해 중 하나로 세운다(§11-1~11-9) |
|
|
453
|
+
| 절 %s 인용 %d 에 출처 줄이 없습니다 — 원본 위치 한 구절(주소·쪽·행)을 적습니다 | `quotes[]` 에 `source_text`·`source_url` 둘 다 없다 | 원문의 위치 한 구절(주소·쪽·행)을 적는다(§11-8) |
|
|
454
|
+
| 절 %s 도해 「%s」의 글자가 표시 %dpx 로 섭니다 — %dpx 이상이 되게 wide 로 두거나 노드를 줄입니다 | 도해 SVG 의 가장 작은 font-size × (표시 폭 ÷ viewBox 폭) < 15px(readability R1 과 같은 잣대) | `wide: true` 로 전폭에 세우거나 노드·라벨을 줄인다(§11-9) |
|
|
455
|
+
| 절 %s 도해 「%s」가 %d바이트입니다 — 상한 %d바이트, 노드를 줄이거나 도판(PNG)으로 싣습니다 | `diagrams[].svg` 가 250,000B 초과 | 도해를 나누거나 PNG 도판으로 |
|
|
456
|
+
| 절 %s 도해 「%s」에 viewBox 가 없거나 svg 가 아닙니다 — 폭을 알 수 없어 싣지 않습니다 | 루트 `<svg>` 에 viewBox 가 없다 | `diagram.mjs export` 로 굳힌 SVG 를 넣는다(§11-9) |
|
|
457
|
+
| 절 %s 시계열 「%s」의 표식 「%s」가 점에 없습니다 — marks[].x 는 points[].x 와 같은 값이어야 섭니다 | `charts[].marks[].x` 가 어느 `points[].x` 와도 같지 않다 | 점의 x 값 그대로 적는다(§11-7) |
|
|
450
458
|
| 절 N 「이름」의 k번째 문단이 M자입니다 | 문단 한 개 220자 초과(세 줄) | 두 문장으로 끊거나 수치를 표로 내린다 |
|
|
451
459
|
| 절 N 「이름」 산문에 참조 번호가 M개입니다 | 산문(문단·도입문·여백 주석)에 6.6·2.12a 같은 번호 4개 이상 | 번호는 표·건·판단 항목에만, 산문은 뜻으로 잇는다 |
|
|
452
460
|
| 판단 요청 N항이 산문으로만 섭니다 | `open_items` 6항 이상인데 asks 절에 표·흐름도 0 | 한눈표(항 · 지금 · 그대로 두면 · 바꾸면 — 두 길 대칭, 권고 열·시한 칩 없음) + 관문 흐름도(§11-2). |
|
|
@@ -458,7 +466,7 @@ python3 assets/render.py <report.json>
|
|
|
458
466
|
| 도판 「%s」 출처 줄에 제작 설명이나 그림 번호가 있습니다(「%s」) — 위치 한 구절만, 제작 기록은 origin 으로 | `role:"figure"` 전부(provider 무관)의 `source_text` 를 주소를 지운 뒤 정규식 `(합성·재현)(이며·임·함·한 것·했) \| 예시(이며·임·라) \| (실제·실기기) 화면이 아님 \| 도안이 아님 \| 자리 표시 \| 규격(과 문구)?의 출처 \| 찍(음·은 것) \| 배율 \| N배 밀도 \| 크롭 \| 잘라 \| 자름 \| 그림 N-N` 으로 센다. **캡션은 보지 않는다** — 캡션의 한 낱말 「— 합성」·「재현」·「갈무리」는 정직성 이름표다. 출처 줄에서도 맨 낱말 「합성」·「재현」은 안 걸고 서술형만 건다. 「골밀도」 같은 값 이름은 「N배 밀도」가 아니라 안 걸린다 | 출처 줄에는 위치 한 구절만. 만든 방식·결정 경로는 `origin`(렌더 안 됨)과 도판 HTML 머리 주석에. 바깥 원본이 없는 우리 도안·흐름은 출처 줄을 비운다(readability R12e) |
|
|
459
467
|
| 표 「%s」 주석이 %d문장입니다 — ※ 주석은 %d문장 안 | `tables[].note` 문장 3 이상(둘째 `%d` 는 상한 2). 문장은 렌더러가 문장 블록을 세우는 판별(`boundary.py` — 날짜 마침표 「2026. 9. 30.」·「9. 8.」은 경계가 아님)로 센다. | 두 문장 안으로, 나머지는 본문·건으로(readability R12a) |
|
|
460
468
|
| 판단 항목 「%s」 본문이 %d문장입니다 — %d문장 안 | `open_items[].body` 문장 5 이상(둘째 `%d` 는 상한 4 · 자수 220 은 §10 의 R11f 행이 따로 센다) | 기한과 두 길 이름 → 길마다 달라지는 것 → 근거 절 번호까지 네 문장. 상세는 건에(readability R12b) |
|
|
461
|
-
| 앞 절이 뒤 절을 가리키는 곳이 %d개입니다(%s) — 낱말·규칙은 첫 등장 자리에서 풀고, 뒤 절이 앞 절을 되짚는 방향만 둡니다(readability R12h) | 절 i(배열 순서, 1부터)의 산문 — `sections[].name·sub·lede·paragraphs` · `tables[].caption·cols·rows·note` · `flow.caption·note`·`rows[].no`(건 좌표 꼴 `^§?\d{1,2}[-.]\d{1,2}$` 의 앞 숫자)·`cells[].text·note` · `marg(why/key/advice)` · 그 절이 `kind:"cases"` 면 같은 그룹 `cases[].symptom·cause·fix·how_to_check·remaining` · `kind:"asks"` 면 `cta[]`·`open_items[].title·body` · `sections[].plates` 가 가리키는 `assets[].caption·source_text` — 에서 정규식 `§\s*(\d+)\.\d+` 와 `(?<![\d.])(\d{1,2})절` 의 앞 숫자가 i 보다 큰 것을 센다. `summary[].lead·body` 와 `refs[]` 는 세지 않는다 — 요약 근거 게이트(§8)가 `refs` 로 서고 「해당 절」·「근거」 단추가 되므로 뜀이 아니다. `%s` 는 자리와 걸린 낱말 최대 3개 | 낱말·규칙·예시 선언은 **첫 등장 자리에서 한 구절**로 풀고, 뒤 절 번호 대신 그 자리에서 뜻이 서는 말을 둔다. 근거 건은 `refs` 로만. 뒤 절이 앞 절을 되짚는 방향(05절에서 「03절 표」)은 둔다(readability R12h · SKILL 0.5단계 7행) |
|
|
469
|
+
| 앞 절이 뒤 절을 가리키는 곳이 %d개입니다(%s) — 낱말·규칙은 첫 등장 자리에서 풀고, 뒤 절이 앞 절을 되짚는 방향만 둡니다(readability R12h) | 절 i(배열 순서, 1부터)의 산문 — `sections[].name·sub·lede·paragraphs` · `tables[].caption·cols·rows·note` · `flow.caption·note`·`rows[].no`(건 좌표 꼴 `^§?\d{1,2}[-.]\d{1,2}$` 의 앞 숫자)·`cells[].text·note` · `marg(why/key/advice)` · 그 절이 `kind:"cases"` 면 같은 그룹 `cases[].symptom·cause·fix·how_to_check·remaining` · `kind:"asks"` 면 `cta[]`·`open_items[].title·body` · `sections[].plates` 가 가리키는 `assets[].caption·source_text` — 에서 정규식 `§\s*(\d+)\.\d+` 와 `(?<![\d.])(\d{1,2})절` 의 앞 숫자가 i 보다 큰 것을 센다. 「N차 … 제N절」(남의 문서의 절 번호, 예 「검수 보고 4차 제5절」)은 건너뛴다. `summary[].lead·body` 와 `refs[]` 는 세지 않는다 — 요약 근거 게이트(§8)가 `refs` 로 서고 「해당 절」·「근거」 단추가 되므로 뜀이 아니다. `%s` 는 자리와 걸린 낱말 최대 3개 | 낱말·규칙·예시 선언은 **첫 등장 자리에서 한 구절**로 풀고, 뒤 절 번호 대신 그 자리에서 뜻이 서는 말을 둔다. 근거 건은 `refs` 로만. 뒤 절이 앞 절을 되짚는 방향(05절에서 「03절 표」)은 둔다(readability R12h · SKILL 0.5단계 7행) |
|
|
462
470
|
| 표 「%s」의 「%s」 열은 모든 행이 같습니다 — 차이를 못 나르는 열은 주석으로 | `doc_type` 에 「브리핑」 · `kind:"asks"` 절의 `tables[]` 가운데 행이 셋 이상인 표에서, 첫 열을 뺀 한 열이 모든 행에서 같은 값(빈 칸 아님)이다. asks 밖 절(일정 표 「누가」 열 등)·행 둘 이하·빈 칸뿐인 열은 안 센다 | 그 열을 빼고 ※ 주석 한 줄로(visual-first §5). 선택지를 가르는 열만 한눈표에 둔다(§12.3b 대칭 불변식) |
|
|
463
471
|
| 건 행 이름 「%s」 낱말이 %d곳에도 쓰였습니다(%s) — 한 낱말은 한 장치에만, 다른 자리는 다른 낱말로 | 데이터가 준 `labels` 의 `symptom`·`cause`·`fix`·`check` 값이 `kind≠cases` 절의 `sub`·`tables[].caption`·`tables[].cols` 에 낱말로 선다(뒤에 조사 하나까지 같은 낱말 — 「판단력」·「판단이다」는 다른 낱말). 괄호 안은 자리 이름 최대 3개. `labels` 를 안 준 기본 이름·건 절 자신·절 이름·도입문·산문은 안 본다 | 의견 절의 열·부제는 「의견」으로(§3.5 · visual-first §3) |
|
|
464
472
|
| 선택지 기호 %s 에 이름이 %d개입니다(「%s」·「%s」) — 기호는 한 목록에만 붙이고 이름은 표마다 같게 | 모든 표에서 한 축(열 머리 줄, 또는 행 첫 칸 열)마다 `^([A-Z])[\s·:.)]+(\S.*)$` 꼴을 모으고, 그 축의 기호가 A 부터 차례(둘 이상)일 때만 선택지 정의로 친다. 문서 전체에서 한 글자에 이름이 둘 이상이면 센다(짧은 이름과 긴 이름이 갈린 것도). 산문(「방식 A」)·A 부터 차례가 아닌 축(부분을 가리키는 행)·괄호 부연 차이는 안 센다 | A·B·C 는 한 목록에만. 같은 선택지는 표마다 같은 이름(visual-first §3) |
|
|
@@ -637,6 +645,10 @@ CSS 로는 못 한다. 우측 판독부에 남는 것은 문서 전체의 사실
|
|
|
637
645
|
| `cells[].note` | 칸의 둘째 줄(작은 글자). 조건·한정을 담는다 |
|
|
638
646
|
| `cells[].path` | 경로·기술 문자열. 별도 활자로 서고 `/` 앞에서 끊어 읽는다 |
|
|
639
647
|
|
|
648
|
+
**`mode: "cause"` — 원인 사슬.** 「해야 할 것 → 이미 된 것 → 그러면」이 아니라 「사실 → 우리 상태 → 결과」를
|
|
649
|
+
잇는 사슬(요금이 왜 붙었나, 장애가 왜 났나)에는 첫 칸의 노란 강조 면이 뜻을 비튼다. `"mode": "cause"` 를 주면
|
|
650
|
+
그 면을 걷어 세 칸이 같은 무게로 선다. 열 수·칸 수 계약은 그대로다(2026-09-16).
|
|
651
|
+
|
|
640
652
|
**그림 번호는 도판과 한 셈을 쓴다** — 한 절에 흐름도와 도판이 같이 있으면 본문 순서대로
|
|
641
653
|
`그림 N-1`·`그림 N-2` 가 된다. 따로 세면 같은 번호가 두 번 나와 상호참조가 무엇을 가리키는지
|
|
642
654
|
알 수 없게 된다.
|
|
@@ -809,6 +821,84 @@ CSS 로는 못 한다. 우측 판독부에 남는 것은 문서 전체의 사실
|
|
|
809
821
|
- 짝이 안 맞거나 모르는 톤은 그대로 글자로 나간다 — 조용히 사라지지 않는다.
|
|
810
822
|
- 편집기 서식 구간(`_runs`)과 함께 와도 된다. 구간이 태그를 씌운 뒤 표기를 푼다.
|
|
811
823
|
|
|
824
|
+
## 11-7. 시계열 — 한 값이 시간에 따라 어떻게 갔나 (2026-09-16)
|
|
825
|
+
|
|
826
|
+
분포 막대(11-4)는 「몇 개의 수를 견준다」. 하루·달 단위로 잰 값이 **언제 계단을 만드는지**는 선이어야 보인다 —
|
|
827
|
+
AWS 요금 조사(2026-09)에서 트래픽·로그·DB 용량이 8월 1일 앞뒤로 평평하다는 것을 막대로는 말할 수 없었다.
|
|
828
|
+
|
|
829
|
+
```json
|
|
830
|
+
"charts": [
|
|
831
|
+
{ "kind": "series", "caption": "ALB 처리량",
|
|
832
|
+
"unit": "GB", "points": [{ "x": "2026-05-01", "y": "8.7" }, { "x": "2026-05-02", "y": "10.5" }],
|
|
833
|
+
"marks": [{ "x": "2026-08-01", "label": "8/1 과금 시작" }],
|
|
834
|
+
"note": "하루 단위 합산." }
|
|
835
|
+
]
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
| 칸 | 뜻 |
|
|
839
|
+
|---|---|
|
|
840
|
+
| `kind` | `"series"`. 없으면 분포 막대다 |
|
|
841
|
+
| `points[].x` · `.y` | x 는 `YYYY-MM-DD` 가 기본(달 눈금과 달별 글자 행을 자동으로 세운다). 그 밖의 문자열도 된다(여섯 눈금). y 는 표기 그대로 |
|
|
842
|
+
| `marks[].x` · `.label` | 사건 세로선. x 는 어느 `points[].x` 와 **같은 값**이어야 선다(아니면 ⚠) |
|
|
843
|
+
| `summary[]` | `{label, value, note?}` — 글자 행을 손으로 줄 때. 없으면 달마다 평균·최대를 자동으로 적는다 |
|
|
844
|
+
| `unit` · `caption` · `note` | 막대와 같다 |
|
|
845
|
+
|
|
846
|
+
인라인 SVG 로 그리되 **스크립트는 0** 이다. 글자(눈금·표식 라벨)는 SVG text 인데 CSS 활자 등급(`--t-sm`)을
|
|
847
|
+
물려받아 본문과 같은 크기로 선다. 값을 전부 글자로 남길 수 없으므로 **달마다 평균·최대**가 글자 행으로 남는다 —
|
|
848
|
+
막대와 같은 원칙(그림이 안 보여도 문서가 선다). 그림 번호는 막대·흐름도·도판과 한 셈이다.
|
|
849
|
+
|
|
850
|
+
## 11-8. 원문 인용 — 문구를 원문 그대로 (2026-09-16)
|
|
851
|
+
|
|
852
|
+
스킬 「반드시 지키는 것」 11 은 「인용은 원문 문구 그대로」라고 하는데, 관보에는 그 자리가 없었다 — 표 칸은
|
|
853
|
+
20자, 산문은 우리 글이다. 공식 문서·계약·회신의 문구를 **손대지 않고** 싣는 자리다.
|
|
854
|
+
|
|
855
|
+
```json
|
|
856
|
+
"quotes": [
|
|
857
|
+
{ "text": "Starting March 1, 2024, Amazon RDS automatically charges you for RDS Extended Support.",
|
|
858
|
+
"lang": "en",
|
|
859
|
+
"source_text": "AWS 문서 › Amazon RDS Extended Support (4문단)",
|
|
860
|
+
"source_url": "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/extended-support.html",
|
|
861
|
+
"verdict": "직접 확인",
|
|
862
|
+
"translation": "표준 지원이 끝난 다음 날부터 자동으로 과금함." }
|
|
863
|
+
]
|
|
864
|
+
```
|
|
865
|
+
|
|
866
|
+
| 칸 | 뜻 |
|
|
867
|
+
|---|---|
|
|
868
|
+
| `text` | 원문 그대로. 인라인 표기(굵게·칩)를 **풀지 않는다**. 줄바꿈은 그대로 선다 |
|
|
869
|
+
| `lang` | 원문 언어 코드. `en` 이면 낱말 단위 접기가 라틴 규칙으로 바뀐다 |
|
|
870
|
+
| `source_text` · `source_url` | 출처 줄(도판과 같은 장치). **둘 다 없으면 ⚠** — 독자가 원본에 닿아야 인용이다 |
|
|
871
|
+
| `verdict` | 도판과 같은 축(직접 확인 · 조사 보고 · 미확인) — 캡션 끝 칩 |
|
|
872
|
+
| `translation` | 우리말 풀이 한 문장(선택). 우리 글이므로 표기를 푼다 |
|
|
873
|
+
|
|
874
|
+
인용은 절 본문의 **맨 앞**에 선다(원문이 가장 강한 근거이고 표·도해는 그것을 줄인 것). `kind` 와 무관하다.
|
|
875
|
+
번호는 없다 — 그림·표가 아니다.
|
|
876
|
+
|
|
877
|
+
## 11-9. 도해 — Archify 를 흡수한다 (2026-09-16)
|
|
878
|
+
|
|
879
|
+
구조·흐름·순서·데이터 흐름·상태 전이는 Archify(`tt-a1i/archify`, 에이전트 스킬)로 그린다. 관보에는 그 산출물의
|
|
880
|
+
**정지 SVG 만** 인라인으로 앉힌다 — 절차는 스킬 `references/diagrams.md`, 굳히는 도구는 `assets/diagram.mjs`.
|
|
881
|
+
|
|
882
|
+
```json
|
|
883
|
+
"diagrams": [
|
|
884
|
+
{ "id": "upgrade-path", "caption": "업그레이드가 지나는 세 관문",
|
|
885
|
+
"svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1200 520\">…</svg>",
|
|
886
|
+
"alt": "스냅샷 복원 → 시험 → 운영 적용", "verdict": "직접 확인", "wide": true,
|
|
887
|
+
"origin": "archify workflow v2 · ir/upgrade-path.workflow.json" }
|
|
888
|
+
]
|
|
889
|
+
```
|
|
890
|
+
|
|
891
|
+
| 칸 | 뜻 |
|
|
892
|
+
|---|---|
|
|
893
|
+
| `svg` | `diagram.mjs export` 가 굳힌 SVG 문자열. 서버가 다시 걷는다 — 스크립트·스타일·foreignObject·이벤트·애니메이션·외부 href(문서 안 `#`·`data:image/` 만 남는다). 루트 width/height 는 버리고 viewBox 로 선다 |
|
|
894
|
+
| `id` | `{{그림:id}}` 로 가리키는 이름 · 메모가 무는 `data-uid` |
|
|
895
|
+
| `caption` · `alt` · `source_text` · `verdict` · `note` | 도판과 같다. 우리가 그린 도해의 출처 줄은 비운다(R12e) |
|
|
896
|
+
| `wide` | 전폭(1030px). 글자가 표시 15px 에 못 미치는 넓은 도해는 켠다 — 그리드 밖, 절 끝에 선다 |
|
|
897
|
+
| `origin` | 렌더 안 됨 — IR 경로·생성 도구를 남긴다 |
|
|
898
|
+
|
|
899
|
+
관문(⚠, §10-1): 바이트 250,000 초과 · viewBox 없음 · **가장 작은 글자 × (표시 폭 ÷ viewBox 폭) < 15px**(readability R1).
|
|
900
|
+
`diagram.mjs` 가 같은 잣대로 먼저 재므로 렌더러 ⚠ 는 마지막 그물이다. 그림 번호는 차트 → 흐름도 → 도해 → 도판 순.
|
|
901
|
+
|
|
812
902
|
## 12. 판정 밖의 두 모드 — 가르치는 문서 · R&D 브리핑
|
|
813
903
|
|
|
814
904
|
독자가 **판정**하는 문서(검수 보고)와 **배우는** 문서, **결정하는** 문서는 뼈대가 다르다.
|
|
@@ -895,7 +985,7 @@ Submissions·WHO/SUPPORT 정책 브리프(1:3:25)가 **같은 뼈대**를 요구
|
|
|
895
985
|
| 두 길의 결과 | 항마다 「지금 · 그대로 두면 · 바꾸면」을 **대칭**으로. 한 길의 결과만 있는 열 금지. 길이 셋 이상(도안 결재형)이면 행이 선택지, 열이 **이용자가 보는 것**(모양 · 누르면 · 넘기면 · 앱의 선례)만 — 편익·해악·불확실성은 선택지 비교 행렬 한 곳에(같은 행을 두 표에 세우지 않는다, readability R12c) | `asks` 절 `tables[]` **한 곳** — 열 「항 · 지금 · 그대로 두면 · 바꾸면」(도안 결재형은 「도안 · 모양 · 누르면 · 앱의 선례」). 권고 열·시한 칩 없음. 모든 행이 같은 값인 열은 ※ 주석으로(렌더러 ⚠) |
|
|
896
986
|
| 배경 | 무엇을 왜 검토했는가 — 요지 브리핑판. **앞선 결정을 사실로 쓰는 조건**: 결정권이 고객에게 있는 항은 고객이 정한 기록(회신 원문·회의록·결정문)이 있고 그 항이 원문에서 실제로 닫혀 있을 때만(「정해야 함·차기」로 남아 있지 않을 때) 사실로 쓴다. 기록이 없거나 FCG·내부 대화에서 정했으면 사실로 쓰지 않고 결정 물음이나 FCG 의견 절로 둔다. FCG 권한 안의 작업 결정(작업 순서·일정)은 「FCG 작업」으로 사실 서술한다. 본문에는 결정 출처·날짜를 적지 않는다 — 확인은 원고 밖 결정 장부(작업 폴더 `decisions.json`, 발행 안 함)에서 한다 | `summary` **4~6문단**(칸 수의 정본은 SKILL.md 4~6단계). 결정에 직접 닿는 문단만 `body`(세 문장 안), 근거 문단은 `lead` 한 줄 + `refs` 로 두고 `body` 는 비운다 — 본문은 건이 가진다(readability R12g). 집행자: 결정 기록 확인은 사람 눈과 독자 검토 「사실 대조」 관점(`decisions`) · 주체 없는 결정 명사는 렌더러 ⚠(§10-1 과정 어휘) · 문단 모양은 「구조」 관점 |
|
|
897
987
|
| 선택지 비교 | **견줄 것 × 길** 전치 행렬 — 행이 견줄 것(편익 · 해악/비용 · 불확실성 · 이행 장벽 · 성숙도 · 근거 등급 · 정할 자리 · 다시 볼 때), 열이 길. **판정 열 없음**. **지금 열린 판단 항목의 선택지를 모두 열로 세운다** — 검토했으나 고르게 하지 않는 길은 더 둘 수 있다. 앞선 결정으로 이미 닫힌 물음의 길은 요지 한 줄과 건으로 내린다. 물음이 바뀐 판에서는 행렬부터 다시 짠다 | `kind:"note"` 절 + `tables[]`. 「하지 않기로 한 것」 절은 두지 않는다 — 그 내용은 이 표의 「정할 자리」·「다시 볼 때」 행이 받는다. 「근거 등급」은 여기서 **행의 첫 칸**으로 서고, §10 검사는 열과 행 첫 칸을 다 본다 |
|
|
898
|
-
| 근거 | 물음 → 관찰 → 판단 → 재현 방법 → 남은 불확실성 | `kind:"cases"` 절. `labels` 로 필드표 라벨을 바꾼다(`symptom`→물음 · `cause`→관찰 · `fix`→판단 · `check`→재현 방법). `grades` = 근거 등급 4단 |
|
|
988
|
+
| 근거 | 물음 → 관찰 → 판단 → 재현 방법 → 남은 불확실성 | `kind:"cases"` 절. `labels` 로 필드표 라벨을 바꾼다(`symptom`→물음 · `cause`→관찰 · `fix`→판단 · `check`→재현 방법). `grades` = 근거 등급 4단 · 요지 문단 끝 근거 이름 `labels.refs` 는 「근거 건」 — 기본 「해당 절」 뒤에 건 번호(`04-1`)가 서면 절 번호로 읽힌다 |
|
|
899
989
|
| 한눈에 | 근거 전체의 강도 대조 | `kind:"status"` 절(대조표) |
|
|
900
990
|
| FCG 의견 | 낼지 말지는 따로 정한다 — 내면 **한 절 한 자리**, 표 한 장(선택지 · 의견 · 까닭 · 근거 — **선택지마다 한 행**) | `kind:"note"` 절 하나, 이름에 「의견」. 부제·표 열에도 「의견」을 쓰고 건 행 이름(`labels` — 「판단」 등)은 쓰지 않는다(§10-1 「건 행 이름」). 제목 · 표 열 · 칩 · 여백 주석 · 요약면에는 권고를 배지 않는다 |
|
|
901
991
|
| 바꾸는 길의 위험 | 바꾸는 길로 갔을 때의 위험과 완화. 없으면 「해당 없음」과 그 근거 | `kind:"note"` 절 + `tables[]` |
|
|
@@ -916,7 +1006,7 @@ Submissions·WHO/SUPPORT 정책 브리프(1:3:25)가 **같은 뼈대**를 요구
|
|
|
916
1006
|
도안 결재형 본보기는 `assets/example/report-briefing-designs.json`, 두 길 기본형은 `report-briefing.json`.
|
|
917
1007
|
|
|
918
1008
|
**분량은 1:3:25 다** — `guide`(1쪽에서 결정) : `summary`+`asks`(3쪽) : `parts` 본문(25쪽) : `appendix`(그 뒤).
|
|
919
|
-
첫 쪽만 읽고 결정할 수 있어야 하고, 본문은 그 결정을 **검증**하러 오는 사람을 위한 것이다.
|
|
1009
|
+
첫 쪽만 읽고 결정할 수 있어야 하고, 본문은 그 결정을 **검증**하러 오는 사람을 위한 것이다. 읽는 법(`guide`) 물음이 그 첫 쪽의 길이다 — 무엇을 정하는가 · 선택지는 어디서 보는가 · 원본은 어디서 직접 보는가(0.5단계 5a) · 시간이 없다면 어디를 보는가.
|
|
920
1010
|
|
|
921
1011
|
단서 셋.
|
|
922
1012
|
- 브리핑은 content-pipeline §4.2 유형 **J(삭제)와 충돌한다** — 검수 보고에서 「채택되지 않은 대안」은 지우지만,
|
|
@@ -985,12 +1075,12 @@ Submissions·WHO/SUPPORT 정책 브리프(1:3:25)가 **같은 뼈대**를 요구
|
|
|
985
1075
|
|
|
986
1076
|
```json
|
|
987
1077
|
{"title": "시험판에서 고른 도안의 화면 확인 요청",
|
|
988
|
-
"body": "시험판을 받으신 뒤 7일 안에
|
|
1078
|
+
"body": "시험판을 받으신 뒤 7일 안에 확인 프로필 표의 네 경우대로 보이는지 확인해 주시기 바랍니다. 표와 다르게 보이면 그 화면을 회신해 주시기 바랍니다.",
|
|
989
1079
|
"refs": ["4.2"]}
|
|
990
1080
|
```
|
|
991
1081
|
|
|
992
1082
|
- 표지 `meta`·`notice` 에는 사실문으로만 올린다(「시험판 확인은 받으신 뒤 7일 안」). 요청 문장은 이 건 하나에만 둔다.
|
|
993
|
-
-
|
|
1083
|
+
- 표지·현황 줄·다른 절에 「확인 부탁드립니다」를 흩어 두지 않는다 — 요청은 요청 절에만(SKILL 5a 「보고 칸에 요청문을 두지 마라」). `guide`(읽는 법)에는 이 건을 가리키는 요청 한 줄을 둘 수 있다(「시험판 확인은 확인 프로필 표의 네 경우로 부탁드립니다」 꼴, 2026-09-15 사용자 결정).
|
|
994
1084
|
- 제목이 「… 확인 요청」으로 끝나면 렌더러가 기한 ⚠ 를 「확인 요청 「…」에 기한이 없습니다」로 가른다 — 결정 물음 문구(「결정을 요청한 것이 아닙니다」)로 오분류되지 않는다.
|
|
995
1085
|
- 무엇을 확인하면 되는지는 §12.3b 「확인 프로필」 표가 받는다.
|
|
996
1086
|
|
|
@@ -1206,3 +1296,6 @@ Submissions·WHO/SUPPORT 정책 브리프(1:3:25)가 **같은 뼈대**를 요구
|
|
|
1206
1296
|
- 문서마다 달라지는 것(장 색 · PART 색 · 러닝 푸터 · 판형)은 `gazette.py` 의 `tail_css()` 가 낸다.
|
|
1207
1297
|
- 렌더러의 원본은 레포 하나다(`private/builder/`).
|
|
1208
1298
|
스킬에 사본을 두지 마라 — `render.py` 가 `FORKABLE` 목록으로 막고 있다.
|
|
1299
|
+
- 원고 전체를 훑는 ⚠ 를 더하거나 서식 등록부(`REPORT_TEMPLATES`)에 칸을 더하면 `tests/builder.test.py` 와 로컬 `render.py` 로 끝내지 않는다.
|
|
1300
|
+
서버는 등록부 항목을 통째로 설정에 섞어 렌더러에 넘기는데, 시험은 원고만 넣고 `render.py` 는 `render`·`css`·폰트만 얹는다 — 등록부에만 있는 칸을 원고로 세는 오탐은 서버를 거친 `dry_run` 에서만 드러난다.
|
|
1301
|
+
실제 보고서 body 를 레포 개발 스택의 `dry_run` 에 한 번 태운다(절차: genetics lim `cases/dev-스택이-OrbStack-덧판https-base_url으로-떠-있으면-…`).
|
|
@@ -283,7 +283,7 @@ RANK_TAG = ['live', 'data', 'done', 'soon'] # 배지 종류
|
|
|
283
283
|
그래서 검수·보고 문서의 `open_items[].title` 은 전부 1인칭이다 — `검증에 쓴 표본이 작음`, `같은 전화번호를 쓰는 계정의 주인은 우리가 판단할 수 없음`.
|
|
284
284
|
결정 문서(브리핑)의 판단 항목은 다르다 — 고객이 고를 **물음**이다(SKILL 0.5단계 5행 · lim-portal-format §12.5b).
|
|
285
285
|
독자의 할 일이 둘이면(결정 + 시험판 확인) 둘째는 `open_items` 두 번째 건 「… 확인 요청」으로 두고 본문 첫 문장에 상대 기한을 넣는다 —
|
|
286
|
-
표지·현황 줄에는 사실문으로만
|
|
286
|
+
표지·현황 줄에는 사실문으로만 올린다. 읽는 법(`guide`)에는 요청 절의 확인 요청을 가리키는 요청 한 줄을 둘 수 있다(2026-09-15 사용자 결정) — 그 밖의 요청문은 두지 않는다(보고 자리, SKILL 5a). 집행자: `gazette.py` `brief_checks()` 기한 ⚠(「확인 요청」 문구로 갈림) · 독자 검토 decider 관점.
|
|
287
287
|
|
|
288
288
|
| 규칙 | 어기면 |
|
|
289
289
|
|---|---|
|
|
@@ -472,7 +472,7 @@ RANK_TAG = ['live', 'data', 'done', 'soon'] # 배지 종류
|
|
|
472
472
|
2026-08-18 이전에는 이 값이 **한 번도 도구에 도달하지 않았다**(서버가 `error_log` 로만 흘렸다) — 에이전트는 「경고 없음」으로 읽었고 확인할 방법이 없었다.
|
|
473
473
|
지금은 dry_run 과 실제 발행 응답 **양쪽에** 실린다.
|
|
474
474
|
|
|
475
|
-
⚠ 가 하나라도 있으면 그것부터 고친다. 작성자가 없앨 수 없는 경고(
|
|
475
|
+
⚠ 가 하나라도 있으면 그것부터 고친다. 작성자가 없앨 수 없는 경고(`lim-portal-format.md` §10 통과 기준 표의 행)는
|
|
476
476
|
줄마다 까닭을 핸드오프에 적는다 — 경고 기준선을 기계로 대조하는 도구는 아직 없어, 새 경고가 그 속에 묻히지 않게 사람이 판마다 대조한다.
|
|
477
477
|
경고를 안고 고객에게 링크를 보내지 마라.
|
|
478
478
|
|
|
@@ -568,7 +568,7 @@ RANK_TAG = ['live', 'data', 'done', 'soon'] # 배지 종류
|
|
|
568
568
|
도구가 `publish` 를 명시적으로 참으로 주지 않는 한 고객에게 안 보인다. 여는 것은 10단계다.
|
|
569
569
|
|
|
570
570
|
수정은 SKILL.md 8단계 「수정 절차」(`get_report` 머리줄 확인 → 백업 → 고침 → 로컬 렌더·dry_run·독자 검토 → `update_report` → 9단계).
|
|
571
|
-
**같은 링크가 새 내용을 가리킨다.** 초안 `update_report` 도 **본문 판(`body_rev`)** 을 올리고 사용자가 그 판을 읽으므로 독자 검토를 통과한 본문 판만
|
|
571
|
+
**같은 링크가 새 내용을 가리킨다.** 초안 `update_report` 도 **본문 판(`body_rev`)** 을 올리고 사용자가 그 판을 읽으므로 독자 검토를 통과한 본문 판만 올린다. 사용자가 「바로 반영」을 지시하면 먼저 올리고 검토를 곧바로 뒤따라 돌린다 — 발행 이력 행(`history`)은 공개 뒤에만 는다(SKILL 3c).
|
|
572
572
|
사람이 고친 초안이면 덮기 전에 묻는다 — `update_report` 에 `body_rev` 잠금 칸이 아직 없어 기계가 막지 않는다.
|
|
573
573
|
|
|
574
574
|
포털 셸이 문서 **둘레**에 놓는 것(검토 회신·핵심 요약·진척률 바)은 저작 대상이 아니다.
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
| R12e 도판 출처 | 출처 줄(`assets[].source_text`)은 **원본 위치 한 구절** — 쪽·행·주소·건 §·부록 N 가운데 하나(R11c 의 40자 안). 결정 절처럼 건보다 앞에 서는 도판은 뒤 절 § 대신 규격이 실린 부록 N 을 가리킨다(뒤 절 § 는 R12h 에 걸린다). 제3자 자료에서 낸 도판은 그 한 구절로 독자가 원본에 닿아야 한다(주소, 또는 `source_url` 이 있는 건 §). `source_url` 은 도판에 그려지지 않으므로 대신하지 못한다. **제작 방식**(합성·재현·예시·자리 표시·잘라냄·배율·촬영 환경)과 결정 경로는 `origin`(렌더 안 됨)과 도판 HTML 머리 주석에만 적는다. 바깥 원본이 없는 우리 도안·흐름은 출처 줄을 비운다. 그림 번호는 출처 줄에 쓰지 않는다(번호는 옮겨 다닌다). 정직성은 캡션의 한 낱말(「— 도안」·「— 재현」·「— 합성」)이면 된다. 기계는 두 가지를 센다 — 위치 없음은 `provider:"user-provided"`(파일로 싣는 합성 포함)만(W2), 제작 설명·그림 번호는 `role:"figure"` 전부의 출처 줄. 캡션은 안 본다 | gazette.py `text_scan`(W2 · 제작 메타) · 캡션·부록의 노이즈는 사람 눈 | ⚠ |
|
|
57
57
|
| R12f 되풀이 | **같은 항의 이름·기한**이 문서 전체에서 서는 자리 ≤ **3** — 네 자리부터는 되풀이 | 없음 — 사람 눈 | — |
|
|
58
58
|
| R12g PART 사이 | 같은 사실(시각·금액·건 제목·핵심 명제)이 **두 PART** 에 서면 한쪽은 건 번호로 가리키기만 한다. 요지·대조표·건(같은 건의 세 배율)은 한 PART — 요청 없는 문서에서 「경과 / 원인」으로 가르지 않는다(process.md 「PART 를 먼저 가른다」). 두 절 `marg.key` 가 낱말만 다르고 같은 명제면 되풀이다 — 글자 비교로는 안 잡힌다. 요약면 수치·요지 lead 가 문서 전체를 줄여 말하는 것은 예외(요지 문단이 건을 하나씩 옮겨 적으면 예외 아님). 실사례 2026-09-12: PART I 사실 27개 중 21개가 PART II 에 다시 섬, dry_run 경고 0 | 없음 — 독자 검토 「구조」 관점 · 사람 눈(렌더러 ⚠ 는 제안 단계) | — |
|
|
59
|
-
| R12h 선참조 | 앞 절이 **뒤 절**을 가리키지 않는다 — 요지 문단·판단 항목 본문의 `§n.n`(근거는 `refs[]` 로만 — 「해당 절」·「근거」 단추가 되므로 뜀이 아니다), 흐름도 「건」 열의 뒤 절 건 번호, 도판 출처 줄의 뒤 절 `§`, 「NN절」
|
|
59
|
+
| R12h 선참조 | 앞 절이 **뒤 절**을 가리키지 않는다 — 요지 문단·판단 항목 본문의 `§n.n`(근거는 `refs[]` 로만 — 「해당 절」·「근거」 단추가 되므로 뜀이 아니다), 흐름도 「건」 열의 뒤 절 건 번호, 도판 출처 줄의 뒤 절 `§`, 「NN절」 낱말(남의 문서의 절 번호 「검수 보고 4차 제5절」처럼 「N차 … 제N절」 꼴은 이 문서의 뒤 절이 아니라 세지 않는다 — 2026-09-15). 낱말·규칙·예시 선언은 **첫 등장 자리에서 한 구절**(부록·앞 회차 보고에만 두지 않는다). 뒤 절이 앞 절을 되짚는 방향만 허용. 되풀이 금지(R12g)와 다른 축이다 — 되풀이를 빼되 그 자리에서 뜻이 서게 하는 한 구절은 남긴다. 실사례 2026-09-14 고객사 3차 보고 「읽다가 시험보는 줄 알 것 같음」: 2판 교차 참조 62 → 순서를 고친 3판에도 선형 읽기 뜀 지점 45(정의가 부록·원고 밖 / 앞 절의 뒤 절 번호) → 4판 0 | gazette.py `text_scan`(W5 — 절 i 의 산문(이름·부제·도입문·문단·표·흐름도 좌표·칸·여백 주석·같은 그룹의 건·판단 항목·강조문·도판 캡션·출처 줄)에서 앞 숫자가 i 보다 큰 `§n.n`·「NN절」을 센다. `summary[]`·`refs[]` 는 안 센다. 본보기 두 벌의 뒤 절 참조 10곳과 서식 문서 §12.5b 를 같은 날 같이 고쳤다) · 정의 위치(①)는 독자 검토 「선형 읽기」 관점(`reader-review.workflow.js` linear, jumps 0 목표) · 사람 눈 | ⚠ · — |
|
|
60
60
|
| R13 캡처 틀 문법 | 캡처를 품은 **틀**을 선언 없이 찾는다 — img 에서 위로 올라가며 테두리가 보이는 첫 조상(자기 포함), 없으면 가장 바깥 overflow 자르개, 그것도 없으면 img 자신(`figure.mjs` `frameOf`). 틀이 둘 이상이면 「비교 컷」이다. 값은 R13a~d. 틀이 하나뿐인 도판(번호 표식 캡처·합성 한 장)에는 R13a 의 가로세로비·R13b·R13c 만 선다. 알려진 한계: 칩 이름표까지 품은 카드에 테두리를 두르면 그 카드가 틀로 잡혀 칩 글자가 R13b 로 걸린다 — 테두리는 캡처 상자에 둔다 | figure.mjs | a·b·c ✗ · d ⚠ |
|
|
61
61
|
| R13a 좌우 같은 틀 | 비교 틀은 **좌우로 나란히** 선다 · 나란한 틀의 높이 차·폭 차 ≤ `R13.heightTol`·`widthTol`(2px) · 이미지 배율 차 ≤ 1% · 가로세로비 왜곡 ≤ 1%(왜곡은 틀 하나여도). 칸 폭 = (표시 폭 − 좌우 여백×2 − 사이) ÷ 2 — 숫자는 `PAIR`(전폭 `wide` · 본문 단 `column`)가 정본이고 `pair.mjs` 가 그대로 쓴다. 틀 높이는 두 칸 중 큰 쪽, 짧은 쪽은 흰 여백 — 늘리거나 따로 잘라 맞추지 않는다. 예외: 모든 틀이 가로 띠(폭 ≥ 높이×3)이고 같은 폭이면 위아래 허용 | figure.mjs · pair.mjs | ✗ |
|
|
62
62
|
| R13b 틀 안 글자 0 | 틀 안(틀의 자손이거나 글리프 중심이 틀 안)에 HTML 글자 **0**. 빈 자리는 회색 점선이 말하고, 이름은 틀 위 칩(R13d)이 말한다. 예외는 원 번호 한 글자(①~⑳ — 읽는 순서) | figure.mjs | ✗ |
|
|
@@ -179,6 +179,7 @@
|
|
|
179
179
|
## 8. 실물 확인 — dry_run 경고 0 이 목표, 그 다음은 눈
|
|
180
180
|
|
|
181
181
|
1. 로컬 `render.py` 와 `update_report(dry_run: true)` — 통과 기준(`lim-portal-format.md` §10 「통과 = 작성자 몫 ⚠0」)이 될 때까지 원고를 고친다.
|
|
182
|
+
단 경고가 규칙의 뜻과 어긋나는 자리를 짚으면(남의 문서 절 번호를 이 문서의 뒤 절로 세는 것처럼) 원고를 깎지 않는다 — 오탐으로 판정해 렌더러를 고치고 대조군 시험을 둔다. 그 수리가 운영에 오르기 전이면 번호 대신 이름으로 위치를 살린다. 렌더러·서버 관문이 운영보다 앞선 동안 옛 규칙이 내는 경고는 `lim-portal-format.md` §10 통과 기준 표의 행으로 넘긴다.
|
|
182
183
|
2. 발행 뒤 **열람 주소를 실제로 연다**(Cursor 인앱 브라우저 `iab` 나 헤드리스 크롬). 그림 수·이미지 로드 수·표·흐름도·차트 수를 세고
|
|
183
184
|
판단 절과 그림 절을 캡처로 본다. 「구조 확인 outline」은 블록이 섰는지만 말하지 보이는지는 말하지 않는다.
|
|
184
185
|
3. 큰 원고(70KB 이상)는 채팅에 붙여넣지 말고 MCP 서버를 자식으로 띄워 파일 인자로 보낸다
|