goldnead/statamic-activity
Goldnead/Statamic Activity adds an activity log to Statamic, letting you record and review user actions and content changes across your site. A lightweight way to track what happens in the control panel and keep an audit trail of edits, publishes, and other events.
composer update — v1.6.0 ships no migration, so there is nothing to publish or run. But this release changes runtime behaviour on upgrade, so the update is not "done" until you have checked retention.config/activity.php, specifically retention.days. On an installation that has never published the config, this value now defaults to 365 instead of "keep everything".php artisan activity:prune --dry-run
config/activity.php. Expect support questions about the missing field.'days' => null), or "prune at N days" (ACTIVITY_RETENTION_DAYS=N / 'days' => N) — then verify with a dry run before the scheduler fires.// config/activity.php (published)
'retention' => [
'days' => env('ACTIVITY_RETENTION_DAYS', 365), // null = keep forever
// 'anonymize_after_days' still exists and is still per brand — see Gotchas
],
Pin the policy in config, not in env alone. The env var is convenient per environment, but the default belongs in the published config file so the behaviour is visible in code review. env('ACTIVITY_RETENTION_DAYS', 365) in the repo, ACTIVITY_RETENTION_DAYS= per environment.
Fresh installs. If you want the historical "never delete" behaviour, set 'days' => null in your published config before the scheduler first runs activity:prune. Doing it after the first run means the data is already gone.
Upgrading an install with a previously published config. Your file still reads env('ACTIVITY_RETENTION_DAYS') with no default, so it keeps everything — you are not affected by the new 365-day default, but you are also silently out of step with the documented default. Either edit the line to add the default, or re-publish and diff. Pick one deliberately and note it in the upgrade runbook.
Installation-wide, not per brand. Retention is one number for the whole store. activity:prune already deleted across every brand using a single value; the old per-brand field on the Settings page was never read. If you had built internal docs, dashboards, or support macros around "brand X retains 30 days, brand Y retains 400", those are now wrong — collapse them to one global value.
Ad-hoc pruning. Use --days=N on the command for one-off reductions (e.g. trimming a noisy import) rather than temporarily changing config and forgetting to revert it. Any window below 1 is now refused, so there is no accidental --days=0 wipe.
Scheduling and alerting. Schedule the command and make failures visible. Because an invalid window now makes the command exit with an error and delete nothing, a bad env var (empty string, 0, "365 ", a typo) turns pruning quietly into "never runs". Wire the exit code into your monitoring rather than assuming the cron line is enough.
// routes/console.php or the scheduler
$schedule->command('activity:prune')
->daily()
->onFailure(fn () => /* notify: retention window invalid or prune failed */);
Deployment order. Set ACTIVITY_RETENTION_DAYS (or the config default) before the new code goes live, so the first scheduled run under v1.6.0 uses the window you chose rather than the package default.
Treat the config key as the extension point. If you need custom policies (per-tenant retention, legal holds), implement them in your own command or job that reads the same retention.days value — do not reintroduce per-brand storage and expect the package to honour it.
activity:prune after updating will delete everything older than 365 days on installs with no config file and no env var. There is no migration, no notice, no log line you will see unless you look. Run --dry-run first, every time, on every environment.env() and no default" keeps data forever; "no config / re-published config" prunes at 365. Two sites on the same package version will behave differently. When someone reports "we lost history" or "pruning never runs", check whether config/activity.php exists and what the days line says before touching anything else.0 and negatives are refused, not clamped. The global window, --days, and any per-event-type window below 1 or non-numeric cause the command to error out and delete nothing. That is the right safety behaviour, but it means a dynamically computed window (e.g. --days=${RETENTION}) can halt pruning entirely instead of pruning conservatively. Validate the value in your deploy pipeline.ACTIVITY_RETENTION_DAYS= in a .env is not null and not 365 — it is an empty string that must not be treated as a valid window. Prefer leaving the variable out of .env entirely when you want the default.'days' => null means "keep forever", but only if it survives. Casting it through (int) anywhere in your own code turns it into 0, which the command now refuses. Keep it null end-to-end.retention.anonymize_after_days is still stored per brand, while activity:anonymize runs across the whole store. Do not present the anonymise setting to users as per-brand scoping — it is not. Track this as a known gap rather than building on it.brand_settings rows are harmless but real. Values previously stored for retention.days per brand are no longer read. There is no migration; delete them by hand if you want a clean table, but do not write code that depends on their absence.config/activity.php exist and is it cached (php artisan config:clear)? (2) is retention.days present, and is it null? (3) does ACTIVITY_RETENTION_DAYS resolve to a number ≥ 1? (4) does activity:prune --dry-run exit non-zero? (5) is the scheduled command actually registered and running (check schedule:list)? (6) are per-event-type windows overriding the global one?php artisan config:cache from before the upgrade will keep serving the old value.config/activity.php. Clear caches with php artisan optimize:clear if the old field still renders.null case and the < 1 rejection case explicitly — those are the two behaviours most likely to regress.How can I help you explore Laravel packages today?