feat(managementapi): container virtual upstream association routes part 11/15 (S17 Phase 6 plan: 42/42)
Why
A container virtual repository is creatable, and part 9 refuses deleting its upstreams, but nothing can build its upstream list: docker and oci are the two contract formats with no association routes. This part registers their five routes each over the one container junction store. Deps.ContainerVirtualUpstreams is a VirtualUpstreamStore, so its assignment in wire_management.go is the store's compile-time interface check, the one promised on feat(datastore): the container upstream write e... (!2843 - merged) • Hayley Swimelar • 19.5.
The user docs, the audit-record row, the run recipe, and both association sidecars said docker and oci accept no upstreams, so they move with the routes. The e2e catalog scenarios for these routes, e2e.docker.upstreams.list-verdicts and e2e.oci.upstreams.list-verdicts included, land in part 14, test(managementapi): cover the container upstre... (!2859 - merged) • Hayley Swimelar • 19.5.
Main's 8b15d1dc serves include_permissions on every format's upstream list, so the docker and oci lists answer per-row verdicts from this part on, and this part's tests pin them per segment. Part 12 will drop its test asserting the container lists ignore the parameter, test(managementapi): cover the container upstre... (!2857 - merged) • Hayley Swimelar • 19.5.
Depends on: part 9's delete guard, deployed before this part's associate route serves traffic, feat(datastore): refuse deleting a container vi... (!2854 - merged) • Hayley Swimelar • 19.5.
What
The comment on the datastore test TestContainerVirtualUpstreamStore_Dissociate_ReportsTheVacatedRow moves to the present tense ("Its one caller audits from this value") because this part wires that caller, the dissociate handler.
The rebase resolved the run recipe against main's S30 step 15 and S32 step 14: this part's "All four formats are served" paragraph replaces the S30 "matches no route" one, the smoke record cites the 2026-09-25 run at 80 passed, and the seed-container-virtual intro and driver.sh's seed comment now call the seed a direct SQL insert rather than a call to the association routes.
The stack
Fifteen parts, each stacked on the last, each green on its own, reviewed in this order. Each part stays under 700 changed lines, counted as insertions plus deletions against the part it targets.
The delete guard in part 9 must be deployed before part 11's associate route serves traffic. Merge order alone does not close the window: within one rollout, a pod without the guard can answer 202 on destructive=true for an upstream that a new pod has just associated, leaving a tombstone the reaper refuses.
Related to #314 (closed)