fix: Codex review round 4 — panic recovery, subtree preservation

- PerformAsync: defer/recover wraps task execution, broadcasts error on panic
- Command: preserve existing subtree when overwriting placeholder parent

Remaining known architectural:
- fs.go TOCTOU (needs openat/fd-based ops)
- Global lockMap (needs per-Core registry)
- ServiceShutdown goroutine on timeout (inherent to wg.Wait)

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Snider 2026-03-20 17:52:48 +00:00
parent ee9e715243
commit 61b034335a
2 changed files with 20 additions and 5 deletions

View file

@ -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

View file

@ -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 := "<nil>"
if taskType != nil {
typeName = taskType.String()
}
err = E("core.PerformAsync", Join(" ", "no handler found for task type", typeName), nil)
typeName := "<nil>"
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})