artshelf 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +103 -0
- package/LICENSE +21 -0
- package/README.md +282 -0
- package/SPEC.md +675 -0
- package/dist/src/cli.js +1149 -0
- package/dist/src/ledger.js +846 -0
- package/dist/src/registry.js +137 -0
- package/dist/src/time.js +71 -0
- package/dist/src/types.js +1 -0
- package/docs/.nojekyll +1 -0
- package/docs/agent-usage.html +325 -0
- package/docs/agent-usage.md +392 -0
- package/docs/index.html +172 -0
- package/docs/install.html +137 -0
- package/docs/quickstart.html +142 -0
- package/docs/reference.html +170 -0
- package/docs/site.css +639 -0
- package/docs/theme.js +42 -0
- package/package.json +56 -0
- package/skills/artshelf/SKILL.md +373 -0
package/SPEC.md
ADDED
|
@@ -0,0 +1,675 @@
|
|
|
1
|
+
# Artshelf V1 Spec
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
Agents and humans create temporary directories, backups, run artifacts, debug
|
|
6
|
+
outputs, and quarantine folders during work. Those artifacts often have a clear
|
|
7
|
+
reason when created, but that reason is lost later. Cleanup then becomes risky:
|
|
8
|
+
we either keep everything forever or delete based on weak filesystem age.
|
|
9
|
+
|
|
10
|
+
Artshelf makes artifact creation accountable at the moment it happens.
|
|
11
|
+
|
|
12
|
+
## One-Line Product Definition
|
|
13
|
+
|
|
14
|
+
Artshelf is a tiny CLI for putting temporary artifacts, backups, and run outputs
|
|
15
|
+
somewhere accountable, with an expiry tag and a cleanup plan.
|
|
16
|
+
|
|
17
|
+
## Goals
|
|
18
|
+
|
|
19
|
+
- Record why an artifact exists, who created it, and how long it should stay.
|
|
20
|
+
- Make due cleanup visible without guessing from filesystem timestamps.
|
|
21
|
+
- Make cleanup previewable and auditable.
|
|
22
|
+
- Give agents a deterministic tool they can call instead of leaving scratch
|
|
23
|
+
files behind.
|
|
24
|
+
- Stay small enough that agents actually use it.
|
|
25
|
+
|
|
26
|
+
## Non-Goals
|
|
27
|
+
|
|
28
|
+
- Not a full backup system.
|
|
29
|
+
- Not a daemon.
|
|
30
|
+
- Not Kortex.
|
|
31
|
+
- Not a desired-state reconciler.
|
|
32
|
+
- Not a general disk cleaner.
|
|
33
|
+
- Not a content indexer.
|
|
34
|
+
- Not a credential scanner in v1.
|
|
35
|
+
- Not allowed to silently delete files.
|
|
36
|
+
|
|
37
|
+
## V1 CLI
|
|
38
|
+
|
|
39
|
+
### `artshelf put`
|
|
40
|
+
|
|
41
|
+
Records an existing file or directory in the ledger.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
artshelf put <path> --reason "why this exists" --ttl 7d --kind scratch
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Required:
|
|
48
|
+
|
|
49
|
+
- `path`
|
|
50
|
+
- `--reason`
|
|
51
|
+
- one of `--ttl`, `--retain-until`, or `--manual-review`
|
|
52
|
+
|
|
53
|
+
Optional:
|
|
54
|
+
|
|
55
|
+
- `--kind scratch|backup|run-artifact|evidence|cache|quarantine|other`
|
|
56
|
+
- `--cleanup trash|review|delete` (`delete` records intent, but cleanup
|
|
57
|
+
execution refuses it as `cleanup-refused`)
|
|
58
|
+
- `--owner <string>`
|
|
59
|
+
- `--label <label>` repeatable
|
|
60
|
+
- `--ledger <path>`
|
|
61
|
+
- `--registry <path>`
|
|
62
|
+
- `--json`
|
|
63
|
+
|
|
64
|
+
Defaults:
|
|
65
|
+
|
|
66
|
+
- `kind=other`
|
|
67
|
+
- `cleanup=review`
|
|
68
|
+
- `owner=manual`
|
|
69
|
+
|
|
70
|
+
`put` should refuse to record a path that does not exist unless a future flag
|
|
71
|
+
explicitly supports planned artifacts. After appending the record, `put`
|
|
72
|
+
registers the ledger in the ledger registry. Registry registration is
|
|
73
|
+
best-effort: if it fails, the record remains appended and output includes a
|
|
74
|
+
registry warning or `registryError`.
|
|
75
|
+
|
|
76
|
+
### `artshelf ledgers`
|
|
77
|
+
|
|
78
|
+
Lists or registers known Artshelf ledgers.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
artshelf ledgers list
|
|
82
|
+
artshelf ledgers list --plain
|
|
83
|
+
artshelf ledgers add --ledger <path> --name <project> --scope repo
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Rules:
|
|
87
|
+
|
|
88
|
+
- `list` validates each registered ledger by default and reports
|
|
89
|
+
ok/missing/invalid status, entry counts, and warning/error counts so agents can
|
|
90
|
+
detect stale registry entries without a separate validate pass. It reads
|
|
91
|
+
ledgers but never mutates them, and exits non-zero when the registry or any
|
|
92
|
+
registered ledger is broken.
|
|
93
|
+
- `list --plain` is the fast path that lists registered ledgers without reading
|
|
94
|
+
them; it does not validate and exits zero whenever the registry itself is
|
|
95
|
+
readable.
|
|
96
|
+
- `add` requires an existing ledger path.
|
|
97
|
+
- `--name` defaults from the ledger path when omitted.
|
|
98
|
+
- `--scope` is optional; when omitted, Artshelf infers `repo`, `user`, or
|
|
99
|
+
`other` from the ledger path.
|
|
100
|
+
|
|
101
|
+
### `artshelf list`
|
|
102
|
+
|
|
103
|
+
Shows ledger entries in a human-readable format.
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
artshelf list
|
|
107
|
+
artshelf list --json
|
|
108
|
+
artshelf list --status active
|
|
109
|
+
artshelf list --status resolved --json
|
|
110
|
+
artshelf list --all --status active --json
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`--status` filters the audit trail to one record status:
|
|
114
|
+
|
|
115
|
+
- `active`
|
|
116
|
+
- `review-required`
|
|
117
|
+
- `trashed`
|
|
118
|
+
- `cleanup-refused`
|
|
119
|
+
- `resolved`
|
|
120
|
+
|
|
121
|
+
`--all` reads every registered ledger through the registry. All-mode reads
|
|
122
|
+
validate registered ledgers first and report stale or invalid entries before
|
|
123
|
+
returning records.
|
|
124
|
+
|
|
125
|
+
### `artshelf find`
|
|
126
|
+
|
|
127
|
+
Read-only ledger query for integrations that need idempotent artifact
|
|
128
|
+
registration without parsing `list` output.
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
artshelf find --path <path> --json
|
|
132
|
+
artshelf find --path <path> --owner <agent-or-runtime> --label <task-or-run-id> --status active --json
|
|
133
|
+
artshelf find --all --owner <agent-or-runtime> --json
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Accepted selectors:
|
|
137
|
+
|
|
138
|
+
- `--path <path>`: exact artifact path match after path normalization.
|
|
139
|
+
- `--owner <string>`
|
|
140
|
+
- `--label <label>` repeatable; all labels must match.
|
|
141
|
+
- `--status active|review-required|trashed|cleanup-refused|resolved`
|
|
142
|
+
|
|
143
|
+
`find` requires at least one selector. It never creates, resolves, moves, or
|
|
144
|
+
deletes records. `--all` applies the same selector set to every registered
|
|
145
|
+
ledger.
|
|
146
|
+
|
|
147
|
+
### `artshelf get`
|
|
148
|
+
|
|
149
|
+
Read-only lookup of a single ledger record by Artshelf id.
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
artshelf get <id>
|
|
153
|
+
artshelf get <id> --json
|
|
154
|
+
artshelf get <id> --all --json
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`get` is for audit and handoff follow-up. Missing ids are an error. `--all`
|
|
158
|
+
searches registered ledgers until the id is found.
|
|
159
|
+
|
|
160
|
+
### `artshelf due`
|
|
161
|
+
|
|
162
|
+
Shows entries whose retention has expired or that need manual review.
|
|
163
|
+
Only `active` records participate in due classification; records already handled
|
|
164
|
+
by cleanup execution remain visible through `list` and validation.
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
artshelf due
|
|
168
|
+
artshelf due --json
|
|
169
|
+
artshelf due --all --json
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
V1 due statuses:
|
|
173
|
+
|
|
174
|
+
- `due`
|
|
175
|
+
- `manual-review`
|
|
176
|
+
- `missing-path`
|
|
177
|
+
- `kept`
|
|
178
|
+
|
|
179
|
+
`--all` classifies active entries across registered ledgers.
|
|
180
|
+
|
|
181
|
+
### `artshelf validate`
|
|
182
|
+
|
|
183
|
+
Checks ledger health without mutating files.
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
artshelf validate
|
|
187
|
+
artshelf validate --json
|
|
188
|
+
artshelf validate --all --json
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
V1 validation checks:
|
|
192
|
+
|
|
193
|
+
- ledger file is parseable JSONL
|
|
194
|
+
- required fields are present
|
|
195
|
+
- IDs are unique
|
|
196
|
+
- paths are absolute or resolvable
|
|
197
|
+
- TTL/retain-until/manual-review is valid
|
|
198
|
+
- cleanup action is known
|
|
199
|
+
- resolved records include `resolvedAt` and `resolutionReason`
|
|
200
|
+
- handled cleanup records include required cleanup metadata (`cleanupPlanId`,
|
|
201
|
+
`receiptPath`, and `cleanedAt`; trashed records also require `targetPath`)
|
|
202
|
+
- active and review-required recorded paths still exist, reported as warnings not hard failures
|
|
203
|
+
- trashed `targetPath` values still exist, reported as warnings not hard failures
|
|
204
|
+
|
|
205
|
+
`--all` validates registered ledgers and reports stale registry entries when a
|
|
206
|
+
registered ledger is missing from disk.
|
|
207
|
+
|
|
208
|
+
### `artshelf review`
|
|
209
|
+
|
|
210
|
+
Runs validation, due classification, and cleanup plan preview without mutating
|
|
211
|
+
files or writing a plan.
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
artshelf review --json
|
|
215
|
+
artshelf review --all --json
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
`review` is the compact report surface for scheduled checks. `--all` reads every
|
|
219
|
+
registered ledger from the registry; stale, invalid, and valid no-op ledgers are
|
|
220
|
+
included with a `not-created` plan instead of writing a plan file.
|
|
221
|
+
|
|
222
|
+
In `--all` mode, review emits an aggregate triage summary on top of the
|
|
223
|
+
per-ledger detail. JSON includes a `summary` block with affected-ledger, due,
|
|
224
|
+
manual-review, missing-path, executable, and skipped counts plus the preview
|
|
225
|
+
plan ids; JSON also includes the next safe action. Human output adds a one-line
|
|
226
|
+
triage count and states the same next safe action (repair broken ledgers, dry-run
|
|
227
|
+
cleanup, inspect missing paths, or nothing to do). Review never writes a plan, so
|
|
228
|
+
the next action always points at an explicit follow-up command.
|
|
229
|
+
|
|
230
|
+
### `artshelf doctor`
|
|
231
|
+
|
|
232
|
+
Reports whether Artshelf is healthy on the current machine without mutating
|
|
233
|
+
anything.
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
artshelf doctor
|
|
237
|
+
artshelf doctor --json
|
|
238
|
+
artshelf doctor --ledger <path>
|
|
239
|
+
artshelf doctor --registry <path>
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Doctor reports:
|
|
243
|
+
|
|
244
|
+
- CLI version and Node runtime version.
|
|
245
|
+
- The selected/default ledger path and selected/global registry path, and whether they exist.
|
|
246
|
+
- Registered ledger health, flagging stale (missing from disk) and invalid
|
|
247
|
+
(unparseable or malformed) entries.
|
|
248
|
+
- The cleanup safety posture, including that `cleanup --execute` is scoped to
|
|
249
|
+
one selected/default ledger and still requires a reviewed `--plan-id`, that
|
|
250
|
+
global execute is refused, that `cleanup=delete` is refused in v1, and that
|
|
251
|
+
physical trash purge requires a separate reviewed purge plan.
|
|
252
|
+
|
|
253
|
+
A healthy machine exits 0. A broken registry file or any stale or invalid
|
|
254
|
+
registered ledger exits non-zero with actionable errors. Humans should run
|
|
255
|
+
`artshelf doctor` after install or when `--all` commands behave unexpectedly; agents
|
|
256
|
+
may run it on a schedule to catch stale registry entries before relying on
|
|
257
|
+
cleanup planning. Doctor never creates plans, receipts, or records.
|
|
258
|
+
|
|
259
|
+
### `artshelf status`
|
|
260
|
+
|
|
261
|
+
The lightweight daily "what is going on?" view across ledgers.
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
artshelf status
|
|
265
|
+
artshelf status --json
|
|
266
|
+
artshelf status --all --json
|
|
267
|
+
artshelf status --all --registry <path> --json
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Status reports:
|
|
271
|
+
|
|
272
|
+
- Registry health and the number of registered ledgers (with single `--ledger`
|
|
273
|
+
it reports just that ledger).
|
|
274
|
+
- Per-ledger and aggregated counts of active artifacts, kept, due,
|
|
275
|
+
manual-review, and missing-path entries.
|
|
276
|
+
- The pending cleanup count: how many entries a cleanup plan would currently
|
|
277
|
+
contain, computed read-only without writing a plan.
|
|
278
|
+
|
|
279
|
+
`artshelf status --all --json` is suitable for cron and reporting, and the human
|
|
280
|
+
output is short enough to paste into a chat. Status is strictly read-only: it
|
|
281
|
+
never creates plans or receipts and never mutates records. A healthy machine
|
|
282
|
+
exits 0. In `--all` mode, a broken registry or any stale or invalid registered
|
|
283
|
+
ledger exits non-zero. Due entries are normal operational state and do not change
|
|
284
|
+
the exit code. With single `--ledger`, a not-yet-created ledger reports empty
|
|
285
|
+
counts.
|
|
286
|
+
|
|
287
|
+
### `artshelf cleanup --dry-run`
|
|
288
|
+
|
|
289
|
+
Creates a cleanup plan when there are executable cleanup entries, but does not
|
|
290
|
+
mutate artifacts. If there are no executable cleanup entries, dry-run reports
|
|
291
|
+
`planId=not-created`, `planPath=null`, and does not write a plan file.
|
|
292
|
+
If an existing plan has the same executable cleanup entries, Artshelf reuses that
|
|
293
|
+
plan id, refreshes `generatedAt`, rewrites the same plan file, and refreshes the
|
|
294
|
+
Artshelf-owned plan artifact record instead of creating a duplicate plan.
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
artshelf cleanup --dry-run
|
|
298
|
+
artshelf cleanup --dry-run --json
|
|
299
|
+
artshelf cleanup --dry-run --all --json
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Written plans must include:
|
|
303
|
+
|
|
304
|
+
- `planId`
|
|
305
|
+
- generated timestamp
|
|
306
|
+
- candidate entry IDs
|
|
307
|
+
- planned action per entry
|
|
308
|
+
- skipped/refused entries with reasons
|
|
309
|
+
- plan file path
|
|
310
|
+
|
|
311
|
+
`--all` creates dry-run plans only for registered ledgers that have executable
|
|
312
|
+
cleanup entries, and only after every registered ledger validates. Global
|
|
313
|
+
cleanup execution is refused.
|
|
314
|
+
|
|
315
|
+
When a dry-run writes a cleanup plan, Artshelf appends or refreshes an Artshelf-owned
|
|
316
|
+
ledger record for the plan file with `owner=artshelf`, `kind=run-artifact`,
|
|
317
|
+
`ttl=14d`, `cleanup=trash`, and labels including `artshelf`, `cleanup-plan`, and the
|
|
318
|
+
plan id.
|
|
319
|
+
|
|
320
|
+
### `artshelf cleanup --execute`
|
|
321
|
+
|
|
322
|
+
Executes a previously generated cleanup plan.
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
artshelf cleanup --execute --plan-id <id>
|
|
326
|
+
artshelf cleanup --execute --plan-id <id> --json
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Rules:
|
|
330
|
+
|
|
331
|
+
- Requires `--plan-id`.
|
|
332
|
+
- Refuses to generate a fresh live cleanup set during execute.
|
|
333
|
+
- Writes a cleanup receipt and appends or refreshes an Artshelf-owned ledger record
|
|
334
|
+
for that receipt with `owner=artshelf`, `kind=run-artifact`, `ttl=30d`,
|
|
335
|
+
`cleanup=review`, and labels including `artshelf`, `cleanup-receipt`, and the
|
|
336
|
+
plan id.
|
|
337
|
+
- Updates touched ledger records so handled artifacts stop appearing as active
|
|
338
|
+
cleanup candidates.
|
|
339
|
+
- Uses trash/review behavior by default.
|
|
340
|
+
- `delete` is refused in v1: even when a ledger entry says `cleanup=delete`,
|
|
341
|
+
execute records a `cleanup-refused` receipt (`delete is disabled in v1`) and
|
|
342
|
+
never removes the file. Physical deletion is only available later through a
|
|
343
|
+
separately reviewed `artshelf trash purge --execute` plan for quarantined trash.
|
|
344
|
+
|
|
345
|
+
### `artshelf trash list`
|
|
346
|
+
|
|
347
|
+
Read-only listing of records that cleanup execution moved into Artshelf trash
|
|
348
|
+
(`status=trashed`).
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
artshelf trash list
|
|
352
|
+
artshelf trash list --ledger <path> --json
|
|
353
|
+
artshelf trash list --all --json
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Rules:
|
|
357
|
+
|
|
358
|
+
- Reports `id`, `targetPath`, `cleanedAt`, `receiptPath`, `cleanupPlanId`, and a
|
|
359
|
+
human-readable `age` for each trashed record.
|
|
360
|
+
- Never moves, deletes, or resolves records.
|
|
361
|
+
- `--all` reads every registered ledger through the registry and validates those
|
|
362
|
+
ledgers first, the same way `list --all` and `review --all` do.
|
|
363
|
+
|
|
364
|
+
### `artshelf trash purge`
|
|
365
|
+
|
|
366
|
+
Approval-first physical deletion of quarantined trash. Trashed artifacts stay in
|
|
367
|
+
Artshelf trash until a separately reviewed purge plan removes them, mirroring the
|
|
368
|
+
cleanup dry-run/execute boundary.
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
artshelf trash purge --older-than <ttl> --dry-run --ledger <path> --json
|
|
372
|
+
artshelf trash purge --execute --plan-id <id> --ledger <path> --json
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Rules:
|
|
376
|
+
|
|
377
|
+
- Scoped to a single ledger. `--all` is refused for purge (it is only supported
|
|
378
|
+
by `trash list`); there is no global blind delete.
|
|
379
|
+
- Requires either `--dry-run` or `--execute`; there is no non-persisted preview
|
|
380
|
+
that looks like an executable reviewed plan.
|
|
381
|
+
- `--dry-run` builds an age-based purge plan from records whose `cleanedAt` is
|
|
382
|
+
older than `--older-than`, writes it to `<ledger-dir>/purge-plans/<id>.json`,
|
|
383
|
+
and registers an Artshelf-owned plan record (`ttl=14d`, `cleanup=review`, labels
|
|
384
|
+
including `artshelf`, `trash-purge-plan`, and the purge plan id). No-op dry-runs
|
|
385
|
+
report `not-created` and write no plan file.
|
|
386
|
+
- The purge plan records `purgePlanId`, `generatedAt`, `ledgerPath`,
|
|
387
|
+
`olderThan`, and the computed `cutoff`. Each executable entry includes
|
|
388
|
+
`id`, `targetPath`, `cleanedAt`, `receiptPath`, and `cleanupPlanId`; skipped
|
|
389
|
+
records include `id`, `targetPath`, and the skip `reason`.
|
|
390
|
+
- `--execute` requires a `--plan-id` produced by an earlier reviewed dry-run; it
|
|
391
|
+
refuses to compute a fresh purge set and refuses to rerun a purge plan with an
|
|
392
|
+
already completed receipt. It physically removes each planned trash target,
|
|
393
|
+
skipping entries whose record is missing, is no longer `trashed`, or whose
|
|
394
|
+
target is already gone. Before removal it also re-checks that the plan entry
|
|
395
|
+
still matches the live ledger record and that the target remains inside Artshelf's
|
|
396
|
+
ledger-local trash directory for that cleanup plan.
|
|
397
|
+
- Writes a `started` purge receipt to `<ledger-dir>/purge-receipts/<id>.json`
|
|
398
|
+
before deletion, records `pending` and `deleting` result states during the run,
|
|
399
|
+
then completes the receipt with `purged`, `skipped`, or `failed` results. If an
|
|
400
|
+
interrupted purge left a started receipt, a later execute resumes from those
|
|
401
|
+
results and reconciles a `deleting` entry whose target is already gone as
|
|
402
|
+
`purged`.
|
|
403
|
+
- Registers the completed receipt (`ttl=30d`, `cleanup=review`, labels including
|
|
404
|
+
`artshelf`, `trash-purge-receipt`, and the purge plan id) so the final deletion
|
|
405
|
+
stays auditable.
|
|
406
|
+
- Marks purged records `resolved` with `purgedAt`, `purgePlanId`, and
|
|
407
|
+
`purgeReceiptPath`, so they no longer reappear as trashed.
|
|
408
|
+
|
|
409
|
+
### `artshelf resolve`
|
|
410
|
+
|
|
411
|
+
Marks a handled, missing, or no-longer-needed record as manually resolved while
|
|
412
|
+
keeping it in the ledger audit trail.
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
artshelf resolve <id> --status resolved --reason <text>
|
|
416
|
+
artshelf resolve <id> --status resolved --reason <text> --json
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
Rules:
|
|
420
|
+
|
|
421
|
+
- Requires `<id>`, `--status resolved`, and `--reason`.
|
|
422
|
+
- Does not move or delete files.
|
|
423
|
+
- Removes the record from future `due` and cleanup dry-run output.
|
|
424
|
+
- Keeps the record visible through `list` and `list --status resolved`.
|
|
425
|
+
- Refuses records that are already `resolved`; the original reason is preserved.
|
|
426
|
+
|
|
427
|
+
## Ledger Storage
|
|
428
|
+
|
|
429
|
+
V1 supports two scopes:
|
|
430
|
+
|
|
431
|
+
- repo-local: `.shelf/ledger.jsonl`
|
|
432
|
+
- user-global: `~/.shelf/ledger.jsonl`
|
|
433
|
+
|
|
434
|
+
Default behavior:
|
|
435
|
+
|
|
436
|
+
- If the current directory is inside a git repo, write repo-local.
|
|
437
|
+
- Otherwise write user-global.
|
|
438
|
+
- Allow `--ledger <path>` for explicit tests and unusual workflows.
|
|
439
|
+
|
|
440
|
+
V1 also supports a user-level registry of known ledgers:
|
|
441
|
+
|
|
442
|
+
- registry: `~/.shelf/ledgers.json`
|
|
443
|
+
- `ARTSHELF_REGISTRY` or `--registry <path>` can override the registry path.
|
|
444
|
+
- `put` registers the ledger it writes to.
|
|
445
|
+
- `ledgers add` registers an existing ledger explicitly.
|
|
446
|
+
- `--all` reads registered ledgers as one review surface.
|
|
447
|
+
- `trash list --all` reads trashed records across registered ledgers after
|
|
448
|
+
registry validation.
|
|
449
|
+
- `cleanup --execute --all` and `trash purge --all` are refused; execution stays
|
|
450
|
+
scoped to one explicit ledger and one reviewed plan id.
|
|
451
|
+
|
|
452
|
+
## Ledger Registry Schema
|
|
453
|
+
|
|
454
|
+
```json
|
|
455
|
+
{
|
|
456
|
+
"version": 1,
|
|
457
|
+
"ledgers": [
|
|
458
|
+
{
|
|
459
|
+
"name": "my-repo",
|
|
460
|
+
"path": "/absolute/path/to/repo/.shelf/ledger.jsonl",
|
|
461
|
+
"scope": "repo",
|
|
462
|
+
"createdAt": "2026-06-01T05:42:00Z",
|
|
463
|
+
"updatedAt": "2026-06-01T05:42:00Z"
|
|
464
|
+
}
|
|
465
|
+
]
|
|
466
|
+
}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
## Ledger Record Schema
|
|
470
|
+
|
|
471
|
+
```json
|
|
472
|
+
{
|
|
473
|
+
"id": "shf_20260601_154200_ab12",
|
|
474
|
+
"path": "/absolute/path/to/artifact",
|
|
475
|
+
"kind": "scratch",
|
|
476
|
+
"reason": "debug parser output",
|
|
477
|
+
"createdAt": "2026-06-01T05:42:00Z",
|
|
478
|
+
"retainUntil": "2026-06-04T05:42:00Z",
|
|
479
|
+
"retention": {
|
|
480
|
+
"mode": "ttl",
|
|
481
|
+
"ttl": "3d"
|
|
482
|
+
},
|
|
483
|
+
"cleanup": "trash",
|
|
484
|
+
"owner": "manual",
|
|
485
|
+
"labels": ["debug"],
|
|
486
|
+
"status": "active"
|
|
487
|
+
}
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
V1 record statuses:
|
|
491
|
+
|
|
492
|
+
- `active`: eligible for `due` classification and cleanup dry-run planning.
|
|
493
|
+
- `review-required`: execution surfaced the artifact for manual review.
|
|
494
|
+
- `trashed`: execution moved a `cleanup=trash` artifact into Artshelf trash.
|
|
495
|
+
- `cleanup-refused`: execution refused the requested action, such as physical
|
|
496
|
+
delete in v1.
|
|
497
|
+
- `resolved`: a human or agent marked the record as manually handled.
|
|
498
|
+
|
|
499
|
+
Handled records may include cleanup outcome fields:
|
|
500
|
+
|
|
501
|
+
```json
|
|
502
|
+
{
|
|
503
|
+
"cleanupPlanId": "plan_20260601_154200_cd34",
|
|
504
|
+
"receiptPath": "/absolute/path/.shelf/receipts/plan_20260601_154200_cd34.json",
|
|
505
|
+
"cleanedAt": "2026-06-01T05:45:00Z",
|
|
506
|
+
"targetPath": "/absolute/path/.shelf/trash/plan_20260601_154200_cd34/shf_20260601_154200_ab12-artifact",
|
|
507
|
+
"cleanupReason": "delete is disabled in v1"
|
|
508
|
+
}
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
Manually resolved records include:
|
|
512
|
+
|
|
513
|
+
```json
|
|
514
|
+
{
|
|
515
|
+
"resolvedAt": "2026-06-01T05:45:00Z",
|
|
516
|
+
"resolutionReason": "artifact inspected and no longer needed"
|
|
517
|
+
}
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
Records removed by `artshelf trash purge --execute` become `resolved` and also carry
|
|
521
|
+
the purge provenance:
|
|
522
|
+
|
|
523
|
+
```json
|
|
524
|
+
{
|
|
525
|
+
"resolvedAt": "2026-06-01T06:10:00Z",
|
|
526
|
+
"resolutionReason": "trash purge completed",
|
|
527
|
+
"purgedAt": "2026-06-01T06:10:00Z",
|
|
528
|
+
"purgePlanId": "purge_20260601_061000_ef56",
|
|
529
|
+
"purgeReceiptPath": "/absolute/path/.shelf/purge-receipts/purge_20260601_061000_ef56.json"
|
|
530
|
+
}
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
## Cleanup Safety Model
|
|
534
|
+
|
|
535
|
+
Cleanup execution is intentionally boring and approval-only. Five boundaries
|
|
536
|
+
hold, and every future feature (`status`, `doctor`, `review`, scheduled jobs,
|
|
537
|
+
...) must preserve them rather than add a shortcut around them:
|
|
538
|
+
|
|
539
|
+
- **No daemon.** Artshelf never runs in the background or watches the clock. It
|
|
540
|
+
only does work while you are running a `artshelf` command.
|
|
541
|
+
- **No auto-execute.** No command cleans up as a side effect. The only commands
|
|
542
|
+
that move, trash, or delete files are `artshelf cleanup --execute` and
|
|
543
|
+
`artshelf trash purge --execute`, each run by a human against a separately
|
|
544
|
+
reviewed plan id.
|
|
545
|
+
- **No global execute.** `cleanup --execute --all` and `trash purge --all`
|
|
546
|
+
are refused; `--all` is read-only or dry-run reporting only. Execution is
|
|
547
|
+
always scoped to a single reviewed plan id.
|
|
548
|
+
- **No fresh-plan-then-execute.** `cleanup --execute` refuses to compute a new
|
|
549
|
+
live set. It acts only on a plan id that an earlier `cleanup --dry-run`
|
|
550
|
+
produced and a human reviewed; it will not plan and execute in one step.
|
|
551
|
+
- **No silent deletion.** Cleanup trashes or flags for review and writes a
|
|
552
|
+
receipt to the ledger. The `cleanup=delete` action stays refused in v1; the
|
|
553
|
+
one sanctioned physical deletion is `artshelf trash purge --execute`, which only
|
|
554
|
+
removes already-quarantined trash through its own reviewed purge plan and
|
|
555
|
+
receipt. Nothing leaves the filesystem without an auditable trail.
|
|
556
|
+
|
|
557
|
+
Operational rules that back those boundaries:
|
|
558
|
+
|
|
559
|
+
- Dry-run first.
|
|
560
|
+
- Execute only by plan id.
|
|
561
|
+
- Trash/review before delete.
|
|
562
|
+
- Execute updates ledger state after writing the cleanup receipt. A trashed,
|
|
563
|
+
review-required, or refused record no longer participates in future `due` or
|
|
564
|
+
cleanup dry-run output by default.
|
|
565
|
+
- Missing paths update the report; they are not treated as a successful cleanup
|
|
566
|
+
unless the user explicitly repairs the ledger later.
|
|
567
|
+
- Cleanup never scans arbitrary filesystem paths for deletion in v1.
|
|
568
|
+
- Cleanup only acts on ledger entries.
|
|
569
|
+
- Trash purge is scoped to one ledger, requires a reviewed purge plan id, and
|
|
570
|
+
writes a purge receipt before removing quarantined files.
|
|
571
|
+
|
|
572
|
+
## Agent Usage Contract
|
|
573
|
+
|
|
574
|
+
Agents should call `artshelf put` immediately after creating:
|
|
575
|
+
|
|
576
|
+
- config backups
|
|
577
|
+
- quarantine folders
|
|
578
|
+
- debug output directories
|
|
579
|
+
- temporary repo artifacts
|
|
580
|
+
- one-off generated reports
|
|
581
|
+
- copied files kept for rollback
|
|
582
|
+
|
|
583
|
+
Agents should not run `artshelf cleanup --execute` or
|
|
584
|
+
`artshelf trash purge --execute` without explicit approval naming the ledger path
|
|
585
|
+
and reviewed plan id.
|
|
586
|
+
|
|
587
|
+
Agents may run `artshelf find` and `artshelf get` before `put` to avoid duplicate
|
|
588
|
+
registrations. `find`/`get` are read-only ledger queries; they must not be used
|
|
589
|
+
as permission to clean up or resolve a record.
|
|
590
|
+
|
|
591
|
+
Agents may run `artshelf resolve <id> --status resolved --reason <text>` only
|
|
592
|
+
after explicit confirmation that the record has been handled, is missing, or is
|
|
593
|
+
no longer needed. The reason must be specific; resolve does not move or delete
|
|
594
|
+
files.
|
|
595
|
+
|
|
596
|
+
Scheduled jobs may run:
|
|
597
|
+
|
|
598
|
+
```bash
|
|
599
|
+
artshelf due --json
|
|
600
|
+
artshelf due --all --json
|
|
601
|
+
artshelf review --all --json
|
|
602
|
+
artshelf doctor --json
|
|
603
|
+
artshelf status --all --json
|
|
604
|
+
artshelf cleanup --dry-run --json
|
|
605
|
+
artshelf cleanup --dry-run --all --json
|
|
606
|
+
artshelf trash list --ledger <path> --json
|
|
607
|
+
artshelf trash list --all --json
|
|
608
|
+
artshelf trash purge --older-than <ttl> --dry-run --ledger <path> --json
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
`artshelf review --all --json` is the read-only all-ledger triage surface;
|
|
612
|
+
scheduled reports should include its aggregate `summary` and `nextAction` when
|
|
613
|
+
whole-machine review is needed.
|
|
614
|
+
|
|
615
|
+
Scheduled trash reports may use `artshelf trash list --all --json` for
|
|
616
|
+
registered-ledger discovery and should include trashed record counts and target
|
|
617
|
+
ages. Purge dry-runs stay scoped to one explicit ledger and should report any
|
|
618
|
+
plan id, matching entries, and skipped entries.
|
|
619
|
+
|
|
620
|
+
Scheduled jobs must never run `artshelf cleanup --execute` or
|
|
621
|
+
`artshelf trash purge --execute`; they may only dry-run and report plans for later
|
|
622
|
+
human review.
|
|
623
|
+
|
|
624
|
+
## Dogfood Scenarios
|
|
625
|
+
|
|
626
|
+
1. Record a repo-local `tmp/` scratch directory with a 3-day TTL.
|
|
627
|
+
2. Record a config backup with manual review retention.
|
|
628
|
+
3. Generate a dry-run cleanup plan after TTL expiry using fixture data.
|
|
629
|
+
4. Execute a cleanup plan in a temporary test fixture and verify receipt output.
|
|
630
|
+
5. List trashed records, dry-run an old-trash purge, then execute the reviewed
|
|
631
|
+
purge plan in a fixture and verify receipt output plus resolved ledger state.
|
|
632
|
+
|
|
633
|
+
## V1 Acceptance Criteria
|
|
634
|
+
|
|
635
|
+
- CLI can record entries to JSONL.
|
|
636
|
+
- CLI can register known ledgers and list them with per-ledger validation status
|
|
637
|
+
by default, or a `--plain` fast path that skips validation.
|
|
638
|
+
- CLI can review registered ledgers through `--all` read-only entry points,
|
|
639
|
+
emitting an aggregate triage summary and the next safe action.
|
|
640
|
+
- CLI refuses records without a reason.
|
|
641
|
+
- CLI requires TTL, retain-until, or manual-review.
|
|
642
|
+
- CLI can list, filter by status, and show due entries.
|
|
643
|
+
- CLI can find existing records by path/owner/label/status and get records by id.
|
|
644
|
+
- CLI can mark records manually resolved with a required reason.
|
|
645
|
+
- CLI validates ledger shape.
|
|
646
|
+
- CLI reports machine and registry health through `artshelf doctor`, exiting
|
|
647
|
+
non-zero when the registry or a registered ledger is broken.
|
|
648
|
+
- CLI reports a read-only daily dashboard through `artshelf status`, with
|
|
649
|
+
`--all --json` suitable for cron and human output short enough to paste into
|
|
650
|
+
a chat; status never creates plans, receipts, or records.
|
|
651
|
+
- Cleanup dry-run creates a plan id only when there are executable cleanup
|
|
652
|
+
entries; no-op dry-runs do not write plan files.
|
|
653
|
+
- Cleanup dry-run and execute register the plan/receipt artifacts that Artshelf
|
|
654
|
+
creates.
|
|
655
|
+
- Cleanup execute refuses to run without a plan id.
|
|
656
|
+
- Cleanup execute writes a receipt.
|
|
657
|
+
- CLI can list trashed records (single ledger or `--all`) and purge them through
|
|
658
|
+
an approval-first, ledger-scoped dry-run/execute boundary that writes a purge
|
|
659
|
+
receipt; purge refuses `--all` and never deletes without a reviewed plan id.
|
|
660
|
+
- All core commands support `--json`.
|
|
661
|
+
- Tests cover record/list/find/get/status-filter/due/validate/resolve/registry,
|
|
662
|
+
`artshelf doctor`, the `artshelf status` dashboard, `--all` review, stale-registry,
|
|
663
|
+
dry-run, global-dry-run, execute-plan, and trash list/purge behavior.
|
|
664
|
+
|
|
665
|
+
## Deferred
|
|
666
|
+
|
|
667
|
+
- Cron integration.
|
|
668
|
+
- Agent skill adapters.
|
|
669
|
+
- GitHub Action.
|
|
670
|
+
- Fake/demo mode.
|
|
671
|
+
- Rollback command.
|
|
672
|
+
- Retention classes like keep-daily/weekly/monthly.
|
|
673
|
+
- Dependency roots and pinning.
|
|
674
|
+
- Credential scanning.
|
|
675
|
+
- Public package publishing.
|