@@ -654,9 +654,9 @@ public function adopt_repo( array $args, array $assoc_args ): void {
654654 * # Review artifact cleanup synchronously (bounded; default limit=100)
655655 * wp datamachine-code workspace cleanup run --mode=artifacts --dry-run
656656 *
657- * # Walk a huge workspace in 100-worktree pages
658- * wp datamachine-code workspace cleanup run --mode=artifacts --dry-run --offset=0 -- format=json
659- * wp datamachine-code workspace cleanup run --mode=artifacts --dry- run --offset=100 --format=json
657+ * # Persist a snapshot-safe artifact cleanup plan, then apply it by run ID
658+ * wp datamachine-code workspace cleanup run --mode=artifacts --dry-run --format=json
659+ * wp datamachine-code workspace cleanup apply cleanup- run-20260504120000-abc123
660660 *
661661 * # Full audit (slow on huge workspaces)
662662 * wp datamachine-code workspace cleanup run --mode=artifacts --dry-run --exhaustive --format=json
@@ -903,17 +903,6 @@ private function cleanup_run_input( string $mode, array $assoc_args ): array {
903903 if ( isset ($ assoc_args ['older-than ' ]) && '' !== trim ( (string ) $ assoc_args ['older-than ' ]) ) {
904904 $ input ['older_than ' ] = trim ( (string ) $ assoc_args ['older-than ' ]);
905905 }
906- if ( 'artifacts ' === $ mode ) {
907- if ( isset ($ assoc_args ['limit ' ]) ) {
908- $ input ['limit ' ] = (int ) $ assoc_args ['limit ' ];
909- }
910- if ( isset ($ assoc_args ['offset ' ]) ) {
911- $ input ['offset ' ] = (int ) $ assoc_args ['offset ' ];
912- }
913- if ( ! empty ($ assoc_args ['exhaustive ' ]) ) {
914- $ input ['exhaustive ' ] = true ;
915- }
916- }
917906
918907 return $ input ;
919908 }
@@ -931,6 +920,29 @@ private function run_cleanup_plan( array $assoc_args ): void {
931920 return ;
932921 }
933922
923+ $ input = $ this ->cleanup_plan_input ($ mode , $ assoc_args );
924+ if ( 'json ' !== (string ) ( $ assoc_args ['format ' ] ?? 'table ' ) ) {
925+ $ profile = ! empty ($ input ['include_artifacts ' ]) ? 'includes artifact scan ' : 'local worktree merge signals ' ;
926+ WP_CLI ::log (sprintf ('Planning cleanup (%s; %s)... ' , $ mode , $ profile ));
927+ }
928+
929+ $ result = $ ability ->execute ($ input );
930+ if ( is_wp_error ($ result ) ) {
931+ WP_CLI ::error ($ result ->get_error_message ());
932+ return ;
933+ }
934+
935+ $ this ->render_cleanup_plan_result ($ result , $ assoc_args );
936+ }
937+
938+ /**
939+ * Normalize cleanup plan input shared by `cleanup plan` and dry-run `cleanup run`.
940+ *
941+ * @param string $mode Cleanup mode.
942+ * @param array $assoc_args CLI associative args.
943+ * @return array<string,mixed>
944+ */
945+ private function cleanup_plan_input ( string $ mode , array $ assoc_args ): array {
934946 $ include_artifacts = 'artifacts ' === $ mode || ! empty ($ assoc_args ['include-artifacts ' ]);
935947 $ include_worktrees = 'artifacts ' !== $ mode ;
936948 $ input = array (
@@ -945,18 +957,8 @@ private function run_cleanup_plan( array $assoc_args ): void {
945957 if ( isset ($ assoc_args ['force ' ]) ) {
946958 $ input ['force_artifact_cleanup ' ] = (bool ) $ assoc_args ['force ' ];
947959 }
948- if ( 'json ' !== (string ) ( $ assoc_args ['format ' ] ?? 'table ' ) ) {
949- $ profile = $ include_artifacts ? 'includes artifact scan ' : 'local worktree merge signals ' ;
950- WP_CLI ::log (sprintf ('Planning cleanup (%s; %s)... ' , $ mode , $ profile ));
951- }
952-
953- $ result = $ ability ->execute ($ input );
954- if ( is_wp_error ($ result ) ) {
955- WP_CLI ::error ($ result ->get_error_message ());
956- return ;
957- }
958960
959- $ this -> render_cleanup_plan_result ( $ result , $ assoc_args ) ;
961+ return $ input ;
960962 }
961963
962964 private function run_cleanup_control_ability ( string $ operation , string $ run_id , array $ assoc_args ): void {
@@ -1008,28 +1010,13 @@ private function run_cleanup_review( array $assoc_args ): void {
10081010 return ;
10091011
10101012 case 'artifacts ' :
1011- $ ability = wp_get_ability ('datamachine-code/workspace-worktree-cleanup-artifacts ' );
1012- $ artifact_input = array (
1013- 'dry_run ' => true ,
1014- 'force ' => ! empty ($ assoc_args ['force ' ]),
1015- );
1016- if ( isset ($ assoc_args ['limit ' ]) ) {
1017- $ artifact_input ['limit ' ] = (int ) $ assoc_args ['limit ' ];
1018- }
1019- if ( isset ($ assoc_args ['offset ' ]) ) {
1020- $ artifact_input ['offset ' ] = (int ) $ assoc_args ['offset ' ];
1021- }
1022- if ( ! empty ($ assoc_args ['exhaustive ' ]) ) {
1023- $ artifact_input ['exhaustive ' ] = true ;
1024- }
1025- if ( ! empty ($ assoc_args ['safety-probes ' ]) ) {
1026- $ artifact_input ['safety_probes ' ] = true ;
1027- }
1028- if ( isset ($ assoc_args ['sort ' ]) && '' !== trim ( (string ) $ assoc_args ['sort ' ]) ) {
1029- $ artifact_input ['sort ' ] = trim ( (string ) $ assoc_args ['sort ' ]);
1013+ $ ability = wp_get_ability ('datamachine-code/workspace-cleanup-plan ' );
1014+ $ result = $ ability ? $ ability ->execute ($ this ->cleanup_plan_input ($ mode , $ assoc_args )) : new \WP_Error ('cleanup_plan_ability_missing ' , 'Workspace cleanup plan ability not registered. ' );
1015+ if ( is_wp_error ($ result ) ) {
1016+ WP_CLI ::error ($ result ->get_error_message ());
1017+ return ;
10301018 }
1031- $ result = $ ability ? $ ability ->execute ($ artifact_input ) : new \WP_Error ('artifact_cleanup_ability_missing ' , 'Artifact cleanup ability not registered. ' );
1032- $ this ->render_worktree_artifact_cleanup_result_from_ability ($ result , $ assoc_args );
1019+ $ this ->render_cleanup_plan_result ($ result , $ assoc_args );
10331020 return ;
10341021
10351022 case 'emergency ' :
0 commit comments