Command: python3 - <<'PY'
import json
from pathlib import Path
root=Path('src/adapter/outbound/support')
for p in sorted(x for x in root.rglob('*') if x.is_file() and 'build/' not in str(x)):
    print(p)
print('--- counts ---')
for label,base,pattern in [('prod-java',root/'src/main/java','*.java'),('test-java',root/'src/test/java','*.java')]:
    files=list(base.rglob(pattern)) if base.exists() else []
    print(label,'files=',len(files),'loc=',sum(len(p.read_text(errors='ignore').splitlines()) for p in files))
r=json.load(open('src/config/architecture/modules.json'))['modules']
print('--- registry ---')
print(json.dumps(next(m for m in r if m['id']=='adapter-outbound-support'),indent=2))
PY
printf '%s\n' '--- build.gradle ---'; cat src/adapter/outbound/support/build.gradle
printf '%s\n' '--- README.md ---'; cat src/adapter/outbound/support/README.md
printf '%s\n' '--- CLAUDE.md ---'; cat src/adapter/outbound/support/CLAUDE.md
Working directory: /shared/codebase/clean-architecture-backend-template
Executed at: 2026-08-29T07:19:26Z
Source revision: a24ece9cf797f7ea647e33bf846b115208ed1ba5
Observation boundary: Enumerates the support leaf source/build/document files and registry entry at the recorded revision; downstream consumers are not measured here.
--- stdout/stderr ---
src/adapter/outbound/support/CLAUDE.md
src/adapter/outbound/support/README.md
src/adapter/outbound/support/build.gradle
src/adapter/outbound/support/gradle.lockfile
src/adapter/outbound/support/src/main/java/dev/caskeleton/adapter/outbound/package-info.java
src/adapter/outbound/support/src/main/java/dev/caskeleton/adapter/outbound/support/FailOpenDependencyLogger.java
src/adapter/outbound/support/src/main/java/dev/caskeleton/adapter/outbound/support/OutboundCorrelation.java
src/adapter/outbound/support/src/main/java/dev/caskeleton/adapter/outbound/support/OutboundSupportConfig.java
src/adapter/outbound/support/src/test/java/dev/caskeleton/adapter/outbound/support/FailOpenDependencyLoggerTest.java
--- counts ---
prod-java files= 4 loc= 97
test-java files= 1 loc= 89
--- registry ---
{
  "id": "adapter-outbound-support",
  "gradle_path": ":adapter:outbound:support",
  "source_path": "src/adapter/outbound/support",
  "allowed_dependencies": [
    "domain-core",
    "application-core",
    "shared-contract"
  ],
  "runtime_memberships": [
    "app-bootstrap"
  ]
}
--- build.gradle ---
// Shared base for outbound integration adapters: correlation, fail-open dependency
// logging, and the @Configuration seam. Depended on by messaging/cache/notification/httpclient.
dependencies {
    implementation 'org.springframework.boot:spring-boot-autoconfigure'
    implementation 'org.slf4j:slf4j-api'
}

tasks.withType(JavaCompile).configureEach {
    options.encoding = 'UTF-8'
}
--- README.md ---
# adapter:outbound:support — 설계 결정 참조

아웃바운드 기술 어댑터(`:adapter:outbound:messaging`, `:adapter:outbound:cache-redis`,
`:adapter:outbound:notification`, `:adapter:outbound:httpclient`)가 공유하는 베이스 모듈. 패키지
루트: `dev.caskeleton.adapter.outbound.support`.

허용/금지 의존 정책은 `src/build.gradle` 의
`allowedProjectDependencies['adapter:outbound:support']` 항목이 SSOT 다(이 모듈은 아직 별도
CLAUDE.md 를 두지 않았다). 이 문서는 코드 주석에서 덜어낸 **설계 결정의 근거**를 모아둔 참조용
기록이다 — 코드를 읽다 "왜 이렇게 했나"가 궁금할 때 본다.

## 왜 별도 모듈인가

옛 `adapter-outbound` 모듈을 기술별(`messaging`/`cache-redis`/`notification`/`httpclient`)로
쪼개면서, 네 모듈이 공통으로 필요로 하는 조각 — correlation 조회, fail-open 의존성 로깅,
공유 `@Configuration` 등록 지점 — 을 이 모듈로 뽑아냈다. 네 기술 모듈 모두 `implementation
project(':adapter:outbound:support')` 로 이 모듈에 의존한다.

## FailOpenDependencyLogger — WARN, ERROR 아님

실패를 ERROR 가 아니라 WARN 으로 로깅한다: cache/messaging/notification 선택형 어댑터는
fail-open 이라 의존성 실패가 나도 use case 는 성공한 것이다 — 관측은 하되 escalate 하지 않는다.
이는 호출자에게 직접 노출되는 hard failure 를 ERROR 로 올리는 `httpclient` 모듈의
`OutboundHttpDependencyLogger` 와 명확히 구분된다. 메서드 시그니처가 본문·수신자·페이로드를
받지 않아 PII 가 로그에 닿지 않는다.

## OutboundCorrelation — `"unknown"` 센티넬

MDC 에 correlation id 가 없으면 `null` 이 아니라 `"unknown"` 문자열을 반환해 로그 라인에 필드가
비지 않게 한다. MDC 키는 SSOT(`mdc-keys.yaml`)를 따른다.

## OutboundSupportConfig — 공유 `@Configuration` seam

`FailOpenDependencyLogger` 빈을 `@ConditionalOnMissingBean` 으로 무조건 등록하는 단일
`@Configuration` 이다. 네 기술 모듈이 각자 로거 빈을 중복 등록하지 않고 이 모듈에만 의존하면
와이어링되며, 포크는 같은 타입의 빈을 직접 등록해 오버라이드할 수 있다. 이 모듈 자체는 어떤
`@ConditionalOnProperty` 게이팅도 갖지 않는다 — 게이팅은 각 기술 모듈이 자신의 연동 client
단위로 소유한다.
--- CLAUDE.md ---
# adapter:outbound:support — shared outbound support

## Registered identity

- Module ID: `adapter-outbound-support`
- Gradle path: `:adapter:outbound:support`
- Focused test (derived from Gradle path): `./gradlew :adapter:outbound:support:test --console=plain`
- Runtime baseline: Java 21; repository framework baseline: Spring Boot 4.0.0.
- Registry SSOT: `src/config/architecture/modules.json`.

Package roots: `dev.caskeleton.adapter.outbound` and
`dev.caskeleton.adapter.outbound.support`.

## Responsibility

- Own reusable outbound correlation, fail-open dependency logging, and auto-configuration seams.
- Provide technical support used by registered messaging/cache/notification/httpclient leaves.
- Keep feature-specific clients and routing policies in their owning leaf.

## Boundaries

- Allowed project dependencies are registry-owned: application, domain, and shared contracts.
- No inbound transport, persistence, bootstrap, or sample dependencies.
- No business rules or external-system-specific protocol implementation.

## Tests

Use focused unit tests for support behavior. Do not start a Spring application context unless the
contract being tested is configuration wiring itself.

Exit code: 0
