Identify conflicting sequence registration positions

This commit is contained in:
2026-10-24 00:15:23 +00:00
parent 42663cf07c
commit 57edb8ec19
3 changed files with 51 additions and 7 deletions

View File

@@ -319,7 +319,13 @@ Add ordinary/layered parity and warning-plus-later-error regressions.
## 9. Identify Conflicting Application Registrations In Diagnostics — P2 ## 9. Identify Conflicting Application Registrations In Diagnostics — P2
Status: [ ] Open. Status: [x] Complete.
Completion: definition validation retains original one-based registration
positions and names both positions in each overlap diagnostic. No input path or
new public diagnostic structure is invented. A mixed-selector regression verifies
multiple conflicts even after an earlier invalid registration is skipped.
Verified registration/facade coverage through `./scripts/check.sh`.
Owner: [`compileSequenceIdentityPolicy`](../../internal/engine/sequence_registration.go), Owner: [`compileSequenceIdentityPolicy`](../../internal/engine/sequence_registration.go),
lines 119–128. lines 119–128.

View File

@@ -1,5 +1,7 @@
package engine package engine
import "fmt"
type sequenceSelectorSegmentKind uint8 type sequenceSelectorSegmentKind uint8
const ( const (
@@ -87,9 +89,13 @@ func DefinitionWithSequenceIdentityRegistrations[T any](definition Definition[T]
func compileSequenceIdentityPolicy(registrations []sequenceIdentityRegistration) (sequencePolicy, []Diagnostic) { func compileSequenceIdentityPolicy(registrations []sequenceIdentityRegistration) (sequencePolicy, []Diagnostic) {
policy := sequencePolicy{} policy := sequencePolicy{}
compiled := make([]sequenceIdentityRegistration, 0, len(registrations)) type positionedRegistration struct {
registration sequenceIdentityRegistration
position int
}
compiled := make([]positionedRegistration, 0, len(registrations))
var diagnostics []Diagnostic var diagnostics []Diagnostic
for _, registration := range registrations { for position, registration := range registrations {
selector := cloneSequenceIdentitySelector(registration.selector) selector := cloneSequenceIdentitySelector(registration.selector)
if registration.invalidPath != nil { if registration.invalidPath != nil {
diagnostics = append(diagnostics, Diagnostic{ diagnostics = append(diagnostics, Diagnostic{
@@ -113,18 +119,21 @@ func compileSequenceIdentityPolicy(registrations []sequenceIdentityRegistration)
}) })
continue continue
} }
compiled = append(compiled, sequenceIdentityRegistration{selector: selector, identityFields: fields}) compiled = append(compiled, positionedRegistration{
registration: sequenceIdentityRegistration{selector: selector, identityFields: fields},
position: position + 1,
})
} }
for leftIndex := 0; leftIndex < len(compiled); leftIndex++ { for leftIndex := 0; leftIndex < len(compiled); leftIndex++ {
for rightIndex := leftIndex + 1; rightIndex < len(compiled); rightIndex++ { for rightIndex := leftIndex + 1; rightIndex < len(compiled); rightIndex++ {
left, right := compiled[leftIndex], compiled[rightIndex] left, right := compiled[leftIndex].registration, compiled[rightIndex].registration
if !sequenceIdentitySelectorsOverlap(left.selector, right.selector) || equalSequenceIdentityFields(left.identityFields, right.identityFields) { if !sequenceIdentitySelectorsOverlap(left.selector, right.selector) || equalSequenceIdentityFields(left.identityFields, right.identityFields) {
continue continue
} }
diagnostics = append(diagnostics, Diagnostic{ diagnostics = append(diagnostics, Diagnostic{
Code: CodeOptionsInvalid, Severity: SeverityError, Path: selectorDiagnosticPath(left.selector), Code: CodeOptionsInvalid, Severity: SeverityError, Path: selectorDiagnosticPath(left.selector),
Message: "overlapping sequence registrations must use the same identity fields", Message: fmt.Sprintf("sequence registrations %d and %d overlap with conflicting identity fields", compiled[leftIndex].position, compiled[rightIndex].position),
}) })
} }
} }
@@ -133,7 +142,8 @@ func compileSequenceIdentityPolicy(registrations []sequenceIdentityRegistration)
} }
unique := make([]sequenceIdentityRegistration, 0, len(compiled)) unique := make([]sequenceIdentityRegistration, 0, len(compiled))
for _, registration := range compiled { for _, positioned := range compiled {
registration := positioned.registration
duplicate := false duplicate := false
for _, existing := range unique { for _, existing := range unique {
if sequenceIdentitySelectorsEqual(existing.selector, registration.selector) && equalSequenceIdentityFields(existing.identityFields, registration.identityFields) { if sequenceIdentitySelectorsEqual(existing.selector, registration.selector) && equalSequenceIdentityFields(existing.identityFields, registration.identityFields) {

View File

@@ -178,6 +178,34 @@ func TestCompileSequenceIdentityPolicyRejectsConflictingOverlapsWithoutInput(t *
} }
} }
func TestSequenceOverlapDiagnosticsIdentifyOriginalRegistrationPositions(t *testing.T) {
t.Parallel()
_, diagnostics := compileSequenceIdentityPolicy([]sequenceIdentityRegistration{
{selector: sequenceIdentitySelector{literalSequenceSelector("invalid")}},
{selector: sequenceIdentitySelector{literalSequenceSelector("unrelated")}, identityFields: []string{"id"}},
{selector: sequenceIdentitySelector{literalSequenceSelector("collections"), anyMappingSequenceSelector(), literalSequenceSelector("items")}, identityFields: []string{"id"}},
{selector: sequenceIdentitySelector{literalSequenceSelector("collections"), literalSequenceSelector("foo"), literalSequenceSelector("items")}, identityFields: []string{"name"}},
{selector: sequenceIdentitySelector{literalSequenceSelector("collections"), literalSequenceSelector("bar"), literalSequenceSelector("items")}, identityFields: []string{"code"}},
})
if len(diagnostics) != 3 {
t.Fatalf("diagnostics = %#v, want the invalid registration and two conflicts", diagnostics)
}
for _, pair := range []string{"3 and 4", "3 and 5"} {
found := false
for _, diagnostic := range diagnostics {
if strings.Contains(diagnostic.Message, pair) {
found = true
if diagnostic.Code != CodeOptionsInvalid || diagnostic.Path.String() != "" || len(diagnostic.Related) != 0 {
t.Fatalf("definition overlap invented input coordinates: %#v", diagnostic)
}
}
}
if !found {
t.Errorf("conflicting registration positions %s not identified: %#v", pair, diagnostics)
}
}
}
func TestCompileSequenceIdentityPolicyAllowsDisjointSelectorsAndRoot(t *testing.T) { func TestCompileSequenceIdentityPolicyAllowsDisjointSelectorsAndRoot(t *testing.T) {
policy, diagnostics := compileSequenceIdentityPolicy([]sequenceIdentityRegistration{ policy, diagnostics := compileSequenceIdentityPolicy([]sequenceIdentityRegistration{
{selector: sequenceIdentitySelector{}, identityFields: []string{"id"}}, {selector: sequenceIdentitySelector{}, identityFields: []string{"id"}},