This is indeed a bug, and one that I think we have fixed post-5.8.
In 5.8, when a state has no task considered for completion, MakeCompletionTasksMask() sets the reserved bit and then shifts it left once per task, so it lands at BitIndex + TasksNum. FStateTreeTasksCompletionStatus::Push() then calls ResetStatus(State.TasksNum), which only clears bits BitIndex to BitIndex + TasksNum - 1. The reserved bit is never cleared, and because siblings all start at the parent’s NextBitIndexForCompletionMask, a Succeeded status left there by a previous sibling is read back as the new state’s completion. The same TasksNum reset is also used for sustained states in FStateTreeExecutionContext, so that path has the same problem.
This is fixed in UE5 Main but has not made it into a 5.8 release. The fix adds a CompletionTasksNumberOfBits member to FCompactStateTreeState, fills it from the compiler’s NumberOfBitsNeeded (which includes the reserved bit), and passes it to both ResetStatus() calls instead of TasksNum:
https://github.com/EpicGames/UnrealEngine/commit/71b807f41310a603478f5a958d05a93dbf95cecb
Because it changes FCompactStateTreeState in a public header, it will not go into a hotfix. The new member is filled in by the compiler, so your StateTree assets must be recompiled after you take it.
My best recommendation is to cherry-pick that change into both of your engines. If you would rather not touch the header, you can get the same result locally by working out the bit count from the mask at the two ResetStatus() call sites:
// StateTreeTasksStatus.cpp, FStateTreeTasksCompletionStatus::Push()
const int32 ReservedBitIndex = State.CompletionTasksMaskBitsOffset + State.TasksNum;
const bool bHasReservedBit = ReservedBitIndex < 32 && (State.CompletionTasksMask & (1u << ReservedBitIndex)) != 0;
GetStatus(State).ResetStatus(State.TasksNum + (bHasReservedBit ? 1 : 0));
Apply the same change where StateTreeExecutionContext.cpp calls ResetStatus(CurrentState.TasksNum). The task bits sit below BitIndex + TasksNum, so the mask can only have that bit set when it is the reserved one.
I believe you already build from UE5 Main rather than the release branch, could you please confirm which UE5 Main CL or commit it is based on? If FStateTreeTasksCompletionStatus::Push() in that tree already calls ResetStatus(State.CompletionTasksNumberOfBits) and you still see this, that would be a different issue.
Let me know if this fix covers it or if others.
-James
[Attachment Removed]