From 61b034335a7b5aab613905d47661440bb747a418 Mon Sep 17 00:00:00 2001 From: Snider Date: Fri, 20 Mar 2026 17:52:48 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20Codex=20review=20round=204=20=E2=80=94?= =?UTF-8?q?=20panic=20recovery,=20subtree=20preservation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- pkg/core/command.go | 9 +++++++++ pkg/core/task.go | 16 +++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) 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})