@@ -37,15 +37,17 @@ public function read( string $run_id, bool $include_evidence = false, bool $incl
3737 $ child_jobs = $ this ->get_cleanup_run_descendant_jobs ($ job_id );
3838 $ aggregate = $ this ->aggregate_cleanup_child_jobs ($ child_jobs );
3939 $ children = $ aggregate ['children ' ];
40- $ state = $ this ->cleanup_run_state ( (string ) ( $ job ['status ' ] ?? '' ), $ children );
40+ $ parent_status = (string ) ( $ job ['status ' ] ?? '' );
41+ $ state = $ this ->cleanup_run_state ($ parent_status , $ children , $ parent_result , $ aggregate );
4142
4243 $ children_for_output = ( $ include_evidence || $ include_details ) ? $ children : $ this ->summarize_cleanup_children ($ children );
4344 $ output = array (
4445 'success ' => true ,
4546 'state ' => $ state ,
4647 'run_id ' => $ this ->cleanup_run_id ($ job_id ),
4748 'job_id ' => $ job_id ,
48- 'status ' => in_array ($ state , array ( 'children_processing ' , 'partial_failed ' ), true ) ? $ state : ( $ job ['status ' ] ?? '' ),
49+ 'status ' => $ state ,
50+ 'parent_status ' => $ parent_status ,
4951 'created_at ' => $ job ['created_at ' ] ?? '' ,
5052 'parent_completed_at ' => $ job ['completed_at ' ] ?? '' ,
5153 'artifact_cleanup ' => $ aggregate ['artifact_cleanup ' ],
@@ -132,6 +134,7 @@ private function aggregate_cleanup_child_jobs( array $child_jobs ): array {
132134 'processing ' => 0 ,
133135 'completed ' => 0 ,
134136 'failed ' => 0 ,
137+ 'skipped ' => 0 ,
135138 'running ' => 0 ,
136139 'total ' => 0 ,
137140 'statuses ' => array (),
@@ -218,6 +221,7 @@ private function summarize_cleanup_children( array $children ): array {
218221 'processing ' => (int ) ( $ children ['processing ' ] ?? 0 ),
219222 'completed ' => (int ) ( $ children ['completed ' ] ?? 0 ),
220223 'failed ' => (int ) ( $ children ['failed ' ] ?? 0 ),
224+ 'skipped ' => (int ) ( $ children ['skipped ' ] ?? 0 ),
221225 'running ' => (int ) ( $ children ['running ' ] ?? 0 ),
222226 'total ' => (int ) ( $ children ['total ' ] ?? 0 ),
223227 'statuses ' => (array ) ( $ children ['statuses ' ] ?? array () ),
@@ -255,9 +259,9 @@ private function cleanup_run_drain_summary( int $job_id, string $state, array $c
255259 )
256260 )
257261 );
258- $ run_id = $ this ->cleanup_run_id ($ job_id );
259- $ cleanup_items = (array ) ( $ aggregate ['cleanup_items ' ] ?? array () );
260- $ commands = array (
262+ $ run_id = $ this ->cleanup_run_id ($ job_id );
263+ $ cleanup_items = (array ) ( $ aggregate ['cleanup_items ' ] ?? array () );
264+ $ commands = array (
261265 'parent ' => sprintf ('studio wp datamachine drain --job-id=%d ' , $ job_id ),
262266 'verify ' => sprintf ('studio wp datamachine-code workspace cleanup status %s --format=json ' , $ run_id ),
263267 );
@@ -266,7 +270,7 @@ private function cleanup_run_drain_summary( int $job_id, string $state, array $c
266270 }
267271
268272 return array (
269- 'needed ' => in_array ($ state , array ( 'running ' , 'children_processing ' ), true ),
273+ 'needed ' => in_array ($ state , array ( 'running ' , 'waiting_on_children ' ), true ),
270274 'commands ' => $ commands ,
271275 'active_child_job_ids ' => $ active_child_ids ,
272276 'bytes_reclaimed ' => (int ) ( $ cleanup_items ['bytes_reclaimed ' ] ?? 0 ),
@@ -478,6 +482,10 @@ private function count_cleanup_child_status( array &$children, string $status ):
478482 ++$ children ['failed ' ];
479483 return ;
480484 }
485+ if ( str_starts_with ($ status , 'skipped ' ) ) {
486+ ++$ children ['skipped ' ];
487+ return ;
488+ }
481489 if ( str_starts_with ($ status , 'completed ' ) ) {
482490 ++$ children ['completed ' ];
483491 }
@@ -546,9 +554,6 @@ private function merge_cleanup_reason_examples( array &$examples, array $rows ):
546554 private function sum_cleanup_rows_bytes ( array $ rows , array $ fields ): int {
547555 $ total = 0 ;
548556 foreach ( $ rows as $ row ) {
549- if ( ! is_array ($ row ) ) {
550- continue ;
551- }
552557 $ found = false ;
553558 foreach ( $ fields as $ field ) {
554559 if ( isset ($ row [ $ field ]) ) {
@@ -574,22 +579,69 @@ private function sum_cleanup_rows_bytes( array $rows, array $fields ): int {
574579 * @param array $children Child summary.
575580 * @return string
576581 */
577- private function cleanup_run_state ( string $ status , array $ children ): string {
582+ private function cleanup_run_state ( string $ status , array $ children, array $ parent_result , array $ aggregate ): string {
578583 $ parent_state = $ this ->cleanup_job_state ($ status );
579- if ( in_array ($ parent_state , array ( 'cancelled ' , 'partial_failure ' ), true ) ) {
584+ if ( in_array ($ parent_state , array ( 'cancelled ' , 'failed ' ), true ) ) {
580585 return $ parent_state ;
581586 }
582587
583588 if ( (int ) ( $ children ['running ' ] ?? 0 ) > 0 ) {
584- return 'children_processing ' ;
589+ return 'waiting_on_children ' ;
585590 }
586591 if ( (int ) ( $ children ['failed ' ] ?? 0 ) > 0 ) {
587- return 'partial_failed ' ;
592+ return 'failed ' ;
593+ }
594+
595+ $ child_total = (int ) ( $ children ['total ' ] ?? 0 );
596+ $ terminal_child = (int ) ( $ children ['completed ' ] ?? 0 ) + (int ) ( $ children ['skipped ' ] ?? 0 );
597+ if ( $ child_total > 0 && $ terminal_child >= $ child_total ) {
598+ return 'complete ' ;
599+ }
600+
601+ if ( 0 === $ child_total && $ this ->cleanup_parent_has_no_planned_work ($ parent_result , $ aggregate ) ) {
602+ return 'no_work ' ;
588603 }
589604
590605 return $ parent_state ;
591606 }
592607
608+ /**
609+ * Determine whether parent cleanup evidence proves there was no work to schedule.
610+ *
611+ * @param array $parent_result Parent system task result.
612+ * @param array $aggregate Child aggregate.
613+ * @return bool
614+ */
615+ private function cleanup_parent_has_no_planned_work ( array $ parent_result , array $ aggregate ): bool {
616+ if ( array () === $ parent_result ) {
617+ return false ;
618+ }
619+
620+ $ cleanup_items = (array ) ( $ aggregate ['cleanup_items ' ] ?? array () );
621+ if ( (int ) ( $ cleanup_items ['planned_rows ' ] ?? 0 ) > 0 ) {
622+ return false ;
623+ }
624+
625+ if ( array_key_exists ('chunks ' , $ parent_result ) && array () !== (array ) $ parent_result ['chunks ' ] ) {
626+ return false ;
627+ }
628+
629+ if ( isset ($ parent_result ['evidence ' ]) && is_array ($ parent_result ['evidence ' ]) && (int ) ( $ parent_result ['evidence ' ]['planned_chunks ' ] ?? 0 ) > 0 ) {
630+ return false ;
631+ }
632+
633+ if ( isset ($ parent_result ['chunk_row_counts ' ]) && is_array ($ parent_result ['chunk_row_counts ' ]) ) {
634+ foreach ( $ parent_result ['chunk_row_counts ' ] as $ count ) {
635+ if ( (int ) $ count > 0 ) {
636+ return false ;
637+ }
638+ }
639+ return true ;
640+ }
641+
642+ return array_key_exists ('chunks ' , $ parent_result ) || array_key_exists ('chunk_row_counts ' , $ parent_result );
643+ }
644+
593645 /**
594646 * Convert job status into cleanup run state.
595647 *
@@ -604,7 +656,7 @@ private function cleanup_job_state( string $status ): string {
604656 return 'cancelled ' ;
605657 }
606658 if ( str_starts_with ($ status , 'failed ' ) ) {
607- return 'partial_failure ' ;
659+ return 'failed ' ;
608660 }
609661 if ( str_starts_with ($ status , 'completed ' ) ) {
610662 return 'complete ' ;
0 commit comments