I simplified your examples to just one element. I set up a script called base_class.gd with the following:
extends Node2D
class_name Plant
@onready var timer: Timer = $Timer
func _ready() -> void:
print("Timer: ", timer)
Then I created a new scene and added a single child Timer node to the scene. I then attached a script I called grape.gd with this code:
extends Plant
class_name Grape
func _ready() -> void:
super()
print("CHILD CLASS GRAPE READY")
The output when I played the grape scene is the following:
Timer: Timer:<Timer#29041362199>
CHILD CLASS GRAPE READY
So it appears to work fine.
The only thing I can suggest is to check your node names and paths are correct.
If you follow what I did above with a completely new scene, does it not work as mine did? If not, I honestly do not know what the problem is, sorry.
In the game I am making at the moment my ready functions simply connect to my signal manager. The root node, my “Game” node, in it’s ready function calls an initialise() function. In that function I initialise all my children, passing shared node refrences like Player, Game etc, calling the player settings singletons to set up the player and levels if a game has been loaded etc. I did this as I was running into complications of when ready() was called as well, but by moving it into initialisation functions called when the root was ready, I averted all those problems. Perhaps something like that would help.
I must admit that when I use a base class I tend to not use the _ready() function, as it can be somewhat confusing about when it runs. Instead I again use initialisation functions to do set-up requests.
Anyway, I hope you get your problem solved. Perhaps stepping through your code with the debugger one line at a time will help you see what the program is doing and when.