FAILURE MAP
← Case archive

FA-88751 / Mesh topology invariants / Open access

UV seam test compares edge UVs in traversal order · case 01

Every interior edge is reported as a seam because neighbours traverse it in opposite directions.

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

ROOT CAUSE

The UV pair is stored as a directed tuple rather than keyed by vertex.

VERIFIED REPAIR

Map each geometric vertex to its UV index before comparing.

Unsuccessful approach: Sorting the UV pair ignores which vertex owns which UV and misses swapped assignments.

Case contract

Input [faces, uvfaces]: each face has a parallel list of UV indices. A face whose UV list length differs returns ["uv-mismatch", face index]. A geometric edge is a seam when a later face assigns a different UV index to either endpoint than the first face did. Return sorted seam edges [min,max].

Why this case matters

Mesh processing pipelines (remeshing, export, simulation, printing) trust these topological counts and adjacency answers; a wrong invariant silently accepts broken meshes or rejects valid ones.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
    faces,uvf=x
    seen={}
    seams=set()
    for fi,(f,t) in enumerate(zip(faces,uvf)):
        if len(f)!=len(t): return ['uv-mismatch',fi]
        n=len(f)
        for i in range(n):
            a,b=f[i],f[(i+1)%n]
            ua,ub=t[i],t[(i+1)%n]
            k=(min(a,b),max(a,b))
            m=(ua,ub)
            if k in seen:
                if seen[k]!=m: seams.add(k)
            else: seen[k]=m
    return sorted([list(k) for k in seams])
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['shared uv no seam', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [2, 1, 3]]]], []], ['one endpoint split', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [4, 1, 3]]]], [[1, 2]]], ['both endpoints split', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [5, 4, 3]]]], [[1, 2]]], ['swapped uv indices', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [1, 2, 3]]]], [[1, 2]]], ['cube with seams', [[[[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 5, 4], [1, 2, 6, 5], [2, 3, 7, 6], [3, 0, 4, 7]], [[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 9, 8], [1, 2, 10, 9], [2, 3, 11, 10], [3, 0, 8, 11]]]], [[4, 5], [4, 7], [5, 6], [6, 7]]], ['length mismatch', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [2, 1]]]], ['uv-mismatch', 1]], ['extra uv corner', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2, 7], [2, 1, 3]]]], ['uv-mismatch', 0]]], [['swapped uv indices', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [1, 2, 3]]]], [[1, 2]]], ['cube with seams', [[[[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 5, 4], [1, 2, 6, 5], [2, 3, 7, 6], [3, 0, 4, 7]], [[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 9, 8], [1, 2, 10, 9], [2, 3, 11, 10], [3, 0, 8, 11]]]], [[4, 5], [4, 7], [5, 6], [6, 7]]], ['length mismatch', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [2, 1]]]], ['uv-mismatch', 1]], ['extra uv corner', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2, 7], [2, 1, 3]]]], ['uv-mismatch', 0]], ['quad strip', [[[[0, 1, 4, 3], [1, 2, 5, 4]], [[0, 1, 4, 3], [1, 2, 5, 4]]]], []], ['quad strip split', [[[[0, 1, 4, 3], [1, 2, 5, 4]], [[0, 1, 4, 3], [6, 2, 5, 7]]]], [[1, 4]]], ['disk atlas', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]], [[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [6, 5, 1]]]], [[0, 1], [0, 5]]]], [['shared uv no seam', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [2, 1, 3]]]], []], ['swapped uv indices', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [1, 2, 3]]]], [[1, 2]]], ['cube with seams', [[[[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 5, 4], [1, 2, 6, 5], [2, 3, 7, 6], [3, 0, 4, 7]], [[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 9, 8], [1, 2, 10, 9], [2, 3, 11, 10], [3, 0, 8, 11]]]], [[4, 5], [4, 7], [5, 6], [6, 7]]], ['quad strip split', [[[[0, 1, 4, 3], [1, 2, 5, 4]], [[0, 1, 4, 3], [6, 2, 5, 7]]]], [[1, 4]]], ['disk atlas', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]], [[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [6, 5, 1]]]], [[0, 1], [0, 5]]], ['mismatch after seams', [[[[0, 1, 2], [2, 1, 3], [3, 1, 4]], [[0, 1, 2], [5, 1, 3], [3, 1]]]], ['uv-mismatch', 2]], ['same uv sum collision', [[[[0, 3, 1], [1, 3, 2]], [[0, 3, 1], [1, 3, 2]]]], []]], [['shared uv no seam', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [2, 1, 3]]]], []], ['one endpoint split', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [4, 1, 3]]]], [[1, 2]]], ['both endpoints split', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [5, 4, 3]]]], [[1, 2]]], ['swapped uv indices', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [1, 2, 3]]]], [[1, 2]]], ['cube with seams', [[[[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 5, 4], [1, 2, 6, 5], [2, 3, 7, 6], [3, 0, 4, 7]], [[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 9, 8], [1, 2, 10, 9], [2, 3, 11, 10], [3, 0, 8, 11]]]], [[4, 5], [4, 7], [5, 6], [6, 7]]], ['quad strip', [[[[0, 1, 4, 3], [1, 2, 5, 4]], [[0, 1, 4, 3], [1, 2, 5, 4]]]], []], ['same uv sum collision', [[[[0, 3, 1], [1, 3, 2]], [[0, 3, 1], [1, 3, 2]]]], []]], [['both endpoints split', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [5, 4, 3]]]], [[1, 2]]], ['swapped uv indices', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [1, 2, 3]]]], [[1, 2]]], ['cube with seams', [[[[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 5, 4], [1, 2, 6, 5], [2, 3, 7, 6], [3, 0, 4, 7]], [[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 9, 8], [1, 2, 10, 9], [2, 3, 11, 10], [3, 0, 8, 11]]]], [[4, 5], [4, 7], [5, 6], [6, 7]]], ['length mismatch', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [2, 1]]]], ['uv-mismatch', 1]], ['extra uv corner', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2, 7], [2, 1, 3]]]], ['uv-mismatch', 0]], ['quad strip', [[[[0, 1, 4, 3], [1, 2, 5, 4]], [[0, 1, 4, 3], [1, 2, 5, 4]]]], []], ['disk atlas', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]], [[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [6, 5, 1]]]], [[0, 1], [0, 5]]]]]
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
shared uv no seam[[1, 2]][]Failed
one endpoint split[[1, 2]][[1, 2]]Passed
both endpoints split[[1, 2]][[1, 2]]Passed
swapped uv indices[][[1, 2]]Failed
cube with seams[[0, 1], [0, 3], [0, 4], [1, 2], [1, 5], [2, 3], [2, 6], [3, 7], [4, 5], [4, 7], [5, 6], [6, 7]][[4, 5], [4, 7], [5, 6], [6, 7]]Failed
length mismatch['uv-mismatch', 1]['uv-mismatch', 1]Passed
extra uv corner['uv-mismatch', 0]['uv-mismatch', 0]Passed

SHA-256 / 7852606b5dccbc230a8bd4afeadf5b6a3190e4d59aaf6117e01ce9a2c2f16c96

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
    faces,uvf=x
    seen={}
    seams=set()
    for fi,(f,t) in enumerate(zip(faces,uvf)):
        if len(f)!=len(t): return ['uv-mismatch',fi]
        n=len(f)
        for i in range(n):
            a,b=f[i],f[(i+1)%n]
            ua,ub=t[i],t[(i+1)%n]
            k=(min(a,b),max(a,b))
            m=(min(ua,ub),max(ua,ub))
            if k in seen:
                if seen[k]!=m: seams.add(k)
            else: seen[k]=m
    return sorted([list(k) for k in seams])
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['shared uv no seam', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [2, 1, 3]]]], []], ['one endpoint split', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [4, 1, 3]]]], [[1, 2]]], ['both endpoints split', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [5, 4, 3]]]], [[1, 2]]], ['swapped uv indices', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [1, 2, 3]]]], [[1, 2]]], ['cube with seams', [[[[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 5, 4], [1, 2, 6, 5], [2, 3, 7, 6], [3, 0, 4, 7]], [[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 9, 8], [1, 2, 10, 9], [2, 3, 11, 10], [3, 0, 8, 11]]]], [[4, 5], [4, 7], [5, 6], [6, 7]]], ['length mismatch', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [2, 1]]]], ['uv-mismatch', 1]], ['extra uv corner', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2, 7], [2, 1, 3]]]], ['uv-mismatch', 0]]], [['swapped uv indices', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [1, 2, 3]]]], [[1, 2]]], ['cube with seams', [[[[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 5, 4], [1, 2, 6, 5], [2, 3, 7, 6], [3, 0, 4, 7]], [[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 9, 8], [1, 2, 10, 9], [2, 3, 11, 10], [3, 0, 8, 11]]]], [[4, 5], [4, 7], [5, 6], [6, 7]]], ['length mismatch', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [2, 1]]]], ['uv-mismatch', 1]], ['extra uv corner', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2, 7], [2, 1, 3]]]], ['uv-mismatch', 0]], ['quad strip', [[[[0, 1, 4, 3], [1, 2, 5, 4]], [[0, 1, 4, 3], [1, 2, 5, 4]]]], []], ['quad strip split', [[[[0, 1, 4, 3], [1, 2, 5, 4]], [[0, 1, 4, 3], [6, 2, 5, 7]]]], [[1, 4]]], ['disk atlas', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]], [[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [6, 5, 1]]]], [[0, 1], [0, 5]]]], [['shared uv no seam', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [2, 1, 3]]]], []], ['swapped uv indices', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [1, 2, 3]]]], [[1, 2]]], ['cube with seams', [[[[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 5, 4], [1, 2, 6, 5], [2, 3, 7, 6], [3, 0, 4, 7]], [[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 9, 8], [1, 2, 10, 9], [2, 3, 11, 10], [3, 0, 8, 11]]]], [[4, 5], [4, 7], [5, 6], [6, 7]]], ['quad strip split', [[[[0, 1, 4, 3], [1, 2, 5, 4]], [[0, 1, 4, 3], [6, 2, 5, 7]]]], [[1, 4]]], ['disk atlas', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]], [[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [6, 5, 1]]]], [[0, 1], [0, 5]]], ['mismatch after seams', [[[[0, 1, 2], [2, 1, 3], [3, 1, 4]], [[0, 1, 2], [5, 1, 3], [3, 1]]]], ['uv-mismatch', 2]], ['same uv sum collision', [[[[0, 3, 1], [1, 3, 2]], [[0, 3, 1], [1, 3, 2]]]], []]], [['shared uv no seam', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [2, 1, 3]]]], []], ['one endpoint split', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [4, 1, 3]]]], [[1, 2]]], ['both endpoints split', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [5, 4, 3]]]], [[1, 2]]], ['swapped uv indices', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [1, 2, 3]]]], [[1, 2]]], ['cube with seams', [[[[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 5, 4], [1, 2, 6, 5], [2, 3, 7, 6], [3, 0, 4, 7]], [[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 9, 8], [1, 2, 10, 9], [2, 3, 11, 10], [3, 0, 8, 11]]]], [[4, 5], [4, 7], [5, 6], [6, 7]]], ['quad strip', [[[[0, 1, 4, 3], [1, 2, 5, 4]], [[0, 1, 4, 3], [1, 2, 5, 4]]]], []], ['same uv sum collision', [[[[0, 3, 1], [1, 3, 2]], [[0, 3, 1], [1, 3, 2]]]], []]], [['both endpoints split', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [5, 4, 3]]]], [[1, 2]]], ['swapped uv indices', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [1, 2, 3]]]], [[1, 2]]], ['cube with seams', [[[[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 5, 4], [1, 2, 6, 5], [2, 3, 7, 6], [3, 0, 4, 7]], [[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 9, 8], [1, 2, 10, 9], [2, 3, 11, 10], [3, 0, 8, 11]]]], [[4, 5], [4, 7], [5, 6], [6, 7]]], ['length mismatch', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [2, 1]]]], ['uv-mismatch', 1]], ['extra uv corner', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2, 7], [2, 1, 3]]]], ['uv-mismatch', 0]], ['quad strip', [[[[0, 1, 4, 3], [1, 2, 5, 4]], [[0, 1, 4, 3], [1, 2, 5, 4]]]], []], ['disk atlas', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]], [[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [6, 5, 1]]]], [[0, 1], [0, 5]]]]]
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
shared uv no seam[][]Passed
one endpoint split[[1, 2]][[1, 2]]Passed
both endpoints split[[1, 2]][[1, 2]]Passed
swapped uv indices[][[1, 2]]Failed
cube with seams[[4, 5], [4, 7], [5, 6], [6, 7]][[4, 5], [4, 7], [5, 6], [6, 7]]Passed
length mismatch['uv-mismatch', 1]['uv-mismatch', 1]Passed
extra uv corner['uv-mismatch', 0]['uv-mismatch', 0]Passed

SHA-256 / c4ab2ac9c2dbaec092bd9146ef3a72b6c8e905035b8437d2ecd4400af1936d82

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(x):
    faces,uvf=x
    seen={}
    seams=set()
    for fi,(f,t) in enumerate(zip(faces,uvf)):
        if len(f)!=len(t): return ['uv-mismatch',fi]
        n=len(f)
        for i in range(n):
            a,b=f[i],f[(i+1)%n]
            ua,ub=t[i],t[(i+1)%n]
            k=(min(a,b),max(a,b))
            m={a:ua,b:ub}
            if k in seen:
                if seen[k]!=m: seams.add(k)
            else: seen[k]=m
    return sorted([list(k) for k in seams])
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['shared uv no seam', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [2, 1, 3]]]], []], ['one endpoint split', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [4, 1, 3]]]], [[1, 2]]], ['both endpoints split', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [5, 4, 3]]]], [[1, 2]]], ['swapped uv indices', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [1, 2, 3]]]], [[1, 2]]], ['cube with seams', [[[[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 5, 4], [1, 2, 6, 5], [2, 3, 7, 6], [3, 0, 4, 7]], [[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 9, 8], [1, 2, 10, 9], [2, 3, 11, 10], [3, 0, 8, 11]]]], [[4, 5], [4, 7], [5, 6], [6, 7]]], ['length mismatch', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [2, 1]]]], ['uv-mismatch', 1]], ['extra uv corner', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2, 7], [2, 1, 3]]]], ['uv-mismatch', 0]]], [['swapped uv indices', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [1, 2, 3]]]], [[1, 2]]], ['cube with seams', [[[[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 5, 4], [1, 2, 6, 5], [2, 3, 7, 6], [3, 0, 4, 7]], [[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 9, 8], [1, 2, 10, 9], [2, 3, 11, 10], [3, 0, 8, 11]]]], [[4, 5], [4, 7], [5, 6], [6, 7]]], ['length mismatch', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [2, 1]]]], ['uv-mismatch', 1]], ['extra uv corner', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2, 7], [2, 1, 3]]]], ['uv-mismatch', 0]], ['quad strip', [[[[0, 1, 4, 3], [1, 2, 5, 4]], [[0, 1, 4, 3], [1, 2, 5, 4]]]], []], ['quad strip split', [[[[0, 1, 4, 3], [1, 2, 5, 4]], [[0, 1, 4, 3], [6, 2, 5, 7]]]], [[1, 4]]], ['disk atlas', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]], [[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [6, 5, 1]]]], [[0, 1], [0, 5]]]], [['shared uv no seam', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [2, 1, 3]]]], []], ['swapped uv indices', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [1, 2, 3]]]], [[1, 2]]], ['cube with seams', [[[[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 5, 4], [1, 2, 6, 5], [2, 3, 7, 6], [3, 0, 4, 7]], [[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 9, 8], [1, 2, 10, 9], [2, 3, 11, 10], [3, 0, 8, 11]]]], [[4, 5], [4, 7], [5, 6], [6, 7]]], ['quad strip split', [[[[0, 1, 4, 3], [1, 2, 5, 4]], [[0, 1, 4, 3], [6, 2, 5, 7]]]], [[1, 4]]], ['disk atlas', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]], [[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [6, 5, 1]]]], [[0, 1], [0, 5]]], ['mismatch after seams', [[[[0, 1, 2], [2, 1, 3], [3, 1, 4]], [[0, 1, 2], [5, 1, 3], [3, 1]]]], ['uv-mismatch', 2]], ['same uv sum collision', [[[[0, 3, 1], [1, 3, 2]], [[0, 3, 1], [1, 3, 2]]]], []]], [['shared uv no seam', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [2, 1, 3]]]], []], ['one endpoint split', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [4, 1, 3]]]], [[1, 2]]], ['both endpoints split', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [5, 4, 3]]]], [[1, 2]]], ['swapped uv indices', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [1, 2, 3]]]], [[1, 2]]], ['cube with seams', [[[[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 5, 4], [1, 2, 6, 5], [2, 3, 7, 6], [3, 0, 4, 7]], [[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 9, 8], [1, 2, 10, 9], [2, 3, 11, 10], [3, 0, 8, 11]]]], [[4, 5], [4, 7], [5, 6], [6, 7]]], ['quad strip', [[[[0, 1, 4, 3], [1, 2, 5, 4]], [[0, 1, 4, 3], [1, 2, 5, 4]]]], []], ['same uv sum collision', [[[[0, 3, 1], [1, 3, 2]], [[0, 3, 1], [1, 3, 2]]]], []]], [['both endpoints split', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [5, 4, 3]]]], [[1, 2]]], ['swapped uv indices', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [1, 2, 3]]]], [[1, 2]]], ['cube with seams', [[[[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 5, 4], [1, 2, 6, 5], [2, 3, 7, 6], [3, 0, 4, 7]], [[0, 3, 2, 1], [4, 5, 6, 7], [0, 1, 9, 8], [1, 2, 10, 9], [2, 3, 11, 10], [3, 0, 8, 11]]]], [[4, 5], [4, 7], [5, 6], [6, 7]]], ['length mismatch', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2], [2, 1]]]], ['uv-mismatch', 1]], ['extra uv corner', [[[[0, 1, 2], [2, 1, 3]], [[0, 1, 2, 7], [2, 1, 3]]]], ['uv-mismatch', 0]], ['quad strip', [[[[0, 1, 4, 3], [1, 2, 5, 4]], [[0, 1, 4, 3], [1, 2, 5, 4]]]], []], ['disk atlas', [[[[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [0, 5, 1]], [[0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 5], [6, 5, 1]]]], [[0, 1], [0, 5]]]]]
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
shared uv no seam[][]Passed
one endpoint split[[1, 2]][[1, 2]]Passed
both endpoints split[[1, 2]][[1, 2]]Passed
swapped uv indices[[1, 2]][[1, 2]]Passed
cube with seams[[4, 5], [4, 7], [5, 6], [6, 7]][[4, 5], [4, 7], [5, 6], [6, 7]]Passed
length mismatch['uv-mismatch', 1]['uv-mismatch', 1]Passed
extra uv corner['uv-mismatch', 0]['uv-mismatch', 0]Passed

SHA-256 / 8818bfd096d3ffb9bee18567d4fbd5fa7bbbb3112e1291dca61581fff7a5de2e

Verification & scope

Pure combinatorial teaching model over integer face lists with a stipulated contract; no geometry kernel or file format is implied. 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:51:11.029244+00:00.

Case digest / 086cdab3047bf76d587f30cebb4443e23b6b84abcdc255fabb7c8f5882fd0d24