{"abstract":"A target that is both too high and too far reports \"unreachable\" instead of \"out-of-stroke\".","category":"Inverse kinematics solvers","checks":7,"contract":"Input [l1,l2,d0,stroke,px,py,pz,yaw,arm]. The quill extends downward: d3=d0-pz must lie in [0,stroke] else \"out-of-stroke\" (checked first). Planar reach uses cos t2=(px^2+py^2-l1^2-l2^2)/(2 l1 l2); |cos|>1 returns \"unreachable\". arm \"right\" takes t2<=0. Tool yaw (degrees) equals t1+t2+t4. Return [t1,t2,d3,t4] with angles in degrees wrapped to (-180,180], all rounded to 4 places.","contract_signature":"x","evaluation_group":"w2-inverse_kinematics_solvers-scara-arm","failed_approach":"Merging both checks under \"unreachable\" loses the stroke status entirely.","family":"w2-inverse_kinematics_solvers-scara-arm-status-precedence","id":"FA-88051","implementations":{"attempt":{"sha256":"ffaff952b1a9398e87665ddf6a6c4a7ffc5800ab0d980244f870ab9ac2abc298","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\nimport math\nN = 1\nobservations = []\ndef solve(x):\n    l1,l2,d0,stroke,px,py,pz,yaw,arm=x\n    d3=d0-pz\n    r2=px*px+py*py\n    c=(r2-l1*l1-l2*l2)/(2.0*l1*l2)\n    if d3<0 or d3>stroke or c>1.0 or c<-1.0: return 'unreachable'\n    s=math.sqrt(1.0-c*c)\n    if arm=='right': s=-s\n    t2=math.atan2(s,c)\n    t1=math.atan2(py,px)-math.atan2(l2*s,l1+l2*c)\n    t4=math.radians(yaw)-t1-t2\n    res=[]\n    for t in (t1,t2,t4):\n        d=math.degrees(t)\n        while d>180.0: d-=360.0\n        while d<=-180.0: d+=360.0\n        res.append(round(d,4))\n    return [res[0],res[1],round(d3,4),res[2]]\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['left arm nominal', [[4, 3, 10, 8, 5, 2, 6, 30, 'left']], [-11.5172, 80.4059, 4, -38.8887]], ['right arm nominal', [[4, 3, 10, 8, 5, 2, 6, 30, 'right']], [55.12, -80.4059, 4, 55.2859]], ['quill fully retracted', [[4, 3, 10, 8, 3, 4, 10, 0, 'left']], [16.2602, 90.0, 0, -106.2602]], ['quill fully extended', [[4, 3, 10, 8, 3, 4, 2, 0, 'right']], [90.0, -90.0, 8, 0.0]], ['target above quill home', [[4, 3, 10, 8, 3, 4, 12, 0, 'left']], 'out-of-stroke'], ['below stroke', [[4, 3, 10, 8, 3, 4, 1, 0, 'left']], 'out-of-stroke'], ['out of stroke and reach', [[4, 3, 10, 8, 9, 0, 12, 0, 'left']], 'out-of-stroke']], [['quill fully extended', [[4, 3, 10, 8, 3, 4, 2, 0, 'right']], [90.0, -90.0, 8, 0.0]], ['target above quill home', [[4, 3, 10, 8, 3, 4, 12, 0, 'left']], 'out-of-stroke'], ['below stroke', [[4, 3, 10, 8, 3, 4, 1, 0, 'left']], 'out-of-stroke'], ['out of stroke and reach', [[4, 3, 10, 8, 9, 0, 12, 0, 'left']], 'out-of-stroke'], ['out of reach only', [[4, 3, 10, 8, 9, 0, 5, 0, 'left']], 'unreachable'], ['fully stretched', [[4, 3, 10, 8, 7, 0, 5, 45, 'left']], [0.0, 0.0, 5, 45.0]], ['multi-turn yaw', [[4, 3, 10, 8, -2, 5, 4, 720, 'right']], [145.12, -80.4059, 6, -64.7141]]], [['out of stroke and reach', [[4, 3, 10, 8, 9, 0, 12, 0, 'left']], 'out-of-stroke'], ['out of reach only', [[4, 3, 10, 8, 9, 0, 5, 0, 'left']], 'unreachable'], ['fully stretched', [[4, 3, 10, 8, 7, 0, 5, 45, 'left']], [0.0, 0.0, 5, 45.0]], ['multi-turn yaw', [[4, 3, 10, 8, -2, 5, 4, 720, 'right']], [145.12, -80.4059, 6, -64.7141]], ['negative yaw', [[4, 3, 10, 8, -4, -3, 4, -170, 'left']], [180.0, 90.0, 6, -80.0]], ['yaw 180 seam', [[4, 3, 10, 8, 0, 6, 7, 180, 'right']], [116.3843, -62.7204, 3, 126.3361]], ['reach between r and r2', [[1, 1, 10, 8, 1.2, 1.1, 5, 0, 'left']], [6.9932, 71.0344, 5, -78.0277]]], [['target above quill home', [[4, 3, 10, 8, 3, 4, 12, 0, 'left']], 'out-of-stroke'], ['out of stroke and reach', [[4, 3, 10, 8, 9, 0, 12, 0, 'left']], 'out-of-stroke'], ['yaw 180 seam', [[4, 3, 10, 8, 0, 6, 7, 180, 'right']], [116.3843, -62.7204, 3, 126.3361]], ['reach between r and r2', [[1, 1, 10, 8, 1.2, 1.1, 5, 0, 'left']], [6.9932, 71.0344, 5, -78.0277]], ['back quadrant', [[5, 4, 10, 8, -6, -2, 3, 250, 'right']], [-122.3482, -91.4325, 7, 103.7807]], ['near base hole', [[4, 3, 10, 8, 0.5, 0.2, 5, 0, 'left']], 'unreachable'], ['yaw -540', [[4, 3, 10, 8, 2, -5, 9, -540, 'left']], [-101.5172, 80.4059, 1, -158.8887]]], [['left arm nominal', [[4, 3, 10, 8, 5, 2, 6, 30, 'left']], [-11.5172, 80.4059, 4, -38.8887]], ['right arm nominal', [[4, 3, 10, 8, 5, 2, 6, 30, 'right']], [55.12, -80.4059, 4, 55.2859]], ['quill fully retracted', [[4, 3, 10, 8, 3, 4, 10, 0, 'left']], [16.2602, 90.0, 0, -106.2602]], ['below stroke', [[4, 3, 10, 8, 3, 4, 1, 0, 'left']], 'out-of-stroke'], ['out of stroke and reach', [[4, 3, 10, 8, 9, 0, 12, 0, 'left']], 'out-of-stroke'], ['near base hole', [[4, 3, 10, 8, 0.5, 0.2, 5, 0, 'left']], 'unreachable'], ['yaw -540', [[4, 3, 10, 8, 2, -5, 9, -540, 'left']], [-101.5172, 80.4059, 1, -158.8887]]]]\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":"3b2c15007362b51065e3de1a3bd4e2e5b9df70dbd6f68aebf506478894f17d01","source":"\"\"\"Failure Map reference implementation. Python standard library only.\"\"\"\nimport json\nimport math\nN = 1\nobservations = []\ndef solve(x):\n    l1,l2,d0,stroke,px,py,pz,yaw,arm=x\n    d3=d0-pz\n    r2=px*px+py*py\n    c=(r2-l1*l1-l2*l2)/(2.0*l1*l2)\n    if c>1.0 or c<-1.0: return 'unreachable'\n    if d3<0 or d3>stroke: return 'out-of-stroke'\n    s=math.sqrt(1.0-c*c)\n    if arm=='right': s=-s\n    t2=math.atan2(s,c)\n    t1=math.atan2(py,px)-math.atan2(l2*s,l1+l2*c)\n    t4=math.radians(yaw)-t1-t2\n    res=[]\n    for t in (t1,t2,t4):\n        d=math.degrees(t)\n        while d>180.0: d-=360.0\n        while d<=-180.0: d+=360.0\n        res.append(round(d,4))\n    return [res[0],res[1],round(d3,4),res[2]]\ndef check(label, actual, expected):\n    observations.append({\"check\": label, \"actual\": actual, \"expected\": expected, \"passed\": actual == expected})\nfixtures = [[['left arm nominal', [[4, 3, 10, 8, 5, 2, 6, 30, 'left']], [-11.5172, 80.4059, 4, -38.8887]], ['right arm nominal', [[4, 3, 10, 8, 5, 2, 6, 30, 'right']], [55.12, -80.4059, 4, 55.2859]], ['quill fully retracted', [[4, 3, 10, 8, 3, 4, 10, 0, 'left']], [16.2602, 90.0, 0, -106.2602]], ['quill fully extended', [[4, 3, 10, 8, 3, 4, 2, 0, 'right']], [90.0, -90.0, 8, 0.0]], ['target above quill home', [[4, 3, 10, 8, 3, 4, 12, 0, 'left']], 'out-of-stroke'], ['below stroke', [[4, 3, 10, 8, 3, 4, 1, 0, 'left']], 'out-of-stroke'], ['out of stroke and reach', [[4, 3, 10, 8, 9, 0, 12, 0, 'left']], 'out-of-stroke']], [['quill fully extended', [[4, 3, 10, 8, 3, 4, 2, 0, 'right']], [90.0, -90.0, 8, 0.0]], ['target above quill home', [[4, 3, 10, 8, 3, 4, 12, 0, 'left']], 'out-of-stroke'], ['below stroke', [[4, 3, 10, 8, 3, 4, 1, 0, 'left']], 'out-of-stroke'], ['out of stroke and reach', [[4, 3, 10, 8, 9, 0, 12, 0, 'left']], 'out-of-stroke'], ['out of reach only', [[4, 3, 10, 8, 9, 0, 5, 0, 'left']], 'unreachable'], ['fully stretched', [[4, 3, 10, 8, 7, 0, 5, 45, 'left']], [0.0, 0.0, 5, 45.0]], ['multi-turn yaw', [[4, 3, 10, 8, -2, 5, 4, 720, 'right']], [145.12, -80.4059, 6, -64.7141]]], [['out of stroke and reach', [[4, 3, 10, 8, 9, 0, 12, 0, 'left']], 'out-of-stroke'], ['out of reach only', [[4, 3, 10, 8, 9, 0, 5, 0, 'left']], 'unreachable'], ['fully stretched', [[4, 3, 10, 8, 7, 0, 5, 45, 'left']], [0.0, 0.0, 5, 45.0]], ['multi-turn yaw', [[4, 3, 10, 8, -2, 5, 4, 720, 'right']], [145.12, -80.4059, 6, -64.7141]], ['negative yaw', [[4, 3, 10, 8, -4, -3, 4, -170, 'left']], [180.0, 90.0, 6, -80.0]], ['yaw 180 seam', [[4, 3, 10, 8, 0, 6, 7, 180, 'right']], [116.3843, -62.7204, 3, 126.3361]], ['reach between r and r2', [[1, 1, 10, 8, 1.2, 1.1, 5, 0, 'left']], [6.9932, 71.0344, 5, -78.0277]]], [['target above quill home', [[4, 3, 10, 8, 3, 4, 12, 0, 'left']], 'out-of-stroke'], ['out of stroke and reach', [[4, 3, 10, 8, 9, 0, 12, 0, 'left']], 'out-of-stroke'], ['yaw 180 seam', [[4, 3, 10, 8, 0, 6, 7, 180, 'right']], [116.3843, -62.7204, 3, 126.3361]], ['reach between r and r2', [[1, 1, 10, 8, 1.2, 1.1, 5, 0, 'left']], [6.9932, 71.0344, 5, -78.0277]], ['back quadrant', [[5, 4, 10, 8, -6, -2, 3, 250, 'right']], [-122.3482, -91.4325, 7, 103.7807]], ['near base hole', [[4, 3, 10, 8, 0.5, 0.2, 5, 0, 'left']], 'unreachable'], ['yaw -540', [[4, 3, 10, 8, 2, -5, 9, -540, 'left']], [-101.5172, 80.4059, 1, -158.8887]]], [['left arm nominal', [[4, 3, 10, 8, 5, 2, 6, 30, 'left']], [-11.5172, 80.4059, 4, -38.8887]], ['right arm nominal', [[4, 3, 10, 8, 5, 2, 6, 30, 'right']], [55.12, -80.4059, 4, 55.2859]], ['quill fully retracted', [[4, 3, 10, 8, 3, 4, 10, 0, 'left']], [16.2602, 90.0, 0, -106.2602]], ['below stroke', [[4, 3, 10, 8, 3, 4, 1, 0, 'left']], 'out-of-stroke'], ['out of stroke and reach', [[4, 3, 10, 8, 9, 0, 12, 0, 'left']], 'out-of-stroke'], ['near base hole', [[4, 3, 10, 8, 0.5, 0.2, 5, 0, 'left']], 'unreachable'], ['yaw -540', [[4, 3, 10, 8, 2, -5, 9, -540, 'left']], [-101.5172, 80.4059, 1, -158.8887]]]]\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":"Deterministic planar or low-dimensional teaching model with a stipulated convention; not a general robotics library. 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-inverse_kinematics_solvers-scara-arm-status-precedence","generated_at":"2026-09-29T14:51:04.559127+00:00","license":"CC0-1.0","python":"3.12.14","seed":1,"split":"open-access"},"relevance":"Inverse kinematics code turns task-space goals into joint commands; a wrong branch, sign, limit or update order sends a real arm to the wrong pose.","root_cause":"The stroke check runs after the planar reach check, contradicting the stated precedence.","sha256":"18eabe3fe584d5f098ea81b6779ee9007e7494e64b365ad8c4337500d44e0b25","title":"SCARA reports reach failure before stroke failure · 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":40.859,"exit_code":1,"observations":[{"actual":[-11.5172,80.4059,4,-38.8887],"check":"left arm nominal","expected":[-11.5172,80.4059,4,-38.8887],"passed":true},{"actual":[55.12,-80.4059,4,55.2859],"check":"right arm nominal","expected":[55.12,-80.4059,4,55.2859],"passed":true},{"actual":[16.2602,90.0,0,-106.2602],"check":"quill fully retracted","expected":[16.2602,90.0,0,-106.2602],"passed":true},{"actual":[90.0,-90.0,8,0.0],"check":"quill fully extended","expected":[90.0,-90.0,8,0.0],"passed":true},{"actual":"unreachable","check":"target above quill home","expected":"out-of-stroke","passed":false},{"actual":"unreachable","check":"below stroke","expected":"out-of-stroke","passed":false},{"actual":"unreachable","check":"out of stroke and reach","expected":"out-of-stroke","passed":false}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"left arm nominal\", \"actual\": [-11.5172, 80.4059, 4, -38.8887], \"expected\": [-11.5172, 80.4059, 4, -38.8887], \"passed\": true}, {\"check\": \"right arm nominal\", \"actual\": [55.12, -80.4059, 4, 55.2859], \"expected\": [55.12, -80.4059, 4, 55.2859], \"passed\": true}, {\"check\": \"quill fully retracted\", \"actual\": [16.2602, 90.0, 0, -106.2602], \"expected\": [16.2602, 90.0, 0, -106.2602], \"passed\": true}, {\"check\": \"quill fully extended\", \"actual\": [90.0, -90.0, 8, 0.0], \"expected\": [90.0, -90.0, 8, 0.0], \"passed\": true}, {\"check\": \"target above quill home\", \"actual\": \"unreachable\", \"expected\": \"out-of-stroke\", \"passed\": false}, {\"check\": \"below stroke\", \"actual\": \"unreachable\", \"expected\": \"out-of-stroke\", \"passed\": false}, {\"check\": \"out of stroke and reach\", \"actual\": \"unreachable\", \"expected\": \"out-of-stroke\", \"passed\": false}], \"passed\": false}\n"},"broken":{"elapsed_ms":38.832,"exit_code":1,"observations":[{"actual":[-11.5172,80.4059,4,-38.8887],"check":"left arm nominal","expected":[-11.5172,80.4059,4,-38.8887],"passed":true},{"actual":[55.12,-80.4059,4,55.2859],"check":"right arm nominal","expected":[55.12,-80.4059,4,55.2859],"passed":true},{"actual":[16.2602,90.0,0,-106.2602],"check":"quill fully retracted","expected":[16.2602,90.0,0,-106.2602],"passed":true},{"actual":[90.0,-90.0,8,0.0],"check":"quill fully extended","expected":[90.0,-90.0,8,0.0],"passed":true},{"actual":"out-of-stroke","check":"target above quill home","expected":"out-of-stroke","passed":true},{"actual":"out-of-stroke","check":"below stroke","expected":"out-of-stroke","passed":true},{"actual":"unreachable","check":"out of stroke and reach","expected":"out-of-stroke","passed":false}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"left arm nominal\", \"actual\": [-11.5172, 80.4059, 4, -38.8887], \"expected\": [-11.5172, 80.4059, 4, -38.8887], \"passed\": true}, {\"check\": \"right arm nominal\", \"actual\": [55.12, -80.4059, 4, 55.2859], \"expected\": [55.12, -80.4059, 4, 55.2859], \"passed\": true}, {\"check\": \"quill fully retracted\", \"actual\": [16.2602, 90.0, 0, -106.2602], \"expected\": [16.2602, 90.0, 0, -106.2602], \"passed\": true}, {\"check\": \"quill fully extended\", \"actual\": [90.0, -90.0, 8, 0.0], \"expected\": [90.0, -90.0, 8, 0.0], \"passed\": true}, {\"check\": \"target above quill home\", \"actual\": \"out-of-stroke\", \"expected\": \"out-of-stroke\", \"passed\": true}, {\"check\": \"below stroke\", \"actual\": \"out-of-stroke\", \"expected\": \"out-of-stroke\", \"passed\": true}, {\"check\": \"out of stroke and reach\", \"actual\": \"unreachable\", \"expected\": \"out-of-stroke\", \"passed\": false}], \"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."}}