Looking at your code and the videos, the issue is likely that your animation is overwriting the attachment change on the head_top slot each frame. When an animation has keyframes that control attachment visibility on a slot, the AnimationState will keep resetting that slot's attachment every time the animation is applied — overriding any manual set_attachment calls you make.
The fact that set_attachment("head_top", "head_top2") works in ready() but not alongside your other switching code suggests the animation hasn't started yet in ready(), so the attachment sticks. But once the animation is running and being applied every frame, it wins.
Notice that head_light switches fine — this is probably because the head_light slot is not keyed in your animation (or is keyed differently), while head_top is keyed in the animation with attachment keys that reset it.
Here are a few ways to fix this:
Option 1 — Don't key head_top in the animation. If the animation doesn't have attachment keyframes for the head_top slot, your manual set_attachment call will persist. Remove those keyframes from the animation in the Spine editor.
Option 2 — Use a second track for the attachment swap. Apply your attachment-changing animation on a higher track (e.g., track 1) so it overrides track 0. You can create a short animation in the Spine editor that simply sets the desired attachment, then play it on track 1.
Option 3 — Reset the slot after applying the animation. Call set_attachment after AnimationState has been applied for that frame. In spine-godot, the order of operations matters — the animation state is applied before the skeleton is rendered. You may be setting the attachment, then the animation state applies and overwrites it. Make sure your toggle_light_dir() is called after the animation update step, not before.
Option 4 — Use setSlotsToSetupPose carefully. If your animation uses a "reset" track approach, calling skeleton.set_slots_to_setup_pose() before applying your manual changes can help, but this is more disruptive.
The most robust long-term solution is Option 1 (don't key the slot in the animation) or Option 2 (use a dedicated track). Option 3 is the quickest fix without changing your Spine project — just ensure toggle_light_dir() is called after the animation state updates each frame.