@cybereality Yeah, it behaves precisely as I'd expect it to.
Code.ready() calls succeeding sibling's method Scene.change_color(). This method then tries to use its class property that hasn't been initialized yet. It isn't initialized because initialization happens in Scene.ready() which is yet to be called after Code._ready() finishes.
If you reverse the order of Code and Scene nodes - it'll work just fine, because now Scene.ready() did initialize the property before Code.ready() calls a method that uses it.
Calling Scene.change_color() from the parent's ready() will, of course, work, regardless of sibling order because both sibling's ready() will already finish their jobs at that point.
...
I think the main point of confusion for many people is that onready
thingy. When property is declared onready it's the same as adding the assignment expression at the start of _ready() callback:
onready var foo = "godot"
is same as:
var foo
func _ready():
foo = "godot"
In other words, doing: var foo = "godot"
will initialize the class property very early in the scene setup process, immediately upon node object creation. It happens before the nodes is even added to the tree. Referring to other nodes here is guaranteed to fail.
Conversely, onready var foo = "godot"
initializes at the very end of the setup process - right before _ready() is called. Referring to other nodes here will always be successful.