feat(virtual): decision core and upstreams (S13 Step 12, part 1/2)

📚 Stacked MRs

This change is split into 2 stacked MRs. Each part targets the previous one (part 1 targets main); review and merge proceed bottom-up. Part 2 carries a documented review-size waiver — see its description.

📦 What this MR does

Part 1 of 2 of S13 virtual/remote foundation — Step 12: virtual resolution: the pure, network-free half of the virtual resolver — the slice the plan's step-12 "Size watch" note names as the natural first cut.

  • The decision-array vocabulary from Virtual resolution: the per-position decision (undecided, failed, succeeded), the winner verdict (first success preceded only by failures), the Phase 2 probe set, the Lookup-result and Probe-outcome decision mappings, and the conditional-request validator (If-None-Match only for a revalidate-conditional snapshot).
  • The ResolveResult verdict shape: serve-from-cache vs stream plus the winning position. The resolver performs no blob I/O; the caller dispatches on the verdict.
  • Upstream positions: UpstreamSpec with validating NewHostedUpstream / NewRemoteUpstream constructors and hosted/remote kind derivation from the remote handle.
  • The winner computation is pinned three ways: the spec worked examples, a rapid confluence property (once the verdict is determined, no resolution order of the remaining positions changes it), and a differential against a naive reference implementation.
  • Extends the ADR 023 no-reverse-dependency guard to internal/virtual (.golangci.yaml).
  • Adds the canonical Phase 2/3 fakes (FakeProber, FakeFetcher) to internal/remote/remotetest, each panicking when nothing is configured so a test that forgets to script one fails loudly instead of reading as an upstream failure. The constructor tests here use them, and part 2's resolver suite builds on them.

⚠️ Review-size waiver

This MR is ~1,100 reviewable LoC, above the 500 guideline in docs/dev/development-model.md. It ships whole deliberately:

  • The slice is the one the plan's step-12 "Size watch" note sanctions: the pure decision-array core, with no network.
  • The decision vocabulary, the verdict shape, and the upstream-position constructors are one dependency cluster, and their tests — the spec worked examples, the rapid confluence property, the naive-reference differential, and the constructor guards — must land with the code they pin.
  • Splitting the cluster further would produce parts that strand tests from the functions they pin, which is strictly worse for review than the size.
  • The review round grew it further: the canonical prober/fetcher fakes and their guard tests moved into internal/remote/remotetest rather than being minted locally, which is the placement the seam's other consumers will share.

Spec coverage

Requirement Tests
Virtual resolution decision-array worked examples TestWinner_SpecWorkedExamples, TestProbeSet
Lookup and probe outcomes map to positional decisions; hosted misses fail, remote misses stay probe-eligible TestDecisionForLookup, TestDecisionForProbe
Conditional HEAD only for a stale cache row with a stored validator TestConditionalETag
Winner stability under any probe-completion order TestWinner_StableOnceDetermined, TestWinner_MatchesNaiveReference (property-based)
Upstream-set construction rejects nil collaborators and zero IDs TestNewHostedUpstream, TestNewRemoteUpstream, TestUpstreamKind_String, TestResolveOutcome_String

🔍 Notes for reviewers

  • Plan drift, sanctioned: the plan's step-12 Files list names only resolve.go/resolve_test.go; the step's own "Size watch" note directs this exact split ("the pure decision-array winner computation and eligibility filtering (no network) is the natural first slice"), and the .golangci.yaml guard extension was ratified to ride in the implementing MR.
  • No e2e scenario catalog update: internal/virtual is an internal library with no user-reachable surface until the virtual repository HTTP handlers (S30–S32) compose it.

Related to #333 (closed)

:bricks: Stacked MRs (review/merge bottom-up)

Edited by David Fernandez

Merge request reports

Loading
Loading