82 lines
5.9 KiB
Markdown
82 lines
5.9 KiB
Markdown
---
|
|
title: "ca-tmpl write-time import-gate 훅(G5/G7) 오탐 — shared-contract 주석 정리 차단"
|
|
source_type: error-note
|
|
status: raw
|
|
tags: [hooks, write-gate, false-positive, clean-architecture, shared-contract, refactoring, comment-cleanup, ca-skeleton]
|
|
created: 2026-06-19
|
|
---
|
|
|
|
# ca-tmpl import-gate 훅의 오탐 — intra-module import 와 주석 속 금지 토큰
|
|
|
|
## Parent
|
|
|
|
- `[[raw/branch-notes/chore-shared-contract-comment-cleanup]]`
|
|
- `[[raw/branch-notes/chore-sample-portfolio-comment-cleanup]]` — 동일 G7 오탐 재발(`enableDefaultTyping()` 리터럴)
|
|
|
|
## 맥락
|
|
|
|
`shared-contract` 모듈의 결정-근거 주석을 README 로 이전(comment-only)하는 중, PreToolUse 훅
|
|
`.claude/hooks/ca_import_gate.py` 가 **주석만 바꾸는 Edit 3건을 차단**했다. 이 훅은 write 시점에
|
|
projected(편집 후 **전체 파일**) 내용을 스캔해 G1~G8 금지 패턴을 막는다 — ArchUnit/Gradle
|
|
빌드 게이트의 부분집합을 "디스크에 닿기 전"에 잡는 용도.
|
|
|
|
## 현상 — 차단 3건
|
|
|
|
1. **G5 (shared-contract stdlib-only)** — `response/BulkEnvelope.java`, `operation/Operation.java`
|
|
- 차단 라인: `import dev.caskeleton.shared.error.OperationalError;` /
|
|
`import dev.caskeleton.shared.response.ApiError;`
|
|
- 이유: `JAVA_ONLY_RE = ^import\s+java\.` 만 허용하고, 그 외 모든 `^import \S` 를 위반으로 본다.
|
|
shared-contract 의 gradle 매트릭스 의존이 `[]` 라서, **같은 모듈 내 다른 패키지** import
|
|
(`dev.caskeleton.shared.error.*`)조차 cross-module 의존으로 오탐한다.
|
|
- 실제로는 정당한 intra-module import — 컴파일·`verifyCleanArchitectureDependencies`·ArchUnit
|
|
모두 통과하는 코드다(빌드 게이트는 모듈/프로젝트 단위라 패키지 간 import 를 막지 않음).
|
|
|
|
2. **G7 (`\bInheritableThreadLocal\b` 금지)** — `concurrency/DomainContextPropagator.java`,
|
|
`concurrency/ThreadLocalDomainContextPropagator.java`
|
|
- 차단 라인: 주석이 `{@code InheritableThreadLocal}` 을 **언급**(=쓰지 말라고 설명)하는 줄.
|
|
- 이유: `CVE_RE` 가 줄 어디에든 토큰이 있으면 매치한다 — **사용**과 **언급**을 구분하지 못한다.
|
|
원본 코드도 같은 토큰을 주석에 갖고 있었지만 훅 도입 전 커밋이라 통과했을 뿐.
|
|
- whole-file scan 이므로, 한 파일에 토큰이 2곳(클래스 JavaDoc + 인라인 주석)이면 **한 번의
|
|
write 로 둘 다** 제거해야 통과한다(한 곳만 고치면 나머지가 여전히 차단).
|
|
|
|
## 왜 위험/성가신가
|
|
|
|
- "주석만 바꾸는" 안전한 작업이 차단되어, 작업자가 (a) 정당한 import 를 지우거나(컴파일 깨짐)
|
|
(b) gradle 매트릭스를 약화시키는(규칙 자체는 옳음) 잘못된 "수정"으로 유혹받기 쉽다.
|
|
- 훅 메시지가 "매트릭스/ArchUnit 을 먼저 바꾸라"고 안내하지만, 이 경우 규칙 변경은 **틀린 대응**이다
|
|
— 규칙은 정당하고 훅의 매칭이 과도할 뿐.
|
|
|
|
## 회피 (이번 작업에서 택한 대응)
|
|
|
|
- **G5 파일(Operation/BulkEnvelope)**: import 를 건드리지 않기 위해 **두 파일의 코드 주석은 미정리**로
|
|
남기고, 두 클래스의 결정 근거는 README 에만 수록. import 제거·매트릭스 약화 둘 다 거부.
|
|
- **G7 파일(concurrency)**: 주석에서 `InheritableThreadLocal` **리터럴**을 동의어로 표현
|
|
("the inheritance-based variant" / snake_case 규칙명 `no_inheritable_thread_local")해 토큰을 제거.
|
|
의미는 README(.md — 이 훅은 `src/**/*.java` 만 검사하므로 미게이트)가 전체 용어로 보존.
|
|
한 파일의 두 토큰은 **클래스 JavaDoc + 인라인 주석을 한 Edit 으로 묶어** 동시 제거.
|
|
- Bash heredoc / `echo >` 우회 쓰기는 시도하지 않음(설계상 동일 차단 대상이며 우회는 규약 위반).
|
|
|
|
## 재발 방지 / 교훈
|
|
|
|
- shared-contract 의 어떤 Java 파일이든 **다른 shared 패키지 import 가 있으면** 이 훅으로 주석 편집이
|
|
막힌다 — comment-only 작업을 계획할 때 미리 `grep -l '^import dev\.caskeleton' src/shared-contract/...`
|
|
로 차단 대상 파일을 식별하고, 그 파일은 README-only(코드 미편집)로 처리한다.
|
|
- `InheritableThreadLocal` 을 *설명*해야 하는 코드(주석)는 코드에 리터럴을 두지 말고 README 에 둔다.
|
|
- **훅 개선 후보**(미적용, 제안만): G5 는 `^import dev\.caskeleton\.shared\.` (자기 모듈 prefix)를
|
|
예외 처리하면 intra-module 오탐이 사라진다. G7 은 사용(`new InheritableThreadLocal`/`extends
|
|
InheritableThreadLocal`/`<...>`)만 매치하고 주석/`{@code ...}` 언급은 통과시키면 오탐이 준다.
|
|
단 규칙 변경은 매트릭스/ArchUnit/훅 SSOT 정렬 필요 — 본 작업 범위 밖.
|
|
|
|
## 재발 인스턴스 — sample-portfolio (2026-06-19)
|
|
|
|
- 파일: `adapter/web/dto/request/SamplePolymorphicRequest.java`
|
|
- 차단: G7 — 클래스 JavaDoc 을 한 줄로 합치며 `{@code ObjectMapper.enableDefaultTyping()}` 리터럴이 한 라인에 들어가자 write 차단(`G7 금지 패턴 (CVE/가상스레드 안전)`). 이 메서드 호출은 CVE-2019-14379 RCE 입구로, ArchUnit `no_jackson_enable_default_typing_call` 의 대상 토큰.
|
|
- 원본도 같은 토큰을 JavaDoc 에 갖고 있었으나 훅 도입 전 커밋이라 통과했을 뿐 — 위 G7 분석과 동일(사용 vs 언급 미구분).
|
|
- 대응: 소스 주석은 "Jackson's unsafe default-typing entry point (CVE-2019-14379)" 로 우회(리터럴 메서드명 제거), 정확한 메서드명은 `sample-portfolio/README.md`(.md, 게이트 비대상)에 보존. 규칙 변경·우회 쓰기 모두 거부.
|
|
|
|
## 관련
|
|
|
|
- 형제 작업의 다른 함정: [[raw/errors/ca-comment-to-readme-subagents-overstrip-runtime-strings]]
|
|
— comment→README 작업이 런타임 문자열까지 손대는 behavior change. (이번 작업은 diff audit 으로
|
|
enum 값/문자열 리터럴 불변 확인 → 해당 함정은 회피.)
|