Files
llm-wiki/raw/branch-notes/experiment-nplus1-feed-api-replay.md
T

308 lines
22 KiB
Markdown

---
title: branch / experiment-nplus1-feed-api-replay (11-stage real DB and HTTP replay)
source_type: branch-note
status: raw
id: BR-NPLUS1-PRESENTATION-PREP-002
kind: project-work-item
project: nplus1-presentation-prep
work_item: WI-NPLUS1-PRESENTATION-PREP-002
inherits:
- DEC-NPLUS1-PRESENTATION-PREP-LEARNING-001@1
- DEC-NPLUS1-PRESENTATION-PREP-SUBSTRATE-001@1
- DEC-NPLUS1-PRESENTATION-PREP-EVIDENCE-001@1
- DEC-NPLUS1-PRESENTATION-PREP-SCOPE-001@1
refines: []
overrides: []
depends_on: [WI-NPLUS1-PRESENTATION-PREP-001]
contract_packet: 1
branch: experiment-nplus1-feed-api-replay
git_branch: lab/nplus1-api-replay
parent_branch:
related_projects: [nplus1-presentation-prep, ca-skeleton]
tags: [branch, nplus1-presentation-prep, persistence, testing, api-design, postgresql, hands-on-lab]
created: 2026-07-15
target_merge:
status_label: review
evidence_grade: locally-verified
contract_packet_sha256: 88f5e6fd5ec219c6017f8213d076cde1803e63e62f978fe926f86ddcbc616f41
---
# branch: experiment-nplus1-feed-api-replay
> Layer: `raw/branch-notes/` — 기존 [[raw/branch-notes/experiment-nplus1-highlight-feed]]의 L1~L16/Crown/L12 결과를, 실제 PostgreSQL과 HTTP로 한 단계씩 재현할 수 있게 만든 11-checkpoint replay 브랜치다.
> 실제 Git branch는 `lab/nplus1-api-replay`다. wiki slug는 파일명 규칙에 맞춘 별도 식별자다.
<!-- section-id: branch-parent -->
## 부모 (필수)
- [[raw/project-notes/nplus1-presentation-prep]]
- 관련 선행 작업: [[raw/branch-notes/experiment-nplus1-highlight-feed]] — 각 랩의 원래 문제·측정·해법 사슬을 소유한다. 이 노트는 그 결과를 checkout 가능한 API/DB 학습 경로로 만드는 작업만 소유한다.
<!-- GENERATED: branch-contract:start -->
<!-- section-id: branch-contract-packet -->
## 브랜치 계약 패킷
- **생성 시 프로젝트 개정**: `1`
- **패킷 스키마**: `contract_packet: 1`
- **완료 조건**: 11개 replay tag와 guide mapping을 고정하고, clean clone/worktree에서 Compose·HTTP·PostgreSQL smoke 및 full-check 상태를 재검증하며 lab profile의 deployment 비활성 증거를 기록한다.
<!-- section-id: inherited-project-decisions -->
### 상속한 프로젝트 결정
| Decision Ref | Project Summary | Branch Application | Source |
|---|---|---|---|
| `DEC-NPLUS1-PRESENTATION-PREP-LEARNING-001@1` | Measure→Break→Diagnose→Fix→Re-measure→Generalize를 랩 완료 루프로 사용한다. | 11개 checkout point마다 동일한 reset→HTTP→DB 관찰 절차를 제공한다. | [[raw/project-notes/nplus1-presentation-prep]] |
| `DEC-NPLUS1-PRESENTATION-PREP-SUBSTRATE-001@1` | ca-tmpl production substrate를 사용하고 학습 API·측정 경로는 profile과 sibling 경로로 격리한다. | `/api/lab/**`와 marker-owned fixture를 `lab` profile에 한정한다. | [[raw/project-notes/nplus1-presentation-prep]] |
| `DEC-NPLUS1-PRESENTATION-PREP-EVIDENCE-001@1` | local·Testcontainers 결과는 locally-verified로만 기록하고 prod evidence로 승격하지 않는다. | Compose/Testcontainers 결과를 `locally-verified`로만 기록한다. | [[raw/project-notes/nplus1-presentation-prep]] |
| `DEC-NPLUS1-PRESENTATION-PREP-SCOPE-001@1` | same-store CQRS-lite까지를 현재 범위로 두고 full CQRS는 ca-tmpl contract escalation 이후에만 허용한다. | Crown 1-query와 L12 2-query endpoint를 병존시킨다. | [[raw/project-notes/nplus1-presentation-prep]] |
<!-- section-id: branch-local-decisions -->
### 브랜치 지역 결정
기존 branch-local 결정은 아래 `## Decision Evidence Map`의 D1~D5가 소유한다.
<!-- section-id: declared-overrides -->
### 선언한 예외
- 없음.
<!-- GENERATED: branch-contract:end -->
<!-- section-id: branch-goal -->
## 목표
마지막 Crown/L12 상태만 남은 작업 트리에서는 L1의 컬렉션 N+1부터 Crown의 통합 쿼리까지를 HTTP와 실제 DB로 순서대로 관찰하기 어렵다. 이를 11개의 독립 checkout point로 고정한다.
- 각 tag에서 Docker PostgreSQL을 띄우고 lab fixture를 reset한 뒤 API 응답과 DB row를 직접 확인한다.
- 정상 `/api/feed` 동작은 바꾸지 않고, `lab` profile에서만 학습용 `/api/lab/**` 경로를 제공한다.
- Crown의 1-query read와 L12의 same-store CQRS-lite 2-query read를 같은 것으로 포장하지 않고, 별도 endpoint와 문서로 비교 가능하게 둔다.
- 이슈: 사용자 요청 — N+1 랩을 실 API/DB로 단계별 학습
- PR: 없음 (로컬 replay branch)
<!-- section-id: branch-scope -->
## 범위
### 포함 범위
- L1, L2, L3, L4, L5, L6, L14, L15, L16, Crown, L12 순서의 정확히 11개 commit/tag.
- lab profile, marker-safe fixture, HTTP 관찰 endpoint, PostgreSQL 확인 절차와 단계별 가이드.
- Crown 및 L12의 실제 PostgreSQL/Testcontainers 검증과 이력 tag의 L1 smoke 검증.
### 제외 범위
- `/api/feed`의 production 계약 또는 기본 보안 정책 변경.
- L12를 별도 read store·outbox 동기화가 있는 Full CQRS로 확장.
- 프로덕션 배포, 부하/latency SLA, 성능 수치의 운영 일반화.
## 근거
| Source | 정당화하는 결정 |
|---|---|
| [[raw/official-docs/cqrs-pattern-azure-architecture-center]] | D3: 같은 저장소에서 read/write 논리를 분리하는 CQRS-lite와 별도 저장소 CQRS를 구분한다. |
| [[raw/official-docs/spring-data-jpa-projections-spring-official]] | D3: application 반환용 DTO/projection을 통해 aggregate hydration과 read shape를 분리하는 선택지를 뒷받침한다. |
| [[raw/official-docs/test-taxonomy-testcontainers-official]] | D4: in-memory 대체물이 아닌 Docker의 실제 PostgreSQL로 integration evidence를 얻는 선택을 뒷받침한다. |
## TODO
- [x] 11개 replay commit/tag를 사용자 지정 학습 순서로 고정 — 등급: `actually-implemented`
- [x] `lab` profile에서 실제 DB reset 및 HTTP feed 관찰 경로 제공 — 등급: `actually-implemented`
- [x] Crown/L12 최신 상태의 Docker HTTP + PostgreSQL smoke 수행 — 등급: `locally-verified`
- [x] L1 historical tag의 독립 Docker HTTP smoke 수행 — 등급: `locally-verified`
- [x] 누적 focused Gradle suite 및 dependency/public-path/env verifier 수행 — 등급: `locally-verified`
- [ ] 전체 `./gradlew check`를 branch 변경과 무관한 base architecture failure 없이 통과 — 등급: `needs-confirmation` (아래 §검증 기록 참조)
## 진행 중 메모
- application repository의 replay branch는 `54cf7e7` / `nplus1-replay-l12`까지 tag가 완료된 상태다.
- full `check`의 유일한 base failure는 별도 수정 범위로 남겼다. 이 노트의 evidence grade는 local Docker/Gradle 검증까지만 나타낸다.
## 결정 사항
- 2026-07-15 D1: 학습 순서를 원래 구현 시간순이 아니라 `L1 → L2 → L3 → L4 → L5 → L6 → L14 → L15 → L16 → Crown → L12`의 11개 tag로 고정한다. / 이유: 사용자가 각 commit으로 이동해 API/DB를 직접 관찰해야 한다. / 검토한 대안: 마지막 코드 하나와 문서만 제공. / 근거: 사용자 요구; 구체 tag 순서는 외부 자료가 정하지 않으므로 `UNSUPPORTED_IMPL_DECISION`.
- 2026-07-15 D2: 공개 학습 reset은 `lab` profile의 `/api/lab/**`에만 두고, anonymous access는 `lab:reset` 하나에만 허용한다. / 이유: 실제 HTTP 재현은 가능해야 하지만 정상 profile의 feed/권한 정책을 약화하면 안 된다. / 검토한 대안: `/api/feed`에 reset/debug 파라미터 추가 또는 lab profile 전체 anonymous 허용. / 근거: D4 및 사용자 범위; profile/permission의 구체 모양은 `UNSUPPORTED_IMPL_DECISION`.
- 2026-07-15 D3: Crown은 1 native query API, L12는 same-store CQRS-lite 2-query read port API로 병존시킨다. / 이유: 쿼리 수 최소화와 application read-model 분리는 서로 다른 선택지다. / 검토한 대안: L12가 Crown endpoint를 조용히 대체. / 근거: `AZURE-CQRS-C2`, `AZURE-CQRS-C3`, `SPRING-PROJ-C4`.
- 2026-07-15 D4: Testcontainers 검증에 더해 fresh Docker Compose PostgreSQL에서 HTTP response와 SQL row count를 확인한다. / 이유: test-only assertion으로는 사용자가 직접 API/DB를 따라 보는 목표를 충족하지 못한다. / 검토한 대안: integration test 결과만 보관. / 근거: `TC-OFFICIAL-C1`, `TC-OFFICIAL-C3`, `TC-OFFICIAL-C5`.
- 2026-07-15 D5: Hibernate `addScalar`를 Java SQL compile-time checker로 설명하지 않는다. / 이유: 이는 native-query result extraction의 runtime type mapping이며 SQL 문법/컬럼 존재성은 실행 시점에 검증된다. / 검토한 대안: `addScalar`가 SQL 안전성을 보장한다고 문서화. / 근거: 구현 관찰; `UNSUPPORTED_IMPL_DECISION`.
## 결정-근거 매핑
| Decision ID | Decision | 선택 조건 (언제 이 결정 / 언제 대안) | Supporting Claims | Evidence Strength | Open Risk |
|---|---|---|---|---|---|
| D1 | 11개의 checkout 가능한 learning checkpoint | 사용자가 단계별 API/DB 관찰을 원할 때; 단일 현재 상태만 필요하면 하나의 branch 상태로 충분 | User request; `UNSUPPORTED_IMPL_DECISION` | user-scoped requirement | history를 rewrite하면 tag/문서 매핑도 함께 갱신해야 함 |
| D2 | lab-only reset/API/anonymous boundary | 로컬 학습 profile일 때만; 정상 runtime에서는 lab bean/route 자체를 등록하지 않음 | `TC-OFFICIAL-C1`, `TC-OFFICIAL-C3`; `UNSUPPORTED_IMPL_DECISION` | official-vendor-doc + unit/HTTP local verification | lab profile을 production에 실수로 활성화하지 않는 운영 절차는 별도 확인 필요 |
| D3 | Crown 1-query와 L12 2-query CQRS-lite 병존 | endpoint-specific optimization을 비교할 때; 별도 store가 필요하면 Full CQRS contract를 별도 설계 | `raw/official-docs/cqrs-pattern-azure-architecture-center.md#AZURE-CQRS-C2`, `#AZURE-CQRS-C3`, `raw/official-docs/spring-data-jpa-projections-spring-official.md#SPRING-PROJ-C4` | official-vendor-doc | L12은 keyset/visibility를 Crown처럼 모두 포함하지 않음 |
| D4 | real PostgreSQL HTTP+DB smoke | SQL dialect, container wiring, public response shape를 함께 확인할 때 | `raw/official-docs/test-taxonomy-testcontainers-official.md#TC-OFFICIAL-C1`, `#TC-OFFICIAL-C3`, `#TC-OFFICIAL-C5` | official-vendor-doc + local runtime evidence | production traffic/permissions을 검증한 것은 아님 |
| D5 | `addScalar` compile-time 보장 부정 | native query mapping 설명 시 항상 적용 | Local code/runtime behavior; `UNSUPPORTED_IMPL_DECISION` | locally verified implementation fact | native SQL의 syntax/plan error는 CI compile이 아니라 query execution에서 발견됨 |
## 구현 가이드
### 1. 고정 replay checkpoint
> **Trace**: D1. 단계 순서와 tag 이름은 사용자 학습 요구에서 정한 `UNSUPPORTED_IMPL_DECISION`이다. 각 checkpoint의 원래 N+1 원인/해결 설명은 [[raw/branch-notes/experiment-nplus1-highlight-feed]]를 참조한다.
| 순서 | Stage | Commit | Tag | checkout 후 주 관찰점 |
|---:|---|---|---|---|
| 1 | L1 | `e68dd67` | `nplus1-replay-l1` | lazy highlights 컬렉션 N+1 |
| 2 | L2 | `138eb67` | `nplus1-replay-l2` | EAGER ToOne fetch 수 |
| 3 | L3 | `dc7495c` | `nplus1-replay-l3` | two-bag fetch의 예상 실패 |
| 4 | L4 | `f257196` | `nplus1-replay-l4` | collection fetch join + paging의 in-memory paging |
| 5 | L5 | `be2a123` | `nplus1-replay-l5` | batch fetch paging |
| 6 | L6 | `cad3c15` | `nplus1-replay-l6` | DTO scalar projection, entity load 0 |
| 7 | L14 | `26b196c` | `nplus1-replay-l14` | window query로 parent별 Top-N |
| 8 | L15 | `8ff0771` | `nplus1-replay-l15` | keyset cursor |
| 9 | L16 | `3310897` | `nplus1-replay-l16` | viewer visibility + keyset |
| 10 | Crown Task 4 | `3f0b82e` | `nplus1-replay-crown` | Top-N + keyset + visibility one query |
| 11 | L12 | `54cf7e7` | `nplus1-replay-l12` | CQRS-lite projection port, parent/child two queries |
각 stage의 세부 실습은 application repository의 `docs/superpowers/plans/*nplus1*lab-guide.md`, `docs/notes/L*.md`, `docs/notes/crown.md`를 사용한다. `L12`를 마지막 tag로 둔 것은 원래 번호가 아니라 이 replay의 학습 순서다.
### 2. lab profile의 API/권한 경계
> **Trace**: D2, D4 / `TC-OFFICIAL-C1`, `TC-OFFICIAL-C3`.
>
> - **UNSUPPORTED_IMPL_DECISION**: `lab` profile과 `lab:reset` permission 이름, marker 소유 방식, anonymous allowlist의 구체 구현은 외부 자료가 정하지 않는다. 정상 profile과 분리된 학습 reset 및 최소 권한 허용이라는 사용자 범위를 우선했다.
| 항목 | replay 계약 |
|---|---|
| profile | lab controller/usecase/fixture는 local `lab` profile에만 등록된다. |
| normal runtime | 기존 advisor를 유지하고 lab usecase/route를 등록하지 않는다. `/api/feed`의 기존 계약을 바꾸지 않는다. |
| reset authorization | `POST /api/lab/feed:reset?count=N``lab:reset`을 선언한다. lab profile의 security advisor는 `AnonymousAuthenticationToken`을 인식해 anonymous에는 `lab:reset` 하나만 허용하고, 다른 permission은 거부한다. unit+HTTP로 확인했다. |
| fixture ownership | `created_by = nplus1-lab` marker 데이터만 삭제/재생성한다. |
| input guard | `page=10001`은 HTTP 400, `VALIDATION_FAILED`다. |
### 3. Crown과 L12의 의도적 차이
> **Trace**: D3 / `AZURE-CQRS-C2`, `AZURE-CQRS-C3`, `SPRING-PROJ-C4`.
| 경로 | 목적 | SQL 관찰값 | 포함 범위 |
|---|---|---|---|
| `GET /api/lab/feed` at Crown/L12 | Crown Task 4 최적화 | `CROWN_TASK4_ONE_QUERY`, prepared statement 1, entity load 0 | visible parent keyset + Top-3 child를 하나의 native query로 읽음 |
| `GET /api/lab/feed/read-model` at L12 | CQRS-lite read model | parent projection 1 + child Top-3 query 1, integration test에서 entity/collection hydration 0 | same store의 application query port; Crown을 대체하지 않음 |
L12의 native child mapping에 사용한 `addScalar`는 runtime 결과 타입 매핑이다. SQL 문자열의 문법, table/column 이름, plan을 Java compiler가 검증하게 만드는 기능은 아니다. 따라서 native SQL은 Testcontainers/실제 PostgreSQL 실행으로 검증한다.
## 검증 기록
### Docker HTTP + PostgreSQL smoke — final L12 tag
fresh `nplus1-final` Compose stack에서 `nplus1-replay-l12`(`54cf7e7`)을 실행했다.
| 수행 | 결과 | 등급 |
|---|---|---|
| `POST /api/lab/feed:reset?count=100` | `success=true`, feed item 100개, highlight 1,961개 | `locally-verified` |
| `GET /api/lab/feed?size=20&viewer=lab-user-008` | item 20개, `strategy=CROWN_TASK4_ONE_QUERY`, `prepared=1`, `entityLoads=0`, parent당 Top-3 최대 3개 | `locally-verified` |
| `GET /api/lab/feed/read-model?page=0&size=20` | `success=true`, item 20개, parent당 Top-3 최대 3개 | `locally-verified` |
| `GET` with `page=10001` | HTTP 400, `VALIDATION_FAILED` | `locally-verified` |
| PostgreSQL `psql` | `created_by = nplus1-lab` row count 100 | `locally-verified` |
| anonymous authorization | `AnonymousAuthenticationToken``lab:reset`만 허용하고 다른 permission은 거부하는 unit+HTTP 검증 | `locally-verified` |
### Historical L1 smoke
fresh stack에서 `nplus1-replay-l1`(`e68dd67`)을 별도로 실행했다.
| 수행 | 결과 | 등급 |
|---|---|---|
| reset `count=10` | `success=true` | `locally-verified` |
| feed request | item 10개, `strategy=L1_LAZY_HIGHLIGHTS`, `prepared=24`, `collectionFetch=10` | `locally-verified` |
### Focused regression suite
다음 filtered cumulative suite는 `BUILD SUCCESSFUL`, test result의 `failures=0`, `errors=0`이었다.
```bash
cd /home/donghyeon/workspace/ca-tmpl-nplus1-api/src
./gradlew spotlessApply :application-core:test --tests dev.caskeleton.application.feed.GetFeedReadModelUseCaseTest --tests dev.caskeleton.application.feed.lab.LabFeedUseCaseTest --tests dev.caskeleton.application.feed.lab.LabFeedCursorTest :adapter:inbound:web:test --tests dev.caskeleton.adapter.inbound.web.controller.lab.LabFeedControllerTest :app-bootstrap:test --tests dev.caskeleton.bootstrap.contract.DeveloperExperienceContractTest --tests dev.caskeleton.adapter.outbound.jpa.feed.FeedReadModelUseCaseIT --tests dev.caskeleton.adapter.outbound.jpa.feed.FeedCrownIT --tests dev.caskeleton.adapter.outbound.jpa.feed.FeedVisibilityIT --tests dev.caskeleton.adapter.outbound.jpa.feed.FeedKeysetIT --tests dev.caskeleton.adapter.outbound.jpa.feed.FeedTopNIT --tests dev.caskeleton.adapter.outbound.jpa.feed.FeedProjectionIT --tests dev.caskeleton.adapter.outbound.jpa.feed.FeedBatchFetchIT --tests dev.caskeleton.adapter.outbound.jpa.feed.FeedFetchJoinPagingIT --tests dev.caskeleton.adapter.outbound.jpa.feed.FeedToOneEagerIT --tests dev.caskeleton.adapter.outbound.jpa.feed.FeedMultipleBagIT --tests dev.caskeleton.bootstrap.lab.LabFeedPersistenceIT
```
명령에 포함된 L1~L6, L14~L16, Crown, L12의 stage integration test class는 모두 통과했다.
다음 verifier도 통과했다.
```bash
./gradlew verifyCleanArchitectureDependencies verifyPublicPathSnapshot verifyEnvKeys
```
### Full check의 기준선 실패
`./gradlew check`에는 정확히 하나의 잔여 architecture failure가 있었다. 이는 이 replay branch가 수정하지 않은 base commit `6f0b0d6``IdempotencyRecordEntity.requestHash`에 있는 `columnDefinition = "char(64)"`가 vendor-neutral entity rule을 위반한 것이다. 따라서 이 노트는 full check를 green이라고 주장하지 않으며, 재현 브랜치의 실패로 귀속하지 않는다.
## 엣지·실패·의존
- **실패·엣지 경로**: lab profile 밖에서 lab reset을 사용하려 하면 endpoint/usecase가 등록되지 않아야 한다. lab profile에서도 anonymous access는 `lab:reset` 하나에만 한정되고, 다른 permission은 거부된다.
- **실패·엣지 경로**: native query의 `addScalar` 타입이 결과와 맞지 않거나 SQL이 잘못되면 compile이 아니라 integration/runtime 실행에서 실패한다.
- **실패·엣지 경로**: L12 read model이 Crown과 동등한 visibility/keyset solution이라고 가정하면 안 된다. L12은 same-store read port의 2-query projection이고 Crown 최적화 endpoint는 유지된다.
- **다른 계약 의존**: [[raw/branch-notes/experiment-nplus1-highlight-feed]]의 각 랩 의미와 [[raw/branch-notes/feature-application-query-bypass-contract]]의 CQRS-lite/read-port 경계를 소비한다. Full CQRS physical read store는 후자 D2의 escalation 범위다.
- **검증 환경 의존**: `DeveloperExperienceContractTest`가 root `AGENTS.md` 존재를 요구해 replay worktree에 일시적인 ignored bridge를 두고 test 후 제거했다. 이는 application commit에 포함되지 않는다.
## 검증해야 할 주장
| Claim | Why uncertain | How to verify | Status |
|---|---|---|---|
| 11개 tag가 다른 machine에서도 compose/API guide대로 재현된다 | 로컬 Docker, Gradle cache, port 상태에 의존 | 깨끗한 clone/worktree에서 tag별 compose smoke 수행 | `needs-confirmation` |
| lab profile이 배포 환경에서 활성화되지 않는다 | local profile boundary는 production deployment policy를 증명하지 않음 | deployment manifest/env registry audit | `needs-confirmation` |
| `IdempotencyRecordEntity` failure를 수정한 뒤 full `check`가 green이 된다 | 현재 branch가 해당 base failure를 고치지 않음 | 별도 base-fix branch에서 full check 실행 | `planned` |
## 마주친 문제
- `DeveloperExperienceContractTest`가 checkout worktree root의 `AGENTS.md`를 요구했다.
- 원인: replay worktree의 contract discovery 조건.
- 시도: test 실행 중 ignored bridge를 일시적으로 제공.
- 해결: test 통과 후 bridge를 삭제했고 application history에는 포함하지 않았다.
- 별도 오류 노트: 아래 Cluster의 raw error 노트.
- full `check``IdempotencyRecordEntity.requestHash` vendor-specific `columnDefinition`에서 멈췄다.
- 원인: base `6f0b0d6`에 이미 존재한 rule violation.
- 해결: replay scope 밖으로 남기고 base failure로 명시했다.
## 묶음
<!-- GENERATED: interviews:start -->
- [[raw/interviews/crown-one-query-vs-cqrs-lite-read-model]]
- [[raw/interviews/native-query-addscalar-runtime-validation]]
<!-- GENERATED: interviews:end -->
<!-- GENERATED: errors:start -->
- [[raw/errors/developer-experience-contract-agents-bridge-2026-07-15]]
- [[raw/errors/idempotency-column-definition-base-check-failure-2026-07-15]]
<!-- GENERATED: errors:end -->
<!-- GENERATED: blog-topics:start -->
- [[raw/blog-topics/nplus1-lab-checkoutable-api-replay-2026-07-15]]
<!-- GENERATED: blog-topics:end -->
아래 raw leaf는 생성되었고, 이 branch를 `## Parent` upward link로 가진다. 두 번째 블로그 주제는 별도 raw 문서로 추출하지 않았다.
### Sub-branches (세부 작업)
- 없음 — 11개 checkpoint는 하나의 replay branch history로 관리한다.
### 오류 기록 (이 branch 작업 중 발생)
- [[raw/errors/developer-experience-contract-agents-bridge-2026-07-15]] — replay worktree root discovery와 임시 ignored bridge.
- [[raw/errors/idempotency-column-definition-base-check-failure-2026-07-15]] — base `6f0b0d6`의 vendor-neutral entity rule failure.
### 면접 준비 (이 작업에서 나올 수 있는 면접 질문)
- [[raw/interviews/crown-one-query-vs-cqrs-lite-read-model]] — one-query optimization과 same-store CQRS-lite를 구분하는 기준.
- [[raw/interviews/native-query-addscalar-runtime-validation]] — native mapping type과 SQL compile-time validation의 차이.
### job-posting tie-ins (이 작업에서 파생된 글감)
- [[raw/blog-topics/nplus1-lab-checkoutable-api-replay-2026-07-15]] — 테스트 코드를 실제 API/DB 학습 환경으로 변환한 방법.
- `raw/blog-topics/crown-query-and-cqrs-lite-boundary-2026-07-15.md` — 이번 capture에서는 별도 raw 문서로 추출하지 않았다.
- derived blog: 생성 전. canonical 검증 후 `wiki/blog/` 후보를 결정한다.
## 관련 일일 노트
- 해당 없음 — 이 캡처 시점에는 별도 daily-note를 만들지 않았다.
## 완료 후 정리
- PR 링크: 없음.
- 리뷰 메모: 11개 replay tag와 L1/final Docker smoke, focused suite, architecture/public-path/env verifier를 local에서 확인했다.
- 머지 결과 / 배포 환경: local Docker Compose + local PostgreSQL만 검증. production 배포 검증 없음.
- **wiki 추출 대상** (verified만, `wiki/projects/`로만 추출):
- `actually-implemented` 항목: lab-profile replay API, 11 stage tag catalog, Crown/L12 분리 경로.
- `locally-verified` 항목: L1 historical smoke, final Docker HTTP/PostgreSQL smoke, filtered Gradle suite/verifier.
- `prod-verified` 항목: 없음.
- **추출하지 않을 항목** (planned / documented-only / abandoned): base `IdempotencyRecordEntity` rule failure 해결, production profile/deployment verification.