fix(agent): don't auto-complete sessions with pending approvals
Some checks failed
ci / build-test (push) Has been cancelled
ci / docker-build (push) Has been cancelled
Desktop App / Build Linux (amd64) (push) Has been cancelled
Desktop App / Attach to Release (push) Has been cancelled

The auto-complete fired when the agent hit the P5 approval gate — it
queued a config_mutation run for approval, the P5 gate blocked further
runs, the turn ended, and auto-complete closed the session as 'partial'.
The operator's approval would then land on a dead task.

Fix: hasPendingApprovals check — if the session has any executions in
pending_approval state, skip auto-complete. The session stays in
'executing' until the operator approves (or denies).

VERSION 0.7.5 → 0.7.6
This commit is contained in:
2026-07-16 00:17:50 +02:00
parent df24cae507
commit 876f181068
3 changed files with 27 additions and 1 deletions

View File

@@ -1 +1 @@
0.7.5
0.7.6

View File

@@ -931,6 +931,23 @@ func (s *store) allPlanStepsTerminal(ctx context.Context, sessionID string) bool
return total > 0 && total == terminal
}
// hasPendingApprovals reports whether this session has any executions in
// pending_approval state. Used by autoCompleteIfPlanDone to avoid closing a
// session that's blocked waiting for operator approval — the agent hit the
// P5 gate and can't continue until the operator responds.
func (s *store) hasPendingApprovals(ctx context.Context, sessionID string) bool {
if s == nil || sessionID == "" || sessionID == "ephemeral" {
return false
}
var count int
s.pool.QueryRow(ctx, `
SELECT COUNT(*) FROM nomos_plan_executions pe
JOIN executions ex ON ex.entity_id = pe.execution_id
WHERE pe.session_id = $1 AND ex.status = 'pending_approval'`,
sessionID).Scan(&count)
return count > 0
}
// planStep is a persisted plan step, as returned to the frontend for hydration
// (the panel otherwise only sees steps live via plan.proposed/plan.step.*).
type planStep struct {

View File

@@ -376,6 +376,15 @@ func (a *agent) autoCompleteIfPlanDone(ctx context.Context, sessionID, responseT
if err != nil || sess.Status != "executing" {
return
}
// Don't auto-complete if there are pending approvals — the agent is
// blocked waiting for the operator, not done. Auto-completing here
// would close the session and the operator's approval would land on a
// dead task. Confirmed in eval: agent hits P5 approval gate, turn
// ends, auto-complete fires incorrectly because the approval-queue
// `run` responses were logged as success=true in agent_activity.
if a.store.hasPendingApprovals(ctx, sessionID) {
return
}
discovery := a.store.hadDiscovery(ctx, sessionID)
writeback := a.store.hadEntityWriteback(ctx, sessionID)
// (a) all plan steps terminal, OR (b) agent did discovery (ran `run`).