{"abstract":"The tool heading is off by the sum of the shoulder and elbow angles.","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":"Subtracting only t2 still leaves the shoulder rotation in the tool heading.","family":"w2-inverse_kinematics_solvers-scara-arm-screw-yaw-coupling","id":"FA-88036","implementations":{"attempt":{"sha256":"8a73c9c05be0571a464a2c47a3d97d05066b6b3c247461666510444b265c98b9","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    if d3<0 or d3>stroke: return 'out-of-stroke'\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    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)-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']], [['right arm nominal', [[4, 3, 10, 8, 5, 2, 6, 30, 'right']], [55.12, -80.4059, 4, 55.2859]], ['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]]], [['quill fully retracted', [[4, 3, 10, 8, 3, 4, 10, 0, 'left']], [16.2602, 90.0, 0, -106.2602]], ['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]]], [['quill fully extended', [[4, 3, 10, 8, 3, 4, 2, 0, 'right']], [90.0, -90.0, 8, 0.0]], ['multi-turn yaw', [[4, 3, 10, 8, -2, 5, 4, 720, 'right']], [145.12, -80.4059, 6, -64.7141]], ['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]], ['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]], ['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":"eed2f6a8df89055a76a0e7a213ee6dfd448a88875616206ea37111961334e3ff","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    if d3<0 or d3>stroke: return 'out-of-stroke'\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    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)\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']], [['right arm nominal', [[4, 3, 10, 8, 5, 2, 6, 30, 'right']], [55.12, -80.4059, 4, 55.2859]], ['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]]], [['quill fully retracted', [[4, 3, 10, 8, 3, 4, 10, 0, 'left']], [16.2602, 90.0, 0, -106.2602]], ['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]]], [['quill fully extended', [[4, 3, 10, 8, 3, 4, 2, 0, 'right']], [90.0, -90.0, 8, 0.0]], ['multi-turn yaw', [[4, 3, 10, 8, -2, 5, 4, 720, 'right']], [145.12, -80.4059, 6, -64.7141]], ['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]], ['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]], ['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-screw-yaw-coupling","generated_at":"2026-09-29T14:51:04.430476+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":"t4 is set to the requested yaw directly, forgetting that t1 and t2 also rotate the tool.","sha256":"217af27110a906cc3ebc5a7db242a1f6834f8f2daad8c91c635a42b16429f153","title":"SCARA tool roll ignores the arm joints · 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.382,"exit_code":1,"observations":[{"actual":[-11.5172,80.4059,4,-50.4059],"check":"left arm nominal","expected":[-11.5172,80.4059,4,-38.8887],"passed":false},{"actual":[55.12,-80.4059,4,110.4059],"check":"right arm nominal","expected":[55.12,-80.4059,4,55.2859],"passed":false},{"actual":[16.2602,90.0,0,-90.0],"check":"quill fully retracted","expected":[16.2602,90.0,0,-106.2602],"passed":false},{"actual":[90.0,-90.0,8,90.0],"check":"quill fully extended","expected":[90.0,-90.0,8,0.0],"passed":false},{"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":"out-of-stroke","check":"out of stroke and reach","expected":"out-of-stroke","passed":true}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"left arm nominal\", \"actual\": [-11.5172, 80.4059, 4, -50.4059], \"expected\": [-11.5172, 80.4059, 4, -38.8887], \"passed\": false}, {\"check\": \"right arm nominal\", \"actual\": [55.12, -80.4059, 4, 110.4059], \"expected\": [55.12, -80.4059, 4, 55.2859], \"passed\": false}, {\"check\": \"quill fully retracted\", \"actual\": [16.2602, 90.0, 0, -90.0], \"expected\": [16.2602, 90.0, 0, -106.2602], \"passed\": false}, {\"check\": \"quill fully extended\", \"actual\": [90.0, -90.0, 8, 90.0], \"expected\": [90.0, -90.0, 8, 0.0], \"passed\": false}, {\"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\": \"out-of-stroke\", \"expected\": \"out-of-stroke\", \"passed\": true}], \"passed\": false}\n"},"broken":{"elapsed_ms":38.688,"exit_code":1,"observations":[{"actual":[-11.5172,80.4059,4,30.0],"check":"left arm nominal","expected":[-11.5172,80.4059,4,-38.8887],"passed":false},{"actual":[55.12,-80.4059,4,30.0],"check":"right arm nominal","expected":[55.12,-80.4059,4,55.2859],"passed":false},{"actual":[16.2602,90.0,0,0.0],"check":"quill fully retracted","expected":[16.2602,90.0,0,-106.2602],"passed":false},{"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":"out-of-stroke","check":"out of stroke and reach","expected":"out-of-stroke","passed":true}],"passed":false,"stderr":"","stdout":"{\"observations\": [{\"check\": \"left arm nominal\", \"actual\": [-11.5172, 80.4059, 4, 30.0], \"expected\": [-11.5172, 80.4059, 4, -38.8887], \"passed\": false}, {\"check\": \"right arm nominal\", \"actual\": [55.12, -80.4059, 4, 30.0], \"expected\": [55.12, -80.4059, 4, 55.2859], \"passed\": false}, {\"check\": \"quill fully retracted\", \"actual\": [16.2602, 90.0, 0, 0.0], \"expected\": [16.2602, 90.0, 0, -106.2602], \"passed\": false}, {\"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\": \"out-of-stroke\", \"expected\": \"out-of-stroke\", \"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."}}