feat(agentic): hard-delete plans on plan delete

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-01 16:32:03 +00:00
parent afb6b0f881
commit afc7b063ee
4 changed files with 43 additions and 15 deletions

View file

@ -303,7 +303,7 @@ func (s *PrepSubsystem) cmdPlanDelete(options core.Options) core.Result {
return core.Result{Value: err, OK: false}
}
core.Print(nil, "archived: %s", output.Deleted)
core.Print(nil, "deleted: %s", output.Deleted)
return core.Result{Value: output, OK: true}
}

View file

@ -1093,15 +1093,12 @@ func TestCommands_CmdPlanDelete_Good(t *testing.T) {
assert.True(t, r.OK)
})
assert.Contains(t, output, "archived:")
assert.Contains(t, output, "deleted:")
assert.True(t, fs.Exists(created.Path))
assert.False(t, fs.Exists(created.Path))
plan, err := readPlan(PlansRoot(), created.ID)
require.NoError(t, err)
assert.Equal(t, "archived", plan.Status)
assert.False(t, plan.ArchivedAt.IsZero())
assert.Contains(t, plan.Notes, "RFC contract says soft delete")
_, readErr := readPlan(PlansRoot(), created.ID)
require.Error(t, readErr)
}
func TestCommands_CmdExtract_Good(t *testing.T) {

View file

@ -366,5 +366,31 @@ func archivePlanResult(input PlanDeleteInput, missingMessage, op string) (*Plan,
}
func deletePlanResult(input PlanDeleteInput, missingMessage, op string) (*Plan, error) {
return archivePlanResult(input, missingMessage, op)
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
}
planPath := planPath(PlansRoot(), plan.ID)
if deleteResult := fs.Delete(planPath); !deleteResult.OK {
deleteErr, _ := deleteResult.Value.(error)
return nil, core.E(op, "failed to delete plan", deleteErr)
}
if plan.Slug != "" {
stateFile := statePath(plan.Slug)
if fs.Exists(stateFile) {
if deleteResult := fs.Delete(stateFile); !deleteResult.OK {
deleteErr, _ := deleteResult.Value.(error)
return nil, core.E(op, "failed to delete plan state", deleteErr)
}
}
}
return plan, nil
}

View file

@ -263,6 +263,13 @@ func TestPlan_PlanDelete_Good(t *testing.T) {
Title: "Delete Me", Objective: "Will be deleted",
})
planBeforeDelete, err := readPlan(PlansRoot(), createOut.ID)
require.NoError(t, err)
require.NoError(t, writePlanStates(planBeforeDelete.Slug, []WorkspaceState{{
Key: "pattern",
Value: "observer",
}}))
_, delOut, err := s.planDelete(context.Background(), nil, PlanDeleteInput{
ID: createOut.ID,
Reason: "No longer needed",
@ -271,13 +278,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))
assert.False(t, fs.Exists(statePath(planBeforeDelete.Slug)))
archivedPlan, readErr := readPlan(PlansRoot(), createOut.ID)
require.NoError(t, readErr)
assert.Equal(t, "archived", archivedPlan.Status)
assert.False(t, archivedPlan.ArchivedAt.IsZero())
assert.Contains(t, archivedPlan.Notes, "No longer needed")
_, readErr := readPlan(PlansRoot(), createOut.ID)
require.Error(t, readErr)
}
func TestPlan_PlanDelete_Bad_MissingID(t *testing.T) {