fix: assent-window auto-approve goroutine used the request-scoped context
Verified live testing the new atomic pct_create: an assent-window auto-approved pct_create appeared to "run" (logged "auto-approved... running now") but the execution stayed stuck at 'approved' forever. Root cause: `go executeApprovedViaAPI(ctx, ...)` passed the MCP tool-call's own context — which is cancelled the instant the triggering /chat request's HTTP response completes, i.e. on every normal turn. The spawned goroutine's POST to the approval-decision endpoint died with "context canceled" before it could even start the real work, and nothing surfaced this to the operator or the agent — the execution just sat at 'approved' with no error, indistinguishable from "still running." This is exactly the context-lifetime bug class httpapi's own approval goroutine (executeApprovedAction) already avoided by using context.Background() — it had just been missed in these two call sites (apt_upgrade and pct_create auto-approve). Fixed both to use context.Background(), matching the correct pattern already in place elsewhere. Audited for other goroutines spawned with a request-scoped ctx — none found; the sshExec internal goroutines are synchronous/waited-on via select and correctly scoped to the call. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -404,7 +404,19 @@ func newServer(pool *db.Pool, agentID uuid.UUID) *mcp.Server {
|
|||||||
pool.Exec(ctx, `UPDATE executions SET status='pending_approval', risk_class='config_mutation' WHERE entity_id=$1`, id)
|
pool.Exec(ctx, `UPDATE executions SET status='pending_approval', risk_class='config_mutation' WHERE entity_id=$1`, id)
|
||||||
createApproval(ctx, pool, id, targetID, "apt_upgrade", params, "config_mutation")
|
createApproval(ctx, pool, id, targetID, "apt_upgrade", params, "config_mutation")
|
||||||
if approved := autoApprove(ctx, pool, id); approved {
|
if approved := autoApprove(ctx, pool, id); approved {
|
||||||
go executeApprovedViaAPI(ctx, id, targetSlug, "apt_upgrade:"+params)
|
// context.Background(), NOT ctx: ctx is scoped to this
|
||||||
|
// MCP tool call, which ends (and cancels) as soon as the
|
||||||
|
// chat turn's HTTP response completes — normal, expected,
|
||||||
|
// happens on every turn. A goroutine meant to outlive the
|
||||||
|
// request must not inherit its context, or the async work
|
||||||
|
// dies silently the instant the turn ends. Found live:
|
||||||
|
// every assent-window auto-approved pct_create failed
|
||||||
|
// with "context canceled" the moment the triggering
|
||||||
|
// /chat request finished — exactly the same context-
|
||||||
|
// lifetime class of bug, in the one place it had been
|
||||||
|
// missed (httpapi's own approval goroutine already used
|
||||||
|
// context.Background() correctly).
|
||||||
|
go executeApprovedViaAPI(context.Background(), id, targetSlug, "apt_upgrade:"+params)
|
||||||
slog.Info("mcp: apt_upgrade auto-approved via assent window", "execution_id", id)
|
slog.Info("mcp: apt_upgrade auto-approved via assent window", "execution_id", id)
|
||||||
return textResult(fmt.Sprintf("apt_upgrade on %s auto-approved via assent window — execution %s running.", targetSlug, id)), nil
|
return textResult(fmt.Sprintf("apt_upgrade on %s auto-approved via assent window — execution %s running.", targetSlug, id)), nil
|
||||||
}
|
}
|
||||||
@@ -421,7 +433,7 @@ func newServer(pool *db.Pool, agentID uuid.UUID) *mcp.Server {
|
|||||||
pool.Exec(ctx, `UPDATE executions SET status='pending_approval', risk_class='config_mutation' WHERE entity_id=$1`, id)
|
pool.Exec(ctx, `UPDATE executions SET status='pending_approval', risk_class='config_mutation' WHERE entity_id=$1`, id)
|
||||||
createApproval(ctx, pool, id, targetID, "pct_create", params, "config_mutation")
|
createApproval(ctx, pool, id, targetID, "pct_create", params, "config_mutation")
|
||||||
if approved := autoApprove(ctx, pool, id); approved {
|
if approved := autoApprove(ctx, pool, id); approved {
|
||||||
go executeApprovedViaAPI(ctx, id, targetSlug, "pct_create:"+params)
|
go executeApprovedViaAPI(context.Background(), id, targetSlug, "pct_create:"+params) // see context.Background() comment above (apt_upgrade case) — same bug, same fix
|
||||||
slog.Info("mcp: pct_create auto-approved via assent window", "execution_id", id)
|
slog.Info("mcp: pct_create auto-approved via assent window", "execution_id", id)
|
||||||
return textResult(fmt.Sprintf("pct_create on %s auto-approved via assent window — execution %s running. The LXC is being provisioned now.", targetSlug, id)), nil
|
return textResult(fmt.Sprintf("pct_create on %s auto-approved via assent window — execution %s running. The LXC is being provisioned now.", targetSlug, id)), nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user