docs(specs): S32's resolution decision is bounded by resolve_timeout
Summary
S32 rules that a container virtual read has no aggregate deadline of its own,
and that the bound is the client's own deadline. Nothing on the server side holds
that bound: Go's WriteTimeout does not cancel the handler, internal/format/oci
mounts no http.TimeoutHandler, and both standalone read arms replace the
connection's write deadline before the fetch, so server.timeouts.write is not a
ceiling on the fan-out. Past the client's own patience its connection is gone while
the fan-out keeps issuing upstream requests and holding a database connection, and
a retrying client starts another.
S13 has since added virtual_repositories.resolution.resolve_timeout (spec
!2249 (merged), implementation #1139 (closed), armed inside the resolver, 90s default sized to
cover container's Phase 2 worst case). This amendment records that S32 consumes
it and reverses the two sentences that said the opposite. The third stands: the
key belongs on S13's shared block, not on this slice, and S32 still adds no proto
field and no YAML key.
Two consequences were unrecorded, and both are here:
- The Error Cases table gains a fifth row. The table calls itself "exhaustive
over all three" S13 error classes and has none for a resolution abandoned on an
aggregate deadline. It is a fifth row and not a fifth
Retry-Aftervalue: S13 puts the expiry in the coalescing class, so the503carriesRetry-After: 1rather than a handler-computed value. resolution_outcomegainsbudget_expired. The set had no value for the verdict and folded a collection-mode expiry underunavailable, which merges a self-inflicted timeout with a real upstream outage. The two clear on opposite levers:resolve_timeoutorprobe_concurrencyfor the first, an upstream's own recovery for the second. The name is merged S31's for the same verdict, adopted rather than coined, because a scraped label value cannot be renamed once it ships.
Spec-only. No code, no configuration schema, no proto field.
Governing ADRs
- ADR-004 (data and application limits): the 20-upstream cap is what makes the unbounded sweep reachable, and the new passage cites it as the worst-case input. Conforms; no limit is changed.
- ADR-009 (API design): fixes routes, URL uniformity, and the virtual
tags/list/referrers404 NAME_UNKNOWN. It fixes no read-failure envelope taxonomy, and this change adds no route, method, or client-facing URL. The new row reusesUNAVAILABLE, already in the envelope vocabulary. Conforms.
Mirror freshness checked with scripts/adr-freshness.sh: it reports STALE on one
unsynced commit touching 012_usage_data_collection.md only, which neither ADR
above is affected by.
Testing
None: documentation only. The behavior this records is already on main and
already covered by #1139 (closed)'s suite, internal/virtual/resolve_budget_test.go
(budget expiry in Phase 1 and Phase 2, the cached-copy serve, the warm-serve and
cold-fill cases, the single-flight follower, and the client-disconnect companions
that keep ResolveBudgetExpired and ResolverCancelled distinct).
Notes for the reviewer
- This is the Step 11 half of the single S32 amendment the container-virtual
plan books under
## Dependencies. !2830 (merged) carries the Step 10a half (the data-error cause list, three to six). The two halves touch disjoint passages and can merge in either order. - One S31 sentence is corrected here too, on reviewer request, after this MR
originally left it to S31's owner.
docs/specs/S31-npm-virtual.md:1242said "Neither S30 nor S32 references it today" ofresolve_timeout. S30 already referenced it (its budget row is written against the key, atdocs/specs/S30-maven-virtual.md:546), so the sentence was half stale before this MR and fully stale after. b977bcc2 replaces it and keeps the placement argument it supported: the key sits on S13's block because the bound is a property of the resolution, and the contrast with the two keys S31 does propose (negative_cache_ttl,position_wait_timeout) survives.
Related to #1139 (closed)