The primitive decided a raced outcome by draining a hard-coded four
microtasks and then asking whether the task had landed. That made the
answer depend on scheduling rather than on what was observed: a caller
abort could fix the terminal owner synchronously and a rejection later in
the same call stack still won the public result, so `race()` disagreed
with `terminal()` and the failure taxonomy a caller received depended on
microtask ordering.
Task settlement and the terminal event now share one settle-once state
machine. Whichever callback actually runs first owns the outcome; a value
that loses is compensated exactly once and a rejection that loses is
absorbed, so neither can surface late.
The three consumers that kept their own copies of these mechanics move
onto it. The Image probe and the Resumable fetch transport attached their
caller listener before installing the timer, so a scheduler that threw
rejected the public `probe()`/`execute()` promise natively and left the
listener on the caller's signal; both now close atomically inside their
own Result vocabulary and start no fetch. `snapshotAbortTimers` binds the
scheduler callables once at construction, so replacing a method after
composition can no longer change how work already in flight is bounded.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
All 38 findings from the 2026-08-14 adapter re-review are now FIXED. The twelve
the first pass did not reach — RPC-RR-01, RT-RR-01 through RT-RR-04 and
TR-RR-01 through TR-RR-07 — each landed with a named adversarial test that
failed on the pre-fix source and passes on the landed one, verified by reverting
the source file and re-running.
The ledger also records the six contracts that changed shape and are therefore
breaking for an external implementor: the Browser RPC server-stream lease, the
optional recovery context on AuthSessionPort, the presigned registration
protocol version, the resumable dispose result and cleanup deadline, and the
already-landed required image resolve signal.
Co-Authored-By: Claude Opus 5 (1M context) <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>
TR-RR-05. The shared abortable-operation primitive now distinguishes VALUE,
REJECTED and TERMINAL, so a collaborator's own rejection is no longer forged
into a cancellation and race() always names the same first owner terminal()
reports. The caller signal and scheduler are captured once, so replacing a
method after construction cannot change how an in-flight operation is bounded.
A scheduler that cannot install the deadline is itself terminal: previously it
released the caller listener and left no owner, which made every later abort
invisible. Late values are compensated exactly once.
Both presigned subsystems, which each carried their own copy of these
mechanics, are now projections of that primitive — giving it real production
importers rather than a shared helper nobody used.
TR-RR-01. close() on an active download aborts the scope instead of only
dropping listeners, so a fetch or read already in flight actually stops. The
consumer's stream signal joins the operation's ownership before any I/O begins,
so an already-aborted consumer no longer causes one network request first.
TR-RR-02. The upload abort scope is created before the digest, and the digest
races the caller and the deadline like every other step. A non-settling hash can
no longer hold put() open, and the vault claim and the network call happen only
after the owner is re-checked.
TR-RR-03. A capability registration is a versioned exact union validated at
registration time: the protocol version, HTTPS only, exact own-data fields, a
2xx expected status, and no ambient credential or cookie header — a presigned
URL carries its own authorization, and a session header alongside it would send
the user's credentials to that origin.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
RT-RR-01. An effect or recovery task was registered as retained only after its
public wait expired, so a close() that arrived first saw an empty set and
reported quiescence while the raw task was still running against the authority.
Tasks are now registered when they are created and removed when they settle;
DRAINING keeps its narrower meaning through a separate timed-out set.
RT-RR-02. Admission happened when an event was queued; execution is a second
decision. A queue entry admitted before the stream entered DRAINING no longer
starts running inside it. And an abandoned task may have applied part of its
effect, so the resume token it was based on is discarded and recovery is
required explicitly — the next ordinary event can no longer skip authoritative
recovery on the strength of state a timed-out effect may have invalidated.
RT-RR-03. close() cached the first timeout forever, so a writer that later
settled could never be proved quiescent and the retained registry could never be
pruned. Only an in-flight close is shared now, every writer a close fences is
retained until its tail actually settles, and the tail prunes itself. A second
close therefore converges to success once the writer finishes.
RT-RR-04. Checkpoint work joins writer tails in the physical-task registry from
invocation to settlement and is drained on the same terms.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
RPC-RR-01. openServerStream returned a bare AsyncIterable, which gave the
runtime no way to stop the physical stream or to learn when it actually closed:
iterator.return() is a request a non-cooperative implementation may ignore. The
runtime could therefore time out, report the call finished, and admit a second
stream for the same operation while the first was still running against the
server.
The transport now returns a lease — streamId, frames, cancel(reason) and
waitClosed() — decoded from own data descriptors before the runtime registers
it, so an accessor cannot hand the registry one object and the cancellation
path another. The runtime registers the lease the moment the physical stream
exists, cancels exactly once on exit, and keeps the entry until waitClosed()
settles. A second stream for the same operation is refused as CONFLICT /
RPC_STREAM_DRAINING while that entry stands, and the refusal never reaches the
transport.
While updating the suites this also corrected two RPC-RR-02/RPC-RR-04 stream
tests that were passing for the wrong reason: they sent an input the request
schema rejects, so they never reached the fence or the frame decoder. With the
correct input the frame test fails on a permissive decoder, as it should.
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>
TR-RR-08. Object.keys sees only enumerable own string keys, so a symbol or
non-enumerable extra field passed the exactness check unseen and the property
reads that followed invoked whatever accessor the sender installed — escaping
the Result contract as a native rejection out of a public method. Key exactness
is now checked against own property descriptors inside a catch, and each decode
runs within the adapter's failure boundary so a proxy trap becomes a typed
CORRUPT_DATA result.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
TR-RR-09. The documented BT-IMG-02 contract requires a private response to
carry no-store and fail closed when any directive that describes cacheability
accompanies it. The probe checked only that no-store was present and public was
absent, and the suite pinned the contradictory "private, no-store" as a success.
Both now follow the recorded contract: only no-store and syntactically valid
unknown extensions are admitted, and public, private, immutable, max-age,
s-maxage, no-cache, must-revalidate and proxy-revalidate each fail closed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SW-RR-01. The activation marker was read with response.text() whenever no
Content-Length was present, so a large or non-terminating body could consume
the whole activation step. It now reads through a bounded reader that stops one
byte past the ceiling, cancels its reader, applies a read deadline and decodes
UTF-8 fatally.
SW-RR-02. A matching nonce is not identity. An activation or reset result whose
event.source is null can no longer stand in for the expected worker; only a
strict identity match is admitted.
SW-RR-03. The build generator and the shared manifest decoder now read one
exported extension table, so .mjs and .png stop being emitted-then-refused.
.json is deliberately outside it: every JSON file in a build output is a control
document the generator already excludes, not a cacheable asset.
SW-RR-04. Both caches.open and cache.match are closed as a miss. Letting a
match rejection propagate rejected respondWith itself, so the entry never
reached its network fallback.
WP-RR-01. focus and openWindow now carry the certainty phase showNotification
already had — NOT_APPLIED, MAYBE_APPLIED, CONFIRMED — and an effect that lands
after the handler deadline is observed exactly once. The evidence never
authorizes a retry.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
RPC-RR-02. The server-stream path captured the generation fence outside its
protected boundary and raceWithin invoked clock.sleep outside a promise
boundary, so a synchronous throw from either escaped the Result contract and
skipped the listener and timer release. Both now run inside the boundary, and
release moved to finally.
RPC-RR-03. The runtime snapshotted its transports only after validating the
caller's raw objects, which ran their accessors first. It now decodes the
registry from own data descriptors before anything reads it — refusing an
accessor without invoking it and rejecting extra, inherited and symbol-keyed
fields — and validates that snapshot. Every installed binding registry is a
read facade over a private store instead of a frozen Map whose set, delete and
clear still worked.
RPC-RR-04. Transport results and stream frames are decoded per union variant
from own data descriptors into new frozen values. A throwing getter, an
inherited or extra field, a symbol key, an unknown failure code and an
out-of-range retryAfterMs all close as protocol failures instead of escaping.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
STO-RR-01. finalizePut re-acquired the origin mutation lease it was already
holding. A Web Lock is not reentrant, so an ordinary PUT stopped for good at
FINALIZE; it now calls the locked cleanup directly. A strict non-reentrant fake
lease manager pins one acquire and one release per finalization. The adapter no
longer reports a failed finalization as a plain write success either: the
journal row stays COMMITTED for reconciliation, but the caller is told the
write did not settle.
STO-RR-02. A failure raised while serving a validated request now carries that
request's kind. Defaulting every catch to CAPABILITIES made the client's own
expected-kind check reject genuine quota, integrity and abort failures as
protocol breaches and report them as UNSUPPORTED. Only an envelope the runtime
could not read still answers at protocol level.
STO-RR-03. The worker client decodes a response instead of adopting it: exact
own-data descriptors, the negotiated protocol version, the exact awaited kind,
a code inside the closed BrowserDataFailure set and a boolean retryable. An
accessor, a proxy trap, an inherited or extra field and an unknown code all
close the call as UNSUPPORTED rather than leaving it to time out.
STO-RR-04. A marker read that fails transiently is unknown, not damaged, so it
no longer deletes the candidate that may be serving traffic. Only a confirmed
corrupt or missing marker enters the repair path.
STO-RR-05. Staging never deletes a candidate it did not create. A repair
replaces exact entries in place, so a failed fetch leaves every healthy asset
and the active release usable; a candidate this call created is still removed
on failure.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
LEG-01. AuthSessionPort.recover now takes the request's lifetime context, and
the raw recovery helper returns data only. The sign-out notification moved to
the site that adopts the result, so a recovery that answers after the deadline
or a caller abort is observed and discarded instead of logging the user out of
a request nobody is waiting on.
LEG-02. The V2 client shares V3's credential admission validator instead of
checking the allowed set alone. A bearer profile whose patch omits, empties,
duplicates or corrupts Authorization now fails closed with zero fetches rather
than dispatching an anonymous request under an authenticated profile.
OPT-NET-01. A cursor loader rejection is re-thrown exactly as it is with no
signal at all. Only a signal that has actually aborted classifies the outcome
as PAGINATION_ABORTED, so a real upstream failure stops being filed as a user
cancellation.
OPT-NET-02. defineMutationIntent and the V3 admission site now share the single
isValidIdempotencyKey authority, closing the drift that let a control character
through intent definition.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
LIVE-01. A credential collaborator that returns UNAVAILABLE, throws, rejects
or answers off-contract is an outage of the auth integration, not evidence
about the user's session. Each of those now closes as AUTH_INTEGRATION_FAILURE
with zero fetches, so the composition root's logout path stays reserved for a
genuinely absent session. The synchronous and asynchronous failure sites share
one classifier.
LIVE-02 / LIVE-03. Object.freeze(new Map(...)) freezes the wrapper, not the
backing store, so an exported registry could still be cleared or replaced after
composition. Both the installed REST auth profile registry and the composed
HTTP/event lookups are now read facades over private stores, and every composed
row is an exact own-data snapshot that rejects accessors, inherited and
symbol-keyed fields.
LIVE-04. The total deadline now bounds the physical waits rather than being
checked between them: dispatch and response admission race the attempt signal,
the bounded reader takes that signal, and an abandoned operation is still
observed once so a late native rejection cannot surface unhandled. A body that
completes after the deadline or the caller owns the execution is no longer
admitted; a stale generation keeps its more specific SCOPE_FENCED verdict.
LIVE-05. DEADLINE is no longer treated as a caller-owned cancellation, so a
timeout reaches api.request.failed exactly once while caller, route, scope and
shutdown aborts stay excluded.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
All 64 finding IDs across the five reviews now carry an explicit state and none
remains NOT_STARTED: 51 fixed, 6 promotion-blocked by design, 4 deferred to the
multi-release wire migrations, 2 cohesion refactors not performed, and 1 browser
hypothesis unverified.
BT-UP-05 records that the extraction was attempted and reverted rather than
half-landed, with the specific reason: the five internal owners need a
shared-internals module for roughly forty types, constants and helpers to avoid
an import cycle.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
BT-UP-06: close() previously aborted the lifetime and closed the checkpoint
store immediately, so a caller could not wait for an active operation's terminal
settlement and a late provider result could still race the store.
The runtime now tracks every admitted operation until it settles. close() stays
the compatibility facade that closes admission and starts the drain, while
dispose() returns that same single-flight promise: it aborts the operation
registry, awaits actual settlement, and only then closes the checkpoint store
and cancellation channel. lifecycle() exposes OPEN, CLOSING and CLOSED, and a
draining runtime refuses new admission.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
BT-PRE-02: add the top-level PRESIGNED_TRANSFER_V1 protocol literal to the
capability request and response. A missing, V0 or V2 envelope is closed as
POLICY_REJECTED before the vault registers anything. The server negotiates by
request shape; fields are never dual-emitted into a strict decoder, and the
nested PRESIGNED_MULTIPART_V1 binding protocol is unchanged.
BT-PRE-03: aborting a controller does not settle a fetch that ignores its
signal, so both presigned scopes now race the task, cancel a late response body
and survive a throwing scheduler without leaking the external abort listener.
BT-PRE-04: the vault owns its registration invariants, re-checking method,
href/origin/path agreement, embedded credentials, byte bounds, digest shape and
expiry, so a second issuer cannot register a weaker capability of the same type.
BT-PRE-05: decode each path segment once and require it to round-trip through
the canonical uppercase percent encoder, closing %2f, %5c, %252e%252e, mixed-case
escapes and encoded NUL while still admitting valid opaque UTF-8 segments.
BT-UP-02: inject and snapshot the upload transport clock and scheduler, so
Retry-After delta-seconds and HTTP-date resolve against the same captured now
and a clock rollback clamps to zero instead of producing a negative delay.
BT-IMG-01: make the image resolve() lifetime signal required, replacing the
hidden PRIMARY_REQUIRED preset precondition with a type-level one, and add the
negative typecheck fixture and gate that prove it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SW-06: correlate activation, reset and drain replies by source object identity
against the captured waiting worker or controller, so an arbitrary same-origin
source cannot close this page's admission, and end a request immediately as
PROTOCOL_MISMATCH when the source is swapped instead of waiting for the drain
timeout. requestActivation() and resetOwnedCaches() are single-flight, so ten
concurrent callers share one nonce, listener and postMessage.
SW-07: an empty in-scope client set is vacuously drained rather than rejecting
a waiting worker when the requester already closed.
SW-08: isolate per-client postMessage failures. A client that cannot receive the
drain request fails immediately instead of holding pending state to the timeout,
skipWaiting() is the activation commit and its failure is a rejection, and the
accepted and reload notifications are sent afterwards as best effort.
SW-09: fence late install work. A fenced worker starts no new candidate work, a
late response body from a non-cooperative fetch is cancelled, a throwing digest
maps to a closed outcome, and a second exact delete of the owned candidate cache
is registered once the abandoned install settles - without extending the public
60s bound.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Record the focused subsystem suites, repository gates and the exact status of
every gate that is not green, with attribution rather than assumption:
- ci-artifact-contract remains at its baseline 19 sandbox failures and is never
claimed as green
- WebKit browser capabilities are UNVERIFIED because the engine cannot launch
here; chromium passes
- the two runtime-removal fixtures fail on pre-existing reduced-fixture
arithmetic in scripts/contracts/ci-gates.ts, which is unchanged since the
baseline revision
- provider and staging rollback matrices are UNVERIFIED, not PASS
Also record what was deliberately not performed: the Task 17 extraction refactor
and the multi-release V2 wire rollouts, together with the in-repo prerequisites
that did land for each.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
STO-06: the IndexedDB codec migration commit chain runs entirely inside
IndexedDB callbacks, so up to maxRows records could keep executing past the
caller's cooperative deadline. The monotonic budget is now re-checked before
each record's first write; a started record still completes atomically, the
checkpoint advances only to the last safe key, and a clock failure aborts the
transaction rather than committing an unbounded batch.
STO-07: every OPFS worker request and response now carries
OPFS_WORKER_PROTOCOL_VERSION = 2, responses echo their request kind, and the
client validates the envelope and failure shape strictly while remembering the
expected kind per pending request. A page/worker release mismatch or a reply for
a different operation closes as UNSUPPORTED instead of being decoded as a value
of the wrong shape. UNSUPPORTED is used deliberately: the closed browser-data
taxonomy has no INCOMPATIBLE code and none was invented.
SW-10 and the OPFS/Web Push V2 wire rollouts remain deferred: they are
expand/dual-read/drain/contract deployments across releases rather than a single
in-repo change. The ledger records them as DEFERRED_TO_MIGRATION.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
WP-01: a CAS receipt is only evidence when it names the expected key and the
exact next revision. Write and remove now share one validator, so a stale or
arbitrary repository receipt can no longer be packaged as a confirmed control.
WP-05: a pre-aborted command records the operation the caller requested instead
of always reporting an inspection.
WP-06: bounded fan-out is reported honestly. The subscriptionchange client
handoff and the notification cleanup both emit countBucket and truncated, and an
incomplete cleanup returns { complete: false } and is observed as DEGRADED
separately from revoke authority.
WP-07: the user-visible native notification effect is tracked through
NOT_APPLIED, MAYBE_APPLIED and CONFIRMED phases and surfaced as observation
evidence, never as retry authorization.
WP-02, WP-03 and WP-04 stay open: they need the V2 wire protocol with server
request-shape negotiation, which belongs to the versioned-migration task rather
than this correctness pass. The ledger records them as DEFERRED_TO_MIGRATION.
Web Push remains NOT_SELECTED and AVAILABLE_NOT_COMPOSED.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SW-URL-01: canonicalize each generated root-relative manifest URL against the
registration scope once, re-check same-origin, and share that absolute identity
across install cache keys, fetch classification and cache lookup or delete.
Previously every verified asset fell through to the network.
SW-01: serve verified static requests only from the current release cache. A
CacheStorage-wide match could return a previous release's response for the same
URL while the delete targeted a cache that was never read. The worker scope
facade no longer exposes a wide match at all.
SW-02: cache reset deletes only names that parse as owned, so a foreign cache
sharing the ca-static-v1- prefix survives.
SW-03: unregister() resolving to false is a FAILED unregister, not UNREGISTERED.
SW-04: staged removal reports what happened - ABSENT, UNREGISTERED and PURGED
map to DISABLED, OWNERSHIP_MISMATCH to INCOMPATIBLE and FAILED to FAILED - so a
later release cannot delete the worker while a registration or owned cache is
still present.
SW-05: add the runtime-neutral service-worker-static-manifest codec that owns
exact row keys, the extension and content-type allowlist, the root-relative URL
rule and the length-prefixed canonical bytes. The generator and the build gate
hash those same bytes, and the build gate now decodes and recomputes the set
digest instead of type-casting the manifest.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
BT-X-01: add the shared abortable-operation utility with golden tests for first
terminal owner, idempotent close, listener and timer cleanup under a throwing
scheduler, observed late rejection and late-handle compensation. It carries no
subsystem result taxonomy.
BT-PRE-01: make the presigned download lease lazy and single-start. open() now
validates, claims and consumes the capability without any network I/O; the
fetch, the transfer deadline and the expiry recheck happen at first stream
consumption. The source gained close(), which discards an unused lease with no
I/O and otherwise cancels the body and releases the scope exactly once.
BT-UP-01: require removeEventListener in the AbortSignal structural guard and
isolate release cleanup so a hostile facade cannot replace a typed terminal
result with a rejection.
BT-UP-03: deleteDatabase cannot be cancelled after dispatch, so a blocked
deadline now returns PENDING with effect UNKNOWN instead of a failure that reads
as NOT_APPLIED. A realm-scoped (factory, databaseName) registry prevents
recreating the partition until the native request settles.
BT-UP-04: reject non-finite and negative upload clocks as a dependency failure
instead of letting them bypass every capability expiry comparison.
BT-IMG-02: replace the naive Cache-Control quote stripping with a quote- and
escape-aware tokenizer, so max-age="60 or 60" is no longer read as 60 and a
comma inside a quoted extension is not a directive boundary.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
R-04: install the RPC contract bindings as exact immutable snapshots. Registry
and row data are copied from own data descriptors into frozen null-prototype
maps before validation, so a getter is never invoked, extra and symbol keys and
malformed descriptors are composition-time TypeErrors, and the runtime reads
only the snapshot. A post-validation mutation can no longer change replay
policy, deadlines, byte ceilings or transport selection.
R-01: bound transport stream cleanup. The generation is fenced and listeners
released immediately, and iterator.return() is awaited only within a cleanup
bound, so a non-cooperative iterator cannot keep the application generator, its
listeners or the total deadline alive. Unresolved cleanup stays observed.
R-05: reject oversized WebSocket text frames before allocating an encoded copy
and count UTF-8 bytes incrementally with an early exit, matching TextEncoder for
surrogate pairs and lone surrogates.
R-06: canonicalise clock and generation-fence failures into the closed Result
taxonomy instead of letting them escape as native rejections, with listener and
timer cleanup on every exit path.
Browser RPC remains AVAILABLE_NOT_COMPOSED; R-07 transport evidence is still
required before composition.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
R-02: add an OPEN/DRAINING/CLOSED lifecycle orthogonal to freshness. Effect and
recovery authorities are now awaited under a deadline: on expiry the commit
capability is revoked and the work aborted, the caller gets a bounded
non-retryable IDLE_TIMEOUT, and the underlying task is retained rather than
dropped. A draining stream refuses new events and recovery, and close() returns
a Promise that succeeds only once every retained task actually settled,
reporting IDLE_TIMEOUT otherwise.
R-03: a handoff fail-close moves active, probe, quiescing and transition leases
into a retired-writer set before clearing their references, and close() waits on
current and retired writers together, so an abandoned non-cooperative writer can
no longer make teardown report a false success.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
N-06: export one idempotency-key authority from mutation-intent.ts and use it
in the V2 client. A caller-supplied key is validated before credentials, timers
and fetch, and an invalid value is rejected as VALIDATION_REJECTED /
IDEMPOTENCY_KEY_INVALID rather than trimmed, regenerated or dropped, so a keyed
command can no longer replay while sending no key.
N-07: bound the legacy credential wait by the existing attempt controller,
which already carries the total deadline and the caller signal, so a
non-cooperative owner cannot hold the request open and no extra timer is
introduced. The owner receives the operation context, and the failure follows
ownership: deadline to REQUEST_TIMEOUT, caller to REQUEST_ABORTED, and only a
genuine rejection to AUTH_INTEGRATION_FAILURE. None of these paths fetch.
N-08: readBoundedJson delegates to the common bounded reader, so cancel and
releaseLock throws stay isolated inside the closed result, and the V2
content-type mismatch now cancels the response body.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
N-05: the conditional-validator key was a colon join over components that may
themselves contain colons, so two distinct valid bindings could collide and one
definition's ETag could be prepared for another. The key is now a validated,
byte-bounded fixed tuple encoded with JSON.stringify.
N-09: capture localStorage exactly once and compare StorageEvent.storageArea
against that object identity, so a pulse from sessionStorage or any other area
is rejected instead of matching on key and value alone. The pulse key is
registered in the storage registry as CACHE_INVALIDATION_PULSE.
N-10: race loadPage against the caller signal and re-check before observing a
page, so a non-cooperative loader can neither hold loadAll forever nor have a
post-abort completion accumulated into a successful result.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
STO-03: reject at composition any policy that enables Vary variants while
stripping vary from the stored response allowlist, since every stored variant
would collide on the same cache key.
STO-04: extract one verifyReleaseCandidate authority shared by the stage fast
path and activation. A matching release marker is a claim, not evidence, so a
restage now re-verifies each entry, deletes only the owned candidate on a
mismatch and refetches. Abort or an unreadable candidate is never stage success
and never moves the active pointer.
STO-05: split the availability guard. Staging keeps the fetcher requirement
with ONLINE_ONLY recovery; activation, rollback and cleanup need only cache
storage and the mutation lock, so an offline rollback or quota-recovery cleanup
is no longer reported UNSUPPORTED.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Replace the boolean browser-managed target validator with
resolveBrowserManagedTarget, which returns the parsed canonical absolute URL,
and hand that exact value to the host. Previously the raw href was passed on,
so a relative target was re-resolved against document.baseURI and a hostile
<base> could send the navigation to an origin the policy never approved.
STO-08 stays UNVERIFIED: the capability spec does not exercise the system
picker, so the receiver-binding hypothesis is neither reproduced nor refuted
and no source change was made for it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Repair the compensating half of the OPFS put saga.
The coordinator now owns a single abortPreparedPut() driven by a
composition-owned bounded signal instead of the caller's already aborted one,
and the worker client no longer issues a duplicate fire-and-forget abort.
Journal rows and budget reservations are released only after the physical
effect is confirmed CLEANED or ALREADY_CLEAN; a timeout, malformed response or
EFFECT_UNKNOWN keeps PREPARING/FILES_READY and returns OBJECT_RECONCILE.
New writes carry a transaction-unique physicalGenerationId through the staging
receipt, manifest path and prepared object, so a late compensation deletes only
its own transaction's directory even when a newer transaction legitimately
reuses the same logical generation. v1 paths, receipts and prepared objects stay
readable through the rollback window.
Abort and cleanup hold the origin mutation lease through physical deletion and
staging removal. A transaction that never reached staging returns ALREADY_CLEAN
without waiting for the lease, which would otherwise deadlock against the BEGIN
it is cancelling.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Give the best-effort telemetry adapter a terminal ACTIVE/DISPOSED lifecycle.
dispose() now removes the pagehide listener, clears the queue, invalidates
scheduled callback generations and aborts the in-flight sink; emit after
dispose is a no-op and a sink that ignores the abort cannot reschedule or
update post-dispose state. flush() joins the active delivery instead of
resolving early, and runtime infrastructure teardown disposes telemetry first.
Telemetry and diagnostics capacities are validated at construction against a
documented ceiling, so NaN or Infinity can no longer disable eviction.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Separate per-attempt physical state from the logical execution history. The
executor now keeps one monotonic certainty accumulator joined through
joinMutationEffectCertainty, records MAYBE_APPLIED at dispatch, and reads the
accumulator from every retry-loop fence, final-invariant, cancellation and
timeout return.
A retry-time scope fence landing between the loop-entry check and the
pre-dispatch invariant can no longer downgrade an already dispatched command to
NOT_STARTED.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Install the REST auth profile registry once at composition and make it the
single transport authority for V3. Contract composition now rejects an
unregistered authProfileId, so the executor never resolves a profile at
runtime.
The credential collaborator contributes proof headers only: Fetch credentials
come from the resolved profile, transport-owned and forbidden headers are
rejected, headers outside the profile's allowed set are rejected, and a missing
required header fails closed as AUTH_INTEGRATION_FAILURE with zero fetch calls.
The final invariant re-proves credentials mode and the exact header sets.
Demo mode satisfies the strict bearer profile with a fixed non-secret marker
instead of weakening REFERENCE_EXTERNAL_BEARER. Credential owners now receive
the operation lifetime through AuthOperationContext.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Project one typed HttpExecutionObservation per logical V3 execution through a
closed composition-root projector: only registered diagnostic context keys and
bucketed values reach the sinks, and terminal non-abort failures now emit
exactly one api.request.failed telemetry event. Caller cancellation and scope
fencing record a diagnostic but never a failure event.
routeId becomes a required input at the installed operation-executor boundary
so the feature gateway's low-cardinality route identity survives to the sink.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>