diff --git a/pkg/core/command.go b/pkg/core/command.go index da5598f..1267cb3 100644 --- a/pkg/core/command.go +++ b/pkg/core/command.go @@ -155,6 +155,15 @@ func (c *Core) Command(path string, command ...Command) Result { cmd.commands = make(map[string]*Command) } + // Preserve existing subtree when overwriting a placeholder parent + if existing, exists := c.commands.commands[path]; exists { + for k, v := range existing.commands { + if _, has := cmd.commands[k]; !has { + cmd.commands[k] = v + } + } + } + c.commands.commands[path] = cmd // Build parent chain — "deploy/to/homelab" creates "deploy" and "deploy/to" if missing diff --git a/pkg/core/task.go b/pkg/core/task.go index 553e7ae..386bfcd 100644 --- a/pkg/core/task.go +++ b/pkg/core/task.go @@ -29,6 +29,12 @@ func (c *Core) PerformAsync(t Task) Result { } c.ACTION(ActionTaskStarted{TaskIdentifier: taskID, Task: t}) c.wg.Go(func() { + defer func() { + if rec := recover(); rec != nil { + err := E("core.PerformAsync", Sprint("panic: ", rec), nil) + c.ACTION(ActionTaskCompleted{TaskIdentifier: taskID, Task: t, Result: nil, Error: err}) + } + }() r := c.PERFORM(t) var err error if !r.OK { @@ -36,11 +42,11 @@ func (c *Core) PerformAsync(t Task) Result { err = e } else { taskType := reflect.TypeOf(t) - typeName := "" - if taskType != nil { - typeName = taskType.String() - } - err = E("core.PerformAsync", Join(" ", "no handler found for task type", typeName), nil) + typeName := "" + if taskType != nil { + typeName = taskType.String() + } + err = E("core.PerformAsync", Join(" ", "no handler found for task type", typeName), nil) } } c.ACTION(ActionTaskCompleted{TaskIdentifier: taskID, Task: t, Result: r.Value, Error: err})