Round-2 UX fixes + opt-in trash auto-purge (#74, #75, #76, #77) #78

Merged
Cordy merged 5 commits from fix/ux-round2 into main 2026-07-30 04:05:20 +00:00
Owner

Closes #74
Closes #75
Closes #76
Closes #77

Three dogfood findings plus the feature salvaged from the stale branch.

#74 — "This folder is empty." no longer sticks. The status line is now set from the listing result on every load (say(items.length ? "" : …)), so it clears when you navigate into a folder that has files.

#75.trash hidden from the file browser. It appeared because the storage layer only hides it from non-admins and admins are unscoped, so Personal is the raw backend root. Storage behavior is unchanged (operators still need raw access for recovery and empty-trash); the browser simply filters the entry, since Deleted files is its own view.

#76 — real folder timestamps. S3 has no directories, so synthesised entries carried a zero time and rendered as 0001-01-01. New childModTimes folds each object's LastModified up into its immediate parent in one bounded extra listing per directory (capped at 5000 keys, not one request per subdirectory). Because the .cairn-dir marker participates, a freshly created empty folder reports its creation time and a folder with content reports its newest content — both readings of Nikola's question. The UI now renders any genuinely unknown timestamp as "—" rather than year 1.

#77 — opt-in trash auto-purge, per Nikola's spec: admin-group configurable, explicit opt-in, with a chosen retention time before final deletion. Settings gains trashAutoPurge + trashAutoPurgeAfter (30/90/180 days or 1 year in the Settings panel), read per sweep so changes apply without a restart. Hourly sweeper with three guard rails, because this is the only code path that destroys data without a human asking: the longer of policy retention and the operator's age always wins, legal holds veto absolutely, and every purge emits an auto-purge audit event naming the original path. Off by default = today's behavior exactly.

Tests: sweeper opt-in, expiry selection, retention override, hold veto, audit hook. gofmt/vet/build/test + node --check green.

Closes #74 Closes #75 Closes #76 Closes #77 Three dogfood findings plus the feature salvaged from the stale branch. **#74 — "This folder is empty." no longer sticks.** The status line is now set from the listing result on every load (`say(items.length ? "" : …)`), so it clears when you navigate into a folder that has files. **#75 — `.trash` hidden from the file browser.** It appeared because the storage layer only hides it from non-admins and admins are unscoped, so Personal is the raw backend root. Storage behavior is unchanged (operators still need raw access for recovery and empty-trash); the browser simply filters the entry, since Deleted files is its own view. **#76 — real folder timestamps.** S3 has no directories, so synthesised entries carried a zero time and rendered as `0001-01-01`. New `childModTimes` folds each object's `LastModified` up into its immediate parent in one bounded extra listing per directory (capped at 5000 keys, not one request per subdirectory). Because the `.cairn-dir` marker participates, a freshly created empty folder reports its creation time and a folder with content reports its newest content — both readings of Nikola's question. The UI now renders any genuinely unknown timestamp as "—" rather than year 1. **#77 — opt-in trash auto-purge**, per Nikola's spec: admin-group configurable, explicit opt-in, with a chosen retention time before final deletion. Settings gains `trashAutoPurge` + `trashAutoPurgeAfter` (30/90/180 days or 1 year in the Settings panel), read per sweep so changes apply without a restart. Hourly sweeper with three guard rails, because this is the only code path that destroys data without a human asking: the longer of policy retention and the operator's age always wins, legal holds veto absolutely, and every purge emits an `auto-purge` audit event naming the original path. Off by default = today's behavior exactly. Tests: sweeper opt-in, expiry selection, retention override, hold veto, audit hook. gofmt/vet/build/test + `node --check` green.
Cordy merged commit 35f7e85128 into main 2026-07-30 04:05:20 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Cordy/Cairn#78
No description provided.