Files
llm-wiki/harness/state/semantic-certificates/cd57931a048eff49d788e00b585deb922615310f00e6417b8f0555b96baf4746/e0fca6bc00c98cf9dd8bb67fdb4abccfc0586efc3eb00e5b213f13a578c496e6.json
T

2 lines
160 KiB
JSON

{"audit_request":{"assertions":[{"assertion_id":"BCP-1","condition":"브랜치 완료 시점","line_end":44,"line_start":44,"modality":"must","object":"forbidden module/import fixture가 ArchUnit gate에서 실패한다","predicate":"has_failure_behavior","quote":"- **완료 조건**: forbidden module/import fixture가 ArchUnit gate에서 실패한다","scope":"브랜치 계약 패킷","source_surface":"SURF-D6A6489839CCFAC646E2","subject":"브랜치 완료 조건"},{"assertion_id":"BCP-2","condition":"unconditional","line_end":43,"line_start":43,"modality":"observed","object":"contract_packet: 1","predicate":"has_schema","quote":"- **패킷 스키마**: `contract_packet: 1`","scope":"브랜치 계약 패킷","source_surface":"SURF-D6A6489839CCFAC646E2","subject":"브랜치 계약 패킷"},{"assertion_id":"BCP-3","condition":"생성 시점","line_end":42,"line_start":42,"modality":"observed","object":"1","predicate":"other","quote":"- **생성 시 프로젝트 개정**: `1`","scope":"브랜치 계약 패킷","source_surface":"SURF-D6A6489839CCFAC646E2","subject":"생성 시 프로젝트 개정"},{"assertion_id":"BCP-4","condition":"상속한 프로젝트 결정, Work Item 완료 조건에 적용","line_end":51,"line_start":51,"modality":"must","object":"domain-core·application-core·adapter-*·shared-contract·app-bootstrap·sample-portfolio 책임을 분리한다","predicate":"other","quote":"| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1` | Gradle multi-module에서 domain-core·application-core·adapter-*·shared-contract·app-bootstrap·sample-portfolio 책임을 분리한다 | Work Item 완료 조건에 적용 | [[raw/project-notes/ca-skeleton-operational-contract]] |","scope":"상속한 프로젝트 결정","source_surface":"SURF-D6A6489839CCFAC646E2","subject":"Gradle multi-module module layout (DEC-...-MODULE-LAYOUT-001@1)"},{"assertion_id":"BCP-5","condition":"상속한 프로젝트 결정, Work Item 완료 조건에 적용","line_end":52,"line_start":52,"modality":"must","object":"archunit-junit5 1.3.0","predicate":"uses","quote":"| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-ARCHTEST-001@1` | architecture test는 archunit-junit5 1.3.0을 사용한다 | Work Item 완료 조건에 적용 | [[raw/project-notes/ca-skeleton-operational-contract]] |","scope":"상속한 프로젝트 결정","source_surface":"SURF-D6A6489839CCFAC646E2","subject":"architecture test (DEC-...-STACK-ARCHTEST-001@1)"},{"assertion_id":"BCP-6","condition":"이 packet에서 복제하지 않음","line_end":57,"line_start":57,"modality":"must_not","object":"Decision Evidence Map / 결정-근거 매핑의 D-row (단일 소유, packet 복제 금지)","predicate":"owns","quote":"> 기존 branch-local 결정은 아래 `## Decision Evidence Map / 결정-근거 매핑`의 D-row가 소유하며 이 packet에서 복제하지 않는다.","scope":"브랜치 지역 결정","source_surface":"SURF-D6A6489839CCFAC646E2","subject":"branch-local 결정"},{"assertion_id":"CTV-1","condition":"Spring/JPA/HTTP/adapter/Lombok import를 포함하면","line_end":196,"line_start":196,"modality":"observed","object":"ArchUnit이 실패한다","predicate":"has_failure_behavior","quote":"| `domain-core`가 Spring/JPA/HTTP/adapter/Lombok import를 포함하면 ArchUnit이 실패한다 | rule DSL 구현 전에는 문서상 금지에 불과함 | violating class 추가 → `./gradlew :app-bootstrap:test --tests '*CleanArchitectureTest'` 실패 확인 + `ArchitectureViolationFixtureTest#domain_is_pure_catches_spring_dependency_in_domain_package` negative test 통과 확인 | `actually-implemented` (2026-05-28 round 2) — Lombok forbidden 추가됨 (`lombok..` package). violations-as-data fixture `SpringDependentDomainFixture` 가 `domain_is_pure` rule 의 실 catch 동작을 commit 된 negative test 로 보증. |","scope":"검증해야 할 주장","source_surface":"SURF-4D0C2AF6ED5AD8027D57","subject":"domain-core"},{"assertion_id":"CTV-10","condition":"non-trivial 구현 후 (documented-only)","line_end":205,"line_start":205,"modality":"observed","object":"LLM Wiki branch-note와 derived raw notes 캡처","predicate":"requires","quote":"| ca-tmpl repo-local workflow docs가 non-trivial 구현 후 LLM Wiki branch-note와 derived raw notes 캡처를 요구한다 | 문서 규칙 반영만으로 실제 에이전트 실행을 자동 보장하지는 않음 | `AGENTS.md`, `CLAUDE.md`, `.agents/.claude/.codex` 지침에서 `llm-wiki-capture` 및 Wiki capture 문구 검색 | `documented-only` |","scope":"검증해야 할 주장","source_surface":"SURF-4D0C2AF6ED5AD8027D57","subject":"ca-tmpl repo-local workflow docs"},{"assertion_id":"CTV-11","condition":"unconditional","line_end":206,"line_start":206,"modality":"observed","object":"ArchUnit rule 이 위반을 실제로 catch 한다는 commit 된 증명","predicate":"validates","quote":"| ArchUnit rule 이 위반을 실제로 catch 한다는 commit 된 증명 (negative test fixture) — `violations-as-data` pattern 채택 가능 | 현재 negative 검증은 임시 violating 파일 추가 → 제거 방식 (regression 보호 없음). Spring Modulith 공식 패턴 `modules.detectViolations().getMessages()` + `example/ninvalid` fixture package 가 production precedent | `src/app-bootstrap/src/test/.../violations/` 패키지에 의도된 위반 fixture class 추가 + `assertThatThrownBy(rule::check)` 또는 `evaluationResult.getFailureReport().getDetails()` assertion 으로 exact match 검증. 근거: `raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-AU-C2` | `actually-implemented` (2026-05-28 round 2) — `src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/` 에 6 fixture (1 domain + 5 application) + `ArchitectureViolationFixtureTest` 에 6 negative test 작성. 각 test 가 `ClassFileImporter().importPackages(\"...violations\")` 로 fixture 만 로드한 뒤 해당 rule 의 `EvaluationResult.hasViolation() == true` assert. fixture 는 `src/test/...` 위치라 main `@AnalyzeClasses(importOptions = DoNotIncludeTests.class)` 가 제외 → main suite vacuous pass 위험 없음. Spring Modulith `example/ninvalid` 패턴의 ca-tmpl 채택. |","scope":"검증해야 할 주장","source_surface":"SURF-4D0C2AF6ED5AD8027D57","subject":"ArchUnit rule negative test fixture (violations-as-data)"},{"assertion_id":"CTV-2","condition":"adapter-* project dependency를 선언하면","line_end":197,"line_start":197,"modality":"observed","object":"Gradle 검증이 실패한다","predicate":"has_failure_behavior","quote":"| `application-core`가 `adapter-*` project dependency를 선언하면 Gradle 검증이 실패한다 | Gradle dependency graph rule이 실제로 모든 subproject를 검사해야 함 | `application-core`에 `implementation project(':adapter-persistence')` 추가 → `./gradlew verifyCleanArchitectureDependencies` 실패 확인 | `actually-implemented` |","scope":"검증해야 할 주장","source_surface":"SURF-4D0C2AF6ED5AD8027D57","subject":"application-core"},{"assertion_id":"CTV-3","condition":"adapter module끼리 직접 의존하면","line_end":198,"line_start":198,"modality":"observed","object":"실패한다","predicate":"has_failure_behavior","quote":"| adapter module끼리 직접 의존하면 실패한다 | adapter 간 공유 요구가 생길 때 우회 가능성이 있음 | `adapter-web` -> `adapter-persistence` dependency 추가 → Gradle/ArchUnit 실패 확인 | `actually-implemented` |","scope":"검증해야 할 주장","source_surface":"SURF-4D0C2AF6ED5AD8027D57","subject":"adapter module"},{"assertion_id":"CTV-4","condition":"domain-specific package/class가 들어오면","line_end":199,"line_start":199,"modality":"observed","object":"실패한다","predicate":"has_failure_behavior","quote":"| `shared-contract`에 domain-specific package/class가 들어오면 실패한다 | shared 허용 scope가 넓으면 domain concept가 흘러들 수 있음 | `shared-contract/src/main/java/.../shared/worklog/` 추가 → ArchUnit package scope rule 실패 확인 | `locally-verified` |","scope":"검증해야 할 주장","source_surface":"SURF-4D0C2AF6ED5AD8027D57","subject":"shared-contract"},{"assertion_id":"CTV-5","condition":"sample-portfolio에 의존하면","line_end":200,"line_start":200,"modality":"observed","object":"실패한다","predicate":"has_failure_behavior","quote":"| production module이 `sample-portfolio`에 의존하면 실패한다 | sample은 편의상 import되기 쉬움 | production module에 `implementation project(':sample-portfolio')` 추가 → Gradle dependency rule 실패 확인 | `locally-verified` |","scope":"검증해야 할 주장","source_surface":"SURF-4D0C2AF6ED5AD8027D57","subject":"production module"},{"assertion_id":"CTV-6","condition":"domain object를 response로 반환하면","line_end":201,"line_start":201,"modality":"observed","object":"실패한다","predicate":"has_failure_behavior","quote":"| controller가 domain object를 response로 반환하면 실패한다 | mapper boundary rule이 module boundary만으로는 잡히지 않을 수 있음 | `adapter-web` controller가 domain model을 직접 반환하도록 위반 코드 추가 → ArchUnit 실패 확인 | `locally-verified` |","scope":"검증해야 할 주장","source_surface":"SURF-4D0C2AF6ED5AD8027D57","subject":"controller"},{"assertion_id":"CTV-7","condition":"@Transactional을 직접 import하면","line_end":202,"line_start":202,"modality":"observed","object":"실패한다","predicate":"has_failure_behavior","quote":"| application use case가 `@Transactional`을 직접 import하면 실패한다 | Spring 공식은 `@Transactional`을 지원하므로 ca-tmpl 자체 결정임 | violating use case 추가 → ArchUnit forbidden import 실패 확인 | `locally-verified` |","scope":"검증해야 할 주장","source_surface":"SURF-4D0C2AF6ED5AD8027D57","subject":"application use case"},{"assertion_id":"CTV-8","condition":"needs-confirmation 상태","line_end":203,"line_start":203,"modality":"unknown","object":"의도한 package/path에만 적용된다","predicate":"validates","quote":"| MapStruct generated mapper exemption이 의도한 package/path에만 적용된다 | generated source path가 build tool 설정에 따라 달라질 수 있음 | MapStruct sample mapper 추가 → generated path 확인 → exemption 외 경로 import 시 실패 확인 | `needs-confirmation` |","scope":"검증해야 할 주장","source_surface":"SURF-4D0C2AF6ED5AD8027D57","subject":"MapStruct generated mapper exemption"},{"assertion_id":"CTV-9","condition":"unconditional","line_end":204,"line_start":204,"modality":"observed","object":"ArchUnit으로 잡히지 않는다 (false negative)","predicate":"has_failure_behavior","quote":"| runtime lookup 우회는 ArchUnit으로 잡히지 않는다 | ArchUnit은 bytecode/static dependency 중심이므로 false negative 가능 | `ApplicationContext#getBean` 우회 코드 추가 → ArchUnit false-pass 확인 → manual/Sonar 보완 항목 등록 | `actually-implemented` (2026-05-28 round 2) — D11 `application_does_not_depend_on_application_context` ArchUnit rule 작성. `ApplicationContextDependentFixture` negative test 가 catch 동작 commit 보증. `getBean(String)` string-key bypass 와 `Class.forName(String)` 은 여전히 ArchUnit 정적 분석 범위 밖 (D12) — `application-core/CLAUDE.md` forbidden 섹션에 명시. |","scope":"검증해야 할 주장","source_surface":"SURF-4D0C2AF6ED5AD8027D57","subject":"runtime lookup 우회"},{"assertion_id":"DEM-1","condition":"runtime lookup/reflection 우회는 별도 보완 필요","line_end":177,"line_start":177,"modality":"must","object":"architecture test","predicate":"enforces","quote":"| D1 | CA 경계는 architecture test로 강제 | `raw/official-docs/governance-archunit-official.md#AU-OFF-C1`, `raw/official-docs/governance-archunit-official.md#AU-OFF-C2`, `raw/official-docs/archunit-conditional-on-property-3-layer-pattern.md#AUCP-C5` | `official-vendor-doc + engineering-blog` | ArchUnit은 정적 검사만 가능. runtime lookup / reflection 우회는 별도 보완 필요 |","scope":"결정-근거 매핑","source_surface":"SURF-E27CD9C37785E3562B11","subject":"CA 경계 (D1)"},{"assertion_id":"DEM-10","condition":"FreezingArchRule baseline은 needs-confirmation","line_end":186,"line_start":186,"modality":"must","object":"JUnit 기반 test 실행","predicate":"uses","quote":"| D10 | ArchUnit rule은 JUnit 기반 test로 실행 | `raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C6`, `raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C5` | `official-vendor-doc` | FreezingArchRule baseline 정책은 별도 claim 보강 전까지 `needs-confirmation`으로 둠 |","scope":"결정-근거 매핑","source_surface":"SURF-E27CD9C37785E3562B11","subject":"ArchUnit rule (D10)"},{"assertion_id":"DEM-11","condition":"banned-class ArchUnit rule","line_end":187,"line_start":187,"modality":"must_not","object":"org.springframework.context.ApplicationContext 의존 (getBean(Class<T>)까지 catch)","predicate":"forbids","quote":"| D11 | `application-core` 가 `org.springframework.context.ApplicationContext` 자체에 의존 금지 (banned-class ArchUnit rule). class-literal 기반 `getBean(Class<T>)` 호출까지는 catch 가능 | `raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C5` (`JavaMethodCall` / `JavaConstructorCall` bytecode 기반 access analysis) | `official-vendor-doc` | string-key bean lookup (`getBean(String)`) 과 `Class.forName(String)` 의 string target 은 ArchUnit 이 catch 불가 — D12 의 code review checklist 로 보완 |","scope":"결정-근거 매핑","source_surface":"SURF-E27CD9C37785E3562B11","subject":"application-core (D11)"},{"assertion_id":"DEM-12","condition":"unconditional","line_end":188,"line_start":188,"modality":"observed","object":"string-key getBean(String)/Class.forName(String)/getBeansOfType는 ArchUnit static analysis 한계 — code review checklist로 보완","predicate":"has_failure_behavior","quote":"| D12 | string-key bean lookup / `Class.forName(String)` / `BeanFactory#getBeansOfType` 의 reflection-style bypass 는 ArchUnit static analysis 의 한계 — code review checklist 항목으로 보완 | `raw/official-docs/archunit-user-guide.md` (§6.2 \"accesses ... bytecode offers all this information\" — bytecode 가 string content 자체를 노출하지 않음) | `official-vendor-doc` (negative claim — ArchUnit 이 못 잡는다는 사실의 공식 근거) | runtime container 의존성 그래프 검증 도구 (Spring Boot Actuator `/beans`, Spring Modulith verifier) 도입은 후속 검토 — D1 Open Risk 구체화 |","scope":"결정-근거 매핑","source_surface":"SURF-E27CD9C37785E3562B11","subject":"reflection-style bypass (D12)"},{"assertion_id":"DEM-2","condition":"company-tech-blog 사례를 공식 표준으로 승격 금지","line_end":178,"line_start":178,"modality":"must","object":"Gradle multi-module boundary","predicate":"maps_to","quote":"| D2 | package rule은 Gradle multi-module boundary 기준 | `raw/company-tech-blogs/woowahan-hexagonal-multimodule.md#WW-HEX-C1`, `raw/company-tech-blogs/modulith-kakaobank-techblog-2025.md#KAKAOBANK-MOD-C4`, `raw/branch-notes/feature-skeleton-package-blueprint-contract.md` | `company-case-study + project-decision` | company-tech-blog 사례를 공식 표준으로 승격 금지. ca-tmpl build graph로 별도 검증 필요 |","scope":"결정-근거 매핑","source_surface":"SURF-E27CD9C37785E3562B11","subject":"package rule (D2)"},{"assertion_id":"DEM-3","condition":"UNSUPPORTED_DECISION(CONTRARY) — Buckpal은 domain에 lombok.. allowlist, ca-tmpl 자체 stricter 결정","line_end":179,"line_start":179,"modality":"must_not","object":"forbidden import (Lombok 포함)","predicate":"forbids","quote":"| D3 | `domain-core` forbidden import rule (Lombok 포함) | `raw/company-tech-blogs/woowahan-hexagonal-multimodule.md#WW-HEX-C1`, `raw/official-docs/arch-clean-architecture-uncle-bob.md`, `raw/official-docs/lombok-builder-data-features-official.md#LMB-C1`, `raw/official-docs/lombok-builder-data-features-official.md#LMB-C2`, `raw/official-docs/lombok-builder-data-features-official.md#LMB-C4`, `raw/official-docs/lombok-builder-data-features-official.md#LMB-C5` / **CONTRARY EVIDENCE**: `raw/company-tech-blogs/buckpal-archunit-lombok-allowlist-direct-transactional.md#BUCKPAL-LOMBOK-C1`, `#BUCKPAL-LOMBOK-C2` (Buckpal hex-arch 공식 reference 가 domain purity rule 에서 `lombok..` 명시 allowlist — 정반대 방향) | `company-case-study + engineering-blog + official-vendor-doc` + **UNSUPPORTED_DECISION (CONTRARY)** | LMB-C1~C5 는 `@Builder`/`@Data` 가 무엇을 생성하는지만 증명. **Lombok 금지 자체는 OSS best practice 가 아님** — Buckpal (Hombergs 책 공식 예제, 2.5k stars) 이 domain 에 Lombok 을 명시 allowlist. ca-tmpl D3 는 bytecode 감각성 + framework 독립성을 위한 자체 stricter taste 결정. UNSUPPORTED_DECISION(CONTRARY) 라벨 |","scope":"결정-근거 매핑","source_surface":"SURF-E27CD9C37785E3562B11","subject":"domain-core (D3)"},{"assertion_id":"DEM-4","condition":"unconditional","line_end":180,"line_start":180,"modality":"must_not","object":"adapter-* / app-bootstrap 의존","predicate":"forbids","quote":"| D4 | `application-core` -> `adapter-*` / `app-bootstrap` 의존 금지 | `raw/company-tech-blogs/woowahan-hexagonal-multimodule.md#WW-HEX-C2`, `raw/company-tech-blogs/modulith-kakaobank-techblog-2025.md#KAKAOBANK-MOD-C4`, `raw/official-docs/arch-hexagonal-cockburn.md#HEX-COCKBURN-ORIG-C3` | `company-case-study + engineering-blog` | repository port가 아직 `domain/repository`에 남은 부분은 `feature-application-port-usecase-contract`와 동기화 필요 |","scope":"결정-근거 매핑","source_surface":"SURF-E27CD9C37785E3562B11","subject":"application-core (D4)"},{"assertion_id":"DEM-5","condition":"Spring Modulith 미사용, ArchUnit/package-private convention 필요","line_end":181,"line_start":181,"modality":"must_not","object":"adapter module 간 직접 의존","predicate":"forbids","quote":"| D5 | adapter module 간 직접 의존 금지 | `raw/company-tech-blogs/modulith-kakaobank-techblog-2025.md#KAKAOBANK-MOD-C2`, `raw/company-tech-blogs/modulith-kakaobank-techblog-2025.md#KAKAOBANK-MOD-C4` | `company-case-study` | Spring Modulith 없이 public API/named interface 강제는 약함. ArchUnit/package-private convention 필요 |","scope":"결정-근거 매핑","source_surface":"SURF-E27CD9C37785E3562B11","subject":"adapter module (D5)"},{"assertion_id":"DEM-6","condition":"unconditional","line_end":182,"line_start":182,"modality":"must_not","object":"business/domain concept","predicate":"forbids","quote":"| D6 | `shared-contract` business/domain concept 금지 | `raw/official-docs/feature-first-uncle-bob-screaming-architecture-2011.md#SCREAM-C1`, `raw/branch-notes/feature-skeleton-package-blueprint-contract.md` | `engineering-blog + project-decision` | shared module이 커질수록 common dumping ground가 될 위험 |","scope":"결정-근거 매핑","source_surface":"SURF-E27CD9C37785E3562B11","subject":"shared-contract (D6)"},{"assertion_id":"DEM-7","condition":"project-decision only, Gradle dependency rule + ArchUnit failure로 실증 필요","line_end":183,"line_start":183,"modality":"must_not","object":"production 역수입","predicate":"forbids","quote":"| D7 | `sample-portfolio` production 역수입 금지 | `raw/branch-notes/feature-skeleton-package-blueprint-contract.md` | `project-decision` | 외부 직접 근거는 약함. Gradle dependency rule + ArchUnit failure로 실증 필요 |","scope":"결정-근거 매핑","source_surface":"SURF-E27CD9C37785E3562B11","subject":"sample-portfolio (D7)"},{"assertion_id":"DEM-8","condition":"UNSUPPORTED_DECISION(CONTRARY) — Spring Modulith/Buckpal은 직접 부착, ca-tmpl 자체 stricter 결정","line_end":184,"line_start":184,"modality":"must_not","object":"@Transactional 직접 import","predicate":"forbids","quote":"| D8 | application `@Transactional` 직접 import 금지 | `raw/branch-notes/feature-application-port-usecase-contract.md`, `raw/official-docs/spring-tx-management-reference.md` / **CONTRARY EVIDENCE**: `raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-TX-C1` (Spring Modulith 공식 incubator 가 `@ApplicationModuleListener` 로 `@Transactional` 을 meta-annotation 재노출 — Spring 팀 방향과 정면 충돌), `raw/company-tech-blogs/buckpal-archunit-lombok-allowlist-direct-transactional.md#BUCKPAL-TX-C1` (hex-arch 공식 reference 가 `@Component @Transactional` 직접 부착) | `project-decision + official-vendor-doc` + **UNSUPPORTED_DECISION (CONTRARY)** | Spring 공식은 `@Transactional` 을 지원하고 Spring Modulith 는 meta-annotation 으로 재노출. Buckpal hex-arch reference 도 직접 부착. ca-tmpl D8 forbidden 정책은 **OSS best practice 가 아님** — multi-Gradle-module Hexagonal context 에서 application 모듈 순수성을 위한 자체 stricter 결정. UNSUPPORTED_DECISION(CONTRARY) 라벨 |","scope":"결정-근거 매핑","source_surface":"SURF-E27CD9C37785E3562B11","subject":"application (D8)"},{"assertion_id":"DEM-9","condition":"generated mapper 허용","line_end":185,"line_start":185,"modality":"may","object":"@Generated annotation 기반 ArchUnit predicate (javax.annotation.processing.Generated)","predicate":"uses","quote":"| D9 | MapStruct generated exemption은 `@Generated` annotation 기반 ArchUnit predicate 로 허용 | `raw/official-docs/mapstruct-generated-annotation-official.md#MS-ANNOT-C1`, `#MS-ANNOT-C2` (MapStruct `@Generated` annotation 부착 공식 + `suppressGeneratorTimestamp` 옵션) + `raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-AU-C1` (Spring Modulith 자체가 `annotatedWith(Generated.class)` predicate 를 production 코드에 사용 — DSL 패턴 OSS 검증) | `official-vendor-doc + company-case-study` | annotation FQN 주의: MapStruct = `javax.annotation.processing.Generated`, Spring AOT = `org.springframework.aot.generate.Generated` — ArchUnit predicate 는 MapStruct FQN 사용해야 함. 구현 권고: `.and().areNotAnnotatedWith(javax.annotation.processing.Generated.class)`. ca-tmpl 실제 generated path 검증은 sample mapper 추가 후 통합 테스트 (`needs-confirmation` from `planned` → `actually-implemented` 승급 가능) |","scope":"결정-근거 매핑","source_surface":"SURF-E27CD9C37785E3562B11","subject":"MapStruct generated exemption (D9)"},{"assertion_id":"EFD-1","condition":"skeleton 단계의 빈 module","line_end":245,"line_start":245,"modality":"must","object":"that() 매칭 대상 0개 → failed to check any classes 실패, allowEmptyShould(true)로 허용","predicate":"has_failure_behavior","quote":"- **Edge — empty anchor**: skeleton 단계의 빈 module 은 `that()` 매칭 대상이 0개라 ArchUnit 기본 동작상 `failed to check any classes` 로 실패한다. 의도된 빈 anchor rule 에 `allowEmptyShould(true)` 를 명시해 허용. 근거 사실: [[raw/errors/archunit-empty-should-anchor-2026-05-27]].","scope":"엣지·실패·의존","source_surface":"SURF-ECEC623B871983A58BB6","subject":"empty anchor rule"},{"assertion_id":"EFD-2","condition":"검사 대상 class가 @AnalyzeClasses import scope 밖이면","line_end":246,"line_start":246,"modality":"observed","object":"위반이 있어도 rule이 조용히 통과 (BUILD SUCCESSFUL, false-negative)","predicate":"has_failure_behavior","quote":"- **Failure — vacuous pass (import scope 누락)**: 검사 대상 class 가 `@AnalyzeClasses` import scope 밖이면 위반이 있어도 rule 이 *조용히* 통과(`BUILD SUCCESSFUL`, 에러 신호 없음)한다. `allowEmptyShould(true)` 도 이 false-negative 를 막지 못함. 보완: sample/fixture 를 test import scope 에 포함 + violations-as-data negative fixture(S1) 로 catch 동작을 commit 보증. 근거 사실: [[raw/errors/archunit-test-scope-sample-ticket-inclusion-2026-05-28]].","scope":"엣지·실패·의존","source_surface":"SURF-ECEC623B871983A58BB6","subject":"vacuous pass (import scope 누락)"},{"assertion_id":"EFD-3","condition":"string-key getBean(String)·Class.forName(String)·getBeansOfType","line_end":247,"line_start":247,"modality":"observed","object":"ArchUnit 정적 분석으로 잡을 수 없다 (보완 불가, code review checklist로만 보완)","predicate":"has_failure_behavior","quote":"- **Failure — D11/D12 string-key bypass (알려진 한계, 보완 불가)**: D11 banned-class rule 은 class-literal `getBean(Class<T>)` 까지만 catch 한다. string-key `getBean(String)` · `Class.forName(String)` · `BeanFactory#getBeansOfType` 같은 reflection-style bypass 는 bytecode 가 문자열 내용을 노출하지 않아 ArchUnit 정적 분석으로 **잡을 수 없다**(D12). `application-core/CLAUDE.md` forbidden 섹션의 code review checklist 로만 보완 — 자동 강제 장치 아님. runtime 검증(Actuator `/beans`, Modulith verifier)은 후속 후보(D1/D12 Open Risk).","scope":"엣지·실패·의존","source_surface":"SURF-ECEC623B871983A58BB6","subject":"D11/D12 string-key bypass"},{"assertion_id":"EFD-4","condition":"@Transactional import를 위해","line_end":248,"line_start":248,"modality":"must","object":"testCompileOnly 'org.springframework:spring-tx' (production 영향 없음)","predicate":"requires","quote":"- **Dependency — testCompileOnly**: `TransactionalAnnotatedFixture` 가 `@Transactional` 을 import 하기 위해 `app-bootstrap/build.gradle` 에 `testCompileOnly 'org.springframework:spring-tx'` 추가(production 영향 없음). 관련 class-loading 이슈: [[raw/errors/archunit-testcompileonly-class-loading-2026-06-02]].","scope":"엣지·실패·의존","source_surface":"SURF-ECEC623B871983A58BB6","subject":"TransactionalAnnotatedFixture (app-bootstrap build)"},{"assertion_id":"EFD-5","condition":"sandbox 기본 권한","line_end":249,"line_start":249,"modality":"observed","object":"~/.gradle lock 파일 생성 실패 → escalated 실행으로 해결","predicate":"has_failure_behavior","quote":"- **Dependency — sandbox/Gradle**: Gradle wrapper 가 sandbox 기본 권한에서 `~/.gradle` lock 파일 생성 실패 → escalated 실행으로 해결. 근거: [[raw/errors/gradle-wrapper-readonly-cache-2026-05-28]].","scope":"엣지·실패·의존","source_surface":"SURF-ECEC623B871983A58BB6","subject":"Gradle wrapper"},{"assertion_id":"EFD-6","condition":"ca-tmpl에 실제 MapStruct mapper 아직 없음","line_end":250,"line_start":250,"modality":"unknown","object":"needs-confirmation — sample mapper 추가 후 통합 검증 필요","predicate":"other","quote":"- **Edge — MapStruct exemption (미검증)**: D9 generated mapper `@Generated` exemption 은 ca-tmpl 에 실제 MapStruct mapper 가 아직 없어 `needs-confirmation` — sample mapper 추가 후 통합 검증 필요.","scope":"엣지·실패·의존","source_surface":"SURF-ECEC623B871983A58BB6","subject":"MapStruct generated mapper @Generated exemption (D9)"},{"assertion_id":"IMP-1","condition":"본 branch는 2026-05-28 이미 구현·검증 완료","line_end":225,"line_start":225,"modality":"observed","object":"사후 정제 — ground-truth(@db61075)와 대조해 실제 구현 위치를 역명세","predicate":"other","quote":"> ⚠️ 이 명세는 *사후 정제* 다 — 본 branch 는 2026-05-28 시점에 이미 구현·검증 완료(§진행 중 메모)되었고, 본 section 은 ground-truth(`@db61075`) 와 대조해 실제 구현 위치를 역으로 명세화한 것이다.","scope":"구현 가이드","source_surface":"SURF-4F04B9B0EA7232527290","subject":"구현 가이드 명세"},{"assertion_id":"IMP-10","condition":"fixture가 src/test/ 위치라 main DoNotIncludeTests 분석 제외","line_end":237,"line_start":237,"modality":"observed","object":"ArchitectureViolationFixtureTest + architecture/violations/ package (rule.evaluate(...).hasViolation()==true, vacuous pass 방지)","predicate":"validates","quote":"| S1 (violations-as-data) | `ArchitectureViolationFixtureTest` + `architecture/violations/` package | 본 branch fixture: `SpringDependentDomainFixture`(D3), `ApplicationContextDependentFixture`(D11), `TransactionalAnnotatedFixture`. 각 test 가 `rule.evaluate(VIOLATION_CLASSES).hasViolation()==true` assert. fixture 가 `src/test/` 위치라 main `DoNotIncludeTests` 분석 제외 → vacuous pass 방지 | S1 / SPRING-MOD-AU-C2 |","scope":"구현 가이드","source_surface":"SURF-4F04B9B0EA7232527290","subject":"S1 (violations-as-data)"},{"assertion_id":"IMP-11","condition":"R3 정제, 본 §에 남기지 않음","line_end":239,"line_start":239,"modality":"observed","object":"각각 다른 branch 소유; @Transactional ban rule(application_does_not_use_spring_transactional_annotation) SSOT는 feature-application-port-usecase-contract D3","predicate":"owns","quote":"> **OUT_OF_BRANCH_SCOPE (R3)**: ground-truth `CleanArchitectureTest` 의 boundary-validation(B1/B2/B4/B5/B6/B7, D5 ProblemDetail), streaming(D3 SSE/WebSocket), serialization(BigDecimal), resource-identifier(D17 no_long_id_pk 등), api-contract(D19 AIP-122), business-rule-validation(C1/D1 jakarta.validation) rule 들은 *각각 다른 branch* 소유다. 본 §에는 남기지 않으며 해당 branch ingest 에서 명세한다. @Transactional ban rule(`application_does_not_use_spring_transactional_annotation`)은 코드 attribution 상 [[raw/branch-notes/feature-application-port-usecase-contract]] D3 소유지만 본 branch 테스트 계약에도 포함되어 red/green 확인됨 — SSOT 는 그 branch.","scope":"구현 가이드","source_surface":"SURF-4F04B9B0EA7232527290","subject":"OUT_OF_BRANCH_SCOPE rules (boundary-validation/streaming/serialization 등)"},{"assertion_id":"IMP-2","condition":"base package 선택은 UNSUPPORTED_IMPL_DECISION","line_end":229,"line_start":229,"modality":"observed","object":"app-bootstrap/.../architecture/CleanArchitectureTest.java (@AnalyzeClasses packages=\"dev.caskeleton\", DoNotIncludeTests)","predicate":"maps_to","quote":"| D1 (test 강제) | `app-bootstrap/.../architecture/CleanArchitectureTest.java` | `@AnalyzeClasses(packages = \"dev.caskeleton\", importOptions = DoNotIncludeTests.class)` + `@ArchTest static final ArchRule` 필드들. `@AnalyzeClasses` import scope 선택은 `UNSUPPORTED_IMPL_DECISION` — ARCHUNIT-UG-C6 는 JUnit 통합만 권고하고 base package 선택은 프로젝트 trade-off (`dev.caskeleton` root 단일 scan 으로 결정) | D1 / ARCHUNIT-UG-C5,C6 |","scope":"구현 가이드","source_surface":"SURF-4F04B9B0EA7232527290","subject":"D1 (test 강제)"},{"assertion_id":"IMP-3","condition":"matrix 구체값은 UNSUPPORTED_IMPL_DECISION","line_end":230,"line_start":230,"modality":"observed","object":"src/build.gradle:53 verifyCleanArchitectureDependencies task (allowedProjectDependencies 화이트리스트)","predicate":"maps_to","quote":"| D2 (build-graph) | `src/build.gradle:53` `verifyCleanArchitectureDependencies` task | `allowedProjectDependencies` Map<String,Set<String>> 화이트리스트 9 module + `configurations(api/implementation/compileOnly/runtimeOnly).dependencies.withType(ProjectDependency)` 차집합 → `GradleException`. matrix 구체 값은 `UNSUPPORTED_IMPL_DECISION` — blueprint 결정([[raw/branch-notes/feature-skeleton-package-blueprint-contract]])의 module 목록에서 도출한 trade-off | D2 / WW-HEX-C1, KAKAOBANK-MOD-C4 |","scope":"구현 가이드","source_surface":"SURF-4F04B9B0EA7232527290","subject":"D2 (build-graph)"},{"assertion_id":"IMP-4","condition":"CONTRARY: Buckpal은 lombok.. allowlist (D3 Open Risk)","line_end":231,"line_start":231,"modality":"observed","object":"domain_is_pure rule (..domain.. → lombok.. 등 금지, allowEmptyShould(true))","predicate":"maps_to","quote":"| D3 (domain purity + Lombok) | `domain_is_pure` rule | `noClasses().that().resideInAPackage(\"..domain..\").should().dependOnClassesThat().resideInAnyPackage(..., \"lombok..\", ...)` + `.allowEmptyShould(true)`. forbidden package 목록 구체값은 `UNSUPPORTED_IMPL_DECISION` (CONTRARY: Buckpal 은 `lombok..` allowlist — D3 Open Risk) | D3 / LMB-C1~C5, ARCHUNIT-UG-C4 |","scope":"구현 가이드","source_surface":"SURF-4F04B9B0EA7232527290","subject":"D3 (domain purity + Lombok)"},{"assertion_id":"IMP-5","condition":"unconditional","line_end":232,"line_start":232,"modality":"observed","object":"application_does_not_depend_on_adapters_or_transport rule","predicate":"maps_to","quote":"| D4 (application 격리) | `application_does_not_depend_on_adapters_or_transport` rule | `noClasses().that().resideInAPackage(\"..application..\").should().dependOnClassesThat().resideInAnyPackage(\"..adapter..\",\"..bootstrap..\",\"org.springframework.web..\",...)` | D4 / WW-HEX-C2, HEX-COCKBURN-ORIG-C3 |","scope":"구현 가이드","source_surface":"SURF-4F04B9B0EA7232527290","subject":"D4 (application 격리)"},{"assertion_id":"IMP-6","condition":"unconditional","line_end":233,"line_start":233,"modality":"observed","object":"web_/persistence_/outbound_adapter_does_not_depend_on_* 3 rule (package glob, Modulith named interface 미사용)","predicate":"maps_to","quote":"| D5 (adapter-adapter 격리) | `web_/persistence_/outbound_adapter_does_not_depend_on_*` 3 rule | 각 adapter package 가 sibling adapter package 에 의존 금지. ArchUnit package glob 으로 구현 (Spring Modulith named interface 미사용 — D5 Open Risk) | D5 / KAKAOBANK-MOD-C2,C4 |","scope":"구현 가이드","source_surface":"SURF-4F04B9B0EA7232527290","subject":"D5 (adapter-adapter 격리)"},{"assertion_id":"IMP-7","condition":"unconditional","line_end":234,"line_start":234,"modality":"observed","object":"shared_contract_contains_only_operational_contract_packages rule (allowlist는 UNSUPPORTED_IMPL_DECISION)","predicate":"maps_to","quote":"| D6 (shared scope) | `shared_contract_contains_only_operational_contract_packages` rule | `classes().that().resideInAPackage(\"..shared..\").should().resideInAnyPackage(<operational allowlist>)`. allowlist package 집합은 `UNSUPPORTED_IMPL_DECISION` — blueprint 의 operational contract 목록에서 도출 | D6 / SCREAM-C1 |","scope":"구현 가이드","source_surface":"SURF-4F04B9B0EA7232527290","subject":"D6 (shared scope)"},{"assertion_id":"IMP-8","condition":"unconditional","line_end":235,"line_start":235,"modality":"observed","object":"production_code_does_not_depend_on_sample_portfolio rule","predicate":"maps_to","quote":"| D7 (sample 역수입 금지) | `production_code_does_not_depend_on_sample_portfolio` rule | `noClasses().that().resideOutsideOfPackage(\"..sample.portfolio..\").should().dependOnClassesThat().resideInAPackage(\"..sample.portfolio..\")` | D7 (project-decision) |","scope":"구현 가이드","source_surface":"SURF-4F04B9B0EA7232527290","subject":"D7 (sample 역수입 금지)"},{"assertion_id":"IMP-9","condition":"unconditional","line_end":236,"line_start":236,"modality":"observed","object":"application_does_not_depend_on_application_context rule (FQN haveFullyQualifiedName, getBean(Class)까지 catch)","predicate":"maps_to","quote":"| D11 (ApplicationContext banned-class) | `application_does_not_depend_on_application_context` rule | `noClasses().that().resideInAPackage(\"..application..\").should().dependOnClassesThat().haveFullyQualifiedName(\"org.springframework.context.ApplicationContext\")`. FQN 단일 class 선택은 bytecode access analysis(ARCHUNIT-UG-C5)로 `getBean(Class)` 까지만 catch | D11 / ARCHUNIT-UG-C5 |","scope":"구현 가이드","source_surface":"SURF-4F04B9B0EA7232527290","subject":"D11 (ApplicationContext banned-class)"},{"assertion_id":"SCP-1","condition":"포함 범위","line_end":82,"line_start":82,"modality":"must","object":"Gradle multi-module dependency rule","predicate":"owns","quote":"- Gradle multi-module dependency rule.","scope":"범위","source_surface":"SURF-44485F1BD98A46F9D016","subject":"feature-architecture-enforcement-rules branch"},{"assertion_id":"SCP-10","condition":"제외 범위 (out of scope)","line_end":94,"line_start":94,"modality":"must_not","object":"formatter / style lint 규칙 (제외)","predicate":"other","quote":"- formatter / style lint 규칙.","scope":"범위","source_surface":"SURF-44485F1BD98A46F9D016","subject":"feature-architecture-enforcement-rules branch"},{"assertion_id":"SCP-11","condition":"제외 범위 (out of scope)","line_end":96,"line_start":96,"modality":"must_not","object":"Spring Modulith verifier 도입 (제외)","predicate":"other","quote":"- Spring Modulith verifier 도입.","scope":"범위","source_surface":"SURF-44485F1BD98A46F9D016","subject":"feature-architecture-enforcement-rules branch"},{"assertion_id":"SCP-12","condition":"제외 범위 (out of scope)","line_end":98,"line_start":98,"modality":"must_not","object":"CI workflow job 분리 구현; CI 실행 시점은 feature-ci-quality-gates-contract에서 최종화","predicate":"delegates","quote":"- CI workflow job 분리 구현. CI 실행 시점은 `feature-ci-quality-gates-contract`에서 최종화.","scope":"범위","source_surface":"SURF-44485F1BD98A46F9D016","subject":"feature-architecture-enforcement-rules branch"},{"assertion_id":"SCP-2","condition":"포함 범위","line_end":83,"line_start":83,"modality":"must_not","object":"framework import","predicate":"forbids","quote":"- `domain-core` framework import 금지.","scope":"범위","source_surface":"SURF-44485F1BD98A46F9D016","subject":"domain-core"},{"assertion_id":"SCP-3","condition":"포함 범위","line_end":84,"line_start":84,"modality":"must_not","object":"adapter-* / app-bootstrap 의존","predicate":"forbids","quote":"- `application-core` -> `adapter-*` / `app-bootstrap` 의존 금지.","scope":"범위","source_surface":"SURF-44485F1BD98A46F9D016","subject":"application-core"},{"assertion_id":"SCP-4","condition":"포함 범위","line_end":85,"line_start":85,"modality":"must_not","object":"adapter module 간 직접 의존","predicate":"forbids","quote":"- adapter module 간 직접 의존 금지.","scope":"범위","source_surface":"SURF-44485F1BD98A46F9D016","subject":"adapter module"},{"assertion_id":"SCP-5","condition":"포함 범위","line_end":86,"line_start":86,"modality":"must_not","object":"business/domain concept 오염","predicate":"forbids","quote":"- `shared-contract` business/domain concept 오염 방지.","scope":"범위","source_surface":"SURF-44485F1BD98A46F9D016","subject":"shared-contract"},{"assertion_id":"SCP-6","condition":"포함 범위","line_end":87,"line_start":87,"modality":"must_not","object":"production 역수입","predicate":"forbids","quote":"- `sample-portfolio` production 역수입 금지.","scope":"범위","source_surface":"SURF-44485F1BD98A46F9D016","subject":"sample-portfolio"},{"assertion_id":"SCP-7","condition":"포함 범위","line_end":88,"line_start":88,"modality":"must","object":"mapper boundary rule","predicate":"owns","quote":"- mapper boundary rule.","scope":"범위","source_surface":"SURF-44485F1BD98A46F9D016","subject":"feature-architecture-enforcement-rules branch"},{"assertion_id":"SCP-8","condition":"포함 범위","line_end":89,"line_start":89,"modality":"must","object":"transaction annotation forbidden import rule","predicate":"owns","quote":"- transaction annotation forbidden import rule.","scope":"범위","source_surface":"SURF-44485F1BD98A46F9D016","subject":"feature-architecture-enforcement-rules branch"},{"assertion_id":"SCP-9","condition":"포함 범위","line_end":90,"line_start":90,"modality":"must","object":"ArchUnit rule 위치와 실행 기준","predicate":"owns","quote":"- ArchUnit rule 위치와 실행 기준.","scope":"범위","source_surface":"SURF-44485F1BD98A46F9D016","subject":"feature-architecture-enforcement-rules branch"}],"candidate_manifest_sha256":"2c9f830b236bf7f43bcdf2e1b6a30b40962c339edb903f78001d90b0f2c3be16","candidates":[{"assertion_a":"BCP-4","assertion_b":"BCP-5","candidate_id":"SEM-DE1049E25FEA9972D7CD","grouping_key":{"condition":"상속한 프로젝트 결정, Work Item 완료 조건에 적용","predicate":"","scope":"상속한 프로젝트 결정","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-1","assertion_b":"CTV-11","candidate_id":"SEM-817590C751AFB8FA7C66","grouping_key":{"condition":"","predicate":"","scope":"검증해야 할 주장","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-1","assertion_b":"CTV-2","candidate_id":"SEM-C27305268452A6272D1E","grouping_key":{"condition":"","predicate":"has_failure_behavior","scope":"검증해야 할 주장","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-1","assertion_b":"CTV-3","candidate_id":"SEM-0B141BC033D26D55B894","grouping_key":{"condition":"","predicate":"has_failure_behavior","scope":"검증해야 할 주장","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-1","assertion_b":"CTV-9","candidate_id":"SEM-7450C6C295F096E0447B","grouping_key":{"condition":"","predicate":"has_failure_behavior","scope":"검증해야 할 주장","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-1","assertion_b":"DEM-3","candidate_id":"SEM-29FBDB156B5DDD22BFA7","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-1","assertion_b":"DEM-9","candidate_id":"SEM-86808837B57D6CA358FC","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-1","assertion_b":"IMP-10","candidate_id":"SEM-8CCD41B27D8DBB2DEFE6","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-1","assertion_b":"IMP-4","candidate_id":"SEM-712416434B6C87D6A0F5","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-1","assertion_b":"SCP-2","candidate_id":"SEM-90A0B27B6912B41A98FD","grouping_key":{"condition":"","predicate":"","scope":"","subject":"domain-core"},"rule_ids":["C6"]},{"assertion_a":"CTV-11","assertion_b":"CTV-2","candidate_id":"SEM-47B20DE299F059E210D9","grouping_key":{"condition":"","predicate":"","scope":"검증해야 할 주장","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-11","assertion_b":"CTV-3","candidate_id":"SEM-FA7BF94A8ADEAFC00CA2","grouping_key":{"condition":"","predicate":"","scope":"검증해야 할 주장","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-11","assertion_b":"CTV-9","candidate_id":"SEM-45F370A8715B1575F8E0","grouping_key":{"condition":"unconditional","predicate":"","scope":"검증해야 할 주장","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-11","assertion_b":"DEM-9","candidate_id":"SEM-B409C61E12294C95C3D2","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-11","assertion_b":"IMP-10","candidate_id":"SEM-CED8F23A2E173911F6E9","grouping_key":{"condition":"","predicate":"validates","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-2","assertion_b":"CTV-3","candidate_id":"SEM-37C6062D6C717286AF20","grouping_key":{"condition":"","predicate":"has_failure_behavior","scope":"검증해야 할 주장","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-2","assertion_b":"CTV-9","candidate_id":"SEM-4B084B37745109E61472","grouping_key":{"condition":"","predicate":"has_failure_behavior","scope":"검증해야 할 주장","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-2","assertion_b":"DEM-11","candidate_id":"SEM-B79DCD1EFE8160C2C14B","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-2","assertion_b":"DEM-4","candidate_id":"SEM-D56DB164470CFC4FC40A","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-2","assertion_b":"DEM-9","candidate_id":"SEM-2EDCF97E3EFF0B3A68AB","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-2","assertion_b":"SCP-3","candidate_id":"SEM-3CC9785FF88B3357DAAB","grouping_key":{"condition":"","predicate":"","scope":"","subject":"application-core"},"rule_ids":["C6"]},{"assertion_a":"CTV-3","assertion_b":"CTV-6","candidate_id":"SEM-B9250D666A14D0829BDE","grouping_key":{"condition":"","predicate":"has_failure_behavior","scope":"검증해야 할 주장","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-3","assertion_b":"CTV-9","candidate_id":"SEM-099F4442F418FD514F7A","grouping_key":{"condition":"","predicate":"has_failure_behavior","scope":"검증해야 할 주장","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-3","assertion_b":"DEM-9","candidate_id":"SEM-13963285792338C6A941","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-4","assertion_b":"CTV-5","candidate_id":"SEM-3D700BCEFD5E6A112B88","grouping_key":{"condition":"","predicate":"has_failure_behavior","scope":"검증해야 할 주장","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-4","assertion_b":"CTV-6","candidate_id":"SEM-128F4DF428A5B061DB9D","grouping_key":{"condition":"","predicate":"has_failure_behavior","scope":"검증해야 할 주장","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-4","assertion_b":"CTV-7","candidate_id":"SEM-1849C5FE6697656A0599","grouping_key":{"condition":"","predicate":"has_failure_behavior","scope":"검증해야 할 주장","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-4","assertion_b":"DEM-6","candidate_id":"SEM-34354D53E2D1721FFAD4","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-4","assertion_b":"SCP-5","candidate_id":"SEM-30D235F579528551AE46","grouping_key":{"condition":"","predicate":"","scope":"","subject":"shared-contract"},"rule_ids":["C6"]},{"assertion_a":"CTV-5","assertion_b":"CTV-6","candidate_id":"SEM-F1CE4F3C3B3A3EBB0352","grouping_key":{"condition":"","predicate":"has_failure_behavior","scope":"검증해야 할 주장","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-5","assertion_b":"CTV-7","candidate_id":"SEM-4D2BD38E51BF35C573CE","grouping_key":{"condition":"","predicate":"has_failure_behavior","scope":"검증해야 할 주장","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-5","assertion_b":"DEM-7","candidate_id":"SEM-F8D2B3946CEE1B51E7EA","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-5","assertion_b":"SCP-6","candidate_id":"SEM-E4FA4996A0C477D81124","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-6","assertion_b":"CTV-7","candidate_id":"SEM-811B077C6D3612FC57D8","grouping_key":{"condition":"","predicate":"has_failure_behavior","scope":"검증해야 할 주장","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-7","assertion_b":"DEM-8","candidate_id":"SEM-CB1A0C8EA34D4FD6CBBF","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-7","assertion_b":"EFD-4","candidate_id":"SEM-DB71875B38E9AB74639A","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-8","assertion_b":"DEM-10","candidate_id":"SEM-647A84FFD40AE80DCC34","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-8","assertion_b":"DEM-9","candidate_id":"SEM-E899294897D32D957D34","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-8","assertion_b":"EFD-6","candidate_id":"SEM-5A16157B541EF040EA43","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-9","assertion_b":"DEM-11","candidate_id":"SEM-965DBAD9D2835571DA4E","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-9","assertion_b":"DEM-12","candidate_id":"SEM-EE1221F4DEB291F4EE12","grouping_key":{"condition":"unconditional","predicate":"has_failure_behavior","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-9","assertion_b":"DEM-9","candidate_id":"SEM-38EAC7CF8E51F917C5F1","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-9","assertion_b":"EFD-3","candidate_id":"SEM-5CF0CE4475AF47217CE0","grouping_key":{"condition":"","predicate":"has_failure_behavior","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-9","assertion_b":"IMP-10","candidate_id":"SEM-135628D138D6E8490AAE","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"CTV-9","assertion_b":"IMP-9","candidate_id":"SEM-F0DA3E4109A635E4876E","grouping_key":{"condition":"unconditional","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-10","assertion_b":"DEM-11","candidate_id":"SEM-D01F125598987E9142D0","grouping_key":{"condition":"","predicate":"","scope":"결정-근거 매핑","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-10","assertion_b":"DEM-12","candidate_id":"SEM-BA8AAEC69F9DA8DCDEF6","grouping_key":{"condition":"","predicate":"","scope":"결정-근거 매핑","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-10","assertion_b":"DEM-9","candidate_id":"SEM-523C652399B54198C75B","grouping_key":{"condition":"","predicate":"uses","scope":"결정-근거 매핑","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-10","assertion_b":"EFD-6","candidate_id":"SEM-AB6EC53006EAE5675982","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-11","assertion_b":"DEM-12","candidate_id":"SEM-F42A3F79C187FC2B5E3F","grouping_key":{"condition":"","predicate":"","scope":"결정-근거 매핑","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-11","assertion_b":"DEM-4","candidate_id":"SEM-0F8B5817FBA5D6206094","grouping_key":{"condition":"","predicate":"forbids","scope":"결정-근거 매핑","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-11","assertion_b":"EFD-3","candidate_id":"SEM-BAE3A7B844CB87D2CF0D","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-11","assertion_b":"SCP-3","candidate_id":"SEM-8742AED277D1C151958F","grouping_key":{"condition":"","predicate":"forbids","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-12","assertion_b":"EFD-3","candidate_id":"SEM-6DC92FCA890C69A5A730","grouping_key":{"condition":"","predicate":"has_failure_behavior","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-2","assertion_b":"DEM-3","candidate_id":"SEM-01C2FC2285CC05C13B90","grouping_key":{"condition":"","predicate":"","scope":"결정-근거 매핑","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-2","assertion_b":"DEM-4","candidate_id":"SEM-80134AF1F188B001E220","grouping_key":{"condition":"","predicate":"","scope":"결정-근거 매핑","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-2","assertion_b":"DEM-5","candidate_id":"SEM-BABED1B4D5A3F9606959","grouping_key":{"condition":"","predicate":"","scope":"결정-근거 매핑","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-2","assertion_b":"DEM-6","candidate_id":"SEM-DE289D6771A86DE38727","grouping_key":{"condition":"","predicate":"","scope":"결정-근거 매핑","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-2","assertion_b":"DEM-7","candidate_id":"SEM-A8BA53F8D2FDE3CE221F","grouping_key":{"condition":"","predicate":"","scope":"결정-근거 매핑","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-3","assertion_b":"IMP-4","candidate_id":"SEM-54022D45B4C258D9334B","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-3","assertion_b":"SCP-2","candidate_id":"SEM-C97E0DE80FAEFF430B9A","grouping_key":{"condition":"","predicate":"forbids","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-4","assertion_b":"DEM-5","candidate_id":"SEM-A6D5CDE839C52232AA04","grouping_key":{"condition":"","predicate":"forbids","scope":"결정-근거 매핑","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-4","assertion_b":"SCP-3","candidate_id":"SEM-99141C4C957B575A6FA4","grouping_key":{"condition":"","predicate":"forbids","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-6","assertion_b":"DEM-7","candidate_id":"SEM-3F83BFB66A2F09068A95","grouping_key":{"condition":"","predicate":"forbids","scope":"결정-근거 매핑","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-6","assertion_b":"SCP-5","candidate_id":"SEM-4180108281E0D8003F7D","grouping_key":{"condition":"","predicate":"forbids","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-7","assertion_b":"SCP-6","candidate_id":"SEM-263557E7460539797EC3","grouping_key":{"condition":"","predicate":"forbids","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-8","assertion_b":"EFD-4","candidate_id":"SEM-0C8B7CBB94CCBAB7A97F","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"DEM-9","assertion_b":"EFD-6","candidate_id":"SEM-32CA9186E754C1608455","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"EFD-1","assertion_b":"EFD-2","candidate_id":"SEM-36C1C58278C72057DF3E","grouping_key":{"condition":"","predicate":"has_failure_behavior","scope":"엣지·실패·의존","subject":""},"rule_ids":["C6"]},{"assertion_a":"EFD-2","assertion_b":"IMP-2","candidate_id":"SEM-D95457DBBFE1C4E114C9","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"EFD-4","assertion_b":"IMP-10","candidate_id":"SEM-092CCE2A43246D196511","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"IMP-2","assertion_b":"IMP-3","candidate_id":"SEM-38882C2C3E21B9AF8C87","grouping_key":{"condition":"","predicate":"maps_to","scope":"구현 가이드","subject":""},"rule_ids":["C6"]},{"assertion_a":"IMP-2","assertion_b":"IMP-4","candidate_id":"SEM-01AF436C625EB63BF75F","grouping_key":{"condition":"","predicate":"maps_to","scope":"구현 가이드","subject":""},"rule_ids":["C6"]},{"assertion_a":"IMP-2","assertion_b":"IMP-7","candidate_id":"SEM-D84C59D959DDE399E8BA","grouping_key":{"condition":"","predicate":"maps_to","scope":"구현 가이드","subject":""},"rule_ids":["C6"]},{"assertion_a":"IMP-3","assertion_b":"IMP-4","candidate_id":"SEM-C1B7FF32816A3F6F4A0D","grouping_key":{"condition":"","predicate":"maps_to","scope":"구현 가이드","subject":""},"rule_ids":["C6"]},{"assertion_a":"IMP-3","assertion_b":"IMP-7","candidate_id":"SEM-624093697CBC9023DC74","grouping_key":{"condition":"","predicate":"maps_to","scope":"구현 가이드","subject":""},"rule_ids":["C6"]},{"assertion_a":"IMP-4","assertion_b":"IMP-7","candidate_id":"SEM-409286BCEB09BB5054F4","grouping_key":{"condition":"","predicate":"maps_to","scope":"구현 가이드","subject":""},"rule_ids":["C6"]},{"assertion_a":"SCP-1","assertion_b":"SCP-7","candidate_id":"SEM-570DF9848B4AB111FDD1","grouping_key":{"condition":"포함 범위","predicate":"owns","scope":"범위","subject":"feature-architecture-enforcement-rules branch"},"rule_ids":["BASE"]},{"assertion_a":"SCP-1","assertion_b":"SCP-8","candidate_id":"SEM-CE1F3F3E01F0F7EC9E7B","grouping_key":{"condition":"포함 범위","predicate":"owns","scope":"범위","subject":"feature-architecture-enforcement-rules branch"},"rule_ids":["BASE"]},{"assertion_a":"SCP-1","assertion_b":"SCP-9","candidate_id":"SEM-D0481D9EE28232FD419C","grouping_key":{"condition":"포함 범위","predicate":"owns","scope":"범위","subject":"feature-architecture-enforcement-rules branch"},"rule_ids":["BASE"]},{"assertion_a":"SCP-10","assertion_b":"SCP-11","candidate_id":"SEM-E77A0F0CD9196942F24D","grouping_key":{"condition":"제외 범위 (out of scope)","predicate":"other","scope":"범위","subject":"feature-architecture-enforcement-rules branch"},"rule_ids":["BASE"]},{"assertion_a":"SCP-7","assertion_b":"SCP-8","candidate_id":"SEM-9170ABB4CCA96938CAA3","grouping_key":{"condition":"포함 범위","predicate":"owns","scope":"범위","subject":"feature-architecture-enforcement-rules branch"},"rule_ids":["BASE"]},{"assertion_a":"SCP-7","assertion_b":"SCP-9","candidate_id":"SEM-E6565A8804212349EEA7","grouping_key":{"condition":"포함 범위","predicate":"owns","scope":"범위","subject":"feature-architecture-enforcement-rules branch"},"rule_ids":["BASE"]},{"assertion_a":"SCP-8","assertion_b":"SCP-9","candidate_id":"SEM-B3790F92A32FE5C6A76B","grouping_key":{"condition":"포함 범위","predicate":"owns","scope":"범위","subject":"feature-architecture-enforcement-rules branch"},"rule_ids":["BASE"]}],"coverage":{"assertions":58,"candidate_pairs":84,"eligible_surfaces":6,"processed_surfaces":6},"document_sha256":"e0fca6bc00c98cf9dd8bb67fdb4abccfc0586efc3eb00e5b213f13a578c496e6","explicit_blocking":[],"mode":"local","ontology_sha256":"76d41a29233c830e1940ebb3244263e2b9bfb8dd6a01f2a1d1c51ce5a1b10468","output_schema":"semantic-audit-result/v1","schema_version":"semantic-verdict-request/v1","subject":"raw/branch-notes/feature-architecture-enforcement-rules.md"},"audit_request_sha256":"9196e3653631248990a319279309d0f5251675fe3c26cb73559facd9f344286b","audit_result":{"auditor":{"contract_version":"semantic-coherence/v1","model_id":"claude-opus-4-8","run_id":"a7a57920061665b61"},"mode":"local","request_sha256":"9196e3653631248990a319279309d0f5251675fe3c26cb73559facd9f344286b","schema_version":"semantic-audit-result/v1","subject":"raw/branch-notes/feature-architecture-enforcement-rules.md","verdicts":[{"candidate_id":"SEM-DE1049E25FEA9972D7CD","evidence_a":{"line_end":51,"line_start":51,"quote":"| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1` | Gradle multi-module에서 domain-core·application-core·adapter-*·shared-contract·app-bootstrap·sample-portfolio 책임을 분리한다 | Work Item 완료 조건에 적용 | [[raw/project-notes/ca-skeleton-operational-contract]] |"},"evidence_b":{"line_end":52,"line_start":52,"quote":"| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-ARCHTEST-001@1` | architecture test는 archunit-junit5 1.3.0을 사용한다 | Work Item 완료 조건에 적용 | [[raw/project-notes/ca-skeleton-operational-contract]] |"},"proof_manifest":null,"rationale":"BCP-4(module layout DEC)와 BCP-5(archunit-junit5 1.3.0 stack DEC)는 상속한 프로젝트 결정의 서로 다른 두 항목으로, 겹치지 않는 별개 계약을 제공한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-817590C751AFB8FA7C66","evidence_a":{"line_end":196,"line_start":196,"quote":"| `domain-core`가 Spring/JPA/HTTP/adapter/Lombok import를 포함하면 ArchUnit이 실패한다 | rule DSL 구현 전에는 문서상 금지에 불과함 | violating class 추가 → `./gradlew :app-bootstrap:test --tests '*CleanArchitectureTest'` 실패 확인 + `ArchitectureViolationFixtureTest#domain_is_pure_catches_spring_dependency_in_domain_package` negative test 통과 확인 | `actually-implemented` (2026-05-28 round 2) — Lombok forbidden 추가됨 (`lombok..` package). violations-as-data fixture `SpringDependentDomainFixture` 가 `domain_is_pure` rule 의 실 catch 동작을 commit 된 negative test 로 보증. |"},"evidence_b":{"line_end":206,"line_start":206,"quote":"| ArchUnit rule 이 위반을 실제로 catch 한다는 commit 된 증명 (negative test fixture) — `violations-as-data` pattern 채택 가능 | 현재 negative 검증은 임시 violating 파일 추가 → 제거 방식 (regression 보호 없음). Spring Modulith 공식 패턴 `modules.detectViolations().getMessages()` + `example/ninvalid` fixture package 가 production precedent | `src/app-bootstrap/src/test/.../violations/` 패키지에 의도된 위반 fixture class 추가 + `assertThatThrownBy(rule::check)` 또는 `evaluationResult.getFailureReport().getDetails()` assertion 으로 exact match 검증. 근거: `raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-AU-C2` | `actually-implemented` (2026-05-28 round 2) — `src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/` 에 6 fixture (1 domain + 5 application) + `ArchitectureViolationFixtureTest` 에 6 negative test 작성. 각 test 가 `ClassFileImporter().importPackages(\"...violations\")` 로 fixture 만 로드한 뒤 해당 rule 의 `EvaluationResult.hasViolation() == true` assert. fixture 는 `src/test/...` 위치라 main `@AnalyzeClasses(importOptions = DoNotIncludeTests.class)` 가 제외 → main suite vacuous pass 위험 없음. Spring Modulith `example/ninvalid` 패턴의 ca-tmpl 채택. |"},"proof_manifest":null,"rationale":"CTV-11은 negative-test fixture(violations-as-data) 증명 메커니즘을 제공하고 CTV-1은 그 메커니즘이 커버하는 D3 domain-purity 규칙의 개별 검증 주장이다. 상호 배타적 값 없음.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-C27305268452A6272D1E","evidence_a":{"line_end":196,"line_start":196,"quote":"| `domain-core`가 Spring/JPA/HTTP/adapter/Lombok import를 포함하면 ArchUnit이 실패한다 | rule DSL 구현 전에는 문서상 금지에 불과함 | violating class 추가 → `./gradlew :app-bootstrap:test --tests '*CleanArchitectureTest'` 실패 확인 + `ArchitectureViolationFixtureTest#domain_is_pure_catches_spring_dependency_in_domain_package` negative test 통과 확인 | `actually-implemented` (2026-05-28 round 2) — Lombok forbidden 추가됨 (`lombok..` package). violations-as-data fixture `SpringDependentDomainFixture` 가 `domain_is_pure` rule 의 실 catch 동작을 commit 된 negative test 로 보증. |"},"evidence_b":{"line_end":197,"line_start":197,"quote":"| `application-core`가 `adapter-*` project dependency를 선언하면 Gradle 검증이 실패한다 | Gradle dependency graph rule이 실제로 모든 subproject를 검사해야 함 | `application-core`에 `implementation project(':adapter-persistence')` 추가 → `./gradlew verifyCleanArchitectureDependencies` 실패 확인 | `actually-implemented` |"},"proof_manifest":null,"rationale":"CTV-1(domain-core framework import 실패)과 CTV-2(application-core->adapter Gradle 검증 실패)는 서로 다른 subject의 별개 규칙이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-0B141BC033D26D55B894","evidence_a":{"line_end":196,"line_start":196,"quote":"| `domain-core`가 Spring/JPA/HTTP/adapter/Lombok import를 포함하면 ArchUnit이 실패한다 | rule DSL 구현 전에는 문서상 금지에 불과함 | violating class 추가 → `./gradlew :app-bootstrap:test --tests '*CleanArchitectureTest'` 실패 확인 + `ArchitectureViolationFixtureTest#domain_is_pure_catches_spring_dependency_in_domain_package` negative test 통과 확인 | `actually-implemented` (2026-05-28 round 2) — Lombok forbidden 추가됨 (`lombok..` package). violations-as-data fixture `SpringDependentDomainFixture` 가 `domain_is_pure` rule 의 실 catch 동작을 commit 된 negative test 로 보증. |"},"evidence_b":{"line_end":198,"line_start":198,"quote":"| adapter module끼리 직접 의존하면 실패한다 | adapter 간 공유 요구가 생길 때 우회 가능성이 있음 | `adapter-web` -> `adapter-persistence` dependency 추가 → Gradle/ArchUnit 실패 확인 | `actually-implemented` |"},"proof_manifest":null,"rationale":"CTV-1(domain purity)과 CTV-3(adapter-adapter 격리)은 별개 규칙으로 각자 독립 실패 동작을 서술한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-7450C6C295F096E0447B","evidence_a":{"line_end":196,"line_start":196,"quote":"| `domain-core`가 Spring/JPA/HTTP/adapter/Lombok import를 포함하면 ArchUnit이 실패한다 | rule DSL 구현 전에는 문서상 금지에 불과함 | violating class 추가 → `./gradlew :app-bootstrap:test --tests '*CleanArchitectureTest'` 실패 확인 + `ArchitectureViolationFixtureTest#domain_is_pure_catches_spring_dependency_in_domain_package` negative test 통과 확인 | `actually-implemented` (2026-05-28 round 2) — Lombok forbidden 추가됨 (`lombok..` package). violations-as-data fixture `SpringDependentDomainFixture` 가 `domain_is_pure` rule 의 실 catch 동작을 commit 된 negative test 로 보증. |"},"evidence_b":{"line_end":204,"line_start":204,"quote":"| runtime lookup 우회는 ArchUnit으로 잡히지 않는다 | ArchUnit은 bytecode/static dependency 중심이므로 false negative 가능 | `ApplicationContext#getBean` 우회 코드 추가 → ArchUnit false-pass 확인 → manual/Sonar 보완 항목 등록 | `actually-implemented` (2026-05-28 round 2) — D11 `application_does_not_depend_on_application_context` ArchUnit rule 작성. `ApplicationContextDependentFixture` negative test 가 catch 동작 commit 보증. `getBean(String)` string-key bypass 와 `Class.forName(String)` 은 여전히 ArchUnit 정적 분석 범위 밖 (D12) — `application-core/CLAUDE.md` forbidden 섹션에 명시. |"},"proof_manifest":null,"rationale":"CTV-1은 정적으로 잡히는 위반, CTV-9는 잡히지 않는 runtime false-negative로 subject/조건이 다르며 공존한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-29FBDB156B5DDD22BFA7","evidence_a":{"line_end":196,"line_start":196,"quote":"| `domain-core`가 Spring/JPA/HTTP/adapter/Lombok import를 포함하면 ArchUnit이 실패한다 | rule DSL 구현 전에는 문서상 금지에 불과함 | violating class 추가 → `./gradlew :app-bootstrap:test --tests '*CleanArchitectureTest'` 실패 확인 + `ArchitectureViolationFixtureTest#domain_is_pure_catches_spring_dependency_in_domain_package` negative test 통과 확인 | `actually-implemented` (2026-05-28 round 2) — Lombok forbidden 추가됨 (`lombok..` package). violations-as-data fixture `SpringDependentDomainFixture` 가 `domain_is_pure` rule 의 실 catch 동작을 commit 된 negative test 로 보증. |"},"evidence_b":{"line_end":179,"line_start":179,"quote":"| D3 | `domain-core` forbidden import rule (Lombok 포함) | `raw/company-tech-blogs/woowahan-hexagonal-multimodule.md#WW-HEX-C1`, `raw/official-docs/arch-clean-architecture-uncle-bob.md`, `raw/official-docs/lombok-builder-data-features-official.md#LMB-C1`, `raw/official-docs/lombok-builder-data-features-official.md#LMB-C2`, `raw/official-docs/lombok-builder-data-features-official.md#LMB-C4`, `raw/official-docs/lombok-builder-data-features-official.md#LMB-C5` / **CONTRARY EVIDENCE**: `raw/company-tech-blogs/buckpal-archunit-lombok-allowlist-direct-transactional.md#BUCKPAL-LOMBOK-C1`, `#BUCKPAL-LOMBOK-C2` (Buckpal hex-arch 공식 reference 가 domain purity rule 에서 `lombok..` 명시 allowlist — 정반대 방향) | `company-case-study + engineering-blog + official-vendor-doc` + **UNSUPPORTED_DECISION (CONTRARY)** | LMB-C1~C5 는 `@Builder`/`@Data` 가 무엇을 생성하는지만 증명. **Lombok 금지 자체는 OSS best practice 가 아님** — Buckpal (Hombergs 책 공식 예제, 2.5k stars) 이 domain 에 Lombok 을 명시 allowlist. ca-tmpl D3 는 bytecode 감각성 + framework 독립성을 위한 자체 stricter taste 결정. UNSUPPORTED_DECISION(CONTRARY) 라벨 |"},"proof_manifest":null,"rationale":"CTV-1(검증 주장)과 DEM-3(결정-근거)은 모두 domain-core가 framework/Lombok import를 금지(실패)한다는 동일 contract property에 같은 값을 부여한다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-86808837B57D6CA358FC","evidence_a":{"line_end":196,"line_start":196,"quote":"| `domain-core`가 Spring/JPA/HTTP/adapter/Lombok import를 포함하면 ArchUnit이 실패한다 | rule DSL 구현 전에는 문서상 금지에 불과함 | violating class 추가 → `./gradlew :app-bootstrap:test --tests '*CleanArchitectureTest'` 실패 확인 + `ArchitectureViolationFixtureTest#domain_is_pure_catches_spring_dependency_in_domain_package` negative test 통과 확인 | `actually-implemented` (2026-05-28 round 2) — Lombok forbidden 추가됨 (`lombok..` package). violations-as-data fixture `SpringDependentDomainFixture` 가 `domain_is_pure` rule 의 실 catch 동작을 commit 된 negative test 로 보증. |"},"evidence_b":{"line_end":185,"line_start":185,"quote":"| D9 | MapStruct generated exemption은 `@Generated` annotation 기반 ArchUnit predicate 로 허용 | `raw/official-docs/mapstruct-generated-annotation-official.md#MS-ANNOT-C1`, `#MS-ANNOT-C2` (MapStruct `@Generated` annotation 부착 공식 + `suppressGeneratorTimestamp` 옵션) + `raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-AU-C1` (Spring Modulith 자체가 `annotatedWith(Generated.class)` predicate 를 production 코드에 사용 — DSL 패턴 OSS 검증) | `official-vendor-doc + company-case-study` | annotation FQN 주의: MapStruct = `javax.annotation.processing.Generated`, Spring AOT = `org.springframework.aot.generate.Generated` — ArchUnit predicate 는 MapStruct FQN 사용해야 함. 구현 권고: `.and().areNotAnnotatedWith(javax.annotation.processing.Generated.class)`. ca-tmpl 실제 generated path 검증은 sample mapper 추가 후 통합 테스트 (`needs-confirmation` from `planned` → `actually-implemented` 승급 가능) |"},"proof_manifest":null,"rationale":"CTV-1(D3 domain purity)과 DEM-9(D9 MapStruct exemption)은 별개 결정이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-8CCD41B27D8DBB2DEFE6","evidence_a":{"line_end":196,"line_start":196,"quote":"| `domain-core`가 Spring/JPA/HTTP/adapter/Lombok import를 포함하면 ArchUnit이 실패한다 | rule DSL 구현 전에는 문서상 금지에 불과함 | violating class 추가 → `./gradlew :app-bootstrap:test --tests '*CleanArchitectureTest'` 실패 확인 + `ArchitectureViolationFixtureTest#domain_is_pure_catches_spring_dependency_in_domain_package` negative test 통과 확인 | `actually-implemented` (2026-05-28 round 2) — Lombok forbidden 추가됨 (`lombok..` package). violations-as-data fixture `SpringDependentDomainFixture` 가 `domain_is_pure` rule 의 실 catch 동작을 commit 된 negative test 로 보증. |"},"evidence_b":{"line_end":237,"line_start":237,"quote":"| S1 (violations-as-data) | `ArchitectureViolationFixtureTest` + `architecture/violations/` package | 본 branch fixture: `SpringDependentDomainFixture`(D3), `ApplicationContextDependentFixture`(D11), `TransactionalAnnotatedFixture`. 각 test 가 `rule.evaluate(VIOLATION_CLASSES).hasViolation()==true` assert. fixture 가 `src/test/` 위치라 main `DoNotIncludeTests` 분석 제외 → vacuous pass 방지 | S1 / SPRING-MOD-AU-C2 |"},"proof_manifest":null,"rationale":"IMP-10은 CTV-1이 참조하는 D3 negative test(SpringDependentDomainFixture)의 fixture 구현 위치/메커니즘 detail을 제공한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-712416434B6C87D6A0F5","evidence_a":{"line_end":196,"line_start":196,"quote":"| `domain-core`가 Spring/JPA/HTTP/adapter/Lombok import를 포함하면 ArchUnit이 실패한다 | rule DSL 구현 전에는 문서상 금지에 불과함 | violating class 추가 → `./gradlew :app-bootstrap:test --tests '*CleanArchitectureTest'` 실패 확인 + `ArchitectureViolationFixtureTest#domain_is_pure_catches_spring_dependency_in_domain_package` negative test 통과 확인 | `actually-implemented` (2026-05-28 round 2) — Lombok forbidden 추가됨 (`lombok..` package). violations-as-data fixture `SpringDependentDomainFixture` 가 `domain_is_pure` rule 의 실 catch 동작을 commit 된 negative test 로 보증. |"},"evidence_b":{"line_end":231,"line_start":231,"quote":"| D3 (domain purity + Lombok) | `domain_is_pure` rule | `noClasses().that().resideInAPackage(\"..domain..\").should().dependOnClassesThat().resideInAnyPackage(..., \"lombok..\", ...)` + `.allowEmptyShould(true)`. forbidden package 목록 구체값은 `UNSUPPORTED_IMPL_DECISION` (CONTRARY: Buckpal 은 `lombok..` allowlist — D3 Open Risk) | D3 / LMB-C1~C5, ARCHUNIT-UG-C4 |"},"proof_manifest":null,"rationale":"IMP-4는 D3를 domain_is_pure 코드 규칙으로 매핑한 구현 detail이고 CTV-1은 그 검증 주장으로 서로를 보완한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-90A0B27B6912B41A98FD","evidence_a":{"line_end":196,"line_start":196,"quote":"| `domain-core`가 Spring/JPA/HTTP/adapter/Lombok import를 포함하면 ArchUnit이 실패한다 | rule DSL 구현 전에는 문서상 금지에 불과함 | violating class 추가 → `./gradlew :app-bootstrap:test --tests '*CleanArchitectureTest'` 실패 확인 + `ArchitectureViolationFixtureTest#domain_is_pure_catches_spring_dependency_in_domain_package` negative test 통과 확인 | `actually-implemented` (2026-05-28 round 2) — Lombok forbidden 추가됨 (`lombok..` package). violations-as-data fixture `SpringDependentDomainFixture` 가 `domain_is_pure` rule 의 실 catch 동작을 commit 된 negative test 로 보증. |"},"evidence_b":{"line_end":83,"line_start":83,"quote":"- `domain-core` framework import 금지."},"proof_manifest":null,"rationale":"SCP-2(범위: domain-core framework import 금지)와 CTV-1(검증)은 domain-core가 framework import를 금지한다는 동일 property에 같은 값을 부여한다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-47B20DE299F059E210D9","evidence_a":{"line_end":206,"line_start":206,"quote":"| ArchUnit rule 이 위반을 실제로 catch 한다는 commit 된 증명 (negative test fixture) — `violations-as-data` pattern 채택 가능 | 현재 negative 검증은 임시 violating 파일 추가 → 제거 방식 (regression 보호 없음). Spring Modulith 공식 패턴 `modules.detectViolations().getMessages()` + `example/ninvalid` fixture package 가 production precedent | `src/app-bootstrap/src/test/.../violations/` 패키지에 의도된 위반 fixture class 추가 + `assertThatThrownBy(rule::check)` 또는 `evaluationResult.getFailureReport().getDetails()` assertion 으로 exact match 검증. 근거: `raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-AU-C2` | `actually-implemented` (2026-05-28 round 2) — `src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/` 에 6 fixture (1 domain + 5 application) + `ArchitectureViolationFixtureTest` 에 6 negative test 작성. 각 test 가 `ClassFileImporter().importPackages(\"...violations\")` 로 fixture 만 로드한 뒤 해당 rule 의 `EvaluationResult.hasViolation() == true` assert. fixture 는 `src/test/...` 위치라 main `@AnalyzeClasses(importOptions = DoNotIncludeTests.class)` 가 제외 → main suite vacuous pass 위험 없음. Spring Modulith `example/ninvalid` 패턴의 ca-tmpl 채택. |"},"evidence_b":{"line_end":197,"line_start":197,"quote":"| `application-core`가 `adapter-*` project dependency를 선언하면 Gradle 검증이 실패한다 | Gradle dependency graph rule이 실제로 모든 subproject를 검사해야 함 | `application-core`에 `implementation project(':adapter-persistence')` 추가 → `./gradlew verifyCleanArchitectureDependencies` 실패 확인 | `actually-implemented` |"},"proof_manifest":null,"rationale":"CTV-11(negative-fixture 증명)과 CTV-2(별개 Gradle dependency 규칙)는 겹치지 않는 detail을 제공한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-FA7BF94A8ADEAFC00CA2","evidence_a":{"line_end":206,"line_start":206,"quote":"| ArchUnit rule 이 위반을 실제로 catch 한다는 commit 된 증명 (negative test fixture) — `violations-as-data` pattern 채택 가능 | 현재 negative 검증은 임시 violating 파일 추가 → 제거 방식 (regression 보호 없음). Spring Modulith 공식 패턴 `modules.detectViolations().getMessages()` + `example/ninvalid` fixture package 가 production precedent | `src/app-bootstrap/src/test/.../violations/` 패키지에 의도된 위반 fixture class 추가 + `assertThatThrownBy(rule::check)` 또는 `evaluationResult.getFailureReport().getDetails()` assertion 으로 exact match 검증. 근거: `raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-AU-C2` | `actually-implemented` (2026-05-28 round 2) — `src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/` 에 6 fixture (1 domain + 5 application) + `ArchitectureViolationFixtureTest` 에 6 negative test 작성. 각 test 가 `ClassFileImporter().importPackages(\"...violations\")` 로 fixture 만 로드한 뒤 해당 rule 의 `EvaluationResult.hasViolation() == true` assert. fixture 는 `src/test/...` 위치라 main `@AnalyzeClasses(importOptions = DoNotIncludeTests.class)` 가 제외 → main suite vacuous pass 위험 없음. Spring Modulith `example/ninvalid` 패턴의 ca-tmpl 채택. |"},"evidence_b":{"line_end":198,"line_start":198,"quote":"| adapter module끼리 직접 의존하면 실패한다 | adapter 간 공유 요구가 생길 때 우회 가능성이 있음 | `adapter-web` -> `adapter-persistence` dependency 추가 → Gradle/ArchUnit 실패 확인 | `actually-implemented` |"},"proof_manifest":null,"rationale":"CTV-11(fixture 증명)과 CTV-3(adapter-adapter 규칙)은 별개 항목이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-45F370A8715B1575F8E0","evidence_a":{"line_end":206,"line_start":206,"quote":"| ArchUnit rule 이 위반을 실제로 catch 한다는 commit 된 증명 (negative test fixture) — `violations-as-data` pattern 채택 가능 | 현재 negative 검증은 임시 violating 파일 추가 → 제거 방식 (regression 보호 없음). Spring Modulith 공식 패턴 `modules.detectViolations().getMessages()` + `example/ninvalid` fixture package 가 production precedent | `src/app-bootstrap/src/test/.../violations/` 패키지에 의도된 위반 fixture class 추가 + `assertThatThrownBy(rule::check)` 또는 `evaluationResult.getFailureReport().getDetails()` assertion 으로 exact match 검증. 근거: `raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-AU-C2` | `actually-implemented` (2026-05-28 round 2) — `src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/` 에 6 fixture (1 domain + 5 application) + `ArchitectureViolationFixtureTest` 에 6 negative test 작성. 각 test 가 `ClassFileImporter().importPackages(\"...violations\")` 로 fixture 만 로드한 뒤 해당 rule 의 `EvaluationResult.hasViolation() == true` assert. fixture 는 `src/test/...` 위치라 main `@AnalyzeClasses(importOptions = DoNotIncludeTests.class)` 가 제외 → main suite vacuous pass 위험 없음. Spring Modulith `example/ninvalid` 패턴의 ca-tmpl 채택. |"},"evidence_b":{"line_end":204,"line_start":204,"quote":"| runtime lookup 우회는 ArchUnit으로 잡히지 않는다 | ArchUnit은 bytecode/static dependency 중심이므로 false negative 가능 | `ApplicationContext#getBean` 우회 코드 추가 → ArchUnit false-pass 확인 → manual/Sonar 보완 항목 등록 | `actually-implemented` (2026-05-28 round 2) — D11 `application_does_not_depend_on_application_context` ArchUnit rule 작성. `ApplicationContextDependentFixture` negative test 가 catch 동작 commit 보증. `getBean(String)` string-key bypass 와 `Class.forName(String)` 은 여전히 ArchUnit 정적 분석 범위 밖 (D12) — `application-core/CLAUDE.md` forbidden 섹션에 명시. |"},"proof_manifest":null,"rationale":"CTV-11은 정적 규칙이 실제로 잡음을 fixture로 증명하는 주장, CTV-9는 runtime bypass가 잡히지 않는다는 별개 subject의 한계로 둘 다 참이며 공존한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-B409C61E12294C95C3D2","evidence_a":{"line_end":206,"line_start":206,"quote":"| ArchUnit rule 이 위반을 실제로 catch 한다는 commit 된 증명 (negative test fixture) — `violations-as-data` pattern 채택 가능 | 현재 negative 검증은 임시 violating 파일 추가 → 제거 방식 (regression 보호 없음). Spring Modulith 공식 패턴 `modules.detectViolations().getMessages()` + `example/ninvalid` fixture package 가 production precedent | `src/app-bootstrap/src/test/.../violations/` 패키지에 의도된 위반 fixture class 추가 + `assertThatThrownBy(rule::check)` 또는 `evaluationResult.getFailureReport().getDetails()` assertion 으로 exact match 검증. 근거: `raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-AU-C2` | `actually-implemented` (2026-05-28 round 2) — `src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/` 에 6 fixture (1 domain + 5 application) + `ArchitectureViolationFixtureTest` 에 6 negative test 작성. 각 test 가 `ClassFileImporter().importPackages(\"...violations\")` 로 fixture 만 로드한 뒤 해당 rule 의 `EvaluationResult.hasViolation() == true` assert. fixture 는 `src/test/...` 위치라 main `@AnalyzeClasses(importOptions = DoNotIncludeTests.class)` 가 제외 → main suite vacuous pass 위험 없음. Spring Modulith `example/ninvalid` 패턴의 ca-tmpl 채택. |"},"evidence_b":{"line_end":185,"line_start":185,"quote":"| D9 | MapStruct generated exemption은 `@Generated` annotation 기반 ArchUnit predicate 로 허용 | `raw/official-docs/mapstruct-generated-annotation-official.md#MS-ANNOT-C1`, `#MS-ANNOT-C2` (MapStruct `@Generated` annotation 부착 공식 + `suppressGeneratorTimestamp` 옵션) + `raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-AU-C1` (Spring Modulith 자체가 `annotatedWith(Generated.class)` predicate 를 production 코드에 사용 — DSL 패턴 OSS 검증) | `official-vendor-doc + company-case-study` | annotation FQN 주의: MapStruct = `javax.annotation.processing.Generated`, Spring AOT = `org.springframework.aot.generate.Generated` — ArchUnit predicate 는 MapStruct FQN 사용해야 함. 구현 권고: `.and().areNotAnnotatedWith(javax.annotation.processing.Generated.class)`. ca-tmpl 실제 generated path 검증은 sample mapper 추가 후 통합 테스트 (`needs-confirmation` from `planned` → `actually-implemented` 승급 가능) |"},"proof_manifest":null,"rationale":"CTV-11(fixture 증명)과 DEM-9(D9 MapStruct exemption)은 별개 항목이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-CED8F23A2E173911F6E9","evidence_a":{"line_end":206,"line_start":206,"quote":"| ArchUnit rule 이 위반을 실제로 catch 한다는 commit 된 증명 (negative test fixture) — `violations-as-data` pattern 채택 가능 | 현재 negative 검증은 임시 violating 파일 추가 → 제거 방식 (regression 보호 없음). Spring Modulith 공식 패턴 `modules.detectViolations().getMessages()` + `example/ninvalid` fixture package 가 production precedent | `src/app-bootstrap/src/test/.../violations/` 패키지에 의도된 위반 fixture class 추가 + `assertThatThrownBy(rule::check)` 또는 `evaluationResult.getFailureReport().getDetails()` assertion 으로 exact match 검증. 근거: `raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-AU-C2` | `actually-implemented` (2026-05-28 round 2) — `src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/architecture/violations/` 에 6 fixture (1 domain + 5 application) + `ArchitectureViolationFixtureTest` 에 6 negative test 작성. 각 test 가 `ClassFileImporter().importPackages(\"...violations\")` 로 fixture 만 로드한 뒤 해당 rule 의 `EvaluationResult.hasViolation() == true` assert. fixture 는 `src/test/...` 위치라 main `@AnalyzeClasses(importOptions = DoNotIncludeTests.class)` 가 제외 → main suite vacuous pass 위험 없음. Spring Modulith `example/ninvalid` 패턴의 ca-tmpl 채택. |"},"evidence_b":{"line_end":237,"line_start":237,"quote":"| S1 (violations-as-data) | `ArchitectureViolationFixtureTest` + `architecture/violations/` package | 본 branch fixture: `SpringDependentDomainFixture`(D3), `ApplicationContextDependentFixture`(D11), `TransactionalAnnotatedFixture`. 각 test 가 `rule.evaluate(VIOLATION_CLASSES).hasViolation()==true` assert. fixture 가 `src/test/` 위치라 main `DoNotIncludeTests` 분석 제외 → vacuous pass 방지 | S1 / SPRING-MOD-AU-C2 |"},"proof_manifest":null,"rationale":"CTV-11(violations-as-data 증명 주장)과 IMP-10(동일 패턴의 fixture package/위치 구현 detail)은 같은 패턴을 보완적으로 서술한다. 배타 책임 인수 없음.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-37C6062D6C717286AF20","evidence_a":{"line_end":197,"line_start":197,"quote":"| `application-core`가 `adapter-*` project dependency를 선언하면 Gradle 검증이 실패한다 | Gradle dependency graph rule이 실제로 모든 subproject를 검사해야 함 | `application-core`에 `implementation project(':adapter-persistence')` 추가 → `./gradlew verifyCleanArchitectureDependencies` 실패 확인 | `actually-implemented` |"},"evidence_b":{"line_end":198,"line_start":198,"quote":"| adapter module끼리 직접 의존하면 실패한다 | adapter 간 공유 요구가 생길 때 우회 가능성이 있음 | `adapter-web` -> `adapter-persistence` dependency 추가 → Gradle/ArchUnit 실패 확인 | `actually-implemented` |"},"proof_manifest":null,"rationale":"CTV-2(application->adapter)와 CTV-3(adapter-adapter)은 별개 규칙이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-4B084B37745109E61472","evidence_a":{"line_end":197,"line_start":197,"quote":"| `application-core`가 `adapter-*` project dependency를 선언하면 Gradle 검증이 실패한다 | Gradle dependency graph rule이 실제로 모든 subproject를 검사해야 함 | `application-core`에 `implementation project(':adapter-persistence')` 추가 → `./gradlew verifyCleanArchitectureDependencies` 실패 확인 | `actually-implemented` |"},"evidence_b":{"line_end":204,"line_start":204,"quote":"| runtime lookup 우회는 ArchUnit으로 잡히지 않는다 | ArchUnit은 bytecode/static dependency 중심이므로 false negative 가능 | `ApplicationContext#getBean` 우회 코드 추가 → ArchUnit false-pass 확인 → manual/Sonar 보완 항목 등록 | `actually-implemented` (2026-05-28 round 2) — D11 `application_does_not_depend_on_application_context` ArchUnit rule 작성. `ApplicationContextDependentFixture` negative test 가 catch 동작 commit 보증. `getBean(String)` string-key bypass 와 `Class.forName(String)` 은 여전히 ArchUnit 정적 분석 범위 밖 (D12) — `application-core/CLAUDE.md` forbidden 섹션에 명시. |"},"proof_manifest":null,"rationale":"CTV-2(잡히는 dependency 규칙)와 CTV-9(runtime false-negative)는 subject가 다르다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-B79DCD1EFE8160C2C14B","evidence_a":{"line_end":197,"line_start":197,"quote":"| `application-core`가 `adapter-*` project dependency를 선언하면 Gradle 검증이 실패한다 | Gradle dependency graph rule이 실제로 모든 subproject를 검사해야 함 | `application-core`에 `implementation project(':adapter-persistence')` 추가 → `./gradlew verifyCleanArchitectureDependencies` 실패 확인 | `actually-implemented` |"},"evidence_b":{"line_end":187,"line_start":187,"quote":"| D11 | `application-core` 가 `org.springframework.context.ApplicationContext` 자체에 의존 금지 (banned-class ArchUnit rule). class-literal 기반 `getBean(Class<T>)` 호출까지는 catch 가능 | `raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C5` (`JavaMethodCall` / `JavaConstructorCall` bytecode 기반 access analysis) | `official-vendor-doc` | string-key bean lookup (`getBean(String)`) 과 `Class.forName(String)` 의 string target 은 ArchUnit 이 catch 불가 — D12 의 code review checklist 로 보완 |"},"proof_manifest":null,"rationale":"CTV-2(application-core->adapter 금지, D4)와 DEM-11(application-core의 ApplicationContext 의존 금지, D11)은 같은 subject에 대한 서로 다른 금지 대상으로 공존한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-D56DB164470CFC4FC40A","evidence_a":{"line_end":197,"line_start":197,"quote":"| `application-core`가 `adapter-*` project dependency를 선언하면 Gradle 검증이 실패한다 | Gradle dependency graph rule이 실제로 모든 subproject를 검사해야 함 | `application-core`에 `implementation project(':adapter-persistence')` 추가 → `./gradlew verifyCleanArchitectureDependencies` 실패 확인 | `actually-implemented` |"},"evidence_b":{"line_end":180,"line_start":180,"quote":"| D4 | `application-core` -> `adapter-*` / `app-bootstrap` 의존 금지 | `raw/company-tech-blogs/woowahan-hexagonal-multimodule.md#WW-HEX-C2`, `raw/company-tech-blogs/modulith-kakaobank-techblog-2025.md#KAKAOBANK-MOD-C4`, `raw/official-docs/arch-hexagonal-cockburn.md#HEX-COCKBURN-ORIG-C3` | `company-case-study + engineering-blog` | repository port가 아직 `domain/repository`에 남은 부분은 `feature-application-port-usecase-contract`와 동기화 필요 |"},"proof_manifest":null,"rationale":"CTV-2와 DEM-4는 모두 application-core가 adapter-*/app-bootstrap 의존을 금지(실패)한다는 D4 결정에 같은 값을 부여한다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-2EDCF97E3EFF0B3A68AB","evidence_a":{"line_end":197,"line_start":197,"quote":"| `application-core`가 `adapter-*` project dependency를 선언하면 Gradle 검증이 실패한다 | Gradle dependency graph rule이 실제로 모든 subproject를 검사해야 함 | `application-core`에 `implementation project(':adapter-persistence')` 추가 → `./gradlew verifyCleanArchitectureDependencies` 실패 확인 | `actually-implemented` |"},"evidence_b":{"line_end":185,"line_start":185,"quote":"| D9 | MapStruct generated exemption은 `@Generated` annotation 기반 ArchUnit predicate 로 허용 | `raw/official-docs/mapstruct-generated-annotation-official.md#MS-ANNOT-C1`, `#MS-ANNOT-C2` (MapStruct `@Generated` annotation 부착 공식 + `suppressGeneratorTimestamp` 옵션) + `raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-AU-C1` (Spring Modulith 자체가 `annotatedWith(Generated.class)` predicate 를 production 코드에 사용 — DSL 패턴 OSS 검증) | `official-vendor-doc + company-case-study` | annotation FQN 주의: MapStruct = `javax.annotation.processing.Generated`, Spring AOT = `org.springframework.aot.generate.Generated` — ArchUnit predicate 는 MapStruct FQN 사용해야 함. 구현 권고: `.and().areNotAnnotatedWith(javax.annotation.processing.Generated.class)`. ca-tmpl 실제 generated path 검증은 sample mapper 추가 후 통합 테스트 (`needs-confirmation` from `planned` → `actually-implemented` 승급 가능) |"},"proof_manifest":null,"rationale":"CTV-2(D4)와 DEM-9(D9 MapStruct exemption)은 별개 규칙이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-3CC9785FF88B3357DAAB","evidence_a":{"line_end":197,"line_start":197,"quote":"| `application-core`가 `adapter-*` project dependency를 선언하면 Gradle 검증이 실패한다 | Gradle dependency graph rule이 실제로 모든 subproject를 검사해야 함 | `application-core`에 `implementation project(':adapter-persistence')` 추가 → `./gradlew verifyCleanArchitectureDependencies` 실패 확인 | `actually-implemented` |"},"evidence_b":{"line_end":84,"line_start":84,"quote":"- `application-core` -> `adapter-*` / `app-bootstrap` 의존 금지."},"proof_manifest":null,"rationale":"CTV-2(검증)와 SCP-3(범위)는 모두 application-core->adapter-*/app-bootstrap 의존 금지라는 동일 property에 같은 값을 부여한다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-B9250D666A14D0829BDE","evidence_a":{"line_end":198,"line_start":198,"quote":"| adapter module끼리 직접 의존하면 실패한다 | adapter 간 공유 요구가 생길 때 우회 가능성이 있음 | `adapter-web` -> `adapter-persistence` dependency 추가 → Gradle/ArchUnit 실패 확인 | `actually-implemented` |"},"evidence_b":{"line_end":201,"line_start":201,"quote":"| controller가 domain object를 response로 반환하면 실패한다 | mapper boundary rule이 module boundary만으로는 잡히지 않을 수 있음 | `adapter-web` controller가 domain model을 직접 반환하도록 위반 코드 추가 → ArchUnit 실패 확인 | `locally-verified` |"},"proof_manifest":null,"rationale":"CTV-3(adapter-adapter)과 CTV-6(controller domain object 반환=mapper boundary)는 별개 규칙이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-099F4442F418FD514F7A","evidence_a":{"line_end":198,"line_start":198,"quote":"| adapter module끼리 직접 의존하면 실패한다 | adapter 간 공유 요구가 생길 때 우회 가능성이 있음 | `adapter-web` -> `adapter-persistence` dependency 추가 → Gradle/ArchUnit 실패 확인 | `actually-implemented` |"},"evidence_b":{"line_end":204,"line_start":204,"quote":"| runtime lookup 우회는 ArchUnit으로 잡히지 않는다 | ArchUnit은 bytecode/static dependency 중심이므로 false negative 가능 | `ApplicationContext#getBean` 우회 코드 추가 → ArchUnit false-pass 확인 → manual/Sonar 보완 항목 등록 | `actually-implemented` (2026-05-28 round 2) — D11 `application_does_not_depend_on_application_context` ArchUnit rule 작성. `ApplicationContextDependentFixture` negative test 가 catch 동작 commit 보증. `getBean(String)` string-key bypass 와 `Class.forName(String)` 은 여전히 ArchUnit 정적 분석 범위 밖 (D12) — `application-core/CLAUDE.md` forbidden 섹션에 명시. |"},"proof_manifest":null,"rationale":"CTV-3(잡히는 규칙)과 CTV-9(runtime false-negative)는 subject가 다르다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-13963285792338C6A941","evidence_a":{"line_end":198,"line_start":198,"quote":"| adapter module끼리 직접 의존하면 실패한다 | adapter 간 공유 요구가 생길 때 우회 가능성이 있음 | `adapter-web` -> `adapter-persistence` dependency 추가 → Gradle/ArchUnit 실패 확인 | `actually-implemented` |"},"evidence_b":{"line_end":185,"line_start":185,"quote":"| D9 | MapStruct generated exemption은 `@Generated` annotation 기반 ArchUnit predicate 로 허용 | `raw/official-docs/mapstruct-generated-annotation-official.md#MS-ANNOT-C1`, `#MS-ANNOT-C2` (MapStruct `@Generated` annotation 부착 공식 + `suppressGeneratorTimestamp` 옵션) + `raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-AU-C1` (Spring Modulith 자체가 `annotatedWith(Generated.class)` predicate 를 production 코드에 사용 — DSL 패턴 OSS 검증) | `official-vendor-doc + company-case-study` | annotation FQN 주의: MapStruct = `javax.annotation.processing.Generated`, Spring AOT = `org.springframework.aot.generate.Generated` — ArchUnit predicate 는 MapStruct FQN 사용해야 함. 구현 권고: `.and().areNotAnnotatedWith(javax.annotation.processing.Generated.class)`. ca-tmpl 실제 generated path 검증은 sample mapper 추가 후 통합 테스트 (`needs-confirmation` from `planned` → `actually-implemented` 승급 가능) |"},"proof_manifest":null,"rationale":"CTV-3(D5)과 DEM-9(D9)은 별개 규칙이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-3D700BCEFD5E6A112B88","evidence_a":{"line_end":199,"line_start":199,"quote":"| `shared-contract`에 domain-specific package/class가 들어오면 실패한다 | shared 허용 scope가 넓으면 domain concept가 흘러들 수 있음 | `shared-contract/src/main/java/.../shared/worklog/` 추가 → ArchUnit package scope rule 실패 확인 | `locally-verified` |"},"evidence_b":{"line_end":200,"line_start":200,"quote":"| production module이 `sample-portfolio`에 의존하면 실패한다 | sample은 편의상 import되기 쉬움 | production module에 `implementation project(':sample-portfolio')` 추가 → Gradle dependency rule 실패 확인 | `locally-verified` |"},"proof_manifest":null,"rationale":"CTV-4(shared-contract scope, D6)와 CTV-5(sample 역수입, D7)은 별개 규칙이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-128F4DF428A5B061DB9D","evidence_a":{"line_end":199,"line_start":199,"quote":"| `shared-contract`에 domain-specific package/class가 들어오면 실패한다 | shared 허용 scope가 넓으면 domain concept가 흘러들 수 있음 | `shared-contract/src/main/java/.../shared/worklog/` 추가 → ArchUnit package scope rule 실패 확인 | `locally-verified` |"},"evidence_b":{"line_end":201,"line_start":201,"quote":"| controller가 domain object를 response로 반환하면 실패한다 | mapper boundary rule이 module boundary만으로는 잡히지 않을 수 있음 | `adapter-web` controller가 domain model을 직접 반환하도록 위반 코드 추가 → ArchUnit 실패 확인 | `locally-verified` |"},"proof_manifest":null,"rationale":"CTV-4(shared-contract)와 CTV-6(mapper boundary)은 별개 규칙이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-1849C5FE6697656A0599","evidence_a":{"line_end":199,"line_start":199,"quote":"| `shared-contract`에 domain-specific package/class가 들어오면 실패한다 | shared 허용 scope가 넓으면 domain concept가 흘러들 수 있음 | `shared-contract/src/main/java/.../shared/worklog/` 추가 → ArchUnit package scope rule 실패 확인 | `locally-verified` |"},"evidence_b":{"line_end":202,"line_start":202,"quote":"| application use case가 `@Transactional`을 직접 import하면 실패한다 | Spring 공식은 `@Transactional`을 지원하므로 ca-tmpl 자체 결정임 | violating use case 추가 → ArchUnit forbidden import 실패 확인 | `locally-verified` |"},"proof_manifest":null,"rationale":"CTV-4(shared-contract)와 CTV-7(@Transactional import)은 별개 규칙이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-34354D53E2D1721FFAD4","evidence_a":{"line_end":199,"line_start":199,"quote":"| `shared-contract`에 domain-specific package/class가 들어오면 실패한다 | shared 허용 scope가 넓으면 domain concept가 흘러들 수 있음 | `shared-contract/src/main/java/.../shared/worklog/` 추가 → ArchUnit package scope rule 실패 확인 | `locally-verified` |"},"evidence_b":{"line_end":182,"line_start":182,"quote":"| D6 | `shared-contract` business/domain concept 금지 | `raw/official-docs/feature-first-uncle-bob-screaming-architecture-2011.md#SCREAM-C1`, `raw/branch-notes/feature-skeleton-package-blueprint-contract.md` | `engineering-blog + project-decision` | shared module이 커질수록 common dumping ground가 될 위험 |"},"proof_manifest":null,"rationale":"CTV-4와 DEM-6는 모두 shared-contract가 business/domain concept를 금지(실패)한다는 D6에 같은 값을 부여한다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-30D235F579528551AE46","evidence_a":{"line_end":199,"line_start":199,"quote":"| `shared-contract`에 domain-specific package/class가 들어오면 실패한다 | shared 허용 scope가 넓으면 domain concept가 흘러들 수 있음 | `shared-contract/src/main/java/.../shared/worklog/` 추가 → ArchUnit package scope rule 실패 확인 | `locally-verified` |"},"evidence_b":{"line_end":86,"line_start":86,"quote":"- `shared-contract` business/domain concept 오염 방지."},"proof_manifest":null,"rationale":"CTV-4(검증)와 SCP-5(범위)는 모두 shared-contract의 domain/business concept 오염 금지라는 동일 property(D6)에 같은 값을 부여한다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-F1CE4F3C3B3A3EBB0352","evidence_a":{"line_end":200,"line_start":200,"quote":"| production module이 `sample-portfolio`에 의존하면 실패한다 | sample은 편의상 import되기 쉬움 | production module에 `implementation project(':sample-portfolio')` 추가 → Gradle dependency rule 실패 확인 | `locally-verified` |"},"evidence_b":{"line_end":201,"line_start":201,"quote":"| controller가 domain object를 response로 반환하면 실패한다 | mapper boundary rule이 module boundary만으로는 잡히지 않을 수 있음 | `adapter-web` controller가 domain model을 직접 반환하도록 위반 코드 추가 → ArchUnit 실패 확인 | `locally-verified` |"},"proof_manifest":null,"rationale":"CTV-5(sample 역수입)와 CTV-6(mapper boundary)은 별개 규칙이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-4D2BD38E51BF35C573CE","evidence_a":{"line_end":200,"line_start":200,"quote":"| production module이 `sample-portfolio`에 의존하면 실패한다 | sample은 편의상 import되기 쉬움 | production module에 `implementation project(':sample-portfolio')` 추가 → Gradle dependency rule 실패 확인 | `locally-verified` |"},"evidence_b":{"line_end":202,"line_start":202,"quote":"| application use case가 `@Transactional`을 직접 import하면 실패한다 | Spring 공식은 `@Transactional`을 지원하므로 ca-tmpl 자체 결정임 | violating use case 추가 → ArchUnit forbidden import 실패 확인 | `locally-verified` |"},"proof_manifest":null,"rationale":"CTV-5(sample 역수입)와 CTV-7(@Transactional import)은 별개 규칙이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-F8D2B3946CEE1B51E7EA","evidence_a":{"line_end":200,"line_start":200,"quote":"| production module이 `sample-portfolio`에 의존하면 실패한다 | sample은 편의상 import되기 쉬움 | production module에 `implementation project(':sample-portfolio')` 추가 → Gradle dependency rule 실패 확인 | `locally-verified` |"},"evidence_b":{"line_end":183,"line_start":183,"quote":"| D7 | `sample-portfolio` production 역수입 금지 | `raw/branch-notes/feature-skeleton-package-blueprint-contract.md` | `project-decision` | 외부 직접 근거는 약함. Gradle dependency rule + ArchUnit failure로 실증 필요 |"},"proof_manifest":null,"rationale":"CTV-5와 DEM-7은 모두 sample-portfolio production 역수입 금지(실패)라는 D7에 같은 값을 부여한다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-E4FA4996A0C477D81124","evidence_a":{"line_end":200,"line_start":200,"quote":"| production module이 `sample-portfolio`에 의존하면 실패한다 | sample은 편의상 import되기 쉬움 | production module에 `implementation project(':sample-portfolio')` 추가 → Gradle dependency rule 실패 확인 | `locally-verified` |"},"evidence_b":{"line_end":87,"line_start":87,"quote":"- `sample-portfolio` production 역수입 금지."},"proof_manifest":null,"rationale":"CTV-5(검증)와 SCP-6(범위)는 모두 sample-portfolio production 역수입 금지라는 동일 property(D7)에 같은 값을 부여한다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-811B077C6D3612FC57D8","evidence_a":{"line_end":201,"line_start":201,"quote":"| controller가 domain object를 response로 반환하면 실패한다 | mapper boundary rule이 module boundary만으로는 잡히지 않을 수 있음 | `adapter-web` controller가 domain model을 직접 반환하도록 위반 코드 추가 → ArchUnit 실패 확인 | `locally-verified` |"},"evidence_b":{"line_end":202,"line_start":202,"quote":"| application use case가 `@Transactional`을 직접 import하면 실패한다 | Spring 공식은 `@Transactional`을 지원하므로 ca-tmpl 자체 결정임 | violating use case 추가 → ArchUnit forbidden import 실패 확인 | `locally-verified` |"},"proof_manifest":null,"rationale":"CTV-6(mapper boundary)과 CTV-7(@Transactional import 금지)은 별개 규칙이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-CB1A0C8EA34D4FD6CBBF","evidence_a":{"line_end":202,"line_start":202,"quote":"| application use case가 `@Transactional`을 직접 import하면 실패한다 | Spring 공식은 `@Transactional`을 지원하므로 ca-tmpl 자체 결정임 | violating use case 추가 → ArchUnit forbidden import 실패 확인 | `locally-verified` |"},"evidence_b":{"line_end":184,"line_start":184,"quote":"| D8 | application `@Transactional` 직접 import 금지 | `raw/branch-notes/feature-application-port-usecase-contract.md`, `raw/official-docs/spring-tx-management-reference.md` / **CONTRARY EVIDENCE**: `raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-TX-C1` (Spring Modulith 공식 incubator 가 `@ApplicationModuleListener` 로 `@Transactional` 을 meta-annotation 재노출 — Spring 팀 방향과 정면 충돌), `raw/company-tech-blogs/buckpal-archunit-lombok-allowlist-direct-transactional.md#BUCKPAL-TX-C1` (hex-arch 공식 reference 가 `@Component @Transactional` 직접 부착) | `project-decision + official-vendor-doc` + **UNSUPPORTED_DECISION (CONTRARY)** | Spring 공식은 `@Transactional` 을 지원하고 Spring Modulith 는 meta-annotation 으로 재노출. Buckpal hex-arch reference 도 직접 부착. ca-tmpl D8 forbidden 정책은 **OSS best practice 가 아님** — multi-Gradle-module Hexagonal context 에서 application 모듈 순수성을 위한 자체 stricter 결정. UNSUPPORTED_DECISION(CONTRARY) 라벨 |"},"proof_manifest":null,"rationale":"CTV-7(검증)과 DEM-8(결정)은 모두 application의 @Transactional 직접 import 금지(실패)라는 D8에 같은 값을 부여한다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-DB71875B38E9AB74639A","evidence_a":{"line_end":202,"line_start":202,"quote":"| application use case가 `@Transactional`을 직접 import하면 실패한다 | Spring 공식은 `@Transactional`을 지원하므로 ca-tmpl 자체 결정임 | violating use case 추가 → ArchUnit forbidden import 실패 확인 | `locally-verified` |"},"evidence_b":{"line_end":248,"line_start":248,"quote":"- **Dependency — testCompileOnly**: `TransactionalAnnotatedFixture` 가 `@Transactional` 을 import 하기 위해 `app-bootstrap/build.gradle` 에 `testCompileOnly 'org.springframework:spring-tx'` 추가(production 영향 없음). 관련 class-loading 이슈: [[raw/errors/archunit-testcompileonly-class-loading-2026-06-02]]."},"proof_manifest":null,"rationale":"EFD-4는 D8 검증용 TransactionalAnnotatedFixture가 @Transactional을 import하도록 testCompileOnly spring-tx라는 빌드 detail을 제공하고 CTV-7은 그 규칙의 실패 주장이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-647A84FFD40AE80DCC34","evidence_a":{"line_end":203,"line_start":203,"quote":"| MapStruct generated mapper exemption이 의도한 package/path에만 적용된다 | generated source path가 build tool 설정에 따라 달라질 수 있음 | MapStruct sample mapper 추가 → generated path 확인 → exemption 외 경로 import 시 실패 확인 | `needs-confirmation` |"},"evidence_b":{"line_end":186,"line_start":186,"quote":"| D10 | ArchUnit rule은 JUnit 기반 test로 실행 | `raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C6`, `raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C5` | `official-vendor-doc` | FreezingArchRule baseline 정책은 별도 claim 보강 전까지 `needs-confirmation`으로 둠 |"},"proof_manifest":null,"rationale":"CTV-8(MapStruct exemption 경로 검증, D9)과 DEM-10(ArchUnit rule의 JUnit 실행, D10)은 별개 subject다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-E899294897D32D957D34","evidence_a":{"line_end":203,"line_start":203,"quote":"| MapStruct generated mapper exemption이 의도한 package/path에만 적용된다 | generated source path가 build tool 설정에 따라 달라질 수 있음 | MapStruct sample mapper 추가 → generated path 확인 → exemption 외 경로 import 시 실패 확인 | `needs-confirmation` |"},"evidence_b":{"line_end":185,"line_start":185,"quote":"| D9 | MapStruct generated exemption은 `@Generated` annotation 기반 ArchUnit predicate 로 허용 | `raw/official-docs/mapstruct-generated-annotation-official.md#MS-ANNOT-C1`, `#MS-ANNOT-C2` (MapStruct `@Generated` annotation 부착 공식 + `suppressGeneratorTimestamp` 옵션) + `raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-AU-C1` (Spring Modulith 자체가 `annotatedWith(Generated.class)` predicate 를 production 코드에 사용 — DSL 패턴 OSS 검증) | `official-vendor-doc + company-case-study` | annotation FQN 주의: MapStruct = `javax.annotation.processing.Generated`, Spring AOT = `org.springframework.aot.generate.Generated` — ArchUnit predicate 는 MapStruct FQN 사용해야 함. 구현 권고: `.and().areNotAnnotatedWith(javax.annotation.processing.Generated.class)`. ca-tmpl 실제 generated path 검증은 sample mapper 추가 후 통합 테스트 (`needs-confirmation` from `planned` → `actually-implemented` 승급 가능) |"},"proof_manifest":null,"rationale":"CTV-8과 DEM-9는 모두 D9 MapStruct generated exemption을 다루며 상충 없이 같은 방향(허용/검증필요)으로 일치한다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-5A16157B541EF040EA43","evidence_a":{"line_end":203,"line_start":203,"quote":"| MapStruct generated mapper exemption이 의도한 package/path에만 적용된다 | generated source path가 build tool 설정에 따라 달라질 수 있음 | MapStruct sample mapper 추가 → generated path 확인 → exemption 외 경로 import 시 실패 확인 | `needs-confirmation` |"},"evidence_b":{"line_end":250,"line_start":250,"quote":"- **Edge — MapStruct exemption (미검증)**: D9 generated mapper `@Generated` exemption 은 ca-tmpl 에 실제 MapStruct mapper 가 아직 없어 `needs-confirmation` — sample mapper 추가 후 통합 검증 필요."},"proof_manifest":null,"rationale":"CTV-8과 EFD-6는 모두 D9 MapStruct exemption을 needs-confirmation으로 동일 판정한다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-965DBAD9D2835571DA4E","evidence_a":{"line_end":204,"line_start":204,"quote":"| runtime lookup 우회는 ArchUnit으로 잡히지 않는다 | ArchUnit은 bytecode/static dependency 중심이므로 false negative 가능 | `ApplicationContext#getBean` 우회 코드 추가 → ArchUnit false-pass 확인 → manual/Sonar 보완 항목 등록 | `actually-implemented` (2026-05-28 round 2) — D11 `application_does_not_depend_on_application_context` ArchUnit rule 작성. `ApplicationContextDependentFixture` negative test 가 catch 동작 commit 보증. `getBean(String)` string-key bypass 와 `Class.forName(String)` 은 여전히 ArchUnit 정적 분석 범위 밖 (D12) — `application-core/CLAUDE.md` forbidden 섹션에 명시. |"},"evidence_b":{"line_end":187,"line_start":187,"quote":"| D11 | `application-core` 가 `org.springframework.context.ApplicationContext` 자체에 의존 금지 (banned-class ArchUnit rule). class-literal 기반 `getBean(Class<T>)` 호출까지는 catch 가능 | `raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C5` (`JavaMethodCall` / `JavaConstructorCall` bytecode 기반 access analysis) | `official-vendor-doc` | string-key bean lookup (`getBean(String)`) 과 `Class.forName(String)` 의 string target 은 ArchUnit 이 catch 불가 — D12 의 code review checklist 로 보완 |"},"proof_manifest":null,"rationale":"DEM-11(ApplicationContext banned-class 규칙, class-literal getBean까지 catch)은 CTV-9의 runtime 우회 우려를 부분적으로 다루는 규칙 detail을 제공하며 상호 배타 값 없이 공존한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-EE1221F4DEB291F4EE12","evidence_a":{"line_end":204,"line_start":204,"quote":"| runtime lookup 우회는 ArchUnit으로 잡히지 않는다 | ArchUnit은 bytecode/static dependency 중심이므로 false negative 가능 | `ApplicationContext#getBean` 우회 코드 추가 → ArchUnit false-pass 확인 → manual/Sonar 보완 항목 등록 | `actually-implemented` (2026-05-28 round 2) — D11 `application_does_not_depend_on_application_context` ArchUnit rule 작성. `ApplicationContextDependentFixture` negative test 가 catch 동작 commit 보증. `getBean(String)` string-key bypass 와 `Class.forName(String)` 은 여전히 ArchUnit 정적 분석 범위 밖 (D12) — `application-core/CLAUDE.md` forbidden 섹션에 명시. |"},"evidence_b":{"line_end":188,"line_start":188,"quote":"| D12 | string-key bean lookup / `Class.forName(String)` / `BeanFactory#getBeansOfType` 의 reflection-style bypass 는 ArchUnit static analysis 의 한계 — code review checklist 항목으로 보완 | `raw/official-docs/archunit-user-guide.md` (§6.2 \"accesses ... bytecode offers all this information\" — bytecode 가 string content 자체를 노출하지 않음) | `official-vendor-doc` (negative claim — ArchUnit 이 못 잡는다는 사실의 공식 근거) | runtime container 의존성 그래프 검증 도구 (Spring Boot Actuator `/beans`, Spring Modulith verifier) 도입은 후속 검토 — D1 Open Risk 구체화 |"},"proof_manifest":null,"rationale":"CTV-9와 DEM-12는 모두 string-key getBean(String)/Class.forName(String) 등 reflection-style bypass가 ArchUnit 정적 분석으로 잡히지 않고 code review로만 보완된다는 동일 사실을 서술한다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-38EAC7CF8E51F917C5F1","evidence_a":{"line_end":204,"line_start":204,"quote":"| runtime lookup 우회는 ArchUnit으로 잡히지 않는다 | ArchUnit은 bytecode/static dependency 중심이므로 false negative 가능 | `ApplicationContext#getBean` 우회 코드 추가 → ArchUnit false-pass 확인 → manual/Sonar 보완 항목 등록 | `actually-implemented` (2026-05-28 round 2) — D11 `application_does_not_depend_on_application_context` ArchUnit rule 작성. `ApplicationContextDependentFixture` negative test 가 catch 동작 commit 보증. `getBean(String)` string-key bypass 와 `Class.forName(String)` 은 여전히 ArchUnit 정적 분석 범위 밖 (D12) — `application-core/CLAUDE.md` forbidden 섹션에 명시. |"},"evidence_b":{"line_end":185,"line_start":185,"quote":"| D9 | MapStruct generated exemption은 `@Generated` annotation 기반 ArchUnit predicate 로 허용 | `raw/official-docs/mapstruct-generated-annotation-official.md#MS-ANNOT-C1`, `#MS-ANNOT-C2` (MapStruct `@Generated` annotation 부착 공식 + `suppressGeneratorTimestamp` 옵션) + `raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-AU-C1` (Spring Modulith 자체가 `annotatedWith(Generated.class)` predicate 를 production 코드에 사용 — DSL 패턴 OSS 검증) | `official-vendor-doc + company-case-study` | annotation FQN 주의: MapStruct = `javax.annotation.processing.Generated`, Spring AOT = `org.springframework.aot.generate.Generated` — ArchUnit predicate 는 MapStruct FQN 사용해야 함. 구현 권고: `.and().areNotAnnotatedWith(javax.annotation.processing.Generated.class)`. ca-tmpl 실제 generated path 검증은 sample mapper 추가 후 통합 테스트 (`needs-confirmation` from `planned` → `actually-implemented` 승급 가능) |"},"proof_manifest":null,"rationale":"CTV-9(runtime bypass 한계)와 DEM-9(D9 MapStruct exemption)은 별개 subject다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-5CF0CE4475AF47217CE0","evidence_a":{"line_end":204,"line_start":204,"quote":"| runtime lookup 우회는 ArchUnit으로 잡히지 않는다 | ArchUnit은 bytecode/static dependency 중심이므로 false negative 가능 | `ApplicationContext#getBean` 우회 코드 추가 → ArchUnit false-pass 확인 → manual/Sonar 보완 항목 등록 | `actually-implemented` (2026-05-28 round 2) — D11 `application_does_not_depend_on_application_context` ArchUnit rule 작성. `ApplicationContextDependentFixture` negative test 가 catch 동작 commit 보증. `getBean(String)` string-key bypass 와 `Class.forName(String)` 은 여전히 ArchUnit 정적 분석 범위 밖 (D12) — `application-core/CLAUDE.md` forbidden 섹션에 명시. |"},"evidence_b":{"line_end":247,"line_start":247,"quote":"- **Failure — D11/D12 string-key bypass (알려진 한계, 보완 불가)**: D11 banned-class rule 은 class-literal `getBean(Class<T>)` 까지만 catch 한다. string-key `getBean(String)` · `Class.forName(String)` · `BeanFactory#getBeansOfType` 같은 reflection-style bypass 는 bytecode 가 문자열 내용을 노출하지 않아 ArchUnit 정적 분석으로 **잡을 수 없다**(D12). `application-core/CLAUDE.md` forbidden 섹션의 code review checklist 로만 보완 — 자동 강제 장치 아님. runtime 검증(Actuator `/beans`, Modulith verifier)은 후속 후보(D1/D12 Open Risk)."},"proof_manifest":null,"rationale":"CTV-9와 EFD-3은 모두 D11/D12 string-key bypass가 ArchUnit 정적 분석으로 잡히지 않는다는 동일 실패 동작을 서술한다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-135628D138D6E8490AAE","evidence_a":{"line_end":204,"line_start":204,"quote":"| runtime lookup 우회는 ArchUnit으로 잡히지 않는다 | ArchUnit은 bytecode/static dependency 중심이므로 false negative 가능 | `ApplicationContext#getBean` 우회 코드 추가 → ArchUnit false-pass 확인 → manual/Sonar 보완 항목 등록 | `actually-implemented` (2026-05-28 round 2) — D11 `application_does_not_depend_on_application_context` ArchUnit rule 작성. `ApplicationContextDependentFixture` negative test 가 catch 동작 commit 보증. `getBean(String)` string-key bypass 와 `Class.forName(String)` 은 여전히 ArchUnit 정적 분석 범위 밖 (D12) — `application-core/CLAUDE.md` forbidden 섹션에 명시. |"},"evidence_b":{"line_end":237,"line_start":237,"quote":"| S1 (violations-as-data) | `ArchitectureViolationFixtureTest` + `architecture/violations/` package | 본 branch fixture: `SpringDependentDomainFixture`(D3), `ApplicationContextDependentFixture`(D11), `TransactionalAnnotatedFixture`. 각 test 가 `rule.evaluate(VIOLATION_CLASSES).hasViolation()==true` assert. fixture 가 `src/test/` 위치라 main `DoNotIncludeTests` 분석 제외 → vacuous pass 방지 | S1 / SPRING-MOD-AU-C2 |"},"proof_manifest":null,"rationale":"IMP-10은 CTV-9가 언급한 ApplicationContextDependentFixture(D11) negative test의 구현 detail을 제공한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-F0DA3E4109A635E4876E","evidence_a":{"line_end":204,"line_start":204,"quote":"| runtime lookup 우회는 ArchUnit으로 잡히지 않는다 | ArchUnit은 bytecode/static dependency 중심이므로 false negative 가능 | `ApplicationContext#getBean` 우회 코드 추가 → ArchUnit false-pass 확인 → manual/Sonar 보완 항목 등록 | `actually-implemented` (2026-05-28 round 2) — D11 `application_does_not_depend_on_application_context` ArchUnit rule 작성. `ApplicationContextDependentFixture` negative test 가 catch 동작 commit 보증. `getBean(String)` string-key bypass 와 `Class.forName(String)` 은 여전히 ArchUnit 정적 분석 범위 밖 (D12) — `application-core/CLAUDE.md` forbidden 섹션에 명시. |"},"evidence_b":{"line_end":236,"line_start":236,"quote":"| D11 (ApplicationContext banned-class) | `application_does_not_depend_on_application_context` rule | `noClasses().that().resideInAPackage(\"..application..\").should().dependOnClassesThat().haveFullyQualifiedName(\"org.springframework.context.ApplicationContext\")`. FQN 단일 class 선택은 bytecode access analysis(ARCHUNIT-UG-C5)로 `getBean(Class)` 까지만 catch | D11 / ARCHUNIT-UG-C5 |"},"proof_manifest":null,"rationale":"IMP-9는 D11 ApplicationContext banned-class 규칙의 FQN 구현(getBean(Class)까지 catch)을 제공하고 CTV-9는 그 catch 범위와 D12 한계를 일치되게 서술한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-D01F125598987E9142D0","evidence_a":{"line_end":186,"line_start":186,"quote":"| D10 | ArchUnit rule은 JUnit 기반 test로 실행 | `raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C6`, `raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C5` | `official-vendor-doc` | FreezingArchRule baseline 정책은 별도 claim 보강 전까지 `needs-confirmation`으로 둠 |"},"evidence_b":{"line_end":187,"line_start":187,"quote":"| D11 | `application-core` 가 `org.springframework.context.ApplicationContext` 자체에 의존 금지 (banned-class ArchUnit rule). class-literal 기반 `getBean(Class<T>)` 호출까지는 catch 가능 | `raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C5` (`JavaMethodCall` / `JavaConstructorCall` bytecode 기반 access analysis) | `official-vendor-doc` | string-key bean lookup (`getBean(String)`) 과 `Class.forName(String)` 의 string target 은 ArchUnit 이 catch 불가 — D12 의 code review checklist 로 보완 |"},"proof_manifest":null,"rationale":"DEM-10(JUnit 실행, D10)과 DEM-11(ApplicationContext 금지, D11)은 별개 결정이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-BA8AAEC69F9DA8DCDEF6","evidence_a":{"line_end":186,"line_start":186,"quote":"| D10 | ArchUnit rule은 JUnit 기반 test로 실행 | `raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C6`, `raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C5` | `official-vendor-doc` | FreezingArchRule baseline 정책은 별도 claim 보강 전까지 `needs-confirmation`으로 둠 |"},"evidence_b":{"line_end":188,"line_start":188,"quote":"| D12 | string-key bean lookup / `Class.forName(String)` / `BeanFactory#getBeansOfType` 의 reflection-style bypass 는 ArchUnit static analysis 의 한계 — code review checklist 항목으로 보완 | `raw/official-docs/archunit-user-guide.md` (§6.2 \"accesses ... bytecode offers all this information\" — bytecode 가 string content 자체를 노출하지 않음) | `official-vendor-doc` (negative claim — ArchUnit 이 못 잡는다는 사실의 공식 근거) | runtime container 의존성 그래프 검증 도구 (Spring Boot Actuator `/beans`, Spring Modulith verifier) 도입은 후속 검토 — D1 Open Risk 구체화 |"},"proof_manifest":null,"rationale":"DEM-10(JUnit 실행)과 DEM-12(string-key bypass 한계)는 별개 subject다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-523C652399B54198C75B","evidence_a":{"line_end":186,"line_start":186,"quote":"| D10 | ArchUnit rule은 JUnit 기반 test로 실행 | `raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C6`, `raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C5` | `official-vendor-doc` | FreezingArchRule baseline 정책은 별도 claim 보강 전까지 `needs-confirmation`으로 둠 |"},"evidence_b":{"line_end":185,"line_start":185,"quote":"| D9 | MapStruct generated exemption은 `@Generated` annotation 기반 ArchUnit predicate 로 허용 | `raw/official-docs/mapstruct-generated-annotation-official.md#MS-ANNOT-C1`, `#MS-ANNOT-C2` (MapStruct `@Generated` annotation 부착 공식 + `suppressGeneratorTimestamp` 옵션) + `raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-AU-C1` (Spring Modulith 자체가 `annotatedWith(Generated.class)` predicate 를 production 코드에 사용 — DSL 패턴 OSS 검증) | `official-vendor-doc + company-case-study` | annotation FQN 주의: MapStruct = `javax.annotation.processing.Generated`, Spring AOT = `org.springframework.aot.generate.Generated` — ArchUnit predicate 는 MapStruct FQN 사용해야 함. 구현 권고: `.and().areNotAnnotatedWith(javax.annotation.processing.Generated.class)`. ca-tmpl 실제 generated path 검증은 sample mapper 추가 후 통합 테스트 (`needs-confirmation` from `planned` → `actually-implemented` 승급 가능) |"},"proof_manifest":null,"rationale":"DEM-10(ArchUnit rule JUnit 실행, D10)과 DEM-9(MapStruct @Generated exemption, D9)은 별개 결정이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-AB6EC53006EAE5675982","evidence_a":{"line_end":186,"line_start":186,"quote":"| D10 | ArchUnit rule은 JUnit 기반 test로 실행 | `raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C6`, `raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C5` | `official-vendor-doc` | FreezingArchRule baseline 정책은 별도 claim 보강 전까지 `needs-confirmation`으로 둠 |"},"evidence_b":{"line_end":250,"line_start":250,"quote":"- **Edge — MapStruct exemption (미검증)**: D9 generated mapper `@Generated` exemption 은 ca-tmpl 에 실제 MapStruct mapper 가 아직 없어 `needs-confirmation` — sample mapper 추가 후 통합 검증 필요."},"proof_manifest":null,"rationale":"DEM-10(FreezingArchRule baseline needs-confirmation)과 EFD-6(MapStruct exemption needs-confirmation)은 서로 다른 미확정 항목이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-F42A3F79C187FC2B5E3F","evidence_a":{"line_end":187,"line_start":187,"quote":"| D11 | `application-core` 가 `org.springframework.context.ApplicationContext` 자체에 의존 금지 (banned-class ArchUnit rule). class-literal 기반 `getBean(Class<T>)` 호출까지는 catch 가능 | `raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C5` (`JavaMethodCall` / `JavaConstructorCall` bytecode 기반 access analysis) | `official-vendor-doc` | string-key bean lookup (`getBean(String)`) 과 `Class.forName(String)` 의 string target 은 ArchUnit 이 catch 불가 — D12 의 code review checklist 로 보완 |"},"evidence_b":{"line_end":188,"line_start":188,"quote":"| D12 | string-key bean lookup / `Class.forName(String)` / `BeanFactory#getBeansOfType` 의 reflection-style bypass 는 ArchUnit static analysis 의 한계 — code review checklist 항목으로 보완 | `raw/official-docs/archunit-user-guide.md` (§6.2 \"accesses ... bytecode offers all this information\" — bytecode 가 string content 자체를 노출하지 않음) | `official-vendor-doc` (negative claim — ArchUnit 이 못 잡는다는 사실의 공식 근거) | runtime container 의존성 그래프 검증 도구 (Spring Boot Actuator `/beans`, Spring Modulith verifier) 도입은 후속 검토 — D1 Open Risk 구체화 |"},"proof_manifest":null,"rationale":"DEM-11은 무엇이 잡히는지(class-literal getBean(Class))를, DEM-12는 무엇이 못 잡히는지(string-key)를 서술하는 상호 보완적 nuance로 상충 없다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-0F8B5817FBA5D6206094","evidence_a":{"line_end":187,"line_start":187,"quote":"| D11 | `application-core` 가 `org.springframework.context.ApplicationContext` 자체에 의존 금지 (banned-class ArchUnit rule). class-literal 기반 `getBean(Class<T>)` 호출까지는 catch 가능 | `raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C5` (`JavaMethodCall` / `JavaConstructorCall` bytecode 기반 access analysis) | `official-vendor-doc` | string-key bean lookup (`getBean(String)`) 과 `Class.forName(String)` 의 string target 은 ArchUnit 이 catch 불가 — D12 의 code review checklist 로 보완 |"},"evidence_b":{"line_end":180,"line_start":180,"quote":"| D4 | `application-core` -> `adapter-*` / `app-bootstrap` 의존 금지 | `raw/company-tech-blogs/woowahan-hexagonal-multimodule.md#WW-HEX-C2`, `raw/company-tech-blogs/modulith-kakaobank-techblog-2025.md#KAKAOBANK-MOD-C4`, `raw/official-docs/arch-hexagonal-cockburn.md#HEX-COCKBURN-ORIG-C3` | `company-case-study + engineering-blog` | repository port가 아직 `domain/repository`에 남은 부분은 `feature-application-port-usecase-contract`와 동기화 필요 |"},"proof_manifest":null,"rationale":"DEM-11(application-core의 ApplicationContext 의존 금지, D11)과 DEM-4(adapter-*/app-bootstrap 의존 금지, D4)는 같은 subject에 대한 서로 다른 금지 대상으로 공존한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-BAE3A7B844CB87D2CF0D","evidence_a":{"line_end":187,"line_start":187,"quote":"| D11 | `application-core` 가 `org.springframework.context.ApplicationContext` 자체에 의존 금지 (banned-class ArchUnit rule). class-literal 기반 `getBean(Class<T>)` 호출까지는 catch 가능 | `raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C5` (`JavaMethodCall` / `JavaConstructorCall` bytecode 기반 access analysis) | `official-vendor-doc` | string-key bean lookup (`getBean(String)`) 과 `Class.forName(String)` 의 string target 은 ArchUnit 이 catch 불가 — D12 의 code review checklist 로 보완 |"},"evidence_b":{"line_end":247,"line_start":247,"quote":"- **Failure — D11/D12 string-key bypass (알려진 한계, 보완 불가)**: D11 banned-class rule 은 class-literal `getBean(Class<T>)` 까지만 catch 한다. string-key `getBean(String)` · `Class.forName(String)` · `BeanFactory#getBeansOfType` 같은 reflection-style bypass 는 bytecode 가 문자열 내용을 노출하지 않아 ArchUnit 정적 분석으로 **잡을 수 없다**(D12). `application-core/CLAUDE.md` forbidden 섹션의 code review checklist 로만 보완 — 자동 강제 장치 아님. runtime 검증(Actuator `/beans`, Modulith verifier)은 후속 후보(D1/D12 Open Risk)."},"proof_manifest":null,"rationale":"DEM-11과 EFD-3은 모두 D11이 class-literal getBean(Class)까지만 catch하고 string-key는 못 잡는다는 동일 사실을 일치되게 서술한다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-8742AED277D1C151958F","evidence_a":{"line_end":187,"line_start":187,"quote":"| D11 | `application-core` 가 `org.springframework.context.ApplicationContext` 자체에 의존 금지 (banned-class ArchUnit rule). class-literal 기반 `getBean(Class<T>)` 호출까지는 catch 가능 | `raw/official-docs/archunit-user-guide.md#ARCHUNIT-UG-C5` (`JavaMethodCall` / `JavaConstructorCall` bytecode 기반 access analysis) | `official-vendor-doc` | string-key bean lookup (`getBean(String)`) 과 `Class.forName(String)` 의 string target 은 ArchUnit 이 catch 불가 — D12 의 code review checklist 로 보완 |"},"evidence_b":{"line_end":84,"line_start":84,"quote":"- `application-core` -> `adapter-*` / `app-bootstrap` 의존 금지."},"proof_manifest":null,"rationale":"DEM-11(ApplicationContext ban, D11)과 SCP-3(adapter/bootstrap 의존 금지, D4)은 application-core의 서로 다른 금지 규칙이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-6DC92FCA890C69A5A730","evidence_a":{"line_end":188,"line_start":188,"quote":"| D12 | string-key bean lookup / `Class.forName(String)` / `BeanFactory#getBeansOfType` 의 reflection-style bypass 는 ArchUnit static analysis 의 한계 — code review checklist 항목으로 보완 | `raw/official-docs/archunit-user-guide.md` (§6.2 \"accesses ... bytecode offers all this information\" — bytecode 가 string content 자체를 노출하지 않음) | `official-vendor-doc` (negative claim — ArchUnit 이 못 잡는다는 사실의 공식 근거) | runtime container 의존성 그래프 검증 도구 (Spring Boot Actuator `/beans`, Spring Modulith verifier) 도입은 후속 검토 — D1 Open Risk 구체화 |"},"evidence_b":{"line_end":247,"line_start":247,"quote":"- **Failure — D11/D12 string-key bypass (알려진 한계, 보완 불가)**: D11 banned-class rule 은 class-literal `getBean(Class<T>)` 까지만 catch 한다. string-key `getBean(String)` · `Class.forName(String)` · `BeanFactory#getBeansOfType` 같은 reflection-style bypass 는 bytecode 가 문자열 내용을 노출하지 않아 ArchUnit 정적 분석으로 **잡을 수 없다**(D12). `application-core/CLAUDE.md` forbidden 섹션의 code review checklist 로만 보완 — 자동 강제 장치 아님. runtime 검증(Actuator `/beans`, Modulith verifier)은 후속 후보(D1/D12 Open Risk)."},"proof_manifest":null,"rationale":"DEM-12와 EFD-3은 모두 string-key bean lookup/reflection bypass가 ArchUnit 한계로 code review checklist로만 보완된다는 동일 사실을 서술한다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-01C2FC2285CC05C13B90","evidence_a":{"line_end":178,"line_start":178,"quote":"| D2 | package rule은 Gradle multi-module boundary 기준 | `raw/company-tech-blogs/woowahan-hexagonal-multimodule.md#WW-HEX-C1`, `raw/company-tech-blogs/modulith-kakaobank-techblog-2025.md#KAKAOBANK-MOD-C4`, `raw/branch-notes/feature-skeleton-package-blueprint-contract.md` | `company-case-study + project-decision` | company-tech-blog 사례를 공식 표준으로 승격 금지. ca-tmpl build graph로 별도 검증 필요 |"},"evidence_b":{"line_end":179,"line_start":179,"quote":"| D3 | `domain-core` forbidden import rule (Lombok 포함) | `raw/company-tech-blogs/woowahan-hexagonal-multimodule.md#WW-HEX-C1`, `raw/official-docs/arch-clean-architecture-uncle-bob.md`, `raw/official-docs/lombok-builder-data-features-official.md#LMB-C1`, `raw/official-docs/lombok-builder-data-features-official.md#LMB-C2`, `raw/official-docs/lombok-builder-data-features-official.md#LMB-C4`, `raw/official-docs/lombok-builder-data-features-official.md#LMB-C5` / **CONTRARY EVIDENCE**: `raw/company-tech-blogs/buckpal-archunit-lombok-allowlist-direct-transactional.md#BUCKPAL-LOMBOK-C1`, `#BUCKPAL-LOMBOK-C2` (Buckpal hex-arch 공식 reference 가 domain purity rule 에서 `lombok..` 명시 allowlist — 정반대 방향) | `company-case-study + engineering-blog + official-vendor-doc` + **UNSUPPORTED_DECISION (CONTRARY)** | LMB-C1~C5 는 `@Builder`/`@Data` 가 무엇을 생성하는지만 증명. **Lombok 금지 자체는 OSS best practice 가 아님** — Buckpal (Hombergs 책 공식 예제, 2.5k stars) 이 domain 에 Lombok 을 명시 allowlist. ca-tmpl D3 는 bytecode 감각성 + framework 독립성을 위한 자체 stricter taste 결정. UNSUPPORTED_DECISION(CONTRARY) 라벨 |"},"proof_manifest":null,"rationale":"DEM-2(package rule=Gradle boundary)와 DEM-3(domain-core forbidden import)은 별개 결정이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-80134AF1F188B001E220","evidence_a":{"line_end":178,"line_start":178,"quote":"| D2 | package rule은 Gradle multi-module boundary 기준 | `raw/company-tech-blogs/woowahan-hexagonal-multimodule.md#WW-HEX-C1`, `raw/company-tech-blogs/modulith-kakaobank-techblog-2025.md#KAKAOBANK-MOD-C4`, `raw/branch-notes/feature-skeleton-package-blueprint-contract.md` | `company-case-study + project-decision` | company-tech-blog 사례를 공식 표준으로 승격 금지. ca-tmpl build graph로 별도 검증 필요 |"},"evidence_b":{"line_end":180,"line_start":180,"quote":"| D4 | `application-core` -> `adapter-*` / `app-bootstrap` 의존 금지 | `raw/company-tech-blogs/woowahan-hexagonal-multimodule.md#WW-HEX-C2`, `raw/company-tech-blogs/modulith-kakaobank-techblog-2025.md#KAKAOBANK-MOD-C4`, `raw/official-docs/arch-hexagonal-cockburn.md#HEX-COCKBURN-ORIG-C3` | `company-case-study + engineering-blog` | repository port가 아직 `domain/repository`에 남은 부분은 `feature-application-port-usecase-contract`와 동기화 필요 |"},"proof_manifest":null,"rationale":"DEM-2(Gradle boundary)와 DEM-4(application-core adapter 의존 금지)는 별개 결정이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-BABED1B4D5A3F9606959","evidence_a":{"line_end":178,"line_start":178,"quote":"| D2 | package rule은 Gradle multi-module boundary 기준 | `raw/company-tech-blogs/woowahan-hexagonal-multimodule.md#WW-HEX-C1`, `raw/company-tech-blogs/modulith-kakaobank-techblog-2025.md#KAKAOBANK-MOD-C4`, `raw/branch-notes/feature-skeleton-package-blueprint-contract.md` | `company-case-study + project-decision` | company-tech-blog 사례를 공식 표준으로 승격 금지. ca-tmpl build graph로 별도 검증 필요 |"},"evidence_b":{"line_end":181,"line_start":181,"quote":"| D5 | adapter module 간 직접 의존 금지 | `raw/company-tech-blogs/modulith-kakaobank-techblog-2025.md#KAKAOBANK-MOD-C2`, `raw/company-tech-blogs/modulith-kakaobank-techblog-2025.md#KAKAOBANK-MOD-C4` | `company-case-study` | Spring Modulith 없이 public API/named interface 강제는 약함. ArchUnit/package-private convention 필요 |"},"proof_manifest":null,"rationale":"DEM-2(Gradle boundary)와 DEM-5(adapter-adapter 격리)는 별개 결정이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-DE289D6771A86DE38727","evidence_a":{"line_end":178,"line_start":178,"quote":"| D2 | package rule은 Gradle multi-module boundary 기준 | `raw/company-tech-blogs/woowahan-hexagonal-multimodule.md#WW-HEX-C1`, `raw/company-tech-blogs/modulith-kakaobank-techblog-2025.md#KAKAOBANK-MOD-C4`, `raw/branch-notes/feature-skeleton-package-blueprint-contract.md` | `company-case-study + project-decision` | company-tech-blog 사례를 공식 표준으로 승격 금지. ca-tmpl build graph로 별도 검증 필요 |"},"evidence_b":{"line_end":182,"line_start":182,"quote":"| D6 | `shared-contract` business/domain concept 금지 | `raw/official-docs/feature-first-uncle-bob-screaming-architecture-2011.md#SCREAM-C1`, `raw/branch-notes/feature-skeleton-package-blueprint-contract.md` | `engineering-blog + project-decision` | shared module이 커질수록 common dumping ground가 될 위험 |"},"proof_manifest":null,"rationale":"DEM-2(Gradle boundary)와 DEM-6(shared-contract scope)은 별개 결정이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-A8BA53F8D2FDE3CE221F","evidence_a":{"line_end":178,"line_start":178,"quote":"| D2 | package rule은 Gradle multi-module boundary 기준 | `raw/company-tech-blogs/woowahan-hexagonal-multimodule.md#WW-HEX-C1`, `raw/company-tech-blogs/modulith-kakaobank-techblog-2025.md#KAKAOBANK-MOD-C4`, `raw/branch-notes/feature-skeleton-package-blueprint-contract.md` | `company-case-study + project-decision` | company-tech-blog 사례를 공식 표준으로 승격 금지. ca-tmpl build graph로 별도 검증 필요 |"},"evidence_b":{"line_end":183,"line_start":183,"quote":"| D7 | `sample-portfolio` production 역수입 금지 | `raw/branch-notes/feature-skeleton-package-blueprint-contract.md` | `project-decision` | 외부 직접 근거는 약함. Gradle dependency rule + ArchUnit failure로 실증 필요 |"},"proof_manifest":null,"rationale":"DEM-2(Gradle boundary)와 DEM-7(sample 역수입 금지)은 별개 결정이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-54022D45B4C258D9334B","evidence_a":{"line_end":179,"line_start":179,"quote":"| D3 | `domain-core` forbidden import rule (Lombok 포함) | `raw/company-tech-blogs/woowahan-hexagonal-multimodule.md#WW-HEX-C1`, `raw/official-docs/arch-clean-architecture-uncle-bob.md`, `raw/official-docs/lombok-builder-data-features-official.md#LMB-C1`, `raw/official-docs/lombok-builder-data-features-official.md#LMB-C2`, `raw/official-docs/lombok-builder-data-features-official.md#LMB-C4`, `raw/official-docs/lombok-builder-data-features-official.md#LMB-C5` / **CONTRARY EVIDENCE**: `raw/company-tech-blogs/buckpal-archunit-lombok-allowlist-direct-transactional.md#BUCKPAL-LOMBOK-C1`, `#BUCKPAL-LOMBOK-C2` (Buckpal hex-arch 공식 reference 가 domain purity rule 에서 `lombok..` 명시 allowlist — 정반대 방향) | `company-case-study + engineering-blog + official-vendor-doc` + **UNSUPPORTED_DECISION (CONTRARY)** | LMB-C1~C5 는 `@Builder`/`@Data` 가 무엇을 생성하는지만 증명. **Lombok 금지 자체는 OSS best practice 가 아님** — Buckpal (Hombergs 책 공식 예제, 2.5k stars) 이 domain 에 Lombok 을 명시 allowlist. ca-tmpl D3 는 bytecode 감각성 + framework 독립성을 위한 자체 stricter taste 결정. UNSUPPORTED_DECISION(CONTRARY) 라벨 |"},"evidence_b":{"line_end":231,"line_start":231,"quote":"| D3 (domain purity + Lombok) | `domain_is_pure` rule | `noClasses().that().resideInAPackage(\"..domain..\").should().dependOnClassesThat().resideInAnyPackage(..., \"lombok..\", ...)` + `.allowEmptyShould(true)`. forbidden package 목록 구체값은 `UNSUPPORTED_IMPL_DECISION` (CONTRARY: Buckpal 은 `lombok..` allowlist — D3 Open Risk) | D3 / LMB-C1~C5, ARCHUNIT-UG-C4 |"},"proof_manifest":null,"rationale":"IMP-4는 DEM-3(D3 결정+근거)을 domain_is_pure 코드 규칙으로 매핑한 구현 detail로, 같은 규칙을 보완한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-C97E0DE80FAEFF430B9A","evidence_a":{"line_end":179,"line_start":179,"quote":"| D3 | `domain-core` forbidden import rule (Lombok 포함) | `raw/company-tech-blogs/woowahan-hexagonal-multimodule.md#WW-HEX-C1`, `raw/official-docs/arch-clean-architecture-uncle-bob.md`, `raw/official-docs/lombok-builder-data-features-official.md#LMB-C1`, `raw/official-docs/lombok-builder-data-features-official.md#LMB-C2`, `raw/official-docs/lombok-builder-data-features-official.md#LMB-C4`, `raw/official-docs/lombok-builder-data-features-official.md#LMB-C5` / **CONTRARY EVIDENCE**: `raw/company-tech-blogs/buckpal-archunit-lombok-allowlist-direct-transactional.md#BUCKPAL-LOMBOK-C1`, `#BUCKPAL-LOMBOK-C2` (Buckpal hex-arch 공식 reference 가 domain purity rule 에서 `lombok..` 명시 allowlist — 정반대 방향) | `company-case-study + engineering-blog + official-vendor-doc` + **UNSUPPORTED_DECISION (CONTRARY)** | LMB-C1~C5 는 `@Builder`/`@Data` 가 무엇을 생성하는지만 증명. **Lombok 금지 자체는 OSS best practice 가 아님** — Buckpal (Hombergs 책 공식 예제, 2.5k stars) 이 domain 에 Lombok 을 명시 allowlist. ca-tmpl D3 는 bytecode 감각성 + framework 독립성을 위한 자체 stricter taste 결정. UNSUPPORTED_DECISION(CONTRARY) 라벨 |"},"evidence_b":{"line_end":83,"line_start":83,"quote":"- `domain-core` framework import 금지."},"proof_manifest":null,"rationale":"DEM-3와 SCP-2는 모두 domain-core가 framework/Lombok import를 금지한다는 동일 property에 같은 값을 부여한다(SCP-2 일반 framework, DEM-3 Lombok 포함 구체화).","verdict":"CONSISTENT"},{"candidate_id":"SEM-A6D5CDE839C52232AA04","evidence_a":{"line_end":180,"line_start":180,"quote":"| D4 | `application-core` -> `adapter-*` / `app-bootstrap` 의존 금지 | `raw/company-tech-blogs/woowahan-hexagonal-multimodule.md#WW-HEX-C2`, `raw/company-tech-blogs/modulith-kakaobank-techblog-2025.md#KAKAOBANK-MOD-C4`, `raw/official-docs/arch-hexagonal-cockburn.md#HEX-COCKBURN-ORIG-C3` | `company-case-study + engineering-blog` | repository port가 아직 `domain/repository`에 남은 부분은 `feature-application-port-usecase-contract`와 동기화 필요 |"},"evidence_b":{"line_end":181,"line_start":181,"quote":"| D5 | adapter module 간 직접 의존 금지 | `raw/company-tech-blogs/modulith-kakaobank-techblog-2025.md#KAKAOBANK-MOD-C2`, `raw/company-tech-blogs/modulith-kakaobank-techblog-2025.md#KAKAOBANK-MOD-C4` | `company-case-study` | Spring Modulith 없이 public API/named interface 강제는 약함. ArchUnit/package-private convention 필요 |"},"proof_manifest":null,"rationale":"DEM-4(application-core adapter 의존 금지)와 DEM-5(adapter-adapter 격리)는 별개 결정이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-99141C4C957B575A6FA4","evidence_a":{"line_end":180,"line_start":180,"quote":"| D4 | `application-core` -> `adapter-*` / `app-bootstrap` 의존 금지 | `raw/company-tech-blogs/woowahan-hexagonal-multimodule.md#WW-HEX-C2`, `raw/company-tech-blogs/modulith-kakaobank-techblog-2025.md#KAKAOBANK-MOD-C4`, `raw/official-docs/arch-hexagonal-cockburn.md#HEX-COCKBURN-ORIG-C3` | `company-case-study + engineering-blog` | repository port가 아직 `domain/repository`에 남은 부분은 `feature-application-port-usecase-contract`와 동기화 필요 |"},"evidence_b":{"line_end":84,"line_start":84,"quote":"- `application-core` -> `adapter-*` / `app-bootstrap` 의존 금지."},"proof_manifest":null,"rationale":"DEM-4와 SCP-3은 모두 application-core->adapter-*/app-bootstrap 의존 금지(D4)라는 동일 property에 같은 값을 부여한다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-3F83BFB66A2F09068A95","evidence_a":{"line_end":182,"line_start":182,"quote":"| D6 | `shared-contract` business/domain concept 금지 | `raw/official-docs/feature-first-uncle-bob-screaming-architecture-2011.md#SCREAM-C1`, `raw/branch-notes/feature-skeleton-package-blueprint-contract.md` | `engineering-blog + project-decision` | shared module이 커질수록 common dumping ground가 될 위험 |"},"evidence_b":{"line_end":183,"line_start":183,"quote":"| D7 | `sample-portfolio` production 역수입 금지 | `raw/branch-notes/feature-skeleton-package-blueprint-contract.md` | `project-decision` | 외부 직접 근거는 약함. Gradle dependency rule + ArchUnit failure로 실증 필요 |"},"proof_manifest":null,"rationale":"DEM-6(shared-contract scope)와 DEM-7(sample 역수입)은 별개 결정이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-4180108281E0D8003F7D","evidence_a":{"line_end":182,"line_start":182,"quote":"| D6 | `shared-contract` business/domain concept 금지 | `raw/official-docs/feature-first-uncle-bob-screaming-architecture-2011.md#SCREAM-C1`, `raw/branch-notes/feature-skeleton-package-blueprint-contract.md` | `engineering-blog + project-decision` | shared module이 커질수록 common dumping ground가 될 위험 |"},"evidence_b":{"line_end":86,"line_start":86,"quote":"- `shared-contract` business/domain concept 오염 방지."},"proof_manifest":null,"rationale":"DEM-6와 SCP-5는 모두 shared-contract의 business/domain concept 금지(D6)라는 동일 property에 같은 값을 부여한다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-263557E7460539797EC3","evidence_a":{"line_end":183,"line_start":183,"quote":"| D7 | `sample-portfolio` production 역수입 금지 | `raw/branch-notes/feature-skeleton-package-blueprint-contract.md` | `project-decision` | 외부 직접 근거는 약함. Gradle dependency rule + ArchUnit failure로 실증 필요 |"},"evidence_b":{"line_end":87,"line_start":87,"quote":"- `sample-portfolio` production 역수입 금지."},"proof_manifest":null,"rationale":"DEM-7과 SCP-6은 모두 sample-portfolio production 역수입 금지(D7)라는 동일 property에 같은 값을 부여한다.","verdict":"CONSISTENT"},{"candidate_id":"SEM-0C8B7CBB94CCBAB7A97F","evidence_a":{"line_end":184,"line_start":184,"quote":"| D8 | application `@Transactional` 직접 import 금지 | `raw/branch-notes/feature-application-port-usecase-contract.md`, `raw/official-docs/spring-tx-management-reference.md` / **CONTRARY EVIDENCE**: `raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-TX-C1` (Spring Modulith 공식 incubator 가 `@ApplicationModuleListener` 로 `@Transactional` 을 meta-annotation 재노출 — Spring 팀 방향과 정면 충돌), `raw/company-tech-blogs/buckpal-archunit-lombok-allowlist-direct-transactional.md#BUCKPAL-TX-C1` (hex-arch 공식 reference 가 `@Component @Transactional` 직접 부착) | `project-decision + official-vendor-doc` + **UNSUPPORTED_DECISION (CONTRARY)** | Spring 공식은 `@Transactional` 을 지원하고 Spring Modulith 는 meta-annotation 으로 재노출. Buckpal hex-arch reference 도 직접 부착. ca-tmpl D8 forbidden 정책은 **OSS best practice 가 아님** — multi-Gradle-module Hexagonal context 에서 application 모듈 순수성을 위한 자체 stricter 결정. UNSUPPORTED_DECISION(CONTRARY) 라벨 |"},"evidence_b":{"line_end":248,"line_start":248,"quote":"- **Dependency — testCompileOnly**: `TransactionalAnnotatedFixture` 가 `@Transactional` 을 import 하기 위해 `app-bootstrap/build.gradle` 에 `testCompileOnly 'org.springframework:spring-tx'` 추가(production 영향 없음). 관련 class-loading 이슈: [[raw/errors/archunit-testcompileonly-class-loading-2026-06-02]]."},"proof_manifest":null,"rationale":"EFD-4는 DEM-8(D8 @Transactional 금지 결정) 검증 fixture의 testCompileOnly spring-tx 빌드 detail을 제공한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-32CA9186E754C1608455","evidence_a":{"line_end":185,"line_start":185,"quote":"| D9 | MapStruct generated exemption은 `@Generated` annotation 기반 ArchUnit predicate 로 허용 | `raw/official-docs/mapstruct-generated-annotation-official.md#MS-ANNOT-C1`, `#MS-ANNOT-C2` (MapStruct `@Generated` annotation 부착 공식 + `suppressGeneratorTimestamp` 옵션) + `raw/company-tech-blogs/spring-modulith-archunit-generated-exemption-and-violations-as-data.md#SPRING-MOD-AU-C1` (Spring Modulith 자체가 `annotatedWith(Generated.class)` predicate 를 production 코드에 사용 — DSL 패턴 OSS 검증) | `official-vendor-doc + company-case-study` | annotation FQN 주의: MapStruct = `javax.annotation.processing.Generated`, Spring AOT = `org.springframework.aot.generate.Generated` — ArchUnit predicate 는 MapStruct FQN 사용해야 함. 구현 권고: `.and().areNotAnnotatedWith(javax.annotation.processing.Generated.class)`. ca-tmpl 실제 generated path 검증은 sample mapper 추가 후 통합 테스트 (`needs-confirmation` from `planned` → `actually-implemented` 승급 가능) |"},"evidence_b":{"line_end":250,"line_start":250,"quote":"- **Edge — MapStruct exemption (미검증)**: D9 generated mapper `@Generated` exemption 은 ca-tmpl 에 실제 MapStruct mapper 가 아직 없어 `needs-confirmation` — sample mapper 추가 후 통합 검증 필요."},"proof_manifest":null,"rationale":"EFD-6는 DEM-9(D9 MapStruct exemption 결정)에 대해 실제 mapper 부재로 인한 needs-confirmation edge라는 검증 상태 detail을 보완한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-36C1C58278C72057DF3E","evidence_a":{"line_end":245,"line_start":245,"quote":"- **Edge — empty anchor**: skeleton 단계의 빈 module 은 `that()` 매칭 대상이 0개라 ArchUnit 기본 동작상 `failed to check any classes` 로 실패한다. 의도된 빈 anchor rule 에 `allowEmptyShould(true)` 를 명시해 허용. 근거 사실: [[raw/errors/archunit-empty-should-anchor-2026-05-27]]."},"evidence_b":{"line_end":246,"line_start":246,"quote":"- **Failure — vacuous pass (import scope 누락)**: 검사 대상 class 가 `@AnalyzeClasses` import scope 밖이면 위반이 있어도 rule 이 *조용히* 통과(`BUILD SUCCESSFUL`, 에러 신호 없음)한다. `allowEmptyShould(true)` 도 이 false-negative 를 막지 못함. 보완: sample/fixture 를 test import scope 에 포함 + violations-as-data negative fixture(S1) 로 catch 동작을 commit 보증. 근거 사실: [[raw/errors/archunit-test-scope-sample-ticket-inclusion-2026-05-28]]."},"proof_manifest":null,"rationale":"EFD-1(empty anchor->기본 실패, allowEmptyShould로 허용)과 EFD-2(import scope 밖->조용히 통과, allowEmptyShould로 해결 불가)는 서로 다른 명명된 실패 모드로, 본문이 둘을 명시적으로 구분한다. 상충 아님.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-D95457DBBFE1C4E114C9","evidence_a":{"line_end":246,"line_start":246,"quote":"- **Failure — vacuous pass (import scope 누락)**: 검사 대상 class 가 `@AnalyzeClasses` import scope 밖이면 위반이 있어도 rule 이 *조용히* 통과(`BUILD SUCCESSFUL`, 에러 신호 없음)한다. `allowEmptyShould(true)` 도 이 false-negative 를 막지 못함. 보완: sample/fixture 를 test import scope 에 포함 + violations-as-data negative fixture(S1) 로 catch 동작을 commit 보증. 근거 사실: [[raw/errors/archunit-test-scope-sample-ticket-inclusion-2026-05-28]]."},"evidence_b":{"line_end":229,"line_start":229,"quote":"| D1 (test 강제) | `app-bootstrap/.../architecture/CleanArchitectureTest.java` | `@AnalyzeClasses(packages = \"dev.caskeleton\", importOptions = DoNotIncludeTests.class)` + `@ArchTest static final ArchRule` 필드들. `@AnalyzeClasses` import scope 선택은 `UNSUPPORTED_IMPL_DECISION` — ARCHUNIT-UG-C6 는 JUnit 통합만 권고하고 base package 선택은 프로젝트 trade-off (`dev.caskeleton` root 단일 scan 으로 결정) | D1 / ARCHUNIT-UG-C5,C6 |"},"proof_manifest":null,"rationale":"IMP-2는 @AnalyzeClasses(packages=dev.caskeleton) import scope를 정의하고 EFD-2는 그 scope 누락 시 vacuous pass 실패를 경고하는 보완 관계다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-092CCE2A43246D196511","evidence_a":{"line_end":248,"line_start":248,"quote":"- **Dependency — testCompileOnly**: `TransactionalAnnotatedFixture` 가 `@Transactional` 을 import 하기 위해 `app-bootstrap/build.gradle` 에 `testCompileOnly 'org.springframework:spring-tx'` 추가(production 영향 없음). 관련 class-loading 이슈: [[raw/errors/archunit-testcompileonly-class-loading-2026-06-02]]."},"evidence_b":{"line_end":237,"line_start":237,"quote":"| S1 (violations-as-data) | `ArchitectureViolationFixtureTest` + `architecture/violations/` package | 본 branch fixture: `SpringDependentDomainFixture`(D3), `ApplicationContextDependentFixture`(D11), `TransactionalAnnotatedFixture`. 각 test 가 `rule.evaluate(VIOLATION_CLASSES).hasViolation()==true` assert. fixture 가 `src/test/` 위치라 main `DoNotIncludeTests` 분석 제외 → vacuous pass 방지 | S1 / SPRING-MOD-AU-C2 |"},"proof_manifest":null,"rationale":"EFD-4(testCompileOnly spring-tx 빌드 의존)와 IMP-10(TransactionalAnnotatedFixture 테스트 구조)은 같은 fixture의 서로 다른 구현 측면을 제공한다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-38882C2C3E21B9AF8C87","evidence_a":{"line_end":229,"line_start":229,"quote":"| D1 (test 강제) | `app-bootstrap/.../architecture/CleanArchitectureTest.java` | `@AnalyzeClasses(packages = \"dev.caskeleton\", importOptions = DoNotIncludeTests.class)` + `@ArchTest static final ArchRule` 필드들. `@AnalyzeClasses` import scope 선택은 `UNSUPPORTED_IMPL_DECISION` — ARCHUNIT-UG-C6 는 JUnit 통합만 권고하고 base package 선택은 프로젝트 trade-off (`dev.caskeleton` root 단일 scan 으로 결정) | D1 / ARCHUNIT-UG-C5,C6 |"},"evidence_b":{"line_end":230,"line_start":230,"quote":"| D2 (build-graph) | `src/build.gradle:53` `verifyCleanArchitectureDependencies` task | `allowedProjectDependencies` Map<String,Set<String>> 화이트리스트 9 module + `configurations(api/implementation/compileOnly/runtimeOnly).dependencies.withType(ProjectDependency)` 차집합 → `GradleException`. matrix 구체 값은 `UNSUPPORTED_IMPL_DECISION` — blueprint 결정([[raw/branch-notes/feature-skeleton-package-blueprint-contract]])의 module 목록에서 도출한 trade-off | D2 / WW-HEX-C1, KAKAOBANK-MOD-C4 |"},"proof_manifest":null,"rationale":"IMP-2(D1 test->CleanArchitectureTest)와 IMP-3(D2 build-graph->verifyCleanArchitectureDependencies)은 별개 규칙의 구현 매핑이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-01AF436C625EB63BF75F","evidence_a":{"line_end":229,"line_start":229,"quote":"| D1 (test 강제) | `app-bootstrap/.../architecture/CleanArchitectureTest.java` | `@AnalyzeClasses(packages = \"dev.caskeleton\", importOptions = DoNotIncludeTests.class)` + `@ArchTest static final ArchRule` 필드들. `@AnalyzeClasses` import scope 선택은 `UNSUPPORTED_IMPL_DECISION` — ARCHUNIT-UG-C6 는 JUnit 통합만 권고하고 base package 선택은 프로젝트 trade-off (`dev.caskeleton` root 단일 scan 으로 결정) | D1 / ARCHUNIT-UG-C5,C6 |"},"evidence_b":{"line_end":231,"line_start":231,"quote":"| D3 (domain purity + Lombok) | `domain_is_pure` rule | `noClasses().that().resideInAPackage(\"..domain..\").should().dependOnClassesThat().resideInAnyPackage(..., \"lombok..\", ...)` + `.allowEmptyShould(true)`. forbidden package 목록 구체값은 `UNSUPPORTED_IMPL_DECISION` (CONTRARY: Buckpal 은 `lombok..` allowlist — D3 Open Risk) | D3 / LMB-C1~C5, ARCHUNIT-UG-C4 |"},"proof_manifest":null,"rationale":"IMP-2(D1)와 IMP-4(D3 domain_is_pure)는 별개 규칙의 구현 매핑이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-D84C59D959DDE399E8BA","evidence_a":{"line_end":229,"line_start":229,"quote":"| D1 (test 강제) | `app-bootstrap/.../architecture/CleanArchitectureTest.java` | `@AnalyzeClasses(packages = \"dev.caskeleton\", importOptions = DoNotIncludeTests.class)` + `@ArchTest static final ArchRule` 필드들. `@AnalyzeClasses` import scope 선택은 `UNSUPPORTED_IMPL_DECISION` — ARCHUNIT-UG-C6 는 JUnit 통합만 권고하고 base package 선택은 프로젝트 trade-off (`dev.caskeleton` root 단일 scan 으로 결정) | D1 / ARCHUNIT-UG-C5,C6 |"},"evidence_b":{"line_end":234,"line_start":234,"quote":"| D6 (shared scope) | `shared_contract_contains_only_operational_contract_packages` rule | `classes().that().resideInAPackage(\"..shared..\").should().resideInAnyPackage(<operational allowlist>)`. allowlist package 집합은 `UNSUPPORTED_IMPL_DECISION` — blueprint 의 operational contract 목록에서 도출 | D6 / SCREAM-C1 |"},"proof_manifest":null,"rationale":"IMP-2(D1)와 IMP-7(D6 shared_contract)은 별개 규칙의 구현 매핑이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-C1B7FF32816A3F6F4A0D","evidence_a":{"line_end":230,"line_start":230,"quote":"| D2 (build-graph) | `src/build.gradle:53` `verifyCleanArchitectureDependencies` task | `allowedProjectDependencies` Map<String,Set<String>> 화이트리스트 9 module + `configurations(api/implementation/compileOnly/runtimeOnly).dependencies.withType(ProjectDependency)` 차집합 → `GradleException`. matrix 구체 값은 `UNSUPPORTED_IMPL_DECISION` — blueprint 결정([[raw/branch-notes/feature-skeleton-package-blueprint-contract]])의 module 목록에서 도출한 trade-off | D2 / WW-HEX-C1, KAKAOBANK-MOD-C4 |"},"evidence_b":{"line_end":231,"line_start":231,"quote":"| D3 (domain purity + Lombok) | `domain_is_pure` rule | `noClasses().that().resideInAPackage(\"..domain..\").should().dependOnClassesThat().resideInAnyPackage(..., \"lombok..\", ...)` + `.allowEmptyShould(true)`. forbidden package 목록 구체값은 `UNSUPPORTED_IMPL_DECISION` (CONTRARY: Buckpal 은 `lombok..` allowlist — D3 Open Risk) | D3 / LMB-C1~C5, ARCHUNIT-UG-C4 |"},"proof_manifest":null,"rationale":"IMP-3(D2)와 IMP-4(D3)는 별개 규칙의 구현 매핑이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-624093697CBC9023DC74","evidence_a":{"line_end":230,"line_start":230,"quote":"| D2 (build-graph) | `src/build.gradle:53` `verifyCleanArchitectureDependencies` task | `allowedProjectDependencies` Map<String,Set<String>> 화이트리스트 9 module + `configurations(api/implementation/compileOnly/runtimeOnly).dependencies.withType(ProjectDependency)` 차집합 → `GradleException`. matrix 구체 값은 `UNSUPPORTED_IMPL_DECISION` — blueprint 결정([[raw/branch-notes/feature-skeleton-package-blueprint-contract]])의 module 목록에서 도출한 trade-off | D2 / WW-HEX-C1, KAKAOBANK-MOD-C4 |"},"evidence_b":{"line_end":234,"line_start":234,"quote":"| D6 (shared scope) | `shared_contract_contains_only_operational_contract_packages` rule | `classes().that().resideInAPackage(\"..shared..\").should().resideInAnyPackage(<operational allowlist>)`. allowlist package 집합은 `UNSUPPORTED_IMPL_DECISION` — blueprint 의 operational contract 목록에서 도출 | D6 / SCREAM-C1 |"},"proof_manifest":null,"rationale":"IMP-3(D2)와 IMP-7(D6)은 별개 규칙의 구현 매핑이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-409286BCEB09BB5054F4","evidence_a":{"line_end":231,"line_start":231,"quote":"| D3 (domain purity + Lombok) | `domain_is_pure` rule | `noClasses().that().resideInAPackage(\"..domain..\").should().dependOnClassesThat().resideInAnyPackage(..., \"lombok..\", ...)` + `.allowEmptyShould(true)`. forbidden package 목록 구체값은 `UNSUPPORTED_IMPL_DECISION` (CONTRARY: Buckpal 은 `lombok..` allowlist — D3 Open Risk) | D3 / LMB-C1~C5, ARCHUNIT-UG-C4 |"},"evidence_b":{"line_end":234,"line_start":234,"quote":"| D6 (shared scope) | `shared_contract_contains_only_operational_contract_packages` rule | `classes().that().resideInAPackage(\"..shared..\").should().resideInAnyPackage(<operational allowlist>)`. allowlist package 집합은 `UNSUPPORTED_IMPL_DECISION` — blueprint 의 operational contract 목록에서 도출 | D6 / SCREAM-C1 |"},"proof_manifest":null,"rationale":"IMP-4(D3)와 IMP-7(D6)은 별개 규칙의 구현 매핑이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-570DF9848B4AB111FDD1","evidence_a":{"line_end":82,"line_start":82,"quote":"- Gradle multi-module dependency rule."},"evidence_b":{"line_end":88,"line_start":88,"quote":"- mapper boundary rule."},"proof_manifest":null,"rationale":"SCP-1(branch owns Gradle multi-module dependency rule)과 SCP-7(branch owns mapper boundary rule)은 같은 owner가 서로 다른 in-scope 관심사를 소유하는 것으로, object가 달라 단일 소유 충돌이 아니다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-CE1F3F3E01F0F7EC9E7B","evidence_a":{"line_end":82,"line_start":82,"quote":"- Gradle multi-module dependency rule."},"evidence_b":{"line_end":89,"line_start":89,"quote":"- transaction annotation forbidden import rule."},"proof_manifest":null,"rationale":"SCP-1(Gradle dependency rule)과 SCP-8(transaction annotation forbidden import rule)은 branch가 소유하는 서로 다른 관심사다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-D0481D9EE28232FD419C","evidence_a":{"line_end":82,"line_start":82,"quote":"- Gradle multi-module dependency rule."},"evidence_b":{"line_end":90,"line_start":90,"quote":"- ArchUnit rule 위치와 실행 기준."},"proof_manifest":null,"rationale":"SCP-1(Gradle dependency rule)과 SCP-9(ArchUnit rule 위치/실행 기준)은 branch가 소유하는 서로 다른 관심사다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-E77A0F0CD9196942F24D","evidence_a":{"line_end":94,"line_start":94,"quote":"- formatter / style lint 규칙."},"evidence_b":{"line_end":96,"line_start":96,"quote":"- Spring Modulith verifier 도입."},"proof_manifest":null,"rationale":"SCP-10(formatter/style lint 제외)과 SCP-11(Spring Modulith verifier 제외)은 서로 다른 두 out-of-scope 제외 항목이다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-9170ABB4CCA96938CAA3","evidence_a":{"line_end":88,"line_start":88,"quote":"- mapper boundary rule."},"evidence_b":{"line_end":89,"line_start":89,"quote":"- transaction annotation forbidden import rule."},"proof_manifest":null,"rationale":"SCP-7(mapper boundary rule)과 SCP-8(transaction annotation forbidden import rule)은 branch가 소유하는 서로 다른 관심사다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-E6565A8804212349EEA7","evidence_a":{"line_end":88,"line_start":88,"quote":"- mapper boundary rule."},"evidence_b":{"line_end":90,"line_start":90,"quote":"- ArchUnit rule 위치와 실행 기준."},"proof_manifest":null,"rationale":"SCP-7(mapper boundary rule)과 SCP-9(ArchUnit rule 위치/실행 기준)은 branch가 소유하는 서로 다른 관심사다.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-B3790F92A32FE5C6A76B","evidence_a":{"line_end":89,"line_start":89,"quote":"- transaction annotation forbidden import rule."},"evidence_b":{"line_end":90,"line_start":90,"quote":"- ArchUnit rule 위치와 실행 기준."},"proof_manifest":null,"rationale":"SCP-8(transaction annotation forbidden import rule)과 SCP-9(ArchUnit rule 위치/실행 기준)은 branch가 소유하는 서로 다른 관심사다.","verdict":"COMPLEMENTARY"}]},"auditor":{"contract_version":"semantic-coherence/v1","model_id":"claude-opus-4-8","run_id":"a7a57920061665b61"},"auditor_contract_sha256":"1cbc67c27e5183a272687f635e3682a26d56785a1cf144da562eb7edd8f6cbce","coverage":{"candidate_pairs":84,"dropped_pairs":0,"eligible_surfaces":6,"processed_pairs":84,"processed_surfaces":6},"document_id":"cd57931a048eff49d788e00b585deb922615310f00e6417b8f0555b96baf4746","document_sha256":"e0fca6bc00c98cf9dd8bb67fdb4abccfc0586efc3eb00e5b213f13a578c496e6","findings":{"blocking":0,"readiness_blocking":0,"verified":0},"mode":"local","ontology_sha256":"5d601b96f0ca4d75eea89e9086c38e3acf2c4f0c7833846ef58c6a5719603126","policy_sha256":"0465598e9c1c4f2c3400ba51bf4719aa4890d60d56dc83304fb2224e660562b7","proof_manifest_sha256":"37517e5f3dc66819f61f5a7bb8ace1921282415f10551d2defa5c3eb0985b570","schema_version":"semantic-certificate/v1","semantic_audit_sha256":"609e5dd574457bc494e76742a3c7efa75b166f2b878da8bcf38f13d788927421","subject":"raw/branch-notes/feature-architecture-enforcement-rules.md","typed_contract_graph_sha256":"481fc3335a494c454ab13ad1d6a6ddccdec99aa8e083f6720ff8886bfa19cc89","verdict":"PASS"}