diff --git a/cmd/nomos/tasks.go b/cmd/nomos/tasks.go index 6823df0..eca6e69 100644 --- a/cmd/nomos/tasks.go +++ b/cmd/nomos/tasks.go @@ -185,7 +185,7 @@ func (a *agent) handleTaskTool(ctx context.Context, sessionID, name string, args // what the SOUL.md "approve the plan, not each step" model actually // describes. set_goal records the goal + flips status to executing // and nothing more. - return "Goal set: " + goal + ". NEXT: pre-plan with read-only tools (search_knowledge, get_entity, list_lxcs, get_relations), then propose_plan (mandatory — even read-only tasks need a one-step plan; the run handler refuses without one). Do not call run before propose_plan.", true + return "Goal set: " + goal + ". NEXT: pre-plan with read-only tools (search_knowledge, get_entity, list_lxcs, get_relations), then propose_plan (mandatory — even read-only tasks need a one-step plan; the run handler refuses without one). After propose_plan: if all steps are read-only, execute immediately (no approval needed). If any step is config_mutation/destructive, stop and wait for operator approval.", true case "propose_plan": raw, _ := args["steps"].([]any) @@ -249,7 +249,7 @@ func (a *agent) handleTaskTool(ctx context.Context, sessionID, name string, args // the agent forgot), so the old advisory nudge is replaced by the // structural gate: D.1 refuses complete_task without the actual // update_entity_attributes/create_relationship calls. - result := fmt.Sprintf("Plan set (%d steps)%s. STOP. Wait for operator approval — do not call run yet. Approval vocabulary: \"approved\", \"yes\", \"go\", \"proceed\", \"continue\", \"ok\", \"go ahead\". On approval, advance with update_plan_step + run. Do not call propose_plan again.", len(persisted), appendedNote) + result := fmt.Sprintf("Plan set (%d steps)%s. If all steps are read-only, execute now — call update_plan_step(running) + run for each step, no approval needed. If any step is config_mutation/destructive, STOP and wait for operator approval (\"approved\", \"yes\", \"go\", \"proceed\", \"continue\", \"ok\", \"go ahead\"). Do not call propose_plan again.", len(persisted), appendedNote) return result, true case "update_plan_step": diff --git a/nomos/SOUL.md b/nomos/SOUL.md index 6eca0ee..bf71365 100644 --- a/nomos/SOUL.md +++ b/nomos/SOUL.md @@ -30,16 +30,22 @@ Call ONCE with EVERY step end-to-end. The LAST step MUST be: Include target slugs on each step so the panel links them. If you omit the writeback step, one is auto-appended. -### 4. GET APPROVAL — stop and wait -After proposing the plan, END YOUR TURN. Do not call `run`. Do not execute. -Wait for the operator to approve. Approval vocabulary: "approved", "yes", -"go", "proceed", "continue", "ok", "go ahead". The plan window then -auto-approves all subsequent config_mutation commands. +### 4. GET APPROVAL — only if the plan has config_mutation/destructive steps +After proposing the plan, check the step risk classes: +- **All read-only plan?** No approval needed. Go straight to step 5 and + execute — read-only `run` commands auto-run immediately once a plan + exists. Do NOT stop and wait. +- **Any config_mutation or destructive step?** END YOUR TURN. Do not call + `run`. Wait for the operator to approve. Approval vocabulary: "approved", + "yes", "go", "proceed", "continue", "ok", "go ahead". The assent window + then auto-approves subsequent config_mutation commands. -### 5. EXECUTE — `run` calls auto-run under the plan window -Once approved, advance each step with `update_plan_step` (running → done) + -`run`. Do NOT call `propose_plan` again — it is refused once a step has -started. Config_mutation commands auto-execute without per-action approval. +### 5. EXECUTE — `run` calls +Advance each step with `update_plan_step` (running → done) + `run`. Do NOT +call `propose_plan` again — it is refused once a step has started. +Read-only commands auto-run (no approval). Config_mutation commands +auto-run under the assent window (after approval). Destructive commands +always need explicit typed confirmation. ### 6. WRITE BACK + COMPLETE — `complete_task` Call `update_entity_attributes` for every entity you ran `run` against