feat(maven): the merge-path wide event (S30 plan: 19b/25)

Summary

Step 19b's wide event, the merge path's half: the maven_virtual_resolve record's merge-path values — merged vs merged_partial, the merge-side positions[] tokens, the dropped-set records, and the fetch/revalidate legs on the upstream-requests counter — emitted from the mounted classes' own defer, over Step 13's collection.

  • The merge path's outcome and tokens. serveMergedMetadata settles merged vs merged_partial and every live position's token; a position the collection settled none for keeps what the collaborator wrappers observed, and an entry omits outcome altogether when neither settled one, never rendering an empty token.
  • The dropped set reaches positions[] on every read. The assembly registers the soft-deleted associations on the read's recorder — the container sibling's reading — so a dropped association lands as a skipped_soft_deleted entry under its stored position column, on no_upstreams reads included.
  • A collection position's document read books its legs. The merge path's per-position fetcher wraps with a leg recorder: a conditional GET books revalidate — the counter's first producer — a full re-fetch fetch, and the merge path issues no probe, which is where a cold-cache merge spike's C×M amplification appears, per the spec's counter row and the operator catalog.
  • A read that ends parked on the shared budget renders only the tail omitted_budget. The admitted prefix settles no token — the omitted-outcome arm — and only the positions past the admitted set book omitted_budget, per the spec's definition.
  • One event per read across both sidecar parent shapes. The package-level sidecar books the merge's own record; a version-level parent's verdict emits from the winner arm's own instrument pair inside serveVirtualWinnerSidecar, and the mounted defer does not book it twice.

Scope

Step 19b of the S30 plan (### Step 19b: merge-path observability), on main since Step 18 (!3081 (merged)) merged. That MR's rework restructured the mounted classes' serve — the response recorder, the streaming sidecar digests, the self-emitting version-level sidecar arm — and this step's instrumentation is adapted onto that shape, with the recovery gate keeping a panicking sidecar parent's record single.

Depends on

  • Step 18 (!3081 (merged)) — merged; this branch is rebased onto main past it.

Plan and spec amendments

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

  • S30's Wide events section mints the omitted-outcome entry — an entry omits outcome when no verdict resolved for it, a merge position the collection settled none for and no recorded observation filled — the sentence the operator catalog already carried, matching the code's json:"outcome,omitempty" rendering.
  • The plan's Step 10c and Step 11b entries route the dropped soft-deleted set through the read's recorder: Step 10c's drop registers the set there, and Step 19b's emitter reads it from there. Both entries named a SkippedSoftDeleted transport field — on the assembled list and on the facade result — that no production reader used once the recorder route landed, so both fields are removed and the drop's record is pinned where the recorder is reachable.
  • S30's gitlab_artifact_registry_maven_virtual_upstream_requests_total row drops the "revalidate is pinned ahead of the merge path that first books it" clause: this step lands that producer, so the spec now agrees with docs/dev/observability.md and internal/format/maven/virtual_metrics.md, which this MR also corrected.
  • The plan's Step 19b entry records the wiring the values ride (the assembly's dropped-set registration and per-position leg booking, the typed merge tokens, the mounted defer emission, the recorder-route contract surfaces), the new merge-path suite as a Create, and the legs acceptance — a collection position's document read books its fetch/revalidate legs on the upstream-requests counter, which no later step lands.

Notes and gaps

  • The acquire arm's rendering follows the spec's definition. A read that ends parked on the shared budget renders only the budget-omitted tail as omitted_budget, its admitted prefix's entries omitting outcome: the acquire can only error once the prefix admitted something, so the all-omitted_budget rendering the branch first carried described a state the arm cannot be in. An amendment defining the acquire arm's tokens differently would change this rendering, and the code would follow it.
  • The plan's size forecast stands. The Step 19b entry's Source: ~100 · Test: ~250 estimate sits at ~¼ of the realized size; no Source: line in this plan's history has been edited post-implementation, so the plan-time forecast stands and this description carries the size justification instead.

Size justification

~548 reviewable lines (448 source, 100 docs) plus 1,048 test, measured against main with git diff --numstat origin/main...HEAD, marginally past the 500-reviewable-LOC guide. The emitter, the leg wrapper, and the mounted-class wiring are one fixture-driven unit — the suites drive every arm through the composed handler, so cutting the instrumentation from its wiring leaves two MRs whose suites cannot run standalone. Split by file group: virtual_observe.go 189, virtual_serve.go 99, virtual_read.go 70, virtual_metrics.md 48, the plan 29, virtual_assembly.go 28, virtual_metrics.go 25, virtual_sidecar.go 22, virtual_operations.md 12, the rest ≤9, suites 1,048.

Spec coverage

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

Acceptance criteria

Criterion Test
Merge-path values reach the wide event from Step 13's records TestMavenVirtualWideEvent_MergedDocumentRecordsTheMerge, _PartialMergeRecordsMergedPartial, _MergeAbsencesRecordTheirOwnToken, _MergeRecordsACachedContributionAsContributedStale, _MergeRecordsADeniedPositionWithoutDegrading, _MergeFailureRecordsTheMappedArm
Dropped-set records reach positions[] _AllSoftDeletedListRecordsSkippedPositions, _AllSoftDeletedMergeListRecordsSkippedPositions, _ALiveMergeStillRecordsItsDroppedAssociations
Budget-stopped merge's omitted_budget entries reach positions[] _MergeBudgetOmitsTheTail, TestMavenMergeServe_BudgetOmitsThePositionsPastTheAdmittedPrefix
A collection position's document read books its legs TestMavenVirtualWideEvent_MergeBooksItsFamilies, TestMavenVirtualMergeLegRecorder_BooksTheLegTheReadIssued
Omitted-outcome entry TestMavenVirtualMergePositions_OmitTheOutcomeNeitherSettled
The parked read renders only the tail omitted_budget TestMavenMergeServe_ACapAcquireFailureOmitsOnlyTheTail, TestMavenMergeServe_AZeroAdmissionBudgetIsUnavailable
Winner fields always absent on the merge path requireNoWinnerFields in every merge-path case
The version-level sidecar parent books its verdict, once _VersionLevelSidecarBooksItsParentVerdict, _MountedSidecarPanicBooksItsRecord
The package-level sidecar books the merge's own outcome _PackageLevelSidecarBooksOneSidecarEvent

ADR coverage

ADR-001 (namespace-scoped ids only), ADR-004 (the 20-upstream cap bounds the count families), ADR-005 (merge outputs stream, never redirect), ADR-007 (no schema change; the dropped set rides existing reads), ADR-008 (the validator keys on input digests; the merged document is deliberately unstored), ADR-009 (no route change; bounded identity-free labels), ADR-012 (the operational log event sits outside the usage-data decision) — checked against the plan, spec, and cited ADRs by three /validate-step passes; no conflicts.

Tests

go build ./..., go test ./internal/format/maven/..., golangci-lint run, and the comment-caps gate pass at this layer; the pre-commit hook's go-test scope ran green on every commit, including the post-rebase adaptation.

User documentation

No docs/user/ surface changes: the event and counter 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 (!3087 (merged)).

Related to #289 (closed)

Edited by Moaz Khalifa

Merge request reports

Loading
Loading