feat(maven): the winner-path read's plumbing (S30 plan: 17/25)
Summary
The plumbing Step 17's winner-path read assembles over, landed ahead of the handler so the read MR reviews on its own:
- The batched remote lookup projects the checksum columns. Both
maven_virtual_remote_lookup.gostatements projectmaven_remote_files.sha1and.md5besideblob_sha256, and the verdict carries both. A NULLmd5maps to a nil slice — the shape the MD5-under-FIPS rule will read. - The hosted primary serve extracts its shared shape (
download.go):serveResolvedPrimarycarries the access bump and theIf-None-Matchconditional out ofservePrimaryDownload's wrapper, behavior-preserving; the hosted path's tests pin it unchanged. - The usage stream gains the two virtual-only ADR-012 dimensions:
AnchoredEventgainsCacheHitandUpstreamType,TrackAnchoredcopies both onto the emittedAttrs, andEventVirtualCacheMiss(artifact_registry_virtual_cache_miss) joins the catalog (internal/usagedata/events.go,docs/dev/usage-data.md).
No route behavior changes: the dispatch slot keeps the interim 501 until the stacked handler MR.
Scope
Layer 1 of 2 of Step 17 of the S30 plan (### Step 17). The handler layer is !3003 (merged), stacked on this branch. !3003 (merged) owns two follow-throughs from this layer:
- the
VirtualPositionfields, the facade's drain-fallback call, and the revalidate-flag binding test — counted here by an earlier description revision, all three are !3003 (merged)'s diff; - dropping the three not yet emitted marks in
docs/dev/usage-data.md(the intro line, thecache_hitcell, thevirtual_cache_missrow) in the same MR as its emitter, so the qualifier cannot outlive this stack; - moving the miss booking to the serve outcome, per the semantics call the ADR-012/S23 owner made on thread
10e9a52c(confirmed in review by the MR author, 2026-09-28): a hosted winner and a cold remote fill book the miss; a warm copy's hit books the pull alone withcache_hit=true— npm/OCI's one-verdict-per-read pattern (remote_cache_event.go). !3003 (merged)'s miss booking point changes accordingly.
Plan and spec amendments
None — this layer touches no plan or spec text.
Spec coverage
Spec: docs/specs/S30-maven-virtual.md. Plan: docs/plans/2026-09-10-s30-maven-virtual.md — the Files entry's download and usagedata rows; the checksum projection modifies Step 6's maven_virtual_remote_lookup.go.
Acceptance criteria
Behavior arrives with the handler layer; this layer's criteria are structural:
| Criterion | Test |
|---|---|
| Both batched statements project sha1/md5; NULL md5 → nil slice | maven_virtual_remote_lookup_integration_test.go (round-trip subtests); the statement render check runs in maven_virtual_remote_lookup_test.go |
servePrimaryGet reports the arm it served |
TestPrimaryGet_ReportsTheServeArm (redirect, stream, rejected-URL) |
TrackAnchored projects the new dimensions |
TestTracker_TrackAnchored_ProjectsCacheHitAndUpstreamType |
| The miss event joins the catalog | events_test.go, events_internal_test.go catalog assertions, and the microCatalog row |
| The hosted serve is unchanged by the extraction | the existing download_test.go suite, unmodified |
Governing ADRs
- ADR-007 — no schema change; existing columns projected.
- ADR-012 — the two dimensions and the miss event join the stream and the operator catalog; no emitter until the handler layer.
Tests
go build ./..., go test ./..., golangci-lint run, and the comment-cap gate pass on this layer's own tree (verified standalone, with the handler layer's files absent). golangci-lint run --build-tags=integration ./internal/datastore/... under the guardrail's flags (--max-same-issues=0 --max-issues-per-linter=0 --uniq-by-line=false) reports the package's seed-helper baseline plus 9 new contextcheck hits of the same kind at maven_virtual_remote_lookup_integration_test.go:295-311 — the md5 subtest calls the same context-less seed helpers; threading the contexts is the baseline's cleanup, not this diff's.
User documentation
No docs/user/ surface changes: no route behavior changes on this layer, and docs/user/repositories/_index.md already scopes virtual repositories as in development.
Related to #289 (closed)