diff --git a/indra/llappearance/llavatarappearance.cpp b/indra/llappearance/llavatarappearance.cpp
index 46bb29c7f5891343b3e7dec6400b3cf54d9aba07..aa2afc5a28335423287e462f97fec5547ef390a5 100644
--- a/indra/llappearance/llavatarappearance.cpp
+++ b/indra/llappearance/llavatarappearance.cpp
@@ -728,7 +728,7 @@ BOOL LLAvatarAppearance::buildSkeleton(const LLAvatarSkeletonInfo *info)
 		LL_ERRS() << "Can't allocate " << info->mNumBones << " joints" << LL_ENDL;
 		return FALSE;
 	}
-	
+
 	// allocate volumes
 	if (info->mNumCollisionVolumes)
 	{
diff --git a/indra/llappearance/llpolyskeletaldistortion.cpp b/indra/llappearance/llpolyskeletaldistortion.cpp
index 360f17508fdba828c8431a7dc23789d19f95a1f7..8db212231a711387e6d113b5b5170af011fde01e 100644
--- a/indra/llappearance/llpolyskeletaldistortion.cpp
+++ b/indra/llappearance/llpolyskeletaldistortion.cpp
@@ -233,7 +233,7 @@ void LLPolySkeletalDistortion::apply( ESex avatar_sex )
 
     if (mLastWeight != effective_weight && !mIsAnimating)
     {
-        mAvatar->setSkeletonSerialNum(mAvatar->getSkeletonSerialNum() + 1);
+        mAvatar->bumpSkeletonSerialNum();
     }
     mLastWeight = effective_weight;
 }
diff --git a/indra/llcharacter/CMakeLists.txt b/indra/llcharacter/CMakeLists.txt
index 2001259f10399f289abc426de0070bb552fea63a..e386a1ae66b73aa702ab3809ec2f129a53abb585 100644
--- a/indra/llcharacter/CMakeLists.txt
+++ b/indra/llcharacter/CMakeLists.txt
@@ -104,6 +104,11 @@ if (LL_TESTS)
     PROPERTIES
     LL_TEST_ADDITIONAL_LIBRARIES "${BOOST_THREAD_LIBRARY}"
   )
+  set_source_files_properties(
+    llik.cpp
+    PROPERTIES
+    LL_TEST_ADDITIONAL_SOURCE_FILES lljoint.cpp
+  )
   LL_ADD_PROJECT_UNIT_TESTS(llcharacter "${llcharacter_TEST_SOURCE_FILES}")
 endif (LL_TESTS)
 
diff --git a/indra/llcharacter/llcharacter.h b/indra/llcharacter/llcharacter.h
index 90affcc637a7fb5f43f64b9637584783fa223bed..1f2456ef6643cba8d81526583ed9d62123d17120 100644
--- a/indra/llcharacter/llcharacter.h
+++ b/indra/llcharacter/llcharacter.h
@@ -254,9 +254,9 @@ class LLCharacter
 
 	U32				getAppearanceSerialNum() const		{ return mAppearanceSerialNum; }
 	void			setAppearanceSerialNum( U32 num )	{ mAppearanceSerialNum = num; }
-	
+
 	U32				getSkeletonSerialNum() const		{ return mSkeletonSerialNum; }
-	void			setSkeletonSerialNum( U32 num )	{ mSkeletonSerialNum = num; }
+	void			bumpSkeletonSerialNum()	{ ++mSkeletonSerialNum; }
 
 	static std::vector< LLCharacter* > sInstances;
 	static BOOL sAllowInstancesChange ; //debug use
diff --git a/indra/llcharacter/llik.cpp b/indra/llcharacter/llik.cpp
index 91116949e81aa6b5f0ddd48cb3dcc80df6ec39ab..7263bbdc89e02b3cc49eea09d1d8a9b63e6eba59 100644
--- a/indra/llcharacter/llik.cpp
+++ b/indra/llcharacter/llik.cpp
@@ -35,6 +35,7 @@
 
 #include "llik.h"
 
+#include "lljoint.h"
 #include "llmath.h"
 #include "v3math.h"
 #include "llquaternion.h"
@@ -147,27 +148,33 @@ F32 compute_clamped_angle(F32 angle, F32 min_angle, F32 max_angle)
 void LLIK::Joint::Config::setLocalPos(const LLVector3& pos)
 {
     mLocalPos = pos;
-    mFlags |= FLAG_LOCAL_POS;
+    mFlags |= CONFIG_FLAG_LOCAL_POS;
 }
 
 void LLIK::Joint::Config::setLocalRot(const LLQuaternion& rot)
 {
     mLocalRot = rot;
     mLocalRot.normalize();
-    mFlags |= FLAG_LOCAL_ROT;
+    mFlags |= CONFIG_FLAG_LOCAL_ROT;
+}
+
+void LLIK::Joint::Config::setLocalScale(const LLVector3& scale)
+{
+    mLocalScale = scale;
+    mFlags |= CONFIG_FLAG_LOCAL_SCALE;
 }
 
 void LLIK::Joint::Config::setTargetPos(const LLVector3& pos)
 {
     mTargetPos = pos;
-    mFlags |= FLAG_TARGET_POS;
+    mFlags |= CONFIG_FLAG_TARGET_POS;
 }
 
 void LLIK::Joint::Config::setTargetRot(const LLQuaternion& rot)
 {
     mTargetRot = rot;
     mTargetRot.normalize();
-    mFlags |= FLAG_TARGET_ROT;
+    mFlags |= CONFIG_FLAG_TARGET_ROT;
 }
 
 void LLIK::Joint::Config::updateFrom(const Config& other_config)
@@ -182,19 +189,23 @@ void LLIK::Joint::Config::updateFrom(const Config& other_config)
         // find and apply all parameters in other_config
         if (other_config.hasLocalPos())
         {
-            setLocalPos(other_config.getLocalPos());
+            setLocalPos(other_config.mLocalPos);
         }
         if (other_config.hasLocalRot())
         {
-            setLocalRot(other_config.getLocalRot());
+            setLocalRot(other_config.mLocalRot);
         }
         if (other_config.hasTargetPos())
         {
-            setTargetPos(other_config.getTargetPos());
+            setTargetPos(other_config.mTargetPos);
         }
         if (other_config.hasTargetRot())
         {
-            setTargetRot(other_config.getTargetRot());
+            setTargetRot(other_config.mTargetRot);
+        }
+        if (other_config.hasLocalScale())
+        {
+            setLocalScale(other_config.mLocalScale);
         }
         if (other_config.constraintIsDisabled())
         {
@@ -856,11 +867,22 @@ void LLIK::DoubleLimitedHinge::dumpConfig() const
 }
 #endif
 
-LLIK::Joint::Joint(S16 id, const LLVector3& local_pos, const LLVector3& bone)
-    :   mDefaultLocalPos(local_pos), mBone(bone), mID(id)
+LLIK::Joint::Joint(LLJoint* info_ptr) : mInfoPtr(info_ptr)
 {
-    mLocalPos = mDefaultLocalPos;
+    assert(info_ptr != nullptr);
+    mID = mInfoPtr->getJointNum();
+    resetFromInfo();
+}
+
+void LLIK::Joint::resetFromInfo()
+{
+    LLVector3 scale = mInfoPtr->getScale();
+    mLocalPos = mInfoPtr->getPosition().scaledVec(scale);
+    mBone = mInfoPtr->getEnd().scaledVec(scale);
     mLocalPosLength = mLocalPos.length();
+    // This is Correct: we do NOT store info scale in mLocalScale.
+    // mLocalScale represents Puppetry's tweak on  top of whatever is set in the info.
+    mLocalScale.set(1.0f, 1.0f, 1.0f);
 }
 
 void LLIK::Joint::addChild(const ptr_t& child)
@@ -890,14 +912,14 @@ void LLIK::Joint::setParent(const ptr_t& parent)
         // Whatever orientation it has at the start of IK will be its final,
         // which is why we flag it as "locked".  This also simplifies logic
         // elsewhere: in a few places we assume any non-locked Joint has a parent.
-        mConfigFlags = FLAG_LOCAL_ROT;
+        mIkFlags = IK_FLAG_LOCAL_ROT_LOCKED;
     }
     reset();
 }
 
 void LLIK::Joint::reset()
 {
-    mLocalPos = mDefaultLocalPos;
+    resetFromInfo();
     // Note: we don't bother to enforce localRotLocked() here because any call
     // to reset() is expected to be outside the Solver IK iterations.
     mLocalRot = LLQuaternion::DEFAULT;
@@ -977,14 +999,6 @@ F32 LLIK::Joint::recursiveComputeLongestChainLength(F32 length) const
     return longest_length;
 }
 
-void LLIK::Joint::reconfigure(const LLVector3& local_pos, const LLVector3& bone)
-{
-    mDefaultLocalPos = local_pos;
-    mLocalPos = local_pos;
-    mBone = bone;
-    mLocalPosLength = mLocalPos.length();
-}
-
 LLVector3 LLIK::Joint::computeEndTargetPos() const
 {
     // Note: we expect this Joint has either: a target,
@@ -1299,6 +1313,8 @@ void LLIK::Joint::shiftPos(const LLVector3& shift)
 
 void LLIK::Joint::setConfig(const Config& config)
 {
+    // we only remember the config here
+    // it gets applied later when we build the chains
     mConfig = &config;
     mConfigFlags = mConfig ? mConfig->getFlags() : 0;
 }
@@ -1306,13 +1322,15 @@ void LLIK::Joint::setConfig(const Config& config)
 void LLIK::Joint::resetFlags()
 {
     mConfig = nullptr;
-    // root Joint always has FLAG_LOCAL_ROT bit set
-    mConfigFlags = mParent ? 0 : FLAG_LOCAL_ROT;
+    mConfigFlags = 0;
+    // root Joint always has IK_FLAG_LOCAL_ROT_LOCKED set
+    mIkFlags = mParent ? 0 : IK_FLAG_LOCAL_ROT_LOCKED;
 }
 
 void LLIK::Joint::lockLocalRot(const LLQuaternion& local_rot)
 {
     mLocalRot = local_rot;
+    mIkFlags |= IK_FLAG_LOCAL_ROT_LOCKED;
     activate();
     if (!mParent)
     {
@@ -1384,7 +1402,8 @@ void LLIK::Joint::setWorldPos(const LLVector3& pos)
 // this should only be called once before starting IK algorithm iterations
 void LLIK::Joint::setLocalPos(const LLVector3& pos)
 {
-    mLocalPos = pos;
+    mLocalPos = pos.scaledVec(mLocalScale);
+    mLocalPosLength = mLocalPos.length();
     if (!mParent)
     {
         mPos = mLocalPos;
@@ -1407,6 +1426,46 @@ void LLIK::Joint::setLocalRot(const LLQuaternion& new_local_rot)
     }
 }
 
+// only call this if you know what you're doing
+// this should only be called once before starting IK algorithm iterations
+void LLIK::Joint::setLocalScale(const LLVector3& scale)
+{
+    // compute final scale adustment to applly to mLocalPos and mBone
+    constexpr F32 MIN_INVERTABLE_SCALE = 1.0e-15f;
+    LLVector3 re_scale;
+    for (S32 i = 0; i < 3; ++i)
+    {
+        // verify mLocalScale component to avoid introducing NaN
+        re_scale[i] = (mLocalScale[i] > MIN_INVERTABLE_SCALE) ?  scale[i] / mLocalScale[i] : 0.0f;
+    }
+    // We remember the final scale adjustment for later...
+    mLocalScale = scale;
+    // ...and apply it immediately onto mLocalPos and mBone.
+    mBone.scaleVec(re_scale);
+    mLocalPos.scaleVec(re_scale);
+    mLocalPosLength = mLocalPos.length();
+}
+
+// returns local_pos with any non-uniform scale from the "info" removed.
+LLVector3 LLIK::Joint::getPreScaledLocalPos() const
+{
+    LLVector3 pos = mLocalPos;
+    // We inverse-scale mLocalPos because we already applied the info's scale
+    // to mLocalPos so we could perform IK without constantly recomputing it,
+    // and now we're being asked for mLocalPos in the info's pre-scaled frame.
+
+    // Note: mInfoPtr will not be nullptr as per assert() in LLIK::Joint::ctor
+    LLVector3 inv_scale = mInfoPtr->getScale();
+    constexpr F32 MIN_INVERTABLE_SCALE = 1.0e-15f;
+    for (S32 i = 0; i < 3; ++i)
+    {
+        // verify scale component to avoid introducing NaN
+        inv_scale[i] = (inv_scale[i] > MIN_INVERTABLE_SCALE) ?  1.0f / inv_scale[i] : 0.0f;
+    }
+    pos.scaleVec(inv_scale);
+    return pos;
+}
+
 void LLIK::Joint::adjustWorldRot(const LLQuaternion& adjustment)
 {
     mRot = mRot * adjustment;
@@ -1861,10 +1920,10 @@ LLVector3 LLIK::Solver::computeReach(S16 to_id, S16 from_id) const
 void LLIK::Solver::addJoint(
         S16 joint_id,
         S16 parent_id,
-        const LLVector3& local_pos,
-        const LLVector3& bone,
+        LLJoint* joint_info,
         const Constraint::ptr_t& constraint)
 {
+    llassert(joint_info);
     // Note: parent Joints must be added BEFORE their children.
     if (joint_id < 0)
     {
@@ -1892,7 +1951,7 @@ void LLIK::Solver::addJoint(
     {
         parent = itr->second;
     }
-    Joint::ptr_t joint = std::make_shared<Joint>(joint_id, local_pos, bone);
+    Joint::ptr_t joint = std::make_shared<Joint>(joint_info);
     joint->setParent(parent);
     if (parent)
     {
@@ -2046,25 +2105,25 @@ bool LLIK::Solver::updateJointConfigs(const joint_config_map_t& configs)
                     something_changed = true;
                     break;
                 }
-                if ((mask & FLAG_TARGET_POS) > 0
+                if ((mask & CONFIG_FLAG_TARGET_POS) > 0
                         && std::abs(dist_vec(old_target.getTargetPos(), new_target.getTargetPos())) > mAcceptableError)
                 {
                     something_changed = true;
                     break;
                 }
-                if ((mask & FLAG_TARGET_ROT) > 0
+                if ((mask & CONFIG_FLAG_TARGET_ROT) > 0
                             && !LLQuaternion::almost_equal(old_target.getTargetRot(), new_target.getTargetRot()))
                 {
                     something_changed = true;
                     break;
                 }
-                if ((mask & FLAG_LOCAL_POS) > 0
+                if ((mask & CONFIG_FLAG_LOCAL_POS) > 0
                         && std::abs(dist_vec(old_target.getLocalPos(), new_target.getLocalPos())) > mAcceptableError)
                 {
                     something_changed = true;
                     break;
                 }
-                if ((mask & FLAG_LOCAL_ROT) > 0
+                if ((mask & CONFIG_FLAG_LOCAL_ROT) > 0
                             && !LLQuaternion::almost_equal(old_target.getLocalRot(), new_target.getLocalRot()))
                 {
                     something_changed = true;
@@ -2187,7 +2246,7 @@ void LLIK::Solver::rebuildAllChains()
             if (flags & MASK_ROT)
             {
                 LLQuaternion q;
-                if (flags & FLAG_LOCAL_ROT)
+                if (flags & CONFIG_FLAG_LOCAL_ROT)
                 {
                     q = config.getLocalRot();
                 }
@@ -2202,7 +2261,7 @@ void LLIK::Solver::rebuildAllChains()
             if (flags & MASK_POS)
             {
                 LLVector3 p;
-                if (flags & FLAG_LOCAL_POS)
+                if (flags & CONFIG_FLAG_LOCAL_POS)
                 {
                     p = config.getLocalPos();
                 }
@@ -2213,6 +2272,10 @@ void LLIK::Solver::rebuildAllChains()
                 joint->setLocalPos(p);
                 joint->activate();
             }
+            if (flags & CONFIG_FLAG_LOCAL_SCALE)
+            {
+                joint->setLocalScale(config.getLocalScale());
+            }
             continue;
         }
 
@@ -2257,6 +2320,11 @@ void LLIK::Solver::rebuildAllChains()
             joint->setLocalPos(config.getLocalPos());
             joint->activate();
         }
+        if (config.hasLocalScale())
+        {
+            joint->setLocalScale(config.getLocalScale());
+            joint->activate();
+        }
     }
 
     // each sub_base gets its own Chain
@@ -2368,6 +2436,7 @@ void LLIK::Solver::rebuildAllChains()
         if (data_pair.second->isActive())
         {
             mActiveJoints.push_back(data_pair.second);
+            data_pair.second->flagForHarvest();
         }
     }
 }
@@ -2524,18 +2593,18 @@ LLQuaternion LLIK::Solver::getJointWorldRot(S16 joint_id) const
     return rot;
 }
 
-void LLIK::Solver::reconfigureJoint(S16 joint_id, const LLVector3& local_pos, const LLVector3& bone, const Constraint::ptr_t& constraint)
+void LLIK::Solver::resetJointGeometry(S16 joint_id, const Constraint::ptr_t& constraint)
 {
     joint_map_t::iterator itr = mSkeleton.find(joint_id);
     if (itr == mSkeleton.end())
     {
-        LL_WARNS("Puppet") << "failed reconfigure unknown joint_id=" << joint_id << LL_ENDL;
+        LL_WARNS("Puppet") << "failed update unknown joint_id=" << joint_id << LL_ENDL;
         return;
     }
     const Joint::ptr_t& joint = itr->second;
-    joint->reconfigure(local_pos, bone);
+    joint->resetFromInfo();
     joint->setConstraint(constraint);
-    // Note: will need to call computeEffectorNormal() after all Joints are reconfigured.
+    // Note: will need to call computeReach() after all Joint geometries are reset.
 }
 
 void LLIK::Solver::buildChain(Joint::ptr_t joint, joint_list_t& chain, std::set<S16>& sub_bases)
diff --git a/indra/llcharacter/llik.h b/indra/llcharacter/llik.h
index 1d0778de91667402276bd3b2d0d68e7ee1ea371f..17bcba71f5ba59b1e619d8cdd9948634ea8cb81f 100644
--- a/indra/llcharacter/llik.h
+++ b/indra/llcharacter/llik.h
@@ -38,6 +38,8 @@
 #include "v4math.h"
 #include "xform.h"
 
+class LLJoint;
+
 //#define DEBUG_LLIK_UNIT_TESTS
 
 namespace LLIK
@@ -55,22 +57,27 @@ namespace LLIK
 class Solver;
 class Joint;
 
-// local flags
-constexpr U8 FLAG_LOCAL_POS = 1 << 0;
-constexpr U8 FLAG_LOCAL_ROT = 1 << 1;
-constexpr U8 FLAG_LOCAL_SCALE = 1 << 2;
-constexpr U8 FLAG_DISABLE_CONSTRAINT = 1 << 3;
-
-// target flags
-constexpr U8 FLAG_TARGET_POS = 1 << 4;
-constexpr U8 FLAG_TARGET_ROT = 1 << 5;
-constexpr U8 FLAG_HAS_DELEGATED = 1 << 6; // EXPERIMENTAL
-
-constexpr U8 MASK_POS = FLAG_TARGET_POS | FLAG_LOCAL_POS;
-constexpr U8 MASK_ROT = FLAG_TARGET_ROT | FLAG_LOCAL_ROT;
+// config flags
+constexpr U8 CONFIG_FLAG_LOCAL_POS = 1 << 0;
+constexpr U8 CONFIG_FLAG_LOCAL_ROT = 1 << 1;
+constexpr U8 CONFIG_FLAG_LOCAL_SCALE = 1 << 2;
+constexpr U8 CONFIG_FLAG_DISABLE_CONSTRAINT = 1 << 3;
+constexpr U8 CONFIG_FLAG_TARGET_POS = 1 << 4;
+constexpr U8 CONFIG_FLAG_TARGET_ROT = 1 << 5;
+constexpr U8 CONFIG_FLAG_HAS_DELEGATED = 1 << 6; // EXPERIMENTAL
+
+constexpr U8 IK_FLAG_LOCAL_ROT = 1 << 1; // IK has adjusted local_rot
+constexpr U8 IK_FLAG_ACTIVE = 1 << 5;
+constexpr U8 IK_FLAG_LOCAL_ROT_LOCKED = 1 << 7; // local_rot is locked during IK
+
+constexpr U8 MASK_POS = CONFIG_FLAG_TARGET_POS | CONFIG_FLAG_LOCAL_POS;
+constexpr U8 MASK_ROT = CONFIG_FLAG_TARGET_ROT | CONFIG_FLAG_LOCAL_ROT;
 constexpr U8 MASK_TRANSFORM = MASK_POS | MASK_ROT;
-constexpr U8 MASK_LOCAL = FLAG_LOCAL_POS | FLAG_LOCAL_ROT | FLAG_DISABLE_CONSTRAINT;
-constexpr U8 MASK_TARGET = FLAG_TARGET_POS | FLAG_TARGET_ROT;
+constexpr U8 MASK_LOCAL = CONFIG_FLAG_LOCAL_POS | CONFIG_FLAG_LOCAL_ROT | CONFIG_FLAG_DISABLE_CONSTRAINT;
+constexpr U8 MASK_TARGET = CONFIG_FLAG_TARGET_POS | CONFIG_FLAG_TARGET_ROT;
+
+// this mask relates to LLJointState::Usage enum
+constexpr U8 MASK_JOINT_STATE_USAGE = CONFIG_FLAG_LOCAL_POS | CONFIG_FLAG_LOCAL_ROT | CONFIG_FLAG_LOCAL_SCALE;
 
 constexpr F32 IK_DEFAULT_ACCEPTABLE_ERROR = 5.0e-4f; // half millimeter
 
@@ -349,25 +356,28 @@ class Joint
     {
     public:
         // local info is in parent-frame
-        bool hasLocalPos() const { return (mFlags & FLAG_LOCAL_POS) > 0; }
-        bool hasLocalRot() const { return (mFlags & FLAG_LOCAL_ROT) > 0; }
-        bool constraintIsDisabled() const { return (mFlags & FLAG_DISABLE_CONSTRAINT) > 0; }
+        bool hasLocalPos() const { return (mFlags & CONFIG_FLAG_LOCAL_POS) > 0; }
+        bool hasLocalRot() const { return (mFlags & CONFIG_FLAG_LOCAL_ROT) > 0; }
+        bool hasLocalScale() const { return (mFlags & CONFIG_FLAG_LOCAL_SCALE) > 0; }
+        bool constraintIsDisabled() const { return (mFlags & CONFIG_FLAG_DISABLE_CONSTRAINT) > 0; }
         void setLocalPos(const LLVector3& pos);
         void setLocalRot(const LLQuaternion& rot);
-        void disableConstraint() { mFlags |= FLAG_DISABLE_CONSTRAINT; }
+        void setLocalScale(const LLVector3& scale);
+        void disableConstraint() { mFlags |= CONFIG_FLAG_DISABLE_CONSTRAINT; }
         const LLVector3& getLocalPos() const { return mLocalPos; }
         const LLQuaternion& getLocalRot() const { return mLocalRot; }
+        const LLVector3& getLocalScale() const { return mLocalScale; }
 
         // target info is in skeleton root-frame
-        bool hasTargetPos() const { return (mFlags & FLAG_TARGET_POS) > 0; }
-        bool hasTargetRot() const { return (mFlags & FLAG_TARGET_ROT) > 0; }
+        bool hasTargetPos() const { return (mFlags & CONFIG_FLAG_TARGET_POS) > 0; }
+        bool hasTargetRot() const { return (mFlags & CONFIG_FLAG_TARGET_ROT) > 0; }
         void setTargetPos(const LLVector3& pos);
         void setTargetRot(const LLQuaternion& rot);
         const LLVector3& getTargetPos() const { return mTargetPos; }
         const LLQuaternion& getTargetRot() const { return mTargetRot; }
 
-        void delegate() { mFlags |= FLAG_HAS_DELEGATED; } // EXPERIMENTAL
-        bool hasDelegated() const { return (mFlags & FLAG_HAS_DELEGATED) > 0; } // EXPERIMENTAL
+        void delegate() { mFlags |= CONFIG_FLAG_HAS_DELEGATED; } // EXPERIMENTAL
+        bool hasDelegated() const { return (mFlags & CONFIG_FLAG_HAS_DELEGATED) > 0; } // EXPERIMENTAL
 
         U8 getFlags() const { return mFlags; }
 
@@ -376,6 +386,7 @@ class Joint
     private:
         LLVector3 mLocalPos;
         LLQuaternion mLocalRot;
+        LLVector3 mLocalScale;
         LLVector3 mTargetPos;
         LLQuaternion mTargetRot;
         U8 mFlags = 0; // per-feature bits
@@ -384,7 +395,8 @@ class Joint
     using ptr_t = std::shared_ptr<Joint>;
     using child_vec_t = std::vector<ptr_t>;
 
-    Joint(S16 id, const LLVector3& local_pos, const LLVector3& bone);
+    Joint(LLJoint* info);
+    void resetFromInfo();
 
     void addChild(const ptr_t& child);
     void setParent(const ptr_t& parent);
@@ -392,7 +404,7 @@ class Joint
     void relaxRotationsRecursively(F32 blend_factor);
     F32 recursiveComputeLongestChainLength(F32 length) const;
 
-    void reconfigure(const LLVector3& local_pos, const LLVector3& bone);
+    void updateGeometry(const LLVector3& local_pos, const LLVector3& bone);
 
     LLVector3 computeEndTargetPos() const;
     LLVector3 computeWorldTipOffset() const;
@@ -415,6 +427,8 @@ class Joint
     void setLocalPos(const LLVector3& pos);
     void setWorldRot(const LLQuaternion& rot);
     void setLocalRot(const LLQuaternion& new_local_rot);
+    void setLocalScale(const LLVector3& scale);
+    LLVector3 getPreScaledLocalPos() const;
     void adjustWorldRot(const LLQuaternion& adjustment);
     void shiftPos(const LLVector3& shift);
 
@@ -424,9 +438,10 @@ class Joint
     LLVector3 getTargetPos() const { return mConfig->getTargetPos(); }
 
     const Config* getConfig() const { return mConfig; }
-    bool hasPosTarget() const { return (mConfigFlags & FLAG_TARGET_POS) > 0; }
-    bool hasRotTarget() const { return (mConfigFlags & FLAG_TARGET_ROT) > 0; }
+    bool hasPosTarget() const { return (mConfigFlags & CONFIG_FLAG_TARGET_POS) > 0; }
+    bool hasRotTarget() const { return (mConfigFlags & CONFIG_FLAG_TARGET_ROT) > 0; }
     U8 getConfigFlags() const { return mConfigFlags; }
+    U8 getHarvestFlags() const { return (mConfigFlags | mIkFlags) & MASK_LOCAL; }
     void resetFlags();
     void lockLocalRot(const LLQuaternion& local_rot);
 
@@ -439,16 +454,17 @@ class Joint
     ptr_t getSingleActiveChild();
     const LLVector3& getBone() const { return mBone; }
     const LLVector3& getLocalPos() const { return mLocalPos; }
+    const LLVector3& getLocalScale() const { return mLocalScale; }
     F32 getBoneLength() const { return mBone.length(); }
     F32 getLocalPosLength() const { return mLocalPosLength; }
 
     ptr_t getParent() { return mParent; }
-    void activate() { mIsActive = true; }
-    bool isActive() const { return mIsActive; }
-    bool hasDisabledConstraint() const { return (mConfigFlags & FLAG_DISABLE_CONSTRAINT) > 0; }
+    void activate() { mIkFlags |= IK_FLAG_ACTIVE; }
+    bool isActive() const { return mIkFlags & IK_FLAG_ACTIVE; }
+    bool hasDisabledConstraint() const { return (mConfigFlags & CONFIG_FLAG_DISABLE_CONSTRAINT) > 0; }
 
     // Joint::mLocalRot is considered "locked" when its mConfigFlag's FLAG_LOCAL_ROT bit is set
-    bool localRotLocked() const { return (mConfigFlags & FLAG_LOCAL_ROT) > 0; }
+    bool localRotLocked() const { return (mIkFlags & IK_FLAG_LOCAL_ROT_LOCKED) > 0; }
 
     size_t getNumChildren() const { return mChildren.size(); }
 
@@ -457,6 +473,10 @@ class Joint
     void twistTowardTargets(const std::vector<LLVector3>& local_targets, const std::vector<LLVector3>& world_targets);
     void untwist();
 
+    // We call flagForHarvest() when we expect the joint to be updated by IK
+    // so we know to harvest its mLocalRot later.
+    void flagForHarvest() { mIkFlags |= IK_FLAG_LOCAL_ROT; }
+
 #ifdef DEBUG_LLIK_UNIT_TESTS
     void dumpConfig() const;
     void dumpState() const;
@@ -471,7 +491,6 @@ class Joint
 protected:
     std::vector<ptr_t> mChildren;      //List of joint_ids attached to this one.
 
-    LLVector3 mDefaultLocalPos; // default pos in parent-frame
     LLVector3 mLocalPos; // current pos in parent-frame
     LLVector3 mPos; // pos in world-frame
     // The fundamental position formula is:
@@ -483,6 +502,8 @@ class Joint
     // The fundamental orientations formula is:
     //     mRot = mLocalRot * mParent->mRot
 
+    LLVector3 mLocalScale;
+
     LLVector3 mBone;
     // There is another fundamental formula:
     //    world_end_pos = mPos + mBone * mRot
@@ -492,10 +513,17 @@ class Joint
     F32 mLocalPosLength = 0.0f; // cached copy of mLocalPos.length()
     S16 mID = -1;
 
+    // From LLIK::Joint's prespective the LLJoint stores the default
+    // non-animated geometry of the Joint, which occasionally needs to
+    // be known during IK calculations.  LLJoint was implemented way back,
+    // when we were adding raw pointers and we haven't rewritten that logic
+    // yet, so to make its raw-pointer nature more clear we call it mInfoPtr.
+    const LLJoint* mInfoPtr;
+
     const Config* mConfig = nullptr; // pointer into Solver::mJointConfigs
 
     U8 mConfigFlags = 0; // cache of mConfig->mFlags
-    bool mIsActive;
+    U8 mIkFlags = 0; // flags for IK calculations
 };
 
 
@@ -523,7 +551,11 @@ class Solver
     LLVector3 computeReach(S16 to_id, S16 from_id) const;
 
     // Add a Joint to the skeleton.
-    void addJoint(S16 joint_id, S16 parent_id, const LLVector3& local_pos, const LLVector3& bone, const Constraint::ptr_t& constraint);
+    void addJoint(
+            S16 joint_id,
+            S16 parent_id,
+            LLJoint* info_ptr,
+            const Constraint::ptr_t& constraint);
 
     // apply configs and return 'true' if something changed
     bool updateJointConfigs(const joint_config_map_t& configs);
@@ -560,7 +592,7 @@ class Solver
     LLVector3 getJointWorldEndPos(S16 joint_id) const;
     LLQuaternion getJointWorldRot(S16 joint_id) const;
 
-    void reconfigureJoint(S16 joint_id, const LLVector3& local_pos, const LLVector3& bone, const Constraint::ptr_t& constraint);
+    void resetJointGeometry(S16 joint_id, const Constraint::ptr_t& constraint);
 
     void enableDebugIfPossible(); // will be possible when DEBUG_LLIK_UNIT_TESTS defined
 
diff --git a/indra/llcharacter/lljoint.cpp b/indra/llcharacter/lljoint.cpp
index c4c47744775148c917d9e0bf422fe6b1cde9538a..dfb7e021fa8593ceedf919036ece5a33b430609f 100644
--- a/indra/llcharacter/lljoint.cpp
+++ b/indra/llcharacter/lljoint.cpp
@@ -261,7 +261,7 @@ LLJoint *LLJoint::findJoint( const std::string &name )
 	if (name == getName())
 		return this;
 
-	for (joints_t::iterator iter = mChildren.begin();
+	for (joints_t::const_iterator iter = mChildren.begin();
 		 iter != mChildren.end(); ++iter)
 	{
 		LLJoint* joint = *iter;
@@ -329,7 +329,7 @@ void LLJoint::removeAllChildren()
 //--------------------------------------------------------------------
 // getNumChildren()
 //--------------------------------------------------------------------
-U32 LLJoint::getNumChildren()
+U32 LLJoint::getNumChildren() const
 {
     //return of 0 indicates an end effector, > 1 a leaf and 1 a normal joint.
     return mChildren.size();
@@ -338,7 +338,7 @@ U32 LLJoint::getNumChildren()
 //--------------------------------------------------------------------
 // getPosition()
 //--------------------------------------------------------------------
-const LLVector3& LLJoint::getPosition()
+const LLVector3& LLJoint::getPosition() const
 {
 	return mXform.getPosition();
 }
@@ -786,7 +786,7 @@ LLVector3 LLJoint::getWorldPosition()
 //-----------------------------------------------------------------------------
 // getLastWorldPosition()
 //-----------------------------------------------------------------------------
-LLVector3 LLJoint::getLastWorldPosition()
+LLVector3 LLJoint::getLastWorldPosition() const
 {
 	return mXform.getWorldPosition();
 }
@@ -822,7 +822,7 @@ void LLJoint::setWorldPosition( const LLVector3& pos )
 //--------------------------------------------------------------------
 // getRotation()
 //--------------------------------------------------------------------
-const LLQuaternion& LLJoint::getRotation()
+const LLQuaternion& LLJoint::getRotation() const
 {
 	return mXform.getRotation();
 }
@@ -891,7 +891,7 @@ void LLJoint::setWorldRotation( const LLQuaternion& rot )
 //--------------------------------------------------------------------
 // getScale()
 //--------------------------------------------------------------------
-const LLVector3& LLJoint::getScale()
+const LLVector3& LLJoint::getScale() const
 {
     return mXform.getScale();
 }
@@ -1036,7 +1036,7 @@ void LLJoint::updateWorldMatrix()
 //--------------------------------------------------------------------
 // getSkinOffset()
 //--------------------------------------------------------------------
-const LLVector3 &LLJoint::getSkinOffset()
+const LLVector3 &LLJoint::getSkinOffset() const
 {
 	return mSkinOffset;
 }
@@ -1050,27 +1050,5 @@ void LLJoint::setSkinOffset( const LLVector3& offset )
 	mSkinOffset = offset;
 }
 
-
-//-----------------------------------------------------------------------------
-// clampRotation()
-//-----------------------------------------------------------------------------
-void LLJoint::clampRotation(LLQuaternion old_rot, LLQuaternion new_rot)
-{
-	LLVector3 main_axis(1.f, 0.f, 0.f);
-
-	for (joints_t::iterator iter = mChildren.begin();
-		 iter != mChildren.end(); ++iter)
-	{
-		LLJoint* joint = *iter;
-		if (joint->isAnimatable())
-		{
-			main_axis = joint->getPosition();
-			main_axis.normVec();
-			// only care about first animatable child
-			break;
-		}
-	}
-}
-
 // End
 
diff --git a/indra/llcharacter/lljoint.h b/indra/llcharacter/lljoint.h
index 3b452e0476aba3f079a9074b5a1146828aeec4ac..5d6c2b3ba7c8f625a0d9f84fe3ce71b8c24d7c04 100644
--- a/indra/llcharacter/lljoint.h
+++ b/indra/llcharacter/lljoint.h
@@ -236,10 +236,10 @@ class LLJoint
 	void removeAllChildren();
     
     //Tells us if this is a normal joint, leaf, or end effector
-    U32  getNumChildren();
+    U32  getNumChildren() const;
 
 	// get/set local position
-	const LLVector3& getPosition();
+	const LLVector3& getPosition() const;
 	void setPosition( const LLVector3& pos, bool apply_attachment_overrides = false );
 
     // Tracks the default position defined by the skeleton
@@ -252,11 +252,11 @@ class LLJoint
 
 	// get/set world position
 	LLVector3 getWorldPosition();
-	LLVector3 getLastWorldPosition();
+	LLVector3 getLastWorldPosition() const;
 	void setWorldPosition( const LLVector3& pos );
 
 	// get/set local rotation
-	const LLQuaternion& getRotation();
+	const LLQuaternion& getRotation() const;
 	void setRotation( const LLQuaternion& rot );
 
 	// get/set world rotation
@@ -265,7 +265,7 @@ class LLJoint
 	void setWorldRotation( const LLQuaternion& rot );
 
 	// get/set local scale
-	const LLVector3& getScale();
+	const LLVector3& getScale() const;
 	void setScale( const LLVector3& scale, bool apply_attachment_overrides = false );
 
 	// get/set world matrix
@@ -282,13 +282,11 @@ class LLJoint
 	void updateWorldMatrix();
 
 	// get/set skin offset
-	const LLVector3 &getSkinOffset();
+	const LLVector3 &getSkinOffset() const;
 	void setSkinOffset( const LLVector3 &offset);
 
 	LLXformMatrix	*getXform() { return &mXform; }
 
-	void clampRotation(LLQuaternion old_rot, LLQuaternion new_rot);
-
 	virtual BOOL isAnimatable() const { return TRUE; }
 
 	void addAttachmentPosOverride( const LLVector3& pos, const LLUUID& mesh_id, const std::string& av_info, bool& active_override_changed );
diff --git a/indra/llcharacter/tests/llik_test.cpp b/indra/llcharacter/tests/llik_test.cpp
index 58c782224e7ffec9beacb07ec410eb633c6edb69..5a4ecafdd85456698fab591135391144b3b5c902 100644
--- a/indra/llcharacter/tests/llik_test.cpp
+++ b/indra/llcharacter/tests/llik_test.cpp
@@ -27,6 +27,7 @@
  */
 
 #include "../llik.h"
+#include "../lljoint.h"
 #include "../test/lltut.h"
 #include <iostream>
 #include <cmath>
@@ -762,14 +763,6 @@ namespace tut
 	template<> template<>
 	void llik_object::test<7>()
 	{
-        LLIKConstraintFactory factory;
-        S16 root_joint_id = 7;
-        LLIK::Solver solver;
-        constexpr F32 ACCEPTABLE_ERROR = 1.0e-3f; // one mm
-        solver.setAcceptableError(ACCEPTABLE_ERROR);
-        solver.setRootID(root_joint_id);
-        ensure_equals("LLIK::Solver::getRootID", solver.getRootID(), root_joint_id);
-
         // Make a simple skeleton along the y-axis, where each child joint's local_pos
         // is a unit-vector from it's parent's origin.
         //
@@ -782,26 +775,60 @@ namespace tut
         //       x
         //
 
-        LLIK::Constraint::Info info;
-        LLIK::Constraint::ptr_t null_constraint = factory.getConstraint(info);
-
         constexpr F32 BONE_LENGTH = 1.234f;
         constexpr F32 POS_LENGTH = 2.345f;
         S32 num_joints = 3;
         LLVector3 local_pos = POS_LENGTH * LLVector3::y_axis;
         LLVector3 bone = BONE_LENGTH * LLVector3::y_axis;
 
-        // Note: root joint should always have zero bone length,
-        // because IK will target its "end" not its "tip".
-        solver.addJoint(root_joint_id, root_joint_id-1, LLVector3::zero, LLVector3::zero, null_constraint);
-        S16 joint_id = root_joint_id + 1;
-
-        for (S32 i = 0; i < num_joints; ++i)
+        // build LLJoint skeleton
+        std::vector<LLVector3> local_positions;
+        local_positions.push_back(LLVector3());
+        local_positions.push_back(local_pos);
+        local_positions.push_back(local_pos);
+        local_positions.push_back(local_pos);
+
+        std::vector<LLVector3> local_ends;
+        local_ends.push_back(LLVector3());
+        local_ends.push_back(bone);
+        local_ends.push_back(bone);
+        local_ends.push_back(bone);
+
+        std::vector<LLJoint*> joints;
+        LLJoint* parent_joint = nullptr;
+        S32 joint_id = 7;
+        for (S32 i = 0; i < S32(local_positions.size()); ++i)
         {
-            solver.addJoint(joint_id, joint_id-1, local_pos, bone, null_constraint);
+            LLJoint* joint = new LLJoint("foo", parent_joint);
+            joints.push_back(joint);
+            joints[i]->setIsBone(true);
+            joints[i]->setJointNum(joint_id);
+            joints[i]->setPosition(local_positions[i]);
+            joints[i]->setEnd(local_ends[i]);
+            parent_joint = joints[i];
             ++joint_id;
         }
-        S16 last_joint_id = joint_id - 1;
+
+        LLIKConstraintFactory factory;
+        S16 root_joint_id = 7;
+        LLIK::Solver solver;
+        constexpr F32 ACCEPTABLE_ERROR = 1.0e-3f; // one mm
+        solver.setAcceptableError(ACCEPTABLE_ERROR);
+        solver.setRootID(root_joint_id);
+        ensure_equals("LLIK::Solver::getRootID", solver.getRootID(), root_joint_id);
+
+        // build solver skeleton
+        LLIK::Constraint::Info info;
+        LLIK::Constraint::ptr_t null_constraint = factory.getConstraint(info);
+        S16 ik_joint_id = root_joint_id;
+        S16 parent_ik_joint_id = -1;
+        for (S16 i = 0; i < S16(joints.size()); ++i)
+        {
+            solver.addJoint(ik_joint_id, parent_ik_joint_id, joints[i], null_constraint);
+            parent_ik_joint_id = ik_joint_id;
+            ++ik_joint_id;
+        }
+        S16 last_joint_id = ik_joint_id - 1;
 
         F32 reach = solver.computeReach(root_joint_id, last_joint_id).length();
         F32 expected_reach = num_joints * POS_LENGTH + BONE_LENGTH;
@@ -856,6 +883,11 @@ namespace tut
             F32 max_error = solver.solve();
             ensure("LLIK::Solver reachable target sans-constraints after moving root", max_error < ACCEPTABLE_ERROR);
         }
+        // cleanup LLJoints
+        for (auto joint: joints)
+        {
+            delete joint;
+        }
 	}
 
     // LLIK::Solver : unconstrained vs constrained
@@ -863,7 +895,6 @@ namespace tut
 	void llik_object::test<8>()
 	{
         LLIKConstraintFactory factory;
-        S16 joint_id = 0;
         constexpr F32 ACCEPTABLE_ERROR = 3.0e-3f;
 
         // Consider the following chain of Joints:
@@ -899,36 +930,54 @@ namespace tut
         //             3--.   Z
         // We will test each case.
 
+        // build LLJoint skeleton
+        std::vector<LLVector3> local_positions;
+        local_positions.push_back(LLVector3());
+        local_positions.push_back(LLVector3::y_axis);
+        local_positions.push_back(2.0f * LLVector3::y_axis);
+        local_positions.push_back(2.0f * LLVector3::y_axis);
+
+        std::vector<LLVector3> local_ends;
+        local_ends.push_back(LLVector3());
+        local_ends.push_back(2.0f * LLVector3::y_axis);
+        local_ends.push_back(2.0f * LLVector3::y_axis);
+        local_ends.push_back(LLVector3::y_axis);
+
+        std::vector<LLJoint*> joints;
+        LLJoint* parent_joint = nullptr;
+        for (S32 i = 0; i < S32(local_positions.size()); ++i)
+        {
+            LLJoint* joint = new LLJoint("foo", parent_joint);
+            joint->setIsBone(true);
+            joint->setJointNum(i);
+            joint->setPosition(local_positions[i]);
+            joint->setEnd(local_ends[i]);
+            parent_joint = joint;
+            joints.push_back(joint);
+        }
+
         {   // unconstrained
             LLIK::Solver solver;
             solver.setAcceptableError(ACCEPTABLE_ERROR);
-            solver.setRootID(joint_id);
+            solver.setRootID(0);
 
+            // build solver skeleton
             LLIK::Constraint::Info info;
             LLIK::Constraint::ptr_t null_constraint = factory.getConstraint(info);
+            for (S16 i = 0; i < S16(joints.size()); ++i)
+            {
+                solver.addJoint(i, i-1, joints[i], null_constraint);
+            }
+            S16 last_joint_id = joints.size() - 1;
 
-            // Note: addJoint() API is:
-            // Solver::addJoint(joint_id, parent_id, local_pos, bone, constraint)
-
-            // root joint has zero local_pos
-            solver.addJoint(joint_id, joint_id-1, 0.0f * LLVector3::y_axis, 1.0f * LLVector3::y_axis, null_constraint);
-            ++joint_id;
-
-            solver.addJoint(joint_id, joint_id-1, 1.0f * LLVector3::y_axis, 2.0f * LLVector3::y_axis, null_constraint);
-            ++joint_id;
-            solver.addJoint(joint_id, joint_id-1, 2.0f * LLVector3::y_axis, 2.0f * LLVector3::y_axis, null_constraint);
-            ++joint_id;
-            solver.addJoint(joint_id, joint_id-1, 2.0f * LLVector3::y_axis, 1.0f * LLVector3::y_axis, null_constraint);
-            ++joint_id;
-
-            S16 last_joint_id = joint_id - 1;
-
+            // configure a target for end-effector
             LLIK::Joint::Config config;
             config.setTargetPos(3.0f * LLVector3::x_axis - 1.0f * LLVector3::y_axis);
             LLIK::Solver::joint_config_map_t configs;
             configs.insert({last_joint_id, config});
-
             solver.updateJointConfigs(configs);
+
+            // solve
             F32 max_error = solver.solve();
             ensure("LLIK::Solver reachable target sans-constraints should have low error", max_error < ACCEPTABLE_ERROR);
         }
@@ -941,15 +990,16 @@ namespace tut
             //               Z
             LLIK::Solver solver;
             solver.setAcceptableError(ACCEPTABLE_ERROR);
-            solver.setRootID(joint_id);
+            solver.setRootID(0);
 
             LLIK::Constraint::Info info;
             F32 del = 0.2f;
 
-            // root joint doesn't move
+            // build solver skeleton
             LLIK::Constraint::Info info_A;
             LLIK::Constraint::ptr_t null_constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, 0.0f * LLVector3::y_axis, 1.0f * LLVector3::y_axis, null_constraint);
+            S16 joint_id = 0;
+            solver.addJoint(joint_id, joint_id-1, joints[joint_id], null_constraint);
             ++joint_id;
 
             LLIK::Constraint::Info info_CW;
@@ -959,11 +1009,10 @@ namespace tut
             info_CW.mFloats.push_back(-0.5f * F_PI - del); // min_bend
             info_CW.mFloats.push_back(-0.5f * F_PI + del); // max_bend
             LLIK::Constraint::ptr_t right_turn_CW = factory.getConstraint(info_CW);
-            solver.addJoint(joint_id, joint_id-1, 1.0f * LLVector3::y_axis, 2.0f * LLVector3::y_axis, right_turn_CW);
+            solver.addJoint(joint_id, joint_id-1, joints[joint_id], right_turn_CW);
             ++joint_id;
 
-            LLIK::Constraint::Info info_C;
-            solver.addJoint(joint_id, joint_id-1, 2.0f * LLVector3::y_axis, 2.0f * LLVector3::y_axis, right_turn_CW);
+            solver.addJoint(joint_id, joint_id-1, joints[joint_id], right_turn_CW);
             ++joint_id;
 
             LLIK::Constraint::Info info_CCW;
@@ -973,20 +1022,25 @@ namespace tut
             info_CCW.mFloats.push_back(0.5f * F_PI - del); // min_bend
             info_CCW.mFloats.push_back(0.5f * F_PI + del); // max_bend
             LLIK::Constraint::ptr_t right_turn_CCW = factory.getConstraint(info_CCW);
-            solver.addJoint(joint_id, joint_id-1, 2.0f * LLVector3::y_axis, 1.0f * LLVector3::y_axis, right_turn_CCW);
-            ++joint_id;
-
-            S16 last_joint_id = joint_id - 1;
+            solver.addJoint(joint_id, joint_id-1, joints[joint_id], right_turn_CCW);
 
+            // configure a target for end-effector
             LLIK::Joint::Config config;
             config.setTargetPos(3.0f * LLVector3::x_axis - 1.0f * LLVector3::y_axis);
             LLIK::Solver::joint_config_map_t configs;
-            configs.insert({last_joint_id, config});
-
+            configs.insert({joint_id, config});
             //solver.enableDebugIfPossible();
             solver.updateJointConfigs(configs);
+
+            // solve
             F32 max_error = solver.solve();
             ensure("LLIK::Solver reachable target with constraints should have low error", max_error < ACCEPTABLE_ERROR);
+
+        }
+        // cleanup LLJoints
+        for (auto joint: joints)
+        {
+            delete joint;
         }
 	}
 
@@ -994,19 +1048,9 @@ namespace tut
 	template<> template<>
 	void llik_object::test<9>()
 	{
-        LLIKConstraintFactory factory;
-        LLIK::Constraint::Info info;
-        LLIK::Constraint::ptr_t null_constraint = factory.getConstraint(info);
-
-        S16 joint_id = 0;
-        LLIK::Solver solver;
-        constexpr F32 ACCEPTABLE_ERROR = 1.0e-3f; // one mm
-        solver.setAcceptableError(ACCEPTABLE_ERROR);
-        solver.setRootID(joint_id);
-
         // We'll start with a simple skeleton like this: two "arms"
         // joined at a "neck" and a "spine" to the "root",
-        // where each joint local_pos/bone is equal length...
+        // where each joint local_pos/bone is of equal length...
         //
         //  <---(9)<--(8)<--(7,4)-->(5)-->(6)--->
         //                    ^
@@ -1025,56 +1069,109 @@ namespace tut
         // ... and we'll specify reachable endpoints for (3), (6) and (9)
         // to see if it can reach a solution.
 
+        S32 num_spine_joints = 4;
+        S32 num_arm_joints = 3;
+
+        // build LLJoint skeleton
         F32 bone_length = 1.0f;
+        std::vector<LLVector3> local_positions;
+        local_positions.push_back(LLVector3());
+        local_positions.push_back(bone_length * LLVector3::y_axis); // 1
+        local_positions.push_back(bone_length * LLVector3::y_axis);
+        local_positions.push_back(bone_length * LLVector3::y_axis);
+        local_positions.push_back(bone_length * LLVector3::y_axis); // 4
+        local_positions.push_back(bone_length * LLVector3::x_axis);
+        local_positions.push_back(bone_length * LLVector3::x_axis); // 6
+        local_positions.push_back(bone_length * LLVector3::y_axis); // 7
+        local_positions.push_back(-bone_length * LLVector3::x_axis);
+        local_positions.push_back(-bone_length * LLVector3::x_axis); // 9
+
+        std::vector<LLVector3> local_ends;
+        local_ends.push_back(LLVector3());
+        local_ends.push_back(bone_length * LLVector3::y_axis); // 1
+        local_ends.push_back(bone_length * LLVector3::y_axis);
+        local_ends.push_back(bone_length * LLVector3::y_axis); // 3
+        local_ends.push_back(bone_length * LLVector3::x_axis); // 4
+        local_ends.push_back(bone_length * LLVector3::x_axis);
+        local_ends.push_back(bone_length * LLVector3::x_axis); // 6
+        local_ends.push_back(-bone_length * LLVector3::x_axis); // 7
+        local_ends.push_back(-bone_length * LLVector3::x_axis);
+        local_ends.push_back(-bone_length * LLVector3::x_axis); // 9
 
+        S16 joint_id = 0;
+        std::vector<LLJoint*> joints;
+        LLJoint* parent_joint = nullptr;
         // spine
-        S32 num_spine_joints = 4;
         for (S32 i = 0; i < num_spine_joints; ++i)
         {
-            LLVector3 local_pos = bone_length * LLVector3::y_axis;
-            LLVector3 bone = local_pos;
-            solver.addJoint(joint_id, joint_id-1, local_pos, bone, null_constraint);
+            LLJoint* joint = new LLJoint("foo", parent_joint);
+            joint->setIsBone(true);
+            joint->setJointNum(joint_id);
+            joint->setPosition(local_positions[joint_id]);
+            joint->setEnd(local_ends[joint_id]);
+            parent_joint = joint;
+            joints.push_back(joint);
             ++joint_id;
         }
-
-        S16 neck_id = joint_id - 1;
-        S16 parent_joint_id = neck_id;
-
         // right arm
-        S32 num_arm_joints = 3;
+        LLJoint* neck_joint = parent_joint;
+        for (S32 i = 0; i < num_arm_joints; ++i)
         {
-            // first joint is special
-            LLVector3 local_pos = bone_length * LLVector3::y_axis;
-            LLVector3 bone = bone_length * LLVector3::x_axis;
-            solver.addJoint(joint_id, parent_joint_id, local_pos, bone, null_constraint);
-            parent_joint_id = joint_id;
+            LLJoint* joint = new LLJoint("foo", parent_joint);
+            joint->setIsBone(true);
+            joint->setJointNum(joint_id);
+            joint->setPosition(local_positions[joint_id]);
+            joint->setEnd(local_ends[joint_id]);
+            parent_joint = joint;
+            joints.push_back(joint);
             ++joint_id;
         }
-        for (S32 i = 1; i < num_arm_joints; ++i)
+        // left arm
+        parent_joint = neck_joint;
+        for (S32 i = 0; i < num_arm_joints; ++i)
         {
-            LLVector3 local_pos = bone_length * LLVector3::x_axis;
-            LLVector3 bone = local_pos;
-            solver.addJoint(joint_id, parent_joint_id, local_pos, bone, null_constraint);
-            parent_joint_id = joint_id;
+            LLJoint* joint = new LLJoint("foo", parent_joint);
+            joint->setIsBone(true);
+            joint->setJointNum(joint_id);
+            joint->setPosition(local_positions[joint_id]);
+            joint->setEnd(local_ends[joint_id]);
+            parent_joint = joint;
+            joints.push_back(joint);
             ++joint_id;
         }
-        S16 right_hand_id = joint_id - 1;
 
-        // left-arm
-        parent_joint_id = neck_id;
+        LLIKConstraintFactory factory;
+        LLIK::Constraint::Info info;
+        LLIK::Constraint::ptr_t null_constraint = factory.getConstraint(info);
+
+        LLIK::Solver solver;
+        constexpr F32 ACCEPTABLE_ERROR = 1.0e-3f; // one mm
+        solver.setAcceptableError(ACCEPTABLE_ERROR);
+        solver.setRootID(joint_id);
+
+        // build solver skeleton
+        // spine
+        joint_id = 0;
+        for (S32 i = 0; i < num_spine_joints; ++i)
+        {
+            solver.addJoint(joint_id, joint_id-1, joints[joint_id], null_constraint);
+            ++joint_id;
+        }
+        S16 neck_id = joint_id - 1;
+        // right arm
+        S16 parent_joint_id = neck_id;
+        for (S32 i = 0; i < num_arm_joints; ++i)
         {
-            // first joint is special
-            LLVector3 local_pos = bone_length * LLVector3::y_axis;
-            LLVector3 bone = - bone_length * LLVector3::x_axis;
-            solver.addJoint(joint_id, parent_joint_id, local_pos, bone, null_constraint);
+            solver.addJoint(joint_id, parent_joint_id, joints[joint_id], null_constraint);
             parent_joint_id = joint_id;
             ++joint_id;
         }
-        for (S32 i = 1; i < num_arm_joints; ++i)
+        S16 right_hand_id = joint_id - 1;
+        // left-arm
+        parent_joint_id = neck_id;
+        for (S32 i = 0; i < num_arm_joints; ++i)
         {
-            LLVector3 local_pos = - bone_length * LLVector3::x_axis;
-            LLVector3 bone = local_pos;
-            solver.addJoint(joint_id, parent_joint_id, local_pos, bone, null_constraint);
+            solver.addJoint(joint_id, parent_joint_id, joints[joint_id], null_constraint);
             parent_joint_id = joint_id;
             ++joint_id;
         }
@@ -1135,6 +1232,11 @@ namespace tut
             error = dist_vec(solver.getJointWorldEndPos(left_hand_id), left_hand_pos);
             ensure("LLIK::Solver LeftHand should reach target", error < allowable_error);
         }
+        // cleanup LLJoints
+        for (auto joint: joints)
+        {
+            delete joint;
+        }
     }
 
 #ifdef ENABLE_FAILING_UNIT_TESTS
@@ -1405,7 +1507,7 @@ namespace tut
     }
 #endif // ENABLE_FAILING_UNIT_TESTS
 
-    void build_skeleton_arm(LLIKConstraintFactory& factory, LLIK::Solver& solver, bool with_fingers=true)
+    void build_skeleton_arm(std::vector<LLJoint*>& joints, LLIKConstraintFactory& factory, LLIK::Solver& solver, bool with_fingers=true)
 	{
         // This builds arm+hand+fingers skeleton based on the default SL avatar.
 
@@ -1428,8 +1530,17 @@ namespace tut
         {
             LLVector3 local_position = LLVector3::zero;
             LLVector3 bone(0.0f,0.0f,0.08757f);
+
+            LLJoint* joint = new LLJoint("Pelvis", nullptr);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::ptr_t null_constraint = factory.getConstraint(LLIK::Constraint::Info());
-            solver.addJoint(joint_id, -1, local_position, bone, null_constraint);
+
+            solver.addJoint(joint_id, -1, joint, null_constraint);
         }
         ++joint_id;
 
@@ -1437,6 +1548,14 @@ namespace tut
         {
             LLVector3 local_position(0.0f,0.0f,0.08757f);
             LLVector3 bone(-0.014445f,0.0f,0.213712f);
+
+            LLJoint* joint = new LLJoint("Torso", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::TWIST_LIMITED_CONE_CONSTRAINT;
             info.mVectors.push_back(local_position); // forward_axis
@@ -1444,7 +1563,8 @@ namespace tut
             info.mFloats.push_back(-0.0628319f); // min_twist
             info.mFloats.push_back(0.0628319f); // max_twist
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         ++joint_id;
 
@@ -1452,6 +1572,14 @@ namespace tut
         {
             LLVector3 local_position(-0.015318f,0.0f,0.213712f);
             LLVector3 bone(-0.01f,0.0f,0.2151);
+
+            LLJoint* joint = new LLJoint("Chest", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::TWIST_LIMITED_CONE_CONSTRAINT;
             info.mVectors.push_back(local_position); // forward_axis
@@ -1459,7 +1587,8 @@ namespace tut
             info.mFloats.push_back(-0.0628319f); // min_twist
             info.mFloats.push_back(0.0628319f); // max_twist
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         ++joint_id;
 
@@ -1467,12 +1596,21 @@ namespace tut
         {
             LLVector3 local_position(-0.021f,0.123583f,0.165f);
             LLVector3 bone(0.0f,0.10349f,0.0f);
+
+            LLJoint* joint = new LLJoint("Collar", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::SIMPLE_CONE_CONSTRAINT;
             info.mVectors.push_back(local_position); // forward_axis
             info.mFloats.push_back(0.15708f); // cone_angle
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         ++joint_id;
 
@@ -1480,6 +1618,14 @@ namespace tut
         {
             LLVector3 local_position(0.0f,0.10349f,0.0f);
             LLVector3 bone(0.0f,0.260152f,0.0f);
+
+            LLJoint* joint = new LLJoint("Shoulder", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::TWIST_LIMITED_CONE_CONSTRAINT;
             info.mVectors.push_back(local_position); // forward_axis
@@ -1488,7 +1634,8 @@ namespace tut
             info.mFloats.push_back(-1.25664f); // min_twist
             info.mFloats.push_back(1.7952f); // max_twist
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         ++joint_id;
 
@@ -1496,6 +1643,14 @@ namespace tut
         {
             LLVector3 local_position(0.0f,0.260152f,0.0f);
             LLVector3 bone(0.0f,0.2009f,0.0f);
+
+            LLJoint* joint = new LLJoint("Elbow", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::ELBOW_CONSTRAINT;
             info.mVectors.push_back(local_position); // forward_axis
@@ -1505,7 +1660,8 @@ namespace tut
             info.mFloats.push_back(-0.785398f); // min_twist
             info.mFloats.push_back(2.35619f); // max_twist
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         //S16 ELBOW_INDEX = joint_id;
         ++joint_id;
@@ -1514,6 +1670,14 @@ namespace tut
         {
             LLVector3 local_position(0.0f,0.2009f,0.0f);
             LLVector3 bone(0.01274f,0.09898f,0.0147f);
+
+            LLJoint* joint = new LLJoint("Wrist", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::TWIST_LIMITED_CONE_CONSTRAINT;
             info.mVectors.push_back(local_position); // forward_axis
@@ -1521,9 +1685,10 @@ namespace tut
             info.mFloats.push_back(-0.05f); // min_twist
             info.mFloats.push_back(0.05f); // max_twist
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
-        S16 WRIST_INDEX = joint_id;
+        //S16 WRIST_INDEX = joint_id;
         ++joint_id;
 
         if (!with_fingers)
@@ -1535,6 +1700,14 @@ namespace tut
         {
             LLVector3 local_position(0.01274f,0.09898f,0.0147f);
             LLVector3 bone(-0.00098f,0.0392f,-0.00588f);
+
+            LLJoint* joint = new LLJoint("Middle1", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::DOUBLE_LIMITED_HINGE_CONSTRAINT;
             info.mVectors.push_back(local_position); // forward_axis
@@ -1544,7 +1717,8 @@ namespace tut
             info.mFloats.push_back(0.0f); // min_pitch
             info.mFloats.push_back(0.942478f); // max_pitch
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, WRIST_INDEX, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         ++joint_id;
 
@@ -1552,6 +1726,14 @@ namespace tut
         {
             LLVector3 local_position(-0.00098f,0.0392f,-0.00588f);
             LLVector3 bone(-0.00098,0.04802,-0.00784);
+
+            LLJoint* joint = new LLJoint("Middle2", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::KNEE_CONSTRAINT;
             LLVector3 forward_axis = local_position;
@@ -1561,7 +1743,8 @@ namespace tut
             info.mFloats.push_back(0.0f); // min_bend
             info.mFloats.push_back(1.5708f); // max_bend
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         ++joint_id;
 
@@ -1569,6 +1752,14 @@ namespace tut
         {
             LLVector3 local_position(-0.00098f,0.04802f,-0.00784f);
             LLVector3 bone(-0.00196f,0.03234f,-0.00588f);
+
+            LLJoint* joint = new LLJoint("Middle3", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::KNEE_CONSTRAINT;
             LLVector3 forward_axis = local_position;
@@ -1578,7 +1769,8 @@ namespace tut
             info.mFloats.push_back(0.0f); // min_bend
             info.mFloats.push_back(1.25664f); // max_bend
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         //S16 MIDDLE_END_INDEX = joint_id;
         ++joint_id;
@@ -1587,6 +1779,14 @@ namespace tut
         {
             LLVector3 local_position(0.03724f,0.09506f,0.0147f);
             LLVector3 bone(0.01666f,0.03528f,-0.00588f);
+
+            LLJoint* joint = new LLJoint("Index1", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::DOUBLE_LIMITED_HINGE_CONSTRAINT;
             info.mVectors.push_back(local_position); // forward_axis
@@ -1596,7 +1796,8 @@ namespace tut
             info.mFloats.push_back(0.0f); // min_pitch
             info.mFloats.push_back(1.39626f); // max_pitch
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, WRIST_INDEX, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         ++joint_id;
 
@@ -1604,6 +1805,14 @@ namespace tut
         {
             LLVector3 local_position(0.01666f,0.03528f,-0.00588f);
             LLVector3 bone(0.01372f,0.03136f,-0.00588f);
+
+            LLJoint* joint = new LLJoint("Index2", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::KNEE_CONSTRAINT;
             LLVector3 forward_axis = local_position;
@@ -1613,7 +1822,8 @@ namespace tut
             info.mFloats.push_back(0.0f); // min_bend
             info.mFloats.push_back(1.5708f); // max_bend
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         ++joint_id;
 
@@ -1621,6 +1831,14 @@ namespace tut
         {
             LLVector3 local_position(0.01372f,0.03136f,-0.00588f);
             LLVector3 bone(0.01078f,0.0245f,-0.00392f);
+
+            LLJoint* joint = new LLJoint("Index3", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::KNEE_CONSTRAINT;
             LLVector3 forward_axis = local_position;
@@ -1630,7 +1848,8 @@ namespace tut
             info.mFloats.push_back(0.0f); // min_bend
             info.mFloats.push_back(1.25664f); // max_bend
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         //S16 INDEX_END_INDEX = joint_id;
         ++joint_id;
@@ -1639,6 +1858,14 @@ namespace tut
         {
             LLVector3 local_position(-0.0098f,0.09702f,0.00882f);
             LLVector3 bone(-0.01274f,0.03724f,-0.00784f);
+
+            LLJoint* joint = new LLJoint("Ring1", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::DOUBLE_LIMITED_HINGE_CONSTRAINT;
             info.mVectors.push_back(local_position); // forward_axis
@@ -1648,7 +1875,8 @@ namespace tut
             info.mFloats.push_back(0.0f); // min_pitch
             info.mFloats.push_back(1.39626f); // max_pitch
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, WRIST_INDEX, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         ++joint_id;
 
@@ -1656,6 +1884,14 @@ namespace tut
         {
             LLVector3 local_position(-0.01274f,0.03724f,-0.00784f);
             LLVector3 bone(-0.01274f,0.0392f,-0.00882f);
+
+            LLJoint* joint = new LLJoint("Ring2", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::KNEE_CONSTRAINT;
             LLVector3 forward_axis = local_position;
@@ -1665,7 +1901,8 @@ namespace tut
             info.mFloats.push_back(0.0f); // min_bend
             info.mFloats.push_back(1.5708f); // max_bend
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         ++joint_id;
 
@@ -1673,6 +1910,14 @@ namespace tut
         {
             LLVector3 local_position(-0.01274f,0.0392f,-0.00882f);
             LLVector3 bone(-0.0098f,0.02744f,-0.00588f);
+
+            LLJoint* joint = new LLJoint("Ring3", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::KNEE_CONSTRAINT;
             LLVector3 forward_axis = local_position;
@@ -1682,7 +1927,8 @@ namespace tut
             info.mFloats.push_back(0.0f); // min_bend
             info.mFloats.push_back(1.25664f); // max_bend
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         //S16 RING_END_INDEX = joint_id;
         ++joint_id;
@@ -1691,6 +1937,14 @@ namespace tut
         {
             LLVector3 local_position(-0.03038f,0.0931f,0.00294f);
             LLVector3 bone(-0.02352f,0.0245f,-0.00588f);
+
+            LLJoint* joint = new LLJoint("Pinky1", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::DOUBLE_LIMITED_HINGE_CONSTRAINT;
             info.mVectors.push_back(local_position); // forward_axis
@@ -1700,7 +1954,8 @@ namespace tut
             info.mFloats.push_back(0.0f); // min_pitch
             info.mFloats.push_back(1.39626f); // max_pitch
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, WRIST_INDEX, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         ++joint_id;
 
@@ -1708,6 +1963,14 @@ namespace tut
         {
             LLVector3 local_position(-0.02352f,0.0245f,-0.00588f);
             LLVector3 bone(-0.0147f,0.01764f,-0.00392f);
+
+            LLJoint* joint = new LLJoint("Pinky2", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::KNEE_CONSTRAINT;
             LLVector3 forward_axis = local_position;
@@ -1717,7 +1980,8 @@ namespace tut
             info.mFloats.push_back(0.0f); // min_bend
             info.mFloats.push_back(1.5708f); // max_bend
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         ++joint_id;
 
@@ -1725,6 +1989,14 @@ namespace tut
         {
             LLVector3 local_position(-0.0147f,0.01764f,-0.00392f);
             LLVector3 bone(-0.01274f,0.01568f,-0.00392f);
+
+            LLJoint* joint = new LLJoint("Pinky3", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::KNEE_CONSTRAINT;
             LLVector3 forward_axis = local_position;
@@ -1734,7 +2006,8 @@ namespace tut
             info.mFloats.push_back(0.0f); // min_bend
             info.mFloats.push_back(1.25664f); // max_bend
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         //S16 PINKY_END_INDEX = joint_id;
         ++joint_id;
@@ -1743,6 +2016,14 @@ namespace tut
         {
             LLVector3 local_position(0.03038f,0.02548f,0.00392f);
             LLVector3 bone(0.02744f,0.03136f,-0.00098f);
+
+            LLJoint* joint = new LLJoint("Thumb1", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::DOUBLE_LIMITED_HINGE_CONSTRAINT;
             info.mVectors.push_back(local_position); // forward_axis
@@ -1752,7 +2033,8 @@ namespace tut
             info.mFloats.push_back(-0.1f); // min_pitch
             info.mFloats.push_back(0.785398f); // max_pitch
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, WRIST_INDEX, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         ++joint_id;
 
@@ -1760,6 +2042,14 @@ namespace tut
         {
             LLVector3 local_position(0.02744f,0.03136f,-0.00098f);
             LLVector3 bone(0.02254f,0.03038f,-0.00098f);
+
+            LLJoint* joint = new LLJoint("Thumb2", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::KNEE_CONSTRAINT;
             LLVector3 forward_axis = local_position;
@@ -1770,7 +2060,8 @@ namespace tut
             info.mFloats.push_back(0.0f); // min_bend
             info.mFloats.push_back(0.942478f); // max_bend
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         ++joint_id;
 
@@ -1778,6 +2069,14 @@ namespace tut
         {
             LLVector3 local_position(0.02254f,0.03038f,-0.00098f);
             LLVector3 bone(0.0147f,0.0245f,0.0f);
+
+            LLJoint* joint = new LLJoint("Thumb3", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::KNEE_CONSTRAINT;
             LLVector3 forward_axis = local_position;
@@ -1788,7 +2087,8 @@ namespace tut
             info.mFloats.push_back(0.0f); // min_bend
             info.mFloats.push_back(1.25664f); // max_bend
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         //S16 THUMB_END_INDEX = joint_id;
     }
@@ -1805,7 +2105,8 @@ namespace tut
         solver.setAcceptableError(ACCEPTABLE_ERROR);
 
         bool with_fingers = false;
-        build_skeleton_arm(factory, solver, with_fingers);
+        std::vector<LLJoint*> joints;
+        build_skeleton_arm(joints, factory, solver, with_fingers);
         constexpr S16 WRIST_INDEX = 6;
         solver.addWristID(WRIST_INDEX);
 
@@ -1835,8 +2136,15 @@ namespace tut
         solver.solve();
         F32 error = dist_vec(solver.getJointWorldEndPos(ELBOW_INDEX), target_position);
         ensure("LLIK::Solver elbow should reach target", error < ACCEPTABLE_ERROR);
+
+        // cleanup LLJoints
+        for (auto joint: joints)
+        {
+            delete joint;
+        }
     }
 
+
 #ifdef ENABLE_FAILING_UNIT_TESTS
     // LLIK::Solver : wrist position and fingers
     // DISABLED: we're not trying to handle fingers yet
@@ -1898,7 +2206,7 @@ namespace tut
     }
 #endif // ENABLE_FAILING_UNIT_TESTS
 
-    void build_skeleton_with_head_and_arm(LLIKConstraintFactory& factory, LLIK::Solver& solver)
+    void build_skeleton_with_head_and_arm(std::vector<LLJoint*>& joints, LLIKConstraintFactory& factory, LLIK::Solver& solver)
 	{
         // This builds spine, head, and left arm based on the default SL avatar.
         //
@@ -1920,8 +2228,17 @@ namespace tut
         {
             LLVector3 local_position = LLVector3::zero;
             LLVector3 bone(0.0f,0.0f,0.08757f);
+
+            LLJoint* joint = new LLJoint("Pelvis", nullptr);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::ptr_t null_constraint = factory.getConstraint(LLIK::Constraint::Info());
-            solver.addJoint(joint_id, -1, local_position, bone, null_constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, null_constraint);
         }
         ++joint_id;
 
@@ -1929,6 +2246,14 @@ namespace tut
         {
             LLVector3 local_position(0.0f,0.0f,0.08757f);
             LLVector3 bone(-0.014445f,0.0f,0.213712f);
+
+            LLJoint* joint = new LLJoint("Torso", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::TWIST_LIMITED_CONE_CONSTRAINT;
             info.mVectors.push_back(local_position); // forward_axis
@@ -1936,7 +2261,8 @@ namespace tut
             info.mFloats.push_back(-0.0628319f); // min_twist
             info.mFloats.push_back(0.0628319f); // max_twist
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         ++joint_id;
 
@@ -1944,6 +2270,14 @@ namespace tut
         {
             LLVector3 local_position(-0.015318f,0.0f,0.213712f);
             LLVector3 bone(-0.01f,0.0f,0.2151);
+
+            LLJoint* joint = new LLJoint("Chest", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::TWIST_LIMITED_CONE_CONSTRAINT;
             info.mVectors.push_back(local_position); // forward_axis
@@ -1951,7 +2285,8 @@ namespace tut
             info.mFloats.push_back(-0.0628319f); // min_twist
             info.mFloats.push_back(0.0628319f); // max_twist
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         S16 CHEST_ID = joint_id;
         ++joint_id;
@@ -1960,6 +2295,14 @@ namespace tut
         {
             LLVector3 local_position(-0.01f, 0.0, 0.251f);
             LLVector3 bone(0.0f, 0.0f, 0.077f);
+
+            LLJoint* joint = new LLJoint("Neck", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::TWIST_LIMITED_CONE_CONSTRAINT;
             info.mVectors.push_back(local_position); // forward_axis
@@ -1967,7 +2310,8 @@ namespace tut
             info.mFloats.push_back(-0.0628319f); // min_twist
             info.mFloats.push_back(0.0628319f); // max_twist
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         ++joint_id;
 
@@ -1975,6 +2319,14 @@ namespace tut
         {
             LLVector3 local_position(0.0f, 0.0f, 0.076);
             LLVector3 bone(0.0f, 0.0f,0.079f);
+
+            LLJoint* joint = new LLJoint("Head", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::TWIST_LIMITED_CONE_CONSTRAINT;
             info.mVectors.push_back(local_position); // forward_axis
@@ -1982,7 +2334,8 @@ namespace tut
             info.mFloats.push_back(-0.0628319f); // min_twist
             info.mFloats.push_back(0.0628319f); // max_twist
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         ++joint_id;
 
@@ -1990,12 +2343,21 @@ namespace tut
         {
             LLVector3 local_position(-0.021f,0.123583f,0.165f);
             LLVector3 bone(0.0f,0.10349f,0.0f);
+
+            LLJoint* joint = new LLJoint("Collar", joints[CHEST_ID]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::SIMPLE_CONE_CONSTRAINT;
             info.mVectors.push_back(local_position); // forward_axis
             info.mFloats.push_back(0.15708f); // cone_angle
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, CHEST_ID, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         ++joint_id;
 
@@ -2003,6 +2365,14 @@ namespace tut
         {
             LLVector3 local_position(0.0f,0.10349f,0.0f);
             LLVector3 bone(0.0f,0.260152f,0.0f);
+
+            LLJoint* joint = new LLJoint("Shoulder", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::TWIST_LIMITED_CONE_CONSTRAINT;
             info.mVectors.push_back(local_position); // forward_axis
@@ -2011,7 +2381,8 @@ namespace tut
             info.mFloats.push_back(-0.5f * F_PI); // min_twist
             info.mFloats.push_back(0.5f * F_PI); // max_twist
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         ++joint_id;
 
@@ -2019,6 +2390,14 @@ namespace tut
         {
             LLVector3 local_position(0.0f,0.260152f,0.0f);
             LLVector3 bone(0.0f,0.2009f,0.0f);
+
+            LLJoint* joint = new LLJoint("Elbow", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::ELBOW_CONSTRAINT;
             info.mVectors.push_back(local_position); // forward_axis
@@ -2028,7 +2407,8 @@ namespace tut
             info.mFloats.push_back(-0.785398f); // min_twist
             info.mFloats.push_back(2.35619f); // max_twist
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         //S16 ELBOW_INDEX = joint_id;
         ++joint_id;
@@ -2037,6 +2417,14 @@ namespace tut
         {
             LLVector3 local_position(0.0f,0.2009f,0.0f);
             LLVector3 bone(0.01274f,0.09898f,0.0147f);
+
+            LLJoint* joint = new LLJoint("Wrist", joints[joint_id-1]);
+            joint->setIsBone(true);
+            joint->setJointNum(S32(joint_id));
+            joint->setPosition(local_position);
+            joint->setEnd(bone);
+            joints.push_back(joint);
+
             LLIK::Constraint::Info info;
             info.mType = LLIK::Constraint::Info::TWIST_LIMITED_CONE_CONSTRAINT;
             info.mVectors.push_back(local_position); // forward_axis
@@ -2044,7 +2432,8 @@ namespace tut
             info.mFloats.push_back(-0.05f); // min_twist
             info.mFloats.push_back(0.05f); // max_twist
             LLIK::Constraint::ptr_t constraint = factory.getConstraint(info);
-            solver.addJoint(joint_id, joint_id-1, local_position, bone, constraint);
+
+            solver.addJoint(joint_id, joint_id-1, joint, constraint);
         }
         S16 WRIST_ID = joint_id;
 
@@ -2066,7 +2455,8 @@ namespace tut
         constexpr F32 ACCEPTABLE_ERROR = 2.0e-2f;
         solver.setAcceptableError(ACCEPTABLE_ERROR);
 
-        build_skeleton_with_head_and_arm(factory, solver);
+        std::vector<LLJoint*> joints;
+        build_skeleton_with_head_and_arm(joints, factory, solver);
         constexpr U16 WRIST_INDEX = 8;
         solver.addWristID(WRIST_INDEX);
 
@@ -2095,5 +2485,11 @@ namespace tut
         ensure("LLIK::Solver wrist should reach target position", position_error < ACCEPTABLE_ERROR);
         constexpr F32 MIN_ANGLE_ERROR = 0.005f * F_PI;
         ensure("LLIK::Solver wrist should reach target orientation", LLQuaternion::almost_equal(target_orientation, actual_orientation, MIN_ANGLE_ERROR));
+
+        // cleanup LLJoints
+        for (auto joint: joints)
+        {
+            delete joint;
+        }
     }
 }
diff --git a/indra/llcharacter/tests/lljoint_test.cpp b/indra/llcharacter/tests/lljoint_test.cpp
index 617f31b0e46e5877198f25ce8263606ac93a9422..ba227d34550ba5618b06bf9e78817965dca039da 100644
--- a/indra/llcharacter/tests/lljoint_test.cpp
+++ b/indra/llcharacter/tests/lljoint_test.cpp
@@ -234,9 +234,6 @@ namespace tut
         3) void updateWorldPRSParent();
         4) void updateWorldMatrix();
         5) LLXformMatrix *getXform() { return &mXform; }
-        6) void setConstraintSilhouette(LLDynamicArray<LLVector3>& silhouette);
-        7) void clampRotation(LLQuaternion old_rot, LLQuaternion new_rot);
-
 	*/
 }
 
diff --git a/indra/newview/llpuppetevent.cpp b/indra/newview/llpuppetevent.cpp
index d1a78e2297681e3246864a07f15a595612219050..066d59c69bc6d5e3bccb3188a6658bb125b704d5 100644
--- a/indra/newview/llpuppetevent.cpp
+++ b/indra/newview/llpuppetevent.cpp
@@ -177,7 +177,7 @@ void LLPuppetJointEvent::interpolate(F32 del, const LLPuppetJointEvent& A, const
     {
         mPosition = (1.0f - del) * A.mPosition + del * B.mPosition;
     }
-    if ((mMask & LLIK::FLAG_LOCAL_SCALE) && (B.mMask & LLIK::FLAG_LOCAL_SCALE))
+    if ((mMask & LLIK::CONFIG_FLAG_LOCAL_SCALE) && (B.mMask & LLIK::CONFIG_FLAG_LOCAL_SCALE))
     {
         mScale = (1.0f - del) * A.mScale + del * B.mScale;
     }
@@ -187,19 +187,19 @@ void LLPuppetJointEvent::setRotation(const LLQuaternion& rotation)
 {
     mRotation = rotation;
     mRotation.normalize();
-    mMask |= (mRefFrame == PARENT_FRAME ? LLIK::FLAG_LOCAL_ROT : LLIK::FLAG_TARGET_ROT);
+    mMask |= (mRefFrame == PARENT_FRAME ? LLIK::CONFIG_FLAG_LOCAL_ROT : LLIK::CONFIG_FLAG_TARGET_ROT);
 }
 
 void LLPuppetJointEvent::setPosition(const LLVector3& position)
 {
     mPosition = position;
-    mMask |= (mRefFrame == PARENT_FRAME ? LLIK::FLAG_LOCAL_POS : LLIK::FLAG_TARGET_POS);
+    mMask |= (mRefFrame == PARENT_FRAME ? LLIK::CONFIG_FLAG_LOCAL_POS : LLIK::CONFIG_FLAG_TARGET_POS);
 }
 
 void LLPuppetJointEvent::setScale(const LLVector3& scale)
 {
     mScale = scale;
-    mMask |= LLIK::FLAG_LOCAL_SCALE;
+    mMask |= LLIK::CONFIG_FLAG_LOCAL_SCALE;
 }
 
 void LLPuppetJointEvent::setJointID(S32 id)
@@ -214,7 +214,7 @@ size_t LLPuppetJointEvent::getSize() const
     num_bytes += sizeof(S16) + sizeof(S8);  // mJointID, mMask
     num_bytes += (mMask & LLIK::MASK_ROT) ? BYTES_PER_VEC_3 : 0;
     num_bytes += (mMask & LLIK::MASK_POS) ? BYTES_PER_VEC_3 : 0;
-    num_bytes += (mMask & LLIK::FLAG_LOCAL_SCALE) ? BYTES_PER_VEC_3 : 0;
+    num_bytes += (mMask & LLIK::CONFIG_FLAG_LOCAL_SCALE) ? BYTES_PER_VEC_3 : 0;
     return num_bytes;
 }
 
@@ -238,7 +238,7 @@ size_t LLPuppetJointEvent::pack(U8* wptr)
     {
         offset += pack_vec3(wptr+offset, mPosition);
     }
-    if (mMask & LLIK::FLAG_LOCAL_SCALE)
+    if (mMask & LLIK::CONFIG_FLAG_LOCAL_SCALE)
     {
         offset += pack_vec3(wptr+offset, mScale);
     }
@@ -248,7 +248,7 @@ size_t LLPuppetJointEvent::pack(U8* wptr)
         LL_CONT << " rot=" << mRotation;
     if (mMask & LLIK::MASK_POS)
         LL_CONT << " pos=" << mPosition;
-    if (mMask & LLIK::FLAG_LOCAL_SCALE)
+    if (mMask & LLIK::CONFIG_FLAG_LOCAL_SCALE)
         LL_CONT << " scale=" << mScale;
     LL_CONT << " raw=" << LLError::arraylogger(wptr, offset) << " in frame " << (S32)gFrameCount << LL_ENDL;
 
@@ -272,7 +272,7 @@ size_t LLPuppetJointEvent::unpack(U8* wptr)
     {
         offset += unpack_vec3(wptr+offset, mPosition);
     }
-    if (mMask & LLIK::FLAG_LOCAL_SCALE)
+    if (mMask & LLIK::CONFIG_FLAG_LOCAL_SCALE)
     {
         offset += unpack_vec3(wptr+offset, mScale);
     }
@@ -282,7 +282,7 @@ size_t LLPuppetJointEvent::unpack(U8* wptr)
         LL_CONT << " rot=" << mRotation;
     if (mMask & LLIK::MASK_POS)
         LL_CONT << " pos=" << mPosition;
-    if (mMask & LLIK::FLAG_LOCAL_SCALE)
+    if (mMask & LLIK::CONFIG_FLAG_LOCAL_SCALE)
         LL_CONT << " scale=" << mScale;
     LL_CONT << " raw=" << LLError::arraylogger(wptr, offset) << " in frame " << (S32)gFrameCount << LL_ENDL;
 
diff --git a/indra/newview/llpuppetevent.h b/indra/newview/llpuppetevent.h
index 62b0fdd686e8193b61fd9b78856f811b963dee1f..c2f7b2ebcbea80260bcae58d92651dc42f7fc640 100644
--- a/indra/newview/llpuppetevent.h
+++ b/indra/newview/llpuppetevent.h
@@ -59,7 +59,7 @@ class LLPuppetJointEvent
     void setPosition(const LLVector3& position);
     void setScale(const LLVector3& scale);
     void setJointID(S32 id);
-    void disableConstraint() { mMask |= LLIK::FLAG_DISABLE_CONSTRAINT; }
+    void disableConstraint() { mMask |= LLIK::CONFIG_FLAG_DISABLE_CONSTRAINT; }
 
     S16 getJointID() const { return mJointID; }
     LLQuaternion getRotation() const { return mRotation; }
diff --git a/indra/newview/llpuppetmodule.cpp b/indra/newview/llpuppetmodule.cpp
index 56c2f937b78996863032d9c314af3f3e4f9b7337..531bc9d795b472f82db5fc65f96dfbacd1dfdbba 100644
--- a/indra/newview/llpuppetmodule.cpp
+++ b/indra/newview/llpuppetmodule.cpp
@@ -193,17 +193,30 @@ void processJointData(const std::string& key, const LLSD& data)
                 v.mV[VY] = value.get(1).asReal();
                 v.mV[VZ] = value.get(2).asReal();
 
+                // sanity check Puppetry input
+                constexpr F32 MAX_PUPPETRY_INPUT = 10.0f;
+                constexpr F32 MIN_PUPPETRY_INPUT = -MAX_PUPPETRY_INPUT;
+                v.clamp(MIN_PUPPETRY_INPUT, MAX_PUPPETRY_INPUT);
+
+                // Note: LLVector3::clamp() doesn't protect against NaN input
+                // so we explicitly check it here
+                F32 v_length_squared = v.lengthSquared();
+                if (llisnan(v_length_squared))
+                {
+                    continue;
+                }
+
                 if ( param_name == "r" || param_name == "rotation" )
                 {
                     LLQuaternion q;
                     // Packed quaternions have the imaginary part (xyz)
                     // copy the imaginary part
                     memcpy(q.mQ, v.mV, 3 * sizeof(F32));
+
                     // compute the real part
-                    F32 imaginary_length_squared = q.mQ[VX] * q.mQ[VX] + q.mQ[VY] * q.mQ[VY] + q.mQ[VZ] * q.mQ[VZ];
-                    if (imaginary_length_squared > 1.0f)
+                    if (v_length_squared > 1.0f)
                     {
-                        F32 imaginary_length = sqrtf(imaginary_length_squared);
+                        F32 imaginary_length = sqrtf(v_length_squared);
                         q.mQ[VX] /= imaginary_length;
                         q.mQ[VY] /= imaginary_length;
                         q.mQ[VZ] /= imaginary_length;
@@ -211,7 +224,7 @@ void processJointData(const std::string& key, const LLSD& data)
                     }
                     else
                     {
-                        q.mQ[VW] = sqrtf(1.0f - imaginary_length_squared);
+                        q.mQ[VW] = sqrtf(1.0f - v_length_squared);
                     }
                     joint_event.setRotation(q);
                 }
diff --git a/indra/newview/llpuppetmotion.cpp b/indra/newview/llpuppetmotion.cpp
index bae5e948467867c77bc424e1b77c12f8dee9c678..46bd53f604bfcda98fe423d8e72a9c12e04ea2eb 100755
--- a/indra/newview/llpuppetmotion.cpp
+++ b/indra/newview/llpuppetmotion.cpp
@@ -713,19 +713,13 @@ LLSD LLPuppetMotion::getSkeletonData()
     return skeleton_sd;
 }
 
-void LLPuppetMotion::reconfigureJoints()
+void LLPuppetMotion::updateSkeletonGeometry()
 {
-    LLSD skeleton_sd;
     for (state_map_t::iterator iter=mJointStates.begin(); iter!=mJointStates.end(); ++iter)
     {
         S16 joint_id = iter->first;
-        LLPointer<LLJointState> joint_state = iter->second;
-        LLJoint* joint = joint_state->getJoint();
-        LLVector3 local_pos_in_parent_frame = joint->getPosition().scaledVec(joint->getScale());
-        LLVector3 bone_in_local_frame = joint->getEnd().scaledVec(joint->getScale());
         LLIK::Constraint::ptr_t constraint = get_constraint_by_joint_id(joint_id);
-        mIKSolver.reconfigureJoint(joint_id, local_pos_in_parent_frame, bone_in_local_frame, constraint);
-        addJointToSkeletonData(skeleton_sd, joint, local_pos_in_parent_frame, bone_in_local_frame);
+        mIKSolver.resetJointGeometry(joint_id, constraint);
     }
     measureArmSpan();
 }
@@ -803,24 +797,26 @@ void LLPuppetMotion::solveIKAndHarvestResults(const LLIK::Solver::joint_config_m
 
 	LLPuppetEvent broadcast_event;
     const LLIK::Solver::joint_list_t& active_joints = mIKSolver.getActiveJoints();
-    LLVector3 pos;
-    LLQuaternion rot;
     for (auto joint: active_joints)
     {
         S16 id = joint->getID();
-        U8 flags = joint->getConfigFlags();
-        pos = joint->getLocalPos();
-        rot = joint->getLocalRot();
+        U8 flags = joint->getHarvestFlags();
         if (local_puppetry)
         {
             LLPointer<LLJointState> joint_state = mJointStates[id];
-            if (flags | LLIK::FLAG_LOCAL_POS)
+            U32 usage = U32(flags & LLIK::MASK_JOINT_STATE_USAGE);
+            joint_state->setUsage(usage);
+            if (flags & LLIK::CONFIG_FLAG_LOCAL_POS)
+            {
+                joint_state->setPosition(joint->getPreScaledLocalPos());
+            }
+            if (flags & LLIK::CONFIG_FLAG_LOCAL_ROT)
             {
-                joint_state->setPosition(pos);
+                joint_state->setRotation(joint->getLocalRot());
             }
-            if (flags | LLIK::FLAG_LOCAL_ROT)
+            if (flags & LLIK::CONFIG_FLAG_LOCAL_SCALE)
             {
-                joint_state->setRotation(rot);
+                joint_state->setScale(joint->getLocalScale());
             }
             rememberPosedJoint(id, joint_state, now);
         }
@@ -829,15 +825,21 @@ void LLPuppetMotion::solveIKAndHarvestResults(const LLIK::Solver::joint_config_m
 			LLPuppetJointEvent joint_event;
 			joint_event.setJointID(id);
             joint_event.setReferenceFrame(LLPuppetJointEvent::PARENT_FRAME);
-            if (flags | LLIK::FLAG_LOCAL_POS)
+            if (flags & LLIK::CONFIG_FLAG_LOCAL_POS)
             {
-                joint_event.setPosition(pos);
+                // we send positions with correct scale
+                // so they can be applied on the receiving end without modification
+                joint_event.setPosition(joint->getPreScaledLocalPos());
             }
-            if (flags | LLIK::FLAG_LOCAL_ROT)
+            if (flags & LLIK::CONFIG_FLAG_LOCAL_ROT)
             {
-                joint_event.setRotation(rot);
+                joint_event.setRotation(joint->getLocalRot());
             }
-            if (flags | LLIK::FLAG_DISABLE_CONSTRAINT)
+            if (flags & LLIK::CONFIG_FLAG_LOCAL_SCALE)
+            {
+                joint_event.setScale(joint->getLocalScale());
+            }
+            if (flags & LLIK::CONFIG_FLAG_DISABLE_CONSTRAINT)
             {
                 joint_event.disableConstraint();
             }
@@ -861,7 +863,7 @@ void LLPuppetMotion::applyEvent(const LLPuppetJointEvent& event, U64 now, LLIK::
         LLIK::Joint::Config config;
         bool something_changed = false;
         U8 mask = event.getMask();
-        bool local = (bool)(mask & LLIK::FLAG_LOCAL_ROT);
+        bool local = (bool)(mask & LLIK::CONFIG_FLAG_LOCAL_ROT);
         if (mask & LLIK::MASK_ROT)
         {
             if (local)
@@ -876,19 +878,18 @@ void LLPuppetMotion::applyEvent(const LLPuppetJointEvent& event, U64 now, LLIK::
         }
         if (mask & LLIK::MASK_POS)
         {
-            // don't forget to scale by 0.5*mArmSpan
             if (local)
             {
-                // TODO: figure out what to do about scale of local positions
                 config.setLocalPos(event.getPosition());
             }
             else
             {
-                config.setTargetPos(event.getPosition() * 0.5f * mArmSpan);
+                // don't forget to scale by 0.5*mArmSpan
+                config.setTargetPos(event.getPosition() * (0.5f * mArmSpan));
             }
             something_changed = true;
         }
-        if (mask & LLIK::FLAG_DISABLE_CONSTRAINT)
+        if (mask & LLIK::CONFIG_FLAG_DISABLE_CONSTRAINT)
         {
             config.disableConstraint();
             something_changed = true;
@@ -902,7 +903,7 @@ void LLPuppetMotion::applyEvent(const LLPuppetJointEvent& event, U64 now, LLIK::
 
 void LLPuppetMotion::updateFromExpression(Timestamp now)
 {
-    // Note: if we get here: mIsSelf must be true
+    // Note: if we get here mIsSelf must be true
     LLIK::Solver::joint_config_map_t configs;
     for(const auto& event: mExpressionEvents)
     {
@@ -918,7 +919,7 @@ void LLPuppetMotion::updateFromExpression(Timestamp now)
         U8 mask = event.getMask();
         if (mask & LLIK::MASK_ROT)
         {
-            if (mask & LLIK::FLAG_LOCAL_ROT)
+            if (mask & LLIK::CONFIG_FLAG_LOCAL_ROT)
             {
                 config.setLocalRot(event.getRotation());
             }
@@ -930,19 +931,18 @@ void LLPuppetMotion::updateFromExpression(Timestamp now)
         }
         if (mask & LLIK::MASK_POS)
         {
-            // don't forget to scale by 0.5*mArmSpan
-            if (mask & LLIK::FLAG_LOCAL_POS)
+            if (mask & LLIK::CONFIG_FLAG_LOCAL_POS)
             {
-                // TODO: figure out what to do about scale of local positions
                 config.setLocalPos(event.getPosition());
             }
             else
             {
-                config.setTargetPos(event.getPosition() * 0.5f * mArmSpan);
+                // don't forget to scale by 0.5*mArmSpan
+                config.setTargetPos(event.getPosition() * (0.5f * mArmSpan));
             }
             something_changed = true;
         }
-        if (mask & LLIK::FLAG_DISABLE_CONSTRAINT)
+        if (mask & LLIK::CONFIG_FLAG_DISABLE_CONSTRAINT)
         {
             config.disableConstraint();
             something_changed = true;
@@ -973,18 +973,26 @@ void LLPuppetMotion::applyBroadcastEvent(const LLPuppetJointEvent& event, Timest
     state_map_t::iterator state_iter = mJointStates.find(joint_id);
     if (state_iter != mJointStates.end())
     {
-        U8 mask = event.getMask();
-        llassert((mask & LLIK::MASK_TARGET) == 0); // broadcast event always in parent-frame
-        if (mask & LLIK::MASK_ROT)
+        LLPointer<LLJointState> joint_state = state_iter->second;
+        U8 flags = event.getMask();
+        // Note: we assume broadcast event always in parent-frame
+        // e.g. (flags & LLIK::MASK_TARGET) == 0
+        if (flags & LLIK::CONFIG_FLAG_LOCAL_POS)
         {
-            state_iter->second->setRotation(event.getRotation());
+            // we expect received position to be scaled correctly
+            // so it can be applied without modification
+            joint_state->setPosition(event.getPosition());
         }
-        if (mask & LLIK::MASK_POS)
+        if (flags & LLIK::CONFIG_FLAG_LOCAL_ROT)
+        {
+            joint_state->setRotation(event.getRotation());
+        }
+        if (flags & LLIK::CONFIG_FLAG_LOCAL_SCALE)
         {
-            // Note: scale already set by broadcaster (e.g. 1.0 = 0.5*mArmSpan)
-            state_iter->second->setPosition(event.getPosition());
+            joint_state->setScale(event.getScale());
         }
-        rememberPosedJoint(joint_id, state_iter->second, now);
+        joint_state->setUsage((U32)(flags & LLIK::MASK_JOINT_STATE_USAGE));
+        rememberPosedJoint(joint_id, joint_state, now);
     }
 }
 
@@ -1280,15 +1288,12 @@ void LLPuppetMotion::collectJoints(LLJoint* joint)
     }
     // END HACK
 
-    LLPointer<LLJointState> joint_state = new LLJointState;
-    joint_state->setJoint(joint);
-    joint_state->setUsage(LLJointState::ROT | LLJointState::POS);
+    LLPointer<LLJointState> joint_state = new LLJointState(joint);
     S16 joint_id = joint->getJointNum();
     mJointStates[joint_id] = joint_state;
-    LLVector3 local_pos_in_parent_frame = joint->getPosition().scaledVec(joint->getScale());
-    LLVector3 bone_in_local_frame = joint->getEnd().scaledVec(joint->getScale());
+
     LLIK::Constraint::ptr_t constraint = get_constraint_by_joint_id(joint_id);
-    mIKSolver.addJoint(joint_id, parent_id, local_pos_in_parent_frame, bone_in_local_frame, constraint);
+    mIKSolver.addJoint(joint_id, parent_id, joint, constraint);
 
     // Recurse through the children of this joint and add them to our joint control list
     for (LLJoint::joints_t::iterator itr = joint->mChildren.begin();
diff --git a/indra/newview/llpuppetmotion.h b/indra/newview/llpuppetmotion.h
index 8baffd2963d0a7f0870f5a1c20526bceb2385d76..1285f119a901be32402840beaaafb7d48b95ce1d 100755
--- a/indra/newview/llpuppetmotion.h
+++ b/indra/newview/llpuppetmotion.h
@@ -159,7 +159,7 @@ class LLPuppetMotion :
     BOOL canDeprecate() override { return FALSE; }
     void addJointToSkeletonData(LLSD& skeleton_sd, LLJoint* joint, const LLVector3& parent_rel_pos, const LLVector3& tip_rel_end_pos);
     LLSD getSkeletonData();
-    void reconfigureJoints();
+    void updateSkeletonGeometry();
 
 private:
     void measureArmSpan();
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 6237dd8f8ff4741a4728fa80f5127cf7489908f6..d3e43d06f486ba71ff06f8c56740b853330df16b 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -2034,7 +2034,7 @@ void LLVOAvatar::buildCharacter()
     // Currently disabled for control avatars (animated objects), enabled for all others.
     if (mEnableDefaultMotions)
     {
-	startDefaultMotions();
+		startDefaultMotions();
     }
 
 	//-------------------------------------------------------------------------
@@ -8273,7 +8273,7 @@ BOOL LLVOAvatar::processFullyLoadedChange(bool loading)
 	    LLPuppetMotion::ptr_t puppet_motion = getPuppetMotion();
         if (puppet_motion)
         {
-            puppet_motion->reconfigureJoints();
+            puppet_motion->updateSkeletonGeometry();
             LLEventPumps::instance().obtain("SkeletonUpdate").post(LLSD());
         }
     }
@@ -8289,7 +8289,7 @@ BOOL LLVOAvatar::processFullyLoadedChange(bool loading)
 		// Fix for jellydoll initially invisible
 		mNeedsImpostorUpdate = TRUE;
 		mLastImpostorUpdateReason = 6;
-	}	
+	}
 	return changed;
 }
 
@@ -10996,28 +10996,27 @@ void LLVOAvatar::setOverallAppearanceNormal()
 void LLVOAvatar::setOverallAppearanceJellyDoll()
 {
 	if (isControlAvatar())
+	{
 		return;
+	}
 
 	// stop current animations
+	for ( LLVOAvatar::AnimIterator anim_it= mPlayingAnimations.begin();
+		  anim_it != mPlayingAnimations.end();
+		  ++anim_it)
 	{
-		for ( LLVOAvatar::AnimIterator anim_it= mPlayingAnimations.begin();
-			  anim_it != mPlayingAnimations.end();
-			  ++anim_it)
 		{
-			{
-				stopMotion(anim_it->first, TRUE);
-			}
+			stopMotion(anim_it->first, TRUE);
 		}
 	}
 	processAnimationStateChanges();
 
 	// Start any needed anims for jellydoll
 	updateOverallAppearanceAnimations();
-	
+
     LLVector3 pelvis_pos = getJoint("mPelvis")->getPosition();
 	resetSkeleton(false);
     getJoint("mPelvis")->setPosition(pelvis_pos);
-
 }
 
 void LLVOAvatar::setOverallAppearanceInvisible()