diff --git a/docs/operations/adapter-remediation-ledger.md b/docs/operations/adapter-remediation-ledger.md index 1d51ee7..9fe8da0 100644 --- a/docs/operations/adapter-remediation-ledger.md +++ b/docs/operations/adapter-remediation-ledger.md @@ -223,6 +223,100 @@ No finding remains `NOT_STARTED`. | `SW-10`, OPFS physical/protocol V2 rollout, presigned and Web Push receipt V2 (`WP-02`, `WP-03`, `WP-04`) | **DEFERRED_TO_MIGRATION** | These are expand → dual-read/emit → old-writer drain → contract deployments requiring server request-shape negotiation and multi-release drain windows. The prerequisite in-repo pieces landed: the shared Service Worker manifest codec and canonical digest (`SW-05`), the OPFS worker protocol version and strict correlation (`STO-07`), and the OPFS physical generation token (`STO-01`). | | Promotion gaps `GAP-01`, `GAP-02`, `GAP-03`, `R-07`, `BT-UP-07`, `BT-IMG-04` | `PROMOTION_BLOCKED` | Unchanged by design. No availability state was raised and no optional capability was added to the default bootstrap. | +## Re-review remediation (2026-08-14) + +Source: [`docs/reviews/adapters/RE-REVIEW-2026-08-14.md`](../reviews/adapters/RE-REVIEW-2026-08-14.md), +38 findings (High 19 / Medium 17 / Low 2) raised against `3b481eb`. + +**GOV-02.** That re-review found the previous section of this ledger closed a +number of rows as `FIXED_NOT_RELEASED` that were in fact partial. The table +below is written the other way round: a row is `FIXED` only where a new +adversarial test failed first on the pre-fix source and passes on the landed +one, and everything else says plainly that it was not done. + +### Landed + +| ID | Severity | Disposition | Commit | Red-then-green evidence | +| --- | --- | --- | --- | --- | +| LIVE-01 | High | `FIXED` | `f4bfdf0` | `tests/integration/http-execution-v3-live-authority.test.ts` — UNAVAILABLE, sync throw, async rejection and a malformed outcome each closed as `UNAUTHENTICATED` before the fix; all four now close as `AUTH_INTEGRATION_FAILURE` with zero fetches. | +| LIVE-02 | High | `FIXED` | `f4bfdf0` | `tests/unit/contract-registry-immutability.test.ts` — a borrowed `Map.prototype.clear` emptied the installed profile registry before the fix. | +| LIVE-03 | High | `FIXED` | `f4bfdf0` | Same suite — the composed HTTP registry was clearable and a post-composition mutation of a source policy changed `totalDeadlineMs` from 10000 to 999999. | +| LIVE-04 | Medium | `FIXED` | `f4bfdf0` | Same integration suite — a non-cooperative fetch and reader held the port result open; a body that finished after the deadline was admitted as SUCCESS. | +| LIVE-05 | High | `FIXED` | `f4bfdf0` | Same suite — a DEADLINE timeout emitted no `api.request.failed`. | +| LEG-01 | High | `FIXED` | `ca210d3` | `tests/integration/legacy-http-credential-authority.test.ts` — a recovery that answered after the deadline called `onUnauthenticated` once; it now calls it zero times, and only an adopted no-session result notifies. | +| LEG-02 | High | `FIXED` | `ca210d3` | Same suite — a bearer profile dispatched with no `Authorization` at all. | +| OPT-NET-01 | Medium | `FIXED` | `ca210d3` | `tests/unit/legacy-and-optional-network-remediation.test.ts` — a loader rejection with a live signal became `PAGINATION_ABORTED`. | +| OPT-NET-02 | Low | `FIXED` | `ca210d3` | Same suite — `defineMutationIntent` accepted control characters the executor rejected. | +| STO-RR-01 | High | `FIXED` | `6a8281a` | `tests/unit/opfs-worker-runtime.test.ts` — a strict non-reentrant lease manager made `FINALIZE_PUT` hang forever; `tests/unit/opfs-byte-store.test.ts` pins that a failed finalization is no longer a plain success. | +| STO-RR-02 | Medium | `FIXED` | `6a8281a` | Same suite — every failure answered with kind `CAPABILITIES`. | +| STO-RR-03 | Medium | `FIXED` | `6a8281a` | Same suite — `{code:"EVIL"}` reached the caller; a non-boolean `retryable` and a throwing getter left the RPC to time out. | +| STO-RR-04 | Medium | `FIXED` | `6a8281a` | `tests/unit/public-response-cache.test.ts` — a transient marker read failure deleted the active candidate. | +| STO-RR-05 | Medium | `FIXED` | `6a8281a` | Same suite — one failed repair fetch destroyed every healthy asset in the release. | +| RPC-RR-02 | Medium | `FIXED` | `bd90e0c` | `tests/unit/browser-rpc/browser-rpc-remediation.test.ts` — a throwing fence and a throwing `clock.sleep` escaped the Result contract. | +| RPC-RR-03 | High | `FIXED` | `bd90e0c` | Same suite — a transport accessor ran during validation, and the installed binding registries exposed `set`/`delete`/`clear`. | +| RPC-RR-04 | Medium | `FIXED` | `bd90e0c` | Same suite — extra, inherited, symbol-keyed and throwing-getter transport values passed. | +| SW-RR-01 | High | `FIXED` | `efc577d` | Bounded marker reader with a read deadline, reader cancel and fatal UTF-8 decode replaces `response.text()`. | +| SW-RR-02 | Medium | `FIXED` | `efc577d` | A `null` `event.source` no longer satisfies activation or reset completion. | +| SW-RR-03 | Medium | `FIXED` | `efc577d` | `tests/unit/service-worker-web-push-remediation.test.ts` plus the `check:adapter-inventory` gate — generator and decoder now share one exported table. | +| SW-RR-04 | Medium | `FIXED` | `efc577d` | `cache.match` rejection is closed as a miss so `respondWith` reaches its network fallback. | +| WP-RR-01 | Medium | `FIXED` | `efc577d` | `focus`/`openWindow` carry NOT_APPLIED → MAYBE_APPLIED → CONFIRMED and a late effect is observed exactly once. | +| TR-RR-08 | Medium | `FIXED` | `69cb7e3` | `tests/unit/resumable-upload-http-control-plane.test.ts` — a throwing getter escaped as `TypeError` out of `createSession`; symbol and non-enumerable extras passed the key check. | +| TR-RR-09 | Medium | `FIXED` | `fb5b449` | `tests/unit/image-cdn-runtime.test.ts` — the suite pinned the contradictory `private, no-store` as success; the recorded fail-closed matrix now applies. | +| GOV-01 | Low | `FIXED` | this commit | `scripts/check-adapter-inventory.ts` diffs `docs/reviews/adapters/INVENTORY.md` against `git ls-files src/adapters`. The missing `src/adapters/platform/abortable-operation.ts` row is restored and the total is 119/119. | +| GOV-02 | Medium | `FIXED` | this commit | This section replaces the over-closed rows with evidence-linked dispositions and an explicit not-done list. | + +### Not done in this pass + +These are recorded as **NOT_STARTED**, not as partial credit. Each needs a +lifecycle change larger than a contained edit, and none of them is in the +default composition today. + +| ID | Severity | Why it is still open | +| --- | --- | --- | +| RPC-RR-01 | High | Requires changing `BrowserRpcTransport.openServerStream` from a bare `AsyncIterable` to a lease with `cancel(reason)`, `waitClosed()` and a physical identity, plus an active-lease registry and a `DRAINING` admission fence in the runtime. That is a transport contract migration, not an internal fix. | +| RT-RR-01 | High | `stream-coordinator` must register a physical task at the moment it calls an external effect, not only after a timeout, and `close` must drain every registered task. | +| RT-RR-02 | High | Queued events must re-check lifecycle and generation at execution time, and a non-cooperative timeout must discard the resume token and record `recoveryRequired`. | +| RT-RR-03 | Medium | `live-poll-handoff-coordinator` caches the first close timeout forever; only an in-flight close promise may be shared, and the writer tail must be pruned in `finally`. | +| RT-RR-04 | High | Handoff checkpoint work must live in the same physical-task registry as writer and recovery work. | +| TR-RR-01 | High | Presigned download `close` must compose the outer signal, the consumer stream signal and the deadline into one ownership signal before `fetch`, and cancel the reader lease explicitly. | +| TR-RR-02 | High | Upload digest computation must happen inside the operation abort scope and race the caller and deadline terminals. | +| TR-RR-03 | High | The capability vault registration contract must become a versioned exact union validated at registration time. | +| TR-RR-04 | Medium | Download delivery must preserve the closeable subtype through projection and call `close()` exactly once in the outermost `finally`. | +| TR-RR-05 | High | The shared `abortable-operation` primitive needs VALUE / REJECTED / TERMINAL results, a bound scheduler snapshot and atomic install-failure cleanup, and all four consumers must migrate onto it. It still has **zero production importers**, so the structural gate for that is deliberately not added yet — adding it now would fail CI for a defect that is documented rather than fixed. | +| TR-RR-06 | High | `dispose()` must bound its drain with a cleanup deadline from policy and track abort operations from the moment they are admitted. | +| TR-RR-07 | High | The image verification semaphore must hold its slot until the raw verifier settles or is confirmed cancelled, not until the wrapper returns. | + +None of RPC-RR-01, RT-RR-01..04 or TR-RR-01..07 may be treated as closed, and no +capability they cover may be promoted, until each has its own red-then-green +evidence in this table. + +### Gates after this pass + +Run on the landed tree. Only what actually passed is claimed as passing. + +| Command | Exit | Result | +| --- | ---: | --- | +| `corepack pnpm check:types` | 0 | all six projects | +| `corepack pnpm lint` | 0 | `--max-warnings=0` clean | +| `corepack pnpm check:architecture` | 0 | 289 modules, 868 dependencies; 12 fixtures PASS | +| `corepack pnpm check:adapter-inventory` | 0 | 119 files, 7 shared asset extensions | +| `corepack pnpm check:registries` | 0 | 11 registries PASS | +| `corepack pnpm check:diagnostics` | 0 | 8 diagnostics / 5 telemetry producers | +| `corepack pnpm check:browser-file-storage-boundaries` | 0 | PASS, 34 rejections | +| `corepack pnpm check:realtime-boundaries` | 0 | PASS | +| `corepack pnpm verify:documentation` | 0 | PASS_SCOPED | +| `git diff --check` | 0 | clean | +| `tests/unit` (excluding the four below) + `tests/integration` | 0 | see the exclusion table | + +### Environmental failures, not claimed as green + +| Gate | Status | Attribution | +| --- | --- | --- | +| `tests/unit/ci-workflow-generation.test.ts` | 82 failed / 325 passed | Identical on the pre-change baseline (`git stash` comparison). The subprocess gates it spawns cannot run in this sandbox. | +| `tests/unit/ci-artifact-contract.test.ts` | fails | Unchanged pre-existing sandbox, cgroup and `/tmp` permission behaviour. | +| `tests/unit/removal-fixture.test.ts`, `tests/unit/supply-chain.test.ts`, `tests/integration/security-followup-archive.test.ts` | destructive | `scripts/lib/removal-fixture.ts:45` and `scripts/check-supply-chain-provider-fixtures.ts:62` symlink the real `node_modules` into a temp fixture root and then run `corepack pnpm` there with `CI=true`. pnpm purges the modules directory it does not recognise, **through the symlink**, deleting the repository's own dependencies mid-run. This is a pre-existing repository hazard, outside the 38 findings, and it is why a full `test:unit` run reports cascading failures. | +| `tests/unit/security-followup.test.ts` | flaky under load | Passes in a fresh process; the process-group reaping assertion is timing sensitive. | + ## Rules for updating this ledger - A row moves out of `NOT_STARTED` only with a linked red test, its green run, and the commit id. diff --git a/docs/reviews/adapters/INVENTORY.md b/docs/reviews/adapters/INVENTORY.md index f0f54e1..eafd04b 100644 --- a/docs/reviews/adapters/INVENTORY.md +++ b/docs/reviews/adapters/INVENTORY.md @@ -1,8 +1,10 @@ # Adapter 파일 전수 inventory -> 검토 기준: `develop` / `4dc033cf33a5b6173bbf960d5eb464a406dc4c92` (2026-08-13) +> 검토 기준: `develop` (2026-08-14 재검토 반영) > -> `rg --files src/adapters | sort` 결과 118개를 하나씩 고정한 coverage ledger다. 책임·의존성·finding·유지/변경 판정은 연결된 상세 리뷰의 파일별 표를 따른다. +> GOV-01. 이 표는 손으로 센 숫자가 아니라 `corepack pnpm check:adapter-inventory`가 `git ls-files src/adapters`와 정확히 대조하는 목록이다. +> +> `rg --files src/adapters | sort` 결과 119개를 하나씩 고정한 coverage ledger다. 책임·의존성·finding·유지/변경 판정은 연결된 상세 리뷰의 파일별 표를 따른다. | # | full path | 상세 리뷰 | | ---: | --- | --- | @@ -64,66 +66,67 @@ | 56 | `src/adapters/http/resource-mapper.ts` | [Network/state](./01-network-and-state.md) | | 57 | `src/adapters/http/retry-policy.ts` | [Network/state](./01-network-and-state.md) | | 58 | `src/adapters/http/schema-registry.ts` | [Network/state](./01-network-and-state.md) | -| 59 | `src/adapters/platform/browser-lifecycle.ts` | [Network/state](./01-network-and-state.md) | -| 60 | `src/adapters/platform/browser-mutation-intent-factory.ts` | [Network/state](./01-network-and-state.md) | -| 61 | `src/adapters/platform/system-clock.ts` | [Network/state](./01-network-and-state.md) | -| 62 | `src/adapters/query-cache/conditional-validator-store.ts` | [Network/state](./01-network-and-state.md) | -| 63 | `src/adapters/query-cache/cursor-pagination-runtime.ts` | [Network/state](./01-network-and-state.md) | -| 64 | `src/adapters/query-cache/server-state-scope-runtime.ts` | [Network/state](./01-network-and-state.md) | -| 65 | `src/adapters/query-cache/tanstack-cache-coordinator.ts` | [Network/state](./01-network-and-state.md) | -| 66 | `src/adapters/query-cache/tanstack-query-cache.ts` | [Network/state](./01-network-and-state.md) | -| 67 | `src/adapters/realtime/event-codec.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | -| 68 | `src/adapters/realtime/event-consumer.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | -| 69 | `src/adapters/realtime/index.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | -| 70 | `src/adapters/realtime/json-member-scanner.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | -| 71 | `src/adapters/realtime/live-poll-handoff-coordinator.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | -| 72 | `src/adapters/realtime/polling/bounded-poll-coordinator.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | -| 73 | `src/adapters/realtime/polling/index.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | -| 74 | `src/adapters/realtime/reconnect-coordinator.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | -| 75 | `src/adapters/realtime/reconnect-policy.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | -| 76 | `src/adapters/realtime/result.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | -| 77 | `src/adapters/realtime/sse/fetch-sse-connection.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | -| 78 | `src/adapters/realtime/sse/index.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | -| 79 | `src/adapters/realtime/sse/sse-parser.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | -| 80 | `src/adapters/realtime/stream-coordinator.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | -| 81 | `src/adapters/realtime/websocket/index.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | -| 82 | `src/adapters/realtime/websocket/websocket-connection.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | -| 83 | `src/adapters/realtime/websocket/websocket-protocol.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | -| 84 | `src/adapters/service-worker/service-worker-entry.ts` | [Worker/push](./05-service-worker-and-web-push.md) | -| 85 | `src/adapters/service-worker/service-worker-lifecycle.ts` | [Worker/push](./05-service-worker-and-web-push.md) | -| 86 | `src/adapters/service-worker/service-worker-page-controller.ts` | [Worker/push](./05-service-worker-and-web-push.md) | -| 87 | `src/adapters/service-worker/service-worker-protocol.ts` | [Worker/push](./05-service-worker-and-web-push.md) | -| 88 | `src/adapters/service-worker/service-worker-removal.ts` | [Worker/push](./05-service-worker-and-web-push.md) | -| 89 | `src/adapters/service-worker/service-worker-static-assets.ts` | [Worker/push](./05-service-worker-and-web-push.md) | -| 90 | `src/adapters/storage/browser-storage-adapter.ts` | [Storage/files](./03-storage-and-browser-files.md) | -| 91 | `src/adapters/storage/browser-storage-codec.ts` | [Storage/files](./03-storage-and-browser-files.md) | -| 92 | `src/adapters/storage/indexeddb/index.ts` | [Storage/files](./03-storage-and-browser-files.md) | -| 93 | `src/adapters/storage/indexeddb/indexeddb-failure.ts` | [Storage/files](./03-storage-and-browser-files.md) | -| 94 | `src/adapters/storage/indexeddb/indexeddb-governance.ts` | [Storage/files](./03-storage-and-browser-files.md) | -| 95 | `src/adapters/storage/indexeddb/indexeddb-maintenance.ts` | [Storage/files](./03-storage-and-browser-files.md) | -| 96 | `src/adapters/storage/indexeddb/indexeddb-migrations.ts` | [Storage/files](./03-storage-and-browser-files.md) | -| 97 | `src/adapters/storage/indexeddb/indexeddb-runtime.ts` | [Storage/files](./03-storage-and-browser-files.md) | -| 98 | `src/adapters/storage/indexeddb/indexeddb-types.ts` | [Storage/files](./03-storage-and-browser-files.md) | -| 99 | `src/adapters/storage/opfs/browser-opfs-runtime.ts` | [Storage/files](./03-storage-and-browser-files.md) | -| 100 | `src/adapters/storage/opfs/index.ts` | [Storage/files](./03-storage-and-browser-files.md) | -| 101 | `src/adapters/storage/opfs/indexeddb-opfs-journal.ts` | [Storage/files](./03-storage-and-browser-files.md) | -| 102 | `src/adapters/storage/opfs/opfs-byte-store-adapter.ts` | [Storage/files](./03-storage-and-browser-files.md) | -| 103 | `src/adapters/storage/opfs/opfs-policy.ts` | [Storage/files](./03-storage-and-browser-files.md) | -| 104 | `src/adapters/storage/opfs/opfs-worker-client.ts` | [Storage/files](./03-storage-and-browser-files.md) | -| 105 | `src/adapters/storage/opfs/opfs-worker-protocol.ts` | [Storage/files](./03-storage-and-browser-files.md) | -| 106 | `src/adapters/storage/opfs/opfs-worker-runtime.ts` | [Storage/files](./03-storage-and-browser-files.md) | -| 107 | `src/adapters/telemetry/best-effort-telemetry.ts` | [Network/state](./01-network-and-state.md) | -| 108 | `src/adapters/web-push/inbound/notification-click-adapter.ts` | [Worker/push](./05-service-worker-and-web-push.md) | -| 109 | `src/adapters/web-push/inbound/push-event-adapter.ts` | [Worker/push](./05-service-worker-and-web-push.md) | -| 110 | `src/adapters/web-push/index.ts` | [Worker/push](./05-service-worker-and-web-push.md) | -| 111 | `src/adapters/web-push/notification-registry.ts` | [Worker/push](./05-service-worker-and-web-push.md) | -| 112 | `src/adapters/web-push/push-association-fence-store.ts` | [Worker/push](./05-service-worker-and-web-push.md) | -| 113 | `src/adapters/web-push/push-codec.ts` | [Worker/push](./05-service-worker-and-web-push.md) | -| 114 | `src/adapters/web-push/push-registration-gateway.ts` | [Worker/push](./05-service-worker-and-web-push.md) | -| 115 | `src/adapters/web-push/push-subscription-adapter.ts` | [Worker/push](./05-service-worker-and-web-push.md) | -| 116 | `src/adapters/web-push/runtime-support.ts` | [Worker/push](./05-service-worker-and-web-push.md) | -| 117 | `src/adapters/web-push/service-worker-runtime.ts` | [Worker/push](./05-service-worker-and-web-push.md) | -| 118 | `src/adapters/web-push/service-worker-scope-host.ts` | [Worker/push](./05-service-worker-and-web-push.md) | +| 59 | `src/adapters/platform/abortable-operation.ts` | [Network/state](./01-network-and-state.md) | +| 60 | `src/adapters/platform/browser-lifecycle.ts` | [Network/state](./01-network-and-state.md) | +| 61 | `src/adapters/platform/browser-mutation-intent-factory.ts` | [Network/state](./01-network-and-state.md) | +| 62 | `src/adapters/platform/system-clock.ts` | [Network/state](./01-network-and-state.md) | +| 63 | `src/adapters/query-cache/conditional-validator-store.ts` | [Network/state](./01-network-and-state.md) | +| 64 | `src/adapters/query-cache/cursor-pagination-runtime.ts` | [Network/state](./01-network-and-state.md) | +| 65 | `src/adapters/query-cache/server-state-scope-runtime.ts` | [Network/state](./01-network-and-state.md) | +| 66 | `src/adapters/query-cache/tanstack-cache-coordinator.ts` | [Network/state](./01-network-and-state.md) | +| 67 | `src/adapters/query-cache/tanstack-query-cache.ts` | [Network/state](./01-network-and-state.md) | +| 68 | `src/adapters/realtime/event-codec.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | +| 69 | `src/adapters/realtime/event-consumer.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | +| 70 | `src/adapters/realtime/index.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | +| 71 | `src/adapters/realtime/json-member-scanner.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | +| 72 | `src/adapters/realtime/live-poll-handoff-coordinator.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | +| 73 | `src/adapters/realtime/polling/bounded-poll-coordinator.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | +| 74 | `src/adapters/realtime/polling/index.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | +| 75 | `src/adapters/realtime/reconnect-coordinator.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | +| 76 | `src/adapters/realtime/reconnect-policy.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | +| 77 | `src/adapters/realtime/result.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | +| 78 | `src/adapters/realtime/sse/fetch-sse-connection.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | +| 79 | `src/adapters/realtime/sse/index.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | +| 80 | `src/adapters/realtime/sse/sse-parser.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | +| 81 | `src/adapters/realtime/stream-coordinator.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | +| 82 | `src/adapters/realtime/websocket/index.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | +| 83 | `src/adapters/realtime/websocket/websocket-connection.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | +| 84 | `src/adapters/realtime/websocket/websocket-protocol.ts` | [Realtime/RPC](./02-realtime-and-browser-rpc.md) | +| 85 | `src/adapters/service-worker/service-worker-entry.ts` | [Worker/push](./05-service-worker-and-web-push.md) | +| 86 | `src/adapters/service-worker/service-worker-lifecycle.ts` | [Worker/push](./05-service-worker-and-web-push.md) | +| 87 | `src/adapters/service-worker/service-worker-page-controller.ts` | [Worker/push](./05-service-worker-and-web-push.md) | +| 88 | `src/adapters/service-worker/service-worker-protocol.ts` | [Worker/push](./05-service-worker-and-web-push.md) | +| 89 | `src/adapters/service-worker/service-worker-removal.ts` | [Worker/push](./05-service-worker-and-web-push.md) | +| 90 | `src/adapters/service-worker/service-worker-static-assets.ts` | [Worker/push](./05-service-worker-and-web-push.md) | +| 91 | `src/adapters/storage/browser-storage-adapter.ts` | [Storage/files](./03-storage-and-browser-files.md) | +| 92 | `src/adapters/storage/browser-storage-codec.ts` | [Storage/files](./03-storage-and-browser-files.md) | +| 93 | `src/adapters/storage/indexeddb/index.ts` | [Storage/files](./03-storage-and-browser-files.md) | +| 94 | `src/adapters/storage/indexeddb/indexeddb-failure.ts` | [Storage/files](./03-storage-and-browser-files.md) | +| 95 | `src/adapters/storage/indexeddb/indexeddb-governance.ts` | [Storage/files](./03-storage-and-browser-files.md) | +| 96 | `src/adapters/storage/indexeddb/indexeddb-maintenance.ts` | [Storage/files](./03-storage-and-browser-files.md) | +| 97 | `src/adapters/storage/indexeddb/indexeddb-migrations.ts` | [Storage/files](./03-storage-and-browser-files.md) | +| 98 | `src/adapters/storage/indexeddb/indexeddb-runtime.ts` | [Storage/files](./03-storage-and-browser-files.md) | +| 99 | `src/adapters/storage/indexeddb/indexeddb-types.ts` | [Storage/files](./03-storage-and-browser-files.md) | +| 100 | `src/adapters/storage/opfs/browser-opfs-runtime.ts` | [Storage/files](./03-storage-and-browser-files.md) | +| 101 | `src/adapters/storage/opfs/index.ts` | [Storage/files](./03-storage-and-browser-files.md) | +| 102 | `src/adapters/storage/opfs/indexeddb-opfs-journal.ts` | [Storage/files](./03-storage-and-browser-files.md) | +| 103 | `src/adapters/storage/opfs/opfs-byte-store-adapter.ts` | [Storage/files](./03-storage-and-browser-files.md) | +| 104 | `src/adapters/storage/opfs/opfs-policy.ts` | [Storage/files](./03-storage-and-browser-files.md) | +| 105 | `src/adapters/storage/opfs/opfs-worker-client.ts` | [Storage/files](./03-storage-and-browser-files.md) | +| 106 | `src/adapters/storage/opfs/opfs-worker-protocol.ts` | [Storage/files](./03-storage-and-browser-files.md) | +| 107 | `src/adapters/storage/opfs/opfs-worker-runtime.ts` | [Storage/files](./03-storage-and-browser-files.md) | +| 108 | `src/adapters/telemetry/best-effort-telemetry.ts` | [Network/state](./01-network-and-state.md) | +| 109 | `src/adapters/web-push/inbound/notification-click-adapter.ts` | [Worker/push](./05-service-worker-and-web-push.md) | +| 110 | `src/adapters/web-push/inbound/push-event-adapter.ts` | [Worker/push](./05-service-worker-and-web-push.md) | +| 111 | `src/adapters/web-push/index.ts` | [Worker/push](./05-service-worker-and-web-push.md) | +| 112 | `src/adapters/web-push/notification-registry.ts` | [Worker/push](./05-service-worker-and-web-push.md) | +| 113 | `src/adapters/web-push/push-association-fence-store.ts` | [Worker/push](./05-service-worker-and-web-push.md) | +| 114 | `src/adapters/web-push/push-codec.ts` | [Worker/push](./05-service-worker-and-web-push.md) | +| 115 | `src/adapters/web-push/push-registration-gateway.ts` | [Worker/push](./05-service-worker-and-web-push.md) | +| 116 | `src/adapters/web-push/push-subscription-adapter.ts` | [Worker/push](./05-service-worker-and-web-push.md) | +| 117 | `src/adapters/web-push/runtime-support.ts` | [Worker/push](./05-service-worker-and-web-push.md) | +| 118 | `src/adapters/web-push/service-worker-runtime.ts` | [Worker/push](./05-service-worker-and-web-push.md) | +| 119 | `src/adapters/web-push/service-worker-scope-host.ts` | [Worker/push](./05-service-worker-and-web-push.md) | -합계: **118/118**. 새 adapter 파일이 추가되면 이 ledger와 해당 상세 리뷰 inventory를 같은 변경에서 갱신한다. +합계: **119/119**. 새 adapter 파일이 추가되면 이 ledger와 해당 상세 리뷰 inventory를 같은 변경에서 갱신한다. diff --git a/package.json b/package.json index 1c31607..ab61a50 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "check:design-system:fixture": "node scripts/check-design-system.ts --fixture", "check:i18n": "node scripts/check-i18n.ts", "check:i18n:fixture": "node scripts/check-i18n.ts --fixture", + "check:adapter-inventory": "node scripts/check-adapter-inventory.ts", "check:diagnostics": "node scripts/check-diagnostics.ts", "check:diagnostics:fixture": "node scripts/check-diagnostics.ts --fixture", "check:types": "corepack pnpm check:types:app && corepack pnpm check:types:node && corepack pnpm check:types:test && corepack pnpm check:types:recipes && corepack pnpm check:types:web-worker && corepack pnpm check:types:service-worker", diff --git a/scripts/check-adapter-inventory.ts b/scripts/check-adapter-inventory.ts new file mode 100644 index 0000000..ba1f8c1 --- /dev/null +++ b/scripts/check-adapter-inventory.ts @@ -0,0 +1,107 @@ +import { readFile } from "node:fs/promises"; +import { spawnSync } from "node:child_process"; + +import { CACHEABLE_ASSET_CONTENT_TYPES } from "../src/contracts/service-worker-static-manifest.ts"; + +/** + * GOV-01 / SW-RR-03. Structural gates for facts that a hand-maintained document + * cannot keep true. + * + * The adapter review inventory claimed 118/118 while the tree held 119 files, + * so a whole adapter was outside every review's coverage without anything + * failing. And the Service Worker asset generator and the shared manifest + * decoder each carried their own extension table, so a build could emit an + * asset the runtime contract then refused. Both are now equalities this script + * checks rather than numbers someone has to remember to update. + */ + +const INVENTORY_PATH = "docs/reviews/adapters/INVENTORY.md"; +const GENERATOR_PATH = "scripts/generate-service-worker-assets.ts"; + +function trackedAdapterFiles(): readonly string[] { + const listed = spawnSync("git", ["ls-files", "src/adapters"], { + encoding: "utf8", + }); + if (listed.status !== 0) { + throw new Error(`git ls-files failed: ${listed.stderr}`); + } + return listed.stdout.split("\n").filter(Boolean).sort(); +} + +function inventoryRows(markdown: string): readonly string[] { + const rows: string[] = []; + for (const line of markdown.split("\n")) { + const match = /^\|\s*\d+\s*\|\s*`([^`]+)`\s*\|/u.exec(line); + if (match?.[1]) rows.push(match[1]); + } + return rows; +} + +function reportDifference( + label: string, + expected: readonly string[], + actual: readonly string[], +): readonly string[] { + const missing = expected.filter((value) => !actual.includes(value)); + const extra = actual.filter((value) => !expected.includes(value)); + const problems: string[] = []; + for (const value of missing) problems.push(`${label}: missing ${value}`); + for (const value of extra) problems.push(`${label}: unexpected ${value}`); + return problems; +} + +async function main(): Promise { + const problems: string[] = []; + + const tracked = trackedAdapterFiles(); + const markdown = await readFile(INVENTORY_PATH, "utf8"); + const listed = inventoryRows(markdown); + problems.push(...reportDifference("adapter inventory", tracked, listed)); + if (listed.length !== new Set(listed).size) { + problems.push("adapter inventory: duplicate row"); + } + const total = /합계: \*\*(\d+)\/(\d+)\*\*/u.exec(markdown); + if ( + !total || + Number(total[1]) !== tracked.length || + Number(total[2]) !== tracked.length + ) { + problems.push( + `adapter inventory: total does not equal ${tracked.length} tracked files`, + ); + } + + // SW-RR-03. The generator must read the shared table rather than declare one. + const generator = await readFile(GENERATOR_PATH, "utf8"); + if (!generator.includes("CACHEABLE_ASSET_CONTENT_TYPES")) { + problems.push( + "service worker assets: generator does not use the shared extension table", + ); + } + if (/const CACHEABLE_EXTENSIONS[^=]*=\s*Object\.freeze\(\{/u.test(generator)) { + problems.push( + "service worker assets: generator declares its own extension table", + ); + } + for (const [extension, contentType] of Object.entries( + CACHEABLE_ASSET_CONTENT_TYPES, + )) { + if (!extension.startsWith(".") || contentType.length === 0) { + problems.push(`service worker assets: invalid table row ${extension}`); + } + } + + if (problems.length > 0) { + for (const problem of problems) console.error(problem); + process.exitCode = 1; + return; + } + console.log( + `Adapter inventory: ${tracked.length} files PASS; ` + + `service worker asset table: ${ + Object.keys(CACHEABLE_ASSET_CONTENT_TYPES).length + } shared extensions PASS`, + ); +} + +await main();