@uzysjung/agent-harness 26.136.0 → 26.139.0

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.
@@ -14,7 +14,7 @@ import {
14
14
  init_esm_shims,
15
15
  residentCost,
16
16
  resolveBundleRoot
17
- } from "./chunk-WCH7E3G6.js";
17
+ } from "./chunk-4VDIAMSM.js";
18
18
 
19
19
  // src/trust-tier-drift.ts
20
20
  init_esm_shims();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uzysjung/agent-harness",
3
- "version": "26.136.0",
3
+ "version": "26.139.0",
4
4
  "description": "Curate vetted AI-coding skills & plugins by your tech stack — install only what you need, across Claude Code, Codex, OpenCode & Antigravity",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -60,6 +60,13 @@ If you genuinely think a convention is harmful, surface it. Don't fork silently.
60
60
  "Tests pass" is wrong if any were skipped.
61
61
  Default to surfacing uncertainty, not hiding it.
62
62
 
63
+ ## Delegate the Building, Keep the Deciding
64
+ When work turns from deciding to building, delegate it — the main thread holds scope and sequencing,
65
+ not the edits. Dev tracks ship an `implementer` agent; without it use a general-purpose subagent on
66
+ the same contract: **done = a test that fails without the change**. Split before handing off — one
67
+ item per independently testable change, naming its files and its constraint. Review and verification
68
+ belong to a lane other than the one that wrote the code.
69
+
63
70
  ## Anti-Patterns (Forbidden)
64
71
  "feels kind of weak intuitively" / "probably won't be used" → speculation
65
72
  "it's an advanced feature, so low value" → assertion with no criteria
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: implementer
3
+ description: Implementation lane — writes code for a scoped change and closes it with a test that fails without the change. Use whenever work moves from deciding to building: a feature, a bug fix, a refactor, a migration step. Prefer this over implementing on the main thread. Korean triggers: 구현해 · 고쳐 · 만들어 · 적용해 · 리팩터 · 위임.
4
+ tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
5
+ model: opus
6
+ origin: self-authored
7
+ ---
8
+
9
+ # Implementer — 구현 레인
10
+
11
+ 당신은 **코드를 쓰는** 레인이다. 검토하거나 조사하는 것이 아니라 변경을 만들어 끝낸다.
12
+
13
+ ## 종료 조건 (하나뿐)
14
+
15
+ **이 변경을 무는 테스트가 green.** 그 테스트는 변경이 없으면 실패해야 한다 — 통과만 하고 아무것도
16
+ 안 보는 테스트는 종료 조건을 충족하지 않는다. 확신이 안 서면 변경을 잠시 되돌려 테스트가 정말
17
+ 빨간불이 되는지 보고, 다시 적용해 초록불을 확인하라.
18
+
19
+ 테스트를 못 붙인 채로 끝내야 한다면 **완료라고 보고하지 않는다.** 무엇을 못 닫았는지, 왜 못
20
+ 닫았는지("무엇이 풀리면 되는지"까지)를 적고 넘긴다. 이게 이 레인의 존재 이유다 — 구현과 검증이
21
+ 갈리면 검증은 늘 나중으로 밀리고, 나중은 오지 않는다.
22
+
23
+ ## 작업 방식
24
+
25
+ - **편집 직후에 확인한다.** 전체 스위트를 마지막에 한 번 돌리는 대신, 방금 고친 것을 무는
26
+ 파일을 지정해 바로 실행하라(`npx vitest run tests/<관련>.test.ts`, `pytest tests/test_<관련>.py`).
27
+ 실패를 편집에서 멀리 떼어놓을수록 원인을 좁히는 비용이 커진다.
28
+ - **범위 안에 머문다.** 인접 코드를 "개선"하지 않는다. 손대야만 하는 것만 손댄다.
29
+ - **읽고 나서 쓴다.** 바꿀 심볼의 호출부와 export 를 먼저 본다.
30
+ - 기존 코드의 관용구·명명·주석 밀도를 따른다. 취향보다 일관성이 우선이다.
31
+
32
+ ## 멈춰야 할 때
33
+
34
+ 다음이면 **구현을 계속하지 말고 멈춰서 보고**하라. 추측으로 밀고 나가면 되돌리는 비용이 훨씬 크다.
35
+
36
+ - 요구가 두 갈래로 읽히는데 어느 쪽이냐에 따라 결과물이 달라진다
37
+ - 시키지 않은 파일·모듈을 고쳐야만 문제가 풀린다
38
+ - 테스트가 요구와 모순된다 (테스트를 고치는 것이 답일 수도, 요구가 틀렸을 수도 있다)
39
+ - 되돌리기 어려운 것에 손이 닿는다 — 마이그레이션, 스키마, 배포 설정, 외부로 나가는 호출
40
+
41
+ ## 보고 형식
42
+
43
+ ```
44
+ 변경: <무엇을 왜>
45
+ 파일: <경로 목록>
46
+ 테스트: <명령> → <결과>. 이 테스트는 변경 전 <어떻게> 실패한다.
47
+ 미완: <없으면 "없음". 있으면 무엇이 풀리면 닫히는지>
48
+ ```
@@ -7,7 +7,8 @@ BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
7
7
 
8
8
  # 1. git pull (branch가 있고 detached HEAD가 아닐 때)
9
9
  if [ -n "$BRANCH" ] && [ "$BRANCH" != "HEAD" ]; then
10
- git pull --rebase 2>/dev/null || true
10
+ # stdout 버린다 여기 출력이 아래 JSON 앞에 붙으면 계약이 깨진다 (실측 21%).
11
+ git pull --rebase >/dev/null 2>&1 || true
11
12
  fi
12
13
 
13
14
  # 2. SPEC 존재 여부 확인
@@ -47,15 +48,27 @@ if [ "${ORPHANS:-0}" -gt 0 ]; then
47
48
  fi
48
49
 
49
50
  # 5. 세션 컨텍스트 출력
51
+ #
52
+ # **스키마가 계약이다.** CLI 가 읽는 필드가 아니면 **유효 JSON 인 채로 조용히 버려진다** —
53
+ # 훅은 돌고 exit 0 이고 로그에도 남지만 모델은 아무것도 못 본다. 실패가 아무 증상을 안 내므로
54
+ # 사람이 알아채지 못한다. SessionStart 가 읽는 것은
55
+ # `hookSpecificOutput.{additionalContext|initialUserMessage}` 다.
56
+ # 조용히 버려지는 출력은 컨텍스트 비용이 0 인 대신 기능도 0 이다.
50
57
  if [ "$SPEC_EXISTS" = "true" ]; then
51
58
  MSG="Session started. Branch: ${BRANCH:-detached}. SPEC exists — read docs/SPEC.md first (Persistent Anchor). Check Change Log and current Phase before starting work.${COMPACT_WARNING}${ORPHAN_NOTE}"
52
59
  else
53
60
  MSG="Session started. Branch: ${BRANCH:-detached}. No SPEC found.${COMPACT_WARNING}${ORPHAN_NOTE}"
54
61
  fi
55
62
 
63
+ # JSON 문자열 이스케이프 — 백슬래시 먼저, 그 다음 따옴표(순서를 바꾸면 이중 이스케이프된다).
64
+ # `jq` 를 쓰지 않는 이유: 훅은 jq 미설치 환경에서도 돌아야 한다 (cli-development.md).
65
+ ESCAPED=$(printf '%s' "$MSG" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g')
66
+
56
67
  cat <<EOF
57
68
  {
58
- "priority": "INFO",
59
- "message": "$MSG"
69
+ "hookSpecificOutput": {
70
+ "hookEventName": "SessionStart",
71
+ "additionalContext": "$ESCAPED"
72
+ }
60
73
  }
61
74
  EOF
@@ -17,8 +17,9 @@ SSOT — 없으면 갭 판정 기준 자체가 없다.
17
17
  | (예: 문서/위키) | (예: Confluence) | Playwright capture | `~/.<ref>-audit-profile` |
18
18
  | (예: 리포트) | (예: 내부 확보한 정적 HTML/PDF) | 정적 자료 | `docs/raw/` |
19
19
 
20
- - 실측 수단(영속 profile, reference SaaS 조작 제한 등) **playwright-launch.md 가 SSOT** —
21
- rule 수단을 재규정하지 않는다.
20
+ - 실측 수단은 둘로 나뉜다 — **금지**(활성 Chrome attach · 일회성 context 등) `playwright-launch`
21
+ 룰이, **절차**(영속 profile launcher · capture 형태)는 `ui-visual-review` 스킬이 SSOT.
22
+ 본 rule 은 어느 쪽도 재규정하지 않는다.
22
23
 
23
24
  ## 반복 루프 (capture → 핵심 기능 → 완결성 → 발전)
24
25
 
@@ -1,41 +1,32 @@
1
1
  # Change Management
2
2
 
3
- ## Change Request (CR) Classification
3
+ ## Change Request (CR) 분류
4
4
 
5
- 구현 중 SPEC/PRD 변경이 필요한 경우:
5
+ 구현 중 SPEC/PRD 변경이 필요해지면 셋 중 하나로 분류한다:
6
6
 
7
7
  | 유형 | 기준 | 처리 |
8
8
  |------|------|------|
9
- | **Clarification** | 이미 합의된 내용의 구체화 | 에이전트가 즉시 반영 + Change Log 기록 |
10
- | **Minor** | 현재 Phase 내부에 국한 | 에이전트가 제안 → **인간 승인** → Change Log |
11
- | **Major** | AC/Phase/Non-Goals/DO NOT CHANGE 영향 | **인간 결정 필수** → Change Log + 영향 분석 |
9
+ | **Clarification** | 이미 합의된 내용의 구체화 | 즉시 반영 + Change Log |
10
+ | **Minor** | 현재 Phase 내부에 국한 | 제안 → **인간 승인** → Change Log |
11
+ | **Major** | AC 의 Pass/Fail · 다른 Phase 의 입출력 · Non-Goals 경계 · DO NOT CHANGE **하나라도** 건드림 | **인간 결정 필수** → Change Log + 영향 분석 |
12
12
 
13
- ## Classification Rules
13
+ ## DO NOT CHANGE
14
14
 
15
- ```
16
- IF AC의 Pass/Fail에 영향 → Major
17
- IF 다른 Phase의 입력/산출물에 영향 → Major
18
- IF DO NOT CHANGE 영역에 영향 → Major
19
- IF Non-Goals 경계에 영향 → Major
20
- IF 현재 Phase 내부에 국한 → Minor
21
- IF 합의 내용의 구체화 → Clarification
22
- ```
23
-
24
- ## DO NOT CHANGE Protection
25
-
26
- - SPEC/PRD의 DO NOT CHANGE 영역은 **절대 수정 금지**.
27
- - 수정이 불가피한 경우: Major CR 작성 → 인간 결정 대기.
28
- - 안정적으로 보이는 영역도 수정 전 확인: "이 부분도 수정 범위입니까?"
15
+ SPEC/PRD 의 DO NOT CHANGE 영역은 **수정 금지**다. 불가피하면 Major CR 을 쓰고 인간 결정을
16
+ 기다린다. 안정적으로 보이는 영역도 손대기 전에 묻는다 — "이 부분도 수정 범위입니까?"
29
17
 
30
18
  ## Decision Log (ADR)
31
19
 
32
- 구현 중 SPEC명시되지 않은 의사결정은 `docs/decisions/` 에 ADR로 기록:
20
+ SPEC없던 의사결정은 `docs/decisions/` 에 기록한다.
21
+
22
+ - **대상**: 아키텍처 변경 · 외부 의존성 도입/제거 · 데이터 모델 변경 · 보안 정책 · breaking API
23
+ - **비대상**: 한 함수의 구현 디테일 · 임시 워크어라운드 · 명백한 버그 fix
33
24
 
34
25
  ```markdown
35
26
  # ADR-NNN: [결정 제목]
36
27
  - Status: Proposed | Accepted | Superseded | Deprecated
37
28
  - Date: YYYY-MM-DD
38
- - PR: #123 (제안/승인 PR 링크)
29
+ - PR: #123
39
30
  - Supersedes: ADR-MMM (있으면)
40
31
  - Context: [왜 결정이 필요했는가]
41
32
  - Decision: [무엇을 결정했는가]
@@ -43,38 +34,26 @@ IF 합의 내용의 구체화 → Clarification
43
34
  - Consequences: [이 결정의 영향]
44
35
  ```
45
36
 
46
- ### ADR Status 흐름
47
-
48
- ```
49
- Proposed Accepted (Superseded by ADR-N | Deprecated)
50
-
51
- (Rejected 별도 ADR 만듦, PR comment에 사유 기록)
52
- ```
53
-
54
- | Status | 의미 | 다음 가능 transition |
55
- |--------|------|-------------------|
56
- | **Proposed** | PR 작성 + 검토 중. 아직 적용 X | Accepted, (Rejected) |
57
- | **Accepted** | 머지됨. 본 결정에 따라 코드/문서 작성 | Superseded, Deprecated |
58
- | **Superseded** | 다른 ADR이 본 결정을 대체 | (terminal — `Supersedes:` 필드로 link) |
59
- | **Deprecated** | 더 이상 유효하지 않으나 대체 ADR 없음 | (terminal — 사유 PR/section 기록) |
60
-
61
- ### 채택 프로세스
62
-
63
- 1. **Proposed**: 의사결정 필요 시 ADR 초안 PR 생성. Status: Proposed.
64
- 2. **검토**: PR review에서 Alternatives + Consequences 검증.
65
- 3. **Accepted**: 머지 직전 Status: Accepted, PR 번호 채움.
66
- 4. **변경**: 새 ADR로 Supersedes/Deprecates 명시. 기존 ADR Status 갱신.
67
-
68
- ### 어떤 결정이 ADR 대상인가
37
+ | Status | 의미 | 다음 |
38
+ |--------|------|------|
39
+ | **Proposed** | 초안 PR 검토 중, 아직 미적용 | Accepted / 기각 |
40
+ | **Accepted** | 머지됨. 결정에 따라 코드·문서를 쓴다 | Superseded / Deprecated |
41
+ | **Superseded** | 다른 ADR 이 대체 (terminal) | — |
42
+ | **Deprecated** | 무효이나 대체 ADR 없음 (terminal — **사유를 PR/본문에 남긴다**) | — |
69
43
 
70
- - **대상**: 아키텍처 변경, 외부 의존성 도입/제거, 데이터 모델 변경, 보안 정책, breaking API
71
- - **비대상**: 함수의 구현 디테일, 임시 워크어라운드, 명백한 버그 fix
44
+ PR review 에서 `Alternatives` `Consequences` 검증하고, 머지 직전에 Status
45
+ Accepted 바꾸고 PR 번호를 채운다. 기각은 **별도 ADR 만들지 않고**
46
+ PR comment 에 사유를 남긴다. 결정을 바꿀 때는 새 ADR 에 `Supersedes:` 를 쓰고 기존 ADR 의 Status
47
+ 도 함께 갱신한다 — 한쪽만 고치면 어느 것이 현행인지 알 수 없다.
72
48
 
73
- ## Savepoint Protocol
49
+ ## Savepoint
74
50
 
75
- Major CR 적용 전, 또는 주요 변경점에서 savepoint 생성:
51
+ Major CR 적용 전, 또는 되돌리기 어려운 변경 직전에 커밋으로 지점을 남긴다:
76
52
 
77
53
  ```bash
78
- git add -A && git commit -m "chore: savepoint before [변경 설명]"
54
+ git commit -a -m "chore: savepoint before [변경 설명]"
79
55
  ```
80
56
 
57
+ **`git add -A` 를 쓰지 않는다.** 설치 직후처럼 `.gitignore` 가 아직 시크릿을 덮지 못한 상태에서는
58
+ 그 한 줄이 `.env` 를 그대로 커밋한다 — 같은 하네스의 `git-policy` 가 금지하는 것을 savepoint 가
59
+ 수행하게 된다. 추적 중인 변경만 담고, 새 파일이 꼭 필요하면 경로를 하나씩 지정해 추가한다.
@@ -1,39 +1,39 @@
1
1
  # Document Governance
2
2
 
3
- 문서 작성 + 작업 완료 시 추적 동기화 규칙. 프로젝트 문서가 "거짓 상태"가 되는 것을 막는 SSOT 규약 —
4
- 실서비스 운영에서 검증된 관행의 일반화.
3
+ 프로젝트 문서가 "거짓 상태"가 되는 것을 막는 SSOT 규약.
5
4
 
6
5
  ## SSOT 위계 (한 사실은 한 곳)
7
6
 
8
7
  | 문서 | 역할 | 갱신 시점 |
9
8
  |------|------|----------|
10
- | `NORTH_STAR.md` | 왜·어디로 (비전 · 북극성 지표 · Non-Goals · 의사결정 휴리스틱) | 방향 전환 시만 |
11
- | `SPEC.md` (+ `specs/`) | 무엇 (제품/기능 스펙 — 모듈이 커지면 파일 분리) | 기능 정의/변경 |
9
+ | `NORTH_STAR.md` | 왜·어디로 (비전 · 지표 · Non-Goals) | 방향 전환 시만 |
10
+ | `SPEC.md` (+ `specs/`) | 무엇 (제품/기능 스펙) | 기능 정의/변경 |
12
11
  | `PRD.md` | 문제 · 솔루션 · 요구사항 | 제품 방향 변경 |
13
- | `TODO.md` (또는 `tasks/todo.md`) | 다음 할 일 · 진행/완료 추적 | 작업 시작/완료 |
12
+ | `TODO.md` (또는 `tasks/todo.md`·`docs/todo.md`) | 다음 할 일 · 진행/완료 추적 | 작업 시작/완료 |
14
13
  | `README.md` | 진입점 · 현재 상태 (shipped/stack/배포) | 현재 상태 변동 |
15
- | `docs/decisions/` | 아키텍처/의존성/데이터모델/보안 결정 (ADR) | change-management.md 분류 따름 |
14
+ | `docs/decisions/` | ADR (아키텍처·의존성·데이터모델·보안) | change-management 분류 따름 |
16
15
 
17
- - 파일 위치는 프로젝트 레이아웃을 따른다 (루트 또는 `docs/`) **역할·위계·동기화 의무는 불변**.
18
- - **위계 = 충돌 시 상위 우선.** NORTH_STAR 와 SPEC 이 모순되면 NORTH_STAR 가 이긴다.
19
- - **같은 사실을 두 곳에 쓰지 않는다.** 한 곳(SSOT)에 두고 나머지는 링크로 가리킨다. 중복 서술 = drift 의 씨앗.
16
+ - 파일 위치는 프로젝트 레이아웃을 따르되(루트 또는 `docs/`) **역할·위계·동기화 의무는 불변**.
17
+ - **충돌 시 상위 우선** NORTH_STAR 와 SPEC 이 모순되면 NORTH_STAR 가 이긴다.
18
+ - **같은 사실을 두 곳에 쓰지 않는다.** 한 곳에 두고 나머지는 링크로 가리킨다. 중복 서술 = drift 의 씨앗.
20
19
 
21
20
  ## 무엇을 언제 쓰나
22
21
 
23
- - 신규 기능 → **먼저 SPEC 등재, 다음 구현** (spec-first). 대화에서 "추가하자/넣자" = 우선 문서 작업이지 구현 착수가 아니다.
24
- - 아키텍처 / 외부 의존성 / 데이터 모델 / 보안 / breaking API 결정 → **ADR** (`docs/decisions/`, 템플릿은 change-management.md).
22
+ - 신규 기능 → **SPEC 등재가 먼저, 구현은 다음**(spec-first). "추가하자" 문서 작업 지시이지
23
+ 구현 착수 신호가 아니다.
24
+ - 아키텍처 · 외부 의존성 · 데이터 모델 · 보안 · breaking API → **ADR** (템플릿은 change-management).
25
25
  - 진행/완료 추적 → TODO. 현재 상태 변동 → README.
26
26
 
27
27
  ## 작업 완료 처리 (merge = 코드 + 추적 동기화) — 핵심 의무
28
28
 
29
- PR 머지로 작업이 끝난 게 아니다. **머지 직후 같은 작업 단위로**:
29
+ 머지 직후 **같은 작업 단위로** 처리한다:
30
30
 
31
- 1. TODO 해당 항목 → `[x] (✅ #PR번호)`.
31
+ 1. TODO 항목 → `[x] (✅ #PR번호)`.
32
32
  2. AC / Phase / Non-Goals / DO NOT CHANGE 에 영향 시 → SPEC Change Log.
33
- 3. 현재 상태(shipped / 배포) 변동 시 → README §현재 상태.
33
+ 3. shipped/배포 상태 변동 시 → README.
34
34
 
35
- 빠지면 추적 SSOT 가 거짓 상태가 되고, **다음 세션이 완료분을 backlog 로 오인해 중복 작업하거나
36
- 미완분을 완료로 오인해 건너뛴다.** 금지.
35
+ 빠뜨리면 추적 SSOT 가 거짓이 되고, **다음 세션이 완료분을 backlog 로 오인해 중복 작업하거나
36
+ 미완분을 완료로 오인해 건너뛴다.**
37
37
 
38
38
  ## 착수 전 baseline 대조 (Pre-flight)
39
39
 
@@ -62,24 +62,20 @@ PR 머지로 작업이 끝난 게 아니다. **머지 직후 같은 작업 단
62
62
 
63
63
  ## 현행 vs archive
64
64
 
65
- - **현행 SSOT 만 루트/`docs/` 에**: 위 위계 문서 + `specs/` + `decisions/`.
66
- - **히스토리는 `docs/archive/`**격리 ( 버전, 폐기 sub-spec, 완료된 리서치/audit 산출물) —
67
- 지도 파일(`archive/README.md`) 하나로 찾을 있게 한다. 현행 문서에 히스토리가 쌓이면
68
- "현재가 무엇인지"를 읽는 비용이 히스토리에 비례해 커진다.
69
- - 변경 이력이 길어진 문서는 이력만 archive 로 빼고 본문에는 최근분만 남긴다.
65
+ 현행 SSOT 만 루트/`docs/` 둔다 — 위 위계 문서 + `specs/` + `decisions/`. 히스토리(옛 버전, 폐기
66
+ sub-spec, 완료된 audit 산출물)는 `docs/archive/`격리하고 지도 파일(`archive/README.md`) 하나로 찾게 한다. 현행
67
+ 문서에 히스토리가 쌓이면 "지금 무엇이 맞는지" 읽는 비용이 히스토리에 비례해 커진다. 이력이 길어진
68
+ 문서는 이력만 빼고 본문엔 최근분만 남긴다.
70
69
 
71
70
  ## 작성 원칙
72
71
 
73
- - **why 중심** — what 은 코드/diff 가 보여준다.
74
- - SPEC 800줄을 넘으면 기능별 분리 (`spec-scaling` 스킬 참조).
75
- - **추정/임의 단정 금지** — "직관상 별로", "일반적으로 필요", "내 경험상"은 출처 없는 일반화.
76
- 측정/스펙 참조/재현 증거로 입증한다.
72
+ **why 중심으로 쓴다** — what 은 코드와 diff 가 보여준다. SPEC 이 800줄을 넘으면 기능별로 분리한다
73
+ (`spec-scaling` 스킬). 추정·임의 단정 금지는 `CLAUDE.md` Anti-Patterns 소유한다.
77
74
 
78
75
  ## 검증 게이트
79
76
 
80
- dev 트랙 설치 시 `.claude/hooks/spec-drift-check.sh` 가 SPEC/TODO 의 미완 잔존·Status 불일치를
81
- 검출한다 — verify 단계는 경고, ship 단계는 차단.
77
+ `.claude/hooks/spec-drift-check.sh`(전 트랙 설치)가 SPEC/TODO 의 미완 잔존·Status 불일치를
78
+ 검출한다 — verify 경고, ship 차단.
82
79
 
83
- **한계를 알고 써라**: 탐지 경로에 없는 문서 레이아웃은 게이트가 못 본다. 그 경우 본 규약은
84
- 프로즈로만 작동한다 — 안 지켜져도 아무도 안 막는다. 규약만으로 지켜지는 것이 확인되면
85
- 게이트 쪽을 넓혀라, 프로즈를 늘리지 말고.
80
+ **한계를 알고 써라**: 탐지 경로 밖의 문서 레이아웃은 게이트가 못 본다. 그 경우 본 규약은 프로즈로만
81
+ 작동한다 — 안 지켜져도 아무도 안 막는다. 그게 확인되면 **게이트를 넓혀라, 프로즈를 늘리지 말고.**
@@ -19,7 +19,7 @@
19
19
  **한 경로의 증거를 다른 경로에 전용하지 않는다** (플래그가 통과했다고 위저드도 된다고 쓰는 것).
20
20
 
21
21
  실패한 명령은 산출물을 내지 않는다 — **빈 결과는 부재의 증거가 아니다**. 확인 명령에 `2>/dev/null`
22
- 을 붙이면 실패와 "이상 없음"이 구분되지 않는다 (`cli-development.md` 참조).
22
+ 을 붙이면 실패와 "이상 없음"이 구분되지 않는다 (설치돼 있으면 `cli-development.md` 참조).
23
23
 
24
24
  ## 완료 보고 양식
25
25
 
@@ -64,4 +64,4 @@
64
64
 
65
65
  1. 즉시 정정 보고 — 어느 보고가 어떻게 거짓이었는지 명시한다. 조용히 고치지 않는다.
66
66
  2. 수정을 최우선으로 처리한다.
67
- 3. 재발이면 `recurrence-prevention` 사다리를 탄다 (기록 → 룰 → 구조 게이트).
67
+ 3. 재발이면 기록 → 룰 → 구조 게이트 사다리를 탄다 (`recurrence-prevention` 스킬이 없으면 직접).
@@ -1,66 +1,24 @@
1
1
  # Playwright Launch
2
2
 
3
- 브라우저를 띄워 **사용자가 직접 사용**하는 모든 상황 (E2E 수동 확인, UX 비교, fidelity audit, OAuth 1회 로그인 등) 에 적용. 핵심 원칙: **영속 profile + Chrome for Testing 별도 binary + 자동화 0 patten**. MCP 일회성 context = 휘발 → 입력 버벅임. 정리하고 영속 profile로 재기동.
3
+ 브라우저를 띄워 **사용자가 직접 쓰는** 모든 상황(E2E 수동 확인, UX 비교, fidelity audit, OAuth
4
+ 로그인)에 적용. 여기 남긴 것은 **위반을 막는 금지문뿐**이다 — 절차·스크립트 골격·사용 패턴은
5
+ `ui-visual-review` 스킬이 SSOT. 금지문만 상주하는 이유는 위반이 **작업을 시작한 뒤에** 일어나서,
6
+ 스킬 발화를 기다리면 이미 늦기 때문이다.
4
7
 
5
8
  ## 절대 금지
6
9
 
7
- - `mcp__chrome-devtools__*` 로 **사용자 활성 Chrome** attach (입력 latency)
8
- - Playwright MCP (`mcp__plugin_playwright__*`) **일회성** browser context — 영속 profile dir 없음 → IndexedDB / cookie / localStorage / SW 휘발 → 매 launch 마다 재로그인 + 버벅임
9
- - `browser.newContext()` 일회성 패턴
10
- - **사용자 OAuth 입력 시점에 자동화 process 동시 실행** (CDP latency)
11
- - reference SaaS (Linear / Notion / Jira 등) 측 `page.goto(URL)` 또는 `page.reload()` — sidebar click only
10
+ - **사용자 활성 Chrome attach** (`mcp__chrome-devtools__*`) — 입력 latency.
11
+ - **일회성 browser context** (Playwright MCP, `browser.newContext()`) — 영속 profile dir 없어
12
+ cookie/IndexedDB/SW 휘발한다. 매 launch 재로그인 + 버벅임.
13
+ - **사용자가 키를 입력하는 동안 자동화 process 동시 실행** (CDP latency).
14
+ - **reference SaaS(Linear/Notion/Jira 등) 측 `page.goto()`·`page.reload()`** — sidebar click only.
12
15
 
13
- ## 필수 패턴
16
+ ## 필수
14
17
 
15
- 1. **영속 profile dir** 프로젝트별 분리, iter 재사용. cookie + IndexedDB + Service Worker 영구 보존.
16
- - 예: `~/.<project>-dev-audit-profile`, `~/.<reference>-audit-profile`, `${PROJECT_PROFILE_DIR}` env var
17
- 2. **Chrome for Testing 별도 binary** — `npx playwright install chromium` 으로 받은 `~/Library/Caches/ms-playwright/chromium-*/chrome-mac-arm64/Google Chrome for Testing.app`. 사용자 일반 Chrome 과 완전 분리.
18
- 3. **사용자 키 입력 시 자동화 layer 0** — main session 은 chromium 창 띄우기만. **사용자가 키보드 직접 입력**. 자동화 capture/검증은 **별도 process** (`node scripts/<launch>.mjs`) 에서 입력 끝난 후.
19
- 4. **자체 구현 측 = dev bypass auth** — Google OAuth 등 webdriver 차단 회피. 예: `POST /api/v1/_dev/test-login` 으로 JWT cookie 발급.
18
+ 영속 profile dir + Chrome for Testing 별도 binary. 사용자 입력 구간에는 자동화 layer 0 capture 는
19
+ 입력이 끝난 별도 process 에서. 구현 형태는 `ui-visual-review` 스킬 참조.
20
20
 
21
- ## 표준 launch 스크립트 골격
21
+ ## 위반
22
22
 
23
- ```js
24
- import { chromium } from 'playwright';
25
-
26
- const PROFILE_DIR = process.env.PROJECT_PROFILE_DIR || `${process.env.HOME}/.<project>-audit-profile`;
27
- const TARGET = process.env.PROJECT_URL || 'http://localhost:<port>';
28
- const TEST_KEY = process.env.E2E_TEST_KEY || 'e2e-dev-key';
29
-
30
- const ctx = await chromium.launchPersistentContext(PROFILE_DIR, {
31
- headless: false,
32
- viewport: { width: 1440, height: 900 },
33
- args: ['--disable-blink-features=AutomationControlled', '--no-first-run', '--no-default-browser-check'],
34
- });
35
- const page = ctx.pages()[0] ?? (await ctx.newPage());
36
-
37
- // (선택) dev bypass auth — same-origin cookie 자동 저장
38
- await page.goto(`${TARGET}/`, { waitUntil: 'domcontentloaded' });
39
- await ctx.request.post(`${TARGET}/api/v1/_dev/test-login`, {
40
- data: { key: TEST_KEY, email: 'e2e@example.local' },
41
- });
42
- await page.reload({ waitUntil: 'networkidle' });
43
-
44
- // 창 떠 있게 두고 자동화 detach — 사용자가 직접 사용.
45
- // ctx.close() 호출 금지.
46
- ```
47
-
48
- ## 사용 패턴
49
-
50
- ### A. 사용자가 직접 보고 싶을 때 (most common)
51
-
52
- 1. `pkill -f "playwright|Chrome for Testing"` 기존 launcher 정리 (선택)
53
- 2. `node scripts/<project>-launch.mjs` 실행 (chromium 창 살아있어야 함, 백그라운드 X)
54
- 3. 사용자에게 "URL: localhost:<port>, 로그인됨, 직접 사용하세요" 보고
55
- 4. **MCP playwright 로 추가 navigation/evaluate 금지** — 사용자 입력과 충돌
56
-
57
- ### B. fidelity 비교 / audit 자동 capture
58
-
59
- 1. `node scripts/<audit>.mjs` — launchPersistentContext + 비교 시나리오 자동 click
60
- 2. 산출물 = `docs/research/<ref>_audit_<sprint>/iter_<N>/`
61
-
62
- ## 위반 시 즉시 조치
63
-
64
- 1. 일회성 Playwright MCP context 사용 발견 → 닫고 영속 profile launcher 로 재기동
65
- 2. `chrome-devtools` MCP attach 발견 → 즉시 detach + Playwright 영속 패턴 전환
66
- 3. 사용자가 "버벅인다" / "느리다" 호소 → 본 rule 위반 확인 1순위
23
+ 일회성 context 나 devtools attach 를 발견하면 즉시 닫고 영속 profile launcher 로 재기동한다.
24
+ 사용자가 "버벅인다"고 하면 rule 위반 확인이 1순위다.
@@ -84,10 +84,17 @@ EOF
84
84
  bash .claude/skills/codex-consult/scripts/codex-ask.sh -g ./scratch "PROMPT"
85
85
  # attach input image(s) (screenshot to describe, reference for a redraw):
86
86
  bash .claude/skills/codex-consult/scripts/codex-ask.sh -i shot.png "PROMPT"
87
- # override model (default = codex's configured default):
88
- bash .claude/skills/codex-consult/scripts/codex-ask.sh -m gpt-5.2-codex "PROMPT"
87
+ # override model — only when the USER names one (default = codex's own default):
88
+ bash .claude/skills/codex-consult/scripts/codex-ask.sh -m MODEL_ID "PROMPT"
89
89
  ```
90
90
 
91
+ **Leave `-m` off by default.** With no `-m` the wrapper passes no model flag at
92
+ all, so codex uses whatever the user's own config selects — that tracks model
93
+ upgrades for free, while a model id written down here (or remembered from a
94
+ previous session) goes stale and pins the user to a retired model. Codex has no
95
+ `models` subcommand to enumerate against; the model codex actually used is
96
+ printed on **stderr** (`model: …`) — read it back if it matters.
97
+
91
98
  Output contract: **stdout** carries only codex's final message, wrapped in
92
99
  `<untrusted-codex-output>` tags; progress noise and — with `-g` — the list of
93
100
  copied files go to **stderr**. Read both streams.
@@ -1,8 +1,7 @@
1
1
  ---
2
2
  name: gemini-consult
3
3
  description: >-
4
- Consult Google Gemini (via the local Antigravity `agy` CLI, model Gemini 3.1
5
- Pro) for three things: (1) natural, native-sounding KOREAN phrasing — copy,
4
+ Consult Google Gemini (via the local Antigravity `agy` CLI, Pro tier) for three things: (1) natural, native-sounding KOREAN phrasing — copy,
6
5
  UI microcopy, marketing/brochure text, toasts, user-facing messages,
7
6
  translations, rewrites — (2) a MULTI-PERSONA / second-opinion review of a
8
7
  design, plan, spec, PR, or piece of writing, and (3) IMAGE GENERATION via
@@ -70,11 +69,11 @@ not bundled here. The wrapper resolves it at `$AGY_BIN` or `~/.local/bin/agy`.
70
69
  ## How to call it
71
70
 
72
71
  Prefer the **bundled wrapper** — it encodes every guardrail in one place
73
- (neutral cwd so the repo is NOT pulled into agy's workspace, model pin, env
74
- allowlist, secret-shaped-prompt refusal, portable timeout, correct flag order,
75
- artifact collection for image mode). It ships next to this skill when the
76
- harness installs the skill directory. Run it via `bash` (no execute-bit
77
- assumption):
72
+ (neutral cwd so the repo is NOT pulled into agy's workspace, tier→model
73
+ resolution, env allowlist, secret-shaped-prompt refusal, portable timeout,
74
+ correct flag order, artifact collection for image mode). It ships next to this
75
+ skill when the harness installs the skill directory. Run it via `bash` (no
76
+ execute-bit assumption):
78
77
 
79
78
  ```bash
80
79
  # Claude Code, project scope (harness default):
@@ -87,14 +86,19 @@ bash .claude/skills/gemini-consult/scripts/gemini-ask.sh <<'EOF'
87
86
  EOF
88
87
  # image generation — files land in OUT_DIR (see Mode C):
89
88
  bash .claude/skills/gemini-consult/scripts/gemini-ask.sh -g ./scratch "PROMPT"
90
- # override model (default = "Gemini 3.1 Pro (High)"; see `agy models`):
91
- bash .claude/skills/gemini-consult/scripts/gemini-ask.sh -m "Gemini 3.1 Pro (Low)" "PROMPT"
89
+ # pro is the default; -t claude switches vendor (see "Which tier" below):
90
+ bash .claude/skills/gemini-consult/scripts/gemini-ask.sh -t claude "PROMPT"
91
+ # exact model, skipping tier resolution — read the slug out of `agy models`,
92
+ # don't type one from memory (agy rejects a retired model outright):
93
+ bash .claude/skills/gemini-consult/scripts/gemini-ask.sh -m SLUG "PROMPT"
92
94
  ```
93
95
 
94
96
  Output contract: **stdout** carries only Gemini's reply, wrapped in
95
- `<untrusted-gemini-output>` tags; progress and with `-g` — the list of
96
- collected files go to **stderr**. Flags must come *before* the prompt
97
- (trailing flags fail loud), and a prompt starting with `-` needs a `--`
97
+ `<untrusted-gemini-output>` tags; progress, the resolved model line
98
+ (`tier 'pro' model '…'`) and with `-g` the list of collected files go
99
+ to **stderr**. Read that model line back to the user when the tier mattered:
100
+ which model answered is part of the answer. Flags must come *before* the
101
+ prompt (trailing flags fail loud), and a prompt starting with `-` needs a `--`
98
102
  separator first, or use the stdin form.
99
103
 
100
104
  Calls block synchronously — the wrapper caps a call at 300s
@@ -120,11 +124,17 @@ they can't leak into your session:
120
124
  (
121
125
  AGY="${AGY_BIN:-$(command -v agy || echo "$HOME/.local/bin/agy")}"
122
126
  [ -x "$AGY" ] || { echo "agy not found — ask the user to install it" >&2; exit 3; }
127
+ "$AGY" models # → read the list, pick a slug for the tier you want:
128
+ # gemini-*-pro-* · claude-*
123
129
  D="$(mktemp -d)"; trap 'rm -rf "$D"' EXIT
124
- cd "$D" && "$AGY" --model="Gemini 3.1 Pro (High)" -p "PROMPT"
130
+ cd "$D" && "$AGY" --model="<slug>" -p "PROMPT"
125
131
  )
126
132
  ```
127
133
 
134
+ Read the slug out of `agy models` rather than typing one from memory — agy
135
+ rejects a retired model outright (`invalid model selection`, exit 1), so a
136
+ remembered name fails the whole call.
137
+
128
138
  The `( )` subshell matters: in an interactive terminal a bare `cd` persists
129
139
  after the block and silently relocates every later command.
130
140
 
@@ -154,6 +164,48 @@ between `-p` and the prompt makes agy ignore the prompt.
154
164
  (`1. … 2. … 3. …`) and ask for the same numbering back, instead of one call
155
165
  per string.
156
166
 
167
+ ## Which tier — `pro` / `claude`
168
+
169
+ `-t` picks a model **family**; the concrete model is resolved from `agy models`
170
+ on every call, so nothing here pins a version. Two measured facts shape the
171
+ policy below:
172
+
173
+ - **A tier name tells you nothing about recency.** The families do not share a
174
+ version line — measured on 2026-07-26, the fast family was two generations
175
+ ahead of pro. That is exactly why nothing here pins a version string: a slug
176
+ typed from memory gets rejected outright (`invalid model selection`, exit 1),
177
+ and "the newer one" is not a property you can infer from the tier name.
178
+ - **The Gemini quota is one pool per account; Claude models sit outside it.**
179
+ Measured 2026-07-26: `gemini-3.1-pro-high`, `gemini-3.1-pro-low` and a flash
180
+ model all refused with the *identical* `Individual quota reached … Resets in
181
+ 166h` while a `claude` call in the same minute answered normally. Lowering the
182
+ tier does not dodge it — there is nothing below to fall back to.
183
+
184
+ **Every Gemini call this skill makes uses `pro`.** The reason is what the skill is for: it exists because a second model's *judgment* is worth an external round-trip — natural Korean that doesn't read translated, personas that stay distinct, critique that finds what you missed. Those are the calls where a cheaper tier costs you the thing you came for, and a round-trip you have to redo is more expensive than the one you did right. If a call is routine enough that a fast tier would do, it probably shouldn't be an external call at all.
185
+
186
+ **`-t claude`** — agy also serves Claude models. Reach for it when:
187
+
188
+ - The Gemini quota is spent and the work can't wait for the reset; it is the
189
+ only tier still answering.
190
+ - You want a **fresh, uncontaminated read** — the call carries no repo, no
191
+ conversation history, and none of the framing you already committed to. When
192
+ you suspect your own framing is the problem, that is worth more than a
193
+ different vendor.
194
+ - You are running **on Antigravity**, where you already are Gemini: there the
195
+ Claude tier is the outside opinion and the Gemini tiers are the circular one.
196
+
197
+ Not for **Mode A**. This skill exists because Claude's Korean reads translated —
198
+ routing Korean copy back to a Claude model defeats the premise.
199
+
200
+ **So when the Gemini pool is empty, Mode A waits.** Do not quietly reroute Korean
201
+ copy to `-t claude` to have *something* to show: the output would carry exactly
202
+ the quality this skill was created to avoid, and the user would have no way to
203
+ tell it apart from a Gemini answer. Report the refusal and the reset time, say
204
+ that Korean phrasing is unavailable until then, and let the user decide — write
205
+ it themselves, wait, or raise the subscription. An answer the user cannot trust
206
+ is worth less than a clearly stated gap. (Modes B and C have no such premise:
207
+ `-t claude` is a legitimate substitute there.)
208
+
157
209
  ## Mode A — natural Korean phrasing / copy
158
210
 
159
211
  Give Gemini the text plus the context it needs to judge register, then ask for a
@@ -225,8 +277,11 @@ full-cost regeneration.
225
277
  **Image quota is small and shared across model tiers.** A handful of
226
278
  generations can exhaust it; Gemini then answers (politely) that quota is
227
279
  exceeded — the wrapper surfaces that as exit 5 with the explanation inside the
228
- tags. Observed reset horizon: up to ~7 days. Don't retry-loop against a quota
229
- error; relay it and fall back to `codex-consult` for the image.
280
+ tags. Once the whole Gemini pool is spent the call doesn't even reach the tool:
281
+ agy fails with `Individual quota reached` and its own nonzero exit. Observed
282
+ reset horizon: up to ~7 days. **Another Gemini tier would not help** — one
283
+ pool per account; only `-t claude` sits outside it. Don't retry-loop against a quota error; relay it and fall
284
+ back to `codex-consult` for the image.
230
285
 
231
286
  ```
232
287
  이미지 생성 도구로 이미지를 하나 생성해줘:
@@ -243,7 +298,7 @@ inline:
243
298
  [ -x "$AGY" ] || { echo "agy not found — ask the user to install it" >&2; exit 3; }
244
299
  BRAIN="$HOME/.gemini/antigravity-cli/brain"
245
300
  M="$(mktemp)"; D="$(mktemp -d)"; trap 'rm -rf "$D" "$M"' EXIT
246
- cd "$D" && "$AGY" --model="Gemini 3.1 Pro (High)" -p "PROMPT — 생성만 하고 셸 저장은 시도하지 마"
301
+ cd "$D" && "$AGY" --model="<slug from agy models>" -p "PROMPT — 생성만 하고 셸 저장은 시도하지 마"
247
302
  find "$BRAIN" -type f \( -name '*.png' -o -name '*.jpg' \) -newer "$M" # → report/copy these
248
303
  )
249
304
  ```
@@ -256,11 +311,15 @@ candidates, the clustered findings, or the file paths — not raw CLI noise.
256
311
 
257
312
  ## On failure
258
313
 
259
- - exit `2` usage · `3` agy missing (Prerequisite) · `4` secret-shaped prompt
260
- refused · `5` image mode produced no artifacts (read the tagged message for
261
- why) · `124` timed out (`GEMINI_CONSULT_TIMEOUT`, default 300s) do NOT
262
- blindly re-run the same prompt.
314
+ - exit `2` usage (includes an unknown `-t` tier) · `3` agy missing
315
+ (Prerequisite) **or `agy models` offers nothing for the tier** stderr lists
316
+ what it does offer; pick one with `-m`, never assume a substitute tier ·
317
+ `4` secret-shaped prompt refused · `5` image mode produced no artifacts (read
318
+ the tagged message for why) · `124` timed out (`GEMINI_CONSULT_TIMEOUT`,
319
+ default 300s) — do NOT blindly re-run the same prompt.
263
320
  - Any other nonzero exit is agy's own failure — read stderr, report it.
321
+ `Individual quota reached` = the Gemini pool is spent (~7 day reset); retry
322
+ with `-t claude`, not with another Gemini tier.
264
323
  - `Authentication required. Please visit the URL ...` → stop; the user runs
265
324
  `agy` interactively once (Prerequisite).
266
325
  - `a tool required the "command" permission ...` → the prompt induced a shell