feat(maven): the winner-path virtual metric families (S30 plan: 19a/25)

Summary

Step 19a's winner-path metric families, layer 1 of 2: the four collectors — maven_virtual_resolutions_total (maven_virtual_operation, resolution_outcome), maven_virtual_probes_total (maven_virtual_operation), maven_virtual_upstream_requests_total (leg), and maven_virtual_positions_consulted (count-shaped buckets) — with the label and value vocabularies they audit against, the cardinality label budget, and the catalog rows.

  • Bounded low-cardinality labels only. No namespace id, no coordinate, no upstream hostname on a label; the three closed sets (3 operations, 3 legs, 17 outcomes) are pinned with the constants, and the derived label-set tests hold against the budget rows.
  • positions_consulted counts every position a resolution consulted — every position it walked, the ones the eligibility gate excluded and the ones health marked unhealthy included — so zero means an empty upstream list rather than an all-excluded one, and a consistently wide list is visible however its gates answer. Count-shaped buckets {0, 1, 2, 5, 10, 20} mirror pageSizeBuckets (internal/format/oci/metrics.go) and driftRowBuckets (internal/accounting/metrics.go), because the position CHECK caps the list at 20.
  • leg is the second cross-slice union seam, shared bare with npm's upstream-request counter, revalidate pinned ahead of its producer (the merge path). probes_total is not optional: the spec's follow-up accepts the cold-cache probe-amplification risk on the condition that this counter exists.

The wide event, its recorder, wrappers, and install sites land in the event layer stacked on this MR.

Scope

Layer 1 of 2 of Step 19a of the S30 plan (### Step 19a: winner-path observability), stacked on !3003 (merged) until that MR merges; this MR targets its branch.

This MR also carries the test-author agent's comment-caps check step (.claude/agents/test-author.md): the suites this step ships are the first that check gates.

Depends on

  • !3003 (merged) — Step 17's handler; merge it first, then this MR, then the event layer.

Plan and spec amendments

Per docs/dev/conventions.md (### Review-driven plan and spec corrections), this MR carries the corrections its own review proved:

  • Step 19a's Files entry re-records the install sites the review found: the recording decorators wire in handler.go's composeMavenVirtualResolution, virtual_read.go, and virtual_assembly.go — not cmd/artifact-registry/wire_maven.go — and virtual_metrics.md (Create) carries the package page. The positions_consulted range reads 0-to-20 (0 for an empty list).
  • S30's metric row for maven_virtual_upstream_requests_total clarifies the leg vocabulary: probe and fetch are the winner path's Phase 2 and Phase 3 legs, and a collection position books fetch/revalidate, revalidate pinned ahead of the merge path that first books it. The wide event's field-name correction rides the event layer.

Size

~324 reviewable source lines (+270 test, +113 docs) against the !3003 (merged) tip — under the 500-LOC guide.

Spec coverage

Spec: docs/specs/S30-maven-virtual.md (### Observability). Plan: docs/plans/2026-09-10-s30-maven-virtual.md, ### Step 19a.

Acceptance criteria

Criterion Test
Families carry bounded low-cardinality labels only; closed sets bounded; fail-closed drop TestMavenVirtualFamilies_CarryTheirLabelSets, TestMavenVirtualVocabularies_AreClosedAndBounded, TestBookMavenVirtualResolveMetrics_DropsValuesOutsideTheClosedSets
Audit passes with every declared value emitted TestRegisterMetrics_PassesCardinalityAuditWithTheVirtualFamilies
Consulted buckets count-shaped TestMavenVirtualPositionBuckets_AreCountShaped
Derived label-set tests internal/metrics/cardinality_virtual_test.go

ADR coverage

ADR-001 (namespace id rides the event as a field, never a label), ADR-004 (the 20-upstream cap consumed, no slice-local limiter), ADR-012 (operational metrics sit outside the usage-data decision) — checked against the plan, spec, and cited ADRs by the /validate-step pass; no conflicts.

Tests

go build ./..., go test -short ./internal/format/maven/... ./internal/metrics/..., golangci-lint run, and the comment-caps gate pass at this layer standalone, at the pre-commit hook's full go test -short ./... scope.

User documentation

No docs/user/ surface changes: the families are operator-facing and catalogued in docs/dev/observability.md; the end-of-plan docs pass owns the customer-facing write-up.

No e2e scenario added: the virtual read's end-to-end scenarios are Step 20's, the step that mounts the harness the catalog's virtual rows need.

Related to #289 (closed)

Merge request reports

Loading
Loading