feat(maven): sidecar cold-parent fill (S14 plan: 16/19)

Summary

Step 16 of the Maven remote plan: the cold-parent arm of the checksum-sidecar path, replacing Step 12's fillRemoteSidecar stub. A sidecar GET whose parent is uncached resolves the parent by the same layout rules, runs the parent fill with the body draining to cache only (the client asked for a digest, not the file), then synthesizes from the committed row through the shared digest-selection table. The parent fill enters single-flight under the parent's path key, so a sidecar arriving mid-fill coalesces behind the leader with exactly one upstream GET.

Carries the operator-approved freshness-symmetric amendment (recorded in the plan's Step 16 Amendment and mirrored in S14): serveRemoteSidecar now reads through the same freshness-aware RemoteCacheStore lookup as the parent arms. A stale parent revalidates under the parent's path key, conditional on the stored upstream ETag; a 304 keeps the row's digests, a transport failure with a stale row serves that row's digest degraded, and snapshot_metadata_always_revalidate makes version-level snapshot metadata ForceStale on every sidecar request. The now-unused Deps.RemoteCache seam is removed; the serve arm answers the fill's 501 on a partially wired handler.

Governing ADRs

  • ADR-007 — synthesis reads the compute-at-fill digest columns; freshness and conditional revalidation run through upstream_checked_at, the validity windows, and upstream_etag, exactly as the ADR decides. No schema change in this MR.
  • ADR-009 — no route, verb, or payload-contract change; the problem-details error surface is unchanged.
  • ADR-023 — flat internal/format/maven package; format → datastore/remote import direction only; no database/sql.
  • Checked and not engaged: ADR-001, 002, 005, 008, 010, 012, 022.

Per Mirror freshness and internal ADRs, the local mirror is not the authority; no deviation is introduced here.

Spec coverage

Spec: docs/specs/S14-maven-remote.md

Specs live under docs/specs/; acceptance criteria, error cases, and security considerations are the source of truth for what the tests assert (docs/dev/go-testing.md).

Acceptance criteria (Sidecar synthesis)

# Criterion Tests
AC-1 All four extensions TestServeRemoteSidecar_AllFourExtensions, TestRemoteSidecarCold_AllFourExtensions_SynthesizeFromFilledParent
AC-2 FIPS short-circuit TestServeRemoteSidecar_FIPSShortCircuit, TestServeRemoteSidecar_FIPSGate_DirectCall_422, TestServeRemoteSidecar_FIPSOnNonMD5StillSynthesizes
AC-3 No upstream sidecar fetch, any cache state assertNoUpstreamSidecarPath helper invoked by all 10 cold-fill and 5 revalidate cases; TestRemoteSidecarCold_ArtifactParent_FillsThenSynthesizes (sole upstream GET targets the parent path), TestRemoteSidecarRevalidate_Stale304_KeepsRowDigests (zero upstream calls on the follow-up request); the warm suite wires no upstream client at all
AC-4 Cold parent filled, then synthesized TestRemoteSidecarCold_ArtifactParent_FillsThenSynthesizes, TestRemoteSidecarCold_MetadataParent_FillsThenSynthesizes
AC-5 Mid-fill sidecar coalesces TestRemoteSidecarCold_MidFillSidecarCoalesces, TestRemoteSidecarCold_TwoSidecarsShareOneParentFill
AC-6 Stale parent revalidates, then synthesizes TestRemoteSidecarRevalidate_Stale304_KeepsRowDigests, TestRemoteSidecarRevalidate_Stale200_SynthesizesNewDigest, TestRemoteSidecarRevalidate_StaleNoEtag_RefetchesFull, TestRemoteSidecarRevalidate_StaleTransportFailure_ServesCachedDigest, TestRemoteSidecarRevalidate_ForceStaleSnapshotMetadata_RevalidatesEveryRequest
AC-7 No counters TestServeRemoteSidecar_NeverBumpsCounters
AC-8 Sidecar conditional request TestServeRemoteSidecar_ConditionalRequest304, TestServeRemoteSidecar_IfNoneMatchAcrossFieldLines304, TestServeRemoteSidecar_NonMatchingIfNoneMatch200
AC-9 HEAD on a sidecar warm-suite HEAD subtests (TestServeRemoteSidecar_AllFourExtensions), TestRemoteSidecarCold_HeadFillsThenReturnsSynthesisHeaders
AC-10 Sidecar cache-generation consistency TestServeRemoteSidecar_CacheGenerationConsistency_ArtifactParent, TestServeRemoteSidecar_CacheGenerationConsistency_MetadataParent, revalidate suite digest stability

Error mapping

# Rule Tests
E-1 Upstream 404 propagates TestRemoteSidecarCold_ParentUpstream404_Propagates (404, not_found, no cache row)
E-2 Transport failure with cache: serve cached copy TestRemoteSidecarRevalidate_StaleTransportFailure_ServesCachedDigest (degraded stale-row digest, no upstream reach, no new row)
E-3 Transport failure without cache: 503 upstream_unavailable + Retry-After TestRemoteSidecarCold_ParentTransportFailure_503, TestRemoteSidecarCold_ParentDrainFailure_Maps503
E-4 Upstream 5xx not a fallback trigger TestRemoteSidecarCold_ParentUpstream5xx_Mapped (500 → internal_server_error without Retry-After; 503 → upstream_unavailable with Retry-After; status propagated verbatim)
E-5 Path traversal rejected Parent-arm parse, unchanged in this MR; covered by the artifact/metadata suites (Steps 14/15)
E-6 Outbound segment safety Parent-arm URL construction, unchanged in this MR; covered by the Steps 14/15 suites
E-7 Upstream non-404 4xx rejected Shared MapFetchOutcome branch reused unchanged; covered by the Step 13/14 mapping suites — no sidecar-specific branch
E-8 Upstream body exceeds cap → transport-failure treatment S13 fetch-owned; mid-transfer failure classification asserted by TestRemoteSidecarCold_ParentDrainFailure_Maps503

E-5–E-8 route through parent-arm and S13 code this MR does not modify; the rows are listed so the sidecar arm's reuse is explicit, per go-testing.md's dependency-owned-rows rule.

Security considerations

Concern Tests
Checksum trust model TestServeRemoteSidecar_CacheGenerationConsistency_ArtifactParent, TestServeRemoteSidecar_CacheGenerationConsistency_MetadataParent, TestServeRemoteSidecar_HostedAndRemoteSameDigests_ByteIdentical, revalidate suite
FIPS TestServeRemoteSidecar_FIPSShortCircuit, TestServeRemoteSidecar_FIPSGate_DirectCall_422, TestServeRemoteSidecar_FIPSOnNonMD5StillSynthesizes
Error-payload hygiene S13-owned (no blanket header copy; this arm asserts status/code/Retry-After only — TestRemoteSidecarCold_ParentUpstream5xx_Mapped)
Credential hygiene / at rest, SSRF, redirect stripping S13/S17-owned. Not tested in this MR.

docs/testing/ (Guardrail 12)

No e2e scenario is added or affected: the Maven e2e catalog (docs/testing/e2e/maven.md) covers hosted local repositories only, and no catalog row asserts remote sidecar behavior. The remote sidecar path is pinned by the two new integration suites instead (see Spec coverage).

Diff footprint (Guardrail 18)

+1301 −304 total; the plan carries this work as one step (Step 16), and the diff is one request-path behavior with its coverage. Splitting would separate a single fill→revalidate→synthesize flow from the tests that pin it. By file group:

  • Implementation: remote_sidecar.go (+344 −23), remote_stub.go (+20 −37), handler.go (+21 −36), remote_store.go (+7 −8), wire_maven.go (+13 −23) — +405 −127
  • Integration and unit tests: remote_sidecar_cold_integration_test.go (+499), remote_sidecar_revalidate_integration_test.go (+211), remote_sidecar_integration_test.go (+136 −20), remote_sidecar_test.go (+9 −148), remote_artifact_integration_test.go (+8 −1), handler_test.go (+1 −1) — +864 −170
  • Docs: docs/specs/S14-maven-remote.md (+10 −7), docs/plans/2026-07-27-maven-remote.md (+22) — +32 −7

Tests dominate the diff (two-thirds of the added lines); the production change is one function family in remote_sidecar.go.

Related to #286 (closed)

Edited by Moaz Khalifa

Merge request reports

Loading
Loading