artshelf 0.15.0 → 0.16.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 +10 -0
- package/README.md +12 -8
- package/SPEC.md +106 -28
- package/dist/src/commands/dispose.js +88 -0
- package/dist/src/commands/index.js +4 -0
- package/dist/src/dispose.js +706 -0
- package/dist/src/inspect.js +7 -4
- package/dist/src/ledger.js +33 -22
- package/dist/src/shared/flags.js +2 -0
- package/dist/src/shared/help-text.js +24 -0
- package/docs/agent-clean.html +30 -5
- package/docs/agent-monitor.html +2 -1
- package/docs/agent-review.html +6 -4
- package/docs/agent-usage.html +2 -2
- package/docs/agent-usage.md +6 -6
- package/docs/index.html +4 -0
- package/docs/install.html +2 -2
- package/docs/reference.html +30 -3
- package/package.json +1 -1
- package/skills/artshelf/SKILL.md +27 -38
package/CHANGELOG.md
CHANGED
|
@@ -136,6 +136,16 @@
|
|
|
136
136
|
- Moved `artshelf put` registry-warning output from stdout to stderr in human
|
|
137
137
|
mode; `--json` output is unchanged (NGX-429).
|
|
138
138
|
|
|
139
|
+
## [0.16.0](https://github.com/calvinnwq/artshelf/compare/v0.15.0...v0.16.0) (2026-06-24)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
### Features
|
|
143
|
+
|
|
144
|
+
* **dispose:** add approval-gated artifact disposition ([ee2406e](https://github.com/calvinnwq/artshelf/commit/ee2406e1850f6f2775d5d581bb529fac295b5159))
|
|
145
|
+
* **dispose:** add disposition dry-run domain layer ([c7bdbb3](https://github.com/calvinnwq/artshelf/commit/c7bdbb3d8a40ffc3c9c14c7da6eb6acfedb42859))
|
|
146
|
+
* **dispose:** add disposition execute domain layer ([b9fda32](https://github.com/calvinnwq/artshelf/commit/b9fda3254576d99e5c222a6d1a04c25b1a08d8b0))
|
|
147
|
+
* **dispose:** wire disposition CLI command ([7fe7921](https://github.com/calvinnwq/artshelf/commit/7fe792123b2d4a05bef37d360ced573b82dcd322))
|
|
148
|
+
|
|
139
149
|
## [0.15.0](https://github.com/calvinnwq/artshelf/compare/v0.14.0...v0.15.0) (2026-06-20)
|
|
140
150
|
|
|
141
151
|
|
package/README.md
CHANGED
|
@@ -83,7 +83,7 @@ Follow the instructions in https://github.com/calvinnwq/artshelf/blob/main/INSTA
|
|
|
83
83
|
It will install the CLI, copy the portable skill (with its bundled review-report
|
|
84
84
|
renderer), register any existing project ledgers, and — only with your approval —
|
|
85
85
|
schedule a **read-only** daily review. Scheduled jobs review and report only;
|
|
86
|
-
cleanup and purge execution always come back to you. See [INSTALL.md](INSTALL.md)
|
|
86
|
+
cleanup, dispose, and purge execution always come back to you. See [INSTALL.md](INSTALL.md)
|
|
87
87
|
for the full steps.
|
|
88
88
|
|
|
89
89
|
## How it works
|
|
@@ -109,7 +109,7 @@ destructive deletion.
|
|
|
109
109
|
- **Ledger-first**, not filesystem-scan-first — every artifact is a recorded decision.
|
|
110
110
|
- **Dry-run before mutation**, and execute only from a reviewed plan id.
|
|
111
111
|
- **No daemon, no auto-execute, no global execute** — `--all` is read-only or
|
|
112
|
-
dry-run reporting; cleanup and purge refuse it.
|
|
112
|
+
dry-run reporting; cleanup, dispose, and purge refuse it.
|
|
113
113
|
- **No fresh-plan-then-execute shortcut** — review the plan, then run that plan.
|
|
114
114
|
- **Trash before delete** — `cleanup=delete` stays refused; physical deletion
|
|
115
115
|
needs its own reviewed trash purge. No silent deletion, ever.
|
|
@@ -118,8 +118,8 @@ destructive deletion.
|
|
|
118
118
|
registry mutations take a cross-process lock so overlapping commands never
|
|
119
119
|
lose records or leave a half-written ledger.
|
|
120
120
|
- **`--json` on every command**, so agents can act on structured output.
|
|
121
|
-
- **`--agent` on `review`/`status`/`doctor`, `ledgers prune --dry-run`,
|
|
122
|
-
`get --inspect`**, a compact, token-efficient decision packet for agents,
|
|
121
|
+
- **`--agent` on `review`/`status`/`doctor`, `ledgers prune --dry-run`,
|
|
122
|
+
`dispose --dry-run`, and `get --inspect`**, a compact, token-efficient decision packet for agents,
|
|
123
123
|
while the default render stays human-scannable.
|
|
124
124
|
|
|
125
125
|
## Reference
|
|
@@ -147,6 +147,8 @@ artshelf doctor
|
|
|
147
147
|
artshelf update [--json]
|
|
148
148
|
artshelf cleanup --dry-run [--all]
|
|
149
149
|
artshelf cleanup --execute --plan-id <id> [--ledger <path>] [--json]
|
|
150
|
+
artshelf dispose --id <id> --action trash-resolve|resolve-only|snooze|keep --dry-run [--reason <text>] [--ttl <ttl>|--retain-until <date>] [--ledger <path>] [--json|--agent]
|
|
151
|
+
artshelf dispose --execute --plan-id <id> [--ledger <path>] [--json]
|
|
150
152
|
artshelf reconcile --dry-run [--all] [--ledger <path>] [--json]
|
|
151
153
|
artshelf reconcile --execute --plan-id <id> --ledger <path> [--json]
|
|
152
154
|
artshelf trash list [--all] [--ledger <path>] [--json]
|
|
@@ -159,8 +161,9 @@ Use `artshelf help` for a grouped command list, then `artshelf <command> --help`
|
|
|
159
161
|
or `artshelf help <command>` for focused details. Nested commands such as
|
|
160
162
|
`artshelf trash purge --help`, `artshelf ledgers add --help`, and
|
|
161
163
|
`artshelf ledgers prune --help` show only that subcommand. All core commands
|
|
162
|
-
support `--json`; `review`, `status`, `doctor`, `ledgers prune --dry-run`,
|
|
163
|
-
`get --inspect` also take `--agent` for a compact
|
|
164
|
+
support `--json`; `review`, `status`, `doctor`, `ledgers prune --dry-run`,
|
|
165
|
+
`dispose --dry-run`, and `get --inspect` also take `--agent` for a compact
|
|
166
|
+
decision packet; `--ledger`,
|
|
164
167
|
`--registry`, and `--all` are scope flags only on commands that list them.
|
|
165
168
|
</details>
|
|
166
169
|
|
|
@@ -202,8 +205,9 @@ The package includes an agent-facing skill at `skills/artshelf`. Agents that
|
|
|
202
205
|
support local skills can copy or reference this directory to learn when to call
|
|
203
206
|
`artshelf put`, how to report deterministic footnotes after JSON registration,
|
|
204
207
|
why `artshelf find` / `artshelf get` are the read-only idempotency lookup surface,
|
|
205
|
-
why `cleanup --execute`
|
|
206
|
-
dry-run cleanup and trash purge plans as review-report decision packets,
|
|
208
|
+
why `cleanup --execute` and `dispose --execute` require approved reviewed plan ids, how to render
|
|
209
|
+
dry-run cleanup and trash purge plans as review-report decision packets, how to use
|
|
210
|
+
`dispose --agent` for per-record approval packets, and when
|
|
207
211
|
`artshelf resolve <id> --status resolved --reason <text>` may mark a record handled
|
|
208
212
|
without moving or deleting files.
|
|
209
213
|
|
package/SPEC.md
CHANGED
|
@@ -218,7 +218,8 @@ exact next-safe action. It never reads or previews arbitrary file contents:
|
|
|
218
218
|
- `keep` — held for manual review, already resolved, already trashed, or due
|
|
219
219
|
with `cleanup=review`; it needs your judgment but nothing auto-runs.
|
|
220
220
|
- `snooze` — retention has not expired yet; re-inspect after it is due.
|
|
221
|
-
- `trash-safe` — due with `cleanup=trash`; safe to plan a reviewed
|
|
221
|
+
- `trash-safe` — due with `cleanup=trash`; safe to plan a reviewed dispose
|
|
222
|
+
`trash-resolve` decision.
|
|
222
223
|
- `resolve-only` — the recorded path is gone; resolve the record (ledger-only)
|
|
223
224
|
rather than cleaning a file.
|
|
224
225
|
- `blocked` — needs a human decision first: `cleanup=delete` (refused at
|
|
@@ -246,7 +247,7 @@ path: <backup-path>
|
|
|
246
247
|
status: active · cleanup: review · owner: agent · labels: registry-prune
|
|
247
248
|
existence: present (directory, 49 B) · age: 14d · retention: manual-review · due: manual-review
|
|
248
249
|
reason: rollback backup before registry prune
|
|
249
|
-
next: Held for manual review — keep
|
|
250
|
+
next: Held for manual review — run `artshelf dispose --id <id> --action keep --dry-run --reason '<why>' --ledger <ledger-path>` to keep it quiet through a reviewed decision, or choose resolve-only/snooze deliberately.
|
|
250
251
|
ledger: <ledger-path>
|
|
251
252
|
```
|
|
252
253
|
|
|
@@ -325,12 +326,13 @@ registry-prune dry-run for missing registered ledgers, dry-run reconcile for
|
|
|
325
326
|
missing-path or reconcile drift, or nothing to do). Review never writes a plan,
|
|
326
327
|
so the next action always points at an explicit follow-up command.
|
|
327
328
|
|
|
328
|
-
`review`, `status`, `doctor`, `ledgers prune --dry-run`,
|
|
329
|
-
expose agent-oriented render modes. For review/status/doctor, the default human render
|
|
329
|
+
`review`, `status`, `doctor`, `ledgers prune --dry-run`, `dispose --dry-run`,
|
|
330
|
+
and `get --inspect` expose agent-oriented render modes. For review/status/doctor, the default human render
|
|
330
331
|
leads each ledger and summary line with a `✓`/`⚠` attention glyph. `--json` stays
|
|
331
332
|
the full, backward-compatible public audit report; and `--agent` emits a compact,
|
|
332
333
|
deterministic single-line JSON decision packet for agents, taking precedence over
|
|
333
|
-
`--json` when both are passed. For `
|
|
334
|
+
`--json` when both are passed. For `dispose --dry-run`, `--agent` returns the per-request plan or blocked packet.
|
|
335
|
+
For `get --inspect`, `--agent` returns the
|
|
334
336
|
per-record decision packet and requires `--inspect`. For `review`, the packet sorts records into
|
|
335
337
|
ready-for-approval, needs-review-first, and blocked groups. Because review is
|
|
336
338
|
read-only and never mints a cleanup or registry-prune plan, the exact approval
|
|
@@ -687,6 +689,54 @@ Execute rules:
|
|
|
687
689
|
JSON output is deterministic (findings preserve ledger order) so agents can render a
|
|
688
690
|
decision packet and approve a specific plan id.
|
|
689
691
|
|
|
692
|
+
### `artshelf dispose`
|
|
693
|
+
|
|
694
|
+
Approval-gated disposition for one reviewed record. `dispose` is the command
|
|
695
|
+
surface that follows `get --inspect`: inspect stays read-only, then dispose
|
|
696
|
+
creates or executes the exact reviewed plan for the chosen decision.
|
|
697
|
+
|
|
698
|
+
```bash
|
|
699
|
+
artshelf dispose --id <id> --action trash-resolve --dry-run [--reason <text>] [--ledger <path>] [--json|--agent]
|
|
700
|
+
artshelf dispose --id <id> --action resolve-only --dry-run --reason <text> [--ledger <path>] [--json|--agent]
|
|
701
|
+
artshelf dispose --id <id> --action snooze --dry-run (--ttl <ttl>|--retain-until <date>) [--reason <text>] [--ledger <path>] [--json|--agent]
|
|
702
|
+
artshelf dispose --id <id> --action keep --dry-run [--reason <text>] [--ledger <path>] [--json|--agent]
|
|
703
|
+
artshelf dispose --execute --plan-id <id> --ledger <path> [--json]
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
Actions:
|
|
707
|
+
|
|
708
|
+
- `trash-resolve`: move the recorded path into plan-scoped Artshelf trash,
|
|
709
|
+
mark the row `trashed`, and leave physical deletion to a separate trash purge.
|
|
710
|
+
- `resolve-only`: resolve the ledger row only; requires `--reason`.
|
|
711
|
+
- `snooze`: extend retention; requires `--ttl` or `--retain-until`.
|
|
712
|
+
- `keep`: stamp that the record was reviewed and kept.
|
|
713
|
+
|
|
714
|
+
Dry-run rules:
|
|
715
|
+
|
|
716
|
+
- Classifies exactly one record id and action. There is no `--all` path.
|
|
717
|
+
- Writes a reviewed plan only when the request is actionable, under
|
|
718
|
+
`<ledger-dir>/dispose-plans/<id>.json`.
|
|
719
|
+
- Registers the plan as an Artshelf-owned artifact (`owner=artshelf`,
|
|
720
|
+
`kind=run-artifact`, `ttl=14d`, `cleanup=trash`, labels including
|
|
721
|
+
`artshelf`, `dispose-plan`, and the plan id).
|
|
722
|
+
- Prints the exact approval target:
|
|
723
|
+
`approve artshelf dispose ledger <ledger-path> plan <plan-id>`.
|
|
724
|
+
- A blocked request reports `planId=not-created`, writes no plan, and exits
|
|
725
|
+
non-zero while still returning a JSON/agent packet when requested.
|
|
726
|
+
|
|
727
|
+
Execute rules:
|
|
728
|
+
|
|
729
|
+
- Requires one explicit reviewed `--plan-id` and the target `--ledger`.
|
|
730
|
+
- Refuses missing, unknown, id-mismatched, ledger-mismatched, malformed, stale,
|
|
731
|
+
drifted, or target-conflicting plans before mutating.
|
|
732
|
+
- Re-snapshots the subject before execution; stale entries are skipped rather
|
|
733
|
+
than applied.
|
|
734
|
+
- Writes a dispose receipt to `<ledger-dir>/dispose-receipts/<id>.json` and
|
|
735
|
+
registers it as an Artshelf-owned artifact (`ttl=30d`, `cleanup=review`,
|
|
736
|
+
labels including `artshelf`, `dispose-receipt`, and the plan id).
|
|
737
|
+
- There is no fresh-plan-then-execute, no global execute, no daemon, and no
|
|
738
|
+
physical deletion.
|
|
739
|
+
|
|
690
740
|
## Ledger Storage
|
|
691
741
|
|
|
692
742
|
V1 supports two scopes:
|
|
@@ -738,9 +788,9 @@ V1 also supports a user-level registry of known ledgers:
|
|
|
738
788
|
registry validation.
|
|
739
789
|
- Registry-prune artifacts live next to the registry: `registry-prune-plans/`,
|
|
740
790
|
`registry-prune-rollbacks/`, and `registry-prune-receipts/`.
|
|
741
|
-
- `cleanup --execute --all`, `reconcile --execute --all`, and
|
|
742
|
-
are refused; execution stays scoped to one explicit ledger
|
|
743
|
-
reviewed plan id.
|
|
791
|
+
- `cleanup --execute --all`, `dispose --all`, `reconcile --execute --all`, and
|
|
792
|
+
`trash purge --all` are refused; execution stays scoped to one explicit ledger
|
|
793
|
+
or registry and one reviewed plan id.
|
|
744
794
|
|
|
745
795
|
## Ledger Registry Schema
|
|
746
796
|
|
|
@@ -823,6 +873,23 @@ the purge provenance:
|
|
|
823
873
|
}
|
|
824
874
|
```
|
|
825
875
|
|
|
876
|
+
Records touched by `artshelf dispose --execute` carry the dispose audit trail so
|
|
877
|
+
reviewed disposition stays traceable to the plan and receipt that produced it:
|
|
878
|
+
|
|
879
|
+
```json
|
|
880
|
+
{
|
|
881
|
+
"disposePlanId": "dispose_20260601_063000_ab12",
|
|
882
|
+
"disposeReceiptPath": "/absolute/path/.artshelf/dispose-receipts/dispose_20260601_063000_ab12.json",
|
|
883
|
+
"disposedAt": "2026-06-01T06:30:00Z",
|
|
884
|
+
"disposeAction": "trash-resolve",
|
|
885
|
+
"disposeReason": "reviewed and no longer needed"
|
|
886
|
+
}
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
`trash-resolve` also sets `previousPath` and `targetPath`; `resolve-only` sets
|
|
890
|
+
the resolve fields without moving the subject; `snooze` updates retention; and
|
|
891
|
+
`keep` leaves retention intact while making due classification quiet.
|
|
892
|
+
|
|
826
893
|
Records touched by `artshelf reconcile --execute` carry the reconcile audit trail so a
|
|
827
894
|
remap or resolve stays traceable to the reviewed plan that produced it:
|
|
828
895
|
|
|
@@ -895,15 +962,16 @@ hold, and every future feature (`status`, `doctor`, `review`, scheduled jobs,
|
|
|
895
962
|
- **No daemon.** Artshelf never runs in the background or watches the clock. It
|
|
896
963
|
only does work while you are running an `artshelf` command.
|
|
897
964
|
- **No auto-execute.** No command cleans up as a side effect. The only commands
|
|
898
|
-
that move, trash, or delete files are `artshelf cleanup --execute
|
|
899
|
-
`artshelf trash purge --execute`, each run
|
|
900
|
-
reviewed plan id.
|
|
901
|
-
- **No global execute.** `cleanup --execute --all
|
|
902
|
-
are refused; `--all` is read-only or dry-run reporting
|
|
903
|
-
always scoped to a single reviewed plan id.
|
|
904
|
-
- **No fresh-plan-then-execute.** `cleanup --execute`
|
|
905
|
-
live set.
|
|
906
|
-
produced and a human reviewed;
|
|
965
|
+
that move, trash, or delete files are `artshelf cleanup --execute`,
|
|
966
|
+
`artshelf dispose --execute`, and `artshelf trash purge --execute`, each run
|
|
967
|
+
by a human against a separately reviewed plan id.
|
|
968
|
+
- **No global execute.** `cleanup --execute --all`, `dispose --all`, and
|
|
969
|
+
`trash purge --all` are refused; `--all` is read-only or dry-run reporting
|
|
970
|
+
only where supported. Execution is always scoped to a single reviewed plan id.
|
|
971
|
+
- **No fresh-plan-then-execute.** `cleanup --execute` and `dispose --execute`
|
|
972
|
+
refuse to compute a new live set. They act only on plan ids that earlier
|
|
973
|
+
dry-runs produced and a human reviewed; they will not plan and execute in one
|
|
974
|
+
step.
|
|
907
975
|
- **No silent deletion.** Cleanup trashes or flags for review and writes a
|
|
908
976
|
receipt to the ledger. The `cleanup=delete` action stays refused in v1; the
|
|
909
977
|
one sanctioned physical deletion is `artshelf trash purge --execute`, which only
|
|
@@ -940,9 +1008,9 @@ Agents should call `artshelf put` immediately after creating:
|
|
|
940
1008
|
- one-off generated reports
|
|
941
1009
|
- copied files kept for rollback
|
|
942
1010
|
|
|
943
|
-
Agents should not run `artshelf cleanup --execute
|
|
944
|
-
`artshelf trash purge --execute` without
|
|
945
|
-
and reviewed plan id.
|
|
1011
|
+
Agents should not run `artshelf cleanup --execute`,
|
|
1012
|
+
`artshelf dispose --execute`, or `artshelf trash purge --execute` without
|
|
1013
|
+
explicit approval naming the ledger path and reviewed plan id.
|
|
946
1014
|
|
|
947
1015
|
Agents may run `artshelf find` and `artshelf get` before `put` to avoid duplicate
|
|
948
1016
|
registrations. `find`/`get` are read-only ledger queries; they must not be used
|
|
@@ -1004,11 +1072,14 @@ then render a compact decision report from it. The packet schema is
|
|
|
1004
1072
|
docs/skills carry matching copies for browsable docs and portable agent
|
|
1005
1073
|
installs. The report groups decisions into ready-for-approval,
|
|
1006
1074
|
needs-review-first, and blocked sections, and must still include exact approval
|
|
1007
|
-
targets in the message body.
|
|
1075
|
+
targets in the message body. `dispose --dry-run --agent` already emits its own
|
|
1076
|
+
compact per-record approval packet, so it does not require wrapping in this
|
|
1077
|
+
review-report schema.
|
|
1008
1078
|
|
|
1009
1079
|
Scheduled jobs must never run `artshelf cleanup --execute`,
|
|
1010
|
-
`artshelf ledgers prune --execute`,
|
|
1011
|
-
only dry-run and report plans
|
|
1080
|
+
`artshelf ledgers prune --execute`, `artshelf dispose --execute`, or
|
|
1081
|
+
`artshelf trash purge --execute`; they may only dry-run and report plans
|
|
1082
|
+
for later human review.
|
|
1012
1083
|
|
|
1013
1084
|
## Dogfood Scenarios
|
|
1014
1085
|
|
|
@@ -1016,7 +1087,9 @@ only dry-run and report plans for later human review.
|
|
|
1016
1087
|
2. Record a config backup with manual review retention.
|
|
1017
1088
|
3. Generate a dry-run cleanup plan after TTL expiry using fixture data.
|
|
1018
1089
|
4. Execute a cleanup plan in a temporary test fixture and verify receipt output.
|
|
1019
|
-
5.
|
|
1090
|
+
5. Dispose one reviewed record through dry-run and exact execute approval, then
|
|
1091
|
+
verify the receipt, audit fields, and quiet review state.
|
|
1092
|
+
6. List trashed records, dry-run an old-trash purge, then execute the reviewed
|
|
1020
1093
|
purge plan in a fixture and verify receipt output plus resolved ledger state.
|
|
1021
1094
|
|
|
1022
1095
|
## V1 Acceptance Criteria
|
|
@@ -1054,6 +1127,11 @@ only dry-run and report plans for later human review.
|
|
|
1054
1127
|
- Cleanup execute writes a started receipt before moving files, resumes or
|
|
1055
1128
|
replays the same plan id from receipt/trash evidence, and completes the
|
|
1056
1129
|
receipt idempotently.
|
|
1130
|
+
- CLI can dispose one reviewed record through `artshelf dispose`: `--dry-run`
|
|
1131
|
+
creates or reuses an exact reviewed plan for trash-resolve, resolve-only,
|
|
1132
|
+
snooze, or keep; `--execute` applies one plan id against one ledger, refuses
|
|
1133
|
+
`--all`, stale state, and target conflicts, writes a receipt, and never
|
|
1134
|
+
physically deletes.
|
|
1057
1135
|
- CLI can list trashed records (single ledger or `--all`) and purge them through
|
|
1058
1136
|
an approval-first, ledger-scoped dry-run/execute boundary that writes a purge
|
|
1059
1137
|
receipt; purge refuses `--all` and never deletes without a reviewed plan id.
|
|
@@ -1071,14 +1149,14 @@ only dry-run and report plans for later human review.
|
|
|
1071
1149
|
- Package includes the deterministic `ArtshelfReviewReport` schema, canonical
|
|
1072
1150
|
example, and portable renderer script for agent-rendered review reports.
|
|
1073
1151
|
- All core commands support `--json`.
|
|
1074
|
-
- `review`, `status`, `doctor`, `ledgers prune --dry-run`,
|
|
1075
|
-
also support `--agent`, a compact single-line JSON decision
|
|
1076
|
-
that takes precedence over `--json`.
|
|
1152
|
+
- `review`, `status`, `doctor`, `ledgers prune --dry-run`, `dispose --dry-run`,
|
|
1153
|
+
and `get --inspect` also support `--agent`, a compact single-line JSON decision
|
|
1154
|
+
packet for agents that takes precedence over `--json`.
|
|
1077
1155
|
- Tests cover record/list/find/get/status-filter/due/validate/resolve/registry,
|
|
1078
1156
|
`artshelf doctor`, the `artshelf status` dashboard, `--all` review, stale-registry,
|
|
1079
1157
|
dry-run, global-dry-run, execute-plan, cleanup plan-id validation, concurrent
|
|
1080
1158
|
ledger writes, trash list/purge, path provenance validation, registry-prune,
|
|
1081
|
-
and
|
|
1159
|
+
reconcile dry-run/execute, and dispose dry-run/execute behavior.
|
|
1082
1160
|
|
|
1083
1161
|
## Deferred
|
|
1084
1162
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { createDisposePlan, executeDisposePlan } from "../dispose.js";
|
|
2
|
+
import { printCompactJson, printJson } from "../renderers/json.js";
|
|
3
|
+
import { boolFlag, requiredStringFlag, stringFlag } from "../shared/flags.js";
|
|
4
|
+
export function handleDispose(parsed, ledgerPath, json) {
|
|
5
|
+
const dryRun = boolFlag(parsed, "dry-run");
|
|
6
|
+
const execute = boolFlag(parsed, "execute");
|
|
7
|
+
if (dryRun && execute)
|
|
8
|
+
throw new Error("dispose accepts either --dry-run or --execute, not both");
|
|
9
|
+
if (boolFlag(parsed, "all"))
|
|
10
|
+
throw new Error("dispose is scoped to one --ledger and one reviewed plan; --all is not supported");
|
|
11
|
+
if (execute) {
|
|
12
|
+
const planId = requiredStringFlag(parsed, "plan-id");
|
|
13
|
+
const execution = executeDisposePlan(ledgerPath, planId);
|
|
14
|
+
if (json) {
|
|
15
|
+
printJson({ ok: execution.result.status !== "skipped", ledgerPath, execution });
|
|
16
|
+
return execution.result.status === "skipped" ? 1 : 0;
|
|
17
|
+
}
|
|
18
|
+
printDisposeExecution(execution, ledgerPath);
|
|
19
|
+
return execution.result.status === "skipped" ? 1 : 0;
|
|
20
|
+
}
|
|
21
|
+
if (!dryRun)
|
|
22
|
+
throw new Error("dispose requires --dry-run or --execute");
|
|
23
|
+
const id = requiredStringFlag(parsed, "id");
|
|
24
|
+
const action = requiredStringFlag(parsed, "action");
|
|
25
|
+
const plan = createDisposePlan(ledgerPath, {
|
|
26
|
+
id,
|
|
27
|
+
action,
|
|
28
|
+
reason: stringFlag(parsed, "reason"),
|
|
29
|
+
ttl: stringFlag(parsed, "ttl"),
|
|
30
|
+
retainUntil: stringFlag(parsed, "retain-until")
|
|
31
|
+
});
|
|
32
|
+
const approve = disposeApprovalTarget(ledgerPath, plan.planId);
|
|
33
|
+
if (boolFlag(parsed, "agent")) {
|
|
34
|
+
printCompactJson({
|
|
35
|
+
ok: plan.entry !== null,
|
|
36
|
+
command: "dispose",
|
|
37
|
+
ledgerPath,
|
|
38
|
+
id,
|
|
39
|
+
action,
|
|
40
|
+
status: plan.entry ? "ready-for-approval" : "blocked",
|
|
41
|
+
planId: plan.entry ? plan.planId : null,
|
|
42
|
+
approve,
|
|
43
|
+
blocked: plan.blocked
|
|
44
|
+
});
|
|
45
|
+
return plan.entry ? 0 : 1;
|
|
46
|
+
}
|
|
47
|
+
if (json) {
|
|
48
|
+
printJson({ ok: plan.entry !== null, ledgerPath, plan, approve });
|
|
49
|
+
return plan.entry ? 0 : 1;
|
|
50
|
+
}
|
|
51
|
+
printDisposePlan(plan, ledgerPath, approve);
|
|
52
|
+
return plan.entry ? 0 : 1;
|
|
53
|
+
}
|
|
54
|
+
function disposeApprovalTarget(ledgerPath, planId) {
|
|
55
|
+
if (planId === "not-created")
|
|
56
|
+
return null;
|
|
57
|
+
return `approve artshelf dispose ledger ${ledgerPath} plan ${planId}`;
|
|
58
|
+
}
|
|
59
|
+
function printDisposePlan(plan, ledgerPath, approve) {
|
|
60
|
+
if (!plan.entry) {
|
|
61
|
+
process.stdout.write(`dispose plan not-created: blocked ${plan.request.id} ${plan.request.action}\n`);
|
|
62
|
+
if (plan.blocked)
|
|
63
|
+
process.stdout.write(`blocked: ${plan.blocked.reason} — ${plan.blocked.detail}\n`);
|
|
64
|
+
process.stdout.write(`ledger: ${ledgerPath}\n`);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
process.stdout.write(`dispose plan ${plan.planId}: ${plan.entry.action} ${plan.entry.id}\n`);
|
|
68
|
+
process.stdout.write(`reason: ${plan.entry.reason}\n`);
|
|
69
|
+
if (plan.entry.targetPath)
|
|
70
|
+
process.stdout.write(`target: ${plan.entry.targetPath}\n`);
|
|
71
|
+
if (plan.entry.retainUntil)
|
|
72
|
+
process.stdout.write(`retain-until: ${plan.entry.retainUntil}\n`);
|
|
73
|
+
process.stdout.write(`plan: ${plan.planPath ?? "not created"}\n`);
|
|
74
|
+
process.stdout.write(`ledger: ${ledgerPath}\n`);
|
|
75
|
+
if (approve)
|
|
76
|
+
process.stdout.write(`approve: ${approve}\n`);
|
|
77
|
+
}
|
|
78
|
+
function printDisposeExecution(execution, ledgerPath) {
|
|
79
|
+
process.stdout.write(`dispose receipt ${execution.planId}: ${execution.result.status} ${execution.result.action} ${execution.result.id}\n`);
|
|
80
|
+
if (execution.result.reason)
|
|
81
|
+
process.stdout.write(`reason: ${execution.result.reason}\n`);
|
|
82
|
+
if (execution.result.targetPath)
|
|
83
|
+
process.stdout.write(`target: ${execution.result.targetPath}\n`);
|
|
84
|
+
if (execution.result.retainUntil)
|
|
85
|
+
process.stdout.write(`retain-until: ${execution.result.retainUntil}\n`);
|
|
86
|
+
process.stdout.write(`receipt: ${execution.receiptPath}\n`);
|
|
87
|
+
process.stdout.write(`ledger: ${ledgerPath}\n`);
|
|
88
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { normalizeLedgerPath } from "../ledger.js";
|
|
2
2
|
import { boolFlag, stringFlag } from "../shared/flags.js";
|
|
3
3
|
import { handleCleanup } from "./cleanup.js";
|
|
4
|
+
import { handleDispose } from "./dispose.js";
|
|
4
5
|
import { handleDoctor } from "./doctor.js";
|
|
5
6
|
import { handleDue } from "./due.js";
|
|
6
7
|
import { handleFind } from "./find.js";
|
|
@@ -44,6 +45,9 @@ export async function runCommand(parsed) {
|
|
|
44
45
|
case "cleanup":
|
|
45
46
|
status = handleCleanup(parsed, normalizeLedgerPath(stringFlag(parsed, "ledger")), boolFlag(parsed, "json"));
|
|
46
47
|
break;
|
|
48
|
+
case "dispose":
|
|
49
|
+
status = handleDispose(parsed, normalizeLedgerPath(stringFlag(parsed, "ledger")), boolFlag(parsed, "json"));
|
|
50
|
+
break;
|
|
47
51
|
case "reconcile":
|
|
48
52
|
status = handleReconcile(parsed, normalizeLedgerPath(stringFlag(parsed, "ledger")), boolFlag(parsed, "json"));
|
|
49
53
|
break;
|