# 200스테이지 구현 계획 > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development 또는 superpowers:executing-plans로 아래 작업을 순서대로 구현한다. 실행 방식: Native, 전체 시각 검수 승인. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** 확정된 200판 편성, 신규 젤리35종, 이해하기 쉬운 Split, 기존 획득 별 보존을 구현한다. **Architecture:** 19종은 콘텐츠 분류이며 런타임 모드 7종은 유지한다. 신규 형상·콘텐츠를 별도 파일에서 준비하고 검증한 뒤 `STAGES`와 `stage-order.json`을 함께 전환한다. 활성 판과 과거 판 ID를 구분해 진행은 활성 판만, 누적 획득 별은 보존된 과거 기록까지 계산한다. **Tech Stack:** 기존 TypeScript·Three.js·GLSL·Vite·Vitest·Playwright, 권장 Node.js24. 신규 패키지 없음. **Spec:** [확정 설계](../specs/2026-10-29-stage-200-design.md), [200행 편성표](../specs/2026-10-29-stage-200-stages.csv), [형상 목록](../specs/2026-10-29-stage-200-shapes.csv). 상태: **Native 구현 및 승인 범위 전체 시각 검수 완료**. 사용자 승인: 전체 시각 검수, 최종 독립 리뷰 gpt-6-astra / medium. 운영 배포 전이다. ## 실행 상태 아래 세부 절차는 승인 당시 계획을 보존한다. 실제 수행은 이 표와 실행 기록을 기준으로 읽는다. 개별 파일·명령·커밋 분할의 조정은 실행 기록에 적었다. - [x] 준비·작업1: 승인/격리/기준선,19종 분류와 편성 검증 - [x] 작업2: 과거 진행·획득 별·구매·도감 보존 - [x] 작업3~7: 신규35종의 형상·재질·물성·도감·퍼즈 연결 - [x] 작업8~10:200판 콘텐츠·풀이·ID 대응·이관 메타데이터 - [x] 작업11: 활성 편성·배지·기존 검사 번호 이관 - [x] 작업12: 전체 기능·수명주기·메모리·성능 검사 완료(곰 최초 전환 한계 별도 기록) - [x] 작업13: 전체 시각412장 검토·Astra Medium 독립 리뷰·최종 결과 기록 완료 ## Global Constraints - 정확히200판. 소개1~57: 19종×3판. 회전판37~39·43~45·49~51, slow55~57. - 58~200: 일반68묶음×2판 + odd7묶음×1판. 전체94묶음. 묶음 경계에서는 같은 계열 금지. - 같은 세부 모드에서 젤리 재사용 금지. bear-all·bear-spare·odd는 bear 반복 예외. swipe-all은 같은 판의 동일 종류 복수 개체 허용. - 구간별 최초 등장 최소30종. 확정 표 집계44/40, 활성84종=기존49+신규35. 오이 두 형태 합산43/40. - Split solver 풀이98점 이상, 별도의 자연스러운 눈대중 풀이90점 이상. 함정 의무는 폐기하고 스냅·면적 보정은 추가하지 않는다. - 신규는 비생물 사물·음식. 색·크기·회전만 바꾼 복제 금지. 구멍 없는 단순 외곽, 기존 메시·퍼즈 품질 기준 유지. - `stageId`는 불변. 옛 ID 재사용 금지. 표시 순서 변경은 `stage-order.json`만. `legacy-stage-order.json` 수정 금지. - 한국어 문서·주석·커밋, 영어 제품 UI. Game에 모드별 분기 추가 금지. 입력 중 동기 메시 생성 금지. - main 푸시·운영 배포·네이티브 배포는 이번 구현의 자동 후속 작업이 아니다. 커밋은 검증된 작업 단위로 하고 푸시는 별도 요청에 따른다. - Android 에뮬레이터 금지. 네이티브 검증을 추가하면 ADB 실기기 승인 확인, iOS는 작업 생성 복제본만 종료·삭제한다. ## Review Focus 1. 제외된 판에 별·구매 이력이 있는 저장: 재실행해도 획득 별과 잔액을 한 번만 계산하고 구매·장착 유지 — 작업2. 2. 재편 전 전체 완료 또는 제외된 판 이어하기: 첫 활성 미완료로 이동하고, 활성 판의 유효 이어하기 ID는 유지 — 작업2·12. 3. 대표 젤리는 달라도 swipe의 다른 발사에 중복이 있는 입력: 모든 발사 종류 검사에서 거부 — 작업1·9. 4. 둥근 사각형의 길이 3등분과 실제 면적 3등분 차이: 눈대중90점·정답98점 각각 확인, solver만으로 가독성 통과 판정 금지 — 작업3·8. 5. 형상·모드 조합이 바뀐 직후 취소·숨김·재시도·품질 변경: 이전 자원/장비가 남지 않고 입력 가능 상태로 복귀 — 작업12. --- ## 실행 순서와 파일 책임 저장/분류, 형상, 콘텐츠, 통합검증의 네 단계로 나눈다. 서로 같은 카탈로그·형상 레지스트리를 공유하므로 별도 독립 프로젝트로 분리하지 않는다. 작업1~2 → 작업3~7 → 작업8~10 → 작업11~13 순서다. 형상 작업은 각 8종 이하로 검증하고, 아직 준비하지 않은 새 형상 키를 타입에 미리 넣어 빌드를 깨뜨리지 않는다. | 파일 | 역할 | |---|---| | 새 `src/stages/StageVariant.ts` | 기존 레벨 필드에서 19종과 계열을 계산. 검증용·콘텐츠 표시용 순수 분류 | | 새 `src/stages/retired-stage-ids.json` | 승인된 과거 판 중 활성 목록에서 제외된 ID 허용 목록. 알 수 없는 임의 ID는 저장에서 거부 | | 기존 `StageProgress.ts`, `core/ProgressService.ts` | 과거 기록 보존, 활성 번호 투영, 완료 판단, 별 합계 | | 새 `src/jelly/ExpansionShapes.ts` | slow를 제외한32종의 설계 제어점. 기존 `designed()` 사용 | | 새 `src/jelly/shaders/expansion{Split,Everyday,Kitchen,Leisure}.glsl.ts` | 8종씩 절차적 표면·단면. 개별 외관을 유지하며 기존 GLSL 부품 재사용 | | 기존 `JellyShapes.ts`, `JellyMaterial.ts`, `JellyPiece.ts`, `ui/JellyThumbnail.ts` | 종류·빌더·재질·물성·영어 이름·도감 연결 | | 기존 `SlowFood.ts`, 새 `shaders/{daikon,baguette,swissroll}.glsl.ts` | 새 음식3종 프로필·외관·단면 | | 새 `src/stages/StageContentHelpers.ts` | 기존 guide/slicing/swiping/spinning/recutting/halves/stamping 헬퍼를 실제 재사용할 만큼만 추출 | | 새 `src/stages/content/{Split200,Guide200,Motion200,Timing200}.ts` | 모드에 맞는 명시적 콘텐츠 정의. 런타임 랜덤 생성 금지 | | 새 `src/stages/Stage200.ts` | 위 배열을 합친 `STAGE_200_CONTENT`. 활성화 전 테스트 가능한 카탈로그 | | 기존 `StageDefinitions.ts`, `stage-order.json` | 최종 활성200판과 유일한 표시 순서 | | 새 `tests/fixtures/stages-before-200.json` | 작업 시작 시 기존81판의 직렬화된 정의, ID 동일성 및 이관 근거. 게임 번들에는 포함하지 않음 | | 새 `tests/expansionPlan.ts` | Node에서 확정 CSV를 읽는 테스트 전용 도우미. 게임에서 CSV를 파싱하지 않음 | | 새 `e2e/helpers/content.ts` | 콘텐츠 조건/고유 ID를 현재 번호로 바꾸는 E2E 도우미 | ## 실행 전 준비와 선택 사항 - [ ] 사용자에게 실행 방식과 시각 검수 방식을 선택받고 이 문서에 기록한다. 아직 선택 없이 구현·캡처를 시작하지 않는다. 계획 작성과 읽기 검사는 가능하다. - [ ] `git status --short`, `PROGRESS.md`, 적용되는 AGENTS를 읽고 시작 커밋을 기록한다. 브랜치/격리가 필요하면 using-git-worktrees 절차를 따르고 현재 미커밋 설계 파일을 유실하지 않는다. - [ ] Node24·의존성·기존 :4173 서버를 확인하고 `python3 docs/superpowers/specs/2026-10-29-stage-200-verify.py`, `npm test`, `npm run build`를 실행한다. 시작 실패는 이 작업의 회귀와 분리해 기록한다. 추천 실행 방식은 **Native**다. 형상 등록·콘텐츠·저장·E2E가 공통 파일을 자주 바꾸므로 같은 컨텍스트에서 순차 구현하고 최종 독립 리뷰를 받는 편이 효율적이다. SDD 선택 시 작업별 구현·리뷰를 수행하며 공통 파일 변경을 동시 실행하지 않는다. 어느 방식이든 최종 독립 리뷰는 **gpt-6-astra / medium**을 명시한다. ## 작업1: 19종 분류와 콘텐츠 검증의 기반 **Files:** 새 `src/stages/StageVariant.ts`, `tests/StageVariant.test.ts`, `tests/expansionPlan.ts`, `tests/ExpansionPlan.test.ts`, `tests/fixtures/stages-before-200.json`. 변경 `tests/contentLevels.ts`. **Interfaces:** - `StageVariant`는 설계 표의19개 키 union, `StageFamily`는 guide/split/spin/recut/stamp/slow/swipe/bear/odd. - `stageVariant(level: LevelDefinition): StageVariant`, `stageFamily(variant: StageVariant): StageFamily`. - 테스트 도우미 `readExpansionPlan(): ExpansionPlanRow[]`; 행 타입은 `level:number, block:number, mode:StageVariant, jelly:string, asset:'기존 리소스'|'신규 제작'`. 아직 미등록된 신규 키가 있으므로 여기서는 JellyType 단언하지 않는다. - 기존 원본81판은 테스트 fixture로 보존한다. `contentLevels.ts`의 기존58판 품질 검사는 fixture와 고정 legacy 대응표에서 구성하고, 실제 활성 판 검사는 별도 파일로 수행한다. - [ ] RED: 분류 테스트 작성. `spin+cutSteps(line,S)`→spin-mixed, `recut+cutSteps(U,C)`→recut-curve, 정적 curve+line→mixed, curve만→curve, split target3→split3, swipe pieces→swipe-pieces, bear spare→bear-spare를 단언한다. count/speed 변경으로 모드가 바뀌지 않음을 확인한다. - [ ] `npx vitest run tests/StageVariant.test.ts tests/ExpansionPlan.test.ts`로 미구현 분류/reader 때문에 실패하는지 확인한다. - [ ] 기존81판을 fixture에 직렬화하고 원본과 deep equality 검사한다. 기존58판 테스트는 fixture로 전환하되 원래 검증을 삭제하지 않는다. `stageVariant`는 spin/recut를 정적 guide보다 먼저 검사한다. 데이터에 없는 모드를 임의 기본값으로 숨기지 않는다. - [ ] 확정 CSV 검사를 TS 테스트로 옮긴다. 정확히200행·94묶음·44/40최초·신규35를 단언한다. 3종 곰 예외만 허용하고 `levelJellyTypes()`로 swipe의 모든 발사 종류를 검사하는 실패 fixture도 둔다. 새 코드가 없을 때도 원본 CSV 검사와 기존 게임 빌드는 통과해야 한다. - [ ] 위 테스트 및 `npx vitest run tests/LevelOrder.test.ts tests/StageCatalog.test.ts` 통과 후 커밋: `test: 200판 편성 분류와 기존 콘텐츠 기준선 추가`. ## 작업2: 과거 스테이지의 획득 별과 진행 보존 **Files:** 변경 `src/stages/StageProgress.ts`, `src/core/ProgressService.ts`, `src/stages/Collection.ts`, `tests/StageProgress.test.ts`, `tests/ProgressService.test.ts`, `tests/EquipmentService.test.ts`, `tests/Collection.test.ts`; 새 `src/stages/retired-stage-ids.json`(초기 빈 배열). **Interfaces:** - `sanitizeStageProgress(value: unknown, activeIds: readonly string[], retiredIds: readonly string[] = []): StageProgress`. - 기존 `StageProgress` version1 구조 유지. stars/completed/visits는 active∪retired만 보존, currentStageId와 완료 판단은 active만 사용한다. 별도 이중 별 장부를 만들지 않는다. - `ProgressService(store, levelCount, levels = LEVELS, retiredIds = retiredStageIds)`의 네 번째 인자를 추가한다. `progress.stars`와 `visits`는 활성 번호가 있는 항목만 투영; `earnedStars()`는 활성+과거 최고 별 합계. - `clearStage()` 공개 시그니처는 유지하고, activeIds 내 미완료 탐색만 한다. - 도감 복원은 기존 `restoreCollection(value: unknown, stars: StarRecord, levels: readonly LevelDefinition[])`에 선택적 네 번째 인자 `history?: { stars: Readonly>; jellies: Readonly> }`를 더한다. 활성 번호 기반 추론은 유지하고, 전용 도감 저장이 없을 때만 과거 ID 별 기록으로 발견 종류를 합친다. 명시적으로 빈 도감은 그대로 빈 목록이다. - [ ] RED: active=`['new']`, retired=`['old']`, 저장 old별3+완료+방문+이어하기old → old기록 보존, 이어하기new. 반복 sanitize 결과 동일. old별3+new별2 → earned5, 활성번호 별은 new2만, `undefined` 키 없음. 알 수 없는ID와 별9는 버린다. - [ ] RED: 과거 모든 판 완료/활성 일부 완료/활성 전체 완료/유효한 활성 이어하기/활성ID와retiredID 중복을 검사한다. 중복 ID의 별은 한 번만 합산. 상품구매 후 재로딩해 잔액·보유·장착 불변을 확인한다. - [ ] `npx vitest run tests/StageProgress.test.ts tests/ProgressService.test.ts tests/EquipmentService.test.ts`의 기대 실패를 확인한다. - [ ] 위 보존·투영을 구현한다. 숫자 기반 구버전은 고정 legacy 이관 후 active∪retired로 정리한다. 구버전 키는 쓰지 않는다. 저장 실패 후 다시 읽어도 별을 중복 가산하지 않는다. - [ ] 도감에 이미 발견한 등록 리소스는 활성 배치에서 빠져도 유지한다. `sanitizeCollection` 허용 목록은 전체 `JELLY_TYPES`를 사용하고, 전용 도감이 없는 구버전의 발견 추론은 기존 별이 있는 과거 콘텐츠 fixture가 아닌 경량 과거 ID→종류 메타데이터로 복원한다. 그 메타데이터는 작업10 ID 대응표에서 생성해 런타임에 포함한다. 이 작업에서는 임의 retired ID→종류 인자를 받는 테스트로 검증하고 작업10에서 실데이터를 연결한다. - [ ] 위 테스트와 `tests/LevelMigration.test.ts`, `tests/Collection.test.ts` 통과 후 커밋: `fix: 재편에서 제외된 판의 획득 기록 보존`. ## 형상 작업3~6의 공통 계약 `ExpansionShapes.ts`는 `EXPANSION_SHAPES`와 `ExpansionJellyType = keyof typeof EXPANSION_SHAPES`를 내보낸다. 기존 `DesignDef`는 JellyShapes에서 type export하여 재사용하고, ExpansionShapes는 type-only import한다. 런타임 순환 import를 만들지 않는다. JellyShapes의 union·BUILDERS에 등록한다. 각 GLSL 묶음은 `Record<해당8종키,string>`을 내보내 FIELD_GLSL에 합친다. 각 작업은 **외곽·셰이더·물성·도감·퍼즈 연결까지** 완성한다. `surface`와 baked SDF 기준은 기존 경로를 쓴다. 신규8종을 한꺼번에 테스트하되 구현은 한 종류씩 끝낸다. 물성은 기존 비슷한 종류 값을 명시적으로 선택하고 전용 물리 시스템을 추가하지 않는다. 단순색복제 대신 설계 CSV의 식별점을 재현한다. 그래픽 품질 판단은 선택된 시각 검수 단계에서 하며 이 단계의 단위 테스트로 외관 승인이라 주장하지 않는다. ## 작업3: 등분하기 쉬운 음식·사물8종 **Files:** 새 `ExpansionShapes.ts`, `shaders/expansionSplit.glsl.ts`, `tests/ExpansionSplitShapes.test.ts`, `tests/JellyMeshFuzz.expansionSplit.test.ts`; 변경 `JellyShapes.ts`, `JellyMaterial.ts`, `JellyPiece.ts`, `ui/JellyThumbnail.ts`, `tests/meshFuzz.ts`. **Interfaces:** pizza/riceball/chocolatebar/wafer/eraser/waffle/toast/giftbox의 `getJellyShape(type)`·`fieldGlsl(type)`·도감 이름. 위 공통 계약을 따른다. - [ ] RED: 8종이 등록되고 외곽 단순성·면적 양수·도감 이름·전용 색상장·유효 물성이 있는지 검사한다. 반 분할 후보 좌우대칭, 3등분 후보 일정폭, 4등분 후보 양축대칭 조건을 형상별로 검사한다. - [ ] `npx vitest run tests/ExpansionSplitShapes.test.ts` 실패 확인 후 각 형상·셰이더·등록을 구현한다. 치수는 기존 허용 바운딩 박스/국소폭·필릿 기준을 따른다. - [ ] 독립적인 테스트 눈대중 경로를 만든다. 반은 명확한 대칭축, 3등분은 긴 축 길이1/3·2/3, 4등분은 두 대칭축을 사용한다. 각 절단 뒤 실제 offset으로 경로를 이동하며 점수≥90을 단언한다. solver 풀이≥98도 별도로 확인한다. - [ ] `npx vitest run tests/ExpansionSplitShapes.test.ts tests/JellyMeshFuzz.expansionSplit.test.ts tests/JellyMesh.test.ts`와 빌드 통과. FUZZ_GROUPS 합집합 누락/중복을 확인한다. 단면별 기존 최소각1° 등 기준을 낮추지 않는다. - [ ] 커밋: `feat: 균등 분할용 젤리 8종 추가`. ## 작업4: 일상용품·음식8종 **Files:** 새 `shaders/expansionEveryday.glsl.ts`, `tests/ExpansionEverydayShapes.test.ts`, `tests/JellyMeshFuzz.expansionEveryday.test.ts`; 변경 공통 형상 등록 파일과 `tests/meshFuzz.ts`. **Interfaces:** cuttingboard/envelope/luggage/umbrella/bottle/spatula/croissant/chili, 공통 형상 계약. - [ ] RED: 8종의 등록·단순외곽·바운딩·코어·단면 연결 검사와 도마/봉투/가방의 큰 틀 하나를 담을 내부 영역 검사를 작성한다. - [ ] `npx vitest run tests/ExpansionEverydayShapes.test.ts` 실패 확인 후 외곽·전용 표면·젤리 단면·물성·영어 이름을 구현한다. 가는 손잡이·우산 연결부·고추 끝을 국소폭 기준에 맞춘다. - [ ] 위 테스트와 `tests/JellyMeshFuzz.expansionEveryday.test.ts`, `tests/JellyMesh.test.ts`, 빌드 통과. - [ ] 커밋: `feat: 일상용품과 간식 젤리 8종 추가`. ## 작업5: 곡선·재절단용 사물8종 **Files:** 새 `shaders/expansionKitchen.glsl.ts`, `tests/ExpansionKitchenShapes.test.ts`, `tests/JellyMeshFuzz.expansionKitchen.test.ts`; 변경 공통 형상 등록 파일과 `tests/meshFuzz.ts`. **Interfaces:** bell/wateringcan/boot/shovel/paddle/mug/mitten/stapler, 공통 형상 계약. - [ ] RED: 8종의 형상·재질·이름·코어를 검사하고 머그컵/장갑의 큰 잔여 조각에서 U/C 후보가 존재하는지 `createStepGuide`로 검사한다. - [ ] `npx vitest run tests/ExpansionKitchenShapes.test.ts` 실패 확인 후 구현한다. 고리·주둥이·손잡이에 실제 구멍을 만들지 않고 설계 식별점과 넓은 몸체를 유지한다. - [ ] 위 테스트와 `tests/JellyMeshFuzz.expansionKitchen.test.ts`, `tests/JellyMesh.test.ts`, 빌드 통과. - [ ] 커밋: `feat: 곡선과 재절단 사물 젤리 8종 추가`. ## 작업6: 회전판·휘둘러 베기용 사물8종 **Files:** 새 `shaders/expansionLeisure.glsl.ts`, `tests/ExpansionLeisureShapes.test.ts`, `tests/JellyMeshFuzz.expansionLeisure.test.ts`; 변경 공통 형상 등록 파일과 `tests/meshFuzz.ts`. **Interfaces:** tape/battery/remote/padlock/headphones/popsicle/pretzel/macaron, 공통 형상 계약. - [ ] RED: 8종 기본 품질과 아이스바/프레첼 실제 직선 절단, 마카롱 4조각 절단의 메시 가능성을 검사한다. 자물쇠·헤드폰은 구멍 없는 단순 외곽이어야 한다. - [ ] `npx vitest run tests/ExpansionLeisureShapes.test.ts` 실패 확인 후 구현한다. 얇은 고리를 만들지 않고 넓은 젤리 연결부와 표면 무늬로 표현한다. - [ ] 위 테스트와 `tests/JellyMeshFuzz.expansionLeisure.test.ts`, `tests/JellyMesh.test.ts`, 빌드 통과. - [ ] 커밋: `feat: 회전판과 휘둘러 베기 젤리 8종 추가`. ## 작업7: 천천히 썰기 음식3종 **Files:** 변경 `JellyShapes.ts`의 SlowFoodType/SLOW_TYPES/SLOW_TINT, `SlowFood.ts`, `JellyMaterial.ts`, `JellyPiece.ts`, `ui/JellyThumbnail.ts`; 새 `shaders/daikon.glsl.ts`, `baguette.glsl.ts`, `swissroll.glsl.ts`, `tests/ExpansionSlowFood.test.ts`; 필요 시 변경 `SlowFoodGlsl.ts`. 기존 `tests/SlowFood.test.ts`, `SlowFoodMesh.test.ts`, `SlowFoodMaterial.test.ts`, `JellyMeshFuzz.slowFood.test.ts` 확장. **Interfaces:** 기존 `getSlowFoodProfile(type: SlowFoodType): SlowFoodProfile`에 daikon/baguette/swissroll 추가. 우선 기존 capsule/ellipsoid 표현을 사용하고 단면 중심·두 반경·halfLength를 CPU/GLSL이 공유한다. - [ ] RED: 세 종류의 3·4·5회 절단 위치에서 단면이 유효하고, width0.25의 5회 절단이 외곽 안에 있으며, 새 조각의 캡·단면이 메시와 일치하는지 검사한다. - [ ] `npx vitest run tests/ExpansionSlowFood.test.ts` 실패 확인 후 프로필·색상장·SLOW_TINT·등록을 구현한다. 롤케이크는 단면 나선, 바게트는 빵결·칼집, 무는 흰 과육과 길이 방향 굵기 차이가 읽혀야 한다. 구조를 크게 늘리는 프로필 확장이 필요하면 비용을 먼저 보고한다. - [ ] 위 테스트, 기존 SlowFood 세 테스트, `tests/JellyMeshFuzz.slowFood.test.ts`, 빌드 통과. 32개 일반형상과 함께 새35종 전부가 레지스트리에 있는지 검사한다. - [ ] 커밋: `feat: 무 바게트 롤케이크 썰기 젤리 추가`. ## 작업8: Split·정적 가이드 콘텐츠와 눈대중 기준 **Files:** 새 `src/stages/StageContentHelpers.ts`, `content/Split200.ts`, `content/Guide200.ts`, `tests/ExpansionSplitLevels.test.ts`, `tests/ExpansionGuideLevels.test.ts`; 변경 `StageDefinitions.ts`(헬퍼 추출만), `tests/SplitLevels.test.ts`, `tests/SplitExpansion.test.ts`; 새 `tests/fixtures/split-eyeball-paths.ts`. **Interfaces:** `SPLIT_200_STAGES: readonly StageDefinition[]` 33판, `GUIDE_200_STAGES: readonly StageDefinition[]` 33판. ID는 작업10 규칙으로 이 작업에서 발급·원장에 기록하며 임시 ID를 활성화하지 않는다. `splitEyeballPaths(level: LevelDefinition): [Vec2,Vec2][]`는 테스트 전용이며 solver를 호출하지 않는다. 작업10의 `2026-10-29-stage-200-id-map.csv`를 이 작업부터 작성하고 작업9에서 추가한 뒤 작업10에서 전체 검증·확정한다. 헬퍼 추출 시 기존 StageDefinitions의 함수 이름·인자 타입·반환 타입을 그대로 export하며, 새 범용 팩토리 계층은 만들지 않는다. - [ ] RED: 각 배열의 모드·젤리·수량이 CSV 해당행과 일치, Split 가이드0/목표2·3·4/획1·2·3, 혼합은 직선·곡선 모두 포함을 검사한다. - [ ] `npx vitest run tests/ExpansionSplitLevels.test.ts tests/ExpansionGuideLevels.test.ts` 실패 확인 후 기존 헬퍼를 재사용하고 판별 명시 좌표를 작성한다. 1·2·3번째 소개 판은 설계 획 수/난이도 상한을 따른다. 새 혼합 아보카도는 이전 곡선 판 ID를 유지하지 않는다. - [ ] Split33판 모두 독립 눈대중 경로≥90, solver≥98, 획 역순·빗나감 비소모·초과 조각 실패·4조각 조기 완료·라벨 합계100을 검증한다. 기존 함정 의무 테스트는 과거 스펙 근거와 함께 새 기준으로 교체한다. 사물 자체의 메시/풀림 회귀는 유지한다. - [ ] 가이드33판을 StageSession으로 실제 완료한다. 정적 가이드는 `playPerfect`, 혼합은 실제 `tool` 허용 규칙에 맞춰 제출하고 3별·목표 조각 수를 확인한다. 정답 가이드가 외곽 안에서 교차하지 않고 충분한 여유가 있는지 확인한다. - [ ] 위 테스트와 `tests/GuideLevels.test.ts`, `tests/SplitScoring.test.ts`, 빌드 통과 후 커밋: `feat: 눈대중 Split과 가이드 66판 구성`. ## 작업9: 회전판·재절단·타이밍 모드 콘텐츠 이 작업은 두 검증 가능한 단위로 순서대로 진행한다. 같은 리뷰에서 134판을 한 번에 승인하지 않는다. ### 9A 회전판·재절단66판 **Files:** 새 `src/stages/content/Motion200.ts`, `tests/ExpansionMotionLevels.test.ts`; 변경 필요 시 `CurveStages.ts`는 헬퍼 공유만 한다. **Interfaces:** `MOTION_200_STAGES: readonly StageDefinition[]` 66판. 직선·곡선·혼합×회전/재절단 각11판. - [ ] RED: CSV66행·분류·속도상한 일치, 첫 가이드 및 각 실제 큰 잔여 조각에서 다음 가이드가 존재, 정·역방향 입력 모두 완료를 단언한다. - [ ] `npx vitest run tests/ExpansionMotionLevels.test.ts` 실패 확인 후 명시적 단계/각도를 작성한다. 현재 `prepareNextStep()` 생성기를 끝까지 진행해 가이드 인계를 검증한다. 퇴화한 W/S를 통과시키기 위해 임계값을 낮추지 않는다. - [ ] 모든 판에서 목표 조각 수·정상 완료·3별 정답 입력을 확인한다. 기존 `tests/CurveStages.test.ts`의 원본 실패/취소 회귀는 fixture로 유지한다. 이전 최소2별 기준보다 새 정답 경로3별 기준이 어려우면 스펙을 몰래 완화하지 말고 원인을 보고한다. - [ ] 관련 Spin/Recut/CurveGuides 테스트·빌드 통과 후 커밋: `feat: 회전판과 재절단 66판 구성`. ### 9B 타이밍·압착·찾기68판 **Files:** 새 `src/stages/content/Timing200.ts`, `tests/ExpansionTimingLevels.test.ts`; 변경 `src/stages/OddJelly.ts`(기존 고정 생성식을 인자로 재사용), 필요 시 StageContentHelpers. **Interfaces:** `TIMING_200_STAGES: readonly StageDefinition[]` =stamp9+slow9+swipe22+bear18+odd10. `createOddJellyDefinition(seed: number, targetCount: 3|5|7|9, strength: number): OddJellyDefinition`를 기존 배열·배치 로직에서 추출; 기존4판 결과를 유지하는 회귀 테스트를 둔다. - [ ] RED: 각 모드의 판 수·종류·소개 위치를 검증한다. swipe launches가 같은 판 내 한 종류인지와 다른 판에서 재사용되지 않는지 검사한다. odd10판은32개·정답수/변형 강도 유효, 같은 seed면 배열 동일함을 단언한다. - [ ] `npx vitest run tests/ExpansionTimingLevels.test.ts` 실패 확인 후 판별 자리·궤적·색 배열·seed를 명시한다. 설계 표의 상한을 따른다. 속도만 바꾸어 별도 모드로 계산하지 않는다. - [ ] 모든 stamp 자리 정렬3별·슬롯 간 여유, slow 3/4/5칼질 완료·리듬3별, swipe 모두/4조각 풀이, bear 보호색 미압착/전체압착, odd 정확한 정답 선택 완료를 기존 순수 세션 테스트 방식으로 검사한다. - [ ] 관련 Stamp/Slow/Swipe/BearCrush/OddJelly Levels·Session 테스트와 빌드 통과 후 커밋: `feat: 타이밍 압착 찾기 68판 구성`. ## 작업10: ID 대응표와 후보200판 조립 **Files:** 새 `src/stages/Stage200.ts`, `src/stages/retired-stage-jellies.json`, `docs/superpowers/plans/2026-10-29-stage-200-id-map.csv`, `tests/ExpansionCatalog.test.ts`; 변경 `retired-stage-ids.json`, 작업8~9 콘텐츠 파일. **Interfaces:** `STAGE_200_CONTENT: readonly StageDefinition[]` 200판. 대응표 열은 oldStageId/newStageId/newLevel/status/reason, status는 retained/retired/new. 과거 종류 메타데이터는 `Record` 형식이며 swipe는 모든 발사 종류를 담는다. - [ ] ID 규칙: 기존 fixture와 내용 전체가 동일한 판만 옛ID 유지, 그 외는 stage-085부터 미사용 번호를 연속 부여한다(실행 시 이미 새ID가 있으면 현재 최대 이후로 조정). 삭제된061~063도 재사용하지 않는다. old81판은 각각 retained 또는 retired로 한 번씩 원장에 기록한다. - [ ] RED: 후보200판의 ID 중복·삭제ID 재사용·원장 누락·같은ID 내용 변경을 거부하는 테스트를 작성한다. retained는 fixture deep equality, retired는 활성과 서로소. CSV 행의 mode와 levelJellyTypes 및 묶음 위치가 실제 콘텐츠와 일치해야 한다. - [ ] `npx vitest run tests/ExpansionCatalog.test.ts` 실패 확인 후 네 모듈 배열을 합치고 원장을 완성한다. `Stage200`는 표시 순서를 정하지 않으며 실제 순서 배열은 작업11에서 stage-order 한 곳에 쓴다. 테스트 기대 번호는 확정 CSV로 대조한다. - [ ] 작업2의 과거 도감 복원에 `retired-stage-jellies.json`을 연결한다. 생산 메타데이터는 전체 과거 레벨 정의를 번들에 복제하지 않고 ID·종류만 담는다. 제외된 ID의 기록 보존 실데이터 검사를 수행한다. - [ ] `tests/ExpansionCatalog.test.ts`·전체 신규 콘텐츠 테스트·빌드 통과 후 커밋: `feat: 200판 콘텐츠와 과거 ID 대응 확정`. ## 작업11: 실제 활성 편성과 테스트의 번호 의존 갱신 **Files:** 변경 `src/stages/StageDefinitions.ts`, `stage-order.json`, `LevelLabels.ts`, `LevelBadge.ts`(필요한 표시만), `tests/StageCatalog.test.ts`, `tests/LevelLabels.test.ts`, `tests/SplitExpansion.test.ts`, `tests/CurveStages.test.ts`; 새 `e2e/helpers/content.ts`. 변경 `e2e/*.spec.ts` 중 옛번호로 콘텐츠를 선택하는 파일. **Interfaces:** `STAGES = STAGE_200_CONTENT`로 활성화, 기존 `LEVELS/getLevel` 계약 유지. `levelNumberForStage(stageId: string): number`, `levelNumberForVariant(variant: StageVariant, occurrence = 0): number`는 E2E 전용으로 유일 후보/존재를 확인하고 실패하면 명시적으로 throw한다. - [ ] RED: 활성 LEVELS.length=200, 소개/회전/slow 위치, 199→200→전체완료, 새19분류의 영어 배지·최초 소개·묶음 내 배지 반복 정책을 검사한다. 디버그 번호는 현재 표시 순서여야 한다. - [ ] `npx vitest run tests/StageCatalog.test.ts tests/LevelLabels.test.ts` 실패 확인 후 활성 카탈로그와 순서를 한 작업으로 전환한다. 모든 등록 ID를 정확히 한 번 배치하는 orderStages 검증은 그대로 유지한다. - [ ] 기존 테스트를 전수 검색해 콘텐츠 검사와 배치 검사를 분리한다. `tests/contentLevels.ts`는 과거 품질용, 신규 활성200판은 Expansion 테스트용이다. 과거78드릴/80확성기 등 구체적 회귀는 fixture로, 새 표시번호 기대는 CSV로 바꾼다. 맞지 않는 검사를 그냥 삭제하거나 skip하지 않는다. - [ ] `e2e`의 openLevel 숫자·상수·순회상한·저장 fixture를 ID/variant 선택자로 갱신한다. 특정 사물 성능 테스트는 variant만으로 다른 젤리를 고르지 말고 해당 사물과 모드를 명시한다. 더 이상 활성에 없는 조합은 동등한 무거운 활성 조합을 선택하고 기준 변경 이유를 기록한다. - [ ] `npm test`, `npm run build` 통과 후 커밋: `feat: 200판 편성 활성화와 번호 기반 검사 갱신`. ## 작업12: 전체 기능·수명주기·성능 회귀 **Files:** 새 `e2e/stage-200.spec.ts`, `e2e/helpers/play-content.ts`; 변경 `e2e/progress.spec.ts`, `shop.spec.ts`, `shader-compile.spec.ts`, `debug-api.spec.ts`, `helpers/progress.ts`, 필요 시 `perf.spec.ts`의 콘텐츠 선택. **Interfaces:** 테스트 전용 `playContentToResult(page: Page, level: LevelDefinition): Promise`는 기존 모드별 입력 도우미를 선택한다. 결과를 직접 주입하거나 디버그로 성공 상태를 만들지 않는다. 모든 진입은 `openLevel`과 ready/canDraw 및 첫 프레임 대기를 따른다. - [ ] RED: 실제200판에 진입하여 실제 입력으로 정상 결과와 다음 판을 확인하는 모바일 테스트를 작성한다. 별도의 해상도별19종 대표 테스트는 취소→재시도, 숨김→복귀, 품질변경, Home, 도구 전환을 검사한다. 솔버는 테스트 전용이며 제품 번들에는 들어가지 않는다. - [ ] RED: old81판 저장·별·장비 구매 fixture → 첫 로드/새로고침2회/새 판클리어에서 잔액보존, retired이어하기→첫미완료, 활성이어하기 유지, 200판 전체완료를 검사한다. 숫자구버전 이관도 별도 유지한다. - [ ] `npm run build` 후 관련 E2E를 실행하고 실패를 재현한다. 코드 또는 테스트 선택자를 고친다. 실행 중 dist를 덮어쓰지 않는다. - [ ] `npx playwright test e2e/stage-200.spec.ts e2e/progress.spec.ts e2e/shop.spec.ts --project=mobile-chromium` 통과. 데스크톱/WebKit에는19종 대표·저장/종료 경계를 실행한다(전체200판 중복 실행은 기본 아님). - [ ] 신규35종과 새 조합 대표의 low/high 셰이더 컴파일 검사, 기존 입력경로 `inputMeshBuilds=0`, 초기화/전환/폐기 메모리 회귀를 검사한다. 느린 신규 형상은 기존 성능 기준에 따라 원인을 측정하고 필요할 때만 A/B한다. 형상 수가 늘었다고 임계값을 완화하지 않는다. - [ ] `npm test`, `npm run build`, `npm run e2e:mobile` 최종 통과. 부하 실패는 파일 단독 재실행 결과와 최초 실패를 함께 기록한다. 알려진 slow 화면여백 등 기존 실패는 새 성공으로 덮지 않는다. - [ ] 커밋: `test: 200판 플레이와 저장 수명주기 검증`. ## 작업13: 선택한 시각 검수·문서·독립 최종 리뷰 **Files:** 기록 `docs/superpowers/plans/2026-10-29-stage-200.md`, `PROGRESS.md`, `docs/agent-reference/platform-levels.md`; 관련 기존 Split 근거 스펙에 새 설계 supersedes 링크 추가. 캡처·스크립트는 `.playwright-mcp/stage-200/`. - [ ] 시각 검수 선택을 기록한다. **기초 구현+사용자 시각 조정**이면 반복 캡처 비교·취향 수정을 하지 않고 새35종 외형/단면, Split33판 눈대중 가독성, 소개 순서와 모드 전환을 사용자 확인 대상으로 인계한다. 필수 셰이더·메시·기능 검사는 생략하지 않는다. - [ ] **전체 시각 검수** 선택이면 모바일390×844·데스크톱1280×800에서 소개57판 시작114장, 신규35종 시작·동작/단면·결과210장, Split33판의 시작·정답결과132장을 계획한다. 중복은 재사용하여 기본 최대456장 이내로 한다. 다른 모드에서 재사용하는35종/기존형상은 모든 화면을 반복하기보다 기능 검사에서 발견한 특이조합을 추가한다. 범위 증가 전 안내한다. 작성자가 캡처·비교·수정하고 독립 리뷰어가 검토한다. - [ ] 외형 변경 후 해당 메시·콘텐츠·E2E를 다시 검증한다. 기초 방식이면 시각 완료로 기록하지 않는다. 전체 방식도 모든200판의 모든 상태/해상도를 확인한 것으로 과장하지 않는다. - [ ] PROGRESS에 활성200판·형상 수·검증·남은 기기/시각 항목을 갱신한다. 옛 Split 함정 의무와 번호표에 새 기준을 연결한다. DocsSite 공개 기획서 갱신은 별도 요청 시 프로젝트 docs-site 스킬로 수행하며 이번 계획에 자동 포함하지 않는다. - [ ] 최종 독립 리뷰를 `gpt-6-astra / medium`으로 실행한다. 설계·실제 CSV대조·ID원장·저장/잔액·신규형상 품질·선택된 시각 범위를 검토한다. 기초 방식의 사용자 시각 검수를 완료로 판정하지 않도록 리뷰어에게 명시한다. - [ ] 중요 지적 수정 후 관련 검사와 필요 범위 모바일 회귀를 실행하고 최종 커밋: `docs: 200판 구현과 검증 결과 정리`. main푸시/배포는 하지 않는다. ## 계획 자체 검토 결과 규칙→담당: 묶음/소개/신규수량=1·10·11, 모양35종=3~7, Split=3·8, 플레이200판=8~12, 과거기록=2·10·12, 시각선택/최종리뷰=13. 모든 구현 단계는 실패 재현→최소 구현→관련 검증→커밋 순서다. 작성자는 파일 경로·기존 함수 계약·배치 합계와 리뷰 위험5개에 대한 검사 소유 작업을 확인했다. 계획 작성 중에는 제품 테스트를 수행하지 않았고 편성 검사만 재실행했다. ## 실행 기록 - 실행 방식: Native (사용자 승인). - 시각 검수: 전체 (사용자 승인, 최대456장 계획, 중복 재사용). - 시작 커밋:826c9b4. 기준선:161파일2,305테스트·빌드 통과. - 구현·기능 검증·시각 검수·최종 독립 리뷰 완료. 상세 증거는 작업 ledger와 아래 완료 기록에 정리한다. ### 구현 중 확정한 세부 조정 - 기존 헬퍼 추출/odd 런타임 팩토리는 추가하지 않고 오프라인 작성기로 세션 검증한 명시적 데이터를 저장했다. 런타임 탐색·랜덤 배치는 없다. - 181판3등분의 원형 컵받침은 길이등간격 풀이70.07점으로 부적합해 장난감로봇으로 교체했다. 전체 묶음·신규수량 조건은 유지한다. - 과거 콘텐츠 회귀는81판 fixture로 고정하고, 활성200판은 별도 편성/세션/실제입력 검사로 검증한다. 폐기된 E2E 사물/모드 조합은 같은 규칙을 가진 활성ID로 명시 대응한다. - 도감 발견 기록의 저장뿐 아니라 목록 표시도 활성+발견 종류를 합쳐 유지한다. ### 독립 리뷰 및 실행 실패 보완 기록 - Astra Medium: 웨하스 3단 층·마카롱 껍질/크림 단차·접힌 우산 손잡이 누락과 성능 전환 테스트의 옛 번호를 지적했다. 입체 메시 프로필과 손잡이 외곽을 추가하고 실제 활성 전환으로 수정 중이다. - 192판 실제 터치에서 재절단 확대 후 곡선 끝이 390px 화면의 -9.79/399.79px에 놓이는 문제를 재현했다. 반경 배율에 가로/깊이 가이드 범위 제한(반폭 1.05/반깊이 1.3)을 추가한다. 최대 확대 1.8, 포커스 시간 0.4초와 입력 경로는 유지한다. - 수정 전 전체 단위 2,847개·빌드 통과, 모바일 전체200판 최초198통과/2실패(33판 기록 충돌 재실행 통과,192판 수정 중). 데스크톱 후반20판 서버 종료 실패는 서버 독립 후20판 재실행 통과. 최종 통과로 집계하지 않는다. - 2차 보완: 입체22종의 원본좌표 기반 높이·적층 프로필과 우산 손잡이를 추가했다. 32종×9방향의 원본/절단 메시 검사33개 통과(닫힘·양수부피·뒤집힘0·퇴화0·최소각1도 초과). 기존 품질 한계를 완화하지 않았다. - 추가 검수 범위: 웨하스·마카롱·테이프 홈 미리보기 양해상도6장을 사용자에게 알린 뒤 촬영했다. 소개 시작은 가이드 등장0.75초 이후, 결과는900ms 정확도 카운트업 이후 캡처하도록 수정했다. - slow57→stamp58 전환의 동기 셰이더 링크4회를 단독 재현했다. StampMode.keepShaders에서 틀/착지 그림자를 미리 보관하여2회로 줄였고 기존상한3회를 통과했다. - 바게트는 공유 SlowFoodProfile의 scored 옵션으로 실제 깊이0.022의 사선 홈을 추가했다. 원본·분리 원판·남은 몸체에 같은 표면/법선/내부 판정을 쓰며 해당47개 단위검사를 통과했다. 최신 빌드에서112판과 틀 찍기123판을 양해상도 실제 입력으로 재검증하여4개 모두 통과했다. ### 최종 검증 집계(2026-10-30) | 검사 | 결과와 범위 | |---|---| | 단위 | 178파일 2,883개 통과. 바게트 입체 칼집 포함 | | 빌드 | TypeScript strict 및 Vite 빌드 통과 | | 모바일 Chromium | 691통과·21사전 제외. 활성200판 실제 입력 및 기존 기능 회귀 포함 | | 데스크톱 Chromium | 226통과·92범위 제외. 소개/신규/Split 실제 입력·수명주기·저장·상점 | | WebKit | 67통과·251범위 제외. 19종 대표와 저장·상점·수명주기 | | 메모리 | 200판을3회(600회 전환) 반복, 렌더러 자원 증가 없음 | | 최종 형상 보완 | 112바게트·123봉투 양해상도 실제 입력4통과, 입체 확인 홈6통과 | | 시각 | 소개57 시작·신규35 시작/동작/결과·Split33 시작/결과 양해상도. 중복 제외406장+추가 홈6장=412장 검토, 캡처 누락0. UI 겹침·단면 단절·가이드 가림 미발견 | | 성능 | 데스크톱65개 통과+신규 입체/최초 곰5개 재검증 통과. 모바일65통과/112판1실패 후 해당 판3회 및 영향5개 재검증 통과. 모든 검사 사례의 최종 통과를 확인했으며 단일 전체 재실행 통과로 합산하지 않음 | | 독립 리뷰 | gpt-6-astra / medium, 기존 지적 모두 해소·중요한 잔여 코드 결함 없음. 첫 결과 프레임 측정과 별도 곰 전환 기록도 승인 | - 후속 바게트 변경은 전체 단위에 포함하고 브라우저는 영향 있는112판을 새 빌드로 재실행했다. 틀 찍기 예열도 실제 조각과 같은 입체 메시 경로로 정리하여123판을 재검사했다. - 최초 모바일 회귀는444통과/46실패/21제외였다. 옛 번호·조각 수·속도·저장 fixture 의존을 현재 고유ID로 이관했고, 실제192판 카메라 잘림과57→58 셰이더 준비 문제를 수정한 뒤 최종691통과했다. 실패 기록은 작업 로그에 보존한다. - 이 브라우저 검증은 실제 Android/iOS 앱 검증을 대신하지 않는다. 네이티브 빌드·기기·시뮬레이터 실행 및 운영 배포는 수행하지 않았다. 이번 작업이 생성한 시뮬레이터 복제본은 없다. ### 성능 검사에서 확인한 측정 경계 - 최초 데스크톱 성능63통과/2실패. 틀 찍기4회 사례가 실제12판(2회)을 열던 오류를 고유ID 대응45사례로 고쳤다. - 131판 웨하스의 준비18.1ms는 단독3회에서도12~19ms로 재현됐다. 임시 단계/호출스택 계측에서 웨하스 절단은 측면0.4~0.9ms·캡1.4~2.6ms·SDF0.1~1.6ms였고, 초과는 결과 다음 프레임에 시작한132판 곰의 최초 준비였다. 임시 계측은 제거했다. - Split33판은 첫 결과 프레임에 해당 판의 누적 준비/획 비용을 고정한다. 이후 판의 준비 비용을 별도 전환 검사와 구분하며8ms/16ms 기준은 유지한다. 수정 후131판·찍기4회 각3회(총6개) 통과. - Astra Medium은 이 측정 경계 수정을 승인했다. 다만 차가운 곰 최초 준비의12~19ms는 해결됐다고 주장하지 않으며131→132 별도 전환 검사와 한계로 남긴다. 이는 등록35종 입체 변형이나 웨하스 절단 비용이 아니다. - 독립 리뷰에서 중요한 잔여 코드 결함은 없었다. 독립 시각 검토는 소개 컨택트와 수정 대상 원본이며, 계획 범위 전수 검수는 구현자의 주검수 결과다. ### 최종 보완 및 남은 한계 - 모바일112판의15.5ms도 다음113판 odd의 곰 준비와 혼합된 값이었다. 첫 결과 프레임 고정 계측을 공통 함수로 추출해 신규 입체4판에도 적용했다. 바게트의 실제 준비는 단독3회1.5~1.9ms, 획1.2~1.3ms로 통과했다. 임계값과 실제 터치 입력은 유지한다. - 최초 곰131→132 별도 검사: 데스크톱 로딩6.1ms/준비16.5ms/최대 프레임 간격73.0ms/동기 컴파일 막힘2회, 모바일 로딩6.7ms/준비16.8ms/최대 프레임 간격73.1ms/막힘2회. 로딩16ms 기준은 통과하지만 **차가운 곰 준비와 전환 중 순간 지연은 미해결 성능 한계**다. 새 형상의 절단 비용과 구분하며 정상/부드러운 전환을 모두 검증했다고 주장하지 않는다. - 데스크톱 신규 입체4판+최초 곰 전환5개 재검증 통과. 모바일 영향5개 재검증도 통과했다. 마지막 전체 단위178파일2,883개·타입 검사 통과(2026-10-30 00:27). - 최종 작업은 `codex/stage-200`에 보존한다. 공통 등록 파일을 함께 검증하기 위해 형상3~7은 한 커밋, 콘텐츠8~10은 한 커밋으로 묶었으며 활성화/검증/문서 보완도 검증 완료 뒤 함께 커밋한다. 계획의 세부 커밋 제목과 파일 분할은 실행 결과에 맞춰 조정했다. - 성능 검사의 최초 실패·재실행 로그, 원본 캡처 및 컨택트는 작업 worktree의 `.superpowers/sdd/2026-10-29-stage-200/`와 `.playwright-mcp/stage-200/`에 보존한다(gitignore). 운영 main·원격은 변경하지 않았다.