Files
llm-wiki/raw/branch-notes/feature-developer-experience-contract.md
T

444 lines
47 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: branch / feature-developer-experience-contract
source_type: branch-note
status: raw
branch: feature-developer-experience-contract
parent_branch:
related_projects: [ca-skeleton]
governing_docs: [wiki/projects/ca-tmpl/devops-ci-supply-chain-dx]
tags: [branch, ca-skeleton, developer-experience, local-dev]
created: 2026-05-22
target_merge:
status_label: review
id: BR-CA-SKELETON-OPERATIONAL-CONTRACT-033
kind: project-work-item
project: ca-skeleton-operational-contract
work_item: WI-CA-SKELETON-OPERATIONAL-CONTRACT-033
inherits: [DEC-CA-SKELETON-OPERATIONAL-CONTRACT-BOOTSTRAP-001@1, DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-BUILD-001@1]
refines: []
overrides: []
depends_on: []
contract_packet: 1
contract_packet_sha256: d507282d4a550e9385db2a647f07c608950ae223886a80132d45d1957d2a2aee
---
# branch: feature-developer-experience-contract
> Layer: `raw/branch-notes/` — 실무자가 skeleton을 받아 바로 실행, 검증, 확장할 수 있는 local developer experience 기준을 정의합니다.
<!-- section-id: branch-parent -->
## 부모 (필수)
- **Parent project (canonical SSOT)**: [[raw/project-notes/ca-skeleton-operational-contract]]
> ca-skeleton 은 별도 root branch 없이 project-note 가 SSOT 역할. 본 feature branch 는 project-note 의 운영 계약 중 §18 Control Plane Contract → Developer Experience 영역의 결정/근거/금지 사항을 정제한다. governing doc = [[wiki/projects/ca-tmpl/devops-ci-supply-chain-dx]] (DX 슬라이스).
<!-- GENERATED: branch-contract:start -->
<!-- section-id: branch-contract-packet -->
## 브랜치 계약 패킷
- **생성 시 프로젝트 개정**: `1`
- **패킷 스키마**: `contract_packet: 1`
- **완료 조건**: fresh environment에서 ./gradlew bootstrap이 성공한다
<!-- section-id: inherited-project-decisions -->
### 상속한 프로젝트 결정
| Decision Ref | Project Summary | Branch Application | Source |
|---|---|---|---|
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-BOOTSTRAP-001@1` | 신규 환경의 default 진입 명령은 ./gradlew bootstrap이다 | Work Item 완료 조건에 적용 | [[raw/project-notes/ca-skeleton-operational-contract]] |
| `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-BUILD-001@1` | build tool은 Gradle Groovy DSL이다 | Work Item 완료 조건에 적용 | [[raw/project-notes/ca-skeleton-operational-contract]] |
<!-- section-id: branch-local-decisions -->
### 브랜치 지역 결정
> 기존 branch-local 결정은 아래 `## Decision Evidence Map / 결정-근거 매핑`의 D-row가 소유하며 이 packet에서 복제하지 않는다.
| Decision ID | Decision | Relation | Supporting Claims | Status |
|---|---|---|---|---|
<!-- section-id: declared-overrides -->
### 선언한 예외
| Override ID | Overrides | Reason | Approval | Status |
|---|---|---|---|---|
<!-- GENERATED: branch-contract:end -->
<!-- section-id: branch-goal -->
## 목표
좋은 skeleton은 구조가 훌륭한 것에서 끝나지 않습니다. 새 개발자가 로컬에서 빠르게 실행하고, sample contract를 확인하고, 실패 기준을 재현할 수 있어야 합니다.
- 이슈:
- PR:
<!-- section-id: branch-scope -->
## 범위
### 포함 범위
- local bootstrap command.
- `.env.example` 필수 key.
- sample profile 실행/비활성화 기준.
- Testcontainers 또는 local dependency 대체 기준.
- smoke test command.
- README/runbook link 기준.
### 제외 범위
- IDE별 개인 설정.
- cloud development environment 강제.
- production deployment guide.
## 근거 (필수, 최소 1개+)
> 이 branch의 구현·설계 결정의 근거가 되는 외부 자료. 상세 비교는 §외부 근거 / 대안 조사 참조. company-tech-blog 인용은 `company-case-study` 강도이며 official-standard / official-vendor-doc 으로 격상 금지.
| Source | 정당화하는 결정 |
|---|---|
| [[raw/official-docs/dx-testcontainers-java-best-practices]] | D3·D10 — bootstrap step 2 의 local dependency 도구 + Spring Boot 3.1+ `@ServiceConnection` 기반 default integration test backend 근거 (TC-CORE-C1~C4 / TC-SPRING-C1 / TC-REUSE-C1) |
| [[raw/official-docs/dx-mise-asdf-tool-versioning]] | D6 — JDK Temurin 21 LTS + `.tool-versions`/`.sdkmanrc` 핀 (도구를 강제 않고 파일 포맷을 강제하는 전략, DX-TV-C4/C5) |
| [[raw/official-docs/dx-devcontainer-spring-boot]] | D9 — devcontainer 를 default 로 두지 않는 결정의 대안 평가 (DX-DC-C2 development-phase 한정 / DX-DC-C4 VS Code 한정 / DX-DC-C5) |
근거 자료가 raw에 아직 없다면 먼저 `raw-source-template` 또는 `lecture-note-template` 으로 raw에 등록한 뒤 여기서 링크.
## 외부 근거 / 대안 조사 (2026-05-22 — Group G-E: Developer Experience)
본 branch의 `./gradlew bootstrap` 5단계 + Temurin 21 LTS + Testcontainers integration + markdown-link-check 결정에 대한 외부 source.
- **채택 결정 (Gradle bootstrap + Testcontainers + Temurin 21)**:
- [[raw/official-docs/dx-testcontainers-java-best-practices]] — Testcontainers + Spring Boot 3.1 `@ServiceConnection` + reuse/singleton 패턴
- [[raw/official-docs/dx-mise-asdf-tool-versioning]] — mise/asdf/SDKMAN + `.tool-versions` 포맷 + Temurin 21 LTS
- **검토한 대안**:
- **대안 1: `make bootstrap`** — POSIX 표준이나 Windows 친화성 낮음
- **대안 2: `docker compose up` only** — bootstrap 5단계 합성 어려움
- **대안 3: devcontainer (VSCode·Codespaces)** — [[raw/official-docs/dx-devcontainer-spring-boot]] (containers.dev spec, IDE 종속성 + bootstrap 5단계 진입점/smoke 미해결)
- **대안 4: Nix flake** — reproducibility 강점이나 Java 생태계 성숙도 낮음
- **비교 핵심**: Gradle bootstrap이 5단계 합성 가능 + Spring 생태계 정합. Testcontainers `@ServiceConnection`(Spring Boot 3.1+)이 integration test의 boilerplate 제거. devcontainer는 IDE 종속이라 CI/CD와 분리 필요. **보강 후보**: `.tool-versions`(asdf/mise) vs `.sdkmanrc`(SDKMAN) 포맷 차이 — branch note "또는" 표현은 drift 위험, 단일 source로 좁힐 필요.
### 2026-06-15 addendum — D8 link-rot 도구 재조사 (`wiki-decision-researcher`)
D8 의 `markdown-link-check` 선택이 `UNSUPPORTED_DECISION` 이었으므로 대안을 조사했다 (`/branch-spec` §5 자동조사). 3종 비교:
| 도구 | Node 의존 | 유지보수 | CI gate | 비고 |
|---|---|---|---|---|
| `markdown-link-check` (npm/tcort) | 필수 (Docker 우회) | 단일 메인테이너, v3.14.2 (2025-11) | `tcort/github-action-markdown-link-check` | JVM-only repo 에 Node 툴체인 추가 비용 |
| **`lychee` (Rust/lycheeverse)** | **없음 (단일 정적 바이너리)** | 활발 (3,700+ stars, v0.24.2 2026-05, 40+ 프로젝트) | `lycheeverse/lychee-action@v2.0.2+` (CVE-2024-48908 패치 핀 필수) | **권고** — JVM/Gradle repo DX 마찰 최소 |
| `linkinator` (npm/binary) | npm 경로 필수 / 바이너리 옵션 | 활발 (v7.6.1 2026-02, Google Cloud SDK 사용) | `JustinBeckwith/linkinator-action@v1` | Node 도입 시 후보 |
- **조건부 권고**: ca-tmpl 이 `package.json`/Node toolchain 미도입을 유지하는 한 → **lychee** (Node 의존 없음). Node 를 다른 이유로 도입하면 → linkinator. 기존 Docker-first/MegaLinter 파이프라인이면 → markdown-link-check.
- **archiving 상태 (`deferred`)**: 위 비교의 raw 검증 자료(`wiki-source-summarizer` ×6, official + case-study) archiving 은 **사용자 승인 대기 중**. 승인 시 controller 가 dispatch → 생성 후 D8 의 `UNSUPPORTED_DECISION` 라벨을 `official-vendor-doc + company-case-study` 로 격상. 미archiving 상태에서는 D8 을 "조사됨, raw 미archiving" 로 표기(추측 단정 금지).
## TODO
> TODO drained 2026-05-22 — 결정은 아래 "결정 사항" / "Decision Evidence Map" / "Decisionized Work Items" / "테스트 계약" 참조. bootstrap/`.env.example`/sample profile/Testcontainers-local dep/smoke command/README-wiki 연결 모두 표 또는 결정 라인으로 반영됨. 잔존 TODO 없음.
>
> **구현 현황 (2026-06-15 ground truth)**: 본 branch 는 **documented-only / planned 단계** — ca-tmpl `src/` 실 코드에 `bootstrap` Gradle task·`.env.example`·`.tool-versions`·markdown-link-check·smoke test·CI 모두 미작성. 실제로 존재하는 것은 Flyway migration(V1/V3/V4) + JDK 21 toolchain + 수동 `@Container` Testcontainers + sample-portfolio(test-scope, ArchUnit 격리)뿐. 단계별 grade 는 §구현 가이드, drift 는 §Audit & Findings.
>
> **2026-06-24 구현 결과**: direct-owner 범위는 `actually-implemented`이며 Linux local에서 `locally-verified`됐다. `./gradlew bootstrap` 5단계, README command drift gate, `@ServiceConnection` context tests, local-only Testcontainers reuse policy, lychee workflow가 코드에 존재한다. `.env`/sample runtime toggle/fresh-clone CI는 기존 위임 owner를 유지한다. lychee remote CI와 macOS/WSL2는 `needs-confirmation`이다.
## 진행 중 메모
- 2026-06-15: ca-tmpl 코드 대조 결과 본 branch 의 다수 결정이 *아직 미구현(planned)* 이거나 *이미 구현된 sibling branch 와 drift* 함이 확인됨. 자동 rewrite 하지 않고 §Audit & Findings 에 정합 권고로 기록 (사용자 작성 결정 영역). 핵심 drift 5건: `BOOTSTRAP_TASK_ABSENT`, `GRADLE_VERSION_DRIFT`(8.x→실제 9.0.0), `ENV_EXAMPLE_SUPERSEDED`(`.env.example``src/.env`+`verifyEnvKeys` 로 sibling 이 이미 해소), `SAMPLE_ENABLE_MECHANISM_DRIFT`(@Profile 가정 → 실제 ArchUnit+env), `SERVICECONNECTION_NOT_USED`(@ServiceConnection 가정 → 실제 수동 `@Container`).
- 2026-06-24: D3/D4/D8/D10을 구현했다. bootstrap 첫 실행에서 host 5432 collision과 slim JRE RNG provider 누락을 발견해 각각 internal-only DB network와 `SplittableRandom` composition bean으로 해결했다. `./gradlew bootstrap`, `./gradlew test check`, focused ServiceConnection tests를 local에서 검증했다.
- 2026-06-30: CleanArchitectureTest.java의 자원 누수 경고 해결(@SuppressWarnings("resource") 추가 및 import 스타일 정리), README.md에서 누락되었던 feature-developer-experience-contract 식별자 복구로 테스트 통과 확인, Spring Boot 3.5.x EOL 경고 무시를 위한 VS Code settings.json 설정 반영. 추가로 Spring Boot 4.x 업그레이드 시 Testcontainers 2.0 라이브러리와의 마이그레이션 호환성을 면밀히 재평정한 spec 문서(docs/superpowers/specs/2026-06-30-testcontainers-2-0-migration-spec.md) 작성 완료.
## 결정 사항
- 2026-05-22: local 실행은 prod-safe 기본값을 훼손하지 않는 별도 profile/env로만 허용.
- 2026-05-22: sample fixture는 local/dev에서 쉽게 켤 수 있어야 하고 prod에서는 기본 비활성화.
- 2026-05-22: bootstrap command 기본값은 `./gradlew bootstrap`. 없으면 `./gradlew test``docker compose up` wrapper를 제공.
- 2026-05-22: README는 canonical wiki를 대체하지 않고, local start/smoke/adoption entrypoint만 제공.
- 2026-05-22: OS 매트릭스 = Linux (Ubuntu 22.04+), macOS (Apple Silicon 우선), Windows (WSL2 only). CI는 Linux만, 개발자는 3개 OS 검증 의무.
- 2026-05-22: JDK = Temurin 21 LTS. gradle-wrapper 8.x. `.tool-versions` 또는 `.sdkmanrc`로 핀.
- 2026-05-22: bootstrap task 정의 = `./gradlew bootstrap` = (1) `./gradlew compileTestJava` (compile sanity) (2) `docker compose up -d` (local dependencies via Testcontainers config 또는 별도 compose file) (3) Flyway migrate (4) sample profile seed (5) smoke test 실행. 5단계 모두 통과 시 성공.
- 2026-05-22: sample profile default = clone 직후 enabled. prod profile에서는 disabled (`feature-sample-removal-adoption-contract`와 일관).
- 2026-05-22: link-rot 검증 = `markdown-link-check` (npm). CI에서 README + docs/ 전수 검사.
- **2026-06-15 (위 항목 보강/대체 후보 — D8)**: link-rot 도구 재조사 결과 **lychee** (Node-free 단일 Rust 바이너리) 를 조건부 권고. 기존 `markdown-link-check` 결정은 *Node 의존 비용 미평가*였음(ca-tmpl 은 `package.json` 없는 JVM-only repo). 상세·트레이드오프: §외부 근거 2026-06-15 addendum + Decision Evidence Map D8.
- **2026-06-15 (정합 메모 — gradle wrapper)**: 위 "gradle-wrapper 8.x" 결정은 ca-tmpl 실제 `gradle/wrapper/gradle-wrapper.properties`**9.0.0** 과 drift. 핀 *전략*(repo wrapper 로 Gradle 버전 고정)은 유효하나 *버전 숫자*는 9.0.0 으로 정정 필요(§Audit `GRADLE_VERSION_DRIFT`).
## Work Item Contract
각 TODO는 아래 판정 단위로 재작성되어야 canonical 승급 가능합니다. TODO가 단순히 `기준 작성`으로 남아 있으면 이 branch는 완료로 보지 않습니다.
| field | required | rule |
| --- | --- | --- |
| Decision | yes | 구현자가 선택해야 하는 기본값 |
| Allowed | yes | 허용되는 예외와 조건 |
| Forbidden | yes | 절대 금지되는 구현/문서 상태 |
| Required registry update | conditional | error/env/header/log/metric/capability 변경 시 필수 |
| Required contract test | yes | 계약 위반 시 실패해야 하는 테스트 |
| Failure condition | yes | review/build에서 실패로 판정할 상태 |
| Canonical extraction target | yes | `wiki/projects` 승급 위치 |
## 결정-근거 매핑
> 본 branch 의 각 결정을 raw source 의 Claim ID 로 매핑. `선택 조건`(R2): 이 조건일 때 이 결정 / 다른 조건이면 어떤 대안. company-tech-blog 인용은 `company-case-study` 강도이며 official-standard / official-vendor-doc 으로 격상 금지.
| Decision ID | Decision | 선택 조건 (언제 이 결정 / 언제 대안) | Supporting Claims | Evidence Strength | Open Risk |
|---|---|---|---|---|---|
| D1 | local 실행은 prod-safe 기본값을 훼손하지 않는 별도 profile/env 로만 허용 | 항상 — local 편의를 위해 prod 기본값(error detail 노출·body logging 등)을 바꿔야 하면 별도 profile/env override 로만. **금지 대안**: 단일 profile 로 local+prod 겸용(=prod-unsafe default 누출) | (ca-tmpl 고유 정책; 외부 raw claim 없음. parent §9 Env-driven Runtime Config 에 정합) | UNSUPPORTED_DECISION | 외부 standard 부재 — 자체 정책으로만 정당화 |
| D2 | sample fixture 는 local/dev 기본 enabled, prod 기본 disabled | clone 직후 교육/계약검증 목적이면 enabled; prod 배포 profile 이면 disabled. **enable/disable 런타임 메커니즘 owner = `feature-sample-removal-adoption-contract`** (본 branch 는 DX 진입점만, 위임) | (`feature-sample-removal-adoption-contract` 와 연계; 본 branch 외부 raw 직접 claim 없음) | UNSUPPORTED_DECISION (delegated) | enable/disable (env `APP_SAMPLE_ENABLED`, registry-backed; 런타임 토글 *코드 메커니즘*은 owner 확정 대상 — 본 branch 가 단정 안 함) 는 sibling owner, 본 branch 미구현 |
| D3 | bootstrap 단일 entry point = `./gradlew bootstrap` 5단계 (compileTestJava → docker compose up -d → Flyway migrate → sample profile seed → smoke test) | skeleton 채택자가 *single command first-run* 을 원할 때 `./gradlew bootstrap`; CI/스크립트가 단계별 제어 필요하면 각 sub-task 직접 호출. **대안**: `make`(Windows 친화성↓, §외부근거 대안1) / `docker compose up` only(5단계 합성 불가, 대안2) | `raw/official-docs/dx-testcontainers-java-best-practices.md#TC-CORE-C1`, `#TC-CORE-C2`, `#TC-CORE-C3` | `official-vendor-doc` (Testcontainers 가 integration test backend 로 적합함만 증명 — 5단계 합성 자체는 ca-tmpl 고유) | **`bootstrap` task 미존재(`planned`)** — 실제 first-run 은 README `cd src && ./gradlew bootRun`. docker-compose 파일 3종 모두 0-byte(빈). Flyway 만 실존. 5단계 합성·smoke 미구현 (§Audit `BOOTSTRAP_TASK_ABSENT`) |
| D4 | README 는 canonical wiki 를 대체하지 않고 local start/smoke/adoption entrypoint 만 제공 | README 는 *진입점*(첫 실행/스모크/채택 절차)만; 개념·계약 설명이 필요하면 canonical wiki 로 링크. **금지 대안**: README 를 별도 SSOT 로 운영(=canonical 과 drift) | (ca-tmpl 고유 운영 규약; 외부 raw claim 없음) | UNSUPPORTED_DECISION | 외부 standard 부재. 실제 README 존재하나 `bootstrap`/smoke section 없음(`bootRun` 만) → `planned` 부분 |
| D5 | OS 매트릭스 = Linux (Ubuntu 22.04+), macOS (Apple Silicon 우선), Windows (WSL2 only). CI 는 Linux, 개발자는 3개 OS 검증 | Linux = CI 필수 게이트; macOS Apple Silicon / Windows WSL2 = 개발자 로컬 검증 의무. **미지원 대안**: native Windows(non-WSL2) | (ca-tmpl 고유 정책; 외부 raw claim 없음) | UNSUPPORTED_DECISION | Apple Silicon arm64 emulation 비용은 Testcontainers 메모(TC raw §메모)에서 경고만 — 정량 근거 없음 |
| D6 | JDK = Temurin 21 LTS. gradle wrapper 핀(전략). `.tool-versions` 또는 `.sdkmanrc` 로 IDE/CLI 핀 | JDK 강제는 *2층*: build 는 Gradle toolchain(`JavaLanguageVersion.of(21)`), IDE/CLI 는 `.tool-versions`/`.sdkmanrc`. 도구(mise/asdf/SDKMAN)는 강제 안 함 — **파일 포맷만** 강제(DX-TV-C5). 단일 포맷 권장(둘 다 두면 drift) | `raw/official-docs/dx-mise-asdf-tool-versioning.md#DX-TV-C4`, `#DX-TV-C5` | `official-vendor-doc` (asdf 의 `.tool-versions` 단일 spec 위치 정의) | **gradle wrapper 실제 = 9.0.0**(노트 "8.x" 와 drift, §Audit `GRADLE_VERSION_DRIFT`). `.tool-versions`/`.sdkmanrc`/`.mise.toml` 미존재(`planned`) — 실존은 build.gradle toolchain 21 뿐. Temurin 21 EOL(`DX-TV-C8`)·mise↔asdf 호환(`DX-TV-C5` "Does not prove")은 `needs-confirmation` |
| D7 | sample profile default = clone 직후 enabled, prod profile disabled | D2 와 동일 정책의 default 표현. enable/disable 코드 owner = `feature-sample-removal-adoption-contract`(`APP_SAMPLE_ENABLED`); 격리 owner = `feature-sample-domain-contract-fixture`(ArchUnit). 본 branch 는 위임 | (sibling branch 와 일관성; 외부 raw claim 없음) | UNSUPPORTED_DECISION (delegated) | 실제 격리는 Spring `@Profile` 이 아니라 ArchUnit `production_code_does_not_depend_on_sample_portfolio` + test-scope (§Audit `SAMPLE_ENABLE_MECHANISM_DRIFT`) |
| D8 | link-rot 검증 도구 — **lychee**(Node-free 단일 바이너리) 조건부 권고, 기존 `markdown-link-check` 대체 후보 | Node toolchain 미도입 유지 → **lychee**(`lycheeverse/lychee-action@v2.0.2+`); Node 도입 시 → linkinator; 기존 Docker-first/MegaLinter → markdown-link-check (2026-06-15 조사) | (조사됨 — §외부근거 2026-06-15 addendum; raw archiving `deferred`, 사용자 승인 대기) | researched, raw 미archiving (이전 `UNSUPPORTED_DECISION`) | lychee-action CVE-2024-48908 → v2.0.2+ pin 필수. Gradle exec task 래핑·로컬 바이너리 프로비저닝 미설계. archiving 전까지 official Claim ID 부재 |
| D9 | devcontainer 를 default 로 두지 않음 (IDE별 개인 설정 out-of-scope) | IDE 통일이 팀 강제이고 VS Code/Codespaces 단일 환경이면 devcontainer 고려; 다IDE/CI 분리 필요하면 default 제외(현 결정). 근거: spec 은 development-phase 한정(DX-DC-C2), VS Code 한정 통합(DX-DC-C4) | `raw/official-docs/dx-devcontainer-spring-boot.md#DX-DC-C2`, `#DX-DC-C4`, `#DX-DC-C5` | `official-standard` + `official-vendor-doc` | devcontainer + ca-tmpl bootstrap 양립 시연 없음 — 채택 시 별도 검증 필요(Claims To Verify 참조) |
| D10 | Testcontainers (`@ServiceConnection`) 를 default integration test backend 로 둠 | Spring Boot 3.1+ integration test backend = Testcontainers; bootstrap 의 *로컬 dependency*`docker compose`(test lifecycle ≠ bootstrap lifecycle, TC raw §메모). reuse 는 로컬 opt-in / CI off(TC-REUSE-C1) | `raw/official-docs/dx-testcontainers-java-best-practices.md#TC-CORE-C1`, `#TC-SPRING-C1`, `#TC-REUSE-C1` | `official-vendor-doc` (core 정의) + `needs-confirmation` (`@ServiceConnection` verbatim·reuse property 명 미확정) | **실제 코드는 `@ServiceConnection` 미사용 — 수동 `@Container PostgreSQLContainer`** (OutboxAppend/OutboxPublisher/DistributedLock contract test). @ServiceConnection 전환은 `planned` (§Audit `SERVICECONNECTION_NOT_USED`). `TC-SPRING-C1`/`TC-REUSE-C1`/`TC-SINGLETON-C1` 모두 `needs-confirmation` |
| D11 | runtime container의 outbox jitter RNG는 `java.base` 구현을 명시 주입 | slim JRE에서도 startup이 필요하면 `SplittableRandom`; provider-specific algorithm이 필수면 runtime module 포함 대안 | 프로젝트 container stack trace + `OutboxConfigTest` RED/GREEN (외부 raw claim 없음) | `UNSUPPORTED_DECISION` | 알고리즘 품질/성능을 외부 공식 source로 재검토하지 않음. trade-off: provider portability를 startup 안정성보다 우선하지 않음 |
| D12 | local PostgreSQL은 host port를 publish하지 않고 Compose internal network에서만 사용 | app container startup Flyway가 migration owner일 때 internal-only; host DB client가 필요하면 별도 override | 프로젝트 `docker compose config` + port collision 재현 (외부 raw claim 없음) | `UNSUPPORTED_DECISION` | host-side DB tool 사용자는 explicit override 필요. trade-off: zero-conflict 기본값과 직접 접속 편의의 교환 |
| D13 | ArchCondition 초기화 시 발생하는 ECJ 자원 누수 경고(Resource leak)를 `@SuppressWarnings("resource")`로 억제 | 항상 — ArchCondition 익명 이너 클래스 정의 시 컴파일러의 오탐지로 인한 경고 해결 | (프로젝트 빌드 경고 해결용; 외부 raw claim 없음) | `UNSUPPORTED_DECISION` | N/A |
| D14 | Spring Boot 3.5.x EOL 경고를 VS Code `settings.json`에서 무시하도록 설정 | 항상 — Testcontainers 2.x 메이저 업그레이드로 인한 패키지 변경 등 파급 효과를 피하기 위해 3.5.16 버전을 유지하고 IDE 경고만 비활성화 | (IDE 문제 경고 해결용; 외부 raw claim 없음) | `UNSUPPORTED_DECISION` | N/A |
## Decisionized Work Items
| item | Decision | Allowed | Forbidden | Required test |
| --- | --- | --- | --- | --- |
| bootstrap | one command: `./gradlew bootstrap` | wrapper around docker compose/test | multiple competing first-run docs | bootstrap smoke |
| `.env.example` | registry-complete safe local values | comments for secret placeholders | prod secrets in example | env example check |
| sample profile | local/dev enabled, prod disabled | education profile | prod sample endpoint | sample profile smoke |
| README/wiki | README entrypoint, wiki canonical | README links canonical | README as separate truth | doc drift check |
> ⚠️ **2026-06-15 정합 주의**: 위 `.env.example` row 는 sibling [[raw/branch-notes/feature-env-driven-runtime-configuration]] D7(2026-06-08 B 결정)이 **`.env.example` 미사용 + `src/.env` git-tracked 단일 소스 + `verifyEnvKeys` 3-way gate** 로 이미 해소함. 본 branch 의 `.env.example` 결정은 *superseded* — DX coverage 상 "env template self-sufficiency" 관심사는 그 sibling 에 **위임**한다(§Coverage). 자동 삭제하지 않고 정합 권고만(§Audit `ENV_EXAMPLE_SUPERSEDED`).
## DX Defaults (deprecated)
> DX 결정 표 SSOT는 위 "Decisionized Work Items". 별도 DX Defaults 양식은 deprecated.
## 구현 가이드
> *결정(Decisions)* 이 "*무엇*" 이라면 본 §는 "*어디에 어떻게*" 의 사전 명세. 각 sub-section 은 Decision ID + Supporting Claim ID 를 Trace. ca-tmpl `src/` 실 코드 대조(2026-06-15)로 reality grade(`actually-implemented`/`planned`/`documented-only`/delegated)를 셀마다 표기 — 노트 자기보고가 아니라 코드 grep 으로 확정.
>
> **3-rule**: R1 모든 detail 은 Decision+근거 도출 · R2 근거 없는 임의 detail 은 `UNSUPPORTED_IMPL_DECISION`+trade-off · R3 본 branch 결정 범위 밖은 위임(§Audit 에 이관 history).
### 1. bootstrap 단일 진입점 — `./gradlew bootstrap` 5단계
> **Trace**: D3 (5단계 정의) / `dx-testcontainers#TC-CORE-C1~C3`. anchor = ca-tmpl `src/build.gradle`(task 미존재) + `README.md` §로컬 실행 + `docker-compose*.yml`(3종) + `src/adapter-persistence/.../db/migration/`.
>
> - **UNSUPPORTED_IMPL_DECISION**: 5단계의 *합성 메커니즘*(단일 Gradle task vs Makefile vs compose wrapper)은 외부 raw 가 권고하지 않음. Gradle task 채택 trade-off: Spring 생태계 정합·step 별 exit code 분리 가능하나 Windows(WSL2 밖) 친화성은 make 보다 낮음(D5 WSL2 강제로 회피).
| 단계 | 구현 anchor (목표) | ca-tmpl 실제 상태 (2026-06-15) | grade |
|---|---|---|---|
| (1) compile sanity | `./gradlew compileTestJava` | 표준 task 존재 | `actually-implemented` |
| (2) local dependency 기동 | `docker compose up -d` (별도 compose file) | `docker-compose.yml`/`.dev.yml`/`.local.yml` 모두 **0-byte(빈)** | `planned` |
| (3) Flyway migrate | `flyway-core` + `db/migration/V*.sql` | V1__idempotency_record / V3__outbox_event / V4__int_lock (+ sample V2__work_log) 실존, `baseline-on-migrate: false` | `actually-implemented` |
| (4) sample profile seed | sample-portfolio seed | sample-portfolio 모듈 실존(test-scope), 런타임 seed/profile 토글은 sibling 위임 | delegated → `feature-sample-removal-adoption-contract` |
| (5) smoke test | `./gradlew ...smoke` 또는 health probe | `smoke`/`Smoke` task·class **미존재**. health endpoint `GET /api/healthcheck` 는 존재 | `planned` |
| 합성: `bootstrap` task | custom Gradle task 가 5단계 묶음 | **`bootstrap` task 미등록** (`app-bootstrap` 은 *모듈*명이지 task 아님). 현 first-run = `cd src && ./gradlew bootRun` | `planned` (§Audit `BOOTSTRAP_TASK_ABSENT`) |
### 2. tool-version 핀 — Temurin 21 LTS + Gradle wrapper
> **Trace**: D6 / `dx-mise-asdf#DX-TV-C4`,`#DX-TV-C5`. anchor = `src/build.gradle` `java { toolchain { languageVersion = JavaLanguageVersion.of(21) } }` + `gradle/wrapper/gradle-wrapper.properties`.
>
> - **UNSUPPORTED_IMPL_DECISION**: `.tool-versions`(asdf/mise) vs `.sdkmanrc`(SDKMAN) *단일 포맷 선택*. 외부 raw 는 포맷 spec 만 정의, 어느 것을 ca-tmpl default 로 둘지는 미권고. trade-off: `.tool-versions` 가 사실상 표준(DX-TV-C5)이고 mise/asdf 양쪽이 읽으나 mise 100% 호환은 `needs-confirmation`; `.sdkmanrc` 는 SDKMAN 단독. → 단일 source 로 `.tool-versions` 권장(둘 다 두면 drift, §외부근거 보강후보).
| 항목 | 구현 anchor (목표) | ca-tmpl 실제 상태 | grade |
|---|---|---|---|
| build JDK 핀 | Gradle toolchain 21 | `JavaLanguageVersion.of(21)` 실존 | `actually-implemented` |
| Gradle 버전 핀 | repo wrapper 로 고정 | wrapper **9.0.0** (노트 "8.x" 와 drift) | `actually-implemented` (버전 숫자 정정 필요, §Audit `GRADLE_VERSION_DRIFT`) |
| IDE/CLI JDK 핀 | `.tool-versions` 단일 포맷 | `.tool-versions`/`.sdkmanrc`/`.mise.toml` **미존재** | `planned` |
| Temurin 21 LTS EOL 명시 | Adoptium support 페이지 인용 | `DX-TV-C8` `needs-confirmation` (별도 fetch 필요) | `planned` |
### 3. integration test backend — Testcontainers
> **Trace**: D10 / `dx-testcontainers#TC-CORE-C1`,`#TC-SPRING-C1`,`#TC-REUSE-C1`. anchor = ca-tmpl `src/app-bootstrap/.../contract/outbox/OutboxAppendTransactionalContractTest.java` 등.
>
> - **UNSUPPORTED_IMPL_DECISION**: container 공유 전략(`@ServiceConnection` vs 수동 `@Container` singleton). 외부 raw 의 `@ServiceConnection`(`TC-SPRING-C1`)·singleton(`TC-SINGLETON-C1`) 인용이 `needs-confirmation` 이라 verbatim 미확정. trade-off: 실제 코드는 수동 `@Container PostgreSQLContainer` 채택(boilerplate 더 많으나 명시적). @ServiceConnection 전환은 Spring Boot reference 재fetch 후 별도.
| 항목 | 구현 anchor (목표) | ca-tmpl 실제 상태 | grade |
|---|---|---|---|
| integration backend | Testcontainers | `@Testcontainers`+`@Container PostgreSQLContainer` (Outbox/DistributedLock contract test) 실존 | `actually-implemented` (수동 방식) |
| boilerplate 제거 | `@ServiceConnection` (Spring Boot 3.1+) | `@ServiceConnection` **미사용** | `planned` (§Audit `SERVICECONNECTION_NOT_USED`) |
| reuse 정책 | 로컬 opt-in / CI off | `.testcontainers.properties`·`testcontainers.reuse.enable` **미존재** | `planned` |
| bootstrap vs test 분리 | docker compose(bootstrap) ↔ Testcontainers(test) 별도 명시 | compose 파일 빈 상태 → bootstrap 측 미구현 | `planned` |
### 4. README entrypoint + link-rot gate
> **Trace**: D4(README 진입점) + D8(link-rot 도구) / D8 은 §외부근거 2026-06-15 addendum. anchor = ca-tmpl `README.md`(§로컬 실행/§테스트/§환경 변수 규칙) + (link-rot config 미존재).
>
> - **UNSUPPORTED_IMPL_DECISION**: link-rot 도구 선택(lychee vs markdown-link-check vs linkinator). 2026-06-15 조사로 lychee 조건부 권고하나 raw archiving `deferred`(승인 대기). README↔command drift 검사 메커니즘(`verifyReadmeCommands` Gradle task)은 본 branch 임의 설계 — trade-off: 자동 강제 가능하나 ```bash 블록 파싱 규칙은 ca-tmpl 고유.
| 항목 | 구현 anchor (목표) | ca-tmpl 실제 상태 | grade |
|---|---|---|---|
| README local entrypoint | §로컬 실행 (첫 실행 명령) | README 실존, `cd src && ./gradlew bootRun` + `GET /api/healthcheck` | `actually-implemented` (단 `bootstrap`/smoke 미반영) |
| README↔command drift 검사 | Gradle `verifyReadmeCommands` | **미존재** | `planned` |
| link-rot gate | lychee(`lycheeverse/lychee-action@v2.0.2+`) CI 게이트 | config·CI·`package.json` **모두 미존재** | `planned` |
### 5. 위임 관심사 (OUT_OF_BRANCH_SCOPE → 다른 owner)
> 본 branch DX 진입점 밖이지만 governing DX 관심사인 것 — 결정 영역이 sibling owner 에 있으므로 §구현 가이드에 detail 을 남기지 않고 위임(R3). 위임 history 는 §Audit & Findings.
| 위임 관심사 | owner branch | 실제 메커니즘 (ca-tmpl) |
|---|---|---|
| `.env.example` / env key self-sufficiency | [[raw/branch-notes/feature-env-driven-runtime-configuration]] D7 | `src/.env`(git-tracked) + `verifyEnvKeys` 3-way + `env-keys.yaml` SSOT. `.env.example` **미사용** |
| sample enable/disable 런타임 토글 | `feature-sample-removal-adoption-contract` | env `APP_SAMPLE_ENABLED`(registry-backed) + `prod_profile_must_be_false` + sample-off smoke. 런타임 토글 *코드 메커니즘*은 owner 확정 대상(미구현) — 본 branch 가 단정 안 함 |
| sample production 격리 | [[raw/branch-notes/feature-sample-domain-contract-fixture]] D5 | ArchUnit `production_code_does_not_depend_on_sample_portfolio` + test-scope(`actually-implemented`) |
| fresh-clone smoke CI job | `feature-ci-quality-gates-contract` | CI 미존재 — `fresh-clone-smoke` job `planned` |
## 엣지·실패·의존
> R4(깊이 게이트) 캡처용. 정상 경로 외 *구현 중 부딪힐* 실패/엣지/다른 계약 의존.
- **실패·엣지 경로**:
- bootstrap (2) `docker compose up -d` — Docker daemon 미기동 시 즉시 fail-fast + 안내(현 compose 파일 빈 상태이므로 step 2 자체 미정의). 기대: exit ≠ 0 + "Docker 필요" 메시지.
- bootstrap (3) Flyway — out-of-order migration / checksum mismatch 시 fail. `baseline-on-migrate: false` 이므로 빈 DB 가정; 기존 스키마 존재 시 baseline 충돌.
- **Apple Silicon (arm64) emulation** — 일부 Testcontainers image 가 amd64-only 면 emulation → bootstrap 시간 증가(D5 "macOS Apple Silicon 우선" 과 충돌 가능, TC raw §메모 경고만, 정량 근거 없음 → Claims To Verify).
- link-rot false-positive — GitHub/LinkedIn 등 bot-blocker 429/999, Obsidian `[[wikilink]]` 는 표준 URL 아님 → 세 도구 모두 미검출. lychee `accept`/`.lycheeignore` 로 제어, wikilink 는 별도 처리 필요.
- tool-version 불일치 — `.tool-versions` 핀과 CI runner/로컬 JDK 가 다르면 reproducible build 깨짐(D6; ci-quality-gates 와 공유).
- smoke test green ≠ 정상 — 5단계 중 어디서 실패했는지 step 별 exit code 분리 필요(wiki/projects DevOps 문서 "과장 금지" 항목).
- **다른 계약 의존**:
- `[[raw/branch-notes/feature-env-driven-runtime-configuration]]` D7 — `.env`/env-keys SSOT(`verifyEnvKeys`). 이 계약이 `.env.example` 부재를 확정하므로 본 branch 의 env template 관심사는 그쪽 결과를 consume. 그 계약이 바뀌면 본 branch bootstrap step 0(env 준비) 영향.
- `[[raw/branch-notes/feature-sample-removal-adoption-contract]]``APP_SAMPLE_ENABLED` 런타임 토글. bootstrap (4) sample seed 가 이 flag 를 consume.
- `[[raw/branch-notes/feature-sample-domain-contract-fixture]]` D5 — sample-portfolio 격리(ArchUnit). bootstrap 이 sample 을 켜도 prod 경로 침범 없음의 근거.
- `[[raw/branch-notes/feature-ci-quality-gates-contract]]``fresh-clone-smoke` job + Testcontainers reuse CI off 정책. 본 branch 의 테스트 계약(fresh-clone-smoke)이 그 CI gate 에서 실행됨.
- `[[raw/branch-notes/feature-test-taxonomy-fixture-contract]]` — integration test taxonomy 가 Testcontainers 를 default backend 로 둠(D10 과 공유). @ServiceConnection 전환 결정의 공동 영역.
- `[[raw/branch-notes/feature-container-runtime-contract]]` — container JVM/healthcheck/graceful-shutdown 기준. bootstrap 이 띄우는 런타임의 health probe(`/api/healthcheck`)는 그 계약과 정합.
## 테스트 계약
- .env.example 자급자족 검사: clean clone 직후 `cp .env.example .env && ./gradlew bootstrap`만으로 5단계 sub-task(compileTestJava → docker compose up -d → Flyway migrate → sample profile seed → smoke test)가 모두 통과해야 함. 측정 방법: CI에 `fresh-clone-smoke` job 추가 — clean container에서 위 명령 시퀀스 실행 후 exit code 0 + smoke test green. 추가 prompt/수동 입력이 필요하면 fail. **⚠️ 2026-06-15 정합**: sibling `feature-env-driven-runtime-configuration` B 결정으로 `.env.example` 대신 `src/.env`(git-tracked) 사용 → 본 검사의 `cp .env.example .env` 전제는 `src/.env` 기준으로 갱신 필요(§Audit `ENV_EXAMPLE_SUPERSEDED`).
- sample profile이 prod profile에서 켜지면 실패.
- README ↔ 실 command drift 검사: README.md의 code block에 등장하는 모든 `./gradlew`, `docker compose`, `make` command가 실제 build script에 존재해야 함. 측정 방법: `markdown-link-check` + 자체 Gradle task `verifyReadmeCommands`. README parsing: ```bash 블록에서 command 추출 → 각 command의 첫 token이 build script에 정의된 task이거나 system 표준 도구(`docker`, `git` 등)여야 함. 미정의 command 1건이라도 있으면 fail.
- bootstrap command가 하나로 고정되지 않으면 실패.
## 검증해야 할 주장
> 공식 문서 / 사례는 근거지만 ca-tmpl 프로젝트에서의 동작을 자동 보장하지 않음. 구현 전/중/후 실제 검증 대상.
| Claim | Why uncertain | How to verify | Status |
|---|---|---|---|
| `./gradlew bootstrap` 5단계가 clean clone 직후 추가 prompt 없이 모두 통과한다 | bootstrap task 자체가 ca-tmpl 고유 합성 — 외부 raw 가 5단계 합성을 보장하지 않음 (Testcontainers core claim 은 test backend 적합성만 증명) | CI `fresh-clone-smoke` job: clean container 에서 `cp .env.example .env && ./gradlew bootstrap` 실행 → exit code 0 + smoke test green | `planned` |
| README ↔ build script command drift 가 0 건 | README 의 code block 과 실제 task 정의 일치는 자동 보장되지 않음 | Gradle task `verifyReadmeCommands` — README 의 ```bash 블록에서 command 추출 → 첫 token 이 build script task 또는 system 표준 도구인지 검사. 미정의 1건이라도 fail | `planned` |
| `@ServiceConnection` 이 ca-tmpl 의 모든 dependency (PostgreSQL / Redis / Kafka 등) 에 대해 boilerplate 제거를 보장 | `TC-SPRING-C1` 이 `needs-confirmation` — 지원 module 범위 미확정 | Spring Boot reference docs 재fetch 로 supported module list 확보 → ca-tmpl dependency 목록과 교차 | `needs-confirmation` |
| Testcontainers reuse 가 로컬에서 의도된 startup 단축 효과를 내고 CI 에서는 비활성화된다 | `TC-REUSE-C1` property 명과 "must not be enabled in CI" 표현이 `needs-confirmation` | Testcontainers reuse docs (https://java.testcontainers.org/features/reuse/) 재fetch + 로컬 측정 (cold start vs reused) + CI yaml 에서 reuse 플래그 부재 확인 | `needs-confirmation` |
| Temurin 21 LTS 의 EOL 일자가 ca-tmpl 채택 주기 (≥ 36 개월) 와 호환 | `DX-TV-C8` 이 `needs-confirmation` — Adoptium support 페이지 인용 미확보 | https://adoptium.net/support/ 재fetch 로 정확한 EOL 일자 확정 후 branch note 갱신 | `needs-confirmation` |
| mise 와 asdf 가 동일한 `.tool-versions` 파일을 100% 호환 해석 | `DX-TV-C5` "Does not prove" 컬럼에서 명시적으로 보장 안 됨 | mise 공식 페이지 (`.tool-versions` 호환성 섹션) 재fetch + 두 도구로 동일 파일 read/install 시연 | `needs-confirmation` |
| `.sdkmanrc` 와 `.tool-versions` 가 동시 존재할 때 drift 가 발생하지 않는다 (또는 단일 source 정책 채택) | `DX-TV-C7` 이 `needs-confirmation` — SDKMAN `.sdkmanrc` 포맷 verbatim 미확보 | SDKMAN docs (https://sdkman.io/usage#env) fetch → ca-tmpl 정책을 "또는" 에서 단일 source 로 좁힐지 결정 | `planned` |
| Apple Silicon (arm64) 에서 Testcontainers image 의 emulation 비용이 bootstrap 시간 (목표 1단계 분 이내) 을 초과하지 않는다 | Testcontainers raw 메모에서 경고만 됨 — 정량 근거 없음 | M1/M2 환경에서 bootstrap 측정 + arm64 native image 가용 여부 module 별 점검 | `planned` |
| devcontainer 채택 시 ca-tmpl `./gradlew bootstrap` 5단계가 devcontainer 안에서 동등 동작 | `DX-DC-C5` 가 tool/runtime stack 만 보장 — Flyway 순서 / smoke test 자동 보장 안 함 | `.devcontainer/devcontainer.json` 작성 → Codespaces + 로컬 VS Code 양쪽에서 bootstrap 실행 → exit code 비교 | `planned` |
| markdown-link-check 가 README + docs/ 의 모든 wikilink + URL 을 false-positive 없이 검출 | 도구 선택 자체에 외부 spec 미수집 (D8 — 2026-06-15 lychee 권고로 재검토) | npm 패키지 reference 확인 + CI 에서 dry-run → false-positive 목록 수집 후 ignore pattern 확정 | `planned` |
| lychee 가 ca-tmpl 의 README + docs/ relative file link + external URL 을 false-positive 없이 검출하고 CI 에서 broken link 시 exit ≠ 0 | 2026-06-15 조사로 권고됐으나 ca-tmpl 실 파일 dry-run 미실시 + lychee-action CVE pin 필요 | `lychee --root-dir . './docs/**/*.md' './README.md'` dry-run → `.lycheeignore` 수렴 → `.github/workflows/link-check.yml`(`lycheeverse/lychee-action@v2.0.2+`, `fail: true`) 에 broken link 인위 삽입 → exit ≠ 0 확인 | `planned` |
## 관심사 커버리지
> `/coverage` 가 채우는 **생성물** — 손유지 금지. governing 문서(`wiki/projects/ca-tmpl/devops-ci-supply-chain-dx` §DX + parent §18 Developer Experience)가 요구하는 DX 관심사를 본 branch 가 빠짐없이 덮는지. 기준: `rules/coverage-gate.md`. 아래는 `/branch-spec` 가 staged 한 seed — `coverage-auditor` 가 코드/선례 대조로 확정.
| 관심사 (governing) | 상태 | owner | 심각도 | 근거 |
|--------|------|-------|--------|------|
| local bootstrap command (단일 진입점) | covered-here | — | — | D3 (`planned` — `bootstrap` task 미존재) |
| `.env.example` / env template self-sufficiency | delegated | [[raw/branch-notes/feature-env-driven-runtime-configuration]] | OK | D7(sibling) — `src/.env`+`verifyEnvKeys`, §Audit `ENV_EXAMPLE_SUPERSEDED` |
| Testcontainers 또는 local dependency 대체 | covered-here | — | — | D10 (manual `@Container` 실존, `@ServiceConnection` planned) |
| smoke test command | covered-here | — | — | D3 step5 + 테스트 계약 (`planned`) |
| sample profile 실행/비활성화 | delegated | [[raw/branch-notes/feature-sample-removal-adoption-contract]] | OK | D2/D7 위임, §Audit `SAMPLE_ENABLE_MECHANISM_DRIFT` |
| README/runbook link 기준 | covered-here | — | — | D4 + D8 link-rot (`planned`) |
| tool version pinning (Temurin 21 LTS) | covered-here | — | — | D6 (toolchain 21 실존, `.tool-versions` planned) |
| link-rot / dead-link check | covered-here | — | — | D8 lychee 권고 (raw archiving deferred) |
| fresh-clone smoke CI job | delegated | [[raw/branch-notes/feature-ci-quality-gates-contract]] | OK | §엣지·실패·의존 다른 계약 의존 |
## Audit & Findings
> 2026-06-15 `/branch-spec` ca-tmpl `src/` 코드 대조 결과. 사용자 작성 결정 영역이므로 자동 rewrite 하지 않고 **정합 권고만** 기록(추측 단정 금지).
| Finding ID | 유형 | 내용 | 권고 |
|---|---|---|---|
| `BOOTSTRAP_TASK_ABSENT` | planned (drift 아님) | `./gradlew bootstrap` task 미등록(`app-bootstrap` 은 모듈명). 현 first-run = `cd src && ./gradlew bootRun`. docker-compose 3종 0-byte. | D3 를 `planned` 로 명시(완료). Phase C2 구현 시 custom task + step exit code 분리. |
| `GRADLE_VERSION_DRIFT` | drift | 노트 D6 "gradle-wrapper 8.x" vs 실제 `gradle-wrapper.properties` **9.0.0**. | 결정 사항 2026-06-15 정합 라인 + D6 Open Risk 반영(완료). 버전 숫자만 9.0.0 으로 정정, 핀 전략 유효. |
| `ENV_EXAMPLE_SUPERSEDED` | drift (superseded by sibling) | 노트의 `.env.example` 결정(Decisionized Work Items + 테스트 계약)이 [[raw/branch-notes/feature-env-driven-runtime-configuration]] D7(2026-06-08 B: `.env.example` 미사용, `src/.env`+`verifyEnvKeys`+`env-keys.yaml` SSOT)과 충돌. | env template self-sufficiency 관심사를 그 sibling 에 **위임**(§Coverage). 테스트 계약의 `cp .env.example .env` 를 `src/.env` 기준으로 갱신 권고(완료). |
| `SAMPLE_ENABLE_MECHANISM_DRIFT` | drift | 노트 D2/D7 이 Spring `@Profile` enablement 가정. 실제 = ArchUnit `production_code_does_not_depend_on_sample_portfolio` + test-scope(격리, [[raw/branch-notes/feature-sample-domain-contract-fixture]] D5) + env `APP_SAMPLE_ENABLED`(런타임 토글, `feature-sample-removal-adoption-contract` owner; registry-backed, 토글 코드 메커니즘 미구현). | enable/disable·격리 모두 sibling 위임으로 표기(완료). `@Profile` 표현은 sibling 결정으로 대체. 토글 코드 메커니즘은 owner 가 확정(본 branch 단정 안 함). |
| `SERVICECONNECTION_NOT_USED` | planned (drift) | 노트 D10 "@ServiceConnection default" vs 실제 수동 `@Container PostgreSQLContainer`(Outbox/DistributedLock contract test). | D10 reality grade `planned`(완료). @ServiceConnection 전환은 `TC-SPRING-C1` 재fetch 후 별도(Claims To Verify). |
### 2026-06-24 구현 판정
| Finding ID | 결과 | 증거 등급 | 남은 경계 |
|---|---|---|---|
| `BOOTSTRAP_TASK_ABSENT` | `bootstrapCompile` → `bootstrapDependencies` → `bootstrapMigrateAndStart` → `bootstrapSampleContract` → `bootstrapSmoke` 구현 | `locally-verified` | macOS/WSL2 clean clone 미검증 |
| `GRADLE_VERSION_DRIFT` | wrapper 9.0.0 유지, `.tool-versions` Temurin 21.0.11+10 소비 | `actually-implemented` | tool manager별 해석은 미검증 |
| `ENV_EXAMPLE_SUPERSEDED` | `src/.env`를 Compose `env_file`로 소비, 새 `.env.example` 미생성 | `locally-verified` | sibling owner 유지 |
| `SERVICECONNECTION_NOT_USED` | Spring context/slice 2개는 `@ServiceConnection`; direct JDBC/SQLState tests는 명시적 container factory 유지 | `locally-verified` | 공식 지원 범위 source refetch 미완료 |
| `LINK_ROT_GATE_ABSENT` | `lycheeverse/lychee-action@v2.0.2`, `fail: true` workflow 추가 | `actually-implemented` | remote workflow 실행은 `needs-confirmation` |
## 완료 후 wiki 추출 대상
- `wiki/projects/ca-skeleton-operational-contract.md`의 developer experience canonical section.
- `wiki/projects/ca-tmpl/devops-ci-supply-chain-dx.md` 의 DX 슬라이스 (governing doc).
## 마주친 문제
- [[raw/errors/bootstrap-postgres-port-collision-2026-06-24]] — 불필요한 DB host port publish가 기존 5432 container와 충돌; internal-only network로 해결.
- [[raw/errors/slim-jre-random-generator-missing-2026-06-24]] — full JDK test에서 보이지 않던 slim JRE RNG provider 차이; `java.base` RNG bean과 container smoke로 해결.
- 공식 Spring/Testcontainers 문서 web fetch는 403으로 차단됐다. D10의 최신 공식 지원 범위는 `needs-confirmation`을 유지한다.
## 묶음
<!-- GENERATED: branches:start -->
- [[raw/branch-notes/chore-harness-policy-engine-alignment]]
<!-- GENERATED: branches:end -->
### Sub-branches
- [[raw/branch-notes/chore-harness-policy-engine-alignment]] — module registry, strict evidence, platform renderer, risk-profile 기반 개발 하네스 정합.
<!-- GENERATED: sources:start -->
- [[raw/official-docs/dx-devcontainer-spring-boot]]
- [[raw/official-docs/dx-mise-asdf-tool-versioning]]
- [[raw/official-docs/dx-testcontainers-java-best-practices]]
- [[raw/official-docs/supply-chain-gradle-vs-maven-dependency-locking]]
<!-- GENERATED: sources:end -->
<!-- GENERATED: interviews:start -->
- [[raw/interviews/single-command-local-bootstrap]]
<!-- GENERATED: interviews:end -->
<!-- GENERATED: errors:start -->
- [[raw/errors/bootstrap-postgres-port-collision-2026-06-24]]
- [[raw/errors/jpa-repository-scan-miss-multimodule-2026-06-10]]
- [[raw/errors/slim-jre-random-generator-missing-2026-06-24]]
<!-- GENERATED: errors:end -->
<!-- GENERATED: daily-notes:start -->
- [[raw/daily-notes/2026-06-30]]
<!-- GENERATED: daily-notes:end -->
<!-- GENERATED: blog-topics:start -->
- [[raw/blog-topics/five-stage-local-bootstrap-contract-2026-06-24]]
<!-- GENERATED: blog-topics:end -->
> 본 feature branch 는 개발 하네스 정합 child를 소유한다. 추가 child/derived 자료는 이 섹션에서 그룹화한다.
### 오류 기록 (본 feature 작업 중 발생)
- [[raw/errors/bootstrap-postgres-port-collision-2026-06-24]] — host 5432 충돌과 internal-only DB network 결정.
- [[raw/errors/slim-jre-random-generator-missing-2026-06-24]] — slim JRE provider parity 오류와 `java.base` RNG 수정.
### 면접 준비 (이 작업에서 나올 수 있는 면접 질문)
- [[raw/interviews/single-command-local-bootstrap]] — 단일 bootstrap의 단계 분리·실패 계약·문서 drift 질문.
### 블로그·채용공고 연계 글감
- [[raw/blog-topics/five-stage-local-bootstrap-contract-2026-06-24]] — 5단계 bootstrap과 실제로 잡힌 runtime gap 글감.
- Job posting: 없음 — 채용공고에서 파생된 작업이 아님.
- derived blog: 생성 전. canonical 추출 요청이 없어 직접 생성하지 않음.
## 관련 일일 노트
> 이 브랜치를 작업한 날짜들. 양방향 nav 유지.
- [[raw/daily-notes/2026-06-30]]
- (생성 2026-05-22 / branch-spec 2026-06-15 — 해당 daily-note 미연결. 작업 재개 시 `[[raw/daily-notes/YYYY-MM-DD]]` 추가)
## 완료 후 정리
> 머지/종료 시점에 채움.
- PR 링크:
- 리뷰 메모:
- 머지 결과 / 배포 환경:
- **wiki 추출 대상** (verified만, `wiki/projects/`로만 추출):
- `actually-implemented` 항목:
- `locally-verified` 항목:
- `prod-verified` 항목:
- **추출하지 않을 항목** (planned / documented-only / abandoned):