Files
llm-wiki/raw/branch-notes/feature-runtime-context-propagation-contract.md

38 KiB
Raw Permalink Blame History

title, source_type, status, branch, parent_branch, related_projects, governing_docs, tags, created, target_merge, status_label, id, kind, project, work_item, inherits, refines, overrides, depends_on, contract_packet, contract_packet_sha256
title source_type status branch parent_branch related_projects governing_docs tags created target_merge status_label id kind project work_item inherits refines overrides depends_on contract_packet contract_packet_sha256
branch / feature-runtime-context-propagation-contract branch-note raw feature-runtime-context-propagation-contract
ca-skeleton
ca-tmpl
raw/project-notes/ca-skeleton-operational-contract.md
branch
observability
concurrency
virtual-threads
context-propagation
2026-06-09 in-progress BR-CA-SKELETON-OPERATIONAL-CONTRACT-056 project-work-item ca-skeleton-operational-contract WI-CA-SKELETON-OPERATIONAL-CONTRACT-056
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-MODULE-LAYOUT-001@1
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-LANGUAGE-001@1
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-FRAMEWORK-001@1
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-TEST-001@1
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-ARCHTEST-001@1
WI-CA-SKELETON-OPERATIONAL-CONTRACT-002
WI-CA-SKELETON-OPERATIONAL-CONTRACT-001
WI-CA-SKELETON-OPERATIONAL-CONTRACT-025
WI-CA-SKELETON-OPERATIONAL-CONTRACT-027
WI-CA-SKELETON-OPERATIONAL-CONTRACT-022
1 c4b5f766a11a0331d04ca0649fd795aa293d04ef0f05fb0e90b569a921481053

branch: feature-runtime-context-propagation-contract

Layer: raw/branch-notes/ — 단일 브랜치의 TODO·결정·진행 기록. 머지/종료 후 verified 결과는 /ingestwiki/projects/에 추출. 원본은 raw에 영구 보관. status_label: in-progress | review | merged | abandoned 계층 표기: project 의 직접 자식 branch 는 parent_branch:비워두고 related_projects 만 채움.

부모 (필수)

이 branch 는 ca-skeleton 운영 계약 project 의 직접 자식 branch (project 분해표 §8.0 E영역 row 7). parent_branch: 비어있음.

  • Project 의 직접 자식 branch: raw/project-notes/ca-skeleton-operational-contract (§8.0 E영역 priority 7: feature-runtime-context-propagation-contract — "virtual thread 활성화 + 도메인 context 전파 요구 시점 / Java 21 Scoped Values — boundary B6 의 도메인 확장")

이 branch 가 확장 하는 형제 branch (B6 baseline 의 도메인 확장이므로 강결합):

본 branch 가 결정을 위임 하는 형제 branch (Out of scope, §범위 참조):

브랜치 계약 패킷

  • 생성 시 프로젝트 개정: 1
  • 패킷 스키마: contract_packet: 1
  • 완료 조건: runtime context capture·propagation·cleanup과 architecture/contract test가 명시된다

상속한 프로젝트 결정

Decision Ref Project Summary Branch Application Source
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
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-LANGUAGE-001@1 application language는 Java 21 LTS다 Work Item 완료 조건에 적용 raw/project-notes/ca-skeleton-operational-contract
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-FRAMEWORK-001@1 framework는 Spring Boot 3.5.14다 Work Item 완료 조건에 적용 raw/project-notes/ca-skeleton-operational-contract
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-TEST-001@1 test framework는 JUnit 5다 Work Item 완료 조건에 적용 raw/project-notes/ca-skeleton-operational-contract
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

브랜치 지역 결정

기존 branch-local 결정은 아래 ## Decision Evidence Map / 결정-근거 매핑의 D-row가 소유하며 이 packet에서 복제하지 않는다.

Decision ID Decision Relation Supporting Claims Status

선언한 예외

Override ID Overrides Reason Approval Status

목표

ca-skeleton 의 진단(diagnostic) context 전파request_id / trace_id / correlation_id 를 inbound filter 에서 MDC 에 심고 virtual thread 위에서 application layer 까지 전달 — 은 이미 B6 (boundary-validation D13) 에서 구현 완료(actually-implemented: VirtualThreadMdcPropagationTest, VirtualThreadMdcE2ETest, no_inheritable_thread_local ArchUnit rule).

본 branch 는 그 도메인 확장이다: 진단용 6개 MDC 키를 넘어서는 도메인/비즈니스 context(예: 도메인 식별자)를 virtual thread + structured concurrency(StructuredTaskScope.fork()) 경계에서 전파하는 기본 구현 + 스왑 가능 추상화를 제공한다.

2026-06-09 설계 전환 (baseline=nothing → 기본 구현 + 스왑): 초안은 "트리거 전까지 아무것도 선박 안 함(baseline=nothing)"이었으나, 이 skeleton 자신의 rate-limit 선례(RateLimiter 포트 + FixedWindowRateLimiter 기본 + RateLimitAlgorithm/Factory 스왑)에 비춰 과소(under-ambitious)로 판정. rate-limit 의 교훈 = 메커니즘과 값을 분리 — 포트는 값(도메인 key)을 몰라도 추상화 가능. 따라서 메커니즘(경계 넘어 capture/restore)을 기본 구현(plain ThreadLocal)으로 선박하고, *값(key)*만 도메인이 등록하도록 전환. 불가능한 부분(ScopedValue 기본 — preview, --enable-preview 부재)과 도메인 고유 부분(어떤 key)만 deferred.

  • 기본 제공: DomainContextPropagator 포트 + ThreadLocalDomainContextPropagator 기본 구현 + DomainContextStrategy enum + DomainContextPropagatorFactory + DomainContextProperties(ca-skeleton.domain-context.strategy, 기본 THREAD_LOCAL). rate-limit 구조 1:1 미러.
  • 스왑 가능: MICROMETER(stable, 다중 key/Reactor)·SCOPED_VALUE(preview, --enable-preview 시) 는 enum 주석 + factory 확장점으로 예약.
  • 트리거(값 활성화): 도메인 코드가 비즈니스 식별자를 async/fork 경계 너머로 요구하는 시점 (project note L2080) — 그때 DomainContextKey 상수를 도메인이 선언. seam 은 그 전까지 동작하지만 전파할 값이 없음(라우트 없는 RateLimitKeyResolver 와 동일).
  • 이슈:
  • PR:

범위

포함 범위

  • (S1) 통합 cross-boundary 전파 메커니즘 메타-계약 — virtual thread 활성화(spring.threads.virtual.enabled=true) 시 어느 경계에서 어느 메커니즘이 적용되는지 의 단일 위임 맵. 특히 background-job 의 ThreadPoolTaskExecutor+TaskDecorator 모델(pool)과 B6 의 SimpleAsyncTaskExecutor(virtual) 전환의 정합 — 현재 어느 형제도 소유하지 않는 seam.
  • (S2) 도메인 context 전파 메커니즘 추상화 + 기본 구현DomainContextPropagator 포트 + ThreadLocalDomainContextPropagator 기본(default) + DomainContextStrategy enum + factory 스왑. 구현됨(2026-06-09). ScopedValue/Micrometer 는 예약 strategy.
  • (S3) fork 경계 명시적 capture/rebind 규칙 — 도메인 context 는 thread/StructuredTaskScope fork 마다 명시적으로 재확립해야 한다(묵시적 상속 금지). InheritableThreadLocal 금지의 도메인-context 판본. 구현됨: wrap(Runnable/Callable) + capture()/restore() API + no-silent-inheritance 테스트.
  • (S4) ScopedValue 전용 신규 ArchUnit enforcement(활성화 시) — 기존 no_inheritable_thread_local(B6 소유)을 cross-cite 하되, Scoped-Value 오용 차단 룰만 신규 도입. deferred(rule shape 미정, UNSUPPORTED). 단 domain_context_propagation_primitives_stay_unshipped 가드(preview API 차단)는 선박됨.

제외 범위

의도적으로 제외 — 각각 형제 branch 가 SSOT. 본 branch 의 §구현 가이드에 재결정 하지 않고 cross-cite 만 한다(CLAUDE.md §15.5 R3).

근거 (필수, 최소 1개+)

도메인 context 메커니즘 결정(S2)의 근거가 되는 외부 자료. /branch-spec 자동조사(wiki-decision-researcher)가 N=3 alternatives × 공식문서+기술블로그로 생성.

Source 정당화하는 결정
raw/official-docs/scoped-value-jep-446-506-openjdk D2(ScopedValue) 공식 명세 — immutability / bounded lifetime / StructuredTaskScope inheritance
raw/company-tech-blogs/scoped-value-structured-concurrency-softwaremill D2(ScopedValue) production 패턴 사례 (SoftwareMill, 2025-09)
raw/official-docs/micrometer-context-propagation-official D3(Micrometer ContextSnapshot) 공식 API — capture/restore + ThreadLocalAccessor 등록
raw/company-tech-blogs/micrometer-context-propagation-line-be-hase D3(Micrometer) production 사례 (LINE / Ryosuke Hasebe, 2025-02)
raw/official-docs/threadlocal-virtual-threads-java21-oracle D4(plain ThreadLocal) virtual thread 안전성 — per-virtual-thread 독립 copy
raw/company-tech-blogs/threadlocal-capture-restore-att-israel D4(plain ThreadLocal) TaskDecorator capture-restore 패턴 사례 (AT&T Israel, 2022-04)

형제 branch 결정(foundation D11, distributed-tracing D5/D7, background-job D5/D6, boundary-validation D13)은 외부 Source 가 아니라 cross-contract 의존 이므로 §엣지·실패·의존 + Decision Evidence Map 의 Supporting Claims 에 기재.

TODO

각 항목 옆에 증거 등급 표기.

  • (S2) 도메인 context 메커니즘 추상화 + 기본 구현 — 등급: locally-verified (shared-contract DomainContextPropagator/ThreadLocalDomainContextPropagator/DomainContextStrategy/DomainContextPropagatorFactory/DomainContextSnapshot + app-bootstrap DomainContextProperties/DomainContextConfig. :shared-contract:test --tests '*DomainContext*' 11/11 green)
  • (S3) fork 경계 명시적 capture/rebind — 등급: locally-verified (wrap()/capture()/restore(); virtual-thread 전파 + no-silent-inheritance + finally-revert 테스트 통과)
  • (S1) 통합 boundary→mechanism 위임 맵 (cross-cite siblings, reference-only) — 등급: documented-only (package-info.java §S1)
  • (S4) ScopedValue 전용 ArchUnit rule 신규 작성 (활성화 시) — 등급: planned (UNSUPPORTED_IMPL_DECISION — rule shape 미정, 지어내지 않음)
  • (신규) preview-primitive 가드 — SCOPED_VALUE 전략 미선박 강제 — 등급: locally-verified (domain_context_propagation_primitives_stay_unshipped ArchUnit rule, CleanArchitectureTest 47/47 green; production 이 ScopedValue/StructuredTaskScope 참조 시 fail)
  • 트리거 시점 결정: 도메인 context key 집합 명세 (tenantId? userId? …) — 등급: needs-confirmation (도메인이 DomainContextKey 상수 선언 시)
  • B6 baseline(virtual-thread MDC propagation) 사전확인 — 등급: actually-implemented (boundary-validation D13 소유, 본 branch 범위 밖)

진행 중 메모

  • 2026-06-09 설계 전환 + 구현 (선택지 B → 기본구현+스왑, SUPERSEDES 아래 baseline=nothing 메모): rate-limit 선례 대조에서 baseline=nothing 이 과소로 판정 → 기본 구현 + 스왑 추상화로 승급 구현. 선박물: shared-contract/.../concurrency/DomainContextKey·DomainContextPropagator·DomainContextSnapshot·DomainContextStrategy·DomainContextPropagatorFactory·ThreadLocalDomainContextPropagator(기본) + app-bootstrap/.../concurrency/DomainContextProperties·DomainContextConfig. 테스트: :shared-contract:test --tests '*DomainContext*' 11/11 green(set/get/clear, snapshot 불변, restore revert, virtual-thread wrap() 전파, no-silent-inheritance, finally-revert), :app-bootstrap:test --tests '*CleanArchitectureTest' 47/47 green(shared-contract 순수성 + preview-primitive 가드 유지). MICROMETER/SCOPED_VALUE 는 예약 strategy(enum 주석+factory 확장점). package-info 는 baseline=nothing 서술에서 default+swap 서술로 재작성.
  • 2026-06-09 구현(선택지 B 채택, 위 메모로 대체됨): 사용자 지시("문서대로 구현, 하나도 빠짐없이")를 future-activation contract 의 baseline=nothing(D1)과 양립시키기 위해 문서 계약 artifact + baseline 가드 테스트만 선박. (1) src/shared-contract/src/main/java/dev/caskeleton/shared/concurrency/package-info.java — S1 위임 맵 + S2 선택 결정표 + S3 fork rebind 규칙 + S4 deferred 표기를 in-repo Javadoc 으로 인코딩(어노테이션 없는 package-info → .class 미생성, source-only 의도와 일치). (2) CleanArchitectureTestdomain_context_propagation_primitives_stay_unshipped 신규 ArchUnit rule — D1 강제(production 이 ScopedValue/StructuredTaskScope 참조 금지). D2/D3/D4 메커니즘·S4 활성화 룰은 여전히 미선박(planned/UNSUPPORTED). 검증: :shared-contract:clean compileJava, :app-bootstrap:test --tests '*CleanArchitectureTest', verifyCleanArchitectureDependencies 모두 green.
  • 2026-06-09 검증 함정 기록: 첫 arch 테스트 실행이 :shared-contract:compileJava UP-TO-DATE 로 새 package-info 가 classpath 에 없어 stale green 이었음. :shared-contract:clean 후 재실행으로 true green 확보. (preview API 라 위반 fixture 컴파일 불가 → fixture 대신 dormant defence-in-depth rule + 컴파일 게이트 이중방어로 문서화.)
  • 2026-06-09: ScopedValue 는 ca-tmpl src 에 0건 — 도메인 context 전파는 전적으로 greenfield/미선박. B6(진단 MDC)만 구현됨.
  • 2026-06-09 빌드 사실(C1 해소): ca-tmpl 빌드(src/build.gradle, Java 21 toolchain JavaLanguageVersion.of(21))에 --enable-preview 없음. StructuredTaskScope 도 0건. → 현재 D2(ScopedValue) 는 빌드 정책 변경 전까지 unavailable; 트리거 도착 시 기본 후보는 D3/D4.
  • no_inheritable_thread_local rule 은 CleanArchitectureTest.java:623 에 존재하고 본문에서 명시적으로 "feature-boundary-validation-mapping-contract B6" 를 cite — 본 branch 는 재소유 금지, cross-cite.
  • background-job D5(TaskDecorator)는 src/main 에 미구현(planned). 즉 pool-vs-virtual 정합(S1 seam)은 아직 코드로 충돌하지 않은 미래 정합 대상.

결정 사항

각 결정 근거는 위 Sources 또는 형제 branch 결정을 가리킴. 2026-06-09 재구성: D1 이 baseline=nothing → 기본구현+스왑으로 전환. D2/D3/D4 는 트리거 시 택1 이 아니라 seam 뒤 strategy 옵션 — D4(ThreadLocal)가 선박된 기본, D2/D3 는 예약.

  • 2026-06-09: (D1) 도메인 context 전파의 기본 구현 + 스왑 추상화를 선박(SUPERSEDES baseline=nothing). / 이유: rate-limit 선례(메커니즘과 값 분리) — 포트는 도메인 값을 몰라도 추상화 가능하므로 메커니즘은 기본 구현(ThreadLocal)으로 선박하고 *값(key)*만 도메인이 등록. / 근거: raw/project-notes/ca-skeleton-operational-contract L2080(트리거는 이제 활성화에만 적용) + rate-limit 구조 선례(RateLimiter/RateLimitAlgorithm/Factory).
  • 2026-06-09: (D4) plain ThreadLocal capture-restore 를 기본(default) strategy 로 선박THREAD_LOCAL. / 이유: virtual-thread 안전(per-thread copy) + zero dep + InheritableThreadLocal-free + 단순. / 근거: raw/official-docs/threadlocal-virtual-threads-java21-oracle, raw/company-tech-blogs/threadlocal-capture-restore-att-israel.
  • 2026-06-09: (D3) Micrometer Context Propagation 을 예약 strategy(MICROMETER)로 — 스왑 조건: stable-API + 다중 key/Reactor 확장. (enum 주석 + factory 확장점, 미선박. C7: virtual-thread 보장 확인 후 활성화.) / 근거: raw/official-docs/micrometer-context-propagation-official, raw/company-tech-blogs/micrometer-context-propagation-line-be-hase.
  • 2026-06-09: (D2) ScopedValue 를 예약 strategy(SCOPED_VALUE)로 — 스왑 조건: --enable-preview 수용(현재 부재, C1) + StructuredTaskScope 중심 + immutability. (preview API, domain_context_propagation_primitives_stay_unshipped 가드로 production 진입 차단.) / 근거: raw/official-docs/scoped-value-jep-446-506-openjdk, raw/company-tech-blogs/scoped-value-structured-concurrency-softwaremill.
  • 2026-06-09: (D5) 통합 boundary→mechanism 위임 맵 — 각 경계의 전파는 형제 branch 가 소유; 본 branch 는 consolidation view 만 제공(reference-only). / 근거: foundation D11, distributed-tracing D5/D7, background-job D5/D6, boundary-validation D13.
  • 2026-06-09: (D6) fork 경계 명시적 capture/rebind 규칙 — 도메인 context 는 thread/StructuredTaskScope fork 마다 명시적 재확립; 묵시적 상속 금지(InheritableThreadLocal ban 의 도메인 판본). / 근거: boundary-validation D13(no-silent-inheritance) + scoped-value-jep-446-506-openjdk#SV-C2(StructuredTaskScope 내 자동 상속은 scope 안 에 한정).
  • 2026-06-09: (D7) ScopedValue 전용 신규 ArchUnit enforcement — 활성화 시. 구체적 rule shape 는 근거 없음(UNSUPPORTED_DECISION). / 기존 no_inheritable_thread_local(B6) cross-cite.

결정-근거 매핑

Supporting Claims: 외부 raw 는 raw/<slug>.md#<ClaimID>, cross-contract 의존은 형제 branch 의 D<n>.

Decision ID Decision 선택 조건 (언제 이 결정 / 언제 대안) Supporting Claims Evidence Strength Open Risk
D1 도메인 context 기본 구현 + 스왑 추상화 선박 (포트+default+factory) — 구현됨 항상(기본 제공). 트리거는 값(도메인 key) 활성화에만 적용 — 도메인이 DomainContextKey 선언 시. raw/project-notes/ca-skeleton-operational-contract L2080(값 트리거) + rate-limit 구조 선례(RateLimiter/Factory) governing + repo-precedent seam 은 동작하나 도메인 key 0개면 전파 값 없음(라우트 없는 rate-limit 와 동일, 정상)
D4 plain ThreadLocal capture-restore = 선박된 기본 strategy(THREAD_LOCAL) 구현됨 기본값. 다중 key/Reactor 면 D3, preview 수용 시 D2 로 스왑. raw/official-docs/threadlocal-virtual-threads-java21-oracle.md#TL-VT-C1, raw/company-tech-blogs/threadlocal-capture-restore-att-israel.md#ATT-TL-C1 official-vendor-doc + company-case-study + locally-verified(11 tests) finally-clear 는 wrap()/restore() 가 try-with-resources 로 강제(규율 위험 해소). key 증가 시 D3 권고
D3 Micrometer Context Propagation = 예약 strategy(MICROMETER) — 미선박(enum 주석+factory 확장점) 스왑: stable-API + 다중 key/Reactor 확장. raw/official-docs/micrometer-context-propagation-official.md#MCP-C3, #MCP-C4, raw/company-tech-blogs/micrometer-context-propagation-line-be-hase.md#LN-MCP-C1 official-vendor-doc + company-case-study 공식 문서가 virtual thread 명시 보장 없음(C7 — 활성화 전 확인); 추가 의존성
D2 ScopedValue = 예약 strategy(SCOPED_VALUE) — 미선박(preview 차단) 스왑: --enable-preview 수용(현재 부재 C1) + StructuredTaskScope 중심 + immutability. raw/official-docs/scoped-value-jep-446-506-openjdk.md#SV-C1, #SV-C2, raw/company-tech-blogs/scoped-value-structured-concurrency-softwaremill.md#SM-SV-C1 official-standard + company-case-study Java 21 preview(--enable-preview 필요); domain_context_propagation_primitives_stay_unshipped 가드가 production 진입 차단
D5 통합 boundary→mechanism 위임 맵 (consolidation only, reference-only) 항상 — 본 branch 는 경계별 전파를 재결정 안 하고 위임 맵만 제공 foundation D11, distributed-tracing D5/D7, background-job D5/D6, boundary-validation D13 cross-contract (sibling decisions) background-job TaskDecorator(pool) ↔ B6 SimpleAsyncTaskExecutor(virtual) 정합 seam 미소유 — S1 핵심 리스크
D6 fork 경계 명시적 capture/rebind 규칙 (묵시 상속 금지) 항상 (도메인 context 활성화 시) boundary-validation D13 (no-silent-inheritance), raw/official-docs/scoped-value-jep-446-506-openjdk.md#SV-C2 cross-contract + official-standard StructuredTaskScope 자동상속과 수동재확립의 경계가 개발자에게 혼동 가능
D7 ScopedValue 전용 신규 ArchUnit rule (활성화 시) 도메인 context 활성화 + ScopedValue(D2) 채택 시 UNSUPPORTED_DECISION — 구체 rule shape 권고하는 raw 없음. 기존 no_inheritable_thread_local(boundary-validation D13) cross-cite none (unsupported) rule 부재 시 미래 개발자가 도메인 context 를 ThreadLocal 로 오용/누수

구현 가이드

본 branch 는 기본 구현 + 스왑 추상화(rate-limit 패턴)를 선박한다(2026-06-09). §2 의 포트/기본구현/factory 는 locally-verified(11 tests); 도메인 key·MICROMETER/SCOPED_VALUE strategy·S4 활성화 룰만 planned/예약.

1. Boundary → Mechanism 위임 맵 (D5 — REFERENCE ONLY)

Trace: D5. 각 행의 실제 호스팅 = sibling branch. 본 branch 는 consolidation view 만 — 코드 위치는 sibling.

  • UNSUPPORTED_IMPL_DECISION: (1) domain-context 행의 메커니즘은 D2/D3/D4 트리거 선택에 종속 — 트리거 전까지 미정(trade-off: 조기 확정 시 YAGNI 위반). (2) pool↔virtual 전환 정합 seam 행은 mechanism·owner 모두 미정 — 의도적 deferred: background-job D5(TaskDecorator) 구현 완료 + virtual executor 전환이 동시 성립할 때만 활성화(trade-off: 지금 정하면 미구현 D5 에 대한 근거 없는 가정).
경계 (boundary) 전파 대상 메커니즘 소유 branch (actual location) 본 branch 관계
inbound HTTP filter request_id/correlation_id/trace_id MDC SLF4J 2.0+ MDC (virtual-thread aware) boundary-validation D13 (RequestLoggingFilter.java) cross-cite (Out of scope)
outbound HTTP / message W3C traceparent/tracestate, baggage(tenant_id,request_id) Micrometer Tracing distributed-tracing D5/D7/D8 cross-cite (Out of scope)
@Async ThreadPoolTaskExecutor (pool) MDC 4키(request_id/trace_id/correlation_id/tenant_id) TaskDecorator copy (planned, 미구현) background-job D5/D6 cross-cite (Out of scope)
virtual-thread carrier (SimpleAsyncTaskExecutor) 진단 MDC SLF4J 2.0+ MDC, InheritableThreadLocal 금지 boundary-validation D13 (no_inheritable_thread_local CleanArchitectureTest.java:623) cross-cite (Out of scope)
StructuredTaskScope.fork() / thread handoff 도메인 context DomainContextPropagator.wrap()/capture() (기본 THREAD_LOCAL) 선박 본 branch (S2/S3) in scope — 구현됨
pool↔virtual 전환 정합 (TaskDecorator semantics when executor is not a pool) 미소유 seam 본 branch (S1) 신규

2. 도메인 context 추상화 + 기본 구현 (D1/D4 구현

Trace: D1(기본구현+스왑) + D4(THREAD_LOCAL 기본). Supporting: TL-VT-C1, ATT-TL-C1 + rate-limit 선례. 예약 strategy 근거: SV-C1/SV-C2(D2), MCP-C3/MCP-C4(D3).

선박된 코드 (:shared-contract:test --tests '*DomainContext*' 11/11 green):

요소 클래스 위치
포트 DomainContextPropagator shared-contract/.../concurrency/
기본 구현 ThreadLocalDomainContextPropagator (plain ThreadLocal)
strategy enum DomainContextStrategy (THREAD_LOCAL 기본; MICROMETER/SCOPED_VALUE 주석)
factory(확장점) DomainContextPropagatorFactory (switch)
키(도메인 확장점) DomainContextKey<T>
hand-off DomainContextSnapshot + wrap()/capture()/restore()
Spring 와이어링 DomainContextProperties(ca-skeleton.domain-context.strategy) + DomainContextConfig app-bootstrap/.../concurrency/
  • 여전히 planned/예약: 도메인이 선언할 DomainContextKey 상수(C5), MICROMETER strategy(C7 확인 후), SCOPED_VALUE strategy(--enable-preview 시 C1), S4 활성화 룰(UNSUPPORTED).

strategy 스왑 규칙 (DomainContextStrategy / factory):

IF   (build 가 --enable-preview 수용) AND (StructuredTaskScope 중심) AND (context immutable)
THEN ScopedValue                         # D2 — fork 자동상속(scope 내) + immutability
ELIF (stable-API only) AND (Reactor 확장 가능성 OR 다중 domain key)
THEN Micrometer ContextSnapshot          # D3 — ThreadLocalAccessor 등록 1회 + captureAll()
ELSE plain ThreadLocal + capture-restore wrapper   # D4 — 1~2 key, 최소 추상화

# 2026-06-09 빌드 사실(C1): ca-tmpl 빌드에 --enable-preview 없음 + StructuredTaskScope 0건
#   → 현재 IF(D2) 가지는 빌드 정책 변경 전까지 dead. 트리거 시 ELIF/ELSE 부터 평가.
# C5(domain key 수) 미정 시 폴백 순서: 기본 D4(plain TL, 1~2 key) → key 증가/Reactor 도입 시 D3.

3. fork 경계 명시적 capture/rebind 규칙 (D6 — planned)

Trace: D6. Supporting: boundary-validation D13(no-silent-inheritance, actually-implemented) + scoped-value-jep-446-506-openjdk.md#SV-C2.

  • 도메인 context 는 thread/StructuredTaskScope fork 를 넘을 때 명시적으로 재확립한다. 묵시적 상속(InheritableThreadLocal)은 금지 — B6 가 이미 no_inheritable_thread_local(CleanArchitectureTest.java:623)로 차단(cross-cite, 재작성 금지).
  • 단 ScopedValue(D2)는 StructuredTaskScope scope 안 fork 에서는 자동 상속(SV-C2) — 이 한 경우만 예외이며 scope fork 는 여전히 명시적 재확립 필요.
  • 실패 동작: capture/rebind 누락 시 도메인 context 유실 → 계약 위반(테스트로 감지, S4).

4. ScopedValue 전용 ArchUnit enforcement (D7 — UNSUPPORTED_IMPL_DECISION

Trace: D7. UNSUPPORTED_IMPL_DECISION: 구체적 rule shape(무엇을 noClasses/should 로 차단할지)를 권고하는 raw 없음. 활성화 시 신규 작성 대상이며, 그 전까지 기존 no_inheritable_thread_local(B6, boundary-validation D13)만 유효. trade-off: 지금 rule 을 지어내면 근거 없는 결정.

  • REFERENCE ONLY: no_inheritable_thread_local (actual location: app-bootstrap CleanArchitectureTest.java:623, owner=boundary-validation D13).
  • 신규(활성화 시 본 branch host): 도메인 context 를 ThreadLocal 로 오용/누수 차단하는 rule — 형식 미정.

엣지·실패·의존

  • 실패·엣지 경로:
    • --enable-preview 가 CI/build 정책에서 거부됨 → D2(ScopedValue) 불가, D3/D4 로 강등.
    • ca-tmpl 이 StructuredTaskScope 를 전혀 사용하지 않음(현재 grep 0건) → D2 의 fork 자동상속 이점 소멸, D3/D4 와 동등.
    • D4 의 finally-clear 누락 → 같은 virtual thread 내 후속 단계에서 stale 도메인 context 읽기.
    • ScopedValue ↔ OTel ContextStorage(attach/detach) 비호환 → distributed-tracing 의 trace context 와 도메인 context 공존 시 충돌 가능. ⚠️ 근거 raw 미등록 — 자동조사 시 secondary 로만 언급된 별도 SoftwareMill OTel 아티클. 활성화(D2 채택) 전 raw/company-tech-blogs/ 에 정식 등록 후 이 엣지를 설계 제약으로 승격할 것(미등록 상태로 설계 판단에 사용 금지).
  • 다른 계약 의존:

검증해야 할 주장

Claim Why uncertain How to verify Status
(C1) Java 21 LTS 에서 ScopedValue--enable-preview 없이 컴파일 불가 preview API 여부가 빌드 정책을 좌우(D2 선결조건) ca-tmpl build.gradle.kts compileJava options + JEP 446/506 직접 확인 needs-confirmation
(C2) ca-tmpl 이 StructuredTaskScope 를 도메인 경로에서 사용/계획 D2 fork 자동상속 이점의 전제 grep -r StructuredTaskScope src (현재 0건) + 도메인 온보딩 계획 확인 planned
(C3) io.micrometer:context-propagation 이 Spring Boot 3.5.x starter 로 classpath 에 transitive 존재 D3 의 추가 의존성 여부 ./gradlew dependencies 의존성 트리 grep needs-confirmation
(C4) plain ThreadLocal + TaskDecoratorSimpleAsyncTaskExecutor(virtual) 에서 동작 AT&T 사례는 2022(Loom GA 이전) — virtual 미검증 D4 채택 결정 전 사전 spike 의무: virtual thread executor 통합 테스트 작성 planned
(C5) 트리거 시점의 도메인 context key 집합(tenantId? userId? …) 미정 — key 수가 D3 vs D4 선택을 가름 도메인 온보딩 시 use case 별 필요 식별자 명세 needs-confirmation
(C6) pool(TaskDecorator)↔virtual(SimpleAsyncTaskExecutor) 전환 시 context-copy semantics 정합(S1 seam) 어느 형제도 미소유; background-job D5 미구현 background-job TaskDecorator 구현 후 virtual 전환 통합 테스트 planned
(C7) Micrometer ContextSnapshot captureAll()/setThreadLocals() 가 virtual thread 환경에서 안전 공식 문서가 virtual thread 명시 보장 없음(plain TL 간접 지지뿐) Spring Boot 3.3+ 릴리즈 노트 / Micrometer CHANGELOG 의 virtual thread 호환성 명시 raw 등록, 또는 D3 채택 전 통합 테스트 needs-confirmation

관심사 커버리지 (coverage-auditor 자동 생성 — 있을 때)

/coverage 가 채우는 생성물(손유지 금지, 실행 시마다 재생성). governing 문서(raw/project-notes/ca-skeleton-operational-contract.md)가 요구하는 관심사 커버리지. 기준: rules/coverage-gate.md. 2026-06-09 coverage-auditor 판정: Covered (Blocking 0 / Should-fix 0 / Advisory 1).

관심사 (governing doc 출처) 상태 owner 심각도 근거
도메인 context 전파 메커니즘 선택 계약 (§8.0 E row 7) covered-here D1 + D2/D3/D4 조건부 + §구현 §2 선택표
virtual thread + fork 경계 명시적 capture/rebind 규칙 (§8 async boundary, §15) covered-here D6
통합 boundary→mechanism 위임 맵 (§8 전 경계 전파) covered-here D5 (§구현 §1 표)
기본 구현 + 스왑 추상화 (§8.0 E row 7, rate-limit 패턴) covered-here D1 (포트+default+factory, L2080 값 트리거)
pool↔virtual 전환 정합 seam (§15/§18 미소유 신규) covered-here D5 S1 + C6
ScopedValue 전용 ArchUnit enforcement (§15) covered-here Advisory D7 (UNSUPPORTED_DECISION 라벨)
MDC key 카탈로그 + snake↔camel↔kebab (§8/§21) delegated raw/branch-notes/feature-operational-error-observability-foundation (D6/D8/D11/D19) OK §범위 Out of scope + §엣지 의존
W3C traceparent/baggage/sampling (§8 Distributed Tracing) delegated raw/branch-notes/feature-distributed-tracing-contract (D5/D7/D8) OK §범위 Out of scope + §엣지 의존
@Async TaskDecorator MDC-copy + pool sizing (§15/§18) delegated raw/branch-notes/feature-background-job-async-contract (D5/D6/D7/D8) OK §범위 Out of scope + §구현 §1 표
B6 baseline virtual-thread MDC + no_inheritable_thread_local rule (§15) delegated raw/branch-notes/feature-boundary-validation-mapping-contract (D13) OK §목표 + 코드 CleanArchitectureTest.java:623
tenant_id lifecycle/policy (§19 Tenant Policy) delegated raw/branch-notes/feature-tenant-context-policy OK §범위 Out of scope + §엣지 의존
ScopedValue↔OTel ContextStorage 비호환 (§8 공존) covered-here Advisory §엣지 open risk (근거 raw 미등록 — 활성화 전 등록 의무)

마주친 문제

  • (없음)

묶음 (이 branch에서 파생된 자료)

Sub-branches (세부 작업)

  • (없음)

오류 기록 (이 branch 작업 중 발생)

  • (별도 raw/errors 파일 불필요 — 진행 중 메모에 인라인 기록) 2026-06-09 "stale green": Gradle :shared-contract:compileJava UP-TO-DATE 로 새 package-info 가 ArchUnit classpath 에 미반영되어 첫 실행이 가짜 green. 교훈 = 새 소스 추가 후 arch 테스트는 해당 모듈 clean 후 재실행. 재사용 가치 낮아 derived 파일 생성 안 함.

면접 준비 (이 작업에서 나올 수 있는 면접 질문)

  • (후보) "virtual thread 에서 MDC/context 가 왜 안 깨지는가, InheritableThreadLocal 은 왜 금지했는가" — B6 + 본 branch 도메인 확장

강의 (이 작업을 위해 학습한 강의)

  • (없음)

job-posting tie-ins (이 작업에서 파생된 글감)

  • (후보) "Java 21 ScopedValue vs Micrometer Context Propagation vs ThreadLocal — virtual thread 시대의 context 전파 선택"
  • derived blog: 생성 전

외부 근거 자료 (Sources — 자동조사 생성)

관련 일일 노트

  • (없음)

완료 후 정리

머지/종료 시점에 채움. /ingest가 이 섹션 기준으로 wiki/projects/에 추출.

  • PR 링크:
  • 리뷰 메모:
  • 머지 결과 / 배포 환경:
  • 머지 결과 / 배포 환경: 로컬 검증 완료(:shared-contract:test --tests '*DomainContext*' 11/11, :app-bootstrap:test --tests '*CleanArchitectureTest' 47/47). prod 미배포.
  • wiki 추출 대상 (verified만, wiki/projects/로만 추출):
    • actually-implemented 항목: B6 baseline 은 boundary-validation 소유(본 branch 추출 대상 아님)
    • locally-verified 항목: (D1) 도메인 context 기본구현+스왑 추상화 — DomainContextPropagator 포트 + ThreadLocalDomainContextPropagator 기본 + DomainContextStrategy/Factory 스왑 + wrap()/capture()/restore()(S3) + Spring 와이어링 + domain_context_propagation_primitives_stay_unshipped 가드. rate-limit 패턴 미러.
    • prod-verified 항목:
  • 추출하지 않을 항목 (planned / documented-only / abandoned): 도메인 DomainContextKey 상수(C5, 도메인 몫), MICROMETER/SCOPED_VALUE 예약 strategy(D3/D2 미선박), S4 활성화 룰(D7 UNSUPPORTED), D5(reference-only consolidation)