{"abstract":"Rings with a redundant collinear vertex are rejected as spiky.","category":"GIS polygon topology","checks":8,"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\".","contract_signature":"x","evaluation_group":"w2-gis-polygon-topology-simple-ring-validator","failed_approach":"The dot-product test is inverted, flagging straight pass-through vertices and missing real reversals.","family":"w2-gis-polygon-topology-simple-ring-validator-spike-direction-test","id":"FA-70526","implementations":{"attempt":{"sha256":"b1e86702acfbe3311a8d4d5e23a19796d7adaf72f5b1950e52d28408f7d4e9b3","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\nimport math\nN = 1\nobservations = []\ndef solve(x):\n    ring = x\n    if len(ring) < 4:\n        return 'too_few_points'\n    if ring[0] != ring[-1]:\n        return 'not_closed'\n    pts = ring[:-1]\n    n = len(pts)\n    for i in range(n):\n        if pts[i] == pts[(i + 1) % n]:\n            return 'repeated_point'\n    for i in range(n):\n        a, b, c = pts[i - 1], pts[i], pts[(i + 1) % n]\n        cross = (b[0] - a[0]) * (c[1] - b[1]) - (b[1] - a[1]) * (c[0] - b[0])\n        dot = (b[0] - a[0]) * (c[0] - b[0]) + (b[1] - a[1]) * (c[1] - b[1])\n        if cross == 0 and dot > 0:\n            return 'spike'\n    def orient(p, q, r):\n        v = (q[0] - p[0]) * (r[1] - p[1]) - (q[1] - p[1]) * (r[0] - p[0])\n        return (v > 0) - (v < 0)\n    def within(p, q, r):\n        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])\n    def hit(p1, p2, p3, p4):\n        o1, o2, o3, o4 = orient(p1, p2, p3), orient(p1, p2, p4), orient(p3, p4, p1), orient(p3, p4, p2)\n        if o1 * o2 < 0 and o3 * o4 < 0:\n            return True\n        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))\n    for i in range(n):\n        for j in range(i + 1, n):\n            if j == i + 1 or (i == 0 and j == n - 1):\n                continue\n            if hit(pts[i], pts[(i + 1) % n], pts[j], pts[(j + 1) % n]):\n                return 'self_intersection'\n    return 'valid'\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[('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'), ('boundary #7', [[0, 0], [10, 0], [10, 10]], 'too_few_points')], [('control #2', [[0, 0], [5, 0], [10, 0], [10, 10], [0, 10], [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 #11', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 5], [0, 12], [0, 0]], 'spike')], [('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 #15', [[0, 0], [10, 0], [10, 5], [2, 5], [6, 5], [6, 8], [0, 8], [0, 0]], 'spike')], [('control #2', [[0, 0], [5, 0], [10, 0], [10, 10], [0, 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 #17', [[0, 0], [10, 0], [10, 10], [0, 10], [4, 10], [0, 0]], 'spike')], [('control #2', [[0, 0], [5, 0], [10, 0], [10, 10], [0, 10], [0, 0]], 'valid'), ('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 #26', [[0, 0], [6, 0], [6, 6], [0, 6], [3, 6], [3, 9], [0, 9], [0, 0]], 'spike')]]\nfor label, args, expected in fixtures[N-1]:\n    check(label, solve(args), expected)\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"},"broken":{"sha256":"709c716880fce92440ef4e7189a46bd904b42621eb99d76232d8bf9b36b2803b","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\nimport math\nN = 1\nobservations = []\ndef solve(x):\n    ring = x\n    if len(ring) < 4:\n        return 'too_few_points'\n    if ring[0] != ring[-1]:\n        return 'not_closed'\n    pts = ring[:-1]\n    n = len(pts)\n    for i in range(n):\n        if pts[i] == pts[(i + 1) % n]:\n            return 'repeated_point'\n    for i in range(n):\n        a, b, c = pts[i - 1], pts[i], pts[(i + 1) % n]\n        cross = (b[0] - a[0]) * (c[1] - b[1]) - (b[1] - a[1]) * (c[0] - b[0])\n        dot = (b[0] - a[0]) * (c[0] - b[0]) + (b[1] - a[1]) * (c[1] - b[1])\n        if cross == 0:\n            return 'spike'\n    def orient(p, q, r):\n        v = (q[0] - p[0]) * (r[1] - p[1]) - (q[1] - p[1]) * (r[0] - p[0])\n        return (v > 0) - (v < 0)\n    def within(p, q, r):\n        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])\n    def hit(p1, p2, p3, p4):\n        o1, o2, o3, o4 = orient(p1, p2, p3), orient(p1, p2, p4), orient(p3, p4, p1), orient(p3, p4, p2)\n        if o1 * o2 < 0 and o3 * o4 < 0:\n            return True\n        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))\n    for i in range(n):\n        for j in range(i + 1, n):\n            if j == i + 1 or (i == 0 and j == n - 1):\n                continue\n            if hit(pts[i], pts[(i + 1) % n], pts[j], pts[(j + 1) % n]):\n                return 'self_intersection'\n    return 'valid'\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[('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'), ('boundary #7', [[0, 0], [10, 0], [10, 10]], 'too_few_points')], [('control #2', [[0, 0], [5, 0], [10, 0], [10, 10], [0, 10], [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 #11', [[0, 0], [10, 0], [10, 10], [0, 10], [0, 5], [0, 12], [0, 0]], 'spike')], [('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 #15', [[0, 0], [10, 0], [10, 5], [2, 5], [6, 5], [6, 8], [0, 8], [0, 0]], 'spike')], [('control #2', [[0, 0], [5, 0], [10, 0], [10, 10], [0, 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 #17', [[0, 0], [10, 0], [10, 10], [0, 10], [4, 10], [0, 0]], 'spike')], [('control #2', [[0, 0], [5, 0], [10, 0], [10, 10], [0, 10], [0, 0]], 'valid'), ('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 #26', [[0, 0], [6, 0], [6, 6], [0, 6], [3, 6], [3, 9], [0, 9], [0, 0]], 'spike')]]\nfor label, args, expected in fixtures[N-1]:\n    check(label, solve(args), expected)\nprint(json.dumps({\"observations\": observations, \"passed\": all(x[\"passed\"] for x in observations)}, ensure_ascii=False))\nraise SystemExit(0 if all(x[\"passed\"] for x in observations) else 1)\n"}},"limitations":"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.","method":"Deterministic executable model with adversarial boundary fixtures.","provenance":{"created_by":"Failure Map","dependencies":"Python standard library","family":"w2-gis-polygon-topology-simple-ring-validator-spike-direction-test","generated_at":"2026-09-29T14:48:21.605388+00:00","license":"CC0-1.0","python":"3.12.14","seed":1,"split":"open-access"},"relevance":"Topology validators run before editing, overlay and publishing; a wrong diagnosis sends users to fix the wrong vertex or lets broken rings through.","root_cause":"Any collinear vertex is treated as a spike without checking that the boundary reverses.","sha256":"b8b7673df0e8eeb54065013372ac679a1f6e052c43ddba1908f4612dd0691a52","title":"Simple ring validity diagnosis: spike direction test · case 01","variant":1,"variant_policy":"Five numbered records share a model and may reuse boundary fixtures.","verified":true,"visibility":"public","verification":{"attempt":{"elapsed_ms":38.581,"exit_code":1,"observations":[{"actual":"valid","check":"control #0","expected":"valid","passed":true},{"actual":"valid","check":"control #1","expected":"valid","passed":true},{"actual":"spike","check":"control #2","expected":"valid","passed":false},{"actual":"self_intersection","check":"regression #3","expected":"self_intersection","passed":true},{"actual":"self_intersection","check":"regression #4","expected":"spike","passed":false},{"actual":"not_closed","check":"regression #5","expected":"not_closed","passed":true},{"actual":"valid","check":"boundary #6","expected":"valid","passed":true},{"actual":"too_few_points","check":"boundary #7","expected":"too_few_points","passed":true}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"control #0\", \"actual\": \"valid\", \"expected\": \"valid\", \"passed\": true}, {\"check\": \"control #1\", \"actual\": \"valid\", \"expected\": \"valid\", \"passed\": true}, {\"check\": \"control #2\", \"actual\": \"spike\", \"expected\": \"valid\", \"passed\": false}, {\"check\": \"regression #3\", \"actual\": \"self_intersection\", \"expected\": \"self_intersection\", \"passed\": true}, {\"check\": \"regression #4\", \"actual\": \"self_intersection\", \"expected\": \"spike\", \"passed\": false}, {\"check\": \"regression #5\", \"actual\": \"not_closed\", \"expected\": \"not_closed\", \"passed\": true}, {\"check\": \"boundary #6\", \"actual\": \"valid\", \"expected\": \"valid\", \"passed\": true}, {\"check\": \"boundary #7\", \"actual\": \"too_few_points\", \"expected\": \"too_few_points\", \"passed\": true}], \"passed\": false}\n"},"broken":{"elapsed_ms":38.139,"exit_code":1,"observations":[{"actual":"valid","check":"control #0","expected":"valid","passed":true},{"actual":"valid","check":"control #1","expected":"valid","passed":true},{"actual":"spike","check":"control #2","expected":"valid","passed":false},{"actual":"self_intersection","check":"regression #3","expected":"self_intersection","passed":true},{"actual":"spike","check":"regression #4","expected":"spike","passed":true},{"actual":"not_closed","check":"regression #5","expected":"not_closed","passed":true},{"actual":"valid","check":"boundary #6","expected":"valid","passed":true},{"actual":"too_few_points","check":"boundary #7","expected":"too_few_points","passed":true}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"control #0\", \"actual\": \"valid\", \"expected\": \"valid\", \"passed\": true}, {\"check\": \"control #1\", \"actual\": \"valid\", \"expected\": \"valid\", \"passed\": true}, {\"check\": \"control #2\", \"actual\": \"spike\", \"expected\": \"valid\", \"passed\": false}, {\"check\": \"regression #3\", \"actual\": \"self_intersection\", \"expected\": \"self_intersection\", \"passed\": true}, {\"check\": \"regression #4\", \"actual\": \"spike\", \"expected\": \"spike\", \"passed\": true}, {\"check\": \"regression #5\", \"actual\": \"not_closed\", \"expected\": \"not_closed\", \"passed\": true}, {\"check\": \"boundary #6\", \"actual\": \"valid\", \"expected\": \"valid\", \"passed\": true}, {\"check\": \"boundary #7\", \"actual\": \"too_few_points\", \"expected\": \"too_few_points\", \"passed\": true}], \"passed\": false}\n"}},"member_only":{"stages":["fixed"],"fields":["implementations.fixed","verification.fixed","harness","repair"],"note":"The verified repair, its recorded checks, the repair description, and the scoring harness are available to members."}}