From 7dde6b7769027416770ecd713b5ddd9604fbbc02 Mon Sep 17 00:00:00 2001 From: Virgil Date: Wed, 1 Apr 2026 16:14:16 +0000 Subject: [PATCH] feat(agentic): make plan delete a hard delete Co-Authored-By: Virgil --- pkg/agentic/commands_test.go | 9 +++++---- pkg/agentic/plan.go | 2 +- pkg/agentic/plan_compat.go | 22 ++++++++++++++++++++++ pkg/agentic/plan_crud_test.go | 9 ++++----- pkg/agentic/prep.go | 2 +- 5 files changed, 33 insertions(+), 11 deletions(-) diff --git a/pkg/agentic/commands_test.go b/pkg/agentic/commands_test.go index 29daa63..3f4d5d7 100644 --- a/pkg/agentic/commands_test.go +++ b/pkg/agentic/commands_test.go @@ -1094,10 +1094,11 @@ func TestCommands_CmdPlanDelete_Good(t *testing.T) { assert.Contains(t, output, "deleted:") - plan, err := readPlan(PlansRoot(), created.ID) - require.NoError(t, err) - assert.Equal(t, "archived", plan.Status) - assert.False(t, plan.ArchivedAt.IsZero()) + assert.False(t, fs.Exists(created.Path)) + + _, err = readPlan(PlansRoot(), created.ID) + require.Error(t, err) + assert.Contains(t, err.Error(), "not found") } func TestCommands_CmdExtract_Good(t *testing.T) { diff --git a/pkg/agentic/plan.go b/pkg/agentic/plan.go index 79ce743..d0e9b10 100644 --- a/pkg/agentic/plan.go +++ b/pkg/agentic/plan.go @@ -454,7 +454,7 @@ func (s *PrepSubsystem) planUpdate(_ context.Context, _ *mcp.CallToolRequest, in } func (s *PrepSubsystem) planDelete(_ context.Context, _ *mcp.CallToolRequest, input PlanDeleteInput) (*mcp.CallToolResult, PlanDeleteOutput, error) { - plan, err := archivePlanResult(input, "id is required", "planDelete") + plan, err := deletePlanResult(input, "id is required", "planDelete") if err != nil { return nil, PlanDeleteOutput{}, err } diff --git a/pkg/agentic/plan_compat.go b/pkg/agentic/plan_compat.go index bd77457..200f0bc 100644 --- a/pkg/agentic/plan_compat.go +++ b/pkg/agentic/plan_compat.go @@ -364,3 +364,25 @@ func archivePlanResult(input PlanDeleteInput, missingMessage, op string) (*Plan, return plan, nil } + +func deletePlanResult(input PlanDeleteInput, missingMessage, op string) (*Plan, error) { + ref := planReference(input.ID, input.Slug) + if ref == "" { + return nil, core.E(op, missingMessage, nil) + } + + plan, err := readPlan(PlansRoot(), ref) + if err != nil { + return nil, err + } + + if result := fs.Delete(planPath(PlansRoot(), plan.ID)); !result.OK { + deleteErr, _ := result.Value.(error) + if deleteErr == nil { + deleteErr = core.E(op, "failed to delete plan", nil) + } + return nil, core.E(op, "failed to delete plan", deleteErr) + } + + return plan, nil +} diff --git a/pkg/agentic/plan_crud_test.go b/pkg/agentic/plan_crud_test.go index 695aaef..bcba125 100644 --- a/pkg/agentic/plan_crud_test.go +++ b/pkg/agentic/plan_crud_test.go @@ -268,12 +268,11 @@ func TestPlan_PlanDelete_Good(t *testing.T) { assert.True(t, delOut.Success) assert.Equal(t, createOut.ID, delOut.Deleted) - assert.True(t, fs.Exists(createOut.Path)) + assert.False(t, fs.Exists(createOut.Path)) - plan, readErr := readPlan(PlansRoot(), createOut.ID) - require.NoError(t, readErr) - assert.Equal(t, "archived", plan.Status) - assert.False(t, plan.ArchivedAt.IsZero()) + _, readErr := readPlan(PlansRoot(), createOut.ID) + require.Error(t, readErr) + assert.Contains(t, readErr.Error(), "not found") } func TestPlan_PlanDelete_Bad_MissingID(t *testing.T) { diff --git a/pkg/agentic/prep.go b/pkg/agentic/prep.go index 1cc3678..0cfda95 100644 --- a/pkg/agentic/prep.go +++ b/pkg/agentic/prep.go @@ -187,7 +187,7 @@ func (s *PrepSubsystem) OnStartup(ctx context.Context) core.Result { c.Action("plan.from.issue", s.handlePlanFromIssue).Description = "Create a plan from a tracked issue" c.Action("plan.check", s.handlePlanCheck).Description = "Check whether a plan or phase is complete" c.Action("plan.archive", s.handlePlanArchive).Description = "Archive an implementation plan by slug" - c.Action("plan.delete", s.handlePlanDelete).Description = "Archive an implementation plan by ID" + c.Action("plan.delete", s.handlePlanDelete).Description = "Delete an implementation plan by ID" c.Action("plan.list", s.handlePlanList).Description = "List implementation plans with optional filters" c.Action("phase.get", s.handlePhaseGet).Description = "Read a plan phase by slug and order" c.Action("phase.update_status", s.handlePhaseUpdateStatus).Description = "Update plan phase status by slug and order"