docs(specs): scope S16's cache-miss delivery-mode carve-out to a streamed miss

Why

S16's blob-proxy section says "A cache miss is always proxy delivery", covering all three miss shapes. Only one of them can be. A streamed full GET tees the body from the upstream as it arrives, so there is no stored object to pre-sign. The other two answer after the fill commits, so by the time either writes a response there is a stored object and the carve-out has nothing to rest on.

Split out of the Step 15 implementation branch so the contract change is approved on its own, per the plan's rule that merging the spec MR is the approval signal. !1894 (merged) stacks on this branch; see below.

!1894 (merged) stacks on this branch and implements the narrowed carve-out. This MR merging is the approval signal for it, per the plan. !1896 (merged) is independent of both and targets main directly.

What

The delivery-mode carve-out narrows to a streamed miss. Each of the three miss shapes now reads explicitly:

Miss shape Delivery
Streamed full GET Mode-independent, for the reason it always was: nothing stored to point at.
Cold HEAD Never a 307 — but because a HEAD is answered from the row in both modes, not because it is a miss.
Ranged miss Follows the configured mode like any cached ranged read: 206 under proxy, 307 under redirect with the storage backend serving the range.

The ranged miss is the case the old wording got wrong, and it is reachable: the arm fetches the whole blob with no Range forwarded, then serves the range out of local storage. The delivery-mode acceptance criterion carried the same over-broad claim and is corrected to match.

The download-signal criterion becomes mode-scoped. "A cache miss records exactly one download, and a miss that serves nothing records none" turns on an unsatisfiable ranged miss. Under proxy delivery that fills and answers 416, serving no bytes and recording nothing. Under redirect delivery the same request answers 307 and records one, because issuing a redirect is the only serve the arm can observe and satisfiability is the storage backend's to evaluate. Both are correct under their own mode, so a suite asserting the proxy-mode outcome has to pin the mode to be asserting anything.

Three coalescing criteria are gated on #752. The blob arm composes S13's Fetcher directly and constructs no single-flight layer, so there is no coalescing to observe and no follower to answer. The gate says so, and says explicitly not to satisfy it by building a per-request single-flight instance: one coalesces nothing while compiling and passing, so a green assertion would establish the opposite of the rule. Recorded alongside it: the same deferral leaves the fill path with no per-repository in-flight bound, because resolution.max_concurrent_fills_per_repo is enforced inside that layer and nowhere else.

The already-gated follower criterion now names both blockers rather than one. Without #752 there is no follower at all; #320 (closed) is what decides where an early-joining follower's answer comes from.

Verification

Every claim was checked against the implementation before it was written: redirectToCachedBlob returns true, so a 307 does record a download, and getCachedBlob opens the blob before parsing Range, so an unsatisfiable range redirects under redirect delivery. No drift found between the corrected text and the code.

Related to #288

🤖 Generated with Claude Code

Edited by Sylvia Shen

Merge request reports

Loading
Loading