FAILURE MAP
← Case archive

FA-70516 / GIS polygon topology / Open access

Simple ring validity diagnosis: closing edge adjacency · case 01

Every valid ring is reported as self-intersecting.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The first and last edges share the start vertex but are not excluded as adjacent, so their common vertex counts as an intersection.

VERIFIED REPAIR

At the closing edge adjacency step restore `if j == i + 1 or (i == 0 and j == n - 1):`, leaving the rest of the model unchanged.

Unsuccessful approach: Skipping every pair involving the last edge also hides genuine crossings of the closing edge.

Case contract

Input: one ring as a list of integer [x, y]. Report the first failing check in this order: fewer than 4 positions "too_few_points"; first != last "not_closed"; two cyclically consecutive equal vertices "repeated_point"; a vertex where the boundary reverses direction along a line (collinear with a negative dot product of the incoming and outgoing edge, checked cyclically) "spike"; any two non-adjacent edges sharing a point (crossing, touching or collinear overlap) "self_intersection"; otherwise "valid".

Why this case matters

Topology validators run before editing, overlay and publishing; a wrong diagnosis sends users to fix the wrong vertex or lets broken rings through.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
    ring = x
    if len(ring) < 4:
        return 'too_few_points'
    if ring[0] != ring[-1]:
        return 'not_closed'
    pts = ring[:-1]
    n = len(pts)
    for i in range(n):
        if pts[i] == pts[(i + 1) % n]:
            return 'repeated_point'
    for i in range(n):
        a, b, c = pts[i - 1], pts[i], pts[(i + 1) % n]
        cross = (b[0] - a[0]) * (c[1] - b[1]) - (b[1] - a[1]) * (c[0] - b[0])
        dot = (b[0] - a[0]) * (c[0] - b[0]) + (b[1] - a[1]) * (c[1] - b[1])
        if cross == 0 and dot < 0:
            return 'spike'
    def orient(p, q, r):
        v = (q[0] - p[0]) * (r[1] - p[1]) - (q[1] - p[1]) * (r[0] - p[0])
        return (v > 0) - (v < 0)
    def within(p, q, r):
        return min(p[0], r[0]) <= q[0] <= max(p[0], r[0]) and min(p[1], r[1]) <= q[1] <= max(p[1], r[1])
    def hit(p1, p2, p3, p4):
        o1, o2, o3, o4 = orient(p1, p2, p3), orient(p1, p2, p4), orient(p3, p4, p1), orient(p3, p4, p2)
        if o1 * o2 < 0 and o3 * o4 < 0:
            return True
        return (o1 == 0 and within(p1, p3, p2)) or (o2 == 0 and within(p1, p4, p2)) or (o3 == 0 and within(p3, p1, p4)) or (o4 == 0 and within(p3, p2, p4))
    for i in range(n):
        for j in range(i + 1, n):
            if j == i + 1:
                continue
            if hit(pts[i], pts[(i + 1) % n], pts[j], pts[(j + 1) % n]):
                return 'self_intersection'
    return 'valid'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control #0', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]], 'valid'), ('control #1', [[0, 0], [10, 0], [5, 8], [0, 0]], 'valid'), ('control #2', [[0, 0], [5, 0], [10, 0], [10, 10], [0, 10], [0, 0]], 'valid'), ('regression #3', [[0, 0], [10, 10], [10, 0], [0, 10], [0, 0]], 'self_intersection'), ('regression #4', [[0, 0], [10, 0], [10, 10], [10, 5], [0, 5], [0, 0]], 'spike'), ('regression #5', [[5, 0], [10, 0], [10, 10], [0, 10], [0, 0], [5, 0], [0, 0]], 'not_closed'), ('boundary #6', [[0, 0], [10, 0], [10, 10], [0, 0]], 'valid'), ('regression #22', [[0, 0], [10, 0], [0, 10], [10, 10], [0, 0]], 'self_intersection')], [('control #1', [[0, 0], [10, 0], [5, 8], [0, 0]], 'valid'), ('regression #4', [[0, 0], [10, 0], [10, 10], [10, 5], [0, 5], [0, 0]], 'spike'), ('regression #5', [[5, 0], [10, 0], [10, 10], [0, 10], [0, 0], [5, 0], [0, 0]], 'not_closed'), ('boundary #6', [[0, 0], [10, 0], [10, 10], [0, 0]], 'valid'), ('boundary #7', [[0, 0], [10, 0], [10, 10]], 'too_few_points'), ('boundary #8', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 1]], 'not_closed'), ('boundary #9', [[0, 0], [10, 0], [10, 0], [10, 10], [0, 0]], 'repeated_point'), ('regression #28', [[2, 0], [1, 0], [0, 3], [2, 1], [4, 1], [2, 2], [2, 0]], 'self_intersection')], [('control #2', [[0, 0], [5, 0], [10, 0], [10, 10], [0, 10], [0, 0]], 'valid'), ('boundary #7', [[0, 0], [10, 0], [10, 10]], 'too_few_points'), ('boundary #8', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 1]], 'not_closed'), ('boundary #9', [[0, 0], [10, 0], [10, 0], [10, 10], [0, 0]], 'repeated_point'), ('regression #10', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 0], [0, 0]], 'repeated_point'), ('regression #11', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 5], [0, 12], [0, 0]], 'spike'), ('regression #12', [[0, 5], [0, 0], [10, 0], [10, 10], [0, 10], [0, 12], [0, 5]], 'spike'), ('regression #22', [[0, 0], [10, 0], [0, 10], [10, 10], [0, 0]], 'self_intersection')], [('boundary #6', [[0, 0], [10, 0], [10, 10], [0, 0]], 'valid'), ('regression #10', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 0], [0, 0]], 'repeated_point'), ('regression #11', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 5], [0, 12], [0, 0]], 'spike'), ('regression #12', [[0, 5], [0, 0], [10, 0], [10, 10], [0, 10], [0, 12], [0, 5]], 'spike'), ('regression #13', [[0, 0], [4, 0], [4, 4], [8, 4], [8, 8], [4, 8], [4, 4], [0, 4], [0, 0]], 'self_intersection'), ('regression #14', [[0, 0], [10, 0], [10, 10], [5, 0], [0, 10], [0, 0]], 'self_intersection'), ('regression #15', [[0, 0], [10, 0], [10, 5], [2, 5], [6, 5], [6, 8], [0, 8], [0, 0]], 'spike'), ('regression #28', [[2, 0], [1, 0], [0, 3], [2, 1], [4, 1], [2, 2], [2, 0]], 'self_intersection')], [('regression #12', [[0, 5], [0, 0], [10, 0], [10, 10], [0, 10], [0, 12], [0, 5]], 'spike'), ('regression #13', [[0, 0], [4, 0], [4, 4], [8, 4], [8, 8], [4, 8], [4, 4], [0, 4], [0, 0]], 'self_intersection'), ('regression #14', [[0, 0], [10, 0], [10, 10], [5, 0], [0, 10], [0, 0]], 'self_intersection'), ('regression #15', [[0, 0], [10, 0], [10, 5], [2, 5], [6, 5], [6, 8], [0, 8], [0, 0]], 'spike'), ('control #16', [[0, 0], [6, 0], [6, 6], [3, 3], [0, 6], [0, 0]], 'valid'), ('regression #17', [[0, 0], [10, 0], [10, 10], [0, 10], [4, 10], [0, 0]], 'spike'), ('control #18', [[1, 1], [9, 2], [8, 9], [2, 7], [1, 1]], 'valid'), ('regression #22', [[0, 0], [10, 0], [0, 10], [10, 10], [0, 0]], 'self_intersection')]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
control #0self_intersectionvalidFailed
control #1self_intersectionvalidFailed
control #2self_intersectionvalidFailed
regression #3self_intersectionself_intersectionPassed
regression #4spikespikePassed
regression #5not_closednot_closedPassed
boundary #6self_intersectionvalidFailed
regression #22self_intersectionself_intersectionPassed

SHA-256 / 140f9b6626ebc45348e258c83a8ab184c764820ae5e0d8c1aa2298e65152d48d

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
    ring = x
    if len(ring) < 4:
        return 'too_few_points'
    if ring[0] != ring[-1]:
        return 'not_closed'
    pts = ring[:-1]
    n = len(pts)
    for i in range(n):
        if pts[i] == pts[(i + 1) % n]:
            return 'repeated_point'
    for i in range(n):
        a, b, c = pts[i - 1], pts[i], pts[(i + 1) % n]
        cross = (b[0] - a[0]) * (c[1] - b[1]) - (b[1] - a[1]) * (c[0] - b[0])
        dot = (b[0] - a[0]) * (c[0] - b[0]) + (b[1] - a[1]) * (c[1] - b[1])
        if cross == 0 and dot < 0:
            return 'spike'
    def orient(p, q, r):
        v = (q[0] - p[0]) * (r[1] - p[1]) - (q[1] - p[1]) * (r[0] - p[0])
        return (v > 0) - (v < 0)
    def within(p, q, r):
        return min(p[0], r[0]) <= q[0] <= max(p[0], r[0]) and min(p[1], r[1]) <= q[1] <= max(p[1], r[1])
    def hit(p1, p2, p3, p4):
        o1, o2, o3, o4 = orient(p1, p2, p3), orient(p1, p2, p4), orient(p3, p4, p1), orient(p3, p4, p2)
        if o1 * o2 < 0 and o3 * o4 < 0:
            return True
        return (o1 == 0 and within(p1, p3, p2)) or (o2 == 0 and within(p1, p4, p2)) or (o3 == 0 and within(p3, p1, p4)) or (o4 == 0 and within(p3, p2, p4))
    for i in range(n):
        for j in range(i + 1, n):
            if j == i + 1 or j == n - 1:
                continue
            if hit(pts[i], pts[(i + 1) % n], pts[j], pts[(j + 1) % n]):
                return 'self_intersection'
    return 'valid'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control #0', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]], 'valid'), ('control #1', [[0, 0], [10, 0], [5, 8], [0, 0]], 'valid'), ('control #2', [[0, 0], [5, 0], [10, 0], [10, 10], [0, 10], [0, 0]], 'valid'), ('regression #3', [[0, 0], [10, 10], [10, 0], [0, 10], [0, 0]], 'self_intersection'), ('regression #4', [[0, 0], [10, 0], [10, 10], [10, 5], [0, 5], [0, 0]], 'spike'), ('regression #5', [[5, 0], [10, 0], [10, 10], [0, 10], [0, 0], [5, 0], [0, 0]], 'not_closed'), ('boundary #6', [[0, 0], [10, 0], [10, 10], [0, 0]], 'valid'), ('regression #22', [[0, 0], [10, 0], [0, 10], [10, 10], [0, 0]], 'self_intersection')], [('control #1', [[0, 0], [10, 0], [5, 8], [0, 0]], 'valid'), ('regression #4', [[0, 0], [10, 0], [10, 10], [10, 5], [0, 5], [0, 0]], 'spike'), ('regression #5', [[5, 0], [10, 0], [10, 10], [0, 10], [0, 0], [5, 0], [0, 0]], 'not_closed'), ('boundary #6', [[0, 0], [10, 0], [10, 10], [0, 0]], 'valid'), ('boundary #7', [[0, 0], [10, 0], [10, 10]], 'too_few_points'), ('boundary #8', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 1]], 'not_closed'), ('boundary #9', [[0, 0], [10, 0], [10, 0], [10, 10], [0, 0]], 'repeated_point'), ('regression #28', [[2, 0], [1, 0], [0, 3], [2, 1], [4, 1], [2, 2], [2, 0]], 'self_intersection')], [('control #2', [[0, 0], [5, 0], [10, 0], [10, 10], [0, 10], [0, 0]], 'valid'), ('boundary #7', [[0, 0], [10, 0], [10, 10]], 'too_few_points'), ('boundary #8', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 1]], 'not_closed'), ('boundary #9', [[0, 0], [10, 0], [10, 0], [10, 10], [0, 0]], 'repeated_point'), ('regression #10', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 0], [0, 0]], 'repeated_point'), ('regression #11', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 5], [0, 12], [0, 0]], 'spike'), ('regression #12', [[0, 5], [0, 0], [10, 0], [10, 10], [0, 10], [0, 12], [0, 5]], 'spike'), ('regression #22', [[0, 0], [10, 0], [0, 10], [10, 10], [0, 0]], 'self_intersection')], [('boundary #6', [[0, 0], [10, 0], [10, 10], [0, 0]], 'valid'), ('regression #10', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 0], [0, 0]], 'repeated_point'), ('regression #11', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 5], [0, 12], [0, 0]], 'spike'), ('regression #12', [[0, 5], [0, 0], [10, 0], [10, 10], [0, 10], [0, 12], [0, 5]], 'spike'), ('regression #13', [[0, 0], [4, 0], [4, 4], [8, 4], [8, 8], [4, 8], [4, 4], [0, 4], [0, 0]], 'self_intersection'), ('regression #14', [[0, 0], [10, 0], [10, 10], [5, 0], [0, 10], [0, 0]], 'self_intersection'), ('regression #15', [[0, 0], [10, 0], [10, 5], [2, 5], [6, 5], [6, 8], [0, 8], [0, 0]], 'spike'), ('regression #28', [[2, 0], [1, 0], [0, 3], [2, 1], [4, 1], [2, 2], [2, 0]], 'self_intersection')], [('regression #12', [[0, 5], [0, 0], [10, 0], [10, 10], [0, 10], [0, 12], [0, 5]], 'spike'), ('regression #13', [[0, 0], [4, 0], [4, 4], [8, 4], [8, 8], [4, 8], [4, 4], [0, 4], [0, 0]], 'self_intersection'), ('regression #14', [[0, 0], [10, 0], [10, 10], [5, 0], [0, 10], [0, 0]], 'self_intersection'), ('regression #15', [[0, 0], [10, 0], [10, 5], [2, 5], [6, 5], [6, 8], [0, 8], [0, 0]], 'spike'), ('control #16', [[0, 0], [6, 0], [6, 6], [3, 3], [0, 6], [0, 0]], 'valid'), ('regression #17', [[0, 0], [10, 0], [10, 10], [0, 10], [4, 10], [0, 0]], 'spike'), ('control #18', [[1, 1], [9, 2], [8, 9], [2, 7], [1, 1]], 'valid'), ('regression #22', [[0, 0], [10, 0], [0, 10], [10, 10], [0, 0]], 'self_intersection')]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
control #0validvalidPassed
control #1validvalidPassed
control #2validvalidPassed
regression #3self_intersectionself_intersectionPassed
regression #4spikespikePassed
regression #5not_closednot_closedPassed
boundary #6validvalidPassed
regression #22validself_intersectionFailed

SHA-256 / 241e54dd227a64f2db6e539bb8f1ef1dbe3fa64fec3a678e9900b1140bad94a8

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
    ring = x
    if len(ring) < 4:
        return 'too_few_points'
    if ring[0] != ring[-1]:
        return 'not_closed'
    pts = ring[:-1]
    n = len(pts)
    for i in range(n):
        if pts[i] == pts[(i + 1) % n]:
            return 'repeated_point'
    for i in range(n):
        a, b, c = pts[i - 1], pts[i], pts[(i + 1) % n]
        cross = (b[0] - a[0]) * (c[1] - b[1]) - (b[1] - a[1]) * (c[0] - b[0])
        dot = (b[0] - a[0]) * (c[0] - b[0]) + (b[1] - a[1]) * (c[1] - b[1])
        if cross == 0 and dot < 0:
            return 'spike'
    def orient(p, q, r):
        v = (q[0] - p[0]) * (r[1] - p[1]) - (q[1] - p[1]) * (r[0] - p[0])
        return (v > 0) - (v < 0)
    def within(p, q, r):
        return min(p[0], r[0]) <= q[0] <= max(p[0], r[0]) and min(p[1], r[1]) <= q[1] <= max(p[1], r[1])
    def hit(p1, p2, p3, p4):
        o1, o2, o3, o4 = orient(p1, p2, p3), orient(p1, p2, p4), orient(p3, p4, p1), orient(p3, p4, p2)
        if o1 * o2 < 0 and o3 * o4 < 0:
            return True
        return (o1 == 0 and within(p1, p3, p2)) or (o2 == 0 and within(p1, p4, p2)) or (o3 == 0 and within(p3, p1, p4)) or (o4 == 0 and within(p3, p2, p4))
    for i in range(n):
        for j in range(i + 1, n):
            if j == i + 1 or (i == 0 and j == n - 1):
                continue
            if hit(pts[i], pts[(i + 1) % n], pts[j], pts[(j + 1) % n]):
                return 'self_intersection'
    return 'valid'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('control #0', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]], 'valid'), ('control #1', [[0, 0], [10, 0], [5, 8], [0, 0]], 'valid'), ('control #2', [[0, 0], [5, 0], [10, 0], [10, 10], [0, 10], [0, 0]], 'valid'), ('regression #3', [[0, 0], [10, 10], [10, 0], [0, 10], [0, 0]], 'self_intersection'), ('regression #4', [[0, 0], [10, 0], [10, 10], [10, 5], [0, 5], [0, 0]], 'spike'), ('regression #5', [[5, 0], [10, 0], [10, 10], [0, 10], [0, 0], [5, 0], [0, 0]], 'not_closed'), ('boundary #6', [[0, 0], [10, 0], [10, 10], [0, 0]], 'valid'), ('regression #22', [[0, 0], [10, 0], [0, 10], [10, 10], [0, 0]], 'self_intersection')], [('control #1', [[0, 0], [10, 0], [5, 8], [0, 0]], 'valid'), ('regression #4', [[0, 0], [10, 0], [10, 10], [10, 5], [0, 5], [0, 0]], 'spike'), ('regression #5', [[5, 0], [10, 0], [10, 10], [0, 10], [0, 0], [5, 0], [0, 0]], 'not_closed'), ('boundary #6', [[0, 0], [10, 0], [10, 10], [0, 0]], 'valid'), ('boundary #7', [[0, 0], [10, 0], [10, 10]], 'too_few_points'), ('boundary #8', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 1]], 'not_closed'), ('boundary #9', [[0, 0], [10, 0], [10, 0], [10, 10], [0, 0]], 'repeated_point'), ('regression #28', [[2, 0], [1, 0], [0, 3], [2, 1], [4, 1], [2, 2], [2, 0]], 'self_intersection')], [('control #2', [[0, 0], [5, 0], [10, 0], [10, 10], [0, 10], [0, 0]], 'valid'), ('boundary #7', [[0, 0], [10, 0], [10, 10]], 'too_few_points'), ('boundary #8', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 1]], 'not_closed'), ('boundary #9', [[0, 0], [10, 0], [10, 0], [10, 10], [0, 0]], 'repeated_point'), ('regression #10', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 0], [0, 0]], 'repeated_point'), ('regression #11', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 5], [0, 12], [0, 0]], 'spike'), ('regression #12', [[0, 5], [0, 0], [10, 0], [10, 10], [0, 10], [0, 12], [0, 5]], 'spike'), ('regression #22', [[0, 0], [10, 0], [0, 10], [10, 10], [0, 0]], 'self_intersection')], [('boundary #6', [[0, 0], [10, 0], [10, 10], [0, 0]], 'valid'), ('regression #10', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 0], [0, 0]], 'repeated_point'), ('regression #11', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 5], [0, 12], [0, 0]], 'spike'), ('regression #12', [[0, 5], [0, 0], [10, 0], [10, 10], [0, 10], [0, 12], [0, 5]], 'spike'), ('regression #13', [[0, 0], [4, 0], [4, 4], [8, 4], [8, 8], [4, 8], [4, 4], [0, 4], [0, 0]], 'self_intersection'), ('regression #14', [[0, 0], [10, 0], [10, 10], [5, 0], [0, 10], [0, 0]], 'self_intersection'), ('regression #15', [[0, 0], [10, 0], [10, 5], [2, 5], [6, 5], [6, 8], [0, 8], [0, 0]], 'spike'), ('regression #28', [[2, 0], [1, 0], [0, 3], [2, 1], [4, 1], [2, 2], [2, 0]], 'self_intersection')], [('regression #12', [[0, 5], [0, 0], [10, 0], [10, 10], [0, 10], [0, 12], [0, 5]], 'spike'), ('regression #13', [[0, 0], [4, 0], [4, 4], [8, 4], [8, 8], [4, 8], [4, 4], [0, 4], [0, 0]], 'self_intersection'), ('regression #14', [[0, 0], [10, 0], [10, 10], [5, 0], [0, 10], [0, 0]], 'self_intersection'), ('regression #15', [[0, 0], [10, 0], [10, 5], [2, 5], [6, 5], [6, 8], [0, 8], [0, 0]], 'spike'), ('control #16', [[0, 0], [6, 0], [6, 6], [3, 3], [0, 6], [0, 0]], 'valid'), ('regression #17', [[0, 0], [10, 0], [10, 10], [0, 10], [4, 10], [0, 0]], 'spike'), ('control #18', [[1, 1], [9, 2], [8, 9], [2, 7], [1, 1]], 'valid'), ('regression #22', [[0, 0], [10, 0], [0, 10], [10, 10], [0, 0]], 'self_intersection')]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
control #0validvalidPassed
control #1validvalidPassed
control #2validvalidPassed
regression #3self_intersectionself_intersectionPassed
regression #4spikespikePassed
regression #5not_closednot_closedPassed
boundary #6validvalidPassed
regression #22self_intersectionself_intersectionPassed

SHA-256 / 03738e615dd353bda5bc50063aa6b2026cfadbb3bf793af4af31a34e3862ae61

Verification & scope

Stipulated deterministic toy contract on a bounded input domain; results are rounded as stated and no conformance with any published standard or library is claimed. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:48:21.438956+00:00.

Case digest / a3a9e8a8a2463819b4be91bf31d90b1e1381b6dd45968e268a5a66809bba3129