# Browser file and storage recovery runbook 이 runbook은 VD-11 capability를 실제 프로젝트에서 선택한 뒤 사용하는 production 운영 template이다. 현재 skeleton에는 native reference runtime이 `AVAILABLE_NOT_COMPOSED`로 존재하지만 제품 dataset과 bootstrap에는 연결되지 않았으므로 이 문서 자체가 현재 서비스 incident procedure를 활성화하지 않는다. 설치 branch는 owner, dashboard, alert threshold, kill-switch key, backend/storage 연락처와 evidence 위치를 채워야 한다. VD-15가 설계한 origin-wide pressure/GC coordinator, OPFS/Cache forward migration, OPFS real readiness preflight, bounded Cache cleanup과 preview decode probe는 현재 `DESIGNED_NOT_IMPLEMENTED`다. 아래 절차에서 이 기능을 전제로 한 자동 조치는 해당 runtime이 구현·조합된 제품에서만 실행한다. 현재 reference primitive를 coordinator 완료 증거로 사용하지 않는다. ## OPFS 보상 실패와 reconcile (STO-01) `put()`이 실패했는데 보상 cleanup effect가 확인되지 않으면 runtime은 실패를 `OBJECT_RECONCILE` / `CONFLICT`(recovery `RETRY`)로 보고하고 journal row를 남긴다. 이는 결함이 아니라 설계된 상태다. 1. journal에 `PREPARING` 또는 `FILES_READY` row가 남아 있는지 확인한다. 남아 있다면 staging bytes가 아직 존재할 수 있다는 뜻이다. 2. `maintenance.reconcile()`을 실행한다. reconcile은 같은 exact physical generation token만 삭제하고, effect가 여전히 `EFFECT_UNKNOWN`이면 journal을 유지한 채 다시 `OBJECT_RECONCILE`을 반환한다. 3. journal row를 수동으로 삭제하지 않는다. row가 사라지면 stale staging을 추적할 근거가 사라지고 quota만 누수된다. 4. OPFS root나 journal database를 통째로 삭제하거나 schema를 downgrade하지 않는다. rollback은 새 v2 write admission을 닫고 v1+v2 reader를 유지하는 것으로 수행한다. ## 1. 공통 원칙 incident 중에도 다음 작업은 금지한다. - 자동 또는 일괄 `deleteDatabase()` - origin의 모든 `caches.keys()` 삭제 - user-authored/unsynced IndexedDB 또는 OPFS data 자동 purge - schema version downgrade - 무한 reload/update loop - filename, path, record/object key, URL/query, digest, body를 incident log에 복사 - fake/unavailable adapter로 바꾸고 정상 복구로 선언 공통 안전한 degrade 순서는 다음과 같다. ```text 선택 capability의 신규 write/activation 중지 -> 진행 중 operation 정리 -> read-only -> online-only/network-only -> authoritative server re-read ``` read-only나 online-only가 사용자 작성 내용을 잃게 한다면 먼저 export/sync 경로를 제공하고 UI에 명시적으로 알린다. ## 2. 최초 10분 1. 영향 release, browser family/version, capability와 최초 발생 시각을 확인한다. 2. user dismissal과 실제 failure를 분리한다. 3. safe metric의 `failure_kind`, `phase`, `strategy`, `backend`, `size_bucket`, `release_id`만으로 범위를 좁힌다. 4. integrity mismatch, authorization/cache classification breach, committed user-data corruption이면 신규 write/cache activation을 즉시 중지한다. 5. kill switch를 가장 좁은 범위로 적용한다. 6. current와 N-1 release의 schema/cache compatibility를 확인한다. 7. 복구 전후 diagnostic count와 browser smoke evidence를 저장한다. 심각도 시작점: | 상황 | 시작 심각도 | | --- | --- | | authorization 우회, private/auth response cache, content integrity mismatch | P1 | | committed user-authored data unreadable/corrupt, widespread migration failure | P1 | | upload/download 실패 급증, IndexedDB blocked 증가, OPFS reconstructable loss | P2 | | enhanced picker만 실패하고 baseline 정상 | P3 | 실제 조직 severity policy가 있으면 그 정책이 우선한다. ## 3. File/picker/download와 별도 upload example ### 신호 - `PERMISSION_DENIED`, `NOT_READABLE`, `INTEGRITY_FAILED` - closed byte stream이 failure chunk 뒤 추가 byte를 내보내거나 raw exception을 throw - browser handoff는 정상인데 confirmed save가 감소 - large download에서 memory/stall failure - 별도 backend upload를 설치한 feature만 session expiry/conflict, quarantined/available 전환 정체 ### 확인 순서 1. 사용자가 실제 취소한 flow가 error로 집계되지 않았는지 확인한다. 2. user activation 전에 비동기 작업이 추가됐는지 확인한다. 3. native input fallback이 Chromium/Firefox/WebKit에서 동작하는지 확인한다. 4. count, per-file, total bytes, MIME/extension/signature policy version을 확인한다. raw policy가 port caller에서 들어오지 않고 composition registry의 정확한 `FilePolicyReference` identity로 resolve되는지, verification receipt가 같은 profile/file snapshot에 binding됐는지 확인한다. 같은 `policyKey`/`intention`을 가진 새 객체는 거절되어야 한다. 5. File/OPFS/Cache/download byte source가 chunk마다 closed Result를 반환하고 첫 failure에서 consumer와 producer 모두 종료되는지 확인한다. 6. download `Content-Type`, `Content-Disposition`, CORS exposed header와 short-lived capability expiry를 확인한다. browser-managed resolver receipt가 caller의 branded receipt와 정확히 같고 resource ID, media type, safe extension, server max, optional digest와 expiry를 synchronous하게 정확히 binding하는지 확인한다. 현재 save path는 whole-object foreground streaming이며 Range resume가 아니다. partial destination에 append하지 않고, Range를 선택한 제품은 VD-14와 browser-transfer runbook의 별도 절차를 따른다. 7. whole-buffer API 또는 object URL lease 누수가 배포됐는지 확인한다. 8. image preview를 설치했다면 object URL 발급 전에 header dimensions, pixel, decoded-byte와 animation/static-only 제한을 검사하는 VD-15 probe가 실제 조합됐는지 확인한다. 현재 byte/signature 검사만으로 decode safety를 주장하지 않는다. 9. upload를 별도 설치했다면 backend 401/403/409/413/415/422/429/5xx, CORS/preflight, session TTL/clock skew, part checksum, idempotency, orphan cleanup과 quarantine queue를 그 feature runbook에서 확인한다. ### 안전한 조치 - enhanced open/save picker off → native input/direct authorized download - active-content preview off - preview pixel/decode/frame probe가 없거나 실패하면 object URL preview off - client-generated large download off → server-side artifact generation - broken stream adapter off → bounded fallback 또는 operation 중지; raw exception을 성공/EOF로 변환하지 않음 - upload를 별도 설치했다면 multipart concurrency/part size 하향, resumable off → approved hard cap의 simple upload, 문제 MIME 임시 차단 - expired upload session은 authorization 후 새 session; 기존 session을 재활성화하지 않음 성공 판정: - user cancellation 제외 file/download start 대비 success rate 회복 - integrity/authorization mismatch 0 - 세 browser baseline smoke 통과 - object URL/file ref cleanup count 0 leak - preview를 설치한 경우 malformed/oversize/animated image가 object URL 발급 전에 거절되고 native decode resource 0 leak - upload를 별도 설치했다면 orphan session/quarantine backlog SLO와 session cleanup 0 leak ## 4. IndexedDB upgrade blocked/versionchange ### 신호 - `BLOCKED`, `UPGRADE_BLOCKED` 또는 blocked duration bucket 증가 - `versionchange` 뒤 connection이 남음 - repeated reload/update loop - open/maintenance의 `POLICY_REJECTED`: immutable dataset binding missing/mismatch ### 확인 순서 1. target schema와 current/future schema, release ID를 확인한다. 2. registry-issued `authorityToken`, `namespaceToken`, `partitionToken`이 같은 dataset의 승인된 값인지 확인한다. readable namespace/account/business ID를 token이나 physical DB name에 넣지 않는다. 3. physical DB name이 세 opaque token에서만 파생됐는지, caller `databaseNameAssertion`이 exact match인지 확인한다. 4. governance store의 immutable scope + full `BrowserStoragePolicy` binding을 versionchange, post-open, maintenance 모두에서 확인한다. raw token이나 binding 내용을 incident log에 복사하지 않는다. 5. old tab, worker 또는 test/debug context가 connection을 닫지 않는지 확인한다. 6. 모든 connection에 `versionchange`/forced-close handler가 설치됐는지 확인한다. 7. BroadcastChannel prepare hint는 참고만 하고 실제 open/connection registry를 확인한다. 8. N-1 bundle이 future schema를 read-only/online-only로 처리하는지 확인한다. ### 안전한 조치 - 신규 offline write와 background migration 중지 - 사용자에게 다른 tab close와 명시적 retry UI 제공 - current connection을 draining 후 close - upgrade release rollout 중지 또는 compatible N-1 online-only로 rollback - binding mismatch를 caller DB 이름 변경, policy 덮어쓰기 또는 DB 전체 삭제로 우회하지 않고 registry/composition 오류를 forward fix remote tab 강제 종료, database 삭제, 무한 reload는 금지한다. 성공 판정: - connection registry와 blocked request 0 - fresh tab과 two-tab versionchange/blocked smoke 통과 - future schema에서 N-1이 destructive write 없이 기동 - open과 maintenance에서 동일 scope/policy binding 검증 통과 ## 5. IndexedDB migration/corruption ### 신호 - `MIGRATION_FAILED`, unknown codec, schema postcondition failure - `CORRUPT_DATA`, `NOT_READABLE`, forced close - transaction request success 후 commit failure - `LIMIT_EXCEEDED`: dataset logical byte/receipt budget 초과 - lifecycle batch가 proof 없이 실행되거나 TTL/UNTIL_SYNCED record가 잘못 노출·삭제 - migration/lifecycle batch가 500 rows 또는 30,000ms를 넘거나 checkpoint 없이 중단 ### 확인 순서 1. DDL schema version과 record codec version을 구분한다. 2. migration ID, last safe keyset checkpoint, revision fence, `budgetExhausted`, processed/remaining bucket과 old-writer drain proof를 확인한다. 3. caller budget과 구현 absolute budget(500 rows/30,000ms), async operation 사이 deadline check를 확인한다. 4. codec `measureStoredBytes`, retention sidecar `measuredBytes`, `dataset-budget.usedBytes/receiptCount`가 CAS/remove/lifecycle/migration transaction과 함께 갱신됐는지 확인한다. 이 값은 native physical size가 아니다. 5. actual `StoredRecord`가 `key/codecVersion/revision/payload`만 포함하고 `writtenAtEpochMs`, synchronization, `measuredBytes`, `eligibleAtEpochMs`는 `retentionStore` sidecar에 분리돼 있는지 확인한다. 6. receipt retention이 31일 이하인지, configured `maxIdempotencyReceipts`가 1,000,000 이하인지, prune/purge가 receipt count를 같은 transaction에서 감소시켰는지 확인한다. 7. TTL record가 sweep 전 read에서 `EXPIRED_RESOURCE`, query에서 skip되는지, `UNTIL_SYNCED`는 `CONFIRMED`만 eligible한지 확인한다. 8. lifecycle action마다 composition의 `authorizeLifecycle(action, scope, frozen policy)`가 호출됐고 opaque proof가 검증 후 비영속·비관측 상태로 폐기됐는지 확인한다. 9. historical schema fixture에서 같은 migration을 재현한다. 10. reconstructable, synced copy, unsynced/user-authored 분류를 확인한다. 11. partial batch가 row/sidecar/budget/checkpoint와 원자적으로 rollback됐는지 확인한다. 12. full partition purge가 record/retention/idempotency와 등록된 모든 `lifecycleMetadataStores`를 bounded transaction으로 정리하고 immutable governance binding은 유지하는지 확인한다. 13. raw record, opaque scope/proof를 ticket/log에 복사하지 않고 승인된 local recovery tooling만 사용한다. ### 안전한 조치 - migration 중지, repository read-only/online-only - reconstructable만 purge/re-fetch - synced copy는 revision 확인 후 rehydrate - unsynced/user-authored는 quarantine marker + export/sync/recovery - adapter bounded reopen 최대 한 번 - schema/codec compatible fix를 새 forward migration으로 배포 - migration은 old-writer drain authority를 복구한 뒤 더 작은 bounded batch로 checkpoint부터 resume - lifecycle authority가 불명확하면 deletion을 재시도하지 않고 read-only; proof를 운영자가 수동 생성·재사용하지 않음 database 전체 삭제는 data owner가 export/recovery와 영향 scope를 승인한 별도 action이다. 성공 판정: - historical fixture의 fresh, interrupted, resume, replay가 모두 통과 - corrupted user-authored row 자동 삭제 0 - migration canary failure 0 - N-1 rollback/read-only smoke 통과 - dataset budget/sidecar/receipt count 재검증과 bounded lifecycle fixture 통과 ## 6. Origin quota, persistence denial, storage eviction ### 신호 - usage ratio pressure bucket 증가 - `QUOTA_EXCEEDED`, `STORAGE_EVICTED` - startup sentinel과 logical manifest 불일치 - persistence request denied ### 확인 순서 1. `estimate()`가 rough origin total임을 전제로 IndexedDB/OPFS/Cache 공동 증가를 확인한다. 2. exact free space를 계산하거나 예약했다고 가정하지 않는다. 3. expired/reconstructable, synced copy, user-authored 사용량 bucket을 분리한다. 4. IndexedDB의 exact logical dataset budget과 origin-wide `estimate()`를 구분한다. 전자는 codec measurement + conservative reservation이고 native physical usage/free space가 아니다. 5. private mode, WebView, browser storage policy와 user clear 여부를 확인한다. 6. persistence denial을 capability failure가 아닌 best-effort outcome으로 처리했는지 확인한다. ### 안전한 조치 현재 공통 coordinator가 없으면 다음 순서를 자동 실행하거나 “exact one retry”를 보장한다고 기록하지 않는다. 제품 owner가 store별 primitive와 retention policy를 확인해 수동/feature-local로 안전하게 수행하거나 write를 read-only/online-only로 닫는다. VD-15 coordinator가 조합된 경우에만 동일 mutation owner 안에서 다음을 bounded orchestration으로 실행한다. 1. 신규 speculative/reconstructable write admission 중지 2. incomplete candidate와 stale staging 제거 3. expired reconstructable record/object 제거 4. grace가 지난 unreferenced immutable chunk 제거 5. inactive public cache release 제거 6. authoritative revision/sync receipt가 확인된 synced copy compact 7. unsynced/user-authored sync/export UI 8. 필요하면 offline read-only/online-only 각 GC invocation은 기본 100 items/5초, 구현 절대 상한 500 items/30초와 opaque cursor를 지킨다. failed write의 자동 retry는 다음 조건이 모두 맞을 때만 정확히 한 번 허용한다. - 첫 write가 실제 quota failure로 원자적 rollback됨 - 같은 idempotency key, revision fence와 payload digest - 외부 side effect/cross-store publish가 commit되지 않음 - bounded GC가 실제 candidate를 제거했거나 pressure가 내려감 - 현재 revision/generation 재확인과 새 admission token 발급 두 번째 failure나 effect certainty가 unknown이면 retry하지 않는다. `persist()`를 boot나 반복 loop에서 요청하지 않는다. 사용자가 durable offline 기능을 선택하고 unsynced data 보호 이유를 이해하는 동작에서만 요청한다. 성공 판정: - coordinator를 설치한 경우 pressure가 hysteresis lower bound 아래 - partial logical/physical marker mismatch는 rehydrate 또는 explicit read-only/export-required로 닫힘 - 모든 local marker가 소실된 경우 backend opaque installation epoch로 authorization 후 구분하거나 `storage-reset-possible` ambiguity UX를 표시함; 이를 “첫 설치로 판별 완료”라고 기록하지 않음 - user-authored 자동 purge 0 - storage clear 뒤 reconstructable rehydrate/explicit degraded UI 통과 ## 7. OPFS partial write/corruption/worker crash 현재 capability/property probe와 native conformance test는 존재하지만 composition readiness에서 worker/lock/journal/small write-read-delete-cleanup을 한 번에 검증하는 real preflight는 아직 없다. preflight success를 운영 전제에 넣으려면 VD-15 목표 runtime을 먼저 구현한다. ### 신호 - journal이 `PREPARING`/`FILES_READY`에 오래 머묾 - committed logical row와 physical manifest/file 불일치 - digest/length mismatch - worker crash, handle lock timeout, `NoModificationAllowedError` - sensitive policy maintenance의 `POLICY_REJECTED`, proof replay/expiry 또는 authority provider/consumer unavailable ### 확인 순서 1. mutation Web Lock owner와 timeout을 확인한다. 2. worker protocol/version과 모든 sync handle의 `finally close`를 확인한다. 3. journal operation phase별 count와 age bucket을 확인한다. 4. VD-15 forward migrator를 설치하지 않은 현재 v1 reference runtime에서는 physical layout이 `/ca-frontend-opfs-v1/authorities////` 아래의 `objects`, `chunks/sha256`, `staging`인지 확인한다. readable namespace, filename, account ID가 path segment이면 신규 write를 중지한다. migrator를 설치한 조합에는 이 v1 path를 authoritative target 조건으로 적용하지 않는다. 5. journal의 physical scope binding과 logical namespace binding이 둘 다 존재하고, 같은 scope/policy fingerprint를 가리키는지 확인한다. 한쪽만 없거나 mismatch면 자동 재생성하지 않는다. 6. runtime/byte-store composition의 frozen scope와 policy namespace가 일치하는지 확인한다. 7. `LOGOUT`, `UNTIL_SYNCED`, `ACCOUNT_DELETION` maintenance에는 composition의 `requestMaintenanceAuthority` provider와 `consumeMaintenanceAuthority` consumer가 둘 다 주입됐는지 확인한다. application request에는 proof 필드가 없어야 한다. 8. provider가 exact reason/frozen scope/frozen policy에 묶인 새 proof와 최대 5분 expiry를 발급하고, consumer가 같은 binding을 원자적으로 검증·consume해 replay를 거절하는지 확인한다. proof/token/raw scope는 log에 복사하지 않는다. 9. physical path/ID/digest를 log에 복사하지 않고 reconciliation tool이 manifest schema와 digest를 검증하게 한다. 10. logical row가 commit authority인지 확인한다. 11. VD-15 forward migrator를 설치했다면 source/target physical layout와 journal version, migration checkpoint, copy-on-write generation, publish authority, rollback window와 N-1 reader 결과를 확인한다. ### recovery - `PREPARING`: partial staging 검증 후 idempotent resume 또는 purge - `FILES_READY`: expected generation/digest 일치 시 logical commit, 아니면 quarantine - `COMMITTED` + file missing/corrupt: reconstructable만 server rehydrate; user-created private는 read-only/export/recovery - stale staging/orphan chunk는 grace period와 bounded mark/sweep 후 제거 - lock/worker 문제면 OPFS write off → approved capped fallback 또는 online-only - scope binding mismatch면 affected scope를 read-only로 격리하고 registry/ composition을 forward fix; 다른 token path로 bytes를 이동하거나 추측 복구 금지 - sensitive maintenance authority provider/consumer가 없거나 replay/expiry 검증이 실패하면 삭제를 재시도하지 않고 read-only로 전환한다. 운영자가 proof를 직접 생성·주입·재사용하지 않는다. - forward migration 중단이면 target generation을 publish하지 않고 bounded checkpoint에서 resume한다. publish 뒤에는 source generation을 rollback window까지 보존하고, N-1이 target을 이해하지 못하면 read-only/online-only로 닫는다. source/target을 in-place 혼합하거나 layout version을 내리지 않는다. committed object를 sync handle로 in-place repair하지 않는다. 새 generation에 정상 object를 만들고 generation CAS로 logical pointer를 전환한다. 성공 판정: - unresolved old journal 0 - committed read integrity failure 0 - worker/handle registry 0 leak - 각 journal phase fault injection과 concurrent put/delete/GC 통과 - sensitive maintenance의 fresh proof consume 성공, replay/expired/mismatched proof와 provider/consumer 누락은 모두 deletion 전 fail-closed ## 8. Cache Storage/Service Worker stale or poisoned release public static Cache release runtime은 존재하지만 cursor/count/deadline이 있는 bounded inspect/cleanup과 Service Worker waiting/client-drain controller는 아직 없다. Service Worker는 제품이 PWA/offline fetch를 별도로 선택한 경우에만 아래 worker 절차를 적용한다. ### 신호 - incomplete candidate activation - integrity/type/size mismatch - auth/private/no-store/opaque policy rejection - Service Worker를 별도 선택한 조합의 stale worker/update/reload loop - active asset miss 또는 offline boot failure ### 확인 순서 1. hosting/CDN cache와 browser Cache Storage를 별개로 확인한다. 2. active/candidate/previous release ID와 manifest digest를 확인한다. 3. candidate 모든 entry의 status, `expectedContentType`, size, integrity 검증 marker를 확인한다. canonical manifest digest가 정규화된 Content-Type까지 binding하는지 확인한다. 4. 실제 response의 정규화된 `Content-Type`과 manifest `expectedContentType`이 정확히 일치하는지 확인한다. body digest가 맞아도 type mismatch candidate는 폐기한다. 5. config/release manifest/auth/API가 network-only인지 확인한다. 6. query/Vary exact match와 `ignoreSearch/ignoreVary` 미사용을 확인한다. 7. Service Worker를 별도 선택했다면 old controlled clients와 current waiting/active worker 상태를 확인한다. 8. Service Worker를 별도 선택했다면 unregister만 하고 owned cache cleanup을 빠뜨리지 않았는지 확인한다. 9. cleanup caller가 cache name, release registry ID 또는 retain list를 제출할 수 없고 보존 집합이 verified active pointer와 composition retention에서만 계산되는지 확인한다. 10. active pointer/release marker control JSON이 정확히 2 MiB(2,097,152 bytes) cap의 stream reader와 strict UTF-8/runtime schema를 통과하며 oversized body를 초과 지점에서 cancel하는지 확인한다. ### 안전한 조치 static Cache-only 조합: - 신규 candidate activation/cache write 중지 - verified current 또는 previous release로 explicit rollback - incomplete candidate와 runtime-public cache부터 owned-prefix cleanup - 현재 cleanup/inspect가 cache 개수에 대해 unbounded임을 고려해 incident invocation을 추가 budget으로 반복 실행하지 않는다. VD-15 bounded cursor runtime 구현 전에는 큰 namespace를 자동 sweep하지 않는다. Service Worker를 별도 선택한 조합에서만 추가: - fetch interception을 network-only kill switch로 전환 - new worker activation 중지 - 필요하면 unregister + 다음 navigation cleanup migration - old controlled client가 drain되기 전 해당 release cleanup 금지 hosting/CDN purge는 두 branch와 분리된 provider 절차로 실행한다. auth/private response가 cache에 실제 저장됐을 가능성이 있으면 P1로 승격하고 owned affected namespace를 정확히 식별해 제거한다. origin의 unrelated cache는 삭제하지 않는다. 성공 판정: - static Cache-only 조합은 verified active/previous release와 candidate cleanup이 일관되고 network-only fallback smoke를 통과 - Service Worker를 별도 선택한 조합만 clean/old controlled client가 같은 verified release로 수렴하고 online/offline/update/rollback smoke를 통과 - auth/private cache entry 0 - update/reload loop 0 - config/release manifest network-only header contract 통과 ## 9. Drill evidence 실제 capability를 project catalog의 `INSTALLED`로 바꾸기 전에 최소 다음 drill을 실행하고 release-specific evidence를 저장한다. 이 legacy selection label은 primary status `COMPOSED`와 별도 `TrafficAdmission/RuntimeHealth/PromotionEvidence` 축이 준비된 제품 상태를 뜻하며 새로운 primary status literal이 아니다. 각 축의 literal과 contract/provider/browser/operations component gate에서 `PromotionEvidence`를 계산하는 규칙은 completion ledger를 그대로 따른다. | drill | 필수 증적 | | --- | --- | | 별도 upload workflow를 설치한 경우 session abort/expiry/checksum | orphan cleanup, quarantine state, retry/idempotency | | large save abort/integrity | partial commit 0, memory high-water | | IndexedDB two-tab upgrade blocked | close/retry UX, connection leak 0 | | IndexedDB governance/lifecycle | opaque scope binding, logical budget/TTL, proof discard, receipt cap | | interrupted historical migration | old-writer drain proof, 500 rows/30s budget, checkpoint resume, atomicity, N-1 fallback | | quota/persistence denied/storage clear | reconstructable GC, user data 보존, degraded UI | | VD-15 coordinator를 설치한 경우 pressure/one-retry | hysteresis, bounded cursor/deadline, store priority와 retry 최대 1회 | | OPFS journal phase crash/scope mismatch/maintenance authority | bidirectional binding, actual opaque layout, invisible partial object, reconcile summary, integrity, provider/consumer one-time proof | | OPFS preflight/migration을 설치한 경우 | worker/lock/journal/small operation cleanup, interrupted copy-on-write와 N-1 fallback | | cache candidate poison/update rollback | expectedContentType binding, active release 불변, previous rollback, caller retain 부재, 2 MiB control cap, auth rejection | | bounded Cache/SW lifecycle을 설치한 경우 | cursor/count/deadline, waiting activation, controlled-client drain과 network-only rollback | | preview probe를 설치한 경우 | pixel/decoded-byte/static-only rejection, timeout와 decode resource cleanup | evidence에는 raw 사용자 data를 포함하지 않는다. 허용 metadata는 release/build, browser engine/version, fixture ID, fault phase, bounded counts/buckets와 PASS/FAIL 결과뿐이다. promotion 직전에는 다음 repository evidence도 함께 보존한다. - `test:browser-capabilities`가 만든 JUnit에서 Chromium, Firefox, WebKit이 동일 14개 testcase set(File 2, IndexedDB 4, OPFS/Cache/StorageManager 각 1, cross-context invalidation 2, presigned streaming download/multipart upload/Image CDN 각 1)을 실제 실행해 총 42개이며 failure/error/skipped가 모두 0이어야 한다. `verify:browser-capability-evidence`가 engine 집합과 testcase 동일성을 기계적으로 검증한다. 현재 artifact는 Chromium/Firefox 14개씩 총 28개가 통과했지만 WebKit 실행에 필요한 native libraries(예: `libbacktrace.so.0`, `libevent-2.1.so.7`, `libjxl.so.0.8`, `libavif.so.16`과 WPE 계열)가 이 host에 없으므로 아직 promotion 가능 상태가 아니다. - `artifacts/quality/vite-module-inventory.json`에서 optional runtime source root가 production chunk에 없음을 `check:optional-recipes`로 검증한다. - `check:browser-file-storage-boundaries`로 native API 경계 위반을 막고, `test:browser-file-storage-removal`로 runtime과 전용 gate를 제거한 격리 copy가 base typecheck/lint/architecture/test/build/catalog/CI contract를 통과함을 검증한다. - catalog의 `referenceRuntime.status`는 `AVAILABLE_NOT_COMPOSED`, `productionComposition`은 `false`로 유지한다. dataset owner가 모든 policy와 evidence를 승인하고 실제 composition을 추가하기 전에는 `INSTALLED`로 해석하지 않는다. ## 10. 관련 문서 - [Browser data capability completion ledger](../architecture/browser-data-capability-completion-ledger.md) - [Browser file and origin-storage platform](../architecture/browser-file-and-origin-storage.md) - [VD-11 Browser file and origin-storage 경계](../architecture/decisions/VD-11-browser-file-and-origin-storage.md) - [VD-15 Origin storage lifecycle and migration](../architecture/decisions/VD-15-origin-storage-lifecycle-and-migration.md) - [VD-14 Resumable download와 background download](../architecture/decisions/VD-14-resumable-download-and-background-transfer.md) - [Browser transfer recovery](./browser-transfer-recovery.md)