Files
llm-wiki/harness/state/semantic-certificates/10affe6752bb478c4c1079bc82652bf3c8627d68f6111e5110662b8f8c56fa29/dfa8f18c855db22d25a8090d8aee93046b558c1ec42d61a501d77b6bda919e16.json
T

2 lines
84 KiB
JSON

{"audit_request":{"assertions":[{"assertion_id":"A01","condition":"생성 시 프로젝트 개정 1","line_end":96,"line_start":96,"modality":"observed","object":"패킷 스키마 contract_packet: 1","predicate":"has_schema","quote":"- **패킷 스키마**: `contract_packet: 1`","scope":"branch contract packet","source_surface":"SURF-CE9EE097C9135CAE08B2","subject":"브랜치 계약 패킷"},{"assertion_id":"A02","condition":"완료 조건","line_end":97,"line_start":97,"modality":"must","object":"boundary·mapping 6필드 contract와 negative fixture 통과","predicate":"requires","quote":"- **완료 조건**: boundary·mapping 6필드 contract와 negative fixture가 통과한다","scope":"branch completion","source_surface":"SURF-CE9EE097C9135CAE08B2","subject":"브랜치 계약 패킷"},{"assertion_id":"A03","condition":"상속한 프로젝트 결정, Work Item 완료 조건에 적용","line_end":104,"line_start":104,"modality":"observed","object":"default mapper이며 MapStruct는 optional profile (상속한 프로젝트 결정 DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MAPPING-001@1)","predicate":"other","quote":"| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MAPPING-001@1` | 수기 mapper와 record canonical constructor가 default이며 MapStruct는 optional profile이다 | Work Item 완료 조건에 적용 | [[raw/project-notes/ca-skeleton-operational-contract]] |","scope":"mapping tooling","source_surface":"SURF-CE9EE097C9135CAE08B2","subject":"수기 mapper + record canonical constructor"},{"assertion_id":"A04","condition":"이 packet에서 복제하지 않는다","line_end":109,"line_start":109,"modality":"must","object":"branch-local 결정","predicate":"owns","quote":"> 기존 branch-local 결정은 아래 `## Decision Evidence Map / 결정-근거 매핑`의 D-row가 소유하며 이 packet에서 복제하지 않는다.","scope":"branch-local decisions","source_surface":"SURF-CE9EE097C9135CAE08B2","subject":"결정-근거 매핑 D-row"},{"assertion_id":"A05","condition":"status: partially-implemented — 정적 ArchUnit rule 미작성 planned 잔존","line_end":356,"line_start":356,"modality":"must_not","object":"domain object 직접 반환 (response mapper boundary 강제)","predicate":"forbids","quote":"| controller 가 domain object 를 직접 반환하지 않는지 (response mapper boundary 강제) | implicit Jackson serialization 으로 domain object 가 직접 직렬화될 위험 | ArchUnit rule (controller method return type 은 DTO/record/`ResponseEntity<DTO>` 만) + integration test | `partially-implemented` (2026-05-29 3차 패스: `EnvelopeBodyAdvice` 가 모든 컨트롤러 응답을 `Envelope<T>` 로 wrap, 기존 컨트롤러는 DTO record 만 반환 컨벤션. 컨트롤러 반환 타입의 정적 ArchUnit rule 은 미작성 — `planned` 잔존.) |","scope":"controller response boundary","source_surface":"SURF-92150135825E651FE09F","subject":"controller"},{"assertion_id":"A06","condition":"status: planned","line_end":357,"line_start":357,"modality":"must_not","object":"application service method signature 에 직접 노출","predicate":"forbids","quote":"| request DTO 가 application service 의 method signature 에 직접 나타나지 않는지 | DTO 가 service layer 까지 leak 가능성 | ArchUnit rule (`service` package method 의 parameter type 은 `Command`/`Query` record 만) | `planned` |","scope":"application service boundary","source_surface":"SURF-92150135825E651FE09F","subject":"request DTO"},{"assertion_id":"A07","condition":"status: needs-confirmation — envelope 기대값 갱신 wire test 재실행 전","line_end":364,"line_start":364,"modality":"must","object":"unknown field → 400 + VALIDATION_FAILED 거부","predicate":"validates","quote":"| (B1) `spring.jackson.deserialization.fail-on-unknown-properties=true` 가 실제 설정되어 unknown field 가 400 으로 거부되는지 | Spring Boot `JacksonProperties` 가 Jackson default 를 silent override 가능 | `application.yaml` 명시 검증 + integration test (unknown field 가 포함된 JSON 요청 → 400 응답 + `VALIDATION_FAILED` code) | unknown-field 거부와 4종 binding 설정은 기존 테스트 기록이 있으나 당시 envelope 기대값이 제거된 `MALFORMED_REQUEST`였다. D10의 `VALIDATION_FAILED`로 갱신한 wire test 재실행 전까지 `needs-confirmation` |","scope":"request binding","source_surface":"SURF-92150135825E651FE09F","subject":"Jackson deserialization 설정"},{"assertion_id":"A08","condition":"status: actually-implemented — BoundaryDemoControllerWireTest b2","line_end":367,"line_start":367,"modality":"must","object":"absent / null / value 3-state 구분","predicate":"validates","quote":"| (B2) PATCH endpoint 가 absent / null / 빈 값을 mapper 에서 구분하여 처리하는지 | record 기본값으로 mapping 시 PATCH 가 null 로 덮어쓰는 silent overwrite | PATCH integration test (3 케이스: field 없음 → 무변경, field=null → 명시적 null, field=value → 갱신) + JSON Schema validation | `actually-implemented` (2026-05-29 3차 패스: `BoundaryDemoControllerWireTest#b2_patch_field_absent_is_distinguished_from_explicit_null_and_value` 가 PATCH `/demo/boundary/patch-demo` 로 3 케이스 wire-level pin. 기존 `UpdateProfileRequest` + `UpdateProfileCommand` + `UserService.updateProfile` 도 `JsonNullable<T>` / `Patch<T>` 로 마이그레이션 — silent overwrite 위험 제거.) |","scope":"PATCH mapping","source_surface":"SURF-92150135825E651FE09F","subject":"PATCH endpoint mapper"},{"assertion_id":"A09","condition":"status: actually-implemented — ArchUnit rules","line_end":373,"line_start":373,"modality":"must_not","object":"ObjectMapper.enableDefaultTyping() / activateDefaultTyping(LaissezFaireSubTypeValidator) 호출","predicate":"forbids","quote":"| (B5) `ObjectMapper.enableDefaultTyping()` / `activateDefaultTyping(LaissezFaireSubTypeValidator)` 호출이 코드 어디에도 없는지 | CVE-2019-14379 류 gadget chain RCE risk | ArchUnit rule (`enableDefaultTyping` / `LaissezFaireSubTypeValidator` import 금지) + dependency check (jackson-databind 버전 최소 2.10+) | `actually-implemented` (2026-05-29: `no_jackson_laissez_faire_subtype_validator` + `no_jackson_enable_default_typing_call` 두 ArchUnit rule, `DefaultTypingFixture` 가 violations-as-data 로 catch 검증. jackson-databind 버전 확인은 별도 supply-chain branch 책임.) |","scope":"jackson polymorphic deserialization","source_surface":"SURF-92150135825E651FE09F","subject":"코드베이스"},{"assertion_id":"A10","condition":"status: actually-implemented — BoundaryDemoControllerWireTest b8","line_end":378,"line_start":378,"modality":"must","object":"success:false + error.code=BATCH_PARTIAL_FAILURE + error.details[] 항목별 결과 shape","predicate":"has_schema","quote":"| (B8) Bulk endpoint 의 response 가 `success: false` + `error.code = BATCH_PARTIAL_FAILURE` + `error.details[]` 항목별 결과 shape 을 따르는지 | 단일 항목 endpoint 와 schema 혼동 risk | bulk endpoint contract test (전체 성공 / 전체 실패 / 부분 실패 3 케이스) + OpenAPI shape 분기 검증 | `actually-implemented` (2026-05-29 3차 패스: `BoundaryDemoControllerWireTest` 의 3 bulk 케이스 (`b8_all_success`, `b8_partial_failure`, `b8_all_failures_take_the_same_partial_branch`) 가 POST `/demo/boundary/bulk` 로 wire-level 검증. OpenAPI 분기는 스펙 자체가 부재라 별도.) |","scope":"bulk endpoint response","source_surface":"SURF-92150135825E651FE09F","subject":"Bulk endpoint response"},{"assertion_id":"A11","condition":"결정-근거 매핑 라벨 규약","line_end":330,"line_start":330,"modality":"must_not","object":"공식 best practice 로 격상 (company-case-study 라벨링만 허용)","predicate":"forbids","quote":"> 각 결정이 어떤 raw source claim 으로 뒷받침되는지 명시. company-tech-blog 출처는 `company-case-study` 로 라벨링하며 공식 best practice 로 격상하지 않는다.","scope":"evidence labeling","source_surface":"SURF-5BFA9F92FD782C0F1C6A","subject":"company-tech-blog 출처"},{"assertion_id":"A12","condition":"Evidence: UNSUPPORTED_DECISION — cited sources 중 normative 진술 없음","line_end":334,"line_start":334,"modality":"must","object":"모든 경계에 validation/mapping 책임을 둠","predicate":"enforces","quote":"| D1 | 모든 경계에 validation/mapping 책임을 둠 (2026-05-21) | UNSUPPORTED_DECISION (Clean Architecture / Hexagonal boundary 책임 원칙은 일반 design wisdom 이지만 본 branch 가 cite 한 sources — Stripe/Toss/RFC 7807/Spring/Google/JSON:API/GraphQL/GitHub — 중 normative 진술 없음) | N/A | DDD boundary / Hexagonal port-adapter 패턴의 raw 인용 (예: Vaughn Vernon, Reflectoring) 별도 보강 필요 |","scope":"boundary responsibility","source_surface":"SURF-5BFA9F92FD782C0F1C6A","subject":"D1 (경계 validation/mapping 책임)"},{"assertion_id":"A13","condition":"Evidence: UNSUPPORTED_DECISION — project-internal tool selection","line_end":337,"line_start":337,"modality":"observed","object":"수기 mapper + record canonical constructor default; MapStruct optional (architecture exemption + contract test)","predicate":"uses","quote":"| D4 | mapper 도구 기본값 — 수기 mapper + record canonical constructor; MapStruct optional (사용 시 architecture exemption + contract test 필요) | UNSUPPORTED_DECISION (project-internal tool selection; cited sources 중 mapper 도구 선택 관련 normative / vendor 진술 없음) | N/A | 수기 mapper 의 boilerplate 비용 vs MapStruct generated 코드의 architecture leak 위험 trade-off 는 별도 측정 / vendor 비교 필요 |","scope":"mapper tooling decision","source_surface":"SURF-5BFA9F92FD782C0F1C6A","subject":"mapper 도구 기본값 (D4)"},{"assertion_id":"A14","condition":"Evidence: official-standard + official-vendor-doc + company-case-study; sibling branch D5 동일","line_end":338,"line_start":338,"modality":"must_not","object":"RFC 7807 ProblemDetail 채택 (custom envelope 채택, 명시적 거부)","predicate":"forbids","quote":"| D5 | error envelope shape — custom 채택, RFC 7807 ProblemDetail 명시적 거부 (sibling branch `feature-business-rule-validation-contract` 와 동일 결정 공유) | `raw/official-docs/problem-detail-rfc-7807.md#RFC7807-C1` (canonical model `application/problem+json`), `#RFC7807-C2` (`type` URI primary identifier), `#RFC7807-C3` (extension 가능, unknown ignore), `raw/official-docs/spring-problem-detail.md#SPRING-PD-C1` (Spring `ProblemDetail` = RFC 9457 representation), `#SPRING-PD-C2` (모든 Spring MVC 예외가 `ErrorResponse` 구현 — envelope 와 충돌), `raw/official-docs/spring-mvc-rest-exception-handling.md#SPRING-MVC-EXC-C1` (동일 사실의 primary source — 모든 Spring MVC 내장 예외는 `ErrorResponse` 구현), `raw/company-tech-blogs/stripe-error-format.md#STRIPE-ERR-C5` (Stripe 4-종 type enum), `raw/company-tech-blogs/toss-payments-error-format.md#TOSS-ERR-C1` (Toss `{code, message}` 평면) | `official-standard + official-vendor-doc + company-case-study` | sibling branch D5 와 동일 evidence — cross-branch 일관성 확보됨. `SPRING-MVC-EXC-C1` 이 `SPRING-PD-C2` 를 corroborate. 단 RFC 7807 미채택 trade-off 의 ca-tmpl 측 해석은 cited sources 가 직접 권고하지 않음 |","scope":"error envelope shape","source_surface":"SURF-5BFA9F92FD782C0F1C6A","subject":"error envelope (D5)"},{"assertion_id":"A15","condition":"Evidence: official-vendor-doc (Spring normative) + MAPPING_FAILED UNSUPPORTED (D10)","line_end":339,"line_start":339,"modality":"must","object":"VALIDATION 카테고리 (mapper 내부 예외는 별도 @ExceptionHandler → MAPPING_FAILED)","predicate":"maps_to","quote":"| D10 | **B3 블라인드 해소** — `HttpMessageNotReadableException` (JSON 역직렬화 실패) 은 `VALIDATION` 카테고리로 분류; `MethodArgumentNotValidException` (Bean Validation 실패) 은 `VALIDATION` 카테고리로 분류; mapper 내부 예외 (`IllegalArgumentException`, MapStruct NPE, record canonical constructor `IllegalStateException`) 는 Spring 이 자동 처리하지 않으므로 별도 `@ExceptionHandler` 로 처리하며 ca-tmpl operational contract 에서 `MAPPING_FAILED` 카테고리로 분류 | `raw/official-docs/spring-mvc-rest-exception-handling.md#SPRING-MVC-EXC-C1` (모든 Spring MVC 내장 예외는 `ErrorResponse` 구현 — `HttpMessageNotReadableException` 포함), `#SPRING-MVC-EXC-C2` (`ResponseEntityExceptionHandler` 가 모든 Spring MVC 내장 예외 + `ErrorResponseException` 처리), `#SPRING-MVC-EXC-C4` (`HttpMessageNotReadableException` 은 normative 처리 목록에 있음), `#SPRING-MVC-EXC-C5` (`MethodArgumentNotValidException` 은 normative 처리 목록에 있음, {0}=global errors, {1}=field errors); `raw/official-docs/validation-jakarta-bean-validation-3.0-spec.md#JBV-3.0-C5` (PARAMETER ElementType → Bean Validation 으로 method parameter 검증 → `MethodArgumentNotValidException` 발생 경로의 2차 normative 확인); mapper-internal 예외의 `MAPPING_FAILED` 카테고리 코드 자체는 UNSUPPORTED_DECISION — ca-tmpl 고유 operational contract | `official-vendor-doc` (`HttpMessageNotReadableException` / `MethodArgumentNotValidException` → Spring normative) + `official-standard` (JBV-3.0-C5 corroboration) + UNSUPPORTED (`MAPPING_FAILED` 카테고리 코드 및 mapper-internal 예외 분류) | Spring 이 `HttpMessageNotReadableException` 의 HTTP status 를 400 으로 설정한다는 것은 `spring-mvc-rest-exception-handling.md` 의 message code 표에서 직접 명시되지 않음 — `ErrorResponse` 구현체 내부(Spring source)에서 정의됨. `MAPPING_FAILED` 라는 category code 를 Operational Error Category 에 추가하는 결정은 ca-tmpl 내부 결정이며 별도 project-note 갱신 필요 |","scope":"exception categorization","source_surface":"SURF-5BFA9F92FD782C0F1C6A","subject":"HttpMessageNotReadableException / MethodArgumentNotValidException"},{"assertion_id":"A16","condition":"Evidence: official-standard RFC7396 null=deletion + UNSUPPORTED Hexagonal 원칙","line_end":341,"line_start":341,"modality":"must","object":"application command/query mapper 경유 (service 직접 전달 금지); PATCH 시 null vs absent 구분","predicate":"requires","quote":"| D7 | request DTO → application command/query mapper 강제 (DTO 의 service 직접 전달 금지); PATCH 요청 시 mapper 가 null vs absent 를 구분해야 함 (B2 블라인드) | `raw/official-docs/patch-json-merge-rfc7396.md#RFC7396-C2` (\"Null values in the merge patch are given special meaning to indicate the removal of existing values in the target.\" — null=deletion normative), `#RFC7396-C3` (merge patch 는 explicit null 사용 시 부적합), `#RFC7396-C4` (배열 부분 수정 불가 — merge patch 한계) | `official-standard` (null=deletion 근거) + UNSUPPORTED (Hexagonal boundary 원칙 자체) | RFC7396 은 null=deletion 의 normative 근거를 제공하나, Java record mapper 에서 absent field 를 별도 처리하는 구현 방법은 직접 권고하지 않음. Hexagonal port-adapter boundary 원칙 raw (예: Reflectoring, Woowahan) 별도 인용 보강 권장 |","scope":"request mapping boundary","source_surface":"SURF-5BFA9F92FD782C0F1C6A","subject":"request DTO (D7)"},{"assertion_id":"A17","condition":"Evidence: cross-branch-SSOT resource-identifier D17 + project-ssot §34","line_end":348,"line_start":348,"modality":"must","object":"resource-identifier 4 ArchUnit rules 호스팅 등록 (no_find_by_id_without_tenant 는 tenant branch 로 이관)","predicate":"uses","quote":"| D15 | **B9 cross-cite** — Resource identifier ArchUnit rules **4개** (`no_long_id_pk` — `..domain..` 한정, `no_uuid_random_in_controller`, `no_math_random_for_id`, `no_varchar_255_for_id_column`) 를 본 branch ArchUnit suite 에 등록. 구현 skeleton 은 resource-identifier branch §구현 가이드 §6. **5번째 rule `no_find_by_id_without_tenant` 는 `feature-tenant-context-policy` (예정 branch) 로 이관** — tenant 모델 부재 시 production code 가 모두 깨지는 false positive 차단 | [[raw/branch-notes/feature-resource-identifier-contract]] D17 (rule SSOT — 4 rules), D5 (ID generation = domain port + application 주입), D10 (PostgreSQL `uuid` native), D13 (ID 내 tenant 인코딩 거부 — 형식적 위치만). project §34 Stack Commitment (archunit-junit5 1.3.0) | `cross-branch-SSOT` (resource-identifier D17) + `project-ssot` (§34 archunit-junit5 version) | `haveExplicitColumnLength()` custom ArchCondition 의 archunit-junit5 1.3.0 API 호환성 검증 필요 (resource-identifier branch §구현 가이드 §6 UNSUPPORTED_IMPL_DECISION). 본 4개 rule 의 실제 코드는 boundary branch ArchUnit suite 가 호스팅, *결정 SSOT* 는 resource-identifier branch D17. `no_find_by_id_without_tenant` 활성화는 multi-tenancy-contract 도착 시 |","scope":"archunit rule hosting","source_surface":"SURF-5BFA9F92FD782C0F1C6A","subject":"boundary branch ArchUnit suite (D15)"},{"assertion_id":"A18","condition":"B8; PATCH 3-state 구분 실패 시 silent overwrite (B2)","line_end":436,"line_start":436,"modality":"observed","object":"부분 실패와 동일 BATCH_PARTIAL_FAILURE (HTTP 200) branch, 분기는 envelope.success 로만","predicate":"has_failure_behavior","quote":"- **Edge**: PATCH 의 absent vs explicit-null vs value 3-state — 구분 실패 시 silent overwrite (B2). bulk endpoint 의 전체 실패도 부분 실패와 동일 `BATCH_PARTIAL_FAILURE`(HTTP 200) branch 를 타며, 분기는 `envelope.success` 로만 (B8).","scope":"edge conditions","source_surface":"SURF-82E4BE20247CB6B9313F","subject":"bulk endpoint 전체 실패"},{"assertion_id":"A19","condition":"B3; virtual thread ThreadLocal/MDC propagate 실패 시 traceId 유실 (B6)","line_end":437,"line_start":437,"modality":"observed","object":"MappingException wrap 누락 시 INTERNAL_ERROR 로 새어 분류 오류","predicate":"has_failure_behavior","quote":"- **Failure mode**: ① mapper-internal 예외가 `MappingException` wrap 누락 시 `INTERNAL_ERROR` 로 새어 분류 오류 (B3). ② virtual thread 환경에서 `ThreadLocal`/MDC context 가 application layer 까지 propagate 안 되면 traceId 유실 (B6). ③ ArchUnit 정적 강제는 바이트코드 carrier(어노테이션/import/호출)만 탐지 — 메서드 본문 free-form 문자열은 한계.","scope":"failure modes","source_surface":"SURF-82E4BE20247CB6B9313F","subject":"mapper-internal 예외"},{"assertion_id":"A20","condition":"등록 완료","line_end":438,"line_start":438,"modality":"must","object":"canonical SSOT ca-skeleton-operational-contract §6 등록","predicate":"requires","quote":"- **Dependency**: `MAPPING_FAILED` / `BATCH_PARTIAL_FAILURE` 신규 code 는 canonical SSOT [[raw/project-notes/ca-skeleton-operational-contract]] §6 등록에 의존 (등록 완료). package convention glob 은 §20 Skeleton Blueprint 에 의존. B9 ArchUnit rule 은 [[raw/branch-notes/feature-resource-identifier-contract]] D17 을 cross-cite.","scope":"code dependency","source_surface":"SURF-82E4BE20247CB6B9313F","subject":"MAPPING_FAILED / BATCH_PARTIAL_FAILURE 신규 code"},{"assertion_id":"A21","condition":"D10 VALIDATION 카테고리 일체","line_end":219,"line_start":219,"modality":"must","object":"HTTP 400, retryable false — Bean Validation/JSON 파싱/unknown field/enum/polymorphic discriminator 불일치 라우팅","predicate":"maps_to","quote":"| `VALIDATION_FAILED` | 400 | false | Bean Validation 실패, JSON 파싱 실패, unknown field, 알 수 없는 enum value, polymorphic discriminator 불일치 — D10 의 \"VALIDATION 카테고리\" 일체 | `HttpMessageNotReadableException`, `MethodArgumentNotValidException`, `ConstraintViolationException` 모두 라우팅 |","scope":"error code table","source_surface":"SURF-90484F839E1996B87C1C","subject":"VALIDATION_FAILED"},{"assertion_id":"A22","condition":"D14 bulk 부분/전체 실패","line_end":221,"line_start":221,"modality":"must","object":"HTTP 200, retryable false, envelope.success=false — 단일 항목 endpoint 와 동일 응답 표면, 분기는 envelope.success","predicate":"maps_to","quote":"| `BATCH_PARTIAL_FAILURE` | 200 | false | Bulk endpoint 의 부분/전체 실패. HTTP 200 + envelope.success=false (단일 항목 endpoint 와 *동일* 응답 표면, *분기는 envelope.success* 로). | `BulkEnvelope.partial(...)` |","scope":"error code table","source_surface":"SURF-90484F839E1996B87C1C","subject":"BATCH_PARTIAL_FAILURE"},{"assertion_id":"A23","condition":"web/outbound/persistence ACL 어디든; 정적 강제 없이 컨벤션 (사용자 trade-off)","line_end":251,"line_start":251,"modality":"must","object":"MappingException 으로 wrap (그러면 handleMapping 이 MAPPING_FAILED 로 라우팅)","predicate":"requires","quote":"- Mapper 내부 (web/outbound/persistence ACL 어디든) 가 던진 *논리적 mapping 실패* 는 반드시 `MappingException` 으로 **wrap 해서** 던진다. 그러면 `handleMapping` 이 `MAPPING_FAILED` 로 라우팅.","scope":"mapping exception routing","source_surface":"SURF-90484F839E1996B87C1C","subject":"Mapper 내부 논리적 mapping 실패"},{"assertion_id":"A24","condition":"D10 및 B1 테스트 계약과 충돌","line_end":223,"line_start":223,"modality":"observed","object":"제거됨 — unknown field 는 VALIDATION_FAILED 로 통합, 구체적 실패 모드는 error.details.cause 로 surface","predicate":"other","quote":"> **`MALFORMED_REQUEST` 는 제거되었다.** 초기 구현은 unknown field 를 `MALFORMED_REQUEST`(400) 로 매핑했으나 D10 의 \"HttpMessageNotReadableException → VALIDATION category\" 와 본 branch §테스트 계약 \"(B1) 400 + VALIDATION_FAILED\" 와 충돌. `VALIDATION_FAILED` 로 통합하고 *구체적 실패 모드*(UnrecognizedPropertyException / InvalidTypeIdException / JsonParseException 등) 는 `error.details.cause` 로 surface 한다.","scope":"error code deprecation","source_surface":"SURF-90484F839E1996B87C1C","subject":"MALFORMED_REQUEST"},{"assertion_id":"A25","condition":"sample-portfolio 도메인 컨트롤러 + production HealthcheckController 포함; D6 직접 권고","line_end":262,"line_start":262,"modality":"must","object":"모든 @RestController 응답을 Envelope<T> 로 자동 wrap","predicate":"produces","quote":"- *모든* `@RestController` 응답 (sample-portfolio 의 도메인 컨트롤러 + production `HealthcheckController` 포함) 은 `EnvelopeBodyAdvice` 가 자동으로 `Envelope<T>` 로 wrap.","scope":"envelope wrapping","source_surface":"SURF-90484F839E1996B87C1C","subject":"EnvelopeBodyAdvice"},{"assertion_id":"A26","condition":"B4-2; UNSUPPORTED_IMPL_DECISION — rule 명명/algorithm/limit 값 3 임의","line_end":274,"line_start":274,"modality":"must","object":"..adapter.web..dto.. 클래스의 @Valid cascade depth ≤ 3 (위반 시 build 실패)","predicate":"validates","quote":"- ArchUnit `valid_cascade_depth_at_most_three` 규칙이 `..adapter.web..dto..` 패키지 클래스의 `@Valid` 필드를 재귀 따라가며 도메인 내부 클래스 사이의 cascade 깊이를 계산.","scope":"cascade depth enforcement","source_surface":"SURF-90484F839E1996B87C1C","subject":"ArchUnit valid_cascade_depth_at_most_three 규칙"},{"assertion_id":"A27","condition":"B5; D12 + JACK-POLY-C3","line_end":286,"line_start":286,"modality":"must_not","object":"enableDefaultTyping() (no-arg, deprecated) 호출","predicate":"forbids","quote":"- `enableDefaultTyping()` (no-arg, deprecated) 호출 → 차단 (ArchUnit `no_jackson_enable_default_typing_call`).","scope":"jackson default typing","source_surface":"SURF-90484F839E1996B87C1C","subject":"ArchUnit no_jackson_enable_default_typing_call"},{"assertion_id":"A28","condition":"B5; D12 + JACK-POLY-C4","line_end":288,"line_start":288,"modality":"may","object":"허용 — 안전한 allowlist 패턴, 차단 대상 아님 (BasicPolymorphicTypeValidatorAllowlistTest 4 case pin)","predicate":"other","quote":"- `activateDefaultTyping(BasicPolymorphicTypeValidator allowlist)` → **허용**. 차단 대상 아님. 안전한 allowlist 패턴이며 `sample-portfolio` 의 `BasicPolymorphicTypeValidatorAllowlistTest` 가 4 case 로 pin (allowlisted Cat/Dog 통과, 비허용 subtype 거부, 임의 JDK 클래스 거부).","scope":"jackson polymorphic allowlist","source_surface":"SURF-90484F839E1996B87C1C","subject":"activateDefaultTyping(BasicPolymorphicTypeValidator allowlist)"},{"assertion_id":"A29","condition":"D1 + D8; EnvelopeBodyAdvice silent 직렬화 회귀 차단","line_end":299,"line_start":299,"modality":"must_not","object":"controller method 반환 타입이 ..domain.entity.. / ..adapter.persistence.entity.. / ..repository.. 거주 (위반 시 build 실패)","predicate":"forbids","quote":"- `controllers_do_not_return_domain_or_entity_types` — controller method 반환 타입이 `..domain.entity..` 또는 `..adapter.persistence.entity..` 또는 `..repository..` 에 거주하면 build 실패. `EnvelopeBodyAdvice` 의 자동 wrap 이 도메인 객체를 silent 직렬화하는 회귀를 *정적* 으로 차단.","scope":"controller return type","source_surface":"SURF-90484F839E1996B87C1C","subject":"ArchUnit controllers_do_not_return_domain_or_entity_types"},{"assertion_id":"A30","condition":"D7; controller 의 DTO→Command/Query 변환 우회 회귀 차단","line_end":300,"line_start":300,"modality":"must_not","object":"application package public method 가 ..adapter.web..dto.. 파라미터 수용 (위반 시 build 실패)","predicate":"forbids","quote":"- `application_methods_do_not_accept_web_dtos` — application package 의 public method 가 `..adapter.web..dto..` 파라미터를 받으면 build 실패. controller 가 DTO → Command/Query 변환을 우회하는 회귀 차단.","scope":"application parameter type","source_surface":"SURF-90484F839E1996B87C1C","subject":"ArchUnit application_methods_do_not_accept_web_dtos"},{"assertion_id":"A31","condition":"D5 + SPRING-PD-C1/C2 + SPRING-MVC-EXC-C1","line_end":309,"line_start":309,"modality":"must_not","object":"org.springframework.http.ProblemDetail import (D5 RFC 7807 명시적 거부 코드 강제)","predicate":"forbids","quote":"- `no_problem_detail_usage` — `org.springframework.http.ProblemDetail` import 자체를 차단. D5 의 \"RFC 7807 명시적 거부\" 가 코드 단계에서 강제됨. 신규 작업자가 무심코 `ProblemDetail` 을 부활시키면 build 실패.","scope":"problemdetail enforcement","source_surface":"SURF-90484F839E1996B87C1C","subject":"ArchUnit no_problem_detail_usage"},{"assertion_id":"A32","condition":"B2; D7 + RFC7396-C2/C3","line_end":310,"line_start":310,"modality":"must_not","object":"application/merge-patch+json content type 도입 (RFC 7396 미채택 정적 강제, 위반 시 build 실패)","predicate":"forbids","quote":"- `no_merge_patch_json_media_type_string` — `@RequestMapping(consumes=\"application/merge-patch+json\")` 같은 RFC 7396 도입을 build 실패로 차단. B2 의 \"RFC 7396 미채택\" 정적 강제.","scope":"merge patch enforcement","source_surface":"SURF-90484F839E1996B87C1C","subject":"ArchUnit no_merge_patch_json_media_type_string"},{"assertion_id":"A33","condition":"포함 범위","line_end":135,"line_start":135,"modality":"must","object":"application command/query (mapper)","predicate":"maps_to","quote":"- request DTO -> application command/query mapper.","scope":"in-scope","source_surface":"SURF-2586B53D5B17431F44A9","subject":"request DTO"},{"assertion_id":"A34","condition":"포함 범위","line_end":137,"line_start":137,"modality":"must_not","object":"response DTO 직접 노출","predicate":"forbids","quote":"- domain object -> response DTO 직접 노출 금지.","scope":"boundary scope","source_surface":"SURF-2586B53D5B17431F44A9","subject":"domain object"},{"assertion_id":"A35","condition":"포함 범위","line_end":139,"line_start":139,"modality":"must","object":"request context propagation 담당","predicate":"other","quote":"- filter/interceptor request context propagation.","scope":"in-scope","source_surface":"SURF-2586B53D5B17431F44A9","subject":"filter/interceptor"},{"assertion_id":"A36","condition":"제외 범위","line_end":143,"line_start":143,"modality":"observed","object":"특정 도메인 validator 구현 — 본 branch 에서 구현하지 않음","predicate":"other","quote":"- 특정 도메인 validator 구현.","scope":"out-of-scope","source_surface":"SURF-2586B53D5B17431F44A9","subject":"본 branch scope"},{"assertion_id":"A37","condition":"제외 범위","line_end":144,"line_start":144,"modality":"observed","object":"DB/JPA exception mapping — 본 branch 에서 구현하지 않음","predicate":"other","quote":"- DB/JPA exception mapping.","scope":"out-of-scope","source_surface":"SURF-2586B53D5B17431F44A9","subject":"본 branch scope"}],"candidate_manifest_sha256":"b97c089572b07aac6efe4cfff5adef0111a0012e5ec46da6a942787caef549ac","candidates":[{"assertion_a":"A03","assertion_b":"A20","candidate_id":"SEM-9B6B19B12B50D35B2932","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A05","assertion_b":"A06","candidate_id":"SEM-09C4C23E9C67E06C9647","grouping_key":{"condition":"","predicate":"forbids","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A05","assertion_b":"A25","candidate_id":"SEM-9EDCDAA3EDC4148845E3","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A05","assertion_b":"A29","candidate_id":"SEM-F42B72B8935713DFBC0A","grouping_key":{"condition":"","predicate":"forbids","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A07","assertion_b":"A21","candidate_id":"SEM-A256E90C6C65A2B4907A","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A07","assertion_b":"A24","candidate_id":"SEM-BA1AEF52F9B9725415C0","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A08","assertion_b":"A09","candidate_id":"SEM-AA592D614F3D40327A28","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A08","assertion_b":"A10","candidate_id":"SEM-3AF87FC3906856813907","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A09","assertion_b":"A10","candidate_id":"SEM-D608EDA1C37381DFB9FA","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A09","assertion_b":"A27","candidate_id":"SEM-1EABE3ECB52507238C4E","grouping_key":{"condition":"","predicate":"forbids","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A12","assertion_b":"A13","candidate_id":"SEM-B8DF713BD23F31AEBCDF","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A14","assertion_b":"A15","candidate_id":"SEM-7B57FC8A8486B8829C1C","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A14","assertion_b":"A31","candidate_id":"SEM-14076C4101BD7EAF5065","grouping_key":{"condition":"","predicate":"forbids","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A15","assertion_b":"A16","candidate_id":"SEM-A7384E7EEDD8214CEB4D","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A15","assertion_b":"A20","candidate_id":"SEM-A231941E44FED6054F05","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A15","assertion_b":"A21","candidate_id":"SEM-791BDC8E8B1537E99FD2","grouping_key":{"condition":"","predicate":"maps_to","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A15","assertion_b":"A23","candidate_id":"SEM-B7ED647AA7F4C11387A7","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A16","assertion_b":"A30","candidate_id":"SEM-E0A31DD21EF17410E8C5","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A16","assertion_b":"A33","candidate_id":"SEM-D063D4BE219622E8AA52","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A17","assertion_b":"A20","candidate_id":"SEM-9BA95316AD1E3A3C440D","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A18","assertion_b":"A20","candidate_id":"SEM-155192A2E4DD545A7A16","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A18","assertion_b":"A22","candidate_id":"SEM-75C0C91FC70D72101E80","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A19","assertion_b":"A23","candidate_id":"SEM-B4126A354D0988DCACA2","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A20","assertion_b":"A22","candidate_id":"SEM-8A0185E8E137C69DEEEA","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A20","assertion_b":"A23","candidate_id":"SEM-A616145FF4FB28615604","grouping_key":{"condition":"","predicate":"requires","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A21","assertion_b":"A24","candidate_id":"SEM-1D6B225E94E47EB38482","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A25","assertion_b":"A29","candidate_id":"SEM-C95AF1C7F05EFC94C828","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A26","assertion_b":"A30","candidate_id":"SEM-FF6B1D66E74B36A76CB9","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A30","assertion_b":"A33","candidate_id":"SEM-7DC586E87494BD570776","grouping_key":{"condition":"","predicate":"","scope":"","subject":""},"rule_ids":["C6"]},{"assertion_a":"A36","assertion_b":"A37","candidate_id":"SEM-6E705D9D27D728AA905B","grouping_key":{"condition":"제외 범위","predicate":"other","scope":"out-of-scope","subject":"본 branch scope"},"rule_ids":["BASE"]}],"coverage":{"assertions":37,"candidate_pairs":30,"eligible_surfaces":6,"processed_surfaces":6},"document_sha256":"dfa8f18c855db22d25a8090d8aee93046b558c1ec42d61a501d77b6bda919e16","explicit_blocking":[],"mode":"local","ontology_sha256":"76d41a29233c830e1940ebb3244263e2b9bfb8dd6a01f2a1d1c51ce5a1b10468","output_schema":"semantic-audit-result/v1","schema_version":"semantic-verdict-request/v1","subject":"raw/branch-notes/feature-boundary-validation-mapping-contract.md"},"audit_request_sha256":"aa17234af477196f8aa6257c251a3f1a985f88648e2cc4120bd6f8474c45b751","audit_result":{"auditor":{"contract_version":"semantic-coherence/v1","model_id":"claude-opus-4-8","run_id":"ab9c0cc71d7c9e89a"},"mode":"local","request_sha256":"aa17234af477196f8aa6257c251a3f1a985f88648e2cc4120bd6f8474c45b751","schema_version":"semantic-audit-result/v1","subject":"raw/branch-notes/feature-boundary-validation-mapping-contract.md","verdicts":[{"candidate_id":"SEM-9B6B19B12B50D35B2932","evidence_a":{"line_end":104,"line_start":104,"quote":"| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MAPPING-001@1` | 수기 mapper와 record canonical constructor가 default이며 MapStruct는 optional profile이다 | Work Item 완료 조건에 적용 | [[raw/project-notes/ca-skeleton-operational-contract]] |"},"evidence_b":{"line_end":438,"line_start":438,"quote":"- **Dependency**: `MAPPING_FAILED` / `BATCH_PARTIAL_FAILURE` 신규 code 는 canonical SSOT [[raw/project-notes/ca-skeleton-operational-contract]] §6 등록에 의존 (등록 완료). package convention glob 은 §20 Skeleton Blueprint 에 의존. B9 ArchUnit rule 은 [[raw/branch-notes/feature-resource-identifier-contract]] D17 을 cross-cite."},"proof_manifest":null,"rationale":"A03 is the inherited project decision that manual mapper + record canonical constructor is the default (MapStruct optional); A20 is a dependency note that the new MAPPING_FAILED/BATCH_PARTIAL_FAILURE codes depend on canonical SSOT §6 registration (complete). Different contract properties (mapper tooling default vs error-code registration); they neither overlap nor conflict.","verdict":"CONSISTENT"},{"candidate_id":"SEM-09C4C23E9C67E06C9647","evidence_a":{"line_end":356,"line_start":356,"quote":"| controller 가 domain object 를 직접 반환하지 않는지 (response mapper boundary 강제) | implicit Jackson serialization 으로 domain object 가 직접 직렬화될 위험 | ArchUnit rule (controller method return type 은 DTO/record/`ResponseEntity<DTO>` 만) + integration test | `partially-implemented` (2026-05-29 3차 패스: `EnvelopeBodyAdvice` 가 모든 컨트롤러 응답을 `Envelope<T>` 로 wrap, 기존 컨트롤러는 DTO record 만 반환 컨벤션. 컨트롤러 반환 타입의 정적 ArchUnit rule 은 미작성 — `planned` 잔존.) |"},"evidence_b":{"line_end":357,"line_start":357,"quote":"| request DTO 가 application service 의 method signature 에 직접 나타나지 않는지 | DTO 가 service layer 까지 leak 가능성 | ArchUnit rule (`service` package method 의 parameter type 은 `Command`/`Query` record 만) | `planned` |"},"proof_manifest":null,"rationale":"Both forbid a boundary leak but on distinct boundaries: A05 forbids controllers returning domain objects (response boundary); A06 forbids request DTOs appearing in application service method signatures (application boundary). Each supplies a separate constraint without taking over the other's responsibility.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-9EDCDAA3EDC4148845E3","evidence_a":{"line_end":356,"line_start":356,"quote":"| controller 가 domain object 를 직접 반환하지 않는지 (response mapper boundary 강제) | implicit Jackson serialization 으로 domain object 가 직접 직렬화될 위험 | ArchUnit rule (controller method return type 은 DTO/record/`ResponseEntity<DTO>` 만) + integration test | `partially-implemented` (2026-05-29 3차 패스: `EnvelopeBodyAdvice` 가 모든 컨트롤러 응답을 `Envelope<T>` 로 wrap, 기존 컨트롤러는 DTO record 만 반환 컨벤션. 컨트롤러 반환 타입의 정적 ArchUnit rule 은 미작성 — `planned` 잔존.) |"},"evidence_b":{"line_end":262,"line_start":262,"quote":"- *모든* `@RestController` 응답 (sample-portfolio 의 도메인 컨트롤러 + production `HealthcheckController` 포함) 은 `EnvelopeBodyAdvice` 가 자동으로 `Envelope<T>` 로 wrap."},"proof_manifest":null,"rationale":"A25 states EnvelopeBodyAdvice wraps every @RestController response in Envelope<T>; A05 forbids controllers returning domain objects and notes DTO-record-only convention. The wrapping mechanism and the no-domain-return rule are compatible facets of the same response boundary; neither overrides the other.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-F42B72B8935713DFBC0A","evidence_a":{"line_end":356,"line_start":356,"quote":"| controller 가 domain object 를 직접 반환하지 않는지 (response mapper boundary 강제) | implicit Jackson serialization 으로 domain object 가 직접 직렬화될 위험 | ArchUnit rule (controller method return type 은 DTO/record/`ResponseEntity<DTO>` 만) + integration test | `partially-implemented` (2026-05-29 3차 패스: `EnvelopeBodyAdvice` 가 모든 컨트롤러 응답을 `Envelope<T>` 로 wrap, 기존 컨트롤러는 DTO record 만 반환 컨벤션. 컨트롤러 반환 타입의 정적 ArchUnit rule 은 미작성 — `planned` 잔존.) |"},"evidence_b":{"line_end":299,"line_start":299,"quote":"- `controllers_do_not_return_domain_or_entity_types` — controller method 반환 타입이 `..domain.entity..` 또는 `..adapter.persistence.entity..` 또는 `..repository..` 에 거주하면 build 실패. `EnvelopeBodyAdvice` 의 자동 wrap 이 도메인 객체를 silent 직렬화하는 회귀를 *정적* 으로 차단."},"proof_manifest":null,"rationale":"A05 (partially-implemented) says the strict allowlist ArchUnit rule (return type is DTO/record/ResponseEntity<DTO> only) remains planned/unwritten; A29 (implemented) is a denylist rule blocking return types in domain.entity/persistence.entity/repository. Different rule shapes: the denylist provides partial static enforcement while the fuller allowlist is still planned, matching A05's 'partially-implemented' status. Not mutually exclusive.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-A256E90C6C65A2B4907A","evidence_a":{"line_end":364,"line_start":364,"quote":"| (B1) `spring.jackson.deserialization.fail-on-unknown-properties=true` 가 실제 설정되어 unknown field 가 400 으로 거부되는지 | Spring Boot `JacksonProperties` 가 Jackson default 를 silent override 가능 | `application.yaml` 명시 검증 + integration test (unknown field 가 포함된 JSON 요청 → 400 응답 + `VALIDATION_FAILED` code) | unknown-field 거부와 4종 binding 설정은 기존 테스트 기록이 있으나 당시 envelope 기대값이 제거된 `MALFORMED_REQUEST`였다. D10의 `VALIDATION_FAILED`로 갱신한 wire test 재실행 전까지 `needs-confirmation` |"},"evidence_b":{"line_end":219,"line_start":219,"quote":"| `VALIDATION_FAILED` | 400 | false | Bean Validation 실패, JSON 파싱 실패, unknown field, 알 수 없는 enum value, polymorphic discriminator 불일치 — D10 의 \"VALIDATION 카테고리\" 일체 | `HttpMessageNotReadableException`, `MethodArgumentNotValidException`, `ConstraintViolationException` 모두 라우팅 |"},"proof_manifest":null,"rationale":"Both target unknown-field handling: A07 is the (B1) verification item (fail-on-unknown-properties=true, unknown field -> 400 VALIDATION_FAILED, still needs-confirmation pending wire-test rerun); A21 is the error-code table routing unknown field to VALIDATION_FAILED (400). They assert the same target value; A07's needs-confirmation is a test state, not a conflicting value.","verdict":"CONSISTENT"},{"candidate_id":"SEM-BA1AEF52F9B9725415C0","evidence_a":{"line_end":364,"line_start":364,"quote":"| (B1) `spring.jackson.deserialization.fail-on-unknown-properties=true` 가 실제 설정되어 unknown field 가 400 으로 거부되는지 | Spring Boot `JacksonProperties` 가 Jackson default 를 silent override 가능 | `application.yaml` 명시 검증 + integration test (unknown field 가 포함된 JSON 요청 → 400 응답 + `VALIDATION_FAILED` code) | unknown-field 거부와 4종 binding 설정은 기존 테스트 기록이 있으나 당시 envelope 기대값이 제거된 `MALFORMED_REQUEST`였다. D10의 `VALIDATION_FAILED`로 갱신한 wire test 재실행 전까지 `needs-confirmation` |"},"evidence_b":{"line_end":223,"line_start":223,"quote":"> **`MALFORMED_REQUEST` 는 제거되었다.** 초기 구현은 unknown field 를 `MALFORMED_REQUEST`(400) 로 매핑했으나 D10 의 \"HttpMessageNotReadableException → VALIDATION category\" 와 본 branch §테스트 계약 \"(B1) 400 + VALIDATION_FAILED\" 와 충돌. `VALIDATION_FAILED` 로 통합하고 *구체적 실패 모드*(UnrecognizedPropertyException / InvalidTypeIdException / JsonParseException 등) 는 `error.details.cause` 로 surface 한다."},"proof_manifest":null,"rationale":"A24 states MALFORMED_REQUEST was removed and unknown field consolidated into VALIDATION_FAILED; A07 tests the new VALIDATION_FAILED expectation and explicitly notes the old test used the removed MALFORMED_REQUEST envelope. Both agree on the current unknown field -> VALIDATION_FAILED mapping.","verdict":"CONSISTENT"},{"candidate_id":"SEM-AA592D614F3D40327A28","evidence_a":{"line_end":367,"line_start":367,"quote":"| (B2) PATCH endpoint 가 absent / null / 빈 값을 mapper 에서 구분하여 처리하는지 | record 기본값으로 mapping 시 PATCH 가 null 로 덮어쓰는 silent overwrite | PATCH integration test (3 케이스: field 없음 → 무변경, field=null → 명시적 null, field=value → 갱신) + JSON Schema validation | `actually-implemented` (2026-05-29 3차 패스: `BoundaryDemoControllerWireTest#b2_patch_field_absent_is_distinguished_from_explicit_null_and_value` 가 PATCH `/demo/boundary/patch-demo` 로 3 케이스 wire-level pin. 기존 `UpdateProfileRequest` + `UpdateProfileCommand` + `UserService.updateProfile` 도 `JsonNullable<T>` / `Patch<T>` 로 마이그레이션 — silent overwrite 위험 제거.) |"},"evidence_b":{"line_end":373,"line_start":373,"quote":"| (B5) `ObjectMapper.enableDefaultTyping()` / `activateDefaultTyping(LaissezFaireSubTypeValidator)` 호출이 코드 어디에도 없는지 | CVE-2019-14379 류 gadget chain RCE risk | ArchUnit rule (`enableDefaultTyping` / `LaissezFaireSubTypeValidator` import 금지) + dependency check (jackson-databind 버전 최소 2.10+) | `actually-implemented` (2026-05-29: `no_jackson_laissez_faire_subtype_validator` + `no_jackson_enable_default_typing_call` 두 ArchUnit rule, `DefaultTypingFixture` 가 violations-as-data 로 catch 검증. jackson-databind 버전 확인은 별도 supply-chain branch 책임.) |"},"proof_manifest":null,"rationale":"A08 (PATCH absent/null/value 3-state, actually-implemented) and A09 (no Jackson enableDefaultTyping/LaissezFaire, actually-implemented) are unrelated verification items on different contract surfaces; no shared property to conflict.","verdict":"CONSISTENT"},{"candidate_id":"SEM-3AF87FC3906856813907","evidence_a":{"line_end":367,"line_start":367,"quote":"| (B2) PATCH endpoint 가 absent / null / 빈 값을 mapper 에서 구분하여 처리하는지 | record 기본값으로 mapping 시 PATCH 가 null 로 덮어쓰는 silent overwrite | PATCH integration test (3 케이스: field 없음 → 무변경, field=null → 명시적 null, field=value → 갱신) + JSON Schema validation | `actually-implemented` (2026-05-29 3차 패스: `BoundaryDemoControllerWireTest#b2_patch_field_absent_is_distinguished_from_explicit_null_and_value` 가 PATCH `/demo/boundary/patch-demo` 로 3 케이스 wire-level pin. 기존 `UpdateProfileRequest` + `UpdateProfileCommand` + `UserService.updateProfile` 도 `JsonNullable<T>` / `Patch<T>` 로 마이그레이션 — silent overwrite 위험 제거.) |"},"evidence_b":{"line_end":378,"line_start":378,"quote":"| (B8) Bulk endpoint 의 response 가 `success: false` + `error.code = BATCH_PARTIAL_FAILURE` + `error.details[]` 항목별 결과 shape 을 따르는지 | 단일 항목 endpoint 와 schema 혼동 risk | bulk endpoint contract test (전체 성공 / 전체 실패 / 부분 실패 3 케이스) + OpenAPI shape 분기 검증 | `actually-implemented` (2026-05-29 3차 패스: `BoundaryDemoControllerWireTest` 의 3 bulk 케이스 (`b8_all_success`, `b8_partial_failure`, `b8_all_failures_take_the_same_partial_branch`) 가 POST `/demo/boundary/bulk` 로 wire-level 검증. OpenAPI 분기는 스펙 자체가 부재라 별도.) |"},"proof_manifest":null,"rationale":"A08 (PATCH 3-state mapping) and A10 (bulk endpoint response shape) are distinct actually-implemented verification items pinned by the same wire-test class but on different endpoints/properties; no conflict.","verdict":"CONSISTENT"},{"candidate_id":"SEM-D608EDA1C37381DFB9FA","evidence_a":{"line_end":373,"line_start":373,"quote":"| (B5) `ObjectMapper.enableDefaultTyping()` / `activateDefaultTyping(LaissezFaireSubTypeValidator)` 호출이 코드 어디에도 없는지 | CVE-2019-14379 류 gadget chain RCE risk | ArchUnit rule (`enableDefaultTyping` / `LaissezFaireSubTypeValidator` import 금지) + dependency check (jackson-databind 버전 최소 2.10+) | `actually-implemented` (2026-05-29: `no_jackson_laissez_faire_subtype_validator` + `no_jackson_enable_default_typing_call` 두 ArchUnit rule, `DefaultTypingFixture` 가 violations-as-data 로 catch 검증. jackson-databind 버전 확인은 별도 supply-chain branch 책임.) |"},"evidence_b":{"line_end":378,"line_start":378,"quote":"| (B8) Bulk endpoint 의 response 가 `success: false` + `error.code = BATCH_PARTIAL_FAILURE` + `error.details[]` 항목별 결과 shape 을 따르는지 | 단일 항목 endpoint 와 schema 혼동 risk | bulk endpoint contract test (전체 성공 / 전체 실패 / 부분 실패 3 케이스) + OpenAPI shape 분기 검증 | `actually-implemented` (2026-05-29 3차 패스: `BoundaryDemoControllerWireTest` 의 3 bulk 케이스 (`b8_all_success`, `b8_partial_failure`, `b8_all_failures_take_the_same_partial_branch`) 가 POST `/demo/boundary/bulk` 로 wire-level 검증. OpenAPI 분기는 스펙 자체가 부재라 별도.) |"},"proof_manifest":null,"rationale":"A09 (Jackson polymorphic default-typing prohibition) and A10 (bulk response schema) address unrelated contract properties; both actually-implemented, no overlap or conflict.","verdict":"CONSISTENT"},{"candidate_id":"SEM-1EABE3ECB52507238C4E","evidence_a":{"line_end":373,"line_start":373,"quote":"| (B5) `ObjectMapper.enableDefaultTyping()` / `activateDefaultTyping(LaissezFaireSubTypeValidator)` 호출이 코드 어디에도 없는지 | CVE-2019-14379 류 gadget chain RCE risk | ArchUnit rule (`enableDefaultTyping` / `LaissezFaireSubTypeValidator` import 금지) + dependency check (jackson-databind 버전 최소 2.10+) | `actually-implemented` (2026-05-29: `no_jackson_laissez_faire_subtype_validator` + `no_jackson_enable_default_typing_call` 두 ArchUnit rule, `DefaultTypingFixture` 가 violations-as-data 로 catch 검증. jackson-databind 버전 확인은 별도 supply-chain branch 책임.) |"},"evidence_b":{"line_end":286,"line_start":286,"quote":"- `enableDefaultTyping()` (no-arg, deprecated) 호출 → 차단 (ArchUnit `no_jackson_enable_default_typing_call`)."},"proof_manifest":null,"rationale":"Both forbid enableDefaultTyping via the same ArchUnit rule: A09 lists no_jackson_enable_default_typing_call (plus the LaissezFaire rule) as actually-implemented; A27 restates that enableDefaultTyping() is blocked by no_jackson_enable_default_typing_call. Same rule, same prohibition, in agreement.","verdict":"CONSISTENT"},{"candidate_id":"SEM-B8DF713BD23F31AEBCDF","evidence_a":{"line_end":334,"line_start":334,"quote":"| D1 | 모든 경계에 validation/mapping 책임을 둠 (2026-05-21) | UNSUPPORTED_DECISION (Clean Architecture / Hexagonal boundary 책임 원칙은 일반 design wisdom 이지만 본 branch 가 cite 한 sources — Stripe/Toss/RFC 7807/Spring/Google/JSON:API/GraphQL/GitHub — 중 normative 진술 없음) | N/A | DDD boundary / Hexagonal port-adapter 패턴의 raw 인용 (예: Vaughn Vernon, Reflectoring) 별도 보강 필요 |"},"evidence_b":{"line_end":337,"line_start":337,"quote":"| D4 | mapper 도구 기본값 — 수기 mapper + record canonical constructor; MapStruct optional (사용 시 architecture exemption + contract test 필요) | UNSUPPORTED_DECISION (project-internal tool selection; cited sources 중 mapper 도구 선택 관련 normative / vendor 진술 없음) | N/A | 수기 mapper 의 boilerplate 비용 vs MapStruct generated 코드의 architecture leak 위험 trade-off 는 별도 측정 / vendor 비교 필요 |"},"proof_manifest":null,"rationale":"A12 (D1 boundary validation/mapping responsibility, UNSUPPORTED_DECISION) and A13 (D4 mapper tooling default, UNSUPPORTED_DECISION) are two independent decisions each honestly labeled unsupported; different subjects, no conflict.","verdict":"CONSISTENT"},{"candidate_id":"SEM-7B57FC8A8486B8829C1C","evidence_a":{"line_end":338,"line_start":338,"quote":"| D5 | error envelope shape — custom 채택, RFC 7807 ProblemDetail 명시적 거부 (sibling branch `feature-business-rule-validation-contract` 와 동일 결정 공유) | `raw/official-docs/problem-detail-rfc-7807.md#RFC7807-C1` (canonical model `application/problem+json`), `#RFC7807-C2` (`type` URI primary identifier), `#RFC7807-C3` (extension 가능, unknown ignore), `raw/official-docs/spring-problem-detail.md#SPRING-PD-C1` (Spring `ProblemDetail` = RFC 9457 representation), `#SPRING-PD-C2` (모든 Spring MVC 예외가 `ErrorResponse` 구현 — envelope 와 충돌), `raw/official-docs/spring-mvc-rest-exception-handling.md#SPRING-MVC-EXC-C1` (동일 사실의 primary source — 모든 Spring MVC 내장 예외는 `ErrorResponse` 구현), `raw/company-tech-blogs/stripe-error-format.md#STRIPE-ERR-C5` (Stripe 4-종 type enum), `raw/company-tech-blogs/toss-payments-error-format.md#TOSS-ERR-C1` (Toss `{code, message}` 평면) | `official-standard + official-vendor-doc + company-case-study` | sibling branch D5 와 동일 evidence — cross-branch 일관성 확보됨. `SPRING-MVC-EXC-C1` 이 `SPRING-PD-C2` 를 corroborate. 단 RFC 7807 미채택 trade-off 의 ca-tmpl 측 해석은 cited sources 가 직접 권고하지 않음 |"},"evidence_b":{"line_end":339,"line_start":339,"quote":"| D10 | **B3 블라인드 해소** — `HttpMessageNotReadableException` (JSON 역직렬화 실패) 은 `VALIDATION` 카테고리로 분류; `MethodArgumentNotValidException` (Bean Validation 실패) 은 `VALIDATION` 카테고리로 분류; mapper 내부 예외 (`IllegalArgumentException`, MapStruct NPE, record canonical constructor `IllegalStateException`) 는 Spring 이 자동 처리하지 않으므로 별도 `@ExceptionHandler` 로 처리하며 ca-tmpl operational contract 에서 `MAPPING_FAILED` 카테고리로 분류 | `raw/official-docs/spring-mvc-rest-exception-handling.md#SPRING-MVC-EXC-C1` (모든 Spring MVC 내장 예외는 `ErrorResponse` 구현 — `HttpMessageNotReadableException` 포함), `#SPRING-MVC-EXC-C2` (`ResponseEntityExceptionHandler` 가 모든 Spring MVC 내장 예외 + `ErrorResponseException` 처리), `#SPRING-MVC-EXC-C4` (`HttpMessageNotReadableException` 은 normative 처리 목록에 있음), `#SPRING-MVC-EXC-C5` (`MethodArgumentNotValidException` 은 normative 처리 목록에 있음, {0}=global errors, {1}=field errors); `raw/official-docs/validation-jakarta-bean-validation-3.0-spec.md#JBV-3.0-C5` (PARAMETER ElementType → Bean Validation 으로 method parameter 검증 → `MethodArgumentNotValidException` 발생 경로의 2차 normative 확인); mapper-internal 예외의 `MAPPING_FAILED` 카테고리 코드 자체는 UNSUPPORTED_DECISION — ca-tmpl 고유 operational contract | `official-vendor-doc` (`HttpMessageNotReadableException` / `MethodArgumentNotValidException` → Spring normative) + `official-standard` (JBV-3.0-C5 corroboration) + UNSUPPORTED (`MAPPING_FAILED` 카테고리 코드 및 mapper-internal 예외 분류) | Spring 이 `HttpMessageNotReadableException` 의 HTTP status 를 400 으로 설정한다는 것은 `spring-mvc-rest-exception-handling.md` 의 message code 표에서 직접 명시되지 않음 — `ErrorResponse` 구현체 내부(Spring source)에서 정의됨. `MAPPING_FAILED` 라는 category code 를 Operational Error Category 에 추가하는 결정은 ca-tmpl 내부 결정이며 별도 project-note 갱신 필요 |"},"proof_manifest":null,"rationale":"A14 is D5 (custom error envelope, explicit RFC 7807 rejection); A15 is D10 (exception-to-category mapping). Both belong to error handling but govern different properties (envelope shape vs exception categorization); no shared value to conflict.","verdict":"CONSISTENT"},{"candidate_id":"SEM-14076C4101BD7EAF5065","evidence_a":{"line_end":338,"line_start":338,"quote":"| D5 | error envelope shape — custom 채택, RFC 7807 ProblemDetail 명시적 거부 (sibling branch `feature-business-rule-validation-contract` 와 동일 결정 공유) | `raw/official-docs/problem-detail-rfc-7807.md#RFC7807-C1` (canonical model `application/problem+json`), `#RFC7807-C2` (`type` URI primary identifier), `#RFC7807-C3` (extension 가능, unknown ignore), `raw/official-docs/spring-problem-detail.md#SPRING-PD-C1` (Spring `ProblemDetail` = RFC 9457 representation), `#SPRING-PD-C2` (모든 Spring MVC 예외가 `ErrorResponse` 구현 — envelope 와 충돌), `raw/official-docs/spring-mvc-rest-exception-handling.md#SPRING-MVC-EXC-C1` (동일 사실의 primary source — 모든 Spring MVC 내장 예외는 `ErrorResponse` 구현), `raw/company-tech-blogs/stripe-error-format.md#STRIPE-ERR-C5` (Stripe 4-종 type enum), `raw/company-tech-blogs/toss-payments-error-format.md#TOSS-ERR-C1` (Toss `{code, message}` 평면) | `official-standard + official-vendor-doc + company-case-study` | sibling branch D5 와 동일 evidence — cross-branch 일관성 확보됨. `SPRING-MVC-EXC-C1` 이 `SPRING-PD-C2` 를 corroborate. 단 RFC 7807 미채택 trade-off 의 ca-tmpl 측 해석은 cited sources 가 직접 권고하지 않음 |"},"evidence_b":{"line_end":309,"line_start":309,"quote":"- `no_problem_detail_usage` — `org.springframework.http.ProblemDetail` import 자체를 차단. D5 의 \"RFC 7807 명시적 거부\" 가 코드 단계에서 강제됨. 신규 작업자가 무심코 `ProblemDetail` 을 부활시키면 build 실패."},"proof_manifest":null,"rationale":"A14 is the D5 decision rejecting RFC 7807 ProblemDetail; A31 is the ArchUnit rule no_problem_detail_usage that statically enforces exactly that D5 rejection at build time. A31 supplies the enforcement detail for A14's decision without altering it.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-A7384E7EEDD8214CEB4D","evidence_a":{"line_end":339,"line_start":339,"quote":"| D10 | **B3 블라인드 해소** — `HttpMessageNotReadableException` (JSON 역직렬화 실패) 은 `VALIDATION` 카테고리로 분류; `MethodArgumentNotValidException` (Bean Validation 실패) 은 `VALIDATION` 카테고리로 분류; mapper 내부 예외 (`IllegalArgumentException`, MapStruct NPE, record canonical constructor `IllegalStateException`) 는 Spring 이 자동 처리하지 않으므로 별도 `@ExceptionHandler` 로 처리하며 ca-tmpl operational contract 에서 `MAPPING_FAILED` 카테고리로 분류 | `raw/official-docs/spring-mvc-rest-exception-handling.md#SPRING-MVC-EXC-C1` (모든 Spring MVC 내장 예외는 `ErrorResponse` 구현 — `HttpMessageNotReadableException` 포함), `#SPRING-MVC-EXC-C2` (`ResponseEntityExceptionHandler` 가 모든 Spring MVC 내장 예외 + `ErrorResponseException` 처리), `#SPRING-MVC-EXC-C4` (`HttpMessageNotReadableException` 은 normative 처리 목록에 있음), `#SPRING-MVC-EXC-C5` (`MethodArgumentNotValidException` 은 normative 처리 목록에 있음, {0}=global errors, {1}=field errors); `raw/official-docs/validation-jakarta-bean-validation-3.0-spec.md#JBV-3.0-C5` (PARAMETER ElementType → Bean Validation 으로 method parameter 검증 → `MethodArgumentNotValidException` 발생 경로의 2차 normative 확인); mapper-internal 예외의 `MAPPING_FAILED` 카테고리 코드 자체는 UNSUPPORTED_DECISION — ca-tmpl 고유 operational contract | `official-vendor-doc` (`HttpMessageNotReadableException` / `MethodArgumentNotValidException` → Spring normative) + `official-standard` (JBV-3.0-C5 corroboration) + UNSUPPORTED (`MAPPING_FAILED` 카테고리 코드 및 mapper-internal 예외 분류) | Spring 이 `HttpMessageNotReadableException` 의 HTTP status 를 400 으로 설정한다는 것은 `spring-mvc-rest-exception-handling.md` 의 message code 표에서 직접 명시되지 않음 — `ErrorResponse` 구현체 내부(Spring source)에서 정의됨. `MAPPING_FAILED` 라는 category code 를 Operational Error Category 에 추가하는 결정은 ca-tmpl 내부 결정이며 별도 project-note 갱신 필요 |"},"evidence_b":{"line_end":341,"line_start":341,"quote":"| D7 | request DTO → application command/query mapper 강제 (DTO 의 service 직접 전달 금지); PATCH 요청 시 mapper 가 null vs absent 를 구분해야 함 (B2 블라인드) | `raw/official-docs/patch-json-merge-rfc7396.md#RFC7396-C2` (\"Null values in the merge patch are given special meaning to indicate the removal of existing values in the target.\" — null=deletion normative), `#RFC7396-C3` (merge patch 는 explicit null 사용 시 부적합), `#RFC7396-C4` (배열 부분 수정 불가 — merge patch 한계) | `official-standard` (null=deletion 근거) + UNSUPPORTED (Hexagonal boundary 원칙 자체) | RFC7396 은 null=deletion 의 normative 근거를 제공하나, Java record mapper 에서 absent field 를 별도 처리하는 구현 방법은 직접 권고하지 않음. Hexagonal port-adapter boundary 원칙 raw (예: Reflectoring, Woowahan) 별도 인용 보강 권장 |"},"proof_manifest":null,"rationale":"A15 (D10 exception categorization) and A16 (D7 request-DTO-to-command mapper mandate + PATCH null/absent) are distinct decisions on different boundaries; compatible and non-overlapping.","verdict":"CONSISTENT"},{"candidate_id":"SEM-A231941E44FED6054F05","evidence_a":{"line_end":339,"line_start":339,"quote":"| D10 | **B3 블라인드 해소** — `HttpMessageNotReadableException` (JSON 역직렬화 실패) 은 `VALIDATION` 카테고리로 분류; `MethodArgumentNotValidException` (Bean Validation 실패) 은 `VALIDATION` 카테고리로 분류; mapper 내부 예외 (`IllegalArgumentException`, MapStruct NPE, record canonical constructor `IllegalStateException`) 는 Spring 이 자동 처리하지 않으므로 별도 `@ExceptionHandler` 로 처리하며 ca-tmpl operational contract 에서 `MAPPING_FAILED` 카테고리로 분류 | `raw/official-docs/spring-mvc-rest-exception-handling.md#SPRING-MVC-EXC-C1` (모든 Spring MVC 내장 예외는 `ErrorResponse` 구현 — `HttpMessageNotReadableException` 포함), `#SPRING-MVC-EXC-C2` (`ResponseEntityExceptionHandler` 가 모든 Spring MVC 내장 예외 + `ErrorResponseException` 처리), `#SPRING-MVC-EXC-C4` (`HttpMessageNotReadableException` 은 normative 처리 목록에 있음), `#SPRING-MVC-EXC-C5` (`MethodArgumentNotValidException` 은 normative 처리 목록에 있음, {0}=global errors, {1}=field errors); `raw/official-docs/validation-jakarta-bean-validation-3.0-spec.md#JBV-3.0-C5` (PARAMETER ElementType → Bean Validation 으로 method parameter 검증 → `MethodArgumentNotValidException` 발생 경로의 2차 normative 확인); mapper-internal 예외의 `MAPPING_FAILED` 카테고리 코드 자체는 UNSUPPORTED_DECISION — ca-tmpl 고유 operational contract | `official-vendor-doc` (`HttpMessageNotReadableException` / `MethodArgumentNotValidException` → Spring normative) + `official-standard` (JBV-3.0-C5 corroboration) + UNSUPPORTED (`MAPPING_FAILED` 카테고리 코드 및 mapper-internal 예외 분류) | Spring 이 `HttpMessageNotReadableException` 의 HTTP status 를 400 으로 설정한다는 것은 `spring-mvc-rest-exception-handling.md` 의 message code 표에서 직접 명시되지 않음 — `ErrorResponse` 구현체 내부(Spring source)에서 정의됨. `MAPPING_FAILED` 라는 category code 를 Operational Error Category 에 추가하는 결정은 ca-tmpl 내부 결정이며 별도 project-note 갱신 필요 |"},"evidence_b":{"line_end":438,"line_start":438,"quote":"- **Dependency**: `MAPPING_FAILED` / `BATCH_PARTIAL_FAILURE` 신규 code 는 canonical SSOT [[raw/project-notes/ca-skeleton-operational-contract]] §6 등록에 의존 (등록 완료). package convention glob 은 §20 Skeleton Blueprint 에 의존. B9 ArchUnit rule 은 [[raw/branch-notes/feature-resource-identifier-contract]] D17 을 cross-cite."},"proof_manifest":null,"rationale":"A15 flags that adding the MAPPING_FAILED category requires a separate ca-tmpl project-note update; A20 reports that the canonical SSOT §6 registration for MAPPING_FAILED/BATCH_PARTIAL_FAILURE is complete. A20 supplies the fulfillment status for the dependency A15 identified; 'update needed' and 'registration complete' are not mutually exclusive values.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-791BDC8E8B1537E99FD2","evidence_a":{"line_end":339,"line_start":339,"quote":"| D10 | **B3 블라인드 해소** — `HttpMessageNotReadableException` (JSON 역직렬화 실패) 은 `VALIDATION` 카테고리로 분류; `MethodArgumentNotValidException` (Bean Validation 실패) 은 `VALIDATION` 카테고리로 분류; mapper 내부 예외 (`IllegalArgumentException`, MapStruct NPE, record canonical constructor `IllegalStateException`) 는 Spring 이 자동 처리하지 않으므로 별도 `@ExceptionHandler` 로 처리하며 ca-tmpl operational contract 에서 `MAPPING_FAILED` 카테고리로 분류 | `raw/official-docs/spring-mvc-rest-exception-handling.md#SPRING-MVC-EXC-C1` (모든 Spring MVC 내장 예외는 `ErrorResponse` 구현 — `HttpMessageNotReadableException` 포함), `#SPRING-MVC-EXC-C2` (`ResponseEntityExceptionHandler` 가 모든 Spring MVC 내장 예외 + `ErrorResponseException` 처리), `#SPRING-MVC-EXC-C4` (`HttpMessageNotReadableException` 은 normative 처리 목록에 있음), `#SPRING-MVC-EXC-C5` (`MethodArgumentNotValidException` 은 normative 처리 목록에 있음, {0}=global errors, {1}=field errors); `raw/official-docs/validation-jakarta-bean-validation-3.0-spec.md#JBV-3.0-C5` (PARAMETER ElementType → Bean Validation 으로 method parameter 검증 → `MethodArgumentNotValidException` 발생 경로의 2차 normative 확인); mapper-internal 예외의 `MAPPING_FAILED` 카테고리 코드 자체는 UNSUPPORTED_DECISION — ca-tmpl 고유 operational contract | `official-vendor-doc` (`HttpMessageNotReadableException` / `MethodArgumentNotValidException` → Spring normative) + `official-standard` (JBV-3.0-C5 corroboration) + UNSUPPORTED (`MAPPING_FAILED` 카테고리 코드 및 mapper-internal 예외 분류) | Spring 이 `HttpMessageNotReadableException` 의 HTTP status 를 400 으로 설정한다는 것은 `spring-mvc-rest-exception-handling.md` 의 message code 표에서 직접 명시되지 않음 — `ErrorResponse` 구현체 내부(Spring source)에서 정의됨. `MAPPING_FAILED` 라는 category code 를 Operational Error Category 에 추가하는 결정은 ca-tmpl 내부 결정이며 별도 project-note 갱신 필요 |"},"evidence_b":{"line_end":219,"line_start":219,"quote":"| `VALIDATION_FAILED` | 400 | false | Bean Validation 실패, JSON 파싱 실패, unknown field, 알 수 없는 enum value, polymorphic discriminator 불일치 — D10 의 \"VALIDATION 카테고리\" 일체 | `HttpMessageNotReadableException`, `MethodArgumentNotValidException`, `ConstraintViolationException` 모두 라우팅 |"},"proof_manifest":null,"rationale":"Both are maps_to on the same VALIDATION categorization: A15 (D10) routes HttpMessageNotReadableException/MethodArgumentNotValidException to the VALIDATION category; A21 is the error-code table adding HTTP 400, retryable=false, the code name VALIDATION_FAILED, and ConstraintViolationException routing. A21 supplies the concrete code/status detail for D10's decision; consistent direction, no conflict.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-B7ED647AA7F4C11387A7","evidence_a":{"line_end":339,"line_start":339,"quote":"| D10 | **B3 블라인드 해소** — `HttpMessageNotReadableException` (JSON 역직렬화 실패) 은 `VALIDATION` 카테고리로 분류; `MethodArgumentNotValidException` (Bean Validation 실패) 은 `VALIDATION` 카테고리로 분류; mapper 내부 예외 (`IllegalArgumentException`, MapStruct NPE, record canonical constructor `IllegalStateException`) 는 Spring 이 자동 처리하지 않으므로 별도 `@ExceptionHandler` 로 처리하며 ca-tmpl operational contract 에서 `MAPPING_FAILED` 카테고리로 분류 | `raw/official-docs/spring-mvc-rest-exception-handling.md#SPRING-MVC-EXC-C1` (모든 Spring MVC 내장 예외는 `ErrorResponse` 구현 — `HttpMessageNotReadableException` 포함), `#SPRING-MVC-EXC-C2` (`ResponseEntityExceptionHandler` 가 모든 Spring MVC 내장 예외 + `ErrorResponseException` 처리), `#SPRING-MVC-EXC-C4` (`HttpMessageNotReadableException` 은 normative 처리 목록에 있음), `#SPRING-MVC-EXC-C5` (`MethodArgumentNotValidException` 은 normative 처리 목록에 있음, {0}=global errors, {1}=field errors); `raw/official-docs/validation-jakarta-bean-validation-3.0-spec.md#JBV-3.0-C5` (PARAMETER ElementType → Bean Validation 으로 method parameter 검증 → `MethodArgumentNotValidException` 발생 경로의 2차 normative 확인); mapper-internal 예외의 `MAPPING_FAILED` 카테고리 코드 자체는 UNSUPPORTED_DECISION — ca-tmpl 고유 operational contract | `official-vendor-doc` (`HttpMessageNotReadableException` / `MethodArgumentNotValidException` → Spring normative) + `official-standard` (JBV-3.0-C5 corroboration) + UNSUPPORTED (`MAPPING_FAILED` 카테고리 코드 및 mapper-internal 예외 분류) | Spring 이 `HttpMessageNotReadableException` 의 HTTP status 를 400 으로 설정한다는 것은 `spring-mvc-rest-exception-handling.md` 의 message code 표에서 직접 명시되지 않음 — `ErrorResponse` 구현체 내부(Spring source)에서 정의됨. `MAPPING_FAILED` 라는 category code 를 Operational Error Category 에 추가하는 결정은 ca-tmpl 내부 결정이며 별도 project-note 갱신 필요 |"},"evidence_b":{"line_end":251,"line_start":251,"quote":"- Mapper 내부 (web/outbound/persistence ACL 어디든) 가 던진 *논리적 mapping 실패* 는 반드시 `MappingException` 으로 **wrap 해서** 던진다. 그러면 `handleMapping` 이 `MAPPING_FAILED` 로 라우팅."},"proof_manifest":null,"rationale":"A15 (D10) says mapper-internal exceptions are handled by a dedicated @ExceptionHandler and categorized MAPPING_FAILED; A23 supplies the mechanism (mapper must wrap logical failures in MappingException so handleMapping routes to MAPPING_FAILED). Same target category, A23 adding the wrapping mechanism.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-E0A31DD21EF17410E8C5","evidence_a":{"line_end":341,"line_start":341,"quote":"| D7 | request DTO → application command/query mapper 강제 (DTO 의 service 직접 전달 금지); PATCH 요청 시 mapper 가 null vs absent 를 구분해야 함 (B2 블라인드) | `raw/official-docs/patch-json-merge-rfc7396.md#RFC7396-C2` (\"Null values in the merge patch are given special meaning to indicate the removal of existing values in the target.\" — null=deletion normative), `#RFC7396-C3` (merge patch 는 explicit null 사용 시 부적합), `#RFC7396-C4` (배열 부분 수정 불가 — merge patch 한계) | `official-standard` (null=deletion 근거) + UNSUPPORTED (Hexagonal boundary 원칙 자체) | RFC7396 은 null=deletion 의 normative 근거를 제공하나, Java record mapper 에서 absent field 를 별도 처리하는 구현 방법은 직접 권고하지 않음. Hexagonal port-adapter boundary 원칙 raw (예: Reflectoring, Woowahan) 별도 인용 보강 권장 |"},"evidence_b":{"line_end":300,"line_start":300,"quote":"- `application_methods_do_not_accept_web_dtos` — application package 의 public method 가 `..adapter.web..dto..` 파라미터를 받으면 build 실패. controller 가 DTO → Command/Query 변환을 우회하는 회귀 차단."},"proof_manifest":null,"rationale":"A16 is the D7 decision that request DTOs must go through the application command/query mapper (never passed directly to services); A30 is the ArchUnit rule application_methods_do_not_accept_web_dtos that statically enforces that boundary. Decision plus its enforcement, fully compatible.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-D063D4BE219622E8AA52","evidence_a":{"line_end":341,"line_start":341,"quote":"| D7 | request DTO → application command/query mapper 강제 (DTO 의 service 직접 전달 금지); PATCH 요청 시 mapper 가 null vs absent 를 구분해야 함 (B2 블라인드) | `raw/official-docs/patch-json-merge-rfc7396.md#RFC7396-C2` (\"Null values in the merge patch are given special meaning to indicate the removal of existing values in the target.\" — null=deletion normative), `#RFC7396-C3` (merge patch 는 explicit null 사용 시 부적합), `#RFC7396-C4` (배열 부분 수정 불가 — merge patch 한계) | `official-standard` (null=deletion 근거) + UNSUPPORTED (Hexagonal boundary 원칙 자체) | RFC7396 은 null=deletion 의 normative 근거를 제공하나, Java record mapper 에서 absent field 를 별도 처리하는 구현 방법은 직접 권고하지 않음. Hexagonal port-adapter boundary 원칙 raw (예: Reflectoring, Woowahan) 별도 인용 보강 권장 |"},"evidence_b":{"line_end":135,"line_start":135,"quote":"- request DTO -> application command/query mapper."},"proof_manifest":null,"rationale":"A33 states the in-scope mapping request DTO -> application command/query mapper; A16 (D7) states the same mandate with added PATCH null/absent and RFC7396 evidence. Same core requirement, in agreement.","verdict":"CONSISTENT"},{"candidate_id":"SEM-9BA95316AD1E3A3C440D","evidence_a":{"line_end":348,"line_start":348,"quote":"| D15 | **B9 cross-cite** — Resource identifier ArchUnit rules **4개** (`no_long_id_pk` — `..domain..` 한정, `no_uuid_random_in_controller`, `no_math_random_for_id`, `no_varchar_255_for_id_column`) 를 본 branch ArchUnit suite 에 등록. 구현 skeleton 은 resource-identifier branch §구현 가이드 §6. **5번째 rule `no_find_by_id_without_tenant` 는 `feature-tenant-context-policy` (예정 branch) 로 이관** — tenant 모델 부재 시 production code 가 모두 깨지는 false positive 차단 | [[raw/branch-notes/feature-resource-identifier-contract]] D17 (rule SSOT — 4 rules), D5 (ID generation = domain port + application 주입), D10 (PostgreSQL `uuid` native), D13 (ID 내 tenant 인코딩 거부 — 형식적 위치만). project §34 Stack Commitment (archunit-junit5 1.3.0) | `cross-branch-SSOT` (resource-identifier D17) + `project-ssot` (§34 archunit-junit5 version) | `haveExplicitColumnLength()` custom ArchCondition 의 archunit-junit5 1.3.0 API 호환성 검증 필요 (resource-identifier branch §구현 가이드 §6 UNSUPPORTED_IMPL_DECISION). 본 4개 rule 의 실제 코드는 boundary branch ArchUnit suite 가 호스팅, *결정 SSOT* 는 resource-identifier branch D17. `no_find_by_id_without_tenant` 활성화는 multi-tenancy-contract 도착 시 |"},"evidence_b":{"line_end":438,"line_start":438,"quote":"- **Dependency**: `MAPPING_FAILED` / `BATCH_PARTIAL_FAILURE` 신규 code 는 canonical SSOT [[raw/project-notes/ca-skeleton-operational-contract]] §6 등록에 의존 (등록 완료). package convention glob 은 §20 Skeleton Blueprint 에 의존. B9 ArchUnit rule 은 [[raw/branch-notes/feature-resource-identifier-contract]] D17 을 cross-cite."},"proof_manifest":null,"rationale":"A17 (D15) details hosting the 4 resource-identifier ArchUnit rules in the boundary suite with decision SSOT at resource-identifier D17; A20's dependency note states the B9 ArchUnit rule cross-cites resource-identifier D17. Both agree on the B9/resource-identifier D17 cross-citation; no conflict.","verdict":"CONSISTENT"},{"candidate_id":"SEM-155192A2E4DD545A7A16","evidence_a":{"line_end":436,"line_start":436,"quote":"- **Edge**: PATCH 의 absent vs explicit-null vs value 3-state — 구분 실패 시 silent overwrite (B2). bulk endpoint 의 전체 실패도 부분 실패와 동일 `BATCH_PARTIAL_FAILURE`(HTTP 200) branch 를 타며, 분기는 `envelope.success` 로만 (B8)."},"evidence_b":{"line_end":438,"line_start":438,"quote":"- **Dependency**: `MAPPING_FAILED` / `BATCH_PARTIAL_FAILURE` 신규 code 는 canonical SSOT [[raw/project-notes/ca-skeleton-operational-contract]] §6 등록에 의존 (등록 완료). package convention glob 은 §20 Skeleton Blueprint 에 의존. B9 ArchUnit rule 은 [[raw/branch-notes/feature-resource-identifier-contract]] D17 을 cross-cite."},"proof_manifest":null,"rationale":"A18 describes edge behavior (PATCH 3-state silent-overwrite risk; bulk full-failure takes the BATCH_PARTIAL_FAILURE HTTP 200 branch); A20 is a dependency note on code registration and cross-cites. They touch BATCH_PARTIAL_FAILURE but address unrelated facets (runtime edge vs registration dependency); no conflict.","verdict":"CONSISTENT"},{"candidate_id":"SEM-75C0C91FC70D72101E80","evidence_a":{"line_end":436,"line_start":436,"quote":"- **Edge**: PATCH 의 absent vs explicit-null vs value 3-state — 구분 실패 시 silent overwrite (B2). bulk endpoint 의 전체 실패도 부분 실패와 동일 `BATCH_PARTIAL_FAILURE`(HTTP 200) branch 를 타며, 분기는 `envelope.success` 로만 (B8)."},"evidence_b":{"line_end":221,"line_start":221,"quote":"| `BATCH_PARTIAL_FAILURE` | 200 | false | Bulk endpoint 의 부분/전체 실패. HTTP 200 + envelope.success=false (단일 항목 endpoint 와 *동일* 응답 표면, *분기는 envelope.success* 로). | `BulkEnvelope.partial(...)` |"},"proof_manifest":null,"rationale":"A18 (edge) and A22 (error-code table) both state that bulk full-failure follows the same BATCH_PARTIAL_FAILURE path, HTTP 200, with branching by envelope.success. Identical routing, in agreement.","verdict":"CONSISTENT"},{"candidate_id":"SEM-B4126A354D0988DCACA2","evidence_a":{"line_end":437,"line_start":437,"quote":"- **Failure mode**: ① mapper-internal 예외가 `MappingException` wrap 누락 시 `INTERNAL_ERROR` 로 새어 분류 오류 (B3). ② virtual thread 환경에서 `ThreadLocal`/MDC context 가 application layer 까지 propagate 안 되면 traceId 유실 (B6). ③ ArchUnit 정적 강제는 바이트코드 carrier(어노테이션/import/호출)만 탐지 — 메서드 본문 free-form 문자열은 한계."},"evidence_b":{"line_end":251,"line_start":251,"quote":"- Mapper 내부 (web/outbound/persistence ACL 어디든) 가 던진 *논리적 mapping 실패* 는 반드시 `MappingException` 으로 **wrap 해서** 던진다. 그러면 `handleMapping` 이 `MAPPING_FAILED` 로 라우팅."},"proof_manifest":null,"rationale":"A23 states the rule (mapper logical failures must wrap in MappingException -> handleMapping -> MAPPING_FAILED); A19 describes the failure mode when that wrap is omitted (leaks to INTERNAL_ERROR, misclassification, B3). A19 supplies the negative consequence of violating A23's rule; consistent and compatible.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-8A0185E8E137C69DEEEA","evidence_a":{"line_end":438,"line_start":438,"quote":"- **Dependency**: `MAPPING_FAILED` / `BATCH_PARTIAL_FAILURE` 신규 code 는 canonical SSOT [[raw/project-notes/ca-skeleton-operational-contract]] §6 등록에 의존 (등록 완료). package convention glob 은 §20 Skeleton Blueprint 에 의존. B9 ArchUnit rule 은 [[raw/branch-notes/feature-resource-identifier-contract]] D17 을 cross-cite."},"evidence_b":{"line_end":221,"line_start":221,"quote":"| `BATCH_PARTIAL_FAILURE` | 200 | false | Bulk endpoint 의 부분/전체 실패. HTTP 200 + envelope.success=false (단일 항목 endpoint 와 *동일* 응답 표면, *분기는 envelope.success* 로). | `BulkEnvelope.partial(...)` |"},"proof_manifest":null,"rationale":"A20 notes the BATCH_PARTIAL_FAILURE code depends on canonical §6 registration (complete); A22 defines the code's mapping (HTTP 200, retryable=false, envelope.success=false). Registration-status facet and definition facet of the same code, compatible.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-A616145FF4FB28615604","evidence_a":{"line_end":438,"line_start":438,"quote":"- **Dependency**: `MAPPING_FAILED` / `BATCH_PARTIAL_FAILURE` 신규 code 는 canonical SSOT [[raw/project-notes/ca-skeleton-operational-contract]] §6 등록에 의존 (등록 완료). package convention glob 은 §20 Skeleton Blueprint 에 의존. B9 ArchUnit rule 은 [[raw/branch-notes/feature-resource-identifier-contract]] D17 을 cross-cite."},"evidence_b":{"line_end":251,"line_start":251,"quote":"- Mapper 내부 (web/outbound/persistence ACL 어디든) 가 던진 *논리적 mapping 실패* 는 반드시 `MappingException` 으로 **wrap 해서** 던진다. 그러면 `handleMapping` 이 `MAPPING_FAILED` 로 라우팅."},"proof_manifest":null,"rationale":"A20 notes MAPPING_FAILED's canonical §6 registration dependency (complete); A23 describes the runtime routing that produces MAPPING_FAILED (MappingException wrap -> handleMapping). Registration and routing are compatible facets of the same code.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-1D6B225E94E47EB38482","evidence_a":{"line_end":219,"line_start":219,"quote":"| `VALIDATION_FAILED` | 400 | false | Bean Validation 실패, JSON 파싱 실패, unknown field, 알 수 없는 enum value, polymorphic discriminator 불일치 — D10 의 \"VALIDATION 카테고리\" 일체 | `HttpMessageNotReadableException`, `MethodArgumentNotValidException`, `ConstraintViolationException` 모두 라우팅 |"},"evidence_b":{"line_end":223,"line_start":223,"quote":"> **`MALFORMED_REQUEST` 는 제거되었다.** 초기 구현은 unknown field 를 `MALFORMED_REQUEST`(400) 로 매핑했으나 D10 의 \"HttpMessageNotReadableException → VALIDATION category\" 와 본 branch §테스트 계약 \"(B1) 400 + VALIDATION_FAILED\" 와 충돌. `VALIDATION_FAILED` 로 통합하고 *구체적 실패 모드*(UnrecognizedPropertyException / InvalidTypeIdException / JsonParseException 등) 는 `error.details.cause` 로 surface 한다."},"proof_manifest":null,"rationale":"A21 lists unknown field under VALIDATION_FAILED (400); A24 documents that MALFORMED_REQUEST was removed and unknown field consolidated into VALIDATION_FAILED, with specifics surfaced via error.details.cause. A21 already omits MALFORMED_REQUEST, matching A24; both agree.","verdict":"CONSISTENT"},{"candidate_id":"SEM-C95AF1C7F05EFC94C828","evidence_a":{"line_end":262,"line_start":262,"quote":"- *모든* `@RestController` 응답 (sample-portfolio 의 도메인 컨트롤러 + production `HealthcheckController` 포함) 은 `EnvelopeBodyAdvice` 가 자동으로 `Envelope<T>` 로 wrap."},"evidence_b":{"line_end":299,"line_start":299,"quote":"- `controllers_do_not_return_domain_or_entity_types` — controller method 반환 타입이 `..domain.entity..` 또는 `..adapter.persistence.entity..` 또는 `..repository..` 에 거주하면 build 실패. `EnvelopeBodyAdvice` 의 자동 wrap 이 도메인 객체를 silent 직렬화하는 회귀를 *정적* 으로 차단."},"proof_manifest":null,"rationale":"A25 states EnvelopeBodyAdvice auto-wraps all @RestController responses; A29 is the ArchUnit rule controllers_do_not_return_domain_or_entity_types that statically blocks domain objects from being silently serialized by that same auto-wrap. A29 supplies the static guard protecting A25's behavior; fully compatible.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-FF6B1D66E74B36A76CB9","evidence_a":{"line_end":274,"line_start":274,"quote":"- ArchUnit `valid_cascade_depth_at_most_three` 규칙이 `..adapter.web..dto..` 패키지 클래스의 `@Valid` 필드를 재귀 따라가며 도메인 내부 클래스 사이의 cascade 깊이를 계산."},"evidence_b":{"line_end":300,"line_start":300,"quote":"- `application_methods_do_not_accept_web_dtos` — application package 의 public method 가 `..adapter.web..dto..` 파라미터를 받으면 build 실패. controller 가 DTO → Command/Query 변환을 우회하는 회귀 차단."},"proof_manifest":null,"rationale":"A26 (valid_cascade_depth_at_most_three, @Valid cascade depth <= 3) and A30 (application_methods_do_not_accept_web_dtos) are two distinct ArchUnit rules that both touch the ..adapter.web..dto.. package but enforce unrelated constraints (cascade depth vs application parameter type); no conflict.","verdict":"CONSISTENT"},{"candidate_id":"SEM-7DC586E87494BD570776","evidence_a":{"line_end":300,"line_start":300,"quote":"- `application_methods_do_not_accept_web_dtos` — application package 의 public method 가 `..adapter.web..dto..` 파라미터를 받으면 build 실패. controller 가 DTO → Command/Query 변환을 우회하는 회귀 차단."},"evidence_b":{"line_end":135,"line_start":135,"quote":"- request DTO -> application command/query mapper."},"proof_manifest":null,"rationale":"A33 states the in-scope boundary request DTO -> application command/query mapper; A30 is the ArchUnit rule application_methods_do_not_accept_web_dtos enforcing that DTOs never bypass the mapper into application methods. A30 statically enforces the boundary A33 describes.","verdict":"COMPLEMENTARY"},{"candidate_id":"SEM-6E705D9D27D728AA905B","evidence_a":{"line_end":143,"line_start":143,"quote":"- 특정 도메인 validator 구현."},"evidence_b":{"line_end":144,"line_start":144,"quote":"- DB/JPA exception mapping."},"proof_manifest":null,"rationale":"A36 (specific domain validator implementation) and A37 (DB/JPA exception mapping) are two separate items on the same out-of-scope exclusion list; both consistently excluded from the branch, no conflict.","verdict":"CONSISTENT"}]},"auditor":{"contract_version":"semantic-coherence/v1","model_id":"claude-opus-4-8","run_id":"ab9c0cc71d7c9e89a"},"auditor_contract_sha256":"1cbc67c27e5183a272687f635e3682a26d56785a1cf144da562eb7edd8f6cbce","coverage":{"candidate_pairs":30,"dropped_pairs":0,"eligible_surfaces":6,"processed_pairs":30,"processed_surfaces":6},"document_id":"10affe6752bb478c4c1079bc82652bf3c8627d68f6111e5110662b8f8c56fa29","document_sha256":"dfa8f18c855db22d25a8090d8aee93046b558c1ec42d61a501d77b6bda919e16","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":"29574f1a08d763238e0c0a1b0295bfe9bc1478f9752d045334d7981f926ef805","subject":"raw/branch-notes/feature-boundary-validation-mapping-contract.md","typed_contract_graph_sha256":"481fc3335a494c454ab13ad1d6a6ddccdec99aa8e083f6720ff8886bfa19cc89","verdict":"PASS"}