feat(agentic): schedule plan retention cleanup
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
eab30f578e
commit
88f698a608
3 changed files with 66 additions and 0 deletions
|
|
@ -3,6 +3,7 @@
|
|||
package agentic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
|
|
@ -10,6 +11,7 @@ import (
|
|||
)
|
||||
|
||||
const planRetentionDefaultDays = 90
|
||||
const planRetentionScheduleInterval = 24 * time.Hour
|
||||
|
||||
type PlanCleanupOutput struct {
|
||||
Success bool `json:"success"`
|
||||
|
|
@ -55,6 +57,26 @@ func (s *PrepSubsystem) cmdPlanCleanup(options core.Options) core.Result {
|
|||
return core.Result{Value: output, OK: true}
|
||||
}
|
||||
|
||||
// ctx, cancel := context.WithCancel(context.Background())
|
||||
// go s.runPlanCleanupLoop(ctx, time.Minute)
|
||||
func (s *PrepSubsystem) runPlanCleanupLoop(ctx context.Context, interval time.Duration) {
|
||||
if ctx == nil || interval <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
s.planCleanup(core.NewOptions())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PrepSubsystem) planCleanup(options core.Options) core.Result {
|
||||
days := planRetentionDays(options)
|
||||
if days <= 0 {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
package agentic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
|
@ -141,3 +142,43 @@ func TestPlanRetention_PlanArchivedAt_Good_FallsBackToFileModifiedTime(t *testin
|
|||
_, ok := stat.Value.(interface{ ModTime() time.Time })
|
||||
assert.True(t, ok)
|
||||
}
|
||||
|
||||
func TestPlanRetention_RunPlanCleanupLoop_Good_DeletesExpiredPlans(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
t.Setenv("CORE_WORKSPACE", dir)
|
||||
|
||||
s := newTestPrep(t)
|
||||
|
||||
plan := &Plan{
|
||||
ID: "scheduled-plan-abc123",
|
||||
Title: "Scheduled Plan",
|
||||
Status: "archived",
|
||||
Objective: "Remove me on the next retention pass",
|
||||
ArchivedAt: time.Now().AddDate(0, 0, -100),
|
||||
}
|
||||
|
||||
_, err := writePlan(PlansRoot(), plan)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
done := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
s.runPlanCleanupLoop(ctx, time.Millisecond)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
require.Eventually(t, func() bool {
|
||||
return !fs.Exists(core.JoinPath(PlansRoot(), "scheduled-plan-abc123.json"))
|
||||
}, time.Second, 5*time.Millisecond)
|
||||
|
||||
cancel()
|
||||
require.Eventually(t, func() bool {
|
||||
select {
|
||||
case <-done:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}, time.Second, 5*time.Millisecond)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -257,6 +257,9 @@ func (s *PrepSubsystem) OnStartup(ctx context.Context) core.Result {
|
|||
c.Action("agentic.complete", s.handleComplete).Description = "Run completion pipeline (QA → PR → Verify → Ingest → Poke) in background"
|
||||
|
||||
s.hydrateWorkspaces()
|
||||
if planRetentionDays(core.NewOptions()) > 0 {
|
||||
go s.runPlanCleanupLoop(ctx, planRetentionScheduleInterval)
|
||||
}
|
||||
|
||||
c.RegisterQuery(s.handleWorkspaceQuery)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue