Files
tech-log-frontend/docs/reviews/adapters/RE-REVIEW-2026-08-14.md
T
DongHyeonkaandClaude Opus 5 4bff9ca151 chore: sync the frontend template from 4dc033c to 8157ad4
The product was materialized from the template at `4dc033c` and has stayed
on it through 43 template commits, so it was missing all three rounds of
adapter remediation — including files it never had, such as the shared
`abortable-operation` primitive and the `exact-snapshot` decoder that
later fixes are written against. Taking only the newest round was not
possible for that reason: the delta is coherent only as a whole.

The product had not touched `src/adapters` at all since materialization,
so the 140-file delta applied with a three-way merge and no conflicts.
`package.json` was the single overlap and merged cleanly: the product owns
`name`, the template contributed `check:adapter-inventory`,
`check:remediation-ledger` and the image-resolve-signal type fixture.
All 24 product-owned files — README, index.html, CI workflow, i18n
catalog, home page, generated schemas, evidence scripts, component and
visual snapshots — are byte-identical to `main`.

`template.lock.json` now pins the synced revision and tree.

Verified in this repository, not inherited from the template: six type
projects, lint, nine gates (adapter inventory, remediation ledger,
registries, diagnostics, realtime boundaries, architecture, browser
file/storage boundaries, optional recipes, documentation), the production
build, and 2,054 of 2,073 tests. The 19 failures are all in
`tests/unit/ci-artifact-contract.test.ts` and are the same pre-existing
sandbox RLIMIT, EMFILE, umask and `/tmp` permission behaviour the template
records; four suites that failed once under parallel load pass in
isolation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 12:04:58 +09:00

50 KiB

프런트엔드 Adapter 재검토 종합 보고서

  • 검토 기준일: 2026-08-14
  • 저장소: clean-architecture-frontend-template
  • 검토 HEAD: 3b481eb4cf07bf58dadc20fe826261bc299aa4fa
  • 비교 기준: 4dc033cf33a5b6173bbf960d5eb464a406dc4c92
  • 검토 성격: 이전 Adapter 리뷰 반영분에 대한 독립 재검증
  • 저장소 변경: 없음

1. 최종 판정

이전 리뷰의 수정 사항이 상당수 반영된 것은 맞지만, 전체 Adapter 개선이 완료됐다고 판정할 수는 없다.

확정된 잔여 항목은 총 38건이다.

심각도 수량
High 19
Medium 17
Low 2
합계 38

집계는 다음 방식으로 중복을 제거했다. 6개 상세 보고서의 원시 합계는 38건(High 19, Medium 18, Low 1)이다. Network의 공통 abort utility finding을 TR-RR-05에, registry 보고서의 Browser RPC Map finding을 RPC-RR-03에 각각 병합해 기술 finding은 36건(High 19, Medium 16, Low 1)이 됐다. 여기에 GOV-01 Low와 GOV-02 Medium을 추가한 최종 수치가 38건(High 19, Medium 17, Low 2)이다.

이 숫자는 모든 문제가 현재 애플리케이션에서 동시에 발생한다는 뜻이 아니다.

활성 범위 판정
현재 선택된 V3 HTTP/Auth/Telemetry 코드 경로 5개 finding이 production graph에 포함된다. 다만 LIVE-01은 기본 public/config.json의 AUTH_MODE=demo에서는 재현되지 않고 AUTH_MODE=external에서 credential owner가 UNAVAILABLE/throw/reject할 때 활성화된다. LIVE-04는 non-cooperative injected fetch/reader 또는 abort에 협조하지 않는 platform stream에서, LIVE-05의 telemetry 누락은 TELEMETRY_ENABLED=true인 deadline 종료에서 발현한다.
Legacy HTTP V2 현재 기본 경로는 아니지만 rollback 또는 재사용 시 High 2건이 활성화된다.
OPFS, Public Cache, Realtime, Browser RPC, Transfer, Service Worker, Web Push 현재 기본 조립에서는 대부분 비활성이다. 다만 제품 트래픽에 연결하기 전 각 promotion blocker를 닫아야 한다.
기존 Query/Web Storage/일반 Browser File primitive 이번 재검토에서 전체 사용 금지로 판정하지 않았다. 등록된 계약과 정책 범위 안에서 사용 가능하다.
문서와 검증 거버넌스 구현 상태를 실제보다 낙관적으로 표시하는 항목이 있어 수정 완료 판정에 사용할 수 없다.

따라서 권장 운영 판정은 다음과 같다.

  1. 현재 reference/demo 흐름은 계속 개발에 사용할 수 있다.
  2. 실제 인증 REST 도메인은 LIVE-01부터 LIVE-05까지 닫은 뒤 연결한다.
  3. Optional Adapter는 AVAILABLE_NOT_COMPOSED라는 이유만으로 바로 제품 트래픽에 연결하지 않는다.
  4. 아래 항목별 회귀 테스트와 promotion gate를 통과한 기능만 순서대로 활성화한다.

2. 이번 재검토에서 확인한 방법

단순 정적 리뷰에 그치지 않고 다음을 병행했다.

  • 현재 src/adapters 전체 119개 파일과 연결된 application port, contract, bootstrap, test, architecture 문서를 다시 대조했다.
  • 이전 리뷰의 각 finding을 현재 코드에서 FIXED, PARTIAL, NOT FIXED, DEFERRED로 재분류했다.
  • 정상 입력 테스트뿐 아니라 non-cooperative Promise, 늦은 완료, throwing getter, mutable Map, 중복 stream, scheduler 예외, headerless stream 등을 직접 재현했다.
  • 기본 조립 경로와 아직 조립되지 않은 optional 경로를 분리했다.
  • 기존 테스트가 통과하더라도 적대적 경계 사례를 검증하지 않는 경우 coverage gap으로 기록했다.

3. 현재 활성 경로의 잔여 문제

LIVE-01 — 인증 연동 장애가 사용자 인증 실패로 잘못 변환됨

  • 심각도: High
  • 활성 상태: V3에 존재하며 기본 demo에서는 비활성, AUTH_MODE=external에서 credential owner 장애 시 활성
  • 근거:
    • src/bootstrap/runtime-adapters.ts:384-413
    • src/bootstrap/runtime-adapters.ts:449-451
    • src/adapters/http/http-execution-v3.ts:570-612
    • src/adapters/http/http-execution-v3.ts:839-843

문제:

Credential collaborator가 UNAVAILABLE을 반환하거나 동기·비동기 예외를 던지면 일부 경로가 UNAUTHENTICATED 또는 일반 network failure로 변환된다. bootstrap은 UNAUTHENTICATED를 받으면 onUnauthenticated를 호출하므로, 인증 시스템 자체의 장애가 사용자 세션 만료로 오인되어 로그아웃 동작까지 유발할 수 있다.

구현 결정:

  1. 사용자 자격 증명이 실제로 없거나 거절된 경우만 UNAUTHENTICATED로 분류한다.
  2. credential provider의 UNAVAILABLE, throw, malformed response는 AUTH_INTEGRATION_FAILURE로 닫는다.
  3. 이 경우 fetch를 호출하지 않는다.
  4. 이 경우 onUnauthenticated를 호출하지 않는다.
  5. 동기 throw와 비동기 rejection을 동일한 분류 함수로 통합한다.

필수 회귀 테스트:

  • credential provider가 UNAVAILABLE을 반환하면 AUTH_INTEGRATION_FAILURE
  • provider가 동기 throw해도 동일 결과
  • provider가 Promise rejection해도 동일 결과
  • 세 경우 모두 fetch 0회, logout callback 0회
  • 실제 no-session만 UNAUTHENTICATED 및 기존 정책대로 callback 1회

LIVE-02 — REST 인증 프로필 registry가 런타임에 변경 가능함

  • 심각도: High
  • 활성 상태: 현재 기본 조립 경로
  • 근거:
    • src/contracts/rest-profiles.ts:93-96
    • src/contracts/rest-profiles.ts:155-174
    • src/bootstrap/runtime-adapters.ts:377-384
    • src/adapters/http/http-execution-v3.ts:376-382
    • src/adapters/http/http-execution-v3.ts:507-515

문제:

Object.freeze(new Map(...))은 Map의 set, delete, clear를 막지 않는다. 직접 재현에서 Object.isFrozen(map)은 true였지만 clear 후 크기가 1에서 0으로 바뀌었다. bootstrap과 executor가 동일 singleton을 공유하고 요청마다 get을 호출하므로, 사후 변경으로 전체 요청을 중단시키거나 검증을 통과한 인증 정책을 교체할 수 있다.

구현 결정:

  1. raw Map을 외부에 노출하지 않는다.
  2. 내부 private Map을 closure에 보관하고 get, has, entries 등 필요한 read API만 제공한다.
  3. rest-profiles.ts:109-164의 기존 profile row 및 header array 복제·freeze는 그대로 유지한다.
  4. facade를 registry type으로 cast하거나 Map.prototype의 set/delete/clear를 빌려 호출해도 backing store를 변경할 수 없어야 한다.

필수 회귀 테스트:

  • public value에 set, delete, clear가 존재하지 않음
  • cast를 통한 mutator 호출과 borrowed Map.prototype set/delete/clear가 모두 실패
  • mutation 시도 전후 executor가 관찰하는 size, profile identity, credential policy가 불변
  • 기존 row/header array snapshot 테스트는 계속 통과

LIVE-03 — 외부 HTTP/Event 계약 registry와 row가 변경 가능함

  • 심각도: High
  • 활성 상태: HTTP는 현재 활성, Event 쪽은 dormant
  • 근거:
    • src/contracts/external-contract-runtime.ts:500-508
    • src/contracts/external-contract-runtime.ts:562-588
    • src/bootstrap/runtime-adapters.ts:417-439

문제:

합성된 HTTP/Event registry가 ordinary Map으로 노출되고, 검증 후 원본 row 참조도 보존한다. exported composed HTTP singleton은 clear로 3개에서 0개가 되어 현재 경로를 즉시 바꿀 수 있었다. deadline을 10000에서 999999로 바꾼 재현은 mutable product contribution을 합성했을 때의 generic TOCTOU이며, 현재 reference contribution row 자체는 frozen이다.

구현 결정:

  1. compose 단계에서 row를 exact own-data snapshot으로 복제한다.
  2. 중첩 retry, credential, deadline 정책도 깊은 불변 snapshot으로 만든다.
  3. 반환값은 Map이 아니라 읽기 전용 lookup facade로 제한한다.
  4. duplicate operation ID는 기존처럼 fail-close를 유지한다.
  5. HTTP와 Event 모두 같은 composer invariant를 사용한다.

필수 회귀 테스트:

  • 원본 registry 및 row 사후 변경이 composed 결과에 영향 없음
  • clear, set, delete 접근 불가
  • symbol, accessor, prototype 상속 필드 거부
  • duplicate operation ID, event type, contribution ID 및 conflicting package identity 거부

LIVE-04 — 전체 deadline이 fetch와 response admission을 완전히 제한하지 못함

  • 심각도: Medium
  • 활성 상태: 현재 V3 executor에 존재하며 non-cooperative injected fetch/reader 또는 abort 비협조 platform stream에서 발현
  • 근거:
    • src/adapters/http/http-execution-v3.ts:738-744
    • src/adapters/http/http-execution-v3.ts:794-833
    • src/adapters/http/http-execution-v3.ts:973-989
    • src/adapters/http/http-execution-v3.ts:1105-1121

문제:

fetch 및 readBoundedResponseBytes를 직접 await하는 구간이 total deadline과 물리적으로 race하지 않는다. 재현 결과 5ms deadline에서 영원히 끝나지 않는 custom reader는 30ms 이후에도 pending이었고, deadline 뒤 정상 bytes를 반환한 reader는 cancellationOwner가 DEADLINE인데도 SUCCESS가 됐다.

구현 결정:

  1. credential, fetch, body admission, retry sleep을 하나의 terminal-owner primitive로 통합한다.
  2. 각 await를 deadline/caller/scope/shutdown signal과 race한다.
  3. await가 끝난 직후 terminal owner를 다시 확인하고 늦은 성공을 admit하지 않는다.
  4. reader에 AbortSignal을 전달하거나 reader lease에 cancel/waitClosed 계약을 추가한다.
  5. 원래 task rejection을 CLOSED 같은 임의 상태로 덮어쓰지 않는다.

필수 회귀 테스트:

  • non-cooperative fetch가 deadline 이후 port 결과를 붙잡지 않음
  • non-cooperative body reader도 동일
  • deadline 이후 late success는 TIMEOUT
  • caller abort, scope fence, deadline owner가 각각 정확히 보존됨
  • 늦은 native rejection은 unhandled rejection 없이 관찰됨

LIVE-05 — deadline TIMEOUT telemetry가 누락됨

  • 심각도: High
  • 활성 상태: V3 observation 경로에 존재하며 TELEMETRY_ENABLED=true인 deadline 종료에서 발현
  • 근거:
    • src/adapters/http/http-execution-v3.ts:461-464
    • src/adapters/http/http-execution-v3.ts:748-761
    • src/adapters/http/http-execution-v3.ts:494-496
    • src/bootstrap/runtime-adapters.ts:232-241

문제:

projector가 cancellationOwner가 하나라도 있으면 모든 terminal failure를 제외한다. 그 결과 caller/scope cancellation뿐 아니라 운영상 중요한 DEADLINE TIMEOUT도 api.request.failed에서 누락된다.

구현 결정:

  1. CALLER, ROUTE/SCOPE, SHUTDOWN 취소만 failure telemetry에서 제외한다.
  2. DEADLINE은 terminal timeout failure로 발행한다.
  3. diagnostics와 telemetry가 동일 request ID에 대해 정확히 한 번만 발행되게 한다.

필수 회귀 테스트:

  • deadline timeout 시 api.request.failed 정확히 1회
  • caller/scope/shutdown abort 시 0회
  • network/auth integration failure 시 1회
  • retry가 있어도 최종 terminal event만 1회

4. Legacy 및 선택형 Network/State

LEG-01 — V2 인증 복구가 종료된 요청 뒤 늦게 로그아웃을 호출할 수 있음

  • 심각도: High
  • 활성 상태: Legacy V2를 rollback 또는 재사용할 때
  • 근거:
    • src/application/ports/auth-session-port.ts:7-13
    • src/adapters/http/client.ts:331-359
    • src/adapters/http/client.ts:942-974

문제:

AuthSessionPort.recover에 deadline/AbortSignal context가 없다. 외부 race에서 요청은 이미 timeout/abort로 끝났더라도 recoverSession이 늦게 no-session을 반환하면 onUnauthenticated가 호출될 수 있다.

구현 결정:

  1. recover context에 signal, deadline, request/scope identity를 전달한다.
  2. raw recovery helper는 data-only 결과만 반환한다.
  3. race에서 결과가 최종 채택된 뒤에만 notification side effect를 수행한다.
  4. terminal 이후 late completion은 관찰하되 사용자 side effect는 금지한다.

필수 회귀 테스트:

  • deadline/abort 후 늦은 no-session에서 callback 0회
  • 채택된 no-session에서만 1회
  • non-cooperative recovery여도 port는 bounded

LEG-02 — V2 bearer 프로필의 필수 Authorization header가 강제되지 않음

  • 심각도: High
  • 활성 상태: Legacy V2 활성화 시
  • 근거:
    • src/adapters/http/client.ts:525-531
    • src/adapters/http/client.ts:643-675

문제:

REFERENCE_EXTERNAL_BEARER를 선택해도 allowed headers만 검사하고 requiredCredentialHeaders를 검사하지 않는다. 직접 재현에서 Authorization 없이 credentials: omit으로 fetch가 호출됐다.

구현 결정:

V3와 별도 규칙을 유지하지 말고, 하나의 shared credential admission validator가 profile의 허용/필수 header, credentials mode, owner 결과를 모두 검증하게 한다.

필수 회귀 테스트:

  • bearer profile + 빈 patch는 fetch 0회 및 AUTH_INTEGRATION_FAILURE
  • Authorization 형식과 중복 header 검증
  • non-bearer profile에는 불필요한 Authorization을 거부

OPT-NET-01 — Cursor loader의 일반 실패가 abort로 위조됨

  • 심각도: Medium
  • 활성 상태: Cursor pagination 활성화 시
  • 근거:
    • src/adapters/query-cache/cursor-pagination-runtime.ts:16-36
    • src/adapters/query-cache/cursor-pagination-runtime.ts:68-72

문제:

signal이 존재하지만 아직 abort되지 않은 상태에서 loadPage가 reject하면 PAGINATION_ABORTED로 바뀐다. 실제 데이터/네트워크 오류가 사용자 취소처럼 기록된다.

구현 결정:

signal.aborted가 실제 true인 경우만 abort로 분류한다. 현재 public contract를 늘리지 않는다면 signal이 없을 때와 동일하게 loader rejection을 보존해 rethrow한다. 새로운 closed failure를 도입하려면 별도 contract migration으로 처리한다.

필수 회귀 테스트:

  • live signal + loader rejection은 원래 rejection 보존
  • 실제 abort 중 rejection만 PAGINATION_ABORTED
  • late resolve after abort는 admit되지 않음

OPT-NET-02 — Idempotency key 검증 규칙이 두 군데로 갈라짐

  • 심각도: Low
  • 활성 상태: Mutation intent를 외부/동적 입력으로 구성할 때
  • 근거:
    • src/contracts/mutation-intent.ts:26-81
    • src/adapters/http/http-execution-v3.ts:351-373

문제:

defineMutationIntent는 bounded string만 검사해 제어 문자를 허용하고, V3는 자체 validator를 다시 가진다. 규칙 drift 가능성이 남는다.

구현 결정:

공용 isValidIdempotencyKey 하나를 contract layer에 두고 intent 생성과 executor admission에서 재사용한다.

필수 회귀 테스트:

  • 공백, 제어 문자, 최대 길이 경계, Unicode 정책에 대한 공통 parity table

5. Storage 및 Browser File

STO-RR-01 — OPFS 정상 PUT finalization이 동일 Web Lock 재획득으로 교착됨

  • 심각도: High
  • 활성 상태: OPFS를 조립할 때
  • 근거:
    • src/adapters/storage/opfs/opfs-worker-runtime.ts:782-819
    • src/adapters/storage/opfs/opfs-worker-runtime.ts:881-924
    • src/adapters/storage/opfs/opfs-worker-runtime.ts:1292-1331
    • src/adapters/storage/opfs/opfs-byte-store-adapter.ts:291-326

문제:

finalizePut이 origin Web Lock을 보유한 상태에서 cleanupTransaction을 호출하고, cleanupTransaction이 같은 non-reentrant exclusive lock을 다시 요청한다. 정상 PUT도 FINALIZE에서 결정적으로 교착된다. client timeout 뒤 adapter는 finalize 실패를 무시하고 success를 반환하며 COMMITTED journal이 남고, 이후 mutation/reconcile도 같은 경로에서 막힌다.

구현 결정:

  1. 이미 lease를 가진 finalize 경로에서는 cleanupTransactionLocked(..., false)를 호출한다.
  2. lock 획득 함수와 locked 내부 함수를 이름과 타입으로 분리한다.
  3. finalize 실패를 write success로 반환하지 않는다.
  4. journal 제거는 payload commit과 cleanup 결과의 상태 기계에 맞춰 수행한다.

필수 회귀 테스트:

  • 두 번째 동일 lock 획득을 영원히 대기시키는 strict non-reentrant fake
  • 정상 FINALIZE의 Web Lock acquire/release가 각각 정확히 1회
  • 성공 PUT 뒤 journal complete, staging 제거, 이어지는 두 번째 mutation 성공
  • finalize timeout은 success가 아님
  • finalize 실패 시 plain success를 절대 반환하지 않고 journal과 staging이 복구 가능한 상태로 남음
  • 실제 Chromium OPFS capability test

STO-RR-02 — Worker failure response kind가 항상 CAPABILITIES로 전송됨

  • 심각도: Medium
  • 활성 상태: OPFS 활성화 시
  • 근거:
    • src/adapters/storage/opfs/opfs-worker-runtime.ts:291-293
    • src/adapters/storage/opfs/opfs-worker-runtime.ts:1791-1802
    • src/adapters/storage/opfs/opfs-worker-client.ts:92-108

문제:

유효한 요청 처리 중 발생한 quota, integrity, abort 등의 예외도 failure helper 기본값 때문에 CAPABILITIES kind로 응답된다. client의 expected-kind 검증이 이를 protocol mismatch로 판단해 실제 오류를 UNSUPPORTED로 왜곡한다.

구현 결정:

요청 envelope 검증이 끝난 뒤에는 catch가 반드시 validated request.kind를 failure response에 포함하게 한다. envelope 자체를 읽지 못한 경우만 protocol-level failure를 별도로 사용한다.

필수 회귀 테스트:

  • runtime→message host→gateway 실제 연결에서 PUT/GET/DELETE/RECONCILE 각각의 실패가 request kind와 정확히 일치
  • LIMIT/QUOTA/ABORT/INTEGRITY code와 requestId, protocol version, retryable이 끝까지 보존

STO-RR-03 — OPFS client가 임의 failure code를 허용함

  • 심각도: Medium
  • 활성 상태: OPFS 활성화 시
  • 근거:
    • src/adapters/storage/opfs/opfs-worker-client.ts:193-205
    • src/adapters/storage/opfs/opfs-worker-client.ts:580-603
    • src/adapters/storage/opfs/opfs-worker-client.ts:699-729
    • src/application/ports/browser-file-storage/shared.ts:3-21

문제:

strict decoder라는 설명과 달리 failure code와 kind가 문자열인지까지만 확인한다. EVIL 같은 임의 code가 BrowserDataFailure의 closed taxonomy 밖으로 유출된다.

구현 결정:

known request-kind set과 BrowserDataFailureCode set을 각각 runtime membership guard로 정의한다. exact descriptor decoder 안에서 unknown kind/code, version, extra/inherited/symbol/accessor/proxy trap, non-boolean retryable을 모두 거부하고, closed incompatibility code인 UNSUPPORTED로 닫는다. BrowserDataFailureCode에 존재하지 않는 새 임의 code를 만들지 않는다.

필수 회귀 테스트:

  • unknown code/kind, version, inherited/extra/symbol field, getter, proxy trap, non-boolean retryable 거부
  • 모든 malformed worker response에서 public Promise가 reject하지 않고 UNSUPPORTED 결과로 닫힘

STO-RR-04 — Public Cache marker 일시 오류가 활성 cache 삭제로 이어짐

  • 심각도: Medium
  • 활성 상태: Public Cache release 활성화 시
  • 근거:
    • src/adapters/cache-storage/public-response-cache-adapter.ts:421-460
    • src/adapters/cache-storage/public-response-cache-adapter.ts:1311-1347

문제:

동일 candidate의 marker cache.match가 일시적으로 UNAVAILABLE이면 손상 확정 없이 candidate 전체를 삭제한다. 그 candidate가 active이면 pointer가 삭제된 cache를 가리킬 수 있다.

구현 결정:

marker 조회 실패는 UNKNOWN으로 보존하고 삭제하지 않는다. marker가 명백히 없음 또는 corrupt임이 확인된 경우만 repair 경로로 들어간다.

필수 회귀 테스트:

  • active candidate + marker read rejection에서 delete 0회
  • missing/corrupt marker에서만 repair
  • transient failure 뒤 기존 active read 유지

STO-RR-05 — 동일 digest repair가 성공 전에 활성 cache 전체를 삭제함

  • 심각도: Medium
  • 활성 상태: Public Cache release 활성화 시
  • 근거:
    • src/adapters/cache-storage/public-response-cache-adapter.ts:261-372
    • src/adapters/cache-storage/public-response-cache-adapter.ts:438-517

문제:

부분 손상된 동일 digest를 repair할 때 네트워크 fetch 성공 전에 cache 전체를 삭제한다. repair fetch가 실패하면 정상 asset까지 함께 사라진다.

구현 결정:

  1. 손상된 exact entry만 교체하거나 shadow cache에 완성본을 만든다.
  2. 모든 fetch와 검증이 성공한 후 pointer를 원자 전환한다.
  3. 실패 시 기존 active cache를 그대로 유지한다.

필수 회귀 테스트:

  • 한 asset repair 실패 시 다른 active asset 유지
  • pointer는 완성된 candidate만 가리킴
  • 중간 abort/quit에서도 기존 release가 사용 가능

6. Browser RPC 및 Realtime

RPC-RR-01 — Browser RPC stream timeout 뒤 물리 작업이 남고 새 stream이 허용됨

  • 심각도: High
  • 활성 상태: Browser RPC 조립 시
  • 근거:
    • src/adapters/browser-rpc/transport.ts:53-61
    • src/adapters/browser-rpc/browser-rpc-runtime.ts:568-573
    • src/adapters/browser-rpc/browser-rpc-runtime.ts:691-705

문제:

계약이 bare AsyncIterable만 제공한다. runtime은 iterator.return 대기만 제한하고 실제 transport stream을 cancel하거나 waitClosed할 수 없다. non-cooperative next/return timeout 뒤 첫 물리 stream이 살아 있는데 두 번째 stream이 admit되는 것을 재현했다.

구현 결정:

transport.openStream은 next iterator와 별도로 cancel(reason), waitClosed(), physical identity를 가진 lease를 반환해야 한다. runtime은 active lease registry와 DRAINING admission fence를 가진다.

필수 회귀 테스트:

  • timeout 뒤 cancel 정확히 1회
  • waitClosed 전 동일 operation의 새 stream 거부
  • late frame/result 미전달
  • close가 bounded하되 raw task를 retained registry에 남김

RPC-RR-02 — 동기 fence/clock 예외가 Result 경계를 탈출함

  • 심각도: Medium
  • 활성 상태: Browser RPC 조립 시
  • 근거:
    • src/adapters/browser-rpc/browser-rpc-runtime.ts:449-473
    • src/adapters/browser-rpc/browser-rpc-runtime.ts:1267-1293

문제:

stream generationFence.capture가 보호 try 밖에 있고, raceWithin은 clock.sleep(...).then을 직접 호출한다. 동기 throw가 Promise rejection으로 정규화되지 않아 unary/stream Result 계약을 깨고 listener/timer 정리를 건너뛸 수 있다.

구현 결정:

모든 외부 collaborator 호출을 Promise.resolve().then 형태의 안전 경계 안에서 실행하고 finally에서 listener와 timer를 해제한다.

필수 회귀 테스트:

  • throwing capture/sleep/transport가 native rejection이 아닌 closed failure
  • listener와 timer가 정확히 해제

RPC-RR-03 — Browser RPC registry 검증 전 getter가 실행되고 Map도 변경 가능함

  • 심각도: High
  • 활성 상태: raw getter 실행은 Browser RPC runtime 조립 시 활성; Map 변경 가능성은 exported installer/API의 promotion defect
  • 근거:
    • src/adapters/browser-rpc/browser-rpc-runtime.ts:104-122
    • src/adapters/browser-rpc/browser-rpc-runtime.ts:714-790
    • src/contracts/browser-rpc.ts:330-420
    • src/contracts/browser-rpc.ts:457-509

문제:

factory가 installer보다 먼저 raw dependency를 Object.entries/Object.values와 property read로 검사해 getter를 실행한다. transport snapshot도 accessor를 읽는다. row snapshot 일부는 frozen이지만 여섯 installed Map은 Object.freeze(Map)이라 clear/set/delete가 가능하다. 다만 createBrowserRpcRuntime 내부 installed Map은 private이므로 현재 runtime instance를 외부에서 직접 mutate하는 live incident라는 뜻은 아니다.

구현 결정:

  1. raw object를 읽기 전에 exact own-data descriptor decoder를 통과시킨다.
  2. 검증된 snapshot만 dependency validator와 runtime에 전달한다.
  3. installed registry는 private Map + read facade로 교체한다.

필수 회귀 테스트:

  • getter 호출 0회
  • inherited/extra/symbol field 거부
  • mutation API 미노출
  • 원본 입력 사후 변경 무효

RPC-RR-04 — Transport result/frame decoder가 exact하지 않음

  • 심각도: Medium
  • 활성 상태: Browser RPC 조립 시
  • 근거:
    • src/adapters/browser-rpc/browser-rpc-runtime.ts:1075-1114

문제:

in 연산자와 직접 property read를 사용해 inherited/extra field를 허용하고 throwing getter가 Result 밖으로 escape한다. 원본 객체를 그대로 반환해 검증 후 변경도 가능하다.

구현 결정:

각 union variant를 own property descriptor 기반으로 exact decode하고 새 frozen value를 반환한다.

필수 회귀 테스트:

  • extra, inherited, symbol, getter/proxy trap 모두 closed protocol failure
  • 입력 객체 변경 후 decode 결과 불변

RT-RR-01 — Realtime close가 pending raw effect를 놓침

  • 심각도: High
  • 활성 상태: Realtime coordinator 조립 시
  • 근거:
    • src/adapters/realtime/stream-coordinator.ts:182-210
    • src/adapters/realtime/stream-coordinator.ts:467-520
    • src/adapters/realtime/stream-coordinator.ts:631-700
    • src/adapters/realtime/stream-coordinator.ts:970-1020

문제:

effect/recovery task를 timeout이 발생한 뒤에만 retained registry에 등록한다. timeout 전에 close가 호출되면 registry가 비어 있어 성공을 반환하지만 raw authority task는 여전히 실행 중이다.

구현 결정:

외부 effect를 호출하는 순간부터 physical task를 등록하고 settlement까지 유지한다. close는 admission을 먼저 닫고 등록된 모든 task를 bounded drain한다.

필수 회귀 테스트:

  • apply/recovery 시작 직후 close가 raw task를 관찰
  • timeout 전후 어느 시점에도 close 성공과 pending authority가 공존하지 않음

RT-RR-02 — DRAINING 중 queued event가 실행되고 recovery 없이 재개됨

  • 심각도: High
  • 성 상태: Realtime coordinator 조립 시
  • 근거:
    • src/adapters/realtime/stream-coordinator.ts:201-208
    • src/adapters/realtime/stream-coordinator.ts:238-254
    • src/adapters/realtime/stream-coordinator.ts:314-371

문제:

이미 queue에 들어간 event는 실행 시 lifecycle을 다시 확인하지 않아 DRAINING 중 시작된다. raw task가 뒤늦게 끝나면 lifecycle이 STALE로 열리지만 resume state가 남아 다음 ordinary event가 authoritative recovery를 우회한다.

구현 결정:

queue 실행 직전에 lifecycle/generation을 재검증하고, non-cooperative timeout 발생 시 resume token을 폐기하며 recoveryRequired를 명시적으로 기록한다.

필수 회귀 테스트:

  • DRAINING 전 queued event도 실행되지 않음
  • settlement 이후 첫 admitted event는 recovery 완료 전 apply되지 않음

RT-RR-03 — Handoff close timeout이 영구 캐시됨

  • 심각도: Medium
  • 활성 상태: Live/Poll handoff 활성화 시
  • 근거:
    • src/adapters/realtime/live-poll-handoff-coordinator.ts:170
    • src/adapters/realtime/live-poll-handoff-coordinator.ts:672-714

문제:

closePromise가 첫 timeout 결과를 영구 저장한다. retired writer가 이후 실제로 끝나도 close를 다시 호출해 성공으로 수렴하거나 registry를 prune할 수 없다.

구현 결정:

동시에 진행 중인 close promise만 공유하고 완료 뒤 캐시를 비운다. writer tail은 finally에서 prune한다.

필수 회귀 테스트:

  • 첫 close timeout
  • writer settlement
  • 두 번째 close success 및 retained count 0

RT-RR-04 — Handoff checkpoint 작업이 retained registry에 없음

  • 심각도: High
  • 활성 상태: Live/Poll handoff 활성화 시
  • 근거:
    • src/adapters/realtime/live-poll-handoff-coordinator.ts:537-643
    • src/adapters/realtime/live-poll-handoff-coordinator.ts:684-706
    • src/adapters/realtime/live-poll-handoff-coordinator.ts:788-815

문제:

checkpoint Promise는 timeout과 race하지만 retained task로 보관되지 않는다. non-cooperative checkpoint가 실행 중인데 close가 성공할 수 있다.

구현 결정:

writer, checkpoint, recovery를 동일 physical-task registry에서 호출 순간부터 settlement까지 추적한다.

필수 회귀 테스트:

  • pending checkpoint 중 close가 성공하지 않음
  • late checkpoint 결과가 새 generation에 반영되지 않음

7. Browser Transfer

TR-RR-01 — Presigned download close와 별도 stream signal이 물리 I/O를 막지 못함

  • 심각도: High
  • 활성 상태: Presigned transfer 활성화 시
  • 근거:
    • src/adapters/browser-transfer/presigned/presigned-transfer-executor.ts:423-447
    • src/adapters/browser-transfer/presigned/presigned-transfer-executor.ts:489-518
    • src/adapters/browser-transfer/presigned/presigned-transfer-executor.ts:547-575

문제:

active download close는 scope만 release하고 fetch/reader를 abort하지 않는다. open 시 받은 signal과 다른 이미-aborted consumer signal도 fetch 시작 뒤에야 확인된다.

구현 결정:

close, outer signal, consumer stream signal, deadline을 fetch 전에 하나의 ownership signal로 합성하고 reader lease를 명시적으로 cancel/release한다.

필수 회귀 테스트:

  • pending fetch/read 중 close가 실제 abort
  • 이미 aborted consumer signal이면 fetch 0회
  • late chunk가 consumer에 전달되지 않음

TR-RR-02 — Upload digest 계산이 abort/deadline 소유권 밖에 있음

  • 심각도: High
  • 활성 상태: Presigned upload 활성화 시
  • 근거:
    • src/adapters/browser-transfer/presigned/presigned-transfer-executor.ts:269-298

문제:

digest 계산이 operation abort scope를 만들기 전에 수행된다. non-settling 또는 큰 hash 작업은 caller abort와 deadline을 무시한다.

구현 결정:

scope 생성과 terminal owner 등록을 가장 먼저 한다. digest collaborator에 signal/deadline을 전달하는 데 그치지 않고 digest Promise 자체를 caller/deadline terminal과 race한다. terminal이 먼저 소유권을 얻으면 put을 즉시 종료하고, 늦은 fulfill/reject는 관찰한 뒤 폐기한다. digest 완료 뒤 owner를 재검증한 경우에만 vault claim과 network를 수행한다.

필수 회귀 테스트:

  • pending hash가 caller abort/deadline으로 bounded
  • hash timeout 뒤 claim/fetch 0회
  • late hash rejection 관찰

TR-RR-03 — Vault가 안전하지 않은 alternate issuer 등록을 허용함

  • 심각도: High
  • 활성 상태: Presigned capability 사용 시
  • 근거:
    • src/adapters/browser-transfer/presigned/presigned-capability-vault.ts:19-39
    • src/adapters/browser-transfer/presigned/presigned-capability-vault.ts:116-162
    • src/adapters/browser-transfer/presigned/presigned-capability-vault.ts:194-230
    • src/adapters/browser-transfer/presigned/presigned-transfer-executor.ts:899-904

문제:

외부 issuer가 HTTP target과 Authorization header를 포함한 capability를 등록할 수 있고 protocol version도 없다. validator가 executor가 실제 사용하는 invariant 전체를 닫지 않는다.

구현 결정:

등록 계약을 versioned exact union으로 만들고 HTTPS, origin/path, method, allowed/forbidden headers, query, status, content length, expiry를 등록 시점에 검증한다. Authorization 등 ambient credential header는 presigned capability에서 금지한다.

필수 회귀 테스트:

  • HTTP, Authorization, extra/inherited/getter field 거부
  • unknown protocol version 거부
  • expiry/length/status invariant가 vault→executor까지 보존

TR-RR-04 — Download delivery가 source lease를 닫지 않음

  • 심각도: Medium
  • 활성 상태: Download delivery와 presigned source 결합 시
  • 근거:
    • src/application/ports/browser-transfer/presigned-transfer.ts:123-134
    • src/adapters/browser-files/download-delivery-adapter.ts:572-597
    • src/adapters/browser-files/download-delivery-adapter.ts:727-751
    • src/adapters/browser-files/download-delivery-adapter.ts:1017-1042

문제:

port는 close를 요구하지만 delivery consumer가 성공/실패/abort에서 호출하지 않는다. fetch reader와 capability lease가 누수될 수 있다.

구현 결정:

closeable subtype을 projection 과정에서 보존하고 가장 바깥 finally에서 close를 정확히 한 번 호출한다. delivery 결과와 close failure의 우선순위를 계약으로 고정한다.

필수 회귀 테스트:

  • 성공, validation failure, writer failure, abort 모두 close 1회

TR-RR-05 — Scheduler 예외가 terminal ownership을 잃게 하는 공통 결함

  • 심각도: High
  • 활성 상태: Presigned, Resumable, Image 경로에 공통
  • 근거:
    • src/adapters/browser-transfer/presigned/presigned-capability-http-provider.ts:889-936
    • src/adapters/browser-transfer/presigned/presigned-transfer-executor.ts:1019-1065
    • src/adapters/browser-transfer/resumable-upload/fetch-json-transport.ts:65-102
    • src/adapters/browser-transfer/resumable-upload/fetch-json-transport.ts:174-200
    • src/adapters/browser-transfer/resumable-upload/fetch-json-transport.ts:583-630
    • src/adapters/browser-transfer/image-cdn/browser-image-probe.ts:98-104
    • src/adapters/browser-transfer/image-cdn/browser-image-probe.ts:507-540
    • src/adapters/platform/abortable-operation.ts:91-137

문제:

clock/scheduler가 동기 throw하면 caller listener를 제거한 뒤 terminal이 null로 남아 이후 abort가 보이지 않는다. resumable 일부는 원본 scheduler object를 보관해 생성 뒤 method mutation도 관찰한다. 새 공통 helper는 production consumer가 0개이며 ordinary task rejection을 CLOSED로 위조하는 별도 문제도 있다.

구현 결정:

  1. 공통 helper에 VALUE, REJECTED, TERMINAL(owner)를 구분하는 결과를 추가하고 ordinary rejection을 TERMINAL/CLOSED로 위조하지 않는다.
  2. race result와 terminal() 조회 결과가 항상 같은 최초 소유자를 가리키게 한다.
  3. 생성 시 scheduler method를 bind한 immutable snapshot으로 고정한다.
  4. scheduler 설치 실패 시 caller listener와 이미 만든 resource를 원자적으로 정리한다.
  5. 늦은 value/rejection과 필요 시 compensator를 정확히 한 번 관찰한다.
  6. provider, executor, resumable, image를 모두 이 검증된 primitive로 이관한다.

필수 회귀 테스트:

  • throwing scheduler 뒤 caller abort가 유실되지 않음
  • task rejection이 REJECTED이고 CLOSED/TERMINAL로 위조되지 않음
  • race result와 terminal() 최초 owner 일치
  • 생성 뒤 scheduler method 교체가 진행 중 operation에 영향 없음
  • late value/rejection/compensator 각각 정확히 한 번
  • 모든 consumer의 timeout/caller/close/rejection parity
  • helper에 실제 production import가 존재하는 구조 검사

TR-RR-06 — Resumable dispose가 bounded하거나 실제 settlement를 보장하지 않음

  • 심각도: High
  • 활성 상태: Resumable upload 활성화 시
  • 근거:
    • src/adapters/browser-transfer/resumable-upload/resumable-upload-runtime.ts:174-181
    • src/adapters/browser-transfer/resumable-upload/resumable-upload-runtime.ts:230-331
    • src/adapters/browser-transfer/resumable-upload/resumable-upload-runtime.ts:1254-1291
    • src/adapters/browser-transfer/resumable-upload/runtime-policy.ts:1-16

문제:

non-cooperative lock을 기다리는 dispose가 무기한이고, abort operation은 registry에 포함되지 않으며, wrapper가 종료된 뒤 raw provider가 계속 실행될 수 있다.

구현 결정:

cleanup deadline을 정책에 추가하고 모든 admitted physical task를 시작 시점부터 추적한다. dispose는 single-flight로 admission을 닫고 bounded drain 결과를 반환한다.

필수 회귀 테스트:

  • pending lock/provider/abort 각각에서 dispose bounded
  • dispose success 시 재진입 가능한 raw task 0개

TR-RR-07 — Image verification 동시성 cap이 물리 작업 수를 제한하지 못함

  • 심각도: High
  • 활성 상태: Image CDN verification 활성화 시
  • 근거:
    • src/adapters/browser-transfer/image-cdn/image-cdn-runtime.ts:317-325
    • src/adapters/browser-transfer/image-cdn/image-cdn-runtime.ts:348-380
    • src/adapters/browser-transfer/image-cdn/image-cdn-runtime.ts:429-430
    • src/adapters/browser-transfer/image-cdn/image-cdn-runtime.ts:1098-1126
    • src/adapters/browser-transfer/image-cdn/image-cdn-policy.ts:37-54

문제:

caller deadline이 먼저 끝나면 semaphore slot을 release하지만 raw verifier가 계속 실행된다. 반복 호출로 설정 cap보다 많은 물리 verification을 만들 수 있다.

구현 결정:

slot lease는 wrapper 결과가 아니라 raw verifier settlement 또는 확정 cancel/waitClosed까지 유지한다.

필수 회귀 테스트:

  • non-cooperative verifier를 반복 timeout해도 raw concurrency가 cap 이하
  • settlement 후에만 다음 작업 admit

TR-RR-08 — Resumable control-plane decoder가 hostile object에서 예외를 유출함

  • 심각도: Medium
  • 활성 상태: Resumable control plane 활성화 시
  • 근거:
    • src/adapters/browser-transfer/resumable-upload/http-control-plane-adapter.ts:88-150
    • src/adapters/browser-transfer/resumable-upload/http-control-plane-adapter.ts:369-475
    • src/adapters/browser-transfer/resumable-upload/http-control-plane-adapter.ts:582-595

문제:

getter/proxy throw가 public method rejection으로 탈출하고 symbol/non-enumerable extra field를 놓친다.

구현 결정:

property descriptor 기반 exact decoder를 catch boundary 안에서 실행하고 모든 malformed object를 typed CORRUPT_DATA로 반환한다.

필수 회귀 테스트:

  • getter, proxy ownKeys/getOwnPropertyDescriptor trap, symbol, non-enumerable extra

TR-RR-09 — Private Cache-Control 정책이 문서와 구현에서 다름

  • 심각도: Medium
  • 활성 상태: Image CDN activation gate
  • 근거:
    • src/adapters/browser-transfer/image-cdn/browser-image-probe.ts:308-336
    • docs/reviews/adapters/04-browser-transfer.md:286-292
    • tests/unit/image-cdn-runtime.test.ts:1655-1681

문제:

기록된 BT-IMG-02 정책은 private response의 fail-closed matrix를 요구하지만 구현은 no-store 포함 및 public 부재만 확인하고 테스트도 private,no-store를 성공으로 고정한다.

구현 결정:

현재 승인된 리뷰 계약을 기준으로 정확한 private Cache-Control matrix를 구현한다. 정책을 완화하려면 코드만 바꾸지 말고 architecture decision과 contract version을 별도 변경해야 한다.

필수 회귀 테스트:

  • private와 no-store 필수 조합을 중심으로 public, private, immutable, max-age, s-maxage, no-cache, must-revalidate, proxy-revalidate companion 전체 table test

8. Service Worker 및 Web Push

SW-RR-01 — Header 없는 activation marker body를 무제한 읽음

  • 심각도: High
  • 활성 상태: Service Worker 활성화 시
  • 근거:
    • src/adapters/service-worker/service-worker-lifecycle.ts:477-495

문제:

Content-Length가 있을 때만 사전 제한하고, 없으면 response.text()로 전체 body를 읽는다. 512KiB headerless body가 513 pull/524288 bytes 전부 소비되는 것을 재현했으며 무한 stream이면 activate가 영구 대기할 수 있다.

구현 결정:

bounded reader로 최대 marker size + 1 byte까지만 읽고 deadline, cancel, fatal UTF-8 decode를 적용한다.

필수 회귀 테스트:

  • headerless oversized body 조기 cancel
  • non-terminating stream deadline
  • invalid UTF-8 및 late reader failure

SW-RR-02 — null source activation/reset message를 신뢰함

  • 심각도: Medium
  • 활성 상태: Service Worker page controller 활성화 시
  • 근거:
    • src/adapters/service-worker/service-worker-page-controller.ts:329-332
    • src/adapters/service-worker/service-worker-page-controller.ts:418-420

문제:

nonce가 맞으면 event.source가 null이어도 activation/reset 완료로 인정한다.

구현 결정:

기대 controller object와 event.source의 strict identity가 일치할 때만 메시지를 admit한다. null과 교체된 controller는 거부한다.

필수 회귀 테스트:

  • exact nonce + null source 거부
  • 다른 worker source 거부
  • 기대 controller만 성공

SW-RR-03 — Asset generator와 shared manifest decoder의 확장자 규칙이 다름

  • 심각도: Medium
  • 활성 상태: Service Worker build 활성화 시 build blocker
  • 근거:
    • scripts/generate-service-worker-assets.ts:23-31
    • scripts/generate-service-worker-assets.ts:52-68
    • src/contracts/service-worker-static-manifest.ts:34-44
    • src/contracts/service-worker-static-manifest.ts:153-156
    • scripts/lib/service-worker-build-input.ts:95-110

문제:

generator는 .mjs와 .png를 asset으로 허용하지만 shared decoder는 둘 다 거부한다. generator가 정상 생성한 manifest가 runtime contract에서 실패할 수 있다.

구현 결정:

지원 확장자와 asset kind를 하나의 exported contract set으로 만들고 generator와 decoder가 공유한다.

필수 회귀 테스트:

  • 실제 collectStaticAssets output을 resolveServiceWorkerBuildInput과 decodeStaticAssetManifest에 그대로 전달
  • generator 전체 .js/.mjs/.css/.woff2/.svg/.png/.webp fixture 검증
  • decoder-only .json의 포함/제외 정책도 하나의 authoritative exported table에서 결정하고 양방향 검증

WP-RR-01 — Notification click의 늦은 focus/openWindow effect가 관찰되지 않음

  • 심각도: Medium
  • 활성 상태: Web Push 선택 시
  • 근거:
    • src/adapters/web-push/inbound/notification-click-adapter.ts:91-99
    • src/adapters/web-push/inbound/notification-click-adapter.ts:159-175
    • src/adapters/web-push/inbound/push-event-adapter.ts:152-192

문제:

showNotification에는 effect phase와 late observation이 있으나 focus/openWindow에는 없다. deadline 결과 뒤 native focus/openWindow가 성공해도 외부 효과 발생 여부가 기록되지 않는다.

구현 결정:

focus/openWindow도 native 호출 전 NOT_APPLIED, pending 중 MAYBE_APPLIED, fulfillment 시 CONFIRMED로 고정한다. outer terminal 이후 effect별 late value/error를 정확히 한 번 관찰하되, 이 evidence를 retry authorization에는 사용하지 않는다.

필수 회귀 테스트:

  • deadline 전/후 focus/openWindow 성공·실패 matrix
  • terminal 결과와 nativeEffect telemetry의 정확히 한 번 보장

SW-RR-04 — Cache match 실패가 network fallback을 막음

  • 심각도: Medium
  • 활성 상태: Service Worker fetch 활성화 시
  • 근거:
    • src/adapters/service-worker/service-worker-lifecycle.ts:227-233
    • src/adapters/service-worker/service-worker-entry.ts:92-102

문제:

caches.open만 catch하고 cache.match rejection은 전파한다. respondWith 자체가 reject되어 network fallback이 호출되지 않는다.

구현 결정:

cache open/match의 sync throw와 async rejection을 모두 null miss로 닫고 entry가 network fetch를 정확히 한 번 수행하게 한다.

필수 회귀 테스트:

  • open throw/reject, match throw/reject 각각 network 1회
  • network 실패는 원래 network rejection 보존

9. 문서 및 검증 거버넌스

GOV-01 — Adapter inventory가 실제 파일 수와 다름

  • 심각도: Low
  • 근거:
    • 현재 src/adapters 파일 수: 119
    • docs/reviews/adapters/INVENTORY.md 표기: 118/118
    • 누락: src/adapters/platform/abortable-operation.ts

구현 결정:

inventory를 수동 숫자로만 유지하지 말고 production path 목록에서 생성하거나 CI에서 실제 목록과 exact diff한다.

GOV-02 — Remediation ledger가 실제보다 많은 항목을 FIXED로 표시함

  • 심각도: Medium

문제:

다음 ledger 항목은 현재 코드에서 부분 수정 또는 미수정인데 FIXED_NOT_RELEASED로 과도하게 닫혀 있다.

  • Network/State: N-01, N-02, N-03, N-07, N-10
  • Storage/File: STO-07
  • Realtime/RPC: R-01, R-02, R-03, R-04, R-06
  • Browser Transfer: BT-PRE-01, BT-PRE-02, BT-PRE-03, BT-PRE-04, BT-UP-01, BT-UP-02, BT-UP-06, BT-IMG-02, BT-X-01
  • Service Worker/Web Push: SW-05, SW-06, WP-07

STO-01과 STO-04의 원 finding 수정 성과는 유지하되, 각각 새 STO-RR-01과 STO-RR-04/05 blocker를 별도로 연결해야 한다. verify:documentation은 통과하지만 inventory 수와 behavioral acceptance를 검증하지 않는다.

구현 결정:

  1. 코드를 먼저 수정하고 red→green 회귀 증거가 생긴 뒤 ledger를 갱신한다.
  2. PARTIAL, FIXED, DEFERRED, PROMOTION_BLOCKED를 분리한다.
  3. inventory equality, generator/decoder parity, shared helper production consumer 존재를 구조 gate에 추가한다.

점수에 포함하지 않은 문서/API 증거 drift

다음은 위 38건과 중복 집계하지 않는 문서 및 evidence 보정 사항이다.

  • Storage 기존 리뷰 line 7의 “current worktree” 표현은 현재 HEAD가 아니라 base 4dc033c 당시의 historical review임을 명시해야 한다.
  • Service Worker/Web Push 기존 inventory에는 현재 중심 계약인 service-worker-static-manifest와 capability/bootstrap/build 연결 파일이 빠져 있다. current-head addendum으로 실제 활성화 그래프를 보충해야 한다.
  • Realtime 문서가 약속한 lifecycle/limit export와 inspection lifecycle 일부가 public API에 없다. 별도 runtime 결함으로 추가 집계하지 말고 API/evidence mismatch로 표시한다.

10. 이전 리뷰 중 실제로 닫힌 항목

다음 항목은 현재 코드와 테스트에서 실질적으로 반영된 것으로 확인했다.

  • Network/State: N-04, N-08, N-09, N-11
  • N-05: collision fix 구현은 확인했지만 아직 HTTP sidecar에 조립되지 않음
  • N-06: 빈/generated key의 핵심 문제는 수정됐으나 OPT-NET-02의 validator drift는 남음
  • Storage/File: STO-02, STO-03, STO-05
  • STO-06: 문서화된 cooperative migration semantics 범위에서 수정 확인
  • STO-01: 원래의 stale-generation cleanup race는 수정됐으나 STO-RR-01의 새 finalization 교착이 존재
  • STO-04: marker verification의 핵심은 추가됐으나 STO-RR-04/05의 repair 원자성 문제 존재
  • STO-08: UNVERIFIED 표기는 현재 상태와 일치
  • Realtime/RPC: R-05는 수정, R-07은 promotion blocked 표기가 적절
  • Browser Transfer: BT-PRE-05, BT-UP-03, BT-UP-04
  • BT-IMG-01: required-signal TypeScript 계약과 negative fixture만 수정 확인. provider/runtime promotion이 완료됐다는 뜻은 아님
  • BT-UP-05/BT-IMG-03: NOT_PERFORMED 표기가 적절
  • BT-UP-07/BT-IMG-04: promotion blocked 표기가 적절
  • Service Worker/Web Push: SW-URL-01, SW-01, SW-02, SW-03, SW-04, SW-07, SW-08, SW-09, WP-01, WP-05, WP-06
  • SW-10 및 WP-02~04: deferred 표기가 적절

11. 검증 결과

통과

검증 결과
TypeScript type checks 6개 프로젝트 통과
lint 통과
diagnostics boundary 8 diagnostics / 5 telemetry producers 통과
registry checks 11개 통과
architecture boundary 288 modules, 865 dependencies, 12 fixtures 통과
browser file/storage boundary 34개 direct/misplaced case 통과
realtime boundary 통과
component tests 126개 통과
reference feature tests 26개 통과
recipe tests 17개 통과
integration tests 9 files / 52 tests 통과
Network/State focused 12 files / 115 tests 통과
Realtime/RPC focused 16 files / 192 tests 통과
Storage/File focused 11 files / 135 tests 통과
Browser Transfer focused 8 files / 128 tests 통과
Service Worker/Web Push focused 8 suites / 70 tests 통과
Registry immutability focused 3 suites / 20 tests 통과

기존 focused test가 모두 통과하면서도 위 문제가 재현됐다는 점이 중요하다. 정상·협조적 collaborator 중심 테스트가 non-cooperative, late completion, hostile object, 실제 Map mutation을 다루지 않았기 때문이다.

완전 통과로 주장할 수 없는 항목

저장된 완료 실행의 artifacts/tests/unit.xml 기준으로 1604개 중 1585개가 통과하고 19개가 실패했다. 이 완료 실행의 실패는 tests/unit/ci-artifact-contract.test.ts의 sandbox, provider, cgroup, 임시 경로 권한 관련 항목이었다. 해당 Adapter 변경 범위 밖의 파일이지만, baseline 완주 결과와 동일하다고 단정할 증거로 사용하지는 않는다.

별도의 더 최신 unit 재실행은 완주 전에 중단됐고, 중단 시점에 ci-workflow-generation 2건과 http-scenario-evidence 9건의 추가 실패도 관찰됐다. 미완주 결과이므로 최종 합계로 사용하지 않지만, 전체 unit green 또는 단일 원인 실패라고 기록해서는 안 된다.

실제 Browser/WebKit provider qualification, multi-release, staging gate는 이번 로컬 재검토에서 수행하지 않았으며 기존 ledger의 미검증 상태를 유지한다.

12. 구현 순서

질문 없이 구현할 수 있도록 순서를 고정한다.

1차 — 현재 활성 authority 경로

대상:

  • LIVE-01 인증 오류 taxonomy
  • LIVE-02/03 immutable registry
  • LIVE-04 deadline ownership
  • LIVE-05 timeout telemetry

완료 조건:

  • 신규 adversarial tests가 먼저 실패하고 수정 뒤 통과
  • V3 focused, diagnostics, type, integration 전체 통과
  • 실제 authenticated request에서 auth integration outage가 logout을 유발하지 않음

2차 — Legacy 및 Network 보조 기능

대상:

  • LEG-01/02
  • OPT-NET-01/02

완료 조건:

  • V2 rollback qualification과 late notification test 통과
  • cursor rejection/abort 구분
  • shared idempotency validator parity

3차 — Storage

대상:

  • STO-RR-01~05

완료 조건:

  • strict non-reentrant OPFS fake
  • Chromium OPFS test
  • failure-atomic active cache repair
  • worker protocol hostile response tests

4차 — Browser RPC 및 Realtime

대상:

  • RPC-RR-01~04
  • RT-RR-01~04

완료 조건:

  • 모든 raw physical task가 invocation부터 settlement까지 registry에 존재
  • DRAINING 중 신규/queued work 0개
  • close timeout 뒤 재호출로 수렴 가능
  • getter/mutation/extra field 입력이 public Result 밖으로 escape하지 않음

5차 — Browser Transfer

대상:

  • TR-RR-01~09

완료 조건:

  • corrected abortable-operation primitive를 실제 모든 consumer가 사용
  • close/dispose 성공과 pending raw authority가 공존하지 않음
  • vault protocol version 및 exact invariant 검증
  • physical concurrency cap 검증

6차 — Service Worker 및 Web Push

대상:

  • SW-RR-01~04
  • WP-RR-01

완료 조건:

  • bounded marker reader
  • exact worker source identity
  • generator output→decoder end-to-end parity
  • cache failure network fallback
  • late focus/openWindow effect telemetry

7차 — Ledger와 inventory

코드와 회귀 테스트가 모두 끝난 뒤에만 문서 상태를 갱신한다. 문서를 먼저 FIXED로 바꾸지 않는다.

13. 최종 사용 판단

  • 현재 템플릿 전체를 폐기하거나 모든 Adapter 사용을 중단할 필요는 없다.
  • 등록된 Query/Web Storage 및 reference 개발 흐름은 계속 사용할 수 있다.
  • 실제 인증 REST 도메인은 1차 수정이 끝나기 전 production-ready로 보지 않는다.
  • OPFS, Browser RPC, Realtime, Transfer, Service Worker, Web Push는 현재 미조립 상태를 유지한다.
  • 각 optional 기능은 해당 절의 완료 조건을 통과한 뒤 명시적으로 promotion한다.

이번 재검토의 핵심은 기능 수가 많아서 위험한 것이 아니라, timeout/abort 뒤에도 남는 물리 작업, 검증 후 바뀌는 registry, hostile boundary input처럼 기존 정상 테스트가 보지 못한 수명주기와 신뢰 경계 문제다.