@su-record/vibe 3.2.61 → 3.2.62

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vibe",
3
3
  "displayName": "Vibe",
4
- "version": "3.2.61",
4
+ "version": "3.2.62",
5
5
  "description": "Verification harness for AI coding agents — \"done\" is decided by deterministic gates (test exit codes, run-ledger, regression memory), not the model self-report.",
6
6
  "author": {
7
7
  "name": "su-record",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe",
3
- "version": "3.2.61",
3
+ "version": "3.2.62",
4
4
  "description": "Verification harness for AI coding agents — \"done\" is decided by deterministic gates (test exit codes, run-ledger, regression memory), not the model self-report.",
5
5
  "author": {
6
6
  "name": "su-record",
package/CLAUDE.md CHANGED
@@ -69,8 +69,11 @@ Loop semantics SSOT: `vibe/rules/loop-contract.md` (ANCHOR→ACT→JUDGE→RECOR
69
69
  | `npm run gen:plugin-hooks:check` | 훅 정의 세 벌의 드리프트 |
70
70
  | `npm run validate:mermaid` | 렌더 안 되는 다이어그램 — 그림이 안 그려지면 리뷰 표면이 죽는다 |
71
71
  | `npm run validate:plugin-tree` | 배포 트리와 소스 불일치 |
72
+ | `npm run gen:agents-md:check` | CLAUDE.md ↔ AGENTS.md 드리프트 — 한 사실에 집이 둘 |
73
+ | `npm run validate:spec-lifecycle` | SPEC 헤더의 닫힌 집합 이탈 · 죽은 Anchor(코드가 움직였는데 SPEC 이 안 따라옴) |
74
+ | `npm run validate:cache-surface` | 상시 로드 자산 ↔ `vibe/rules/prefix-cache-surface.md` 불일치 |
72
75
 
73
- 마지막 둘은 CI(`test.yml`)에서도 돈다. **배포 순서는 PR 병합 먼저, 태그는 그다음** — 태그를 먼저 밀면 보호 브랜치에 막혀 병합이 실패해도 CI 가 이미 npm 에 게시한다(실측 v3.2.19).
76
+ 이 표 전부가 CI(`test.yml`)에서 돈다 — 아무도 안 돌리는 가드는 아무것도 잡지 못한다. **배포 순서는 PR 병합 먼저, 태그는 그다음** — 태그를 먼저 밀면 보호 브랜치에 막혀 병합이 실패해도 CI 가 이미 npm 에 게시한다(실측 v3.2.19).
74
77
 
75
78
  ### Config Locations
76
79
  | Path | Purpose |
@@ -200,6 +203,7 @@ Public skills use the `vibe.*` namespace and are classified as **entry** / **sta
200
203
  ## Context Management
201
204
 
202
205
  - **Model routing: inherit by default** — 서브에이전트는 세션 모델을 상속한다. 명시적 예외만 tier alias 로 지정 (아키텍처 심층 리뷰 → `opus`). 구세대 "탐색→Haiku·구현→Sonnet" 비용 라우팅은 폐기 — 강한 기본 모델에서 라우팅 우회가 절약보다 품질 손실이 크다
206
+ - **어떤 자산이 프리픽스 캐시를 무효화하는가**: `vibe/rules/prefix-cache-surface.md` (표면별 `Model Experience` + `KV Cache effect`, `validate:cache-surface` 가 실물과 맞춘다)
203
207
  - At 85%+ context: `save_memory` → `/new` → `/vibe.continue` (raised from 70% — `/new` 는 KV prefix cache 를 전량 폐기하므로, 압축 빈도를 낮춰 캐쉬 재사용을 늘린다)
204
208
  - **단계 경계 리셋 (용량 기준과 병행)**: SPEC 승인 시점에 새 세션에서 `/vibe.run` 을 시작하는 선택지를 승인 메시지에 편승시킨다 (`vibe.spec` Step 6 `[2]`). 명확화 왕복 ≥2회 · SPEC 수정 요청 ≥1회 · 분할 SPEC 중 하나라도 충족할 때만 권장 표시. 85% 규칙을 대체하지 않는다 — 용량이 찼을 때가 아니라 **잔류 컨텍스트가 노이즈가 되는 경계**에서 끊는 별개 축이다 (명확화 왕복·기각안 논의 텍스트가 구현에 새는 것을 막는다)
205
209
 
@@ -0,0 +1,45 @@
1
+ /**
2
+ * `CLAUDE.md` → `AGENTS.md` 결정론 생성.
3
+ *
4
+ * ## 왜 생성인가
5
+ *
6
+ * 두 파일은 같은 사실의 두 집이다. `CLAUDE.md` 는 "AGENTS.md 는 재생성된다" 고 선언만 하고
7
+ * 검증하는 명령이 없었고, 그 결과 한 파일 안에서 **과잉 번역과 미번역이 공존**했다
8
+ * (실측 2026-09-02: `pnpm lint:ratchet` 이 `$vibe lint:ratchet` 으로 잘못 번역된 줄 1건,
9
+ * Dual-Harness Doctrine 절의 `/vibe` 디스패처가 번역 안 된 줄 1건).
10
+ *
11
+ * 사람이 두 벌을 손으로 맞추는 한 이 드리프트는 반복된다. 그래서 번역 규칙을 데이터로 꺼내고
12
+ * (`scripts/agents-md-rules.json`) 생성 결과와 저장소 파일을 `--check` 로 비교한다.
13
+ *
14
+ * ## 왜 단순 치환으로는 안 되는가
15
+ *
16
+ * `/vibe` → `$vibe` 를 그냥 돌리면 `@su-record/vibe`·`plugins/vibe/`·`.claude/vibe/` 가 함께 깨진다.
17
+ * 그래서 3단이다: **보호 → 치환 → 복원**. 그 뒤에 두 하네스가 의도적으로 다른 문단
18
+ * (파일 매핑 안내문, Codex 진입점 설명, Git include/exclude)을 `overrides` 로 갈아끼운다.
19
+ *
20
+ * `overrides` 는 **치환 이후** 텍스트에 적용된다 — 즉 `find` 는 번역된 형태(`$vibe`)로 쓴다.
21
+ * 각 override 는 정확히 한 번 매치돼야 한다: 0번이면 대상을 잃은 규칙이고(원문이 바뀌었는데
22
+ * 규칙이 안 따라왔다), 2번 이상이면 의도하지 않은 곳까지 갈아끼운다. 둘 다 게이트가 막는다.
23
+ */
24
+ export interface AgentsMdRules {
25
+ /** 치환에서 제외할 리터럴 — `/vibe` 를 품고 있지만 슬래시 명령이 아닌 것들 */
26
+ protect: string[];
27
+ substitutions: {
28
+ find: string;
29
+ replace: string;
30
+ why: string;
31
+ }[];
32
+ /** 두 하네스가 의도적으로 다른 문단. 치환 이후 텍스트에 적용된다 */
33
+ overrides: {
34
+ find: string;
35
+ replace: string;
36
+ why: string;
37
+ }[];
38
+ }
39
+ export interface GenerateResult {
40
+ output: string;
41
+ /** 규칙 자체의 고장 — 대상을 잃었거나 너무 많이 잡는 규칙 */
42
+ findings: string[];
43
+ }
44
+ export declare function generateAgentsMd(source: string, rules: AgentsMdRules): GenerateResult;
45
+ //# sourceMappingURL=agentsMd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentsMd.d.ts","sourceRoot":"","sources":["../../../src/tools/docs/agentsMd.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,WAAW,aAAa;IAC5B,oDAAoD;IACpD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAChE,0CAA0C;IAC1C,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC7D;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAQD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,cAAc,CA4BrF"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * `CLAUDE.md` → `AGENTS.md` 결정론 생성.
3
+ *
4
+ * ## 왜 생성인가
5
+ *
6
+ * 두 파일은 같은 사실의 두 집이다. `CLAUDE.md` 는 "AGENTS.md 는 재생성된다" 고 선언만 하고
7
+ * 검증하는 명령이 없었고, 그 결과 한 파일 안에서 **과잉 번역과 미번역이 공존**했다
8
+ * (실측 2026-09-02: `pnpm lint:ratchet` 이 `$vibe lint:ratchet` 으로 잘못 번역된 줄 1건,
9
+ * Dual-Harness Doctrine 절의 `/vibe` 디스패처가 번역 안 된 줄 1건).
10
+ *
11
+ * 사람이 두 벌을 손으로 맞추는 한 이 드리프트는 반복된다. 그래서 번역 규칙을 데이터로 꺼내고
12
+ * (`scripts/agents-md-rules.json`) 생성 결과와 저장소 파일을 `--check` 로 비교한다.
13
+ *
14
+ * ## 왜 단순 치환으로는 안 되는가
15
+ *
16
+ * `/vibe` → `$vibe` 를 그냥 돌리면 `@su-record/vibe`·`plugins/vibe/`·`.claude/vibe/` 가 함께 깨진다.
17
+ * 그래서 3단이다: **보호 → 치환 → 복원**. 그 뒤에 두 하네스가 의도적으로 다른 문단
18
+ * (파일 매핑 안내문, Codex 진입점 설명, Git include/exclude)을 `overrides` 로 갈아끼운다.
19
+ *
20
+ * `overrides` 는 **치환 이후** 텍스트에 적용된다 — 즉 `find` 는 번역된 형태(`$vibe`)로 쓴다.
21
+ * 각 override 는 정확히 한 번 매치돼야 한다: 0번이면 대상을 잃은 규칙이고(원문이 바뀌었는데
22
+ * 규칙이 안 따라왔다), 2번 이상이면 의도하지 않은 곳까지 갈아끼운다. 둘 다 게이트가 막는다.
23
+ */
24
+ const SENTINEL = String.fromCharCode(0);
25
+ function splitAll(haystack, needle, replacement) {
26
+ return haystack.split(needle).join(replacement);
27
+ }
28
+ export function generateAgentsMd(source, rules) {
29
+ const findings = [];
30
+ // 긴 리터럴부터 보호한다 — `@su-record/vibe` 가 `su-record/vibe` 보다 먼저 잡혀야 한다
31
+ const protect = [...rules.protect].sort((a, b) => b.length - a.length);
32
+ let text = source;
33
+ protect.forEach((token, i) => {
34
+ text = splitAll(text, token, SENTINEL + i + SENTINEL);
35
+ });
36
+ for (const s of rules.substitutions)
37
+ text = splitAll(text, s.find, s.replace);
38
+ protect.forEach((token, i) => {
39
+ text = splitAll(text, SENTINEL + i + SENTINEL, token);
40
+ });
41
+ for (const o of rules.overrides) {
42
+ const hits = text.split(o.find).length - 1;
43
+ if (hits !== 1) {
44
+ findings.push(`override 가 ${hits}번 매치됐다 (정확히 1번이어야 한다): ${JSON.stringify(o.find.slice(0, 60))}`);
45
+ continue;
46
+ }
47
+ text = splitAll(text, o.find, o.replace);
48
+ }
49
+ return { output: text, findings };
50
+ }
51
+ //# sourceMappingURL=agentsMd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentsMd.js","sourceRoot":"","sources":["../../../src/tools/docs/agentsMd.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAgBH,MAAM,QAAQ,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;AAExC,SAAS,QAAQ,CAAC,QAAgB,EAAE,MAAc,EAAE,WAAmB;IACrE,OAAO,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAc,EAAE,KAAoB;IACnE,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,mEAAmE;IACnE,MAAM,OAAO,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IACvE,IAAI,IAAI,GAAG,MAAM,CAAC;IAClB,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QAC3B,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,aAAa;QAAE,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;IAE9E,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QAC3B,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QAChC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;QAC3C,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;YACf,QAAQ,CAAC,IAAI,CACX,cAAc,IAAI,0BAA0B,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAClF,CAAC;YACF,SAAS;QACX,CAAC;QACD,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AACpC,CAAC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * 프리픽스 캐시 표면 문서(`vibe/rules/prefix-cache-surface.md`)의 판정 로직.
3
+ *
4
+ * 문서가 자산을 나열하는 순간 그것은 **두 번째 집**이 된다 — 실물이 움직이면 조용히 어긋난다.
5
+ * dsh 가 정확히 그렇게 무너졌다: 게이트가 걸린 `packages/README.md` 는 50개 전부 맞았고,
6
+ * 게이트 없는 루트 문서의 레이아웃 트리는 존재하지 않는 그룹 2개를 나열하고 17개를 빠뜨렸다.
7
+ * 그래서 나열은 허용하되 **양방향으로** 검사한다: 문서에 없는 실물도, 실물이 없는 문서 항목도 실패다.
8
+ *
9
+ * 파일시스템 수집은 여기서 하지 않는다 — 이 모듈은 순수 함수로 두고
10
+ * `scripts/validate-cache-surface.ts` 가 실물을 모아 넘긴다.
11
+ */
12
+ /** 자산 문서라면 반드시 답해야 하는 두 질문 */
13
+ export declare const REQUIRED_SUBSECTIONS: readonly ["Model Experience", "KV Cache effect"];
14
+ export interface SurfaceSection {
15
+ id: string;
16
+ /** 마커 아래 본문 (다음 마커 직전까지) */
17
+ body: string;
18
+ /** 본문의 표에서 뽑은 백틱 경로 */
19
+ entries: string[];
20
+ }
21
+ export interface SurfaceFinding {
22
+ code: string;
23
+ message: string;
24
+ }
25
+ /** 문서를 절 단위로 쪼갠다 */
26
+ export declare function parseSurfaceDoc(content: string): SurfaceSection[];
27
+ /** 모든 절이 두 질문에 답했는가 */
28
+ export declare function checkRequiredSubsections(sections: SurfaceSection[]): SurfaceFinding[];
29
+ /**
30
+ * 나열된 절과 실물을 양방향으로 맞춘다.
31
+ *
32
+ * @param sections 문서에서 파싱한 절
33
+ * @param actual 절 id → 실제 존재하는 자산 경로. 여기 없는 절은 집계 절로 보고 건너뛴다
34
+ */
35
+ export declare function checkEnumeratedSurfaces(sections: SurfaceSection[], actual: Record<string, readonly string[]>): SurfaceFinding[];
36
+ export declare function checkCacheSurfaceDoc(content: string, actual: Record<string, readonly string[]>): SurfaceFinding[];
37
+ //# sourceMappingURL=cacheSurface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cacheSurface.d.ts","sourceRoot":"","sources":["../../../src/tools/docs/cacheSurface.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,8BAA8B;AAC9B,eAAO,MAAM,oBAAoB,kDAAmD,CAAC;AAErF,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAsBD,oBAAoB;AACpB,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,EAAE,CAQjE;AAED,uBAAuB;AACvB,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CAarF;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,cAAc,EAAE,EAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,GACxC,cAAc,EAAE,CAsBlB;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,GACxC,cAAc,EAAE,CAMlB"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * 프리픽스 캐시 표면 문서(`vibe/rules/prefix-cache-surface.md`)의 판정 로직.
3
+ *
4
+ * 문서가 자산을 나열하는 순간 그것은 **두 번째 집**이 된다 — 실물이 움직이면 조용히 어긋난다.
5
+ * dsh 가 정확히 그렇게 무너졌다: 게이트가 걸린 `packages/README.md` 는 50개 전부 맞았고,
6
+ * 게이트 없는 루트 문서의 레이아웃 트리는 존재하지 않는 그룹 2개를 나열하고 17개를 빠뜨렸다.
7
+ * 그래서 나열은 허용하되 **양방향으로** 검사한다: 문서에 없는 실물도, 실물이 없는 문서 항목도 실패다.
8
+ *
9
+ * 파일시스템 수집은 여기서 하지 않는다 — 이 모듈은 순수 함수로 두고
10
+ * `scripts/validate-cache-surface.ts` 가 실물을 모아 넘긴다.
11
+ */
12
+ /** 절을 식별하는 기계 마커 — 헤딩 문구가 바뀌어도 판정이 흔들리지 않게 */
13
+ const SURFACE_MARKER = /<!--\s*surface:\s*([a-z0-9-]+)\s*-->/gi;
14
+ /** 자산 문서라면 반드시 답해야 하는 두 질문 */
15
+ export const REQUIRED_SUBSECTIONS = ['Model Experience', 'KV Cache effect'];
16
+ /** `- **Model Experience**: …` 형태로 내용이 있는가 (헤딩만 있고 빈 절은 통과시키지 않는다) */
17
+ function hasSubsection(body, label) {
18
+ const re = new RegExp(String.raw `^\s*[-*#]+\s*\*{0,2}` + label + String.raw `\*{0,2}\s*[::]?\s*(.*)$`, 'im');
19
+ const m = body.match(re);
20
+ if (!m)
21
+ return false;
22
+ return m[1].trim().length > 0 || /\n\s*\S/.test(body.slice((m.index ?? 0) + m[0].length));
23
+ }
24
+ function tableEntries(body) {
25
+ const out = [];
26
+ for (const line of body.split('\n')) {
27
+ if (!line.trimStart().startsWith('|'))
28
+ continue;
29
+ for (const m of line.matchAll(/`([^`\n]+)`/g)) {
30
+ const v = m[1].trim();
31
+ if (/\.(md|js|ts|json)$/.test(v))
32
+ out.push(v);
33
+ }
34
+ }
35
+ return out;
36
+ }
37
+ /** 문서를 절 단위로 쪼갠다 */
38
+ export function parseSurfaceDoc(content) {
39
+ const marks = [...content.matchAll(SURFACE_MARKER)];
40
+ return marks.map((m, i) => {
41
+ const start = (m.index ?? 0) + m[0].length;
42
+ const end = i + 1 < marks.length ? marks[i + 1].index ?? content.length : content.length;
43
+ const body = content.slice(start, end);
44
+ return { id: m[1], body, entries: tableEntries(body) };
45
+ });
46
+ }
47
+ /** 모든 절이 두 질문에 답했는가 */
48
+ export function checkRequiredSubsections(sections) {
49
+ const out = [];
50
+ for (const s of sections) {
51
+ for (const label of REQUIRED_SUBSECTIONS) {
52
+ if (!hasSubsection(s.body, label)) {
53
+ out.push({
54
+ code: 'missing-subsection',
55
+ message: `표면 "${s.id}" 에 "${label}" 절이 없거나 비어 있다.`,
56
+ });
57
+ }
58
+ }
59
+ }
60
+ return out;
61
+ }
62
+ /**
63
+ * 나열된 절과 실물을 양방향으로 맞춘다.
64
+ *
65
+ * @param sections 문서에서 파싱한 절
66
+ * @param actual 절 id → 실제 존재하는 자산 경로. 여기 없는 절은 집계 절로 보고 건너뛴다
67
+ */
68
+ export function checkEnumeratedSurfaces(sections, actual) {
69
+ const out = [];
70
+ for (const [id, paths] of Object.entries(actual)) {
71
+ const section = sections.find((s) => s.id === id);
72
+ if (!section) {
73
+ out.push({ code: 'missing-surface', message: `표면 절 "${id}" 이 문서에 없다.` });
74
+ continue;
75
+ }
76
+ const documented = new Set(section.entries);
77
+ const real = new Set(paths);
78
+ for (const p of real) {
79
+ if (!documented.has(p)) {
80
+ out.push({ code: 'undocumented-asset', message: `${id}: 실물이 문서에 없다 — ${p}` });
81
+ }
82
+ }
83
+ for (const p of documented) {
84
+ if (!real.has(p)) {
85
+ out.push({ code: 'phantom-asset', message: `${id}: 문서에만 있고 실물이 없다 — ${p}` });
86
+ }
87
+ }
88
+ }
89
+ return out;
90
+ }
91
+ export function checkCacheSurfaceDoc(content, actual) {
92
+ const sections = parseSurfaceDoc(content);
93
+ if (sections.length === 0) {
94
+ return [{ code: 'no-surfaces', message: '표면 마커(<!-- surface: id -->)가 하나도 없다.' }];
95
+ }
96
+ return [...checkRequiredSubsections(sections), ...checkEnumeratedSurfaces(sections, actual)];
97
+ }
98
+ //# sourceMappingURL=cacheSurface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cacheSurface.js","sourceRoot":"","sources":["../../../src/tools/docs/cacheSurface.ts"],"names":[],"mappings":"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"}
@@ -13,4 +13,6 @@ export { generateTraceabilityMatrix, formatMatrixAsMarkdown, formatMatrixAsHtml
13
13
  export type { TraceItem, TraceabilityMatrix, TraceSummary, TraceMatrixOptions } from './traceabilityMatrix.js';
14
14
  export { validateSpecDocument, formatSpecValidation, featureSlugFromPath, } from './validateSpecDocument.js';
15
15
  export type { SpecValidationResult, SpecFinding, SpecFindingSeverity, } from './validateSpecDocument.js';
16
+ export { SPEC_STATUSES, SPEC_CLASSES, ANCHOR_REQUIRED_CLASSES, parseSpecLifecycle, checkSpecLifecycle, anchorsRequired, isLifecycleExempt, } from './specLifecycle.js';
17
+ export type { SpecStatus, SpecClass, SpecLifecycleHeader, LifecycleFinding, } from './specLifecycle.js';
16
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/spec/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,EACf,aAAa,EACb,aAAa,EACb,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,QAAQ,EACR,YAAY,EACb,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,WAAW,EACX,SAAS,EACT,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,YAAY,EACb,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,oBAAoB,EACpB,aAAa,EACd,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,2BAA2B,EAC3B,4BAA4B,EAC5B,eAAe,EACf,wBAAwB,EACxB,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,eAAe,EACf,4BAA4B,EAC5B,6BAA6B,EAC7B,0BAA0B,GAC3B,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,0BAA0B,EAC1B,sBAAsB,EACtB,kBAAkB,EACnB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,SAAS,EACT,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EACnB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,oBAAoB,EACpB,WAAW,EACX,mBAAmB,GACpB,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/spec/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,EACf,aAAa,EACb,aAAa,EACb,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,QAAQ,EACR,YAAY,EACb,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,WAAW,EACX,SAAS,EACT,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,YAAY,EACb,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,oBAAoB,EACpB,aAAa,EACd,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,2BAA2B,EAC3B,4BAA4B,EAC5B,eAAe,EACf,wBAAwB,EACxB,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,eAAe,EACf,4BAA4B,EAC5B,6BAA6B,EAC7B,0BAA0B,GAC3B,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,0BAA0B,EAC1B,sBAAsB,EACtB,kBAAkB,EACnB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,SAAS,EACT,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EACnB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,oBAAoB,EACpB,WAAW,EACX,mBAAmB,GACpB,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EACL,aAAa,EACb,YAAY,EACZ,uBAAuB,EACvB,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,UAAU,EACV,SAAS,EACT,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,oBAAoB,CAAC"}
@@ -14,4 +14,6 @@ export { compileExecutionPacket, getHarnessProfile, validateExecutionPacket, wri
14
14
  export { generateTraceabilityMatrix, formatMatrixAsMarkdown, formatMatrixAsHtml } from './traceabilityMatrix.js';
15
15
  // SPEC Code Guard — 산출물이 하류(run/verify)로 넘어가기 전 노드 단위 검사
16
16
  export { validateSpecDocument, formatSpecValidation, featureSlugFromPath, } from './validateSpecDocument.js';
17
+ // SPEC lifecycle — 닫힌 Status/Class 집합과 Anchors 규율 (CI 게이트가 같은 정의를 import 한다)
18
+ export { SPEC_STATUSES, SPEC_CLASSES, ANCHOR_REQUIRED_CLASSES, parseSpecLifecycle, checkSpecLifecycle, anchorsRequired, isLifecycleExempt, } from './specLifecycle.js';
17
19
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/spec/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,iBAAiB;AACjB,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,EACf,aAAa,EACb,aAAa,EACb,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAE5B,aAAa;AACb,OAAO,EACL,QAAQ,EACR,YAAY,EACb,MAAM,gBAAgB,CAAC;AAOxB,iBAAiB;AACjB,OAAO,EACL,YAAY,EACb,MAAM,oBAAoB,CAAC;AAM5B,4BAA4B;AAC5B,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAe9B,sBAAsB;AACtB,OAAO,EACL,0BAA0B,EAC1B,sBAAsB,EACtB,kBAAkB,EACnB,MAAM,yBAAyB,CAAC;AAQjC,yDAAyD;AACzD,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/spec/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,iBAAiB;AACjB,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,EACf,aAAa,EACb,aAAa,EACb,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAE5B,aAAa;AACb,OAAO,EACL,QAAQ,EACR,YAAY,EACb,MAAM,gBAAgB,CAAC;AAOxB,iBAAiB;AACjB,OAAO,EACL,YAAY,EACb,MAAM,oBAAoB,CAAC;AAM5B,4BAA4B;AAC5B,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAe9B,sBAAsB;AACtB,OAAO,EACL,0BAA0B,EAC1B,sBAAsB,EACtB,kBAAkB,EACnB,MAAM,yBAAyB,CAAC;AAQjC,yDAAyD;AACzD,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,2BAA2B,CAAC;AAOnC,6EAA6E;AAC7E,OAAO,EACL,aAAa,EACb,YAAY,EACZ,uBAAuB,EACvB,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,GAClB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,40 @@
1
+ /** SPEC 이 가질 수 있는 lifecycle 상태 — 이 집합 밖의 값은 게이트가 거부한다 */
2
+ export declare const SPEC_STATUSES: readonly ["DRAFT", "APPROVED", "VERIFIED", "SUPERSEDED", "REJECTED"];
3
+ export type SpecStatus = (typeof SPEC_STATUSES)[number];
4
+ /** SPEC 의 변경 종류 — dsh Agent Note 의 닫힌 6종을 그대로 쓴다 */
5
+ export declare const SPEC_CLASSES: readonly ["feature", "bug-fix", "simplification", "architecture", "process", "testing"];
6
+ export type SpecClass = (typeof SPEC_CLASSES)[number];
7
+ /**
8
+ * Anchors 를 요구하는 Class.
9
+ *
10
+ * 코드 경로에 실제로 안착하는 종류만 넣는다. process/testing/simplification 은
11
+ * 규약·워크플로 변경이라 고정할 경로가 없는 경우가 많고, 억지로 요구하면 통과 의식이 된다.
12
+ */
13
+ export declare const ANCHOR_REQUIRED_CLASSES: readonly SpecClass[];
14
+ export interface SpecLifecycleHeader {
15
+ status?: string;
16
+ specClass?: string;
17
+ /** `## Anchors` 절에서 뽑은 경로. 절이 없으면 undefined (빈 배열과 구분한다) */
18
+ anchors?: string[];
19
+ }
20
+ export interface LifecycleFinding {
21
+ code: string;
22
+ message: string;
23
+ }
24
+ /**
25
+ * 분할 SPEC 의 phase 파일은 헤더를 요구하지 않는다 — 헤더는 `_index.md` 한 벌이
26
+ * SSOT 다. phase 파일마다 Status 를 두면 둘이 어긋나는 새 드리프트를 만든다.
27
+ */
28
+ export declare function isLifecycleExempt(specPath: string): boolean;
29
+ /** SPEC 원문에서 lifecycle 헤더를 읽는다 */
30
+ export declare function parseSpecLifecycle(content: string): SpecLifecycleHeader;
31
+ /** VERIFIED + 코드성 Class 면 Anchors 절이 있고 비어 있지 않아야 한다 */
32
+ export declare function anchorsRequired(header: SpecLifecycleHeader): boolean;
33
+ /**
34
+ * 내용만으로 판정 가능한 lifecycle 검사.
35
+ *
36
+ * @param content SPEC 마크다운 원문
37
+ * @param specPath 파일 경로 — phase 파일 면제 판정에 쓴다
38
+ */
39
+ export declare function checkSpecLifecycle(content: string, specPath?: string): LifecycleFinding[];
40
+ //# sourceMappingURL=specLifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"specLifecycle.d.ts","sourceRoot":"","sources":["../../../src/tools/spec/specLifecycle.ts"],"names":[],"mappings":"AAoBA,yDAAyD;AACzD,eAAO,MAAM,aAAa,sEAAuE,CAAC;AAClG,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAExD,oDAAoD;AACpD,eAAO,MAAM,YAAY,yFAOf,CAAC;AACX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtD;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,SAAS,EAA2C,CAAC;AAEpG,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAOD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE3D;AA8BD,kCAAkC;AAClC,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,mBAAmB,CAQvE;AAkCD,wDAAwD;AACxD,wBAAgB,eAAe,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAGpE;AAaD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,gBAAgB,EAAE,CASzF"}
@@ -0,0 +1,147 @@
1
+ /**
2
+ * SPEC lifecycle 규율 — 헤더의 닫힌 집합과 "썩은 앵커" 를 판정한다.
3
+ *
4
+ * 배경: `.vibe/specs/**` 는 VERIFIED 로 확정된 뒤에도 코드가 움직이면 그대로 남는다.
5
+ * 실측(2026-09-02): 29개 중 20개에 Status 줄이 아예 없었고, 남은 9개의 값은 5가지
6
+ * 형태로 갈라져 있었다 (`COMPLETE`, 소문자 `verified`, 후행 공백 …). 템플릿은
7
+ * `DRAFT | APPROVED` 만 선언했으니 선언과 실물이 이미 어긋나 있었다.
8
+ *
9
+ * 두 가지를 고정한다:
10
+ * - **닫힌 집합** — Status·Class 는 여기 있는 값만 쓸 수 있다. 새 값을 만들려면
11
+ * 이 파일을 고쳐야 하고, 그 순간 리뷰 대상이 된다.
12
+ * - **Anchors** — VERIFIED 이고 코드에 고정되는 Class 인 SPEC 은 자기가 안착한
13
+ * 경로를 나열해야 한다. 그 경로가 사라지면 게이트가 실패한다. lifecycle 표기만으로는
14
+ * 썩음을 못 잡는다 — 문서는 Status 를 바꾸지 않은 채 늙기 때문이다.
15
+ *
16
+ * 경로 존재 검사는 여기서 하지 않는다. 이 모듈은 순수 함수로 두고(하네스·CWD 무관),
17
+ * 파일시스템 판정은 `scripts/validate-spec-lifecycle.ts` 가 맡는다.
18
+ */
19
+ import path from 'path';
20
+ /** SPEC 이 가질 수 있는 lifecycle 상태 — 이 집합 밖의 값은 게이트가 거부한다 */
21
+ export const SPEC_STATUSES = ['DRAFT', 'APPROVED', 'VERIFIED', 'SUPERSEDED', 'REJECTED'];
22
+ /** SPEC 의 변경 종류 — dsh Agent Note 의 닫힌 6종을 그대로 쓴다 */
23
+ export const SPEC_CLASSES = [
24
+ 'feature',
25
+ 'bug-fix',
26
+ 'simplification',
27
+ 'architecture',
28
+ 'process',
29
+ 'testing',
30
+ ];
31
+ /**
32
+ * Anchors 를 요구하는 Class.
33
+ *
34
+ * 코드 경로에 실제로 안착하는 종류만 넣는다. process/testing/simplification 은
35
+ * 규약·워크플로 변경이라 고정할 경로가 없는 경우가 많고, 억지로 요구하면 통과 의식이 된다.
36
+ */
37
+ export const ANCHOR_REQUIRED_CLASSES = ['feature', 'bug-fix', 'architecture'];
38
+ const STATUS_LINE = /^\s*[-*]?\s*\*{0,2}Status\*{0,2}\s*[::]\s*(.+)$/im;
39
+ const CLASS_LINE = /^\s*[-*]?\s*\*{0,2}Class\*{0,2}\s*[::]\s*(.+)$/im;
40
+ const ANCHORS_HEADING = /^#{2,4}\s*(?:Anchors|앵커)\s*$/im;
41
+ const LIST_ITEM = /^\s*[-*]\s+(.+)$/;
42
+ /**
43
+ * 분할 SPEC 의 phase 파일은 헤더를 요구하지 않는다 — 헤더는 `_index.md` 한 벌이
44
+ * SSOT 다. phase 파일마다 Status 를 두면 둘이 어긋나는 새 드리프트를 만든다.
45
+ */
46
+ export function isLifecycleExempt(specPath) {
47
+ return /^phase-\d+-/.test(path.basename(specPath));
48
+ }
49
+ /**
50
+ * 헤더 값에서 첫 토큰만 뽑는다 — `VERIFIED (2026-08-29 — …)` 의 괄호 뒷말은 자유 서술이다.
51
+ *
52
+ * 하이픈은 구분자로 쓰지 않는다: Class `bug-fix` 가 `bug` 로 잘린다.
53
+ */
54
+ function firstToken(value) {
55
+ return value.trim().split(/[\s(·—]/)[0].trim();
56
+ }
57
+ function parseAnchors(content) {
58
+ const heading = content.match(ANCHORS_HEADING);
59
+ if (!heading)
60
+ return undefined;
61
+ const level = (heading[0].match(/^#+/) ?? ['#'])[0].length;
62
+ const after = content.slice((heading.index ?? 0) + heading[0].length);
63
+ const next = after.search(new RegExp(`^#{1,${level}}\\s`, 'm'));
64
+ const body = next === -1 ? after : after.slice(0, next);
65
+ const out = [];
66
+ for (const line of body.split('\n')) {
67
+ const m = line.match(LIST_ITEM);
68
+ if (!m)
69
+ continue;
70
+ const code = m[1].match(/`([^`]+)`/);
71
+ out.push((code ? code[1] : m[1]).trim());
72
+ }
73
+ return out;
74
+ }
75
+ /** SPEC 원문에서 lifecycle 헤더를 읽는다 */
76
+ export function parseSpecLifecycle(content) {
77
+ const status = content.match(STATUS_LINE);
78
+ const specClass = content.match(CLASS_LINE);
79
+ return {
80
+ status: status ? firstToken(status[1]) : undefined,
81
+ specClass: specClass ? firstToken(specClass[1]).toLowerCase() : undefined,
82
+ anchors: parseAnchors(content),
83
+ };
84
+ }
85
+ function checkStatus(header, out) {
86
+ if (!header.status) {
87
+ out.push({
88
+ code: 'no-spec-status',
89
+ message: `Status 필드가 없다 — lifecycle 을 판정할 수 없다 (${SPEC_STATUSES.join(' | ')}).`,
90
+ });
91
+ return;
92
+ }
93
+ if (!SPEC_STATUSES.includes(header.status)) {
94
+ out.push({
95
+ code: 'invalid-spec-status',
96
+ message: `Status 값을 알 수 없다: "${header.status}" — ${SPEC_STATUSES.join(' | ')} 중 하나여야 한다.`,
97
+ });
98
+ }
99
+ }
100
+ function checkClass(header, out) {
101
+ if (!header.specClass) {
102
+ out.push({
103
+ code: 'no-spec-class',
104
+ message: `Class 필드가 없다 — Anchors 요구 여부를 판정할 수 없다 (${SPEC_CLASSES.join(' | ')}).`,
105
+ });
106
+ return;
107
+ }
108
+ if (!SPEC_CLASSES.includes(header.specClass)) {
109
+ out.push({
110
+ code: 'invalid-spec-class',
111
+ message: `Class 값을 알 수 없다: "${header.specClass}" — ${SPEC_CLASSES.join(' | ')} 중 하나여야 한다.`,
112
+ });
113
+ }
114
+ }
115
+ /** VERIFIED + 코드성 Class 면 Anchors 절이 있고 비어 있지 않아야 한다 */
116
+ export function anchorsRequired(header) {
117
+ return header.status === 'VERIFIED'
118
+ && ANCHOR_REQUIRED_CLASSES.includes(header.specClass ?? '');
119
+ }
120
+ function checkAnchors(header, out) {
121
+ if (!anchorsRequired(header))
122
+ return;
123
+ if (!header.anchors || header.anchors.length === 0) {
124
+ out.push({
125
+ code: 'no-anchors',
126
+ message: `VERIFIED + Class ${header.specClass} 인 SPEC 은 "## Anchors" 절에 안착한 경로를 나열해야 한다 `
127
+ + '— 경로가 사라지면 게이트가 썩음을 잡는다.',
128
+ });
129
+ }
130
+ }
131
+ /**
132
+ * 내용만으로 판정 가능한 lifecycle 검사.
133
+ *
134
+ * @param content SPEC 마크다운 원문
135
+ * @param specPath 파일 경로 — phase 파일 면제 판정에 쓴다
136
+ */
137
+ export function checkSpecLifecycle(content, specPath) {
138
+ if (specPath && isLifecycleExempt(specPath))
139
+ return [];
140
+ const header = parseSpecLifecycle(content);
141
+ const out = [];
142
+ checkStatus(header, out);
143
+ checkClass(header, out);
144
+ checkAnchors(header, out);
145
+ return out;
146
+ }
147
+ //# sourceMappingURL=specLifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"specLifecycle.js","sourceRoot":"","sources":["../../../src/tools/spec/specLifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,yDAAyD;AACzD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,CAAU,CAAC;AAGlG,oDAAoD;AACpD,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,SAAS;IACT,SAAS;IACT,gBAAgB;IAChB,cAAc;IACd,SAAS;IACT,SAAS;CACD,CAAC;AAGX;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAyB,CAAC,SAAS,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC;AAcpG,MAAM,WAAW,GAAG,mDAAmD,CAAC;AACxE,MAAM,UAAU,GAAG,kDAAkD,CAAC;AACtE,MAAM,eAAe,GAAG,gCAAgC,CAAC;AACzD,MAAM,SAAS,GAAG,kBAAkB,CAAC;AAErC;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,OAAO,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;AACrD,CAAC;AAED;;;;GAIG;AACH,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AACjD,CAAC;AAED,SAAS,YAAY,CAAC,OAAe;IACnC,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAC/C,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAE/B,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC3D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACtE,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,KAAK,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;IAChE,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAExD,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAChC,IAAI,CAAC,CAAC;YAAE,SAAS;QACjB,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QACrC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,kCAAkC;AAClC,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC5C,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;QAClD,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS;QACzE,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC;KAC/B,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,MAA2B,EAAE,GAAuB;IACvE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACnB,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,gBAAgB;YACtB,OAAO,EAAE,yCAAyC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI;SAChF,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IACD,IAAI,CAAE,aAAmC,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QAClE,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,qBAAqB;YAC3B,OAAO,EAAE,sBAAsB,MAAM,CAAC,MAAM,OAAO,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa;SAC1F,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,MAA2B,EAAE,GAAuB;IACtE,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QACtB,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,2CAA2C,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI;SACjF,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IACD,IAAI,CAAE,YAAkC,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QACpE,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,oBAAoB;YAC1B,OAAO,EAAE,qBAAqB,MAAM,CAAC,SAAS,OAAO,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa;SAC3F,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,eAAe,CAAC,MAA2B;IACzD,OAAO,MAAM,CAAC,MAAM,KAAK,UAAU;WAC7B,uBAA6C,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;AACvF,CAAC;AAED,SAAS,YAAY,CAAC,MAA2B,EAAE,GAAuB;IACxE,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC;QAAE,OAAO;IACrC,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnD,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,oBAAoB,MAAM,CAAC,SAAS,4CAA4C;kBACrF,0BAA0B;SAC/B,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe,EAAE,QAAiB;IACnE,IAAI,QAAQ,IAAI,iBAAiB,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,CAAC;IAEvD,MAAM,MAAM,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAuB,EAAE,CAAC;IACnC,WAAW,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACzB,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACxB,YAAY,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"validateSpecDocument.d.ts","sourceRoot":"","sources":["../../../src/tools/spec/validateSpecDocument.ts"],"names":[],"mappings":"AAiBA,MAAM,MAAM,mBAAmB,GAAG,IAAI,GAAG,IAAI,CAAC;AAE9C,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,oBAAoB;IACnC,wCAAwC;IACxC,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,iCAAiC;IACjC,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAwBD,+CAA+C;AAC/C,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG5D;AA6MD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,oBAAoB,CAyBtB;AAED,mCAAmC;AACnC,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAUzE"}
1
+ {"version":3,"file":"validateSpecDocument.d.ts","sourceRoot":"","sources":["../../../src/tools/spec/validateSpecDocument.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,mBAAmB,GAAG,IAAI,GAAG,IAAI,CAAC;AAE9C,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,oBAAoB;IACnC,wCAAwC;IACxC,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,iCAAiC;IACjC,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAwBD,+CAA+C;AAC/C,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG5D;AAyND;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,oBAAoB,CA0BtB;AAED,mCAAmC;AACnC,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAUzE"}
@@ -14,6 +14,7 @@
14
14
  * (dual-harness-doctrine 운영 규칙 2).
15
15
  */
16
16
  import path from 'path';
17
+ import { checkSpecLifecycle } from './specLifecycle.js';
17
18
  const REQ_ID = /\bREQ-([a-z0-9-]+)-(\d{3})\b/g;
18
19
  const STAKES_LINE = /^\s*[-*]?\s*\*{0,2}Stakes\*{0,2}\s*[::]\s*(.+)$/im;
19
20
  const VALID_STAKES = ['demo', 'prototype', 'production'];
@@ -215,6 +216,17 @@ function checkPlaceholders(content, findings) {
215
216
  }
216
217
  }
217
218
  }
219
+ /**
220
+ * lifecycle 헤더(Status·Class·Anchors) — 닫힌 집합 판정은 `specLifecycle.ts` 가 소유한다.
221
+ *
222
+ * P1 인 이유: 값이 열려 있으면 `.vibe/specs/` 전수를 도는 CI 게이트가 무엇을 통과로
223
+ * 볼지 정할 수 없다. 실측으로 5가지 표기가 공존했고 그중 둘은 템플릿에 없는 값이었다.
224
+ */
225
+ function checkLifecycle(content, specPath, findings) {
226
+ for (const f of checkSpecLifecycle(content, specPath)) {
227
+ findings.push({ severity: 'P1', code: f.code, message: f.message });
228
+ }
229
+ }
218
230
  function checkScenarios(content, findings) {
219
231
  if (!/^#{1,3}\s*\d*\.?\s*Scenarios/im.test(content)) {
220
232
  findings.push({
@@ -243,6 +255,7 @@ export function validateSpecDocument(content, options = {}) {
243
255
  const featureSlug = options.specPath ? featureSlugFromPath(options.specPath) : undefined;
244
256
  const requirementIds = checkRequirements(content, featureSlug, findings);
245
257
  checkStakes(content, findings);
258
+ checkLifecycle(content, options.specPath, findings);
246
259
  checkDoneCriteria(content, findings);
247
260
  checkGrounding(content, findings);
248
261
  checkPlaceholders(content, findings);
@@ -1 +1 @@
1
- {"version":3,"file":"validateSpecDocument.js","sourceRoot":"","sources":["../../../src/tools/spec/validateSpecDocument.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validateSpecDocument.js","sourceRoot":"","sources":["../../../src/tools/spec/validateSpecDocument.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@su-record/vibe",
3
- "version": "3.2.61",
3
+ "version": "3.2.62",
4
4
  "description": "AI Coding Framework for Claude Code — 7+ agents, 52 skills, multi-LLM orchestration",
5
5
  "type": "module",
6
6
  "main": "dist/cli/index.js",
@@ -31,7 +31,7 @@
31
31
  "validate:counts": "tsx scripts/validate-counts.ts",
32
32
  "lint": "oxlint",
33
33
  "lint:ratchet": "tsx scripts/lint-ratchet.ts",
34
- "verify:all": "pnpm lint && pnpm lint:ratchet && pnpm gen:skill-docs:check && pnpm sync:agent-models:check && pnpm validate:skill-invocation && pnpm validate:counts",
34
+ "verify:all": "pnpm lint && pnpm lint:ratchet && pnpm gen:skill-docs:check && pnpm sync:agent-models:check && pnpm validate:skill-invocation && pnpm validate:counts && pnpm gen:agents-md:check && pnpm validate:spec-lifecycle && pnpm validate:cache-surface",
35
35
  "test": "vitest run",
36
36
  "test:watch": "vitest",
37
37
  "test:coverage": "vitest run --coverage",
@@ -44,7 +44,11 @@
44
44
  "gen:plugin-hooks": "npx tsx scripts/gen-plugin-hooks.ts",
45
45
  "gen:plugin-hooks:check": "npx tsx scripts/gen-plugin-hooks.ts --check",
46
46
  "validate:plugin-tree": "npx tsx scripts/build-plugin.ts && git diff --exit-code --stat HEAD -- plugins/",
47
- "validate:mermaid": "npx tsx scripts/validate-mermaid.ts"
47
+ "validate:mermaid": "npx tsx scripts/validate-mermaid.ts",
48
+ "validate:spec-lifecycle": "npx tsx scripts/validate-spec-lifecycle.ts",
49
+ "validate:cache-surface": "npx tsx scripts/validate-cache-surface.ts",
50
+ "gen:agents-md": "npx tsx scripts/gen-agents-md.ts",
51
+ "gen:agents-md:check": "npx tsx scripts/gen-agents-md.ts --check"
48
52
  },
49
53
  "keywords": [
50
54
  "ai",
@@ -17,6 +17,10 @@ Cursor is not supported — do not generate or check Cursor-specific context fil
17
17
  **Source of truth:**
18
18
  - **`CLAUDE.md` is the content SSOT.** Always edit it first; `AGENTS.md` is a regenerated derivative.
19
19
  - Behavioral block: `skills/vibe.docs/templates/behavioral-principles.md` (4 Karpathy principles, wrapped in `<!-- VIBE-BEHAVIORAL:START/END -->` markers).
20
+ - **번역 규칙이 파일로 존재하면 그 파일이 SSOT다.** 프로젝트 루트에 `scripts/agents-md-rules.json` 이 있으면
21
+ 아래 4·5번 손 치환 목록을 쓰지 않고 `npm run gen:agents-md` 로 생성한다 — 규칙을 두 곳에 적으면
22
+ 그 순간 두 벌이 되고, 어느 쪽이 맞는지 판정할 수단이 사라진다. 검증은 `npm run gen:agents-md:check`.
23
+ (vibe 저장소 자신이 이 경로다. 규칙 파일이 없는 프로젝트는 4·5번을 그대로 적용한다.)
20
24
 
21
25
  **Procedure (applies to both creation and modification):**
22
26
 
@@ -0,0 +1,98 @@
1
+ # 프리픽스 캐시 표면
2
+
3
+ > 매 요청의 **앞부분에 항상 실리는 자산**이 무엇이고, 그것을 건드리면 무엇이 깨지는지.
4
+ > 검증: `npm run validate:cache-surface` (CI `test` job).
5
+
6
+ ## 왜 이 문서가 있는가
7
+
8
+ 컨텍스트 정책(85% 룰, `/new` 가 KV 프리픽스 캐시를 전량 폐기한다)은 `CLAUDE.md` 에 한 줄로
9
+ 적혀 있었다. 그 한 줄은 **어떤 파일을 고치면 캐시가 깨지는지**를 말해주지 않는다 — 그래서
10
+ "상시 로드 자산에 문단 하나 추가" 같은 변경이 리뷰에서 그냥 통과한다. 비용은 그 PR 이
11
+ 아니라 이후 모든 세션의 첫 토큰에서 나온다.
12
+
13
+ 여기서 고정하는 것은 두 가지다: **모델이 이 자산을 어떻게 경험하는가**(Model Experience)와
14
+ **이 자산을 바꾸면 캐시가 어떻게 되는가**(KV Cache effect). 자산이 늘거나 사라지면 게이트가 막는다.
15
+
16
+ ## 무효화의 규칙 (모든 표면 공통)
17
+
18
+ 프리픽스 캐시는 **접두사 일치**로 재사용된다 — 앞에서 한 바이트가 달라지면 그 뒤는 전부
19
+ 다시 계산된다. 따라서 비용은 "얼마나 고쳤나" 가 아니라 **"얼마나 앞에서 고쳤나"** 로 정해진다.
20
+ 표면을 아래 순서대로 읽는다: 위쪽일수록 한 글자의 대가가 크다.
21
+
22
+ <!-- surface: always-loaded-docs -->
23
+ ## 표면 1 — 루트 지침 문서 (`CLAUDE.md` · `AGENTS.md`)
24
+
25
+ - **Model Experience**: 세션 시작 시 **전문이** 시스템 프롬프트에 들어간다. 모델은 이 문서를
26
+ "검색해서 찾는 문서" 가 아니라 **이미 알고 있는 규칙**으로 경험한다 — 그래서 여기 적힌 것은
27
+ 탐색 없이 즉시 적용되고, 여기 없는 것은 존재를 모른다.
28
+ - **KV Cache effect**: 프리픽스의 가장 앞쪽에 가깝다. **한 줄만 고쳐도 그 아래 전체가 무효화**되고,
29
+ 이후 모든 세션이 첫 요청에서 전문을 다시 계산한다. 문단 추가는 상시 비용이므로, 상세는
30
+ `vibe/rules/**` 로 내리고 루트에는 **포인터만** 남긴다.
31
+
32
+ | 자산 | 비고 |
33
+ |---|---|
34
+ | `CLAUDE.md` | content SSOT |
35
+ | `AGENTS.md` | `CLAUDE.md` 에서 생성 (`npm run gen:agents-md`) |
36
+
37
+ <!-- surface: prefix-hooks -->
38
+ ## 표면 2 — 프리픽스에 stdout 을 얹는 훅
39
+
40
+ 훅 전체가 아니라 **턴 앞단에서 도는 두 이벤트**만 해당한다. `PreToolUse`/`PostToolUse`/`Stop` 은
41
+ 턴 중간에 끼어들어 이미 만들어진 프리픽스를 바꾸지 않는다.
42
+
43
+ - **Model Experience**: 이 훅의 stdout 은 모델에게 **사용자가 말하지 않은 지시문**으로 보인다.
44
+ 출처 표시가 없으면 사용자 발화와 구분되지 않으므로, 출력은 짧고 라벨이 붙어야 한다.
45
+ - **KV Cache effect**: `SessionStart` 출력은 세션 프리픽스에 **한 번** 박힌다 — 길이가 그대로
46
+ 상시 비용이다. `UserPromptSubmit` 출력은 **매 턴** 프리픽스 끝에 붙는다. 출력이 턴마다
47
+ 달라지면(시각·카운터·랜덤 순서) 그 지점부터 재사용이 끊기므로, **불변 텍스트를 앞에, 가변
48
+ 텍스트를 뒤에** 둔다.
49
+
50
+ | 이벤트 | 스크립트 |
51
+ |---|---|
52
+ | SessionStart | `hooks/scripts/session-start.js` |
53
+ | UserPromptSubmit | `hooks/scripts/prompt-dispatcher.js` |
54
+
55
+ <!-- surface: agents -->
56
+ ## 표면 3 — 에이전트 frontmatter `description`
57
+
58
+ - **Model Experience**: 에이전트 **본문은 로드되지 않는다.** 모델이 항상 보는 것은 이름과
59
+ `description` 한 줄뿐이고, 위임 여부는 전적으로 그 한 줄로 결정된다. description 이 비면
60
+ 에이전트는 설치돼 있어도 **없는 것과 같다** (실측: frontmatter 가 YAML 파싱에 실패해 통째로
61
+ 버려진 설치본이 11개 중 4개였다 — `CLAUDE.md` Gotchas).
62
+ - **KV Cache effect**: 에이전트 목록 전체가 프리픽스에 실린다. 에이전트를 **추가·삭제**하면
63
+ 목록이 바뀌어 그 뒤가 무효화되고, description 을 한 글자 고쳐도 같다. 본문을 아무리 늘려도
64
+ 프리픽스는 커지지 않는다 — **길이는 본문으로, 신호는 description 으로**.
65
+
66
+ | 에이전트 |
67
+ |---|
68
+ | `agents/architect.md` |
69
+ | `agents/build-error-resolver.md` |
70
+ | `agents/code-reviewer.md` |
71
+ | `agents/e2e-tester.md` |
72
+ | `agents/implementer.md` |
73
+ | `agents/security-reviewer.md` |
74
+ | `agents/tester.md` |
75
+ | `agents/event/event-ops.md` |
76
+ | `agents/event/event-planner.md` |
77
+ | `agents/ui/design-reviewer.md` |
78
+ | `agents/ui/design-system-gen.md` |
79
+
80
+ <!-- surface: skills -->
81
+ ## 표면 4 — 스킬 frontmatter `description` (집계)
82
+
83
+ - **Model Experience**: 에이전트와 같다 — **본문은 호출될 때만** 로드되고, 상시 실리는 것은
84
+ `name` + `description` 이다. 따라서 스킬 본문의 길이는 라우팅 정확도에 기여하지 않는다.
85
+ 라우팅은 description 이 한다.
86
+ - **KV Cache effect**: 스킬 **개수**가 프리픽스 길이를 좌우한다. 스킬을 하나 추가하면 목록이
87
+ 길어져 그 뒤가 무효화된다. 그래서 스택·capability 로 갈라 설치하는 것이다 — 아무 프로젝트에나
88
+ 실릴 이유가 없는 스킬은 내려둔다 (`GLOBAL_SKILLS_*` / `STACK_TO_SKILLS` / `CAPABILITY_SKILLS`).
89
+ - **개수 검증은 여기서 하지 않는다** — `npm run validate:counts` 가 이미 그 사실의 집이다.
90
+ 같은 사실을 두 곳에서 세면 그 순간 두 벌이 된다.
91
+
92
+ ## 새 표면을 추가할 때
93
+
94
+ 1. 위 형식으로 절을 만든다 — `<!-- surface: {id} -->` 마커 + `Model Experience` + `KV Cache effect`.
95
+ 2. 자산을 나열한다면 표의 백틱 경로가 실물과 **정확히 일치**해야 한다. 게이트가 양방향으로 본다:
96
+ 문서에 없는 실물도, 실물이 없는 문서 항목도 실패다.
97
+ 3. 집계로 충분하면 나열하지 않는다. 나열은 그 자체가 유지보수 비용이고, 다른 게이트가 이미
98
+ 세고 있다면 여기서 또 세지 않는다.
@@ -1,7 +1,8 @@
1
1
  # SPEC: {Feature Name}
2
2
 
3
3
  - **Created**: {YYYY-MM-DD}
4
- - **Status**: DRAFT | APPROVED
4
+ - **Status**: DRAFT | APPROVED | VERIFIED | SUPERSEDED | REJECTED (닫힌 집합 — `npm run validate:spec-lifecycle`)
5
+ - **Class**: feature | bug-fix | simplification | architecture | process | testing
5
6
  - **Stakes**: demo | prototype | production — {판정 근거 1구} (SSOT: vibe/rules/loop-contract.md)
6
7
  - **Tech Stack**: {Project tech stack summary}
7
8
 
@@ -153,3 +154,17 @@ Response: 201 {...}
153
154
  - Verification writes `.vibe/runs/{run-id}/evidence.json`; only deterministic Judge results can complete the loop.
154
155
  - Model Judge findings are advisory-only. Human Taste is release-only.
155
156
  - Gate = all Done Criteria pass (exit codes / observed behavior) — loop continues until gates pass, stuck, or max iterations.
157
+
158
+ ---
159
+
160
+ ## Anchors
161
+
162
+ > **Class 가 feature · bug-fix · architecture 이고 Status 를 VERIFIED 로 올릴 때 필수.**
163
+ > 그 외에는 이 절을 통째로 지운다 — 고정할 코드 경로가 없는 SPEC 에 억지로 요구하면 통과 의식이 된다.
164
+
165
+ 이 SPEC 이 안착한 경로를 나열한다. 여기 적힌 경로가 사라지면 `npm run validate:spec-lifecycle`
166
+ 이 실패한다 — 그것이 "코드가 움직였는데 SPEC 이 따라오지 않았다" 를 잡는 유일한 신호다.
167
+ lifecycle 표기만으로는 못 잡는다: 문서는 Status 를 바꾸지 않은 채 늙기 때문이다.
168
+
169
+ - `{src/auth/session.ts}`
170
+ - `{src/auth/session.test.ts}`