@kaydash9999/banker-plugins 0.23.3 → 0.24.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 (78) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +56 -0
  4. package/README.md +14 -6
  5. package/codex/manifest.json +6 -2
  6. package/codex/skills/teamwork/scripts/teamwork.mjs +3 -3
  7. package/codex/transform-matrix.md +6 -2
  8. package/package.json +1 -1
  9. package/skills/build-testbox/SKILL.md +213 -0
  10. package/skills/build-testbox/scripts/args.mjs +156 -0
  11. package/skills/build-testbox/scripts/browser-cell.mjs +181 -0
  12. package/skills/build-testbox/scripts/browsers.mjs +164 -0
  13. package/skills/build-testbox/scripts/containers.mjs +237 -0
  14. package/skills/build-testbox/scripts/detect.mjs +166 -0
  15. package/skills/build-testbox/scripts/proc.mjs +91 -0
  16. package/skills/build-testbox/scripts/report.mjs +109 -0
  17. package/skills/build-testbox/scripts/setup.mjs +144 -0
  18. package/skills/build-testbox/scripts/testbox.mjs +148 -0
  19. package/skills/git-init/SKILL.md +152 -0
  20. package/skills/git-init/scripts/git-init.mjs +440 -0
  21. package/skills/git-init/scripts/marker-block.mjs +321 -0
  22. package/skills/git-init/scripts/payload-scan.mjs +186 -0
  23. package/skills/init-dir/SKILL.md +172 -0
  24. package/skills/init-dir/scripts/init-dir.mjs +308 -0
  25. package/skills/init-dir/scripts/marker-block.mjs +321 -0
  26. package/skills/init-dir/scripts/templates.mjs +100 -0
  27. package/skills/init-dir/templates/KB/README.md +15 -0
  28. package/skills/init-dir/templates/docs/README.md +14 -0
  29. package/skills/init-dir/templates/logs/README.md +14 -0
  30. package/skills/init-dir/templates/logs/gitignore +3 -0
  31. package/skills/init-dir/templates/mockup/README.md +7 -0
  32. package/skills/init-dir/templates/scripts/README.md +39 -0
  33. package/skills/init-dir/templates/scripts/lib/service.mjs +596 -0
  34. package/skills/init-dir/templates/scripts/restart.cmd +13 -0
  35. package/skills/init-dir/templates/scripts/restart.sh +10 -0
  36. package/skills/init-dir/templates/scripts/service.conf +5 -0
  37. package/skills/init-dir/templates/scripts/start.cmd +13 -0
  38. package/skills/init-dir/templates/scripts/start.sh +10 -0
  39. package/skills/init-dir/templates/scripts/status.cmd +13 -0
  40. package/skills/init-dir/templates/scripts/status.sh +10 -0
  41. package/skills/init-dir/templates/scripts/stop.cmd +13 -0
  42. package/skills/init-dir/templates/scripts/stop.sh +10 -0
  43. package/skills/init-dir/templates/tests/README.md +15 -0
  44. package/skills/init-dir/templates/tests/run.cmd +13 -0
  45. package/skills/init-dir/templates/tests/run.mjs +160 -0
  46. package/skills/init-dir/templates/tests/run.sh +10 -0
  47. package/skills/init-dir/templates/tests/tests.conf +7 -0
  48. package/skills/init-dir/templates/work/README.md +8 -0
  49. package/skills/progress-mon/README.md +11 -8
  50. package/skills/progress-mon/SKILL.md +53 -16
  51. package/skills/progress-mon/assets/fixture/README.md +1 -1
  52. package/skills/progress-mon/assets/fixture/adr.json +35 -0
  53. package/skills/progress-mon/assets/fixture/arch.json +1 -1
  54. package/skills/progress-mon/assets/fixture/flow.json +2 -2
  55. package/skills/progress-mon/assets/fixture/variants/arch-auto/arch.json +1 -1
  56. package/skills/progress-mon/assets/fixture/variants/legacy-init/rules.json +6 -0
  57. package/skills/progress-mon/assets/fixture/variants/legacy-init/state.json +5 -0
  58. package/skills/progress-mon/references/adr.md +184 -0
  59. package/skills/progress-mon/references/authoring.md +13 -5
  60. package/skills/progress-mon/scripts/pm-adr-view.mjs +101 -0
  61. package/skills/progress-mon/scripts/pm-adr.mjs +515 -0
  62. package/skills/progress-mon/scripts/pm-check.mjs +59 -22
  63. package/skills/progress-mon/scripts/pm-data.mjs +1 -1
  64. package/skills/progress-mon/scripts/pm-html.mjs +1 -0
  65. package/skills/progress-mon/scripts/pm-memory.mjs +113 -28
  66. package/skills/progress-mon/scripts/pm-render.mjs +6 -4
  67. package/skills/progress-mon/scripts/pm-server.mjs +2 -1
  68. package/skills/progress-mon/scripts/pm-sources.mjs +10 -0
  69. package/skills/progress-mon/scripts/progress-mon.mjs +3 -2
  70. package/skills/tune-tmux/SKILL.md +235 -0
  71. package/skills/tune-tmux/scripts/app-notes.mjs +38 -0
  72. package/skills/tune-tmux/scripts/marker-block.mjs +321 -0
  73. package/skills/tune-tmux/scripts/psmux-caps.json +71 -0
  74. package/skills/tune-tmux/scripts/tmux-config.mjs +251 -0
  75. package/skills/tune-tmux/scripts/tune-tmux.mjs +284 -0
  76. package/skills/ultra-chain/SKILL.md +52 -22
  77. package/skills/ultra-chain/references/chain-ledger.mjs +93 -11
  78. package/skills/ultra-chain/references/chain-report.mjs +13 -2
@@ -0,0 +1,184 @@
1
+ # adr.json (ADR 탭)
2
+
3
+ ADR 탭의 자료. 의사 결정 과정과 결론에 더해, 타인이 결과를 의심할 때 확인할 근거를 함께 적는다.
4
+ init 절차의 ADR 단계와 갱신 규칙(SKILL.md)에서 에이전트가 쓰고, `check` 가 형식과 참조를 확인한다.
5
+ 화면은 자동 갱신하지 않는다. 주소 `?tab=adr`, `export` 파일의 `#adr`.
6
+
7
+ ## 두 층
8
+
9
+ | 층 | 칸 | 뜻 |
10
+ |---|---|---|
11
+ | 프로젝트(하나) | `project.verification` | 단위 기능(모듈)마다 검증 결과와 evidence |
12
+ | 프로젝트(하나) | `project.flow` | 입력, 처리 로직, 출력 |
13
+ | 프로젝트(하나) | `project.proof` | 구현된 기능과 증명 위치, 구현 못 한 기능과 사유 |
14
+ | 결정(여럿) | `decisions[]` | 맥락, 선택지, 결론과 이유, 최적화 로직, 확장 대응, 포기와 얻은 것, 근거 출처 |
15
+
16
+ ```json
17
+ {
18
+ "project": {
19
+ "verification": [
20
+ { "id": "M-1", "module": "<단위 기능>", "result": "<결과>", "evidence": ["<참조>"] },
21
+ { "id": "M-2", "module": "<검증 못 한 단위 기능>", "status": "unverified", "why": "<미검증 사유>" }
22
+ ],
23
+ "flow": { "input": "<입력>", "logic": "<처리 로직>", "output": "<출력>", "refs": ["<참조>"] },
24
+ "proof": {
25
+ "implemented": [{ "feature": "<구현된 기능>", "modules": ["M-1"], "proofs": ["<참조>"] }],
26
+ "notImplemented": [{ "feature": "<구현 못 한 기능>", "why": "<사유>" }]
27
+ }
28
+ },
29
+ "decisions": [
30
+ {
31
+ "id": "ADR-1", "title": "<결정 제목>", "status": "accepted|proposed|superseded", "date": "<YYYY-MM-DD, 선택>",
32
+ "context": "<맥락>",
33
+ "options": [{ "name": "<선택지>", "pros": "<장점>", "cons": "<단점>" }],
34
+ "decision": "<결론>", "why": "<과정과 선택 이유>",
35
+ "optimization": { "process": "<최적 로직인지 검증한 과정>", "modules": ["M-1"], "refs": ["<참조>"] },
36
+ "scaling": { "argument": "<확장 대응 논거>", "limits": "<한계, 선택>", "refs": ["<참조>"] },
37
+ "tradeoffs": { "gaveUp": [{ "what": "<포기한 것>", "why": "<이유>" }], "gained": [{ "what": "<얻은 것>", "purpose": "<목적>" }] },
38
+ "sources": ["<참조>"]
39
+ }
40
+ ]
41
+ }
42
+ ```
43
+
44
+ - 모듈 id 는 `project.verification` 안에서 겹치지 않는다. 결정과 `proof.implemented` 의 `modules` 는 있는 id 만
45
+ - `options` 는 2개 이상. 하나뿐이면 `rejected`(다른 선택지를 버린 사유)를 쓴다
46
+ - `modules`, `refs`(flow), `limits`, `date`, `rejected`, `sources` 는 선택. 그 밖 칸은 필수
47
+ - 모듈 `status` 는 선택. 없거나 `"verified"` 면 검증한 모듈(`result`, `evidence` 필수), `"unverified"` 면 미검증 모듈(`why` 필수, `result`, `evidence` 선택)
48
+ - 모든 글은 화면에서 escape 한다. 비밀값(키, 토큰, 비밀번호)을 적지 않는다
49
+
50
+ ## 사용자 항목과 칸
51
+
52
+ | 사용자 항목 | 층과 칸 | 화면 절 제목 |
53
+ |---|---|---|
54
+ | 1. 최적화 로직 | 결정의 `optimization` | 최적화 로직 |
55
+ | 2. 검증 | `project.verification` | 검증 |
56
+ | 3. 흐름 | `project.flow` | 흐름 |
57
+ | 4. 확장 대응 | 결정의 `scaling` | 확장 대응 |
58
+ | 5. 포기와 얻은 것 | 결정의 `tradeoffs` | 포기와 얻은 것 |
59
+ | 6. 증거 | `project.proof` | 증거 |
60
+
61
+ - 주장에는 증거(사용자 결정 D 안). 근거가 없으면 지어내지 않는다
62
+ - 검증한 모듈의 `evidence`, 구현된 기능의 `proofs` 는 확인된 참조 1개 이상. 확인된 참조는 꼴이 맞고 `gap` 이 아니며 아래 참조 확인에서 FAIL 도 WARN 도 없는 것. 출력 파일 없는 `cmd`, 파일에 없는 이름의 `test`, git 이 꺼졌거나 저장소가 아닌 `commit` 은 세지 않는다. 없으면 FAIL. 벗어나는 길: 그 모듈은 `status: "unverified"` 와 `why`, 그 기능은 `notImplemented` 로 옮김
63
+ - `project.verification`, `project.proof` 칸 전체를 `gap` 으로 두면 FAIL(모듈을 적고 미검증으로, 기능은 `notImplemented` 로)
64
+ - 결정의 `optimization`, `scaling`, `tradeoffs` 와 `project.flow` 는 `{ "gap": "<사유>" }` 로 비울 수 있음(밀도 WARN)
65
+ - 서술 칸(결정의 `optimization` 과 `scaling`)을 채웠는데 `refs` 에 확인된 참조가 0개면 FAIL 이 아니고 WARN(`adr 서술참조` 밀도). 다만 그 칸 수를 집계 배지와 `check` 마지막 줄에 `참조 없는 서술 n` 으로 늘 보인다
66
+ - 화면은 빈 칸을 `근거 없음: <사유>` 로 보이고, 결정 목록에 결정별 3항목 채움 수(`n/3`)와 빈 항목을 보인다
67
+ - 집계 배지 `근거 없음 n칸, 미검증 n, 구현 못 함 n, 참조 없는 서술 n`: adr.json 안의 `{ "gap" }` 수(여섯 항목 칸과 참조 자리), `status: "unverified"` 모듈 수, `notImplemented` 기능 수, `refs` 에 확인된 참조가 0개인 서술 칸 수. 화면 맨 위(프로젝트 요약 위)와 `check` 의 마지막 줄 `adr 근거 없음 n칸, 미검증 n, 구현 못 함 n, 참조 없는 서술 n` 에 늘 보임. 0 이면 0(adr.json 이 없으면 check 는 모두 0). 넷 가운데 하나라도 0 이 아니면 배지는 빨강(모든 모듈이 미검증이거나 서술 칸에 참조가 없는데 초록 0칸만 보이지 않게)
68
+ - `check` 는 참조 확인을 마친 참조(FAIL, WARN 없음)만 센다. 화면은 파일을 읽지 않으므로 꼴이 맞고 `gap` 이 아닌 참조를 센다. 그래서 출력 파일 없는 `cmd` 하나만 적힌 칸은 화면에서 0, `check` 에서 1 로 셀 수 있다
69
+ - check 대조: `check` 명령은 끝에 자료 폴더의 `adr-check.json` 에 그 집계 글, 그때 `adr.json` 글(BOM 을 뗀 뒤)의 sha256, 시각(UTC)을 쓴다. `adr.json` 이 없거나 JSON 이 아니면 쓰지 않는다. 쓰지 못하면 stderr 에 `경고: adr-check.json 쓰기 실패(...)` 만 내고 검사 결과와 종료 코드는 그대로다
70
+ - 화면은 집계 배지 옆에 대조 배지를 보인다
71
+ - 기록이 없거나 꼴이 틀림: `check 기록 없음. 참조 확인은 check`
72
+ - sha256 이 다름: `check(<시각> UTC) 뒤 adr.json 이 바뀜. check 를 다시 실행하면 대조`
73
+ - sha256 과 집계 글이 같음: 초록 `check(<시각> UTC) 와 같음`
74
+ - 집계 글이 다름: 빨강 `check(<시각> UTC) 기준: <check 집계>. 화면은 적힌 참조로 세어 다름. 확인 못 한 참조는 check 의 참조 줄`
75
+ - 대조는 check 를 실행한 때의 참조 파일 기준이다. 그 뒤 참조 파일만 바뀌면(adr.json 은 그대로) 배지는 그대로이므로 다시 확인하려면 check 를 실행한다
76
+
77
+ ## 참조
78
+
79
+ `<참조>` 는 종류에 맞는 칸만 쓴다. 참조 자리에 `{ "gap": "<사유>" }` 도 받는다.
80
+
81
+ | `kind` | 칸 | 화면 글 |
82
+ |---|---|---|
83
+ | `file` | `path`, 선택 `line`(1 이상 정수) | `파일 <path>:<line>` |
84
+ | `test` | `path`, `name`(시험 이름) | `시험 <path> 의 <name>` |
85
+ | `commit` | `hash`(4~40자 16진) | `커밋 <앞 7자리>` |
86
+ | `cmd` | `cmd`, 선택 `output`(출력 파일) | `명령 <cmd>, 출력 <output>` |
87
+
88
+ - 모든 종류에 선택 `note`(짧은 설명)
89
+ - 경로는 프로젝트 루트 기준 상대 경로. 화면은 참조를 글로만 보이고 파일을 읽거나 링크하지 않는다
90
+ - 시험 결과는 실제로 돌린 명령의 출력만 참조한다. 출력 파일이 없으면 `output` 없이 두어 WARN 으로 드러낸다
91
+ - 권장: `cmd` 의 `output` 파일은 `docs/evidence/` 에 두고 머리 3줄에 명령, 종료 코드, 시각을 적는다
92
+
93
+ ```
94
+ 명령: node --test test/
95
+ 종료 코드: 0
96
+ 시각: 2026-10-29T03:00:00Z
97
+ ```
98
+
99
+ - init-dir 로 만든 프로젝트는 `docs/adr/`(사람이 쓴 ADR 문서), `docs/evidence/`(검증 출력)가 근거 출처다
100
+
101
+ ## check 판정
102
+
103
+ 항목 누락(결정 H)
104
+
105
+ | 경우 | check |
106
+ |---|---|
107
+ | 항목 칸이 없거나 꼴이 틀림 | `FAIL adr.json <json 경로> <이유>`(스키마 위반) |
108
+ | 결정의 3칸이나 `project.flow` 가 `{ "gap": "<사유>" }` | `WARN density adr 빈 항목 <값>/<하한>: <json 경로>` |
109
+ | 서술 칸(`optimization`, `scaling`)의 `refs` 에 확인된 참조가 0개 | `WARN density adr 서술참조 <값>/<하한>: <json 경로>`, 마지막 줄 `참조 없는 서술 n` |
110
+ | `gap` 에 사유가 없음 | `FAIL adr.json <json 경로>.gap gap 에 사유 없음` |
111
+ | 검증한 모듈의 `evidence`, 구현된 기능의 `proofs` 에 확인된 참조(FAIL, WARN 없음)가 0개 | `FAIL adr.json <json 경로> 증거 없음: ...`(벗어나는 길을 줄에 적음) |
112
+ | `project.verification`, `project.proof` 칸 전체가 `gap` | `FAIL adr.json <json 경로> 증거 없음: ...` |
113
+
114
+ 참조 확인(결정 I)
115
+
116
+ | 참조 종류 | check |
117
+ |---|---|
118
+ | `file` | 절대 경로, `..` 이 든 경로, 루트 밖을 가리키는 링크는 `FAIL`. 없는 파일, 폴더나 특수 파일(FIFO 등), 줄 수를 넘는 `line` 은 `FAIL` |
119
+ | `test` | 파일 규칙은 `file` 과 같음. 파일 글에 `name` 이 없으면 `WARN` |
120
+ | `commit` | git 저장소에서 없는 커밋은 `FAIL`. `config.json` 의 `sources.git` 이 꺼졌거나 저장소가 아니면 `WARN`(확인 못 함) |
121
+ | `cmd` | 명령 자체는 확인 못 함. `output` 이 있으면 `file` 규칙, 없으면 `WARN` |
122
+
123
+ - 참조 결과 줄은 `FAIL adr.json <json 경로> 참조: <이유>`, `WARN adr.json <json 경로> 참조: <이유>`
124
+ - 참조 파일은 check 에서만 읽는다. 크기 상한(4 MB) 안의 일반 파일만 읽고, 넘으면 줄과 이름 확인을 못 해 `WARN`
125
+ - 처리가 다르다
126
+ - 스키마 위반 FAIL: init 정리 단계에서 FAIL 이 남으면 `adr.json` 을 지우지 않고 같은 폴더의 `adr.json.rejected-<YYYYMMDD-HHMMSS>`(이름이 있으면 `-2` 부터 번호)로 옮겨 "자료 없음" 으로 둔다. 옮긴 파일을 고쳐 `adr.json` 으로 되돌릴 수 있다
127
+ - 참조 FAIL: 파일을 지우지 않는다. init 정리 단계에서 그 참조를 `{ "gap": "참조 확인 실패: <이유>" }` 로 바꾸고 check 를 다시 돌린다. 갱신 때(init 밖)는 FAIL 줄을 보고하고 json 경로를 고친다
128
+ - 증거 없음 FAIL: 정리 단계가 파일을 옮기지 않고 자동으로 고치지도 않는다. 참조를 `gap` 으로 바꾼 결과 증거가 모두 `gap` 이 된 주장은 `정리: 증거가 남지 않은 주장 n건` 줄로 알리고, 에이전트가 판단해 그 모듈을 미검증으로, 그 기능을 `notImplemented` 로 옮긴다
129
+ - `check --adr-cleanup` 이 정리 단계를 적용한 뒤 검사한다(출력 첫 줄 `정리: ...`)
130
+
131
+ 두 원본 갈라짐(결정 K)
132
+
133
+ | 경우 | check |
134
+ |---|---|
135
+ | `docs/adr/*.md` 가운데 어떤 결정의 `sources` 도 가리키지 않는 파일 | 파일마다 `WARN adr.json sources: docs/adr/<파일> 를 가리키는 결정 없음` |
136
+ | `docs/adr/` 가 없거나 `.md` 가 없음 | 줄 없음 |
137
+
138
+ - 연결 유무만 본다. 사람이 쓴 문서와 `adr.json` 의 내용 일치는 검사하지 않는다
139
+
140
+ 밀도(adr.json 이 있으면 늘. 없으면 init 기록이 있을 때만)
141
+
142
+ | `check` 의 이름 | 하한 |
143
+ |---|---|
144
+ | `adr 빈 항목` | 프로젝트 3칸과 결정마다 3칸이 모두 `gap` 아님 |
145
+ | `adr 검증` | `verification` 1 이상 |
146
+ | `adr 흐름` | 입력, 처리, 출력 3칸이 빈 칸 아님 |
147
+ | `adr 증거` | `proof.implemented` 1 이상 |
148
+ | `adr 결정` | 결정 1 이상 |
149
+ | `adr 포기얻음` | 결정마다 `gaveUp`, `gained` 각 1 이상 |
150
+ | `adr 서술참조` | `gap` 이 아닌 서술 칸(`optimization`, `scaling`)마다 `refs` 에 확인된 참조 1 이상. 못 채운 칸은 json 경로로 |
151
+
152
+ - 끝의 밀도 요약에 `density adr ok|low`(init 기록이 있을 때), 마지막 줄은 늘 `adr 근거 없음 n칸, 미검증 n, 구현 못 함 n, 참조 없는 서술 n`
153
+
154
+ ## 좋은 예
155
+
156
+ ```json
157
+ {
158
+ "project": {
159
+ "verification": [
160
+ { "id": "M-1", "module": "설정 읽기", "result": "시험 3개 통과", "evidence": [{ "kind": "test", "path": "test/config.test.mjs", "name": "설정 읽기" }, { "kind": "cmd", "cmd": "node --test test/", "output": "docs/evidence/test-run.md" }] },
161
+ { "id": "M-2", "module": "화면 렌더", "result": "고정 자료로 HTML 확인", "evidence": [{ "kind": "file", "path": "src/render.mjs", "line": 40, "note": "render 함수" }] }
162
+ ],
163
+ "flow": { "input": "config.json 과 자료 파일", "logic": "설정을 읽어 검사한 뒤 탭마다 HTML 로 렌더", "output": "로컬 서버 화면과 단일 HTML", "refs": [{ "kind": "file", "path": "src/main.mjs", "line": 12 }] },
164
+ "proof": {
165
+ "implemented": [{ "feature": "설정 읽기와 화면", "modules": ["M-1", "M-2"], "proofs": [{ "kind": "test", "path": "test/config.test.mjs", "name": "설정 읽기" }, { "kind": "commit", "hash": "1a2b3c4" }] }],
166
+ "notImplemented": [{ "feature": "알림", "why": "범위 밖으로 미룸(계획 9장)" }]
167
+ }
168
+ },
169
+ "decisions": [
170
+ {
171
+ "id": "ADR-1", "title": "자료는 JSON 파일", "status": "accepted", "date": "2026-10-20",
172
+ "context": "의존성 없이 자료를 저장해야 함",
173
+ "options": [{ "name": "JSON 파일", "pros": "내장 모듈만", "cons": "동시 쓰기 잠금 필요" }, { "name": "SQLite", "pros": "질의", "cons": "의존성 추가" }],
174
+ "decision": "JSON 파일", "why": "의존성 0 원칙과 자료 크기가 작음",
175
+ "optimization": { "process": "자료 크기 1 MB 미만에서 읽기 시간을 재어 파일 하나 읽기가 충분함을 확인", "modules": ["M-1"], "refs": [{ "kind": "cmd", "cmd": "node bench.mjs", "output": "docs/evidence/bench.md" }] },
176
+ "scaling": { "argument": "탭마다 파일을 나눠 한 파일이 커지지 않음. 바뀐 파일만 다시 읽음", "limits": "파일 하나가 32 MB 를 넘으면 읽지 않음", "refs": [{ "kind": "file", "path": "src/sources.mjs", "line": 30 }] },
177
+ "tradeoffs": { "gaveUp": [{ "what": "질의 기능", "why": "의존성 0" }], "gained": [{ "what": "설치 없는 실행", "purpose": "어느 PC 에서나 바로 실행" }] },
178
+ "sources": [{ "kind": "file", "path": "docs/adr/0001-data-file.md" }]
179
+ }
180
+ ]
181
+ }
182
+ ```
183
+
184
+ - 나쁜 예: 실행하지 않은 시험을 "통과" 로 적음, 참조 없이 "검증함" 만 적음, 근거가 없는 확장 논거를 지어냄. 결정의 근거가 없으면 `gap` 과 사유, 증거 없는 모듈과 기능은 미검증과 `notImplemented`
@@ -1,6 +1,6 @@
1
1
  # init 작성 지침
2
2
 
3
- init 절차(SKILL.md)에서 에이전트가 4탭 자료를 쓸 때 따르는 기준. 목표 밀도는 이 문서 안에서 확인한다: 아래 "밀도 하한" 표의 수(구성도 영역 5, 상자 20, 연결 15, workflow 흐름 2, 흐름당 단계 7 등)와 "좋은 예" 의 꼴(상자 설명 2~3줄과 칩과 근거, 연결마다 `파일:줄`, 흐름마다 실패 분기와 3문장 설명).
3
+ init 절차(SKILL.md)에서 에이전트가 5탭 자료를 쓸 때 따르는 기준. 목표 밀도는 이 문서 안에서 확인한다: 아래 "밀도 하한" 표의 수(구성도 영역 5, 상자 20, 연결 15, workflow 흐름 2, 흐름당 단계 7 등)와 "좋은 예" 의 꼴(상자 설명 2~3줄과 칩과 근거, 연결마다 `파일:줄`, 흐름마다 실패 분기와 3문장 설명).
4
4
  숫자 하한은 보조 점검이다. 하한을 채워도 아래 좋은 예보다 얇으면(설명 한 줄, 근거 없음, 분기 없음) 보충한다.
5
5
 
6
6
  ## 순서
@@ -9,7 +9,8 @@ init 절차(SKILL.md)에서 에이전트가 4탭 자료를 쓸 때 따르는 기
9
9
  2. `inventory.json` 에 실제 구성요소, 흐름, 외부, 읽은 파일을 적는다(`references/inventory.md`)
10
10
  3. `config.json` 의 `title`, `shortName`, `stories` 를 채운다
11
11
  4. `req-spec.json`, `reqs.json`, `flow.json`, `arch.json` 을 쓴다. 좌표는 쓰지 않는다
12
- 5. `check` 로 검사한다. FAIL 0 까지 최대 3회. `WARN density` 가 남으면 한 번 더 보충(SKILL.md init 절차 8, 9)
12
+ 5. ADR 단계: `adr.json` 을 쓴다(`references/adr.md`, SKILL.md init 절차 8)
13
+ 6. `check` 로 검사한다. FAIL 0 까지 최대 3회. `WARN density` 가 남으면 한 번 더 보충(SKILL.md init 절차 9, 10)
13
14
 
14
15
  ## 밀도 하한
15
16
 
@@ -31,6 +32,9 @@ init 절차(SKILL.md)에서 에이전트가 4탭 자료를 쓸 때 따르는 기
31
32
  | 구현요건 | 교차 대응 | 명세마다 AC 1 이상. 스토리가 있으면 AC 를 거쳐 담당 스토리 1 이상 | `req 명세AC`, `명세스토리` |
32
33
  | 구현요건 | 비고 | `state` 를 적은 명세는 `note`(판정 근거) | `req 비고` |
33
34
  | 진척도 | 기타 항목 | 항목마다 `note`(근거 커밋, 결과). 문제점이 없으면 `--issue` 를 빼고 실행(칸은 빈칸) | `history 비고` |
35
+ | ADR | 여섯 항목 | 프로젝트 3칸과 결정마다 3칸이 `gap` 아님 | `adr 빈 항목` |
36
+ | ADR | 프로젝트 | `verification` 1 이상, `flow` 의 입력, 처리, 출력이 빈 칸 아님, `proof.implemented` 1 이상 | `adr 검증`, `흐름`, `증거` |
37
+ | ADR | 결정 | 결정 1 이상. 결정마다 `tradeoffs` 의 `gaveUp`, `gained` 각 1 이상 | `adr 결정`, `포기얻음` |
34
38
 
35
39
  - 미달은 `WARN density <탭> <항목> <값>/<하한>` 한 줄씩. 종료 코드에 영향 없음
36
40
  - 대상마다 재는 항목은 줄 끝에 `: ` 와 못 채운 대상을 붙인다. 앞 10개, 넘으면 `외 n`
@@ -46,12 +50,16 @@ init 절차(SKILL.md)에서 에이전트가 4탭 자료를 쓸 때 따르는 기
46
50
  | `req 요구` | json 경로 | `users[2]` |
47
51
  | `req 명세AC`, `명세스토리`, `비고` | json 경로 | `users[0].specs[3]` |
48
52
  | `history 비고` | 항목 key | `docs-1` |
53
+ | `adr 빈 항목` | json 경로 | `decisions[1].scaling` |
54
+ | `adr 흐름` | 빈 칸 이름 | `입력` |
55
+ | `adr 포기얻음` | 결정 id | `ADR-2` |
49
56
 
50
- - 수만 재는 항목(영역, 상자, 연결, 범례, 주흐름, 장면, 흐름, `단계:<흐름 id>`)은 이름을 붙이지 않는다. 수를 늘린다
57
+ - 수만 재는 항목(영역, 상자, 연결, 범례, 주흐름, 장면, 흐름, `단계:<흐름 id>`, `adr 검증`, `증거`, `결정`)은 이름을 붙이지 않는다. 수를 늘린다
51
58
  - 하한을 채운 항목은 줄을 내지 않는다. 값을 보려면 자료에서 직접 센다
52
59
  - 자동 배치의 `이름표 덮음`, `이름표가 그림 밖` WARN 은 배치 조언이고 실패가 아니다(자동 배치 선은 상자를 돌아감). 연결을 하한 아래로 줄여 없애지 않는다. 줄이는 법은 `references/arch.md` 의 "자동 배치 WARN 줄이기"
53
- - `check` 끝에 `density <탭> ok|low` 4줄(history, req, workflow, arch)
54
- - 밀도는 init 이 만든 자료 폴더(`state.json` 의 `memory`)나 `inventory.json` 이 있을 때만 잰다
60
+ - `check` 끝에 `density <탭> ok|low` 5줄(history, req, workflow, arch, adr)
61
+ - 밀도는 init 이 만든 자료 폴더(`state.json` 의 `memory`)나 `inventory.json` 이 있을 때만 잰다. ADR 밀도는 `adr.json` 이 있으면 늘 잰다
62
+ - 증거 없는 모듈과 기능(`evidence`, `proofs` 에 FAIL, WARN 없이 확인된 참조 0개)은 밀도가 아니라 FAIL 이다(`references/adr.md` 의 "주장에는 증거")
55
63
 
56
64
  ## 설명 줄 수
57
65
 
@@ -0,0 +1,101 @@
1
+ // pm-adr-view.mjs: ADR 탭 화면 렌더. 검사, 밀도, 집계는 pm-adr.mjs.
2
+ // 화면은 참조를 글로만 보이고 파일을 읽거나 링크하지 않는다. 참조 확인은 check 가 하고, 그 집계는 adr-check.json 으로 대조한다.
3
+ import { DECISION_SLOTS, STATUS, adrTally, arr, isGap, obj, str } from './pm-adr.mjs';
4
+ import { esc } from './pm-html.mjs';
5
+
6
+ const gapHtml = (v) => `<span class="b miss">근거 없음: ${esc(v.gap)}</span>`;
7
+
8
+ // 참조 한 줄 글. 파일을 읽거나 링크하지 않는다.
9
+ export function refText(x) {
10
+ if (!obj(x)) return '';
11
+ if (isGap(x)) return gapHtml(x);
12
+ const note = typeof x.note === 'string' && x.note ? ` <small>${esc(x.note)}</small>` : '';
13
+ const line = Number.isInteger(x.line) ? `:${x.line}` : '';
14
+ const out = {
15
+ file: () => `파일 <code>${esc(x.path)}${line}</code>`,
16
+ test: () => `시험 <code>${esc(x.path)}</code> 의 ${esc(x.name)}`,
17
+ commit: () => `커밋 <code>${esc(String(x.hash || '').slice(0, 7))}</code>`,
18
+ cmd: () => `명령 <code>${esc(x.cmd)}</code>, 출력 ${x.output ? `<code>${esc(x.output)}</code>` : '없음'}`,
19
+ }[x.kind];
20
+ return out ? out() + note : esc(JSON.stringify(x)) + note;
21
+ }
22
+
23
+ const refsHtml = (list) => arr(list).map(refText).filter(Boolean).join('<br>') || '<span class="n">참조 없음</span>';
24
+ const table = (head, rows) => `<table><thead><tr>${head.map((h) => `<th>${esc(h)}</th>`).join('')}</tr></thead><tbody>${rows.join('') || `<tr><td colspan="${head.length}" class="n">없음</td></tr>`}</tbody></table>`;
25
+ const td = (h, cls = '') => `<td${cls ? ` class="${cls}"` : ''}>${h}</td>`;
26
+ const sec = (title, body) => `<h3>${esc(title)}</h3>${body}`;
27
+ // 칸이 gap 이면 근거 없음, 아니면 body(값).
28
+ const slotHtml = (v, body) => (isGap(v) ? `<div>${gapHtml(v)}</div>` : obj(v) || Array.isArray(v) ? body(v) : `<div>${gapHtml({ gap: '칸 없음' })}</div>`);
29
+
30
+ const evidenceHtml = (x) => (x.status === 'unverified' ? `<span class="b unv">미검증: ${esc(x.why)}</span>${arr(x.evidence).length ? `<br>${refsHtml(x.evidence)}` : ''}` : refsHtml(x.evidence));
31
+
32
+ function verificationHtml(v) {
33
+ return table(['id', '단위 기능', '결과', 'evidence'], arr(v).filter(obj).map((x) => `<tr>${td(esc(x.id), 'h')}${td(esc(x.module))}${td(esc(x.result || '-'))}${td(evidenceHtml(x), 'n')}</tr>`));
34
+ }
35
+
36
+ function flowHtml(f) {
37
+ const rows = [['입력', f.input], ['처리', f.logic], ['출력', f.output]].map(([k, t]) => `<tr>${td(k, 't')}${td(str(t) ? esc(t) : gapHtml({ gap: '빈 칸' }))}</tr>`);
38
+ return `${table(['구분', '내용'], rows)}<div class="meta">참조: ${refsHtml(f.refs)}</div>`;
39
+ }
40
+
41
+ function proofHtml(p) {
42
+ const impl = arr(p.implemented).filter(obj).map((x) => `<tr>${td(esc(x.feature))}${td(esc(arr(x.modules).join(', ')), 'h')}${td(refsHtml(x.proofs), 'n')}</tr>`);
43
+ const not = arr(p.notImplemented).filter(obj).map((x) => `<tr>${td(esc(x.feature))}${td(esc(x.why))}</tr>`);
44
+ return `<div class="meta">구현된 기능과 증명 위치</div>${table(['구현된 기능', '모듈', '증명 위치'], impl)}<div class="meta">구현 못 한 기능</div>${table(['구현 못 한 기능', '사유'], not)}`;
45
+ }
46
+
47
+ const filled = (d) => DECISION_SLOTS.filter(([k]) => obj(d[k]) && !isGap(d[k])).length;
48
+ const emptyNames = (d) => DECISION_SLOTS.filter(([k]) => !(obj(d[k]) && !isGap(d[k]))).map(([, n]) => n);
49
+
50
+ function decisionRow(d) {
51
+ const miss = emptyNames(d);
52
+ const st = STATUS[d.status] || d.status;
53
+ return `<tr>${td(esc(d.id), 'h')}${td(esc(d.title))}${td(esc(st))}${td(esc(d.date || '-'), 't')}${td(`${filled(d)}/3`, 'd')}${td(miss.length ? `<span class="b miss">${esc(miss.join(', '))}</span>` : '-', 'n')}</tr>`;
54
+ }
55
+
56
+ function tradeoffsHtml(t) {
57
+ const gave = arr(t.gaveUp).filter(obj).map((x) => `<tr>${td(esc(x.what))}${td(esc(x.why))}</tr>`);
58
+ const got = arr(t.gained).filter(obj).map((x) => `<tr>${td(esc(x.what))}${td(esc(x.purpose))}</tr>`);
59
+ return `${table(['포기한 것', '이유'], gave)}${table(['얻은 것', '목적'], got)}`;
60
+ }
61
+
62
+ function decisionHtml(d) {
63
+ const opts = arr(d.options).filter(obj).map((x) => `<tr>${td(esc(x.name))}${td(esc(x.pros))}${td(esc(x.cons))}</tr>`);
64
+ const rejected = str(d.rejected) ? `<div class="meta">버린 선택지: ${esc(d.rejected)}</div>` : '';
65
+ const optim = (o) => `<div>${esc(o.process)}</div><div class="meta">참조한 모듈: ${esc(arr(o.modules).join(', ') || '없음')}</div><div class="meta">참조: ${refsHtml(o.refs)}</div>`;
66
+ const scale = (s) => `<div>${esc(s.argument)}</div>${str(s.limits) ? `<div class="meta">한계: ${esc(s.limits)}</div>` : ''}<div class="meta">참조: ${refsHtml(s.refs)}</div>`;
67
+ return `<h2>${esc(d.id)} ${esc(d.title)} <small>${esc(STATUS[d.status] || d.status)}</small></h2>
68
+ <div class="meta">맥락: ${esc(d.context)}</div>${table(['선택지', '장점', '단점'], opts)}${rejected}
69
+ <div><b>결론</b>: ${esc(d.decision)}</div><div class="meta">이유: ${esc(d.why)}</div>
70
+ ${sec('최적화 로직', slotHtml(d.optimization, optim))}${sec('확장 대응', slotHtml(d.scaling, scale))}${sec('포기와 얻은 것', slotHtml(d.tradeoffs, tradeoffsHtml))}
71
+ <div class="meta">근거 출처: ${refsHtml(d.sources)}</div>`;
72
+ }
73
+
74
+ // check 대조. 화면은 적힌 참조로, check 는 확인을 마친 참조로 센다(깨진 참조가 있으면 참조 없는 서술 수가 다름).
75
+ // m.adrCheck: check 가 남긴 adr-check.json({ sha, tally, at }), m.adr.sha: 지금 adr.json 의 sha256. text: 화면 집계 글.
76
+ export function checkNote(m, text) {
77
+ const rec = m.adrCheck && obj(m.adrCheck.value);
78
+ const b = (cls, t) => `<span class="b ${cls}">${esc(t)}</span>`;
79
+ if (!rec || typeof rec.sha !== 'string' || typeof rec.tally !== 'string') return b('unv', 'check 기록 없음. 참조 확인은 check');
80
+ const at = typeof rec.at === 'string' ? rec.at.slice(0, 19).replace('T', ' ') : '';
81
+ const when = at ? `check(${at} UTC)` : 'check';
82
+ if (rec.sha !== (m.adr && m.adr.sha)) return b('unv', `${when} 뒤 adr.json 이 바뀜. check 를 다시 실행하면 대조`);
83
+ if (rec.tally === text) return b('done', `${when} 와 같음`);
84
+ return b('miss', `${when} 기준: ${rec.tally}. 화면은 적힌 참조로 세어 다름. 확인 못 한 참조는 check 의 참조 줄`);
85
+ }
86
+
87
+ // ADR 탭 본문. 자료가 없거나 깨졌으면 다른 탭처럼 안내와 스키마 문서.
88
+ export function adrBody(m, emptyNotice) {
89
+ const v = m.adr && obj(m.adr.value);
90
+ if (!v) return emptyNotice('references/adr.md');
91
+ const p = obj(v.project) || {};
92
+ const ds = arr(v.decisions).filter(obj);
93
+ const tally = adrTally(v);
94
+ return `<div class="meta">의사 결정 기록. 위는 프로젝트 전체(검증, 흐름, 증거), 아래는 결정마다(최적화 로직, 확장 대응, 포기와 얻은 것). 자료: <code>adr.json</code>. 참조는 위치만 보이고 이 화면은 파일을 열지 않음. 참조 확인은 check.</div>
95
+ <div class="meta"><span class="b ${tally.any ? 'miss' : 'done'}">${esc(tally.text)}</span> ${checkNote(m, tally.text)}</div>
96
+ <h2>프로젝트 요약</h2>
97
+ ${sec('검증', slotHtml(p.verification, verificationHtml))}${sec('흐름', slotHtml(p.flow, flowHtml))}${sec('증거', slotHtml(p.proof, proofHtml))}
98
+ <h2>결정 목록 <small>${ds.length}건</small></h2>
99
+ ${table(['id', '제목', '상태', '날짜', '결정별 3항목', '빈 항목'], ds.map(decisionRow))}
100
+ ${ds.map(decisionHtml).join('\n')}`;
101
+ }