php-agentic/Console/Commands/PlanRetentionCommand.php
Clotho cc1c4c1adc
Some checks failed
CI / PHP 8.3 (pull_request) Failing after 21s
CI / PHP 8.4 (pull_request) Failing after 19s
feat: add plan archival with retention policy (#34)
- Add `agentic.plan_retention_days` config (default 90 days via AGENTIC_PLAN_RETENTION_DAYS env)
- Add SoftDeletes and `archived_at` timestamp to AgentPlan model
- Add migration for `deleted_at` and `archived_at` columns on agent_plans
- Create `agentic:plan-cleanup` command with --dry-run and --days options
- Schedule retention cleanup to run daily via service provider
- Register PlanRetentionCommand in ConsoleBooting handler
- Add PlanRetentionTest feature test suite covering all retention scenarios
- Fix archive() to store archived_at as dedicated column (not metadata string)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 15:11:55 +00:00

61 lines
1.7 KiB
PHP

<?php
declare(strict_types=1);
namespace Core\Mod\Agentic\Console\Commands;
use Core\Mod\Agentic\Models\AgentPlan;
use Illuminate\Console\Command;
class PlanRetentionCommand extends Command
{
protected $signature = 'agentic:plan-cleanup
{--dry-run : Preview deletions without making changes}
{--days= : Override retention period (overrides agentic.plan_retention_days config)}';
protected $description = 'Permanently delete archived plans past the retention period';
public function handle(): int
{
$days = (int) ($this->option('days') ?? config('agentic.plan_retention_days', 90));
if ($days <= 0) {
$this->info('Retention cleanup is disabled (plan_retention_days is 0).');
return self::SUCCESS;
}
$cutoff = now()->subDays($days);
$query = AgentPlan::where('status', AgentPlan::STATUS_ARCHIVED)
->whereNotNull('archived_at')
->where('archived_at', '<', $cutoff);
$count = $query->count();
if ($count === 0) {
$this->info('No archived plans found past the retention period.');
return self::SUCCESS;
}
if ($this->option('dry-run')) {
$this->info("DRY RUN: {$count} archived plan(s) would be permanently deleted (archived before {$cutoff->toDateString()}).");
return self::SUCCESS;
}
$deleted = 0;
$query->chunkById(100, function ($plans) use (&$deleted): void {
foreach ($plans as $plan) {
$plan->forceDelete();
$deleted++;
}
});
$this->info("Permanently deleted {$deleted} archived plan(s) archived before {$cutoff->toDateString()}.");
return self::SUCCESS;
}
}