feat(oci): observe container virtual repositories (S32 plan: 17/20)

Summary

This MR adds the observability family of container virtual repositories (S32 Step 17).

  • A virtual manifest or blob read writes one oci_virtual_resolve wide event. It carries the resolution_outcome, the winner fields, and one positions[] entry per position the read walked.
  • The virtual probe and the winner's Fetch each write one oci_remote_proxy record, so every fetch sample has a record behind it. The health probe writes none.
  • Three metric families count resolutions, probes, and positions consulted. positions_consulted counts the list width. A read whose upstream list fails to load books no width and omits upstream_count.
  • A virtual read books the winner's fetch and fill on the S16 instruments. A coalesced burst books one fetch and one fill.
  • The token cache reports its occupancy and its over-limit refusals.
  • A manifest GET delivered in full emits ADR-012's artifact_pulled. A hosted winner or a cold remote fill emits virtual_cache_miss. Blob reads emit neither.
  • A failed drain's record names the winner. A committed body that stops short takes its fault as the resolution_outcome. A write that this service's own write deadline cut records infrastructure_error, not cancelled.
  • docs/dev/observability.md and docs/dev/usage-data.md catalog each change.

Merge order

  • Merge after !3019 (merged), or together with it. !3019 (merged) amends S32 to the label names in this diff (oci_virtual_operation, resolution_outcome), the token-cache families, and the positions[] omission rules. Until then the spec on main disagrees with the code.
  • Merge after !3014 (merged) (S30 Step 19a). docs/dev/observability.md:127 says the OCI histogram counts positions as the Maven family counts them, and only !3014 (merged) registers that family. The cross-format reference is deliberate, so npm can follow the same pattern.
  • !3014 (merged) and this MR both add a resolution_outcome entry to internal/metrics/cardinality.go. The second MR to rebase gets a text conflict. That MR merges one union of 22 values (Maven adds leader_canceled), sets the budget to 22, and updates the union test.

Plan and spec amendments

  • !2941 (merged) (rbatnag/s32-plan-batch-2026-09-24) amends Step 17's text. It names the six shared-seam files outside the step's Files entry. It is the batch amendment MR and also serves Step 18, so this MR does not wait for it.
  • !3019 (merged) amends the S32 label names and the positions_consulted row, as above.

Governing ADRs

I read each ADR against the diff. scripts/adr-freshness.sh exited 0.

  • ADR-012: the pull events fire only after a manifest is delivered in full. Proxy delivery is forced, so a disconnect after the header commit emits nothing.
  • ADR-005: no new metric bypasses the shared storage path that carries the delivery label.
  • ADR-004: the positions_consulted buckets end at the 20-source limit.
  • ADR-009: the list-route 404s are unchanged. Handbook !20929 covers them.
  • ADR-007: the diff has no schema change.

Size

The diff is 4,314 LOC: Go source 1,444, tests 2,846, docs 24. The plan scopes the resolve event, the metrics, the usage events, and the catalog as one step. A split would leave each half's catalog and cardinality budgets false. The estimate on !2941 (merged) (~4,090) is stale and needs a refresh.

Testing

  • docs/testing/: no scenario is added. Step 18 owns the e2e catalog.
  • docs/user/: no user docs change. The new surfaces are operator-only.

Related to #291 (closed)

🤖 Generated with Claude Code

Edited by Radamanthus Batnag

Merge request reports

Loading
Loading