← 자료실/sprintsLOW

Sprint 03 (W1+W2 통합) — 드라이런 리허설 (2026-04-21)

sprints/sprint-03-dry-run-2026-04-21.md

Sprint 03 (W1+W2 통합) — 드라이런 리허설 (2026-04-21)

작성: 2026-04-21 (code-lead) · 대상: designs/sprint-03-idle-research.md v1.0 APPROVED §7 TodoWrite 10건 (#01~#10) 문서 상태: v1.0 목적: 2026-06-01 Sprint 03 실제 킥오프 전 실행 시뮬레이션으로 방치 시스템 전체 블로킹·회귀 리스크 사전 노출 범위: 실행 시뮬레이션만. 실제 코드 0줄 (Unity6/ 건드리지 않음). 문서만 작성 감사 기준: sprint-03-idle-research.md v1.0 APPROVED (5 결정 + CP-I1~11 + 10 TodoWrite) · sprint-02-w2-prestige-tech-sketch.md v1.0 APPROVED · d8-stat-key-validation-2026-04-21.md v1.0 APPROVED · shared-checkpoints.md v1.0 · sprint-01-dry-run-2026-04-21.md v1.0 · sprint-02-w1-dry-run-2026-04-21.md v1.0 원칙: Sp03 설계 결정 변경 제안 금지 (v1.0 APPROVED 고정). 감지·권고만. Sp00+Sp01+Sp02 W1 드라이런 학습 누적 반영. 실측 검증 원칙 (Sp02 W1 R-DR-06 FP 교훈).

§0. TL;DR (PD 1분)

항목값
판정Yellow (킥오프 GO 권장. 사전 완화 5건 + Sp02 W2 의존 4건 확정 선결 조건)
TodoWrite 대상 태스크 수10건 (#01~#10) — 설계 §7 그대로
예상 공수 합계 (설계 기준)13.25h (W1 5건 7.25h + W2 5건 6.0h)
실공수 예상 밴드14.5~18.5h (회귀 패치 1.0~1.5h + OnApplicationPause 실험 0.5~1.5h + 하이브리드 결정론 튜닝 0.5~2.0h)
최상위 리스크 3건R-DR-07 (하이브리드 결정론) / R-DR-08 (OnApplicationPause 플랫폼 차이) / R-DR-09 (Sp02 W2 4의존 GetIdleCapHours 헬퍼 미도착) — §0.2
Sp00+Sp01+Sp02 W1 회귀 4패턴 + Sp03 신규 4패턴 누적§12 매트릭스 — CS0579 피로도 Peak (Data 7번째 SO?) / JSON schemaVersion 마이그 신규

§0.1 결론 한 줄

Sp03 TodoWrite 10건은 Happy Path에서 14.5~18.5h 밴드에서 완주 가능. 설계 6.5~9.0h(Sp01) / 13.5~17.0h(Sp02 W1) 대비 누적 복잡도 증가 — W1+W2 통합이 원인. 주 블로킹 축 3개: (1) #03~#04 하이브리드 옵션 C Phase 1(Bulk) + Phase 2(Tail 3,600초 리플레이) RNG 결정론 일관 — CP-M19 "List 인덱스 순" 엄수 (2) #05 IdleTicker MonoBehaviour 싱글턴 + OnApplicationPause 훅 — Unity 에디터 Play Mode 에서는 실제 Suspend 재현 불가 → 실기 테스트 Phase 3+ 이월 명시 (3) #06~#07 Sp02 W2 의존 4건(GetIdleCapHours 헬퍼 / BuildSummary.IdleRateMul / AddSeasonalPp / OnIdleReward 핸들러 예약) 전부 착수 시점에 Green 상태여야 병목 0. #08 테스트 7건 결정론 재현은 #03~#04 설계부터 List 순회 강제가 전제.

§0.2 최상위 리스크 3건

  1. R-DR-07 [High · 하이브리드 계산 결정론 실패] — #03 (Bulk 집계) + #04 (Tail 3,600초 리플레이) 가 동일 입력(elapsedSec, maxFloor, rngSeed)에 대해 에디터 빌드 vs 모바일 IL2CPP 빌드 사이에서 다른 드롭 결과 생성 가능. 근본 원인: DropRateTableSO._lookup Dictionary 순회가 Sp01 W2에서 (floorBucket, mobKind, grade) 복합키 구조 — Dictionary 순회 순서는 .NET Mono vs IL2CPP 사이에서 비결정적. CP-M19는 "리플레이 iteration 은 rows List 사용" 명시하나 실제 구현 시 습관적으로 _lookup 순회 유혹. 테스트(#08) Edit Mode에서 Green이어도 모바일 빌드에서 Red. Sp03 범위 내 실기 테스트 불가 → Edit Mode 테스트에서 List 인덱스 순 강제 assert 필수.
  1. R-DR-08 [High · OnApplicationPause Unity 6 + 모바일 플랫폼 차이] — 설계 §1.4 / §8.1 / CP-I8 명시된 위험. iOS 30초 Suspend 이후 OnApplicationPause(true) 호출 보장 약함. Unity 에디터 Play Mode 에서 OnApplicationPause 는 포커스 전환(Application.isFocused) 시에만 발화 — 실제 모바일 Suspend 시나리오 재현 불가. Smoke Test(#09)는 "시뮬 모드로 10h elapsed 입력" 이므로 OnApplicationPause 실호출 경로 미검증. Sp03 범위에서 모바일 실기 테스트 불가 확정 → CP-M22 "실기 검증 Phase 3+ 이월" PR 본문 명시. 드라이런 추가 권고: Editor Play Mode 수동 시나리오 1개 정의 — "Play 중 Application.Quit() → 재Play → FlushCatchUp 로그 확인".
  1. R-DR-09 [Medium-High · Sp02 W2 의존 4건 중 GetIdleCapHours 헬퍼 미도착 리스크] — Sp02 W2(2026-05-18~2026-05-31) 중 PrestigeCalculator.GetIdleCapHours(in PlayerPrestigeState) 헬퍼 신설은 d8 검증 §3.3 CK-3 체크리스트 항목. Sp02 W2 내부 우선순위가 Calculate Level / BuildSummary / MigrateSeasonalToPermanent 보다 낮음 — 말미에 밀릴 여지. Sp02 W2 회고(2026-05-31) 시점에 GetIdleCapHours 미구현이면 Sp03 #03 하이브리드 cappedSec = min(elapsed, GetIdleCapHours(state) × 3600) 호출부 작성 불가. 사전 완화 A1 (킥오프 D-1): Sp02 W2 PR 목록에서 GetIdleCapHours 헬퍼 머지 확인 필수. 미머지 시 Sp03 킥오프 오전 긴급 선행 (0.5h, #06 이전 삽입).
[2026-04-21 설계 스펙 해소 — 부분 FP] Sp02 W1 R-DR-06 FP 교훈 적용, 4건 전수 설계 문서 실측 완료: (1) GetIdleCapHours(in PlayerPrestigeState) = d8-stat-key-validation-2026-04-21.md:169 public static int 시그니처 명시 ✅ (2) BuildSummary 4필드(IdleRateMul/GoldMul/ExpMul/DropMul) = sprint-02-w2-prestige-tech-sketch.md:230·235 SummarizeChannel + public readonly struct BuildSummary 명시 ✅ (3) AddPermanentPp/AddSeasonalPp(long) = sprint-02-w2-prestige-tech-sketch.md:193-194 public void 2건 ✅ (4) PrestigePpAccumulator.GrantIdleDrip + OnIdleReward 구독 = sprint-02-w2-prestige-tech-sketch.md:381·386·390 §6.3 스케치 + public sealed class ✅. 부분 FP 확정: 설계 스펙 누락 우려 = False Positive (전 4건 APPROVED 문서에 시그니처 존재). 잔여 위험 유지: 2026-05-31 Sp02 W2 실 PR 머지 여부 확인 = A1 여전히 필수(설계 → 구현 drift 가능성). 리스크 재분류 Medium-High → Low-Medium. #06 긴급 선행 가능성 거의 0.

§0.3 사전 완화 액션 5건 (버퍼 기간, 2026-05-31 이전)

  1. A1 [45분, D-1 2026-05-31] — Sp02 W2 의존 4건 전수 확인: (1) PrestigeCalculator.GetIdleCapHours(state) 헬퍼 머지 확인 + 시그니처 public static int GetIdleCapHours(in PlayerPrestigeState state) 정확 일치 (2) PrestigeCalculator.BuildSummary 4필드(IdleRateMul/GoldMul/ExpMul/DropMul) 선언 확인 + d8 §2.1 매핑 테이블과 일치 (3) PlayerPrestigeState.AddPermanentPp(long) / AddSeasonalPp(long) public 접근자 확인 (CP-M21) (4) PrestigePpAccumulator 클래스 + GrantIdleDrip(long elapsedSec, int maxFloor) / OnStageCompleted / GrantBossBonus 메서드 시그니처 머지 확인. 미완료 시 킥오프 GO/NO-GO 판정 재검토.
  1. A2 [30분, D-1] — 하이브리드 결정론 체크리스트 사전 메모: (1) IdleRewardCalculator.Calculate 내부에서 DropRateTableSO._lookup Dictionary 순회 금지 (2) Tail 3,600초 리플레이 iteration은 DropRateTableSO.rows List 인덱스 순 강제 (3) new System.Random(rngSeed) 명시 생성, Unity.Random 사용 금지 (4) Grade 순회 시 foreach (Grade g in Enum.GetValues(typeof(Grade))) 금지, for (int i=0; i<6; i++) 고정. #03/#04 작성 시 매 메서드 상단 주석 1줄.
  1. A3 [20분, D-1] — Sp00+Sp01+Sp02 W1 드라이런 3개 통합 A3 체크리스트 v3: Data 어셈블리 신규 파일 체크 4항목 + PpChannel Runtime 배치 1항목 + Sp03 신규 1항목 = "⚠️ IdleTicker.cs 는 Assets/_Project/Scripts/Runtime/Idle/ 배치. namespace GodBlade.Runtime.Idle 명시. Runtime asmdef Unity.ResourceManager 참조 유지 확인" (CP-M01 재강조).
  1. A4 [15분, D-1] — TSV 경로 해석 메모 재활용: Sp03는 신규 TSV 소비 없음 (방치는 계산 로직만, Sp01/Sp02 W1 TSV 소비자 확정). 단 Smoke Test(#09) 시뮬 입력값 하드코딩: elapsedSec=36000 / maxFloor=100 / prestigeBonus.IdleRateMul=1.0f / GetIdleCapHours=24(무투자) → 기대값 ppGained=50 (CP-M18 공식 일치). A4는 하드코딩 값 1줄 사전 확정 메모.
  1. A5 [20분, D-1] — OnApplicationPause 실기 검증 이월 명시 체크: Sp03 PR 본문 템플릿 1줄 — "IdleTicker.OnApplicationPause(bool) Unity 6 Editor Play Mode 에서 포커스 전환으로 검증. 모바일 iOS Suspend / Android 백그라운드 Kill 실기 검증은 Phase 3+ Build 파이프라인 도입 후 이월 (CP-M22)". PR 리뷰 시 해당 문구 존재 확인.

§1. Sp03 TodoWrite 10건 재확인 표 (설계 §7 그대로)

원천: sprint-03-idle-research.md §7 W1/W2 × 5건 = 10건. 1인 파트타임 11~15h 예산 기준, 합계 13.25h.

W1 — 기반 인프라 + 온라인 틱 (7.25h)

#제목담당예상 공수선행CP 태그
#01GodBlade.Runtime.Idle 네임스페이스 + IdleConstants.cs + IdleRewardBundle.cs structcode-lead0.75h(없음)CP-I2 네임스페이스
#02IdleSaveSchema.cs DTO + IdleSaveService.cs Save/Load/Migratecode-lead1.5h#01CP-I3 저장 회로
#03IdleRewardCalculator.cs 옵션 C Phase 1 (Bulk 집계 공식)code-lead2h#01, Sp02 W2 BuildSummaryCP-I4 공식 일치
#04IdleRewardCalculator.cs Phase 2 (Tail 3,600초 리플레이) + RNG 시드 결정론code-lead1.5h#03CP-I5 결정론 재현
#05IdleTicker.cs MonoBehaviour + OnApplicationPause + singleton + DontDestroyOnLoadcode-lead1.5h#02, #04CP-I1 Unity.ResourceManager

W2 — 통합 + 테스트 + Smoke (6.0h)

#제목담당예상 공수선행CP 태그
#06PrestigePpAccumulator.GrantIdleDrip 실배선 + PlayerPrestigeState.AddPermanentPp 호출code-lead0.75h#05, Sp02 W2CP-I6 이중 적립 방지
#07IdleTicker.FlushCatchUp 이벤트 발화 순서 (Milestones → Drops 로그 → PP 적립 → OnIdleReward)code-lead1.5h#06CP-I7 순서 검증
#08Edit-mode 테스트 7건 (역전 가드 / 캡 / 결정론 / 마일스톤 / Save 라운드트립 / PP 누적 / 30일 Warn)code-lead2h#07GodBlade.Tests.Editor references 확인
#09Smoke Test — 10h elapsed 시뮬 → PP ≈ 50 + Debug.Log 드롭 내역code-lead0.75h#08Sp01 Smoke 포맷 재사용
#10§9 컴파일러 시뮬 11개 체크 + Unity Console 새 에러 0 + PR 머지code-lead1h#01~#09Sp00+Sp01+Sp02 W1 회귀 4패턴 재발 금지

§1.1 순차 그래프

#01 ──> #02 ─┐
             ├──> #05 ──> #06 ──> #07 ──> #08 ──> #09 ──> #10
     #03 ──> #04 ─┘
     (Sp02 W2 BuildSummary 의존)   (Sp02 W2 PrestigePpAccumulator 의존)

§1.2 Sp03 스코프 TSV 실사 (신규 소비 0건)


§2. 태스크 #01 — GodBlade.Runtime.Idle 네임스페이스 + IdleConstants + IdleRewardBundle

§2.1 예상 Happy Path (30~60분 단위)

  1. [10분] Assets/_Project/Scripts/Runtime/Idle/ 폴더 신규 생성 (Unity Project 창 또는 파일 시스템). Sp02 W2 Assets/_Project/Scripts/Runtime/Prestige/ 와 평행 배치.
  2. [10분] IdleConstants.cs 신규 생성. namespace GodBlade.Runtime.Idle. public static class IdleConstants — TICK_INTERVAL_SEC = 1.0f / TAIL_REPLAY_SEC = 3600 / PP_PER_FLOOR_PER_HOUR = 0.05f / MAX_ELAPSED_DAYS = 30 4개 상수.
  3. [15분] IdleRewardBundle.cs 신규 생성. namespace GodBlade.Runtime.Idle. 설계 §4.2 public readonly struct IdleRewardBundle — 7필드 (ElapsedSec long / GoldGained long / ExpGained long / PpGained long / KillsTotal int / DropsCompact IReadOnlyList<(int floor, Grade grade, int count)> / MilestonesReached IReadOnlyList<int>).
  4. [10분] struct 생성자 + Empty 정적 인스턴스 + Combine(in IdleRewardBundle bulk, in IdleRewardBundle tail) 헬퍼(Bulk+Tail 합산용 — #03/#04에서 소비). 설계 §2.4 "finalBundle = bulkBundle + tailBundle" 의사코드 이행.
  5. [5분] 저장 + Unity 에디터 포커스 복귀 → Console 에러 0 확인.

총 0.75h 예상 (설계와 일치).

§2.2 예상 블로킹·회귀 리스크

리스크심각도근본 원인재현 조건대응
⭐ CS0579 재발 (Runtime 신규 파일)Low-MediumSp02 W2에서 이미 Runtime/Prestige/ 하위 4~6 파일 신설된 상태. Runtime/Idle/ 첫 파일 작성 시 Runtime asmdef InternalsVisibleTo 복제 유혹. 단 Runtime asmdef는 Data asmdef와 달리 InternalsVisibleTo 관례 없음 (Sp01/Sp02 W2 확인). 관성적 복제 차단InternalsVisibleTo 복제A3 체크리스트 재활용. Runtime/Idle/ grep [assembly: InternalsVisibleTo → 0건 유지
Grade 타입 참조 경계LowIdleRewardBundle.DropsCompact 필드에 Grade enum 포함 → using GodBlade.Data; 필수 (Sp01 W1 선언 enum). Runtime asmdef가 Data asmdef 참조 유지되어 있음 (Sp01 W2 확인)using 누락using GodBlade.Data; 파일 최상단 명시
IReadOnlyList<T> 직렬화Lowreadonly struct + IReadOnlyList<T> 필드 → System.Collections.Generic. Runtime asmdef 표준—using System.Collections.Generic; 명시
ValueTuple (int, Grade, int) 참조LowC# 7+ ValueTuple. Unity 6 기본 지원—특이사항 없음
Sp02 W1 R-DR-06 FP 교훈 반영 — 실측 검증정보드라이런 시점 TSV 행수 추정 1 off-by-one이 실측 결과 정상이었던 사례—#01는 TSV 무관. 단 Sp01 W2 Grade enum 값 실측 확인 필요 (Common=0 ~ Mythic=5 — Sp01 드라이런 §2.1 확인됨)

Sp00+Sp01+Sp02 W1 회귀 4패턴 재현 가능성:

§2.3 선결 조건 체크

§2.4 Rollback 지점


§3. 태스크 #02 — IdleSaveSchema DTO + IdleSaveService Save/Load/Migrate

§3.1 예상 Happy Path

  1. [10분] IdleSaveSchema.cs 신규 생성. namespace GodBlade.Runtime.Idle. 설계 §3.4 [System.Serializable] public struct IdleSaveDto — 6필드 (schemaVersion int / lastTickUtcMs long / lastFocusExitUtcMs long / lastKnownMaxFloor int / rngSeed uint / savedAtUtcMs long). public const int CURRENT_SCHEMA = 1.
  2. [15분] IdleSaveService.cs 신규 생성. public static class IdleSaveService — Save(IdleSaveDto, string accountId) / Load(string accountId) → IdleSaveDto? / Migrate(IdleSaveDto) → IdleSaveDto. 경로 = Application.persistentDataPath/account/<accountId>/idle.v1.json (Sp02 W2 §5.3 패턴 복제).
  3. [20분] Save 구현 — JsonUtility.ToJson(dto, prettyPrint:true) + File.WriteAllText. Sp02 W2 패턴 재활용: 매 저장 전 idle.v1.json.bak 백업 1회.
  4. [20분] Load 구현 — 파일 미존재 시 null 반환. 존재 시 JsonUtility.FromJson<IdleSaveDto> + Migrate() 호출.
  5. [15분] Migrate switch — case 1: return dto; / default: Debug.LogWarning + BackupCorruptFile + return default. Sp02 W2 §5.4 패턴.
  6. [10분] 저장 + Unity 에디터 포커스 복귀 → Console 에러 0 확인.

총 1.5h 예상 (설계와 일치).

§3.2 예상 블로킹·회귀 리스크

리스크심각도근본 원인재현 조건대응
⭐ Application.persistentDataPath Edit Mode vs Play ModeMediumEdit Mode 에서 Application.persistentDataPath 호출 가능하나 테스트 시 경로 오염. Unity 에디터 캐시는 %APPDATA%/Unity/... 하위 — 실제 빌드와 다른 경로#08 테스트에서 실제 저장 후 Load테스트는 Path.GetTempPath() + 테스트 전용 accountId 사용. Production은 persistentDataPath 유지
JSON schemaVersion 마이그 — 신규 회귀 패턴Medium (Sp03 신규)Sp02 W2 prestige.v1.json + Sp03 idle.v1.json 2개 파일 schemaVersion 독립 관리. 둘 다 CURRENT_SCHEMA=1 시 혼동 여지 (같은 폴더)경로 충돌경로 분리: account/<accountId>/idle.v1.json — 파일명에 스키마 버전 포함 (Sp02 W2 패턴). v2 전환 시 파일명 idle.v2.json 으로 분리 저장
JsonUtility Nullable / List 제한LowJsonUtility 는 List<T> 지원 O / Nullable<T> 지원 약함IdleSaveDto 에 long? 등 사용 시DTO 는 value type 만. lastFocusExitUtcMs = 0 이 미설정 의미 규약
BOM 이중 trim (Sp01 W2 교훈)ZeroSave/Load는 TSV 아닌 JSON. BOM 무관—N/A
파일 I/O 예외 (디스크 가득 / 권한)LowFile.WriteAllText 예외 throw테스트 환경 이상try/catch + Debug.LogError. Save 실패 시 lastTickUtc 에 변경된 값 롤백 정책 (R-DR-06 FP 교훈 — 추정 금지, 실측 후 확정)
accountId = "local" 하드코딩LowSp02 W2 §5.3 결정: Phase 5 Firebase 도입 전 "local" 고정. Sp03도 동일Phase 5 도입 시 마이그현 범위 OK. Sp03는 IdleSaveService.Load("local") 고정

Sp00+Sp01+Sp02 W1 회귀 4패턴 재현 가능성:

Sp03 신규 회귀 패턴:

§3.3 선결 조건 체크

§3.4 Rollback 지점


§4. 태스크 #03 — IdleRewardCalculator Phase 1 (Bulk 집계 공식)

§4.1 예상 Happy Path

  1. [15분] IdleRewardCalculator.cs 신규 생성. namespace GodBlade.Runtime.Idle. public static class IdleRewardCalculator. 설계 §4.2 Calculate 메서드 시그니처 확정:
   public static IdleRewardBundle Calculate(
       long elapsedSec, int maxFloor,
       DropRateTableSO drop, MilestoneRewardCatalogSO milestone,
       in PrestigeCalculator.BuildSummary prestigeBonus,
       int idleCapHours, uint rngSeed)
  1. [10분] 역전 가드 + 캡 적용 (CP-I9 / CP-M23):
  1. [20분] Bulk 구간 분할: if (cappedSec > 3600) bulkSec = cappedSec - 3600; else bulkSec = 0; tailSec = cappedSec - bulkSec;
  2. [30분] AggregateFormula(bulkSec, maxFloor, drop, prestigeBonus) 내부 헬퍼:
  1. [15분] 드롭 기대값 집계: 6 grade × 2 mobKind = 12 조합 순회 → expectedCount = killsBulk × drop.GetDropRate(floorBucket(maxFloor), mob, grade). DropRateTableSO.GetDropRate 는 Sp01 W2 R02 메서드 — 시그니처 확인 필수.
  2. [15분] MilestonesReached 목록: maxFloor 고정(Q3-A)이므로 현재 maxFloor 초과 마일스톤 없음 → 빈 리스트 (CP-I10). Bulk 구간 내 마일스톤 돌파 = 0.
  3. [15분] IdleRewardBundle 반환 + 저장 + Console 에러 0.

총 2h 예상 (설계와 일치).

§4.2 예상 블로킹·회귀 리스크

리스크심각도근본 원인재현 조건대응
⭐ BuildSummary.IdleRateMul 필드명 Sp02 W2 확정 상태 의존 (R-DR-09)Highd8 §2.1 승인 매핑이 Sp02 W2 Importer 실제 구현에서 필드명 정확히 반영되었는지 Sp03 착수 시점 확인 필수. "IdleYieldPct" 그대로 남거나 다른 이름으로 drift 가능성Sp02 W2 회고 미검증A1 사전 완화 필수. 시그니처 in PrestigeCalculator.BuildSummary 로 선언하면 드라이런 시점 컴파일 검증 가능
⭐ avgGoldPerKill / avgExpPerKill 수식 미정Medium설계 §2.3(옵션 B) 언급만, 구체 공식 없음. "floor 기반 선형/log" 결정 공백. Sp04 튜닝 이월 가능성공식 없이 착수1인 파트타임 현실: avgGold = maxFloor × 10 / avgExp = maxFloor × 5 임시 하드코딩 + Sp04 튜닝 TODO 주석. PR 본문 "수식 임시" 명시
DropRateTableSO.GetDropRate 시그니처 일치MediumSp01 W2 실제 머지 시그니처 확인 필요. 설계 §5 축1 "read-only 호출 (RollDrops + ExpectedDrops 두 용도)" 언급만Sp01 W2 실측 미검증A1 사전 완화: Sp01 W2 DropRateTableSO.cs grep public float GetDropRate → 시그니처 확인
floorBucket(maxFloor) 헬퍼LowSp01 W2 DropRateTable 5 버킷 (1-50 / 51-100 / 101-150 / 151-200 / 200+). 헬퍼 Sp01 W2 DropRateTableSO 내부? 또는 IdleRewardCalculator 자체 구현?위치 공백Sp01 W2 실측 필요 — DropRateTableSO.GetBucket(int floor) 존재 시 재사용, 미존재 시 IdleRewardCalculator 내 private 헬퍼 복제
long 오버플로 방어 (CP-M16 Sp02 W2 패턴)Low-MediumkillsBulk avgGold = 3086400 100 10 = 2.6B — int 경계. long 사용 필수int 무심코 사용모든 집계 필드 long. checked 블록 권장
System.Random vs Unity.Random 혼동 (Sp03 신규 회귀)Medium (Sp03 신규)Unity.Mathematics.Random / UnityEngine.Random / System.Random 3개 공존. 결정론 요구 시 System.Random(rngSeed) 만 허용관성적 UnityEngine.Random.Range 사용A2 체크리스트. Calculator 내부 new System.Random(rngSeed) 명시
드롭 기대값 반올림 정책 (설계 §2.3 단점)MediumkillsBulk × rate = 0.37개 같은 분수 결과. 반올림 or Poisson 추첨 결정 필요정책 공백Sp03 범위: (int)Math.Floor(expectedCount) 단순 절삭. PR 본문 "Sp04 Poisson 고려" TODO

Sp00+Sp01+Sp02 W1 회귀 4패턴:

Sp03 신규 회귀 패턴:

§4.3 선결 조건 체크

§4.4 Rollback 지점


§5. 태스크 #04 — IdleRewardCalculator Phase 2 (Tail 3,600초 리플레이) + RNG 결정론

§5.1 예상 Happy Path

  1. [15분] ReplayEvents(tailSec, maxFloor, drop, milestone, prestigeBonus, rngSeed) private 헬퍼. System.Random rng = new System.Random((int)rngSeed); 명시. CP-M19 강제: DropRateTableSO.rows List 인덱스 순 iteration.
  2. [30분] 초당 루프: for (long sec = 0; sec < tailSec; sec++):
  1. [15분] Tail PP: 초당 (maxFloor × 0.05 / 3600) × prestigeBonus.IdleRateMul 누적. double 부동소수 누적 오차 주의 — long 누산 vs 최종 1회 계산 비교.
  2. [15분] MilestonesReached — Tail 구간 maxFloor 고정(Q3-A) → 현재 maxFloor 돌파 이력 없으므로 빈 리스트 (CP-I10).
  3. [15분] Bulk + Tail 합산: IdleRewardBundle.Combine(bulkBundle, tailBundle) 반환.
  4. [15분] 저장 + Console 에러 0 확인.
  5. [15분] 간단 결정론 수동 확인 — Edit Mode에서 같은 seed 2회 호출 → 같은 결과 (정식 테스트는 #08).

총 1.5h 예상 (설계와 일치).

§5.2 예상 블로킹·회귀 리스크

리스크심각도근본 원인재현 조건대응
⭐ Dictionary vs List 순회 결정론 (R-DR-07)HighDropRateTableSO._lookup Dictionary 순회 유혹 — 관성적으로 foreach (var kv in _lookup) 작성. IL2CPP vs Mono 순서 다름 → 모바일 빌드 테스트 Red관성적 Dictionary iterationA2 체크리스트 필수. 매 Grade 순회 for (int i = 0; i < 6; i++) { Grade g = (Grade)i; ... } 강제. _lookup 은 O(1) 조회만
System.Random 시드 전파MediumCalculate 호출자가 rngSeed 변경하지 않으면 매 Catch-up 마다 같은 결과. "쌓여있는 방치 재생성 시 동일" 은 UX 문제 아님(결정론 전제). 단 세션마다 다른 seed 원한다면 lastTickUtcMs 를 seed로 사용혼동Sp03 범위: rngSeed는 IdleTicker 가 매 FlushCatchUp 마다 (uint)DateTime.UtcNow.Ticks 로 갱신 (CP-I5 결정론 ≠ 모든 호출 동일. 동일 seed + 동일 입력 → 동일 출력)
boss 확률 10% 하드코딩Low설계 공백 — Sp01 W2 MobKind 분포 미결정—Sp03 범위: 10% 고정 + PR 본문 "Sp04 튜닝" TODO
tail PP double 누적 오차MediumtailPpFloat += 0.00139f 식 3600번 누적 → 부동소수 오차 0.001~0.005 가능. floor() 후 정수 비교 시 50 vs 49 경계 실패오차 경계값long 누산 권장: tailPp = (long)Math.Floor(tailSec maxFloor 0.05 * idleRateMul / 3600.0) 1회 계산 (Bulk와 동일 경로)
RollDrops 호출 횟수 = tailSec (최대 3,600)Low설계 §2.4 "리플레이 상한 3,600회 고정". 성능 OK (<300ms)—실기 측정은 Sp04 Phase 3+
struct+List 복사 실수 (Sp03 신규 회귀)Medium (Sp03 신규)IdleRewardBundle readonly struct. Combine(in bulk, in tail) 호출 시 in 파라미터 누락하면 복사 2회in 누락시그니처 in IdleRewardBundle bulk, in IdleRewardBundle tail 강제

Sp00+Sp01+Sp02 W1 회귀 4패턴:

Sp03 신규 회귀 패턴:

§5.3 선결 조건 체크

§5.4 Rollback 지점


§6. 태스크 #05 — IdleTicker MonoBehaviour + OnApplicationPause + singleton

§6.1 예상 Happy Path

  1. [15분] IdleTicker.cs 신규 생성. namespace GodBlade.Runtime.Idle. public sealed class IdleTicker : MonoBehaviour. 설계 §4.2 시그니처 적용.
  2. [15분] Singleton 패턴:
   static IdleTicker _instance;
   public static IdleTicker Instance => _instance;
   void Awake() {
       if (_instance != null && _instance != this) { Destroy(gameObject); return; }
       _instance = this;
       DontDestroyOnLoad(gameObject);
   }
   void OnDestroy() { if (_instance == this) _instance = null; }
  1. [15분] 필드 선언 — _prestige / _drop / _milestone / _currentMaxFloor / _accumUnscaled float / _lastTickUtc DateTime. Addressables로 SO 3개 로드는 Start() 내 Addressables.LoadAssetAsync<T> (CP-M01 Unity.ResourceManager 참조 필수).
  2. [15분] Start() → BootstrapFromSave(): IdleSaveService.Load("local") → dto가 null이 아니면 _lastTickUtc = new DateTime(dto.lastTickUtcMs * 10000L) 복원. 즉시 FlushCatchUp() 1회 호출 (설계 §4.4).
  3. [15분] Update() → TickOneSecondIfElapsed(): _accumUnscaled += Time.unscaledDeltaTime; if (_accumUnscaled >= 1f) { OnlineTick(1f); _accumUnscaled -= 1f; }
  4. [10분] OnApplicationPause(bool paused): if (paused) { IdleSaveService.Save(_lastTickUtc, ...); } else { FlushCatchUp(); }
  5. [5분] OnApplicationQuit(): IdleSaveService.Save(...) flush (iOS 대비 이중 저장).
  6. [10분] FlushCatchUp() 스켈레톤 — Δ 계산 + 역전 가드 (CP-I9/CP-M23) + IdleRewardCalculator.Calculate(...) 호출 + _lastTickUtc = DateTime.UtcNow 갱신. 실제 이벤트 발화 순서는 #07 이월.
  7. [10분] 저장 + Console 에러 0 확인.

총 1.5h 예상 (설계와 일치).

§6.2 예상 블로킹·회귀 리스크

리스크심각도근본 원인재현 조건대응
⭐ CS0246 Unity.ResourceManager 재발 (Addressables)MediumAddressables.LoadAssetAsync<DropRateTableSO>(address) 호출 → AsyncOperationHandle<T> 타입 해석 필요 → Unity.ResourceManager asmdef references 필수 (CP-M01)asmdef 누락Sp01 W2에서 이미 GodBlade.Runtime.asmdef:7 Unity.ResourceManager 추가 확인. A3 사전 완화 1항목
⭐ singleton Awake 중복 생성 (Sp03 신규)Medium (Sp03 신규)DontDestroyOnLoad 적용 후 씬 재진입 시 IdleTicker GameObject 중복 생성 가능 — Awake 가드 필요씬 전환 실수if (_instance != null && _instance != this) { Destroy(gameObject); return; } 반드시. _instance == this OnDestroy 에도 필수
⭐ OnApplicationPause 플랫폼 차이 (R-DR-08)HighiOS 30초 Suspend 후 호출 보장 약함 (설계 §8.1). Android 상대 관대. Steam(PC) 포커스만 발화플랫폼별 검증 불가CP-M22: 실기 검증 Phase 3+ 이월. 1분 주기 Safe Flush 추가 — Update() 내부 if (DateTime.UtcNow - _lastSaveUtc > 60s) Save(). 설계 §8.1 권고
Addressables async 로드 실패MediumSO 3개 Addressables 주소 (Rewards/DropRateTable / Rewards/MilestoneRewardCatalog / Prestige/ItemCatalog) 존재 확인 필요주소 driftSp01 W2 / Sp02 W1 실측 주소 확인 (A1 사전 완화 포함)
DateTime.UtcNow.Ticks / 10000L 변환LowTicks = 100ns 단위, ms 단위로 /10000 정확. 단 new DateTime(longValue * 10000L) 복원 시 DateTimeKind.Unspecified — UTC 아닐 수 있음Kind 무심DateTime.SpecifyKind(dt, DateTimeKind.Utc) 필수
Time.unscaledDeltaTime 첫 프레임 0LowStart() 직후 첫 Update는 deltaTime 불안정첫 프레임 tick 손실_accumUnscaled = 0f 초기화 + 1프레임 스킵 허용 (손실 <1초 OK)
IdleTicker MonoBehaviour Tests.Editor 제한MediumEdit Mode 테스트에서 MonoBehaviour Update 호출 불가 — Play Mode 테스트 필요#08 테스트 범위IdleTicker 자체는 Play Mode 테스트 (별도 asmdef). IdleRewardCalculator 순수 static만 Edit Mode로 검증. 설계 §7 #08 "IdleTickerTests + IdleRewardCalculatorTests" 두 그룹 분리

Sp00+Sp01+Sp02 W1 회귀 4패턴:

Sp03 신규 회귀 패턴:

§6.3 선결 조건 체크

§6.4 Rollback 지점


§7. 태스크 #06 — PrestigePpAccumulator.GrantIdleDrip 실배선

§7.1 예상 Happy Path

  1. [10분] Sp02 W2 PrestigePpAccumulator.cs 재열람 — GrantIdleDrip(long elapsedSec, int maxFloor) 메서드 시그니처 확인 (메서드 존재해야 함). 설계 §6.3 명시.
  2. [15분] 구현 — 내부에서 long ppAmount = (long)Math.Floor(elapsedSec / 3600.0 maxFloor 0.05); + _state.AddPermanentPp(ppAmount); 호출. IdleRateMul 곱연산은 Calculator에서 이미 수행 → ppAmount 는 그대로 적립.
  3. [10분] 이중 적립 방지 (CP-I6 / CP-M20): IdleTicker.FlushCatchUp() 에서 GrantIdleDrip 1회만 호출 + OnIdleReward 발화는 그 다음. 같은 메서드 2회 호출 차단.
  4. [10분] Sp02 W2 PlayerPrestigeState.AddPermanentPp(long) public 접근자 확인 (CP-M21) + OnPpChanged 이벤트 발화 확인.
  5. [5분] 저장 + Console 에러 0.

총 0.75h 예상 (설계와 일치).

§7.2 예상 블로킹·회귀 리스크

리스크심각도근본 원인재현 조건대응
⭐ Sp02 W2 GrantIdleDrip 미구현 (R-DR-09)HighSp02 W2 설계 §6.3 시그니처만 존재, 실구현은 Sp03 몫. Sp02 W2 회고 시 메서드 껍데기만 있으면 #06 구현 진행 가능. 껍데기 없으면 Sp02 W2로 역에스컬레이션Sp02 W2 시그니처 누락A1 사전 완화 필수. 시그니처 머지 확인
CP-I6 이중 적립 방지 실패MediumGrantIdleDrip 을 Calculator.ppBulk+ppTail 계산값과 별도로 수식 재계산 시 중복수식 2벌 운영Sp03 설계: Calculator 가 ppGained 계산 → IdleTicker 가 _prestige.AddPermanentPp(bundle.PpGained) 단일 호출. GrantIdleDrip은 수식 재계산 없음, 단순 전달자 역할. 설계 §4.5 순서 준수
AddPermanentPp(0) 방어Low방치 0초 시 ppAmount=0OnPpChanged 불필요 발화if (amount > 0) 가드
Sp02 W2 시그니처 불변 원칙MediumGrantIdleDrip 시그니처 변경 금지. 내부 수식 변경만관성적 시그니처 수정PR 본문 "Sp02 W2 시그니처 불변, 내부 구현만" 명시

Sp00+Sp01+Sp02 W1 회귀 4패턴:

§7.3 선결 조건 체크

§7.4 Rollback 지점


§8. 태스크 #07 — IdleTicker.FlushCatchUp 이벤트 발화 순서

§8.1 예상 Happy Path

  1. [10분] 설계 §4.5 발화 순서 구현 — FlushCatchUp() 내부:
   a. bundle.MilestonesReached.ForEach(floor => MilestoneGranter.Award(floor))
      └─ Sp01 W2 채널 B (빈 리스트일 가능성 높음 — maxFloor 고정)
   b. bundle.DropsCompact → Debug.Log (Sp04 인벤토리 이월, Sp03는 로그만)
   c. _accumulator.GrantIdleDrip(bundle.ElapsedSec, _currentMaxFloor)
   d. OnIdleReward?.Invoke(bundle)
  1. [15분] event Action<IdleRewardBundle> OnIdleReward 공개 — Sp02 W2 PrestigePpAccumulator.GrantIdleDrip 은 c 단계에서 이미 실행, OnIdleReward 는 UI 구독자 전용 (CP-M20).
  2. [15분] Milestones 배치 처리: Sp01 W2 MilestoneGranter.Award(floor) Sp02 W2 Unlock() + GrantMilestone(200) 연쇄 유지 (설계 §4.5 a). Sp03 범위에서 실 돌파 없음 — 빈 리스트 검증 로그 1줄.
  3. [20분] 드롭 로그: foreach ((int floor, Grade grade, int count) in bundle.DropsCompact) Debug.Log($"[Idle] Drop: Floor={floor} Grade={grade} Count={count}"); (Q5-A Sp03 범위).
  4. [15분] IdleSaveService.Save(...) 호출 — lastTickUtc = DateTime.UtcNow 갱신 (설계 §4.5 step 4).
  5. [10분] 저장 + Console 에러 0.
  6. [15분] 수동 검증 — Edit Mode에서 _ticker.FlushCatchUp() 호출 → Console 4단계 로그 순차 확인 (Milestones 0건 → Drops N건 → PP +50 → OnIdleReward 1회).

총 1.5h 예상 (설계와 일치).

§8.2 예상 블로킹·회귀 리스크

리스크심각도근본 원인재현 조건대응
⭐ 발화 순서 어긋남 (CP-I7 / CP-M20)Medium4단계 순서 역전 시 UI 가 State 읽기 전 OnIdleReward 먼저 발화 → UI 에 "+50 PP" 표시되는데 State.PermanentPp 미반영순서 오류단위 테스트 (#08) 에서 4단계 시간 순 assert. OnIdleReward 발화 시점에 _state.PermanentPp 증가 확인
MilestoneGranter.Award Sp02 W2 연쇄MediumAward(100) 내부에서 PlayerPrestigeState.Unlock() 자동 호출 — Sp02 W2 계약. Sp03는 호출자만, 연쇄 로직 수정 금지 (CP-M21)연쇄 실수Sp02 W2 MilestoneGranter.Award 실구현 확인 (A1 사전 완화 포함)
OnIdleReward 구독자 0명LowSp03 범위에서 UI 구독자 없음 (UI는 Sp04+). 이벤트 발화 확인만—Smoke Test(#09) 에서 _ticker.OnIdleReward += bundle => Debug.Log($"+{bundle.PpGained}"); 구독 1줄 추가
Sp02 W2 OnIdleReward 중복 발화자Medium설계 §4.5 / Sp02 W2 §6.2 "OnIdleReward 유일 발화자 = IdleTicker.FlushCatchUp". 다른 곳에서 발화하면 CP-M20 위반중복 발화grep 확인 — OnIdleReward?.Invoke → IdleTicker.cs 1건만 유지
IdleSaveService.Save 실패 시 상태 불일치MediumFlushCatchUp 4단계 성공했는데 Save 실패 → 재Play 시 같은 Δ 2회 Catch-up 적용Save 예외Save 실패 시 _lastTickUtc 롤백 (Save 성공 후에만 갱신). Sp02 W2 §5.5 패턴

Sp00+Sp01+Sp02 W1 회귀 4패턴:

§8.3 선결 조건 체크

§8.4 Rollback 지점


§9. 태스크 #08 — Edit-mode 테스트 7건

§9.1 예상 Happy Path

  1. [10분] Assets/Tests/Editor/Idle/IdleRewardCalculatorTests.cs 신규 생성. namespace GodBlade.Tests.Editor.Idle.
  2. [15분] IdleRewardCalculatorTests [Test] 6건 (설계 §7 #08):
  1. [20분] IdleSaveServiceTests [Test] 1건:
  1. [15분] GodBlade.Tests.Editor.asmdef references 확인 — GodBlade.Runtime / GodBlade.Data / UnityEngine.TestRunner / UnityEditor.TestRunner / Unity.ResourceManager (CP-M03 확장)
  2. [30분] Test Runner Edit Mode 실행 → 7 Green. Red 시 개별 디버그.
  3. [10분] 테스트 파일 저장 + Console 에러 0.

총 2h 예상 (설계와 일치).

§9.2 예상 블로킹·회귀 리스크

리스크심각도근본 원인재현 조건대응
⭐ T3 결정론 재현 실패 (R-DR-07)HighCalculator 내부 Dictionary 순회 잔존 → 2회 호출 결과 달라짐 → Red설계 위반A2 체크리스트 재확인. 실패 시 Calculator 로직 재검토 필수
⭐ GodBlade.Tests.Editor.asmdef references 누락 (CP-M03)MediumSp02 W2에서 GodBlade.Runtime 추가 확인 필요. Sp03에서 Unity.ResourceManager 추가 미확정asmdef 누락 → CS0246 재발A1 사전 완화. Sp02 W2 실측 asmdef references 확인
LogAssert.Expect 타이밍 (T5)LowDebug.LogWarning 발화 전에 LogAssert.Expect 호출 필요. 순서 혼동—LogAssert.Expect(LogType.Warning, regex) → Calculate 호출 → Assert
T4 PP 수식 floor 경계Low50.0 vs 49.999999... 부동소수 오차오차 50 vs 49Assert.AreEqual(50L, bundle.PpGained) + Math.Floor 경계 검증
T7 Save/Load 실제 파일 오염Medium테스트 accountId 고유화 필수. "local" 재사용 시 production 파일 오염accountId 공유테스트 전용 accountId = "test-" + Guid.NewGuid().ToString() 1회용
Play Mode 필요 테스트 Edit Mode로 강행MediumIdleTicker MonoBehaviour Update 동작 Edit Mode 불가. #08는 Calculator + SaveService만MonoBehaviour 테스트 누락범위 명시: #08은 Edit Mode 7건만. IdleTicker Play Mode 테스트 Phase 3+ 이월 PR 본문

Sp00+Sp01+Sp02 W1 회귀 4패턴:

§9.3 선결 조건 체크

§9.4 Rollback 지점


§10. 태스크 #09 — Smoke Test

§10.1 예상 Happy Path

  1. [15분] Editor 스크립트 or Test Runner 시뮬 — IdleRewardCalculator.Calculate(elapsedSec=36000, maxFloor=100, drop=<loaded>, milestone=<loaded>, prestigeBonus=new BuildSummary{IdleRateMul=1f, GoldMul=1f, ExpMul=1f, DropMul=1f}, idleCapHours=24, rngSeed=1234).
  2. [10분] 기대값 검증:
  1. [10분] 드롭 내역 Debug.Log 형식 — "[Idle] Drops: Common x?, Magic x?, Rare x?, Epic x?, Legendary x?, Mythic x0" (Mythic은 3,600 리플레이로는 거의 0).
  2. [10분] 저장 + Console 에러 0.

총 0.75h 예상 (설계와 일치).

§10.2 예상 블로킹·회귀 리스크

리스크심각도근본 원인재현 조건대응
⭐ Smoke Test 기대값 50 PP 불일치MediumavgGoldPerKill / avgExpPerKill 수식 미정(#03) → PP만 스펙 일치 확인. Gold/Exp 미정책수식 튜닝 시 driftSmoke 범위 PP만 assert. Gold/Exp는 bundle.GoldGained > 0 최소 검증
Addressables SO 로드 실패MediumSmoke 시점 Addressables Build 필요Build 미실행Addressables Build Player Content 선행 필수. Sprint 00~02 패턴
Sp02 W2 BuildSummary 생성자 미노출Mediumnew BuildSummary{IdleRateMul=1f, ...} 초기화자 문법 — struct readonly 필드라면 생성자 필수초기화 방식 불일치Sp02 W2 실구현 확인. 필요 시 BuildSummary.Default 정적 팩토리 추가 (Sp02 W2 이월 or 드라이런 권고)

Sp00+Sp01+Sp02 W1 회귀 4패턴:

§10.3 선결 조건 체크

§10.4 Rollback 지점


§11. 태스크 #10 — §9 컴파일러 시뮬 11개 체크 + PR 머지

§11.1 예상 Happy Path

  1. [15분] shared-checkpoints.md §3 Sp03 서브셋 13개 체크 전부 tick. CP-M01/M02/M06/M12/M17~M23 실측 확인.
  2. [10분] Unity Console 새 에러 0 + Warning 재검토 (30일 Warn, Suspend 메시지 허용).
  3. [10분] Addressables Build → Build Player Content 성공.
  4. [10분] Smoke Test(#09) 결과 Console 로그 재실행 확인.
  5. [15분] PR 본문 작성 — 목적 / 변경 요약 (파일별 1줄) / 검증 / 리스크·미해결 4섹션. CP-M22 "실기 검증 Phase 3+ 이월" 문구 명시 (A5).
  6. [5분] PR 제출.

총 1h 예상 (설계와 일치).

§11.2 예상 블로킹·회귀 리스크

리스크심각도근본 원인재현 조건대응
⭐ CP-M 13개 중 미통과 1건 이상High#01~#09 진행 중 놓친 체크 존재 가능누적 피로도체크별 Grep/육안 확인 1회. 미통과 시 즉시 fix + 재체크
Unity Console 새 에러Medium#09 Smoke 후 Warning 레벨 에러 잔존—Smoke 재실행 후 Console Clear → 1회 더 확인
PR 본문 누락 (특히 CP-M22 실기 이월)Low템플릿 4섹션 복제 시 리스크·미해결 섹션 비움 유혹관성A5 사전 완화 필수. "리스크·미해결" 섹션에 CP-M22 문구 복사
Addressables Build 실패Low신규 그룹 추가 없음 (CP-M12/CP-I11) → 빌드 영향 Zero—Sp03는 신규 Addressables 그룹 0

Sp00+Sp01+Sp02 W1 회귀 4패턴:

§11.3 선결 조건 체크

§11.4 Rollback 지점


§12. Sp00+Sp01+Sp02 W1 회귀 4패턴 + Sp03 신규 4패턴 재현 매트릭스

§12.1 태스크 × 기존 회귀 4패턴

값 = Low (L) / Medium (M) / High (H) / Zero (-)
태스크CS0246 재현CS0579/1061 재현TSV 경로 drift네임스페이스 혼동
#01 Namespace + Bundle-L-M-L
#02 SaveService-L-L
#03 Calculator Phase 1-L-L
#04 Calculator Phase 2-L-L
#05 IdleTickerML-L
#06 GrantIdleDrip----
#07 FlushCatchUp----
#08 TestsM--L
#09 Smoke----
#10 PR 머지----

Sp03 전체 기존 회귀 재현 확률:

§12.2 태스크 × Sp03 신규 회귀 4패턴

신규 패턴: (1) OnApplicationPause 플랫폼 차이 (2) DateTime 시계 조작 (3) JSON schemaVersion 마이그 (4) struct+List 복사 실수
태스크OnAppPause 플랫폼DateTime 시계JSON schema 마이그struct 복사
#01 Namespace + Bundle---M (Bundle struct 정의)
#02 SaveService-LM (Migrate switch)-
#03 Calculator Phase 1-L (elapsed 계산)-L
#04 Calculator Phase 2---M (Bundle Combine)
#05 IdleTickerHM (역전 가드)--
#06 GrantIdleDrip----
#07 FlushCatchUpM (Save 타이밍)L-L
#08 Tests-M (T2/T5 시간 assert)M (T7 라운드트립)L
#09 SmokeL---
#10 PR 머지L (PR 본문)---

Sp03 신규 회귀 확률:

§12.3 재현 방지 메커니즘

  1. 구조적 가드 (4건):
  1. 프로세스 가드 (2건):
  1. 학습 가드 (2건):

§13. 하이브리드 계산 수리적 검증 (엣지 케이스 4종)

설계 §2.4 옵션 C 하이브리드가 실제 디아블로 감성 ("돌아왔는데 짜잔!") 과 일치하는지 예상 체감 시뮬. 실측 불가 — 드라이런 수리 검증만.

§13.1 엣지 케이스 1 — 0초 방치 (Just Launched)

§13.2 엣지 케이스 2 — 1시간 직전 방치 (3,599초)

§13.3 엣지 케이스 3 — 34h 캡 초과 방치 (48h 실제)

§13.4 엣지 케이스 4 — 앱 종료 직후 재시작 (0.5초)

§13.5 하이브리드 체감 검증 결론


§14. OnApplicationPause 플랫폼 리스크 상세 (R-DR-08 심화)

§14.1 플랫폼별 발화 타이밍

플랫폼OnApplicationPause(true) 발화OnApplicationPause(false) 발화보장도
Unity 6 Editor (Play Mode)Editor 포커스 잃을 때 (다른 창 클릭)Editor 포커스 복귀부분 — 실제 백그라운드 아님
AndroidHome 버튼 / 앱 전환 / 화면 잠금앱 재진입보장 — API level 차이 존재, Unity 6는 상대 안정
iOSHome 버튼 직후 (< 1초)앱 재진입부분 보장 — Suspend 진입(30초 후) 시점엔 미발화
Steam (PC Windows/Linux)포커스 잃을 때포커스 복귀약함 — OS별 차이, Alt-Tab 미발화 가능

§14.2 iOS Suspend 시나리오 분석

T+0   : 유저 Home 버튼 누름
T+0.5s: OnApplicationPause(true) 발화 → _lastTickUtc = DateTime.UtcNow 저장 (성공 예상)
T+30s : iOS 앱 Suspend (프로세스 동결)
T+10min: 유저 앱 재진입
T+10min+ε: OnApplicationPause(false) 발화 (iOS Resume)
         → FlushCatchUp() 호출, elapsed = 10min 계산 정상

문제 시나리오: T+30s 이전에 iOS 가 앱을 즉시 Kill (메모리 압박) → OnApplicationPause(true) 미발화 → _lastTickUtc 미저장 → 재시작 시 Save 파일의 이전 값 사용, 30초 ~ 10분 격차 누락 가능성.

완화:

  1. Update() 내 1분 주기 Safe Flush (CP-M22) — 1분 이내 격차만 허용
  2. OnApplicationQuit 이중 저장 (설계 §1.4) — iOS는 호출 보장 약함, Android는 상대 강함

§14.3 Android 플랫폼 안정성

§14.4 Steam (PC) 플랫폼

§14.5 Sp03 범위 제약 명시

  1. Play 진입 → IdleTicker 동작 10초 확인
  2. Editor 다른 창 클릭 (포커스 이탈) → Console OnApplicationPause(true) 로그
  3. Editor 복귀 → OnApplicationPause(false) + FlushCatchUp 로그
  4. Play Mode 종료 → OnApplicationQuit 로그

§15. IdleTicker MonoBehaviour Singleton 리스크 상세

§15.1 singleton 패턴 취약점 3건

취약점발생 조건대응
중복 생성씬 전환 시 DontDestroyOnLoad 누적 — 다른 씬에 IdleTicker 프리팹 배치 시 2개 이상Awake 가드: if (_instance != null && _instance != this) { Destroy(gameObject); return; }
OnDestroy 순서Unity 6 Play Mode 종료 시 MonoBehaviour OnDestroy 호출 순서 불확정. _instance == this 체크 없으면 다른 ticker OnDestroy 후 _instance null 설정 → 첫 ticker가 _instance == null 오해if (_instance == this) _instance = null; 가드
Play Mode 첫 프레임 경합Start() 전 Awake 에서 _lastTickUtc 미초기화 → Update() 첫 호출 시 NullReferenceAwake에서 _lastTickUtc = DateTime.UtcNow 초기화 (Start에서 Load 이후 덮어쓰기)

§15.2 DontDestroyOnLoad 누적

§15.3 씬 전환 시 연속성

§15.4 OnDestroy 순서 리스크


§16. Sp02 W2 의존 4건 확정 상태 교차 확인 (A1 핵심)

§16.1 Sp02 W2 착수 시점 Sp03에 필요한 4건 상세

#항목Sp02 W2 설계 위치Sp03에서 필요한 시점확정 상태 표현
D1PrestigeCalculator.GetIdleCapHours(in PlayerPrestigeState) 헬퍼설계 §3.3 (d8 §3.3 CK-3)#03 Calculator Phase 1 시작 시public static int 시그니처 정확 일치. 내부 수식 24 + 2 × min(level, 5)
D2PrestigeCalculator.BuildSummary 4필드설계 §3.3 + d8 §2.1#03 Calculator 시작 시IdleRateMul/GoldMul/ExpMul/DropMul float 필드 존재 + 원하면 생성자 공개
D3PlayerPrestigeState.AddPermanentPp(long) / AddSeasonalPp(long)설계 §3.2#06 GrantIdleDrip 시작 시public 접근자 + OnPpChanged 이벤트 발화 포함
D4PrestigePpAccumulator.GrantIdleDrip(long elapsedSec, int maxFloor) 핸들러 예약설계 §6.3#06 시작 시시그니처만 존재해도 됨 (실구현은 Sp03 #06에서)

§16.2 4건 중 실구현 vs 시그니처만

§16.3 검증 절차 (D-1 킥오프 오전)

  1. Sp02 W2 최종 머지 PR 목록 확인 (GitHub or sprints/sprint-02.md §5)
  2. Unity6/Assets/_Project/Scripts/Runtime/Prestige/ grep:
  1. 누락 시:

§16.4 Sp02 W1 R-DR-06 FP 교훈 반영


§17. 사전 완화 액션 최종 (2026-05-31 킥오프 D-1, 총 2h 내)

§0.3 의 5건 상세화. 코드 0줄.

A1 — Sp02 W2 의존 4건 실측 확인 + asmdef 점검 (45분)

목적: R-DR-09 해소. 킥오프 GO/NO-GO 판정.

작업:

  1. Sp02 W2 최종 머지 PR 확인 (GitHub or sprint-02.md)
  2. Runtime/Prestige/ grep 4건 (§16.3)
  3. Addressables 주소 확인: Rewards/DropRateTable, Rewards/MilestoneRewardCatalog, Prestige/ItemCatalog 3건 존재
  4. GodBlade.Runtime.asmdef:7 Unity.ResourceManager 유지 확인 (CP-M01)
  5. GodBlade.Tests.Editor.asmdef references 에 GodBlade.Runtime + Unity.ResourceManager 확인 (CP-M03)
  6. 결과를 sprints/sprint-03.md §1 킥오프 섹션에 기록

산출: Sp03 킥오프 GO / NO-GO 결정 1줄 + 누락 건수

A2 — 하이브리드 결정론 체크리스트 (30분)

목적: R-DR-07 해소. #03/#04 Calculator 작성 중 List 순회 강제.

작업:

  1. 체크 4항목 메모:
  1. #03 Calculator Phase 1 시작 시 메서드 상단 주석 1줄로 재확인

산출: 결정론 체크리스트 1건

A3 — Data 어셈블리 신규 파일 체크리스트 v3 (20분)

목적: CS0579 재발 차단 + Runtime.Idle 네임스페이스 확정.

작업:

  1. namespace GodBlade.Data.<Sub> (Data 파일) / namespace GodBlade.Runtime.<Sub> (Runtime 파일)
  2. [assembly: InternalsVisibleTo(...)] 복제 절대 금지
  3. using UnityEditor 금지 (런타임/Data). 필요 시 #if UNITY_EDITOR 가드
  4. blittable 필드 유지 (int/enum/float/byte)
  5. Sp02 W1 추가: PpChannel enum은 Runtime/Prestige/PpChannel.cs 별도 파일
  6. Sp03 추가: IdleTicker.cs 는 Runtime/Idle/ 배치. Unity.ResourceManager references 확인

산출: 체크리스트 v3 1건

A4 — Smoke Test 하드코딩 값 메모 (15분)

목적: #09 Smoke Test 기대값 사전 확정.

작업:

  elapsedSec=36000 / maxFloor=100 / prestigeBonus.IdleRateMul=1.0f 
  / GetIdleCapHours=24(무투자) / rngSeed=1234
  → 기대값: bundle.PpGained==50, bundle.DropsCompact.Count>=1

산출: Smoke 기대값 메모 1건

A5 — OnApplicationPause 실기 이월 PR 템플릿 문구 (10분)

목적: CP-M22 준수 + #10 PR 본문 누락 방지.

작업:

  ## 리스크·미해결
  - `IdleTicker.OnApplicationPause(bool)` Unity 6 Editor Play Mode 포커스 전환으로만 검증. 
    모바일 iOS Suspend / Android 백그라운드 Kill 실기 검증은 
    Phase 3+ Build 파이프라인 도입 후 이월 (CP-M22)

산출: PR 본문 문구 1건


§18. Sp02 W1 드라이런 R-DR-06 FP 교훈 반영 (실측 검증 원칙)

§18.1 Sp02 W1 드라이런의 FP 사례

§18.2 Sp03 드라이런에서 FP 가능성 후보

후보드라이런 추정실측 확인 필요 시점FP 가능성
R-DR-09 Sp02 W2 의존 4건 미도착High 리스크2026-05-31 A1부분 FP 확정 (2026-04-21) — 4건 전수 설계 문서 시그니처 존재 (§0.2 블록쿼트 참조). 설계 스펙 누락 우려 = FP. 잔여 위험 = 구현 drift (Low-Medium)
R-DR-07 결정론 실패High 리스크2026-06-05 #08 테스트Low-Medium — A2 체크리스트 준수 시 회피
R-DR-08 OnApplicationPause 플랫폼 차이High 리스크실측 불가 (Phase 3+ 이월)High — Sp03 범위 내 검증 불가, 드라이런 리스크는 완화 매뉴얼만
avgGoldPerKill / avgExpPerKill 수식 미정Medium2026-06-02 #03 착수 시Medium — Sp04 이월 가능성 높음
BuildSummary 생성자 공개MediumA1Low — Sp02 W2 설계 시그니처 확인 가능

§18.3 FP 감수 원칙


§19. 메타정보


§20. 판정 요약

2026-06-01 Sprint 03 킥오프 → GO (Yellow)


(끝 — v1.0, 2026-04-21 code-lead 작성)