feat(maven): add the Maven virtual coordinate and rule adapter (S30 plan: 7/25)
Summary
Step 7 of the S30 Maven virtual plan (7 of 25): the Maven coordinate and rule adapter — the slice-side counterpart of npm's landed internal/format/npm/virtual_rules.go.
internal/format/maven/virtual_rules.go declares the three Maven target_field values (0 group id, 1 artifact id, 2 version) per ADR-007's column legend, the defined set handed to virtual.NewCoordinate (built per call, so widening it is impossible and over-declaring stays the fail-open direction npm's guards pin), NewVirtualCoordinate from a parsed path (a package-level request addresses group and artifact, a version-level request all three), and VirtualRulesFrom, which validates nothing and drops nothing — a dropped corrupt deny is a silent fail-open, so corrupt values instead reach the foundation matcher's fail-closed arm.
Spec coverage
The step's scope is S30, Allow/deny rule evaluation:
- Target-field legend (
0=group_id,1=artifact_id,2=version,smallint— the spec's data-model legend and the evaluation section's set): encoded invirtual_rules.goand pinned by literal-valued tests, so the schema legend itself is what the assertions check. - Address sets by path class (a package-level request addresses group and artifact; any version-level request addresses all three):
TestNewVirtualCoordinate_DecomposesEveryPathClassdrives all seven path classes through realParsePathparses and pins each addressed value plus the version rule's take-no-part direction. - The slice supplies the fields and the values, and filters nothing (the spec hands the whole rule set over unvalidated):
VirtualRulesFrommaps column-for-column without validating or dropping; the addressed-field-carrying-""-is-a-genuine-value cases are pinned on both hand-built and parsed inputs. - Corrupt values fail closed at the matcher: unchecked conversions let a corrupt
rule_type/target_fieldreach the foundation'sevaluate, whose invalid-rule arm fails the upstream closed —TestNewVirtualCoordinate_CorruptRuleValuesFailClosedpins out-of-range, negative, and unreadable values on both read classes, including the corrupt-deny-keeps-denying case.
Not this step's: the rule-scoping evaluation itself is the foundation matcher's (internal/virtual/rules.go, merged); the spec's end-to-end allow/deny criteria are exercised by Steps 10c, 11b, and 17 per the plan's DAG.
Governing ADRs
ADR-007 (database schema): the adapter encodes exactly the maven_virtual_upstream_rules column legend ADR-007 fixes — rule_type 0=allow/1=deny and target_field 0=group_id/1=artifact_id/2=version, both smallint — and the test file pins the schema values by literal rather than by production constant. ADR-012 is untouched: the adapter emits no event; the plan books the virtual pull emissions at Step 17. No other ADR governs this change; checked against the seven ADRs the plan and spec cite.
Reviewable LOC
120 reviewable source lines plus 971 test lines at head e9b0048a5 (1091 added, none removed; git diff --numstat origin/main...HEAD). Under the 500-reviewable-LOC ceiling in docs/dev/development-model.md, so no split or justification is owed.
Testing
internal/format/maven/virtual_rules_test.go mirrors npm's virtual_rules_test.go per the plan and docs/dev/go-testing.md (## Mirroring an existing suite): 10 of the 10 npm suites have direct counterparts, and both enumerated columns (rule_type 0/1, target_field 0/1/2) carry positive per-value suites. npm's TestNewVirtualCoordinate_VersionLengthCap has its counterpart in TestNewVirtualCoordinate_RejectsOverlongFields and TestNewVirtualCoordinate_AcceptsFieldsAtTheCap: NewVirtualCoordinate enforces the coordinate grammar's 255-byte field caps itself, as npm's constructor does.
go test ./internal/format/maven/ passes locally; CI is authoritative.
e2e catalog: no scenario added or affected — the step adds an internal adapter with no route, dispatch, or client-visible behavior change. The plan books the e2e.maven.virtual.* catalog group at Step 20.
Related to #1357