test(oci): drive a docker pull through a hosted-and-remote virtual repository (S32 plan: 18/20)

What does this MR do?

Adds the hermetic end-to-end harness for a container virtual repository: a docker pull-shaped sequence (tag, index, child manifest, config and layers) driven through the dispatcher against a virtual repository that lists one hosted and one remote upstream. It also adds the e2e catalog sections and the run recipe's virtual pull journey. This is Step 18 of the S32 container virtual plan.

  • internal/format/oci/virtual_e2e_test.go lists the hosted upstream at position 1 above the remote at position 2, with an in-process fake registry behind the remote. The hosted repository holds the child manifest, its config and the first layer. The remote's registry holds the whole image. A cold pull resolves each leg to its supplier and pins the exact upstream calls per leg. A warm pull issues zero upstream requests and commits no fill. The file carries no build tag and touches no database, so it runs with the unit suite.
  • docs/testing/e2e/oci.md and docs/testing/e2e/docker.md each gain a ## Virtual repositories section. The out-of-scope line for virtual repositories is gone from both.
  • The container association routes shipped with S17 Phase 6 (6e883a40d), so the "Upstream associations stay out of scope." sentence the plan asked for was already false. Both catalogs' scope paragraphs now say those routes ship. Their ## Upstream associations section is !2859 (merged)'s (S17 Phase 6 Step 42), whose rows have that stack's route tests as evidence, so this MR does not add one.
  • driver.sh gains pull-container-virtual under the pull-journey anchor Step 14 seeded, and smoke runs it. To reuse the existing seeding, seed_container_virtual is split into its create and its association, and write_remote_layer is lifted out of cmd_seed_remote. Neither split changes what the existing callers do.

The branch was rebased onto main at 99423bb35 before pushing, to take S30 Step 17's entry in the SKILL.md verification log. Both sides only appended to that log, so the resolution keeps both entries. The branch's per-file contribution is identical before and after the rebase.

Plan and spec amendments

  • Step 18 upstream order. The harness lists hosted first, not the [remote_a, hosted_b] order of S32's "A child manifest resolves on its own" criterion. With a remote listed first, the resolver probes that remote for every by-digest read only the hosted upstream holds, so "the second pull issues no upstream request" cannot hold. Step 15 already covers the child-manifest criterion in the spec's order (TestVirtualManifest_ChildResolvesFromAnotherPosition).
  • S32 warm-digest criterion. That probe contradicts S32's "Warm digest pull short-circuits", which says a digest held by any upstream resolves at Phase 1. The resolver follows S13's Phase 2 instead. The spec amendment is its own MR for the spec author to review: !3028 (merged). Step 18's plan text cites it as a proposal, which stays true whichever MR merges first.
  • Catalog scope. Step 18's Files entry, ### e2e catalog, and the e2e scenario impact paragraph said to narrow the scope sentence to upstream associations. They now say to remove it, and that S17 Phase 6's Step 42 adds the association section.
  • Step 18 recipe anchors and seams. The Files and Shared seams entries now name the pull-journey anchor Step 14 seeded, and record the two seeding-helper splits.
  • Testing Strategy. Step 18 is removed from the integration-tagged list, because its harness has no build tag.
  • Size. ~740 becomes ~1,350, as measured.
  • Status note. The note under the Status table said step MRs never edit it. docs/dev/conventions.md (### Plan status-table updates) says a step MR records its own row, so the note now says that, and this MR records row 18.

Merge order

  • !3014 (merged) merges first. This MR rebases once it lands. Both MRs append an entry after S30 Step 17's in the SKILL.md verification log, so the resolution keeps both entries. !3014 (merged) does not change the smoke count, so 94 stays correct.
  • This MR merges before !2941 (merged). !2941 (merged) rebases onto it. Both fill rows of the S32 Status table, and the resolved table is: 14 !2940 (merged), 16 !2984 (merged), 17 !3031 (merged), 18 !3029 (merged).
  • !2859 (merged) owns the catalogs' ## Upstream associations section. Whichever of the two merges second removes !2859 (merged)'s "Every … client journey … stays out of scope" sentence, because this MR puts the virtual pull journey in scope.

Acceptance coverage

Criterion Evidence
A docker pull-shaped journey across a hosted and a remote upstream succeeds cold and warm TestVirtualProxy_E2EDockerPullSequence (both subtests)
The second pull issues no upstream request TestVirtualProxy_E2EDockerPullSequence: the call snapshot and the fill count are unchanged from the cold pass
Both catalog files carry the new section, and neither lists a virtual client journey as out of scope docs/testing/e2e/oci.md, docs/testing/e2e/docker.md
driver.sh smoke passes at its new count smoke: 94 passed, 0 failed, on the rebased branch (logged in SKILL.md)

Beyond the plan's criteria, the harness also covers:

  • a stale tag confirmed by a conditional Probe (TestVirtualProxy_E2EStaleTagRevalidatesOn304);
  • a token-auth upstream, where the token the Probe exchanged for serves the Fetches after it (TestVirtualProxy_E2ETokenAuthUpstreamExchangesOnce);
  • the write-verb 405s (TestVirtualProxy_E2EWriteVerbsAreRefused);
  • a remote refusing every read, where the hosted absence decides the answer and nothing the upstream sent reaches the client (TestVirtualProxy_E2ERefusingUpstreamLeaksNothing);
  • a winning remote whose Fetch answers 403, relayed as DENIED with nothing the upstream sent reaching the client (TestVirtualProxy_E2EWinnerFetchRefusalLeaksNothing).

Size

The diff is past the 500-line ceiling:

Group Added Removed
Go test (virtual_e2e_test.go) 957 0
Run recipe (driver.sh, SKILL.md) 328 14
e2e catalog (oci.md, docker.md) 60 9
Plan 44 27

Splitting would not help. The step is a test step: the harness is the test, and it drives one journey through fixtures that only make sense together. The recipe and the catalogs describe the same journey, and the plan puts all three in this step.

e2e scenarios and user documentation

  • e2e catalog: updated as described above.
  • User documentation: not affected. This MR changes no customer-visible surface. docs/user/formats/oci.md is changed by !2941 (merged).

Run recipe

.claude/skills/run-artifact-registry/ changes because the recipe gains a virtual pull journey and its 14 smoke checks. The evidence is the smoke re-run above. No /skill-creator benchmark applies: the change adds a journey the recipe drives, not a change to how the skill triggers. The recipe rides with the step, as it did in !3005 (merged) and !2984 (merged), because Step 18's Files entry puts it in this MR.

Related to #291 (closed)

🤖 Generated with Claude Code

Edited by Sylvia Shen

Merge request reports

Loading
Loading