telemetry가 platform/bounded-capacity.ts의 assertBoundedCapacity를 다시 내보내고 있었다. 같은 파일이 이미 그 심볼을 직접 import해서 쓰고 있고 이 재수출의 소비자는 없었다. 남겨두면 커널로 가는 두 번째 문이 되고, 어댑터가 어댑터를 통해 커널에 닿는 경로를 정당화하게 된다. 같이 담긴 IndexedDB 커널 이행 계획은 커널 -> CP -> MT -> OP -> RT 순서와 각 사본의 동작 변화 지점을 줄 번호로 특정한다. LOC 절감은 -223으로 작고, 이 작업의 근거는 줄 수가 아니라 트랜잭션 상태기계를 4개에서 1개로 만드는 것이라는 점을 명시했다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
22 KiB
IndexedDB 커널 승격 Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 어댑터 4곳이 각자 구현한 IndexedDB 연결·트랜잭션 메커니즘을 src/adapters/platform/의 커널 2파일로 모으고, 4벌이 서로 다른 답을 내던 지점을 하나로 만든다.
Architecture: 커널은 메커니즘만 갖는다 — open 요청을 Promise로 바꾸기, blocked 데드라인, upgrade/error/success 라우팅, 늦게 도착한 연결 닫기, 트랜잭션 상태기계, 커서 펌프. 데이터베이스 이름·스키마·마이그레이션·governance·**실패 분류(taxonomy)**는 각 서브시스템에 남는다. 실패 매핑은 translate 콜백으로 주입하므로 mapIndexedDbException과 mapBrowserDataException이 서로 다른 답을 내는 현 상태가 보존된다.
Tech Stack: TypeScript, IndexedDB, Vitest, tests/helpers/memory-indexeddb.ts(가짜 IDB)
Spec: docs/superpowers/specs/2026-09-16-indexeddb-kernel-promotion-design.md
Global Constraints
src/adapters/platform/은 이미 dependency-cruiser의 kernel carve-out이다. 커널에 파일을 추가하는 데 규칙 변경이 필요 없다(spec §5.3).IDBFactory는 필수 주입 파라미터다.globalThis.indexedDB를 읽으면 안 된다 —eslint.config.ts:40-63이 모든 브라우저 루트에서 그 속성을 막고, 예외는platform/browser-lifecycle.ts한 파일에만 부여돼 있다. 주입받으면eslint.config.ts를 건드릴 필요가 없다(spec §5.1).- 소스 파일을 추가하면
docs/reviews/adapters/INVENTORY.md에 행을 추가하고 하단 합계를 고친다.check:adapter-inventory가git ls-files src/adapters와 집합을 정확히 대조한다. check:adapter-inventory에 abort 래칫이 있다.addEventListener("abort")를 쓰는 어댑터 파일이 24개를 넘으면 실패한다.platform/은 세지 않는다.- 실패 매핑 4벌을 통일하지 마라.
mapIndexedDbException(RT/MT/OP)과mapBrowserDataException(CP)은 같은 에러에 다른 답을 낸다 —ConstraintErrorrecovery가 NONE vs REOPEN,QuotaExceededretryable이 false vs true,NotFound가 MIGRATION_FAILED vs NOT_FOUND. 통일은 별건이고 이 계획의 범위가 아니다. deleteDatabase를 나머지 3벌에 추가하지 마라. CP에만 있는 것이 의도다(spec §2.3).- 이 환경의 알려진 제약:
tests/unit/ci-artifact-contract.test.ts16건이bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted로 실패한다.develop5434760기준선에서도 동일하다. 판정 기준은 그 파일 외의 실패가 0인지다. - 커밋 메시지 끝에 붙일 것:
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
왜 이 작업을 하는가 — LOC로 정당화하지 않는다
| LOC | |
|---|---|
| 4벌에서 삭제 | ~974 |
| 4벌에 추가 (커널 호출부·콜백) | ~281 |
| 커널 신규 2파일 | +~470 |
| 레포 순증감 | ~−223 |
절감은 작다. 성과는 줄 수가 아니라 트랜잭션 상태기계가 4개에서 1개가 되는 것이다. 오늘 그 4개는 이미 서로 다른 답을 낸다:
| 상황 | RT | MT | OP | CP |
|---|---|---|---|---|
| 트랜잭션 안 개별 요청 실패 | 본다 | 기록만 | 안 본다 | 즉시 abort |
| 값 없이 완료 | UNAVAILABLE | UNAVAILABLE | UNAVAILABLE | CORRUPT_DATA |
deleteDatabase |
없음 | 없음 | 없음 | 있음 |
리뷰가 "이미 갈라졌다"고 판정한 근거가 이 표다.
Task 1: 커널 2파일 + 단위 테스트
사본은 손대지 않는다. 코드 추가만 하므로 런타임 동작이 바뀌지 않는다.
Files:
- Create:
src/adapters/platform/indexeddb-connection.ts(~240 LOC) - Create:
src/adapters/platform/indexeddb-transaction.ts(~230 LOC) - Create:
tests/unit/indexeddb-connection.test.ts - Create:
tests/unit/indexeddb-transaction.test.ts - Modify:
docs/reviews/adapters/INVENTORY.md(행 2개 + 합계128/128→130/130)
Interfaces:
-
Produces: spec §3.2·§3.3의 전체 export 시그니처. Task 2~5가 이것만 쓴다.
-
Consumes: 기존 커널
snapshotAbortTimers(platform/abortable-operation.ts),contracts/result.ts의Result -
Step 1: 기존 커널 관례를 읽는다
src/adapters/platform/의 5파일을 읽고 주석 스타일(왜 이 규칙이 있는지를 근거와 함께 적는 방식), 에러 처리, 의존성 주입 방식을 파악한다. 새 파일은 그 관례를 따른다.
- Step 2: 가짜 IDB의 오류 배선을 확인한다
tests/helpers/memory-indexeddb.ts를 읽는다. 커널은 요청 레벨 onerror를 새로 보게 되므로 가짜가 request.error를 채우는지가 전제다.
확인됨(2026-09-16): 채운다. :167-170이 request.error = asException(error) 후 queueMicrotask로 onerror 발화, :309-311이 같은 일을 동기로 한다. 두 경로의 타이밍이 다르므로 테스트에서 주의한다.
- Step 3: 실패하는 테스트를 먼저 쓴다
최소한 아래를 고정한다. 각각 먼저 실패하는 것을 확인한 뒤 구현한다.
연결(tests/unit/indexeddb-connection.test.ts):
- open 성공 /
onerror/ native throw onblocked— deadline 미설정 시BLOCKED, deadline 경과 시BLOCKED_DEADLINE- upgrade
APPLIED - upgrade
REJECTED— versionchange 트랜잭션이 abort되어 스키마가 커밋되지 않는 것까지 확인 - upgrade가 throw →
REJECTED+detail에 thrown value newVersion이 null →upgrade실행 전에UPGRADE_REJECTED- admission
ADMIT/REJECT/FAIL— 거부 시 연결이 닫히는지 - 호출자가 포기한 뒤 늦게 도착한 연결이 닫히는지
CALLER_ABORTtranslate가 각 cause에 대해 호출되는지
트랜잭션(tests/unit/indexeddb-transaction.test.ts):
-
커밋 / abort
-
succeed()없이 완료 →NO_VALUE_PRODUCED -
첫 결과가 이긴다(
succeed이후fail무시) -
커서 순회와
SUSPEND스텝(중첩 요청 체인) -
예산 콜백이 "삭제 행 기준"과 "스캔 행 기준" 양쪽을 표현할 수 있는지
-
CP-4 필수 요건:
abort()가 throw하면 caller-abort 표시를 세우지 않고 transaction 이벤트가 결과를 정한다 -
Step 4: 구현한다 — 좁은 커널 함정을 피한다
기존 커널 abortable-operation.ts:11은 AbortTerminalReason 3멤버를 반환 타입에 박아서 5종이 필요한 http-execution-v3가 아예 못 썼다. 같은 실수를 반복하면 이 작업은 실패다.
구현 후 아래를 확인한다:
IndexedDbFailureCause가 어떤 공개 반환 타입에도 나타나지 않는가 (translate의 입력으로만 쓰이는가)- spec §3.4의 4개 사본 예시(RT/MT/OP/CP)가 전부 수용되는가
하나라도 수용되지 않으면 구현을 멈추고 보고한다. spec에 억지로 맞추지 않는다.
- Step 5: INVENTORY 갱신
docs/reviews/adapters/INVENTORY.md에 두 행을 알파벳 위치에 넣고 번호를 다시 매긴다. 링크는 같은 그룹(platform/) 기존 행과 동일하게 [Network/state](./01-network-and-state.md). 하단 합계를 130/130으로.
- Step 6: 게이트
corepack pnpm check:types:app && corepack pnpm check:types:test \
&& corepack pnpm lint && corepack pnpm check:architecture \
&& corepack pnpm check:adapter-inventory
corepack pnpm test:unit
Expected: 앞 묶음 전부 PASS. test:unit은 ci-artifact-contract.test.ts 외 실패 0.
- Step 7: 커밋
git add src/adapters/platform tests/unit/indexeddb-*.test.ts docs/reviews/adapters/INVENTORY.md
git commit -m "$(cat <<'EOF'
feat: add the shared IndexedDB connection and transaction kernel
네 어댑터가 각자 구현한 open/blocked/upgrade/트랜잭션 메커니즘을 커널로
모은다. 사본은 아직 이행하지 않았으므로 런타임 동작은 그대로다.
실패 분류는 커널에 넣지 않고 translate 콜백으로 주입한다. RT/MT/OP의
mapIndexedDbException과 CP의 mapBrowserDataException이 같은 에러에 다른
답을 내며, 그 차이를 통일하는 것은 별건이기 때문이다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
EOF
)"
Task 2: CP 이행 — indexeddb-checkpoint-store.ts (712 → 약 570)
CP를 먼저 하는 이유: 가장 작고, 동작 변화 지점이 가장 명확하며, 커널의 CP-4 요건(abort가 throw하면 caller-abort를 세우지 않는다)을 조기에 검증한다.
Files:
- Modify:
src/adapters/browser-transfer/resumable-upload/indexeddb-checkpoint-store.ts - Modify:
tests/unit/resumable-upload-checkpoint.test.ts(동작 변화 지점 고정)
Interfaces:
-
Consumes: Task 1의
openIndexedDbDatabase,runIndexedDbTransaction,deleteIndexedDbDatabase -
Produces: 없음 (공개 포트 형태 불변)
-
Step 1: 동작 변화 지점을 테스트로 먼저 고정한다
구현 전에 아래 5개가 현재 동작대로 통과하는지 확인한다. 이행 후에도 같아야 한다.
| # | 지점 | 현재 동작 | 깨지면 |
|---|---|---|---|
| CP-1 | 값 없는 완료 | CORRUPT_DATA/RECONCILE (L541-547) |
번역기가 NO_VALUE_PRODUCED → CORRUPT_DATA를 명시 매핑 안 하면 UNAVAILABLE로 바뀜 |
| CP-2 | durability |
옵션 bag 없음 (L512) | 커널 기본이나 "strict"를 넣으면 체크포인트 쓰기가 조용히 느려짐 (성능 회귀) |
| CP-3 | nativeFailure |
기록 후 즉시 abort (L577-588) | requestFailed(abort 안 함)로 바꾸면 요청 실패 후에도 뒤 요청이 커밋됨. compareAndSwap의 get→put 체인(L321-343)에서 특히 위험 |
| CP-4 | abort()가 throw |
failure를 되돌린다 (L528, L536) |
커밋된 체크포인트를 ABORTED로 보고 |
| CP-5 | deletePartition blocked 타이머 |
네이티브 setTimeout (L450) |
주입형 timers로 바뀜. tests/unit/resumable-upload-checkpoint.test.ts:194가 이 경로를 봄 |
기존 단언 위치: :142 CONFLICT/RECONCILE, :190 UNAVAILABLE/RESUME, :194 blocked PENDING, :238 false-abort 금지.
- Step 2: 삭제 대상 블록을 커널 호출로 바꾼다
| 블록 | 줄 | 대체 |
|---|---|---|
TransactionContext 타입 |
L491-495 | 커널 타입 |
openAndBind의 open 요청 Promise 배선 |
L163-175, L198-217 | openIndexedDbDatabase (upgrade 본문 L176-197은 upgrade 콜백으로, bindScope 호출은 admit으로) |
runCheckpointTransaction |
L497-596 | runIndexedDbTransaction |
bindScope의 트랜잭션 배선 |
L602-623 | runIndexedDbTransaction (검증 로직 L624-652는 queue로 그대로) |
deletePartition의 blocked/settle 배선 |
L431-462, L472-483 | deleteIndexedDbDatabase |
남길 것: PENDING_DELETIONS 레지스트리(L31-41) + 생성 시 검사(L116-122), uploadCheckpointDatabaseName(L73-83), sameScopeBinding(L656-672), snapshotScope(L674-691), snapshotCheckpoint(L693-711).
- Step 3: 게이트 + 표적 테스트
corepack pnpm check:types:app && corepack pnpm lint && corepack pnpm check:architecture
npx vitest run tests/unit/resumable-upload-checkpoint.test.ts --reporter=default
Expected: 전부 PASS. CP-1~CP-5가 이행 전과 같은 답을 내야 한다.
- Step 4: 커밋
git add src/adapters/browser-transfer tests/unit/resumable-upload-checkpoint.test.ts
git commit -m "refactor: move the upload checkpoint store onto the IndexedDB kernel
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>"
Task 3: MT 이행 — indexeddb-maintenance.ts (1558 → 약 1370)
MT를 두 번째로 하는 이유: 커널 사용자 중 연결 핸들을 안 쓰는 유일한 사본이라 openIndexedDbDatabase 단독 사용 경로를 검증한다.
Files:
- Modify:
src/adapters/storage/indexeddb/indexeddb-maintenance.ts - Modify:
tests/unit/indexeddb-maintenance.test.ts
Interfaces:
-
Consumes: Task 1의
openIndexedDbDatabase,openIndexedDbTransaction,runIndexedDbTransaction,walkIndexedDbCursor -
Step 1: 두 개의 함정을 먼저 이해한다
MT-1 (가장 위험). blockedTimeoutMs를 넘기지 마라. 안 넘겨야 오늘 동작(blocked 이벤트 즉시 BLOCKED, L485-492)이 유지된다. 넘기면 배치가 최대 그 시간만큼 매달린다. tests/unit/indexeddb-maintenance.test.ts:289-290이 BLOCKED/retryable:true/RELOAD_OTHER_CONTEXTS를 즉시 받길 기대하므로 값을 넣으면 타임아웃으로 실패한다.
MT-2. upgrade 콜백을 생략하라. 생략해야 오늘의 "upgrade는 곧 실패"(L477-484)가 유지된다. 커널은 생략을 UPGRADE_REJECTED로 해석한다. 실수로 upgrade: () => ({kind:"APPLIED"})를 넣으면 잘못된 스키마로 열린다.
- Step 2: 삭제 대상 블록을 커널 호출로 바꾼다
| 블록 | 줄 | 대체 |
|---|---|---|
TransactionContext 타입 |
L97-101 | 커널 타입 |
openExactVersion의 배선분 |
L434-508, L551-585 | openIndexedDbDatabase (L509-550을 admit으로 이식) |
createTransaction |
L587-604 | openIndexedDbTransaction |
runTransaction |
L606-705 | runIndexedDbTransaction |
| 커서 2곳의 deadline/maxRows/abort 보일러플레이트 | L799-833, L1451-1479 | walkIndexedDbCursor |
MT-3. database.onversionchange = () => database.close()(L543)를 admit 안으로 옮긴다. admit은 성공 경로에서만 실행되므로 등록 시점이 오늘과 같다.
남길 것: 체크포인트 상태기계(readCheckpoint L707-756, commitPrepared L969-1251), prepareRecords(L863-967), clock/epochClock(L401-421), countBucket(L239-245), 저장 술어(L118-223).
- Step 3: 게이트 + 표적 테스트
corepack pnpm check:types:app && corepack pnpm lint && corepack pnpm check:architecture
npx vitest run tests/unit/indexeddb-maintenance.test.ts --reporter=default
Expected: 전부 PASS. 특히 :289-290이 즉시 BLOCKED를 받아야 한다(타임아웃이 아니라).
- Step 4: 커밋
git add src/adapters/storage tests/unit/indexeddb-maintenance.test.ts
git commit -m "refactor: move IndexedDB maintenance onto the kernel
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>"
Task 4: OP 이행 — indexeddb-opfs-journal.ts (1817 → 약 1690)
이 계획에서 동작 변화가 가장 큰 태스크다.
Files:
- Modify:
src/adapters/storage/opfs/indexeddb-opfs-journal.ts - Modify:
tests/unit/indexeddb-opfs-journal.test.ts - 가능성:
tests/helpers/memory-indexeddb.ts보강
Interfaces:
-
Consumes: Task 1의
openIndexedDbDatabase,createIndexedDbConnection,runIndexedDbTransaction,openIndexedDbTransaction,walkIndexedDbCursor -
Step 1: OP-1을 테스트로 먼저 드러낸다 — 이 태스크의 핵심
오늘 OP는 트랜잭션 안의 개별 요청에 .onerror를 하나도 안 단다(파일 전체에서 request .onerror는 L1042의 open 요청 하나뿐). 요청 실패는 transaction.onabort로만 흘러 mapIndexedDbException(transaction.error)(L1158)가 된다.
커널을 쓰면 요청 자신의 오류가 보고된다. 구체적으로 LOGICAL_KEY_INDEX가 unique: true(L1000-1004)이므로 **중복 put은 요청 레벨 ConstraintError → CONFLICT**가 되고, 오늘은 transaction.error가 무엇이냐에 따라 달라진다.
이행 전에 중복 put 테스트를 tests/unit/indexeddb-opfs-journal.test.ts에 추가해 현재 답을 기록하고, 이행 후 달라진 답을 의도된 변경으로 승인한다. 가짜 IDB가 이 경로를 표현하지 못하면 tests/helpers/memory-indexeddb.ts를 먼저 보강한다.
- Step 2: OP-2를 확인한다
오늘 OP의 runTransaction은 succeed 이후 fail이 와도 explicitFailure가 이기지만(L1133-1141, hasValue는 true 유지) oncomplete는 값을 반환한다(L1143-1153). 커널의 "첫 결과가 이긴다" 규칙을 따르면 succeed 후의 fail이 무시된다.
현재 OP 코드에 그 순서가 실제로 발생하는 경로가 있는지 확인하라(spec은 미확인으로 남겼다). 없으면 변화 없음으로 기록하고 넘어간다.
- Step 3: 삭제 대상 블록을 커널 호출로 바꾼다
| 블록 | 줄 | 대체 |
|---|---|---|
TransactionContext 타입 |
L48-51 | 커널 타입 |
| scheduler 기본값 인라인 | L144-153 | snapshotAbortTimers |
openDatabase의 배선분 |
L961-996, L1031-1072 | openIndexedDbDatabase + createIndexedDbConnection (upgrade 본문 L997-1029는 upgrade 콜백으로 그대로 이동) |
runTransaction |
L1098-1174 | runIndexedDbTransaction |
strictReadwriteTransaction |
L1176-1190 | openIndexedDbTransaction(…, "strict") |
| 커서 2곳의 limit 루프 | L604-633, L677-712 | walkIndexedDbCursor |
OP-4. signal은 넣지 않는다. 오늘 없는 취소를 새로 만들지 않는다.
- Step 4: 게이트 + 표적 테스트
corepack pnpm check:types:app && corepack pnpm lint && corepack pnpm check:architecture
npx vitest run tests/unit/indexeddb-opfs-journal.test.ts tests/unit/opfs-byte-store.test.ts --reporter=default
Expected: PASS. OP-1로 인한 답 변화는 Step 1에서 승인한 것만 있어야 한다.
- Step 5: 커밋
git add src/adapters/storage tests
git commit -m "refactor: move the OPFS journal onto the IndexedDB kernel
요청 레벨 오류를 처음으로 보게 된다. unique 인덱스 위반이 트랜잭션 abort
사유가 아니라 요청 자신의 ConstraintError로 보고된다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>"
Task 5: RT 이행 — indexeddb-runtime.ts (2902 → 약 2665)
가장 크고 마지막이다. 다른 셋이 커널을 전부 검증한 뒤에 옮긴다.
Files:
-
Modify:
src/adapters/storage/indexeddb/indexeddb-runtime.ts -
Modify:
tests/unit/indexeddb-runtime.test.ts -
Step 1: RT-1을 번역기에 명시한다
close()가 진행 중 open을 끝내는 원인이 unavailable(operation)(L743)에서 translate({kind:"CLOSED"})로 바뀐다. 번역기가 CLOSED → unavailable을 명시 매핑해야 오늘 동작이 유지된다. 빠뜨리면 close() 중 open이 ABORTED로 보고된다.
기존 실패 코드 단언 위치: tests/unit/indexeddb-runtime.test.ts:339-340, :371-372, :454, :534, :549-550, :734-735.
- Step 2: 삭제 대상 블록을 커널 호출로 바꾼다
| 블록 | 줄 | 대체 |
|---|---|---|
defaultScheduler |
L108-117 | snapshotAbortTimers(scheduler) |
TransactionContext 타입 |
L85-89 | IndexedDbTransactionContext |
waitForOpeningAttempt |
L659-682 | connection.acquire(signal) |
startOpeningAttempt의 배선분 |
L684-745, L776-843, L872-921 중 배선분 | openIndexedDbDatabase (upgrade/admit 콜백 본문은 그대로) |
createTransaction |
L957-974 | openIndexedDbTransaction |
runTransaction |
L976-1074 | runIndexedDbTransaction |
| 커서 5곳의 보일러플레이트 | L1374-1382, L1454-1471, L2364-2389, L2531-2548, L2573-2590, L2644-2661, L2718-2735 | walkIndexedDbCursor + budget.admit |
RT-2. settleNativeRequest/activeOpeningGeneration(L595, L691-697)이 사라진다. 커널의 settle-once와 단일 비행이 같은 역할을 한다. 의미는 같지만 경합 순서가 달라질 수 있다 — 동시 open 테스트를 주의해서 본다.
RT-3. openingRequest 필드(L593)는 오늘도 L712·L2878의 대입 외에 읽는 곳이 없다. 삭제한다.
남길 것: 코덱/영수증/보존/예산(bytes)/governance/migration 목록/상태 브로드캐스트/monotonicClock/countBucket/저장 레코드 술어/purgePartitionRecords의 스토어 순서 로직 — 전부 정책이다.
- Step 3: 게이트 + 전체 테스트
corepack pnpm check:types && corepack pnpm lint && corepack pnpm check:architecture \
&& corepack pnpm check:adapter-inventory
corepack pnpm test:unit
corepack pnpm test:integration
Expected: ci-artifact-contract.test.ts 외 실패 0.
- Step 4: 커밋
git add src/adapters/storage tests/unit/indexeddb-runtime.test.ts
git commit -m "refactor: move the IndexedDB runtime onto the kernel
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>"
완료 판정
corepack pnpm check:types
corepack pnpm lint
corepack pnpm check:architecture
corepack pnpm check:adapter-inventory
corepack pnpm check:bundle
corepack pnpm test:unit # ci-artifact-contract.test.ts 외 실패 0
corepack pnpm test:integration
corepack pnpm test:component
corepack pnpm test:browser-file-storage-removal # error TS 0건
corepack pnpm test:realtime-removal # error TS 0건
그리고 아래가 0이어야 한다 — 커널 밖에 남은 open 요청 배선:
grep -rn "createObjectStore\|onupgradeneeded" src/adapters --include='*.ts' \
| grep -v "src/adapters/platform/" | grep -v "upgrade" | wc -l
실브라우저 확인이 필요하다. 이 계획은 가짜 IDB 위에서만 검증된다. tests/browser-capabilities/indexeddb-runtime.spec.ts(1033 LOC), opfs-runtime.spec.ts(221), resumable-upload.spec.ts(526)를 실브라우저에서 돌려야 blocked/versionchange 실동작 회귀를 잡는다.
이 계획이 하지 않는 것
- 실패 매핑 4벌 통일 —
mapIndexedDbException과mapBrowserDataException이 같은 에러에 다른 답을 내는 것은 별건이다. 이 계획은 그 차이를translate주입으로 보존한다. deleteDatabase를 3벌에 추가 — CP에만 있는 것이 의도다(spec §2.3).- 대형 파일 분할 — 커널 이행으로 RT가 2902 → 2665가 되지만 여전히 크다. 분할은 별도 계획이다.