diff --git a/docs/diagrams/harness-overview/hooks.mmd b/docs/diagrams/harness-overview/hooks.mmd index af0eb457..8da4da8e 100644 --- a/docs/diagrams/harness-overview/hooks.mmd +++ b/docs/diagrams/harness-overview/hooks.mmd @@ -27,8 +27,9 @@ sequenceDiagram rect rgb(40, 26, 26) Note over Claude,Tool: PreToolUse — Bash git commit 실행 전 Claude->>Hook: pre-commit-check.sh (명령어 전달) - Hook->>Hook: pnpm tsc --noEmit - Hook->>Hook: pnpm test + Hook->>Hook: $PM tsc --noEmit (tsconfig.json 있을 때) + Hook->>Hook: $PM run lint (127이면 경고 후 통과) + Hook->>Hook: $PM test alt 실패 Hook-->>Claude: exit 2 (커밋 차단) else 통과 diff --git a/docs/harness-overview.html b/docs/harness-overview.html index b0e31b83..d6c902bc 100644 --- a/docs/harness-overview.html +++ b/docs/harness-overview.html @@ -847,8 +847,9 @@

release --json

rect rgb(40, 26, 26) Note over Claude,Tool: PreToolUse — Bash git commit 실행 전 Claude->>Hook: pre-commit-check.sh (명령어 전달) - Hook->>Hook: pnpm tsc --noEmit - Hook->>Hook: pnpm test + Hook->>Hook: $PM tsc --noEmit (tsconfig.json 있을 때) + Hook->>Hook: $PM run lint (127이면 경고 후 통과) + Hook->>Hook: $PM test alt 실패 Hook-->>Claude: exit 2 (커밋 차단) else 통과 @@ -882,7 +883,8 @@

PreToolUse pre-commit-check.sh

Bash 도구에서 git commit 명령 감지 시 검증 실행.

@@ -2529,6 +2531,11 @@

병합 마커 포맷

Test 자동 회귀 검증 + + tests/fixtures/stock-hooks/pre-lint/pre-commit-check.sh + Test + 자동 회귀 검증 + tests/fixtures/stock-hooks/pre-rn-ios-secrets/protect-files.sh Test diff --git a/docs/harness-overview.template.html b/docs/harness-overview.template.html index 1502a1e9..1f8bcf7f 100644 --- a/docs/harness-overview.template.html +++ b/docs/harness-overview.template.html @@ -606,7 +606,8 @@

PreToolUse pre-commit-check.sh

Bash 도구에서 git commit 명령 감지 시 검증 실행.

diff --git a/docs/hslee/commit-gate-lint/commit-gate-lint-artifact.md b/docs/hslee/commit-gate-lint/commit-gate-lint-artifact.md new file mode 100644 index 00000000..f9676898 --- /dev/null +++ b/docs/hslee/commit-gate-lint/commit-gate-lint-artifact.md @@ -0,0 +1,81 @@ +# commit-gate-lint — Artifact + +*최종 결과물과 학습 내용을 기록한다.* + +## 결과 + +- 커밋 게이트(`templates/.claude/hooks/pre-commit-check.sh`)가 typecheck → **lint** → test 순으로 돈다. + package.json `lint`가 있을 때만 ` run lint` — 위반은 `exit 2`, 127(실행 불가)은 ⚠ 경고 후 통과. + test 게이트는 바꾸지 않았다(127도 차단 — 의도된 비대칭, spec 참조). `has_test_script` → `has_script ` 일반화. +- 배달: 직전 판(blob `ed90db5b`)을 `KNOWN_STOCK_HOOK_SHA256`·`tests/fixtures/stock-hooks/pre-lint/`에 추가 — + stock 설치본은 migrate refresh로 갱신, 커스터마이즈본은 보존(기존 계약). +- 테스트: `tests/hooks-jq-fallback.test.mjs`에 npm 스텁 기반 4케이스 × jq 있음/없음(위반·통과·없음·127), + `tests/migrate-hooks.test.mjs`에 pre-lint 판 refresh 1건. 구 훅으로 바꿔 돌리면 신규 6건이 실패(판별력 확인). +- 문서: overview 카드(lint 단계·127 비대칭)와 `hooks.mmd`(낡은 `pnpm` 하드코딩 → `$PM`, lint 단계) 갱신. + `/verify` 스킬·README는 훅 동작을 서술하지 않아 변경하지 않았다(`/verify` 수정은 stock-template 테이블 갱신만 늘린다). + +### 검증 증거 (ship, HEAD 732beb4, origin/main 5c12947 대비 rebase 불요) +``` +$ npm run test → exit 0 +ℹ tests 1088 · pass 1087 · fail 0 · skipped 1 (skip = CI 전용 jq 매트릭스 가드) +ℹ tests 1 · pass 1 · fail 0 (perf) +$ npm run docs:check → harness overview 생성 상태가 최신입니다. +``` +- 판별력: 구 훅(pre-lint fixture)으로 바꿔 `--test-name-pattern=lint` 실행 시 신규 6건 fail(pass 2 = "lint 없음" 2건). +- 다이어그램: 옵트인하지 않은 task(plan에 단계 없음) — 미실행. + +### 착수 전 실측 (2026-10-05) +- deep-math `npm run lint` → exit 127 `sh: eslint: command not found` (eslint 의존성·설정 모두 없음 — 죽은 스크립트). +- heliosent-profile `bun run lint` → exit 0. +- → 사용자 결정 C안: 위반은 차단, 127만 경고. + +### 후속 후보 (이번 task에서 고치지 않음 — 사용자 지시) +- 템플릿 `detect_pm`이 `bun.lockb`만 봐서 bun 1.2+ 텍스트 락파일 `bun.lock` 프로젝트를 npm으로 오인한다 + (heliosent-profile은 로컬 수정으로 우회 중). +- heliosent-profile의 `pre-commit-check.sh`는 커스터마이즈본이라 migrate가 덮지 않는다 → lint 게이트가 배달되지 않는다. + 수동 병합 또는 bun.lock 수정을 템플릿에 올린 뒤 stock 판정 재검토가 필요하다. +- deep-math의 죽은 `lint` 스크립트: eslint 설치 또는 스크립트 제거(현재는 매 커밋 ⚠ 경고). +- package.json 없는 프로젝트(ruff의 job-scraper)로의 확장. + +## Reviews +*Codex 등 리뷰 실행 시 결과(요약·발견·조치)를 날짜와 함께 남긴다. 남기지 않은 리뷰는 "안 한 것"으로 간주.* +*기계 판독용 마커를 함께 남긴다: ``* + +### 2026-10-05 — Codex read-only (`codex exec --sandbox read-only`, model gpt-6.1-sol) + +- **요약**: P0–P3 결함 없음. 종료 코드 캡처, 유효한 scripts 값의 jq/node 판정, npm 테스트 스텁, + migrate sha·fixture provenance, macOS bash 3.2 구문을 확인. lint/test 127 비대칭은 요구대로 유지. +- **한계**: read-only 샌드박스라 테스트 스위트는 돌리지 않았다(작성 세션이 `npm run test` 1087 pass로 대신 입증). +- **조치**: 없음. 작성 세션의 추가 관찰(기존 동작, 무변경): `"lint": ""`(빈 문자열)은 jq(`"" // empty`는 참)와 + node(`""`는 거짓)의 판정이 갈린다 — jq 쪽은 빈 스크립트를 실행해 exit 0이라 결과는 같다. test도 동일한 기존 동작. + +### 2026-10-05T04:06:41.520Z — codex-shipcheck (harness-team review) + +- engine: codex · scope: diff · tip: 732beb4b2caee20938ce0cc9ed8519f1f162fabf · exit 0 · 2658 B + +```text +전하, `git status`와 `git diff origin/main`을 직접 확인했습니다. 아래 판정은 **현재 워킹트리 포함** 기준입니다. artifact와 handoff에 미커밋 변경이 있으며, 테스트는 재실행하지 않았습니다. 파일은 수정하지 않았습니다. + +| id | 항목 | 심각도 | 판정 | 근거 | +|---|---|---|---|---| +| S1 | 요구사항과 구현 대응 | BLOCKER | pass | spec의 “순서: typecheck → lint → test”와 “127 … 경고 두 줄 후 통과”에 대응하여 diff에 `if has_script lint`, `"$PM" run lint … \|\| lint_rc=$?`, `lint_rc -eq 127` 경고 및 그 외 실패의 `exit 2`가 추가됐습니다. jq/node 공용 판정, stock SHA·fixture·refresh 테스트도 추가됐으며, 공통 jq 블록과 기존 test 차단 동작은 유지됩니다. | +| S2 | 완료 체크의 실재 근거 | MAJOR | pass | plan의 훅·배달·테스트·문서 항목은 해당 diff에 모두 존재합니다. handoff에 `1f38b9c feat(hooks)`와 `732beb4 chore(task)`가 기록되어 실제 git log와 일치합니다. 검증 항목은 artifact의 `$ npm run test → exit 0` 및 `$ npm run docs:check` 출력, 리뷰 항목은 `## Reviews` 기록에 대응합니다. | +| S3 | 스코프 밖 변경 | MAJOR | pass | 제품 변경은 훅·migrate SHA·fixture·관련 테스트·overview에 한정됩니다. artifact는 “overview 카드…와 `hooks.mmd`…갱신”을 명시하고, 후속 범위는 “이번 task에서 고치지 않음”으로 기록합니다. 나머지는 해당 task의 관리 문서입니다. | +| S4 | 실행 리뷰 기록과 마커 | MAJOR | pass | 확인된 기존 리뷰는 artifact `## Reviews`에 날짜·요약·한계·조치와 함께 ``로 기록되어 있습니다. 제공된 문서·diff에서 추가 완료 리뷰의 누락 증거는 없습니다. | +| S5 | 검증 결과의 명령·출력 인용 | BLOCKER | pass | artifact의 fenced block에 `$ npm run test → exit 0`, `ℹ tests 1088 · pass 1087 · fail 0 · skipped 1`, perf 결과 및 `$ npm run docs:check → harness overview 생성 상태가 최신입니다.`가 있습니다. 단순 “통과했다” 선언을 넘어 명령과 결과를 인용합니다. 다만 원본 실행 로그의 진위까지 독립 확인한 판정은 아닙니다. | + +검증 증거 블록은 **미커밋 artifact 변경**에 포함되어 있으므로, 현재 HEAD만으로는 동일한 증거 상태가 전달되지 않습니다. 이번 검증 결과도 작성 세션에서 `## Reviews`에 기록해야 합니다. + +**Verdict: PASS — fail 항목 없음.** +``` + + + +- **판별·조치**: S1–S5 전부 pass, 진짜 결함 없음. "증거 블록이 미커밋"이라는 지적은 사실이며, ship 커밋에 함께 담아 해소한다. + shipcheck는 scope CLI가 `worktree`(미커밋 handoff뿐)를 줬지만 코드 diff가 커밋에 있어 `--scope diff --base origin/main`으로 돌렸다 — + 검증자는 `git status`로 미커밋 문서 변경까지 함께 읽었다. + +## Learnings + +- 훅 템플릿 바이트를 바꾸면 세 곳을 함께 바꿔야 한다: `KNOWN_STOCK_HOOK_SHA256` 직전 판 sha, `stock-hooks//` fixture, + 드리프트 가드의 fixture 개수(`checked`). 이력 완전성 테스트는 `HEAD` 기준이라 **커밋 전에는 반드시 실패**한다. diff --git a/docs/hslee/commit-gate-lint/commit-gate-lint-context.md b/docs/hslee/commit-gate-lint/commit-gate-lint-context.md new file mode 100644 index 00000000..2b442992 --- /dev/null +++ b/docs/hslee/commit-gate-lint/commit-gate-lint-context.md @@ -0,0 +1,22 @@ +# commit-gate-lint — Context Card + + +## Now +- Goal: 커밋 게이트에 package.json `lint` 단계 추가 — 위반 exit 2, 127 경고 후 통과 +- Current atomic step: ship 완료 — push·PR 생성, 머지 금지 +- Stop / human-decision condition: push·PR은 사용자 명시 지시 전 금지 + +## Constraints and settled decisions +- 사용자 결정 C안(2026-10-05): lint 127만 경고, test 게이트는 127도 차단(불변) +- jq-fallback 블록·timeout 120s 불변, detect_pm bun.lock·heliosent 커스텀 훅은 후속(artifact) + +## JIT retrieval map +- Identifiers / symbols: `has_script`, `lint_rc`, `KNOWN_STOCK_HOOK_SHA256` +- Narrow globs: `templates/.claude/hooks/pre-commit-check.sh`, `tests/fixtures/stock-hooks/pre-lint/` +- Read next: `tests/hooks-jq-fallback.test.mjs` (runCommitWithNpm), `tests/migrate-hooks.test.mjs` +- Verification command: `npm run test`, `npm run docs:check` + +## Failure capsules (max 3 unresolved) + +## Resume checklist +- 이력 완전성 테스트는 HEAD 기준 — 커밋 후 재실행 diff --git a/docs/hslee/commit-gate-lint/commit-gate-lint-handoff.md b/docs/hslee/commit-gate-lint/commit-gate-lint-handoff.md new file mode 100644 index 00000000..d602728a --- /dev/null +++ b/docs/hslee/commit-gate-lint/commit-gate-lint-handoff.md @@ -0,0 +1,35 @@ +# commit-gate-lint — Handoff + +(세션 종료 시 post-commit hook이 자동 갱신합니다) + +## 2026-10-05T02:56:11.972Z — 1f38b9c feat(hooks): 커밋 게이트가 package.json lint 스크립트를 돌린다 +docs/diagrams/harness-overview/hooks.mmd | 5 +- + docs/harness-overview.html | 13 +- + docs/harness-overview.template.html | 3 +- + .../commit-gate-lint/commit-gate-lint-artifact.md | 38 ++++++ + .../commit-gate-lint/commit-gate-lint-context.md | 22 ++++ + .../commit-gate-lint/commit-gate-lint-handoff.md | 3 + + .../commit-gate-lint/commit-gate-lint-meta.json | 11 ++ + .../commit-gate-lint/commit-gate-lint-plan.md | 20 ++++ + .../commit-gate-lint/commit-gate-lint-spec.md | 84 +++++++++++++ + src/commands/migrate.mjs | 1 + + templates/.claude/hooks/pre-commit-check.sh | 30 +++-- + tests/fixtures/stock-hooks/README.md | 3 + + .../stock-hooks/pre-lint/pre-commit-check.sh | 133 +++++++++++++++++++++ + tests/hooks-jq-fallback.test.mjs | 56 +++++++++ + tests/migrate-hooks.test.mjs | 12 +- + 15 files changed, 420 insertions(+), 14 deletions(-) + +## 2026-10-05T02:57:56.841Z — 732beb4 chore(task): commit-gate-lint 검증·Codex 리뷰 기록 +.../commit-gate-lint/commit-gate-lint-artifact.md | 8 ++++++++ + .../hslee/commit-gate-lint/commit-gate-lint-context.md | 2 +- + .../hslee/commit-gate-lint/commit-gate-lint-handoff.md | 18 ++++++++++++++++++ + docs/hslee/commit-gate-lint/commit-gate-lint-plan.md | 4 ++-- + 4 files changed, 29 insertions(+), 3 deletions(-) + +## 2026-10-05T04:06:53.880Z — fe3e7fe chore(task): commit-gate-lint ship +.../commit-gate-lint/commit-gate-lint-artifact.md | 35 ++++++++++++++++++++++ + .../commit-gate-lint/commit-gate-lint-context.md | 2 +- + .../commit-gate-lint/commit-gate-lint-handoff.md | 7 +++++ + .../commit-gate-lint/commit-gate-lint-meta.json | 12 +++++++- + 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/docs/hslee/commit-gate-lint/commit-gate-lint-meta.json b/docs/hslee/commit-gate-lint/commit-gate-lint-meta.json new file mode 100644 index 00000000..d3b27b9a --- /dev/null +++ b/docs/hslee/commit-gate-lint/commit-gate-lint-meta.json @@ -0,0 +1,21 @@ +{ + "user": "hslee", + "task": "commit-gate-lint", + "created": "2026-10-05", + "firstActivatedAt": "2026-10-05T02:50:50.742Z", + "status": "open", + "closedAt": null, + "forcedAt": null, + "forcedIssues": null, + "reviews": [ + { + "kind": "codex-shipcheck", + "engine": "codex", + "scope": "diff", + "tip": "732beb4b2caee20938ce0cc9ed8519f1f162fabf", + "at": "2026-10-05T04:06:41.520Z", + "exitCode": 0, + "outputBytes": 2658 + } + ] +} diff --git a/docs/hslee/commit-gate-lint/commit-gate-lint-plan.md b/docs/hslee/commit-gate-lint/commit-gate-lint-plan.md new file mode 100644 index 00000000..ceb1c1da --- /dev/null +++ b/docs/hslee/commit-gate-lint/commit-gate-lint-plan.md @@ -0,0 +1,20 @@ +# commit-gate-lint — Plan + +## 목표 +커밋 게이트가 package.json `lint`를 typecheck와 test 사이에 돌린다 — 위반은 차단, 127은 경고 후 통과. + +## 단계 +- [x] 훅: `has_script` 일반화 + lint 단계(0 통과 / 127 ⚠ / 그 외 exit 2) +- [x] 배달: 직전 판을 `KNOWN_STOCK_HOOK_SHA256`·`tests/fixtures/stock-hooks/pre-lint/`·fixture README에 추가 +- [x] 테스트: hooks-jq-fallback(위반·통과·없음·127, jq 매트릭스) + migrate-hooks(pre-lint 판 refresh) +- [x] 문서: overview 카드·`hooks.mmd`(127 비대칭 명시), `npm run docs:check` +- [x] 검증: `npm run test` 전체 통과 +- [x] Codex read-only 리뷰 → artifact `## Reviews` + +## Ontology 변경 로그 +*개념이 새로 정의되거나 의미가 바뀌면 한 줄로 기록. spec.md의 Ontology 섹션을 갱신할 트리거가 된다.* + +- 2026-10-05: "lint 위반"과 "lint 실행 불가(127)"를 구분 — 후자는 경고만. + +## 참고 +- spec의 PM별 127 실측 표 diff --git a/docs/hslee/commit-gate-lint/commit-gate-lint-spec.md b/docs/hslee/commit-gate-lint/commit-gate-lint-spec.md new file mode 100644 index 00000000..0e788025 --- /dev/null +++ b/docs/hslee/commit-gate-lint/commit-gate-lint-spec.md @@ -0,0 +1,84 @@ +# commit-gate-lint — Spec + +## 목적 / 요구사항 +*문제(오늘 무엇이 안 되는가) → 영향받는 사용자·시스템 → 기대 결과 → 제약 순으로 쓴다. +답 없는 질문은 요구가 아니다 — `## 참고` 절에 `- (open) …`으로 남긴다.* + +- **문제**: 템플릿 커밋 게이트 `templates/.claude/hooks/pre-commit-check.sh`는 `tsc --noEmit`(tsconfig.json 있을 때)과 + package.json `test`만 돌린다. lint는 사람이 `/verify`(`disable-model-invocation: true`)를 부를 때만 돌아, + AI가 무시할 수 있는 규범에 머문다. +- **영향**: 하네스를 설치한 JS 소비자 프로젝트(deep-math·heliosent-profile 등)의 Claude Code 커밋. +- **기대 결과**: package.json에 `lint` 스크립트가 있으면 커밋 직전에 결정론적으로 실행한다. + - 실제 위반(exit ≠ 0, ≠ 127) → `exit 2`로 차단, 기존 두 줄 형식(❌ + `→ 확인 명령`). + - 실행 불가(exit 127, command not found — 린터 미설치·스크립트만 남은 경우) → ⚠ 경고 두 줄 후 통과. + - lint 스크립트 없음 → 건너뜀. jq 유무와 무관하게 같은 판정(jq 없으면 node 폴백). + - 순서: typecheck → lint → test (싼 검사 먼저). +- **의도된 비대칭**: test 게이트는 127이어도 **차단**한다(변경하지 않음). lint만 127을 경고로 강등한다 — + 사용자 결정 C안(2026-10-05). 근거: 착수 전 실측에서 deep-math의 `lint` 스크립트가 eslint 미설치로 exit 127 + (죽은 스크립트)이라, 차단 도입 시 stock 훅을 쓰는 소비자의 모든 커밋이 갑자기 막히는 회귀가 난다. +- **제약**: 특정 린터·규칙 세트를 내장하지 않는다(스택 중립). 훅 timeout(120s)과 `harness:jq-fallback` 공통 블록은 + 건드리지 않는다. package.json 없는 프로젝트(ruff의 job-scraper)·AGENTS.md `lint:` 파싱·auto-format·린터 규칙은 범위 밖. + +## 설계 / 접근 + +- `has_test_script`를 `has_script `으로 일반화(jq `.scripts[$s]` / node `scripts[argv]`), test·lint 공용. +- lint 실행은 `"$PM" run lint` — npm·pnpm·yarn·bun 모두 `run