1558 -> 1430줄. 손수 abort 리스너가 0이 되어 래칫을 23에서 22로 조인다.
보존한 동작 셋:
- blockedTimeoutMs를 넘기지 않는다. blocked 이벤트는 유지보수에게 종단이다
— 배치는 opt-in 백그라운드 패스라 다른 컨텍스트가 사라지길 기대하며
데드라인만큼 매다는 비용이 BLOCKED를 보고하고 재시도시키는 것보다 크다
- upgrade 콜백을 생략한다. 커널은 생략을 "어떤 upgrade든 예상 밖"으로 읽고
거절하는데, 그게 유지보수가 늘 해온 동작이다. 빈 APPLIED를 넣으면 잘못된
스키마로 열린다
- onversionchange는 admit 안, 바인딩 검증 앞에 등록한다
계획이 blocked 회귀 테스트로 지목한 indexeddb-maintenance.test.ts:289-290은
실제로는 onblocked 경로가 아니라 drain 검사였다. MT의 진짜 blocked 경로에는
테스트가 없었으므로 새로 썼다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
712 -> 612줄. 연결 수명주기와 트랜잭션 상태기계가 커널로 갔고 CP의 손수
abort 리스너가 0이 됐다. 그래서 abort 래칫을 24에서 23으로 조인다.
보존한 동작 다섯 가지:
- 값 없는 완료는 CORRUPT_DATA/RECONCILE (translate에 NO_VALUE_PRODUCED를
명시 매핑). 빠뜨리면 UNAVAILABLE로 바뀐다
- durability는 undefined를 명시적으로 넘긴다. 커널 기본이나 strict를 쓰면
체크포인트 쓰기가 조용히 느려진다
- nativeFailure 6곳 전부 context.fail. requestFailed로 바꾸면 요청이
실패했는데도 뒤 요청이 커밋된다
- abort()가 throw하면 caller-abort 주장을 철회한다 (커널이 이미 구현)
- blocked 타이머는 주입형 timers
사양이 빠뜨린 함정 하나를 추가로 막았다. bindScope는 succeed()에 해당하는
것이 없어 그대로 옮기면 정상 바인딩이 NO_VALUE_PRODUCED로 보고된다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
커널이 있는데도 어댑터 24개 파일이 addEventListener("abort")로 같은
race/cleanup을 각자 짠다. 전수 이행은 abort 의미론을 바꾸는 별도 작업이라
한 번에 하지 않고, 개수를 게이트에 고정해 되돌아가지 못하게 한다.
파일을 하나 더 늘려 실제로 막히는 것을 확인했다.
REQUIRED_ABORT_CONSUMERS 주석도 고친다. 그 목록은 재검토가 이름으로 지목한
네 파일만 덮는데, 주석은 Image와 Resumable을 지목하면서 정작 그 두 그룹의
런타임 파일(image-cdn-runtime.ts, resumable-upload-runtime.ts)은 목록에 없어
게이트가 초록불인 채로 사본이 유지되고 있었다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The ledger declared "All 38 are now FIXED" while six of those rows were
reproducibly partial. A summary sentence is cheap and a reviewer reads it
as evidence, so the claim is now derived from a machine-readable record:
`docs/operations/adapter-remediation-dispositions.json` carries each
finding's disposition and the test paths that hold it, and
`check:remediation-ledger` joins that file to the prose, verifies every
evidence path exists, and refuses a blanket closure sentence while any row
is still open.
The shared-abort gate had the same weakness in miniature: it passed when
at least one production file imported the primitive, so an unrelated
import satisfied it while Image and Resumable kept their own diverging
copies. It now requires the four named consumers to resolve their import
to the primitive itself, and prints the exact importer set rather than a
count.
`check:optional-recipes:source` was already failing before this work
(52,078 against a 52,000 budget) and the correctness code above pushed it
further. Duplicate abort mechanics were consolidated first — Image and
Resumable onto the shared primitive, four decoders onto one snapshot
helper — and the remainder is code the review asked for, so the budget is
reset to 54,600 against a measured 53,810 with that reasoning recorded,
rather than the failure being carried forward as if it were green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
TR-RR-06. dispose() now bounds its drain with a cleanupDeadlineMs from policy
and returns the result, so a non-cooperative mutation lock or provider can no
longer make teardown unbounded and an unproved drain is reported as still
CLOSING instead of closed over. The checkpoint store stays open in that case,
because something can still write to it. An abort is admitted physical work
like an upload, so it joins the tracked set rather than being stepped over.
TR-RR-07. The verification slot belongs to the raw verifier, not the wrapper.
Releasing it when the caller's wait expired let an abandoned verification keep
running while a new one was admitted, so repeated aborts produced more
concurrent physical work than the configured cap allows. The slot is now
released only once the raw tasks settle.
TR-RR-04. A presigned byte source owns a fetch reader and a capability lease and
its port requires close(); the delivery consumer never called it. The closeable
subtype is lost in the FileByteSource projection, so a holder keeps it from the
moment the lease exists and the outermost finally closes it exactly once — on
success, validation failure, writer failure and abort alike.
check:adapter-inventory now also fails if the shared abortable-operation
primitive has no production importers. It was safe to add only once the
presigned subsystems actually migrated onto it; a gate that fails CI for a
documented, unfixed defect reports the wrong thing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Four fixtures linked the installed dependencies into a throwaway root with a
single directory symlink at <fixture>/node_modules, then ran pnpm inside that
root. pnpm does not recognise the modules directory it finds there and purges
it; with CI=true it does so without a prompt. The purge followed the symlink and
deleted the repository's own node_modules mid-run, so a test suite uninstalled
the workspace it was running in. That is what produced the cascading,
file-unrelated failures a full test:unit run reported, and it happened twice
while running the suites for the adapter re-review.
scripts/lib/fixture-node-modules.ts replaces all four sites: node_modules is a
real directory whose entries are individual symlinks, so a recursive delete
unlinks the fixture's own links instead of walking through one link into the
shared tree. Resolution is unchanged.
tests/unit/fixture-node-modules.test.ts performs the exact recursive delete pnpm
performs and asserts the source tree survives, and check:adapter-inventory now
fails on any reintroduction of the directory-symlink form — verified by putting
the old line back and watching the gate reject it.
A full tests/unit + tests/integration run now leaves the dependencies intact.
removal-fixture, supply-chain and security-followup-archive, the three suites
that had to be excluded before, pass in that run.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
GOV-01. The adapter inventory claimed 118/118 while the tree held 119 files, so
src/adapters/platform/abortable-operation.ts sat outside every review's
coverage without anything failing. The row is restored and
scripts/check-adapter-inventory.ts now diffs the document against
git ls-files src/adapters, so the count is an equality rather than a number
someone has to remember. The same gate pins that the Service Worker asset
generator reads the shared extension table instead of declaring its own.
GOV-02. The previous ledger closed rows as FIXED_NOT_RELEASED that the
re-review found partial. The new section is written the other way round: a row
reads FIXED only where a named adversarial test failed on the pre-fix source
and passes on the landed one, and the twelve findings this pass did not reach —
RPC-RR-01, RT-RR-01 through RT-RR-04 and TR-RR-01 through TR-RR-07 — are
recorded as NOT_STARTED with the reason each needs a lifecycle change rather
than a contained edit. None of them may be treated as closed and no capability
they cover may be promoted without its own evidence row.
The structural gate for the shared abortable-operation primitive is
deliberately not added yet: it still has zero production importers, and a gate
that fails CI for a documented, unfixed defect would report the wrong thing.
Also records the destructive test hazard found while running the suites:
scripts/lib/removal-fixture.ts and scripts/check-supply-chain-provider-fixtures.ts
symlink the real node_modules into a temp fixture root and run pnpm there, which
purges the repository's own dependencies through the symlink mid-run.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>