Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 79 additions & 1 deletion .claude/harness-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@
│ ├── sync-kwg-reference.sh # KWG 원본 md 파일 동기화
│ ├── check-kwg-drift.py # KWG 원본과의 드리프트 검사
│ ├── check-i18n-parity.py # ko/en 문서 파일 패리티 검사
│ └── check-redirects.py # 사이트 개편 시 사라진 URL의 리다이렉트·목적지 확인
│ ├── check-redirects.py # 사이트 개편 시 사라진 URL의 리다이렉트·목적지 확인
│ ├── check-code-blocks.py # 문서 코드블록 문법·스키마 검사 (L1·L2)
│ ├── check-code-refs.py # 문서가 인용하는 외부 참조 실재 확인 (L3)
│ └── example-e2e.sh # samples/ 실습을 실제로 돌려 재현 확인 (L4)
dry-run/
└── run-dryrun.sh # OpenWave 프로필 드라이런 오케스트레이터
Expand Down Expand Up @@ -362,6 +365,81 @@ bash .claude/scripts/sync-output-samples.sh

docs/ 파일 저장 시 `cd agents/` 블록 직전 `:::tip 실행 전 확인`이 없으면 즉시 경고 출력.

### `check-code-blocks.py`: 코드블록 문법·스키마 검사 (L1·L2)

독자가 복사해 쓰는 예시가 깨지지 않았는지 본다. yaml·json·xml·toml 을 파서에 넣고,
GitHub Actions 는 actionlint, GitLab CI 는 구조 검사, bash 는 `bash -n` 과 shellcheck 로 검사한다.

```bash
python3 .claude/scripts/check-code-blocks.py # 전체 검사
python3 .claude/scripts/check-code-blocks.py --stats # 인벤토리만
python3 .claude/scripts/check-code-blocks.py --selftest # 검사기가 실제로 도는지 확인
```

통과할 수 없는 블록은 펜스에 `validate=skip` 을 단다(예: ` ```yaml validate=skip `).
안티패턴 예시나 `<다이제스트>` 같은 자리표시자가 든 블록에만 쓴다. 현재 2개 블록에 달려 있고
ko·en 양쪽이라 파일에서는 4곳으로 잡힌다.

`website/reference/samples/` 는 검사하지 않는다. `output-sample/` 에서 생성되는 파생물이라
거기서 고치면 다음 재생성에 원복된다. 원본인 `output-sample/` 을 검사한다.

`--selftest` 는 일부러 깨뜨린 블록을 넣어 검출되는지 본다. 결과가 0건일 때 그것이 진짜 0인지
검사기가 아무것도 안 본 것인지 구분하기 위해서다. CI 도 본 검사 앞에 이것을 먼저 돌린다.

같은 이유로 도구가 없어 돌지 못한 검사가 있으면 통과로 세지 않고 실패한다. 필요한 것은
Python 3.11 이상(toml 검사용 tomllib), PyYAML, actionlint, shellcheck 넷이다. 로컬에서 일부를
갖추지 못했다면 `--allow-missing-tools` 로 넘길 수 있지만, 그때 무엇을 못 봤는지 출력에 남는다.
CI 는 이 플래그를 쓰지 않는다.

### `check-code-refs.py`: 외부 참조 실재 확인 (L3)

`uses:` 가 가리키는 액션 태그와 설치 스크립트 URL 이 실제로 있는지 확인한다.
actionlint 는 문법만 보고 참조 실재는 확인하지 않아서 따로 둔다.

```bash
python3 .claude/scripts/check-code-refs.py # 전량
python3 .claude/scripts/check-code-refs.py --changed origin/main # 변경분만
```

문서를 고치지 않아도 깨질 수 있다. 태그가 삭제되거나 URL 이 옮겨 가면 실패한다. 그래서
PR 게이트(`pre-merge.yml` Layer 5)는 변경분만 보고, 전량 확인은 `example-refs.yml` 이
주간 예약으로 돌려 실패 시 이슈만 만든다. 병합은 막지 않는다.

### `example-e2e.sh`: samples 실습 재현 확인 (L4)

`samples/` 세 프로젝트를 문서에 적힌 순서대로 실제로 돌려 기대한 결과가 나오는지 본다.
앞 계층이 문법만 보는 것과 달리 도구를 실행한다.

```bash
bash .claude/scripts/example-e2e.sh # 전체
bash .claude/scripts/example-e2e.sh --selftest # 단언이 실제로 검출하는지 확인
bash .claude/scripts/example-e2e.sh --no-network # 네트워크가 필요한 항목을 건너뛴다
```

단언은 종료 코드가 아니라 값이다. java 컴포넌트 4개 이상, python 5개 이상, 생성한 SBOM 을
grype 가 실제로 파싱하는지, java 스캔에 `GHSA-jfh8-c2jp-5v3q` 가 있는지, nodejs 는 설치 후
컴포넌트가 0보다 큰지와 `vendor/legacy-parser` 에 license 필드가 없는지를 본다.

종료 코드를 믿지 않는 이유가 있다. macOS 의 Docker 는 공유 목록에 없는 호스트 경로를
마운트하면 오류 대신 빈 디렉터리를 붙인다. 그러면 syft 는 종료 코드 0 으로 컴포넌트가 하나도
없는 SBOM 을 내놓는다. `--selftest` 가 빈 입력을 넣어 이 상황이 실패로 판정되는지 확인한다.

자동 실행은 `example-e2e.yml` 이 맡는다. 주간 예약(월요일 03:30 UTC)과 수동 실행,
그리고 `samples/**`·`docs/05-tools/**`·이 스크립트와 워크플로 자신을 건드리는 PR 에서 돈다.
PR 게이트로 상시 도는 계층이 아닌 이유는 grype 취약점 DB 가 2.0GB 단일 파일이라 내려받는 데만
7분이 넘고 컨테이너 이미지 넷과 외부 API 에 의존하기 때문이다. 실패하면 이슈를 만들되 PR 에서는
체크에 이미 보이므로 만들지 않는다.

도구 버전은 태그로 고정한다(syft v1.51.1, grype v0.118.0). 이 조합이 CycloneDX 1.7 을 내고
읽는 짝이다. 낮은 grype 를 쓰면 `sbom format not recognized` 로 이 검사가 걸린다.

`GRYPE_DB_CACHE` 에 디렉터리를 주면 취약점 DB 를 재사용한다.

작업 디렉터리는 저장소 안 `.example-e2e-tmp/` 에 만든다(gitignore 대상, 종료 시 삭제).
`mktemp -d` 를 쓰지 않는 이유는 macOS 에서 그 경로가 `/var/folders` 아래라 Docker Desktop 의
공유 목록에 없기 때문이다. 마운트가 빈 디렉터리로 붙어 실습이 통째로 실패한다. 이 위치를
옮기지 마라.

---

## 5. 시나리오별 사용 예시
Expand Down
Loading
Loading