From 7756856bb2876d7072291079b55214ed59bb0b25 Mon Sep 17 00:00:00 2001 From: DongHyeonka Date: Wed, 16 Sep 2026 18:57:26 +0900 Subject: [PATCH] docs: record how the barrel boundary plan actually executed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 계획과 달랐던 2건(워커 realm tsconfig 제외, spec 정규식이 unsafe로 거부됨)과 측정 결과(번들 영향 0바이트, 회귀 검사 12->14)를 남긴다. 이 샌드박스에서 판정 불가한 게이트 3종과 그 이유도 적는다. Co-Authored-By: Claude Opus 5 (1M context) --- .../2026-09-16-adapter-barrel-boundary.md | 95 +++++++++++++------ 1 file changed, 67 insertions(+), 28 deletions(-) diff --git a/docs/superpowers/plans/2026-09-16-adapter-barrel-boundary.md b/docs/superpowers/plans/2026-09-16-adapter-barrel-boundary.md index 33f79ea..b9968a9 100644 --- a/docs/superpowers/plans/2026-09-16-adapter-barrel-boundary.md +++ b/docs/superpowers/plans/2026-09-16-adapter-barrel-boundary.md @@ -43,7 +43,7 @@ - Produces: 위 8개 배럴이 내보내는 심볼 82개. Task 2가 이 경로들로 import를 바꾼다. 심볼 존재는 spec §7에서 82/82 기계 대조 완료(누락 0, 오타 0). - Consumes: 없음 (첫 태스크) -- [ ] **Step 1: 배럴 5개 생성 (단일 파일 그룹 + query-cache)** +- [x] **Step 1: 배럴 5개 생성 (단일 파일 그룹 + query-cache)** `src/adapters/auth/index.ts`: ```ts @@ -121,7 +121,7 @@ export { } from "./tanstack-query-cache.ts"; ``` -- [ ] **Step 2: 배럴 3개 생성 (주석이 계약인 것들)** +- [x] **Step 2: 배럴 3개 생성 (주석이 계약인 것들)** `src/adapters/platform/index.ts`: ```ts @@ -227,17 +227,17 @@ export { export type { OperationRequestInput } from "./request-builder.ts"; ``` -- [ ] **Step 3: 타입체크로 심볼 존재를 검증한다** +- [x] **Step 3: 타입체크로 심볼 존재를 검증한다** Run: `corepack pnpm check:types:app` Expected: PASS. 실패하면 존재하지 않는 심볼을 재수출한 것이다 — 에러가 지목한 이름을 해당 소스 파일에서 확인하고 배럴에서 빼라. spec §7의 대조표와 대조할 것. -- [ ] **Step 4: 게이트가 새 파일 8개를 거부하는 것을 확인한다 (의도된 실패)** +- [x] **Step 4: 게이트가 새 파일 8개를 거부하는 것을 확인한다 (의도된 실패)** Run: `git add -A && corepack pnpm check:adapter-inventory` Expected: **FAIL.** `git ls-files src/adapters`가 128개를 보고하는데 INVENTORY.md는 120행이므로 불일치를 보고한다. 이 실패를 본 뒤 Step 5로 간다. (실패하지 않으면 `git add`가 안 된 것이다.) -- [ ] **Step 5: INVENTORY.md에 행 8개 추가** +- [x] **Step 5: INVENTORY.md에 행 8개 추가** `docs/reviews/adapters/INVENTORY.md`의 표에 경로 알파벳 순서 위치로 끼워 넣고 번호를 다시 매긴다. 상세 리뷰 링크는 같은 그룹의 기존 행과 동일하게 쓴다. @@ -254,12 +254,12 @@ Expected: **FAIL.** `git ls-files src/adapters`가 128개를 보고하는데 INV 그리고 `docs/reviews/adapters/INVENTORY.md:132`의 `합계: **120/120**` → `합계: **128/128**`. -- [ ] **Step 6: 게이트 3종 통과 확인** +- [x] **Step 6: 게이트 3종 통과 확인** Run: `corepack pnpm check:adapter-inventory && corepack pnpm check:types:app && corepack pnpm check:architecture` Expected: 셋 다 PASS. `check:adapter-inventory`가 `128 files PASS`를 출력한다. -- [ ] **Step 7: 워커 realm 타입체크 — 실행 중 발견한 필수 단계** +- [x] **Step 7: 워커 realm 타입체크 — 실행 중 발견한 필수 단계** > 2026-09-16 실행 중 발견. 계획 초안에는 없었다. > `tsconfig.service-worker.json`은 `src/adapters/service-worker` 폴더를 통째로 @@ -283,7 +283,7 @@ Expected: 셋 다 PASS. `check:adapter-inventory`가 `128 files PASS`를 출력 Run: `corepack pnpm check:types:service-worker` Expected: PASS -- [ ] **Step 8: 제거 드릴이 살아 있는지 확인한다** +- [x] **Step 8: 제거 드릴이 살아 있는지 확인한다** `storage/index.ts`를 새로 만들었으므로 제거 드릴을 돌려 서브폴더 삭제가 여전히 성립하는지 본다. @@ -298,7 +298,7 @@ Expected: 드릴 출력에 `error TS`가 0건이어야 한다. `storage/index.ts > **판정 기준은 드릴의 exit code가 아니라 `error TS` 0건과 `still imported` 부재다.** > CI 환경에서는 전체 PASS를 확인할 것. -- [ ] **Step 9: 커밋** +- [x] **Step 9: 커밋** ```bash git add src/adapters/*/index.ts docs/reviews/adapters/INVENTORY.md tsconfig.service-worker.json @@ -330,7 +330,7 @@ EOF - Consumes: Task 1이 만든 8개 배럴의 심볼 82개 - Produces: `src/` 안에 어댑터 내부 파일을 직접 겨누는 import 0건. Task 3의 게이트 규칙이 이 상태를 전제로 통과한다. -- [ ] **Step 1: 치환 전 위반 건수를 기록한다** +- [x] **Step 1: 치환 전 위반 건수를 기록한다** Run: ```bash @@ -338,7 +338,7 @@ grep -rn 'from "[^"]*adapters/[^"]*"' src/ | grep -v '^src/adapters/' | grep -v ``` Expected: `15`. 이 숫자가 Step 4에서 `0`이 되어야 한다. -- [ ] **Step 2: `runtime-adapters.ts`의 import 블록을 다시 쓴다** +- [x] **Step 2: `runtime-adapters.ts`의 import 블록을 다시 쓴다** `src/bootstrap/runtime-adapters.ts`의 `:6`~`:29` 구간이 한 덩어리다. 아래에서 위로 편집하거나 블록 전체를 한 번에 교체한다 — 위에서부터 고치면 줄 번호가 밀린다. @@ -360,7 +360,7 @@ Expected: `15`. 이 숫자가 Step 4에서 `0`이 되어야 한다. `../adapters/cross-context-invalidation/index.ts` 2줄은 이미 배럴이므로 **건드리지 않는다.** -- [ ] **Step 3: 나머지 4개 파일을 치환한다** +- [x] **Step 3: 나머지 4개 파일을 치환한다** | 파일:줄 | 현재 | 바뀔 것 | |---|---|---| @@ -371,7 +371,7 @@ Expected: `15`. 이 숫자가 Step 4에서 `0`이 되어야 한다. | `.storybook/preview.tsx:5` | `../src/adapters/auth/external-session-adapter.ts` | `../src/adapters/auth/index.ts` | | `.storybook/preview.tsx:6` | `../src/adapters/query-cache/tanstack-query-cache.ts` | `../src/adapters/query-cache/index.ts` | -- [ ] **Step 4: 위반이 0이 된 것을 확인한다** +- [x] **Step 4: 위반이 0이 된 것을 확인한다** Run: ```bash @@ -379,12 +379,12 @@ grep -rn 'from "[^"]*adapters/[^"]*"' src/ | grep -v '^src/adapters/' | grep -v ``` Expected: `0` -- [ ] **Step 5: 타입·린트·아키텍처 게이트** +- [x] **Step 5: 타입·린트·아키텍처 게이트** Run: `corepack pnpm check:types:app && corepack pnpm lint && corepack pnpm check:architecture` Expected: 셋 다 PASS. -- [ ] **Step 6: 번들 예산을 확인한다 — 이 태스크의 최대 위험** +- [x] **Step 6: 번들 예산을 확인한다 — 이 태스크의 최대 위험** 배럴 재수출이 트리셰이킹을 무력화하면 초기 청크가 커진다. `package.json`에 `sideEffects` 선언이 없어 번들러가 모든 모듈을 부작용 있는 것으로 본다. @@ -396,12 +396,12 @@ Expected: PASS (`bundle.initialJsGzipBytes` 상한 204800). 2. 그래도 넘치면 `src/adapters/service-worker/index.ts`에서 `service-worker-lifecycle.ts` 블록을 빼고, 워커 realm은 파일 직접 import를 유지한다. 그리고 Task 3의 게이트 규칙 `from.pathNot`에 워커 진입점을 추가한다. (근거: realm이 다르면 문도 다르다 — `platform`과 같은 논리.) 3. 1·2로 안 되면 이 태스크를 중단하고 보고한다. 예산 초과를 안고 진행하지 않는다. -- [ ] **Step 7: 단위·통합 테스트** +- [x] **Step 7: 단위·통합 테스트** Run: `corepack pnpm test:unit && corepack pnpm test:integration` Expected: PASS. 이 태스크는 import 경로만 바꿨으므로 테스트 결과가 달라질 이유가 없다. 깨지면 배럴이 내보내는 심볼이 원본과 다른 것이다. -- [ ] **Step 8: 커밋** +- [x] **Step 8: 커밋** ```bash git add src/bootstrap src/features .storybook @@ -435,7 +435,7 @@ EOF - Consumes: Task 2가 만든 "위반 0건" 상태. 위반이 남아 있으면 이 규칙 추가가 곧바로 게이트를 깬다. - Produces: `adapter-groups-are-reached-through-their-barrel` 규칙과 그 회귀 fixture 2종 -- [ ] **Step 1: fixture 트리를 만든다 (규칙보다 먼저 — 실패를 먼저 본다)** +- [x] **Step 1: fixture 트리를 만든다 (규칙보다 먼저 — 실패를 먼저 본다)** `tests/fixtures/architecture/dependency-graph/barrel/adapters/http/client.ts`: ```ts @@ -465,7 +465,7 @@ export const barrelComposition = createFixtureHttpClient; > fixture는 `analyzeSourceGraph(dir, "src")`로 분석되어 경로가 `src/...`로 보고된다(`scripts/check-architecture.ts:821-833`). 그래서 `^src/`로 시작하는 규칙이 fixture 트리에 그대로 적용된다. -- [ ] **Step 2: `.dependency-cruiser.json`에 규칙을 추가한다** +- [x] **Step 2: `.dependency-cruiser.json`에 규칙을 추가한다** `forbidden` 배열의 `adapters-do-not-know-other-concrete-adapters` **바로 다음**, `no-circular-dependencies` **앞**에 넣는다. @@ -487,12 +487,12 @@ export const barrelComposition = createFixtureHttpClient; > 규칙 형태 적합성: `scripts/check-architecture.ts:799-815`의 `validateArchitectureRules`는 `from`에 `path`/`pathNot`, `to`에 `path`/`pathNot`/`circular`만 허용한다. 정규식은 `new RegExp(pattern, "u")`로 평가되므로 비캡처 그룹 `(?:...)`이 허용된다. `$1` 역참조는 쓰지 않았다. -- [ ] **Step 3: 규칙이 실제 소스에서 위반 0인지 확인한다** +- [x] **Step 3: 규칙이 실제 소스에서 위반 0인지 확인한다** Run: `corepack pnpm check:architecture` Expected: PASS. FAIL하면 Task 2에서 놓친 import가 있다는 뜻이다 — 출력이 지목한 파일을 배럴 경로로 고쳐라. -- [ ] **Step 4: fixture 검사를 `check-architecture.ts`에 배선한다** +- [x] **Step 4: fixture 검사를 `check-architecture.ts`에 배선한다** `runGraphFixtureChecks()`의 `Promise.all` 블록(`scripts/check-architecture.ts:826-833`)에 `barrelGraph`를 추가한다: @@ -529,12 +529,12 @@ Expected: PASS. FAIL하면 Task 2에서 놓친 import가 있다는 뜻이다 — > 필드명 근거: `ArchitectureViolation`은 `rule` / `severity` / `source` / `target`을 갖는다 (`scripts/check-architecture.ts:35`, 생성부 `:734-739`). `from`/`to`가 아니다. -- [ ] **Step 5: fixture 회귀 검사가 통과하는지 확인한다** +- [x] **Step 5: fixture 회귀 검사가 통과하는지 확인한다** Run: `corepack pnpm check:architecture` Expected: `Architecture graph fixtures: 14 regression checks PASS` (기존 12 + 신규 2). 그리고 전체 PASS. -- [ ] **Step 6: fixture가 실제로 무언가를 잡는지 역검증한다** +- [x] **Step 6: fixture가 실제로 무언가를 잡는지 역검증한다** 규칙을 잠시 무력화해서 fixture가 FAIL하는지 본다. fixture가 항상 통과하면 회귀 검사가 아니다. @@ -548,7 +548,7 @@ corepack pnpm check:architecture; echo "EXIT=$?" ``` Expected: 첫 번째 EXIT는 0이 아니어야 하고(assertion 실패), 되돌린 뒤 EXIT는 0이어야 한다. -- [ ] **Step 7: 커밋** +- [x] **Step 7: 커밋** ```bash git add .dependency-cruiser.json scripts/check-architecture.ts tests/fixtures/architecture/dependency-graph/barrel @@ -578,7 +578,7 @@ EOF - Consumes: Task 3의 규칙 이름 `adapter-groups-are-reached-through-their-barrel` - Produces: 없음 (문서만) -- [ ] **Step 1: `layers.md`에 배럴 경계 절을 추가한다** +- [x] **Step 1: `layers.md`에 배럴 경계 절을 추가한다** "The adapter kernel" 절 **다음에** 아래를 넣는다: @@ -608,7 +608,7 @@ EOF 이것이다. ``` -- [ ] **Step 2: 테스트 이관 규칙을 적는다** +- [x] **Step 2: 테스트 이관 규칙을 적는다** `docs/reviews/adapters/README.md` 끝에 추가: @@ -626,12 +626,12 @@ EOF 아니라 재수출 덤프가 된다. ``` -- [ ] **Step 3: 문서 게이트 확인** +- [x] **Step 3: 문서 게이트 확인** Run: `corepack pnpm lint && corepack pnpm check:architecture` Expected: PASS. (문서 링크 검증이 있으면 `corepack pnpm verify:documentation`도 돌린다.) -- [ ] **Step 4: 커밋** +- [x] **Step 4: 커밋** ```bash git add docs/architecture/layers.md docs/reviews/adapters/README.md @@ -671,3 +671,42 @@ grep -rn 'from "[^"]*adapters/[^"]*"' src/ | grep -v '^src/adapters/' | grep -v - `tests/` 166줄의 배럴 이관 — Task 4 Step 2의 규칙대로 파일을 손댈 때만 한다. - 대형 파일 분할 — 별도 계획. 이 계획이 그 선행조건이다. - spec §6이 남긴 부수 발견 4건(`telemetry:49`의 잉여 재수출, `browser-files/index.ts`의 포트 재수출, `opfs/index.ts` 누락 심볼 3개, `.dependency-cruiser.json:141`의 존재하지 않는 `web-worker` 경로) — 각각 별도 티켓. + +--- + +## 실행 기록 (2026-09-16 완료) + +네 태스크 전부 실행했다. 커밋 5개: `a42d961` (spec·plan), `b29a471` (배럴 8개), +`1606d9b` (소비자 15줄), `27ab17d` (게이트 규칙 + fixture), `eb40bc9` (문서). + +**계획과 달랐던 것 2건.** + +1. **워커 realm 타입체크 실패** — 계획 초안에 없던 Task 1 Step 7이 여기서 나왔다. + `service-worker/index.ts`가 `tsconfig.service-worker.json`의 WebWorker lib + 컴파일에 페이지 realm 파일을 끌어들여 `Cannot find name 'document'`로 실패했다. + 배럴을 그 tsconfig의 `exclude`에 추가해 해결. +2. **spec의 정규식이 게이트에 거부당함** — spec §5.1이 제시한 + `^src/adapters/[^/]+/(?:[^/]+/)?index\.ts$`를 dependency-cruiser가 + "unsafe regular expression"으로 거부했다(`(?:[^/]+/)?` 안의 `+`가 star height 2). + 중첩 없는 교대로 바꿔 통과시켰다: + `^src/adapters/[^/]+/index\.ts$|^src/adapters/[^/]+/[^/]+/index\.ts$` + spec은 이 규칙을 실제로 실행해 본 적이 없었다. + +**측정 결과.** + +- 번들 영향 **0바이트**: 배럴 도입 전후 모두 초기 JS 181114 / 204800 gzip bytes. + `sideEffects` 선언이 없는데도 Rollup이 재수출을 트리셰이킹했다. 계획이 지목한 + 최대 위험은 현실화되지 않았고 대응 3단계는 쓰지 않았다. +- import 그래프: 297 모듈 / 897 의존 → 305 모듈 / 914 의존 (배럴 8개 + fixture 4개). +- 회귀 검사: 12 → 14. 규칙 이름을 바꿔 fixture가 실제로 깨지는 것을 확인했다. +- 배럴 미경유 import: `src/` 15건 → **0건**. + +**PASS:** `check:types` `lint` `check:architecture` `check:adapter-inventory` +`check:bundle` `test:integration`(81) `test:component`(130) `verify:documentation` + +**이 환경에서 판정 불가:** `test:unit`, `test:browser-file-storage-removal`, +`test:realtime-removal`. 셋 다 `tests/unit/ci-artifact-contract.test.ts`의 16건이 +`bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted`로 실패해 exit 1이 +된다. `develop` `5434760` 기준선에서도 동일하게 16건 실패함을 stash 후 실행해 +확인했다. 어댑터 관련 테스트는 전부 통과했고(`test:unit` 1796 passed), 제거 드릴의 +`error TS`는 0건이다. **CI 환경에서 이 셋의 전체 PASS를 확인해야 한다.**