@@ -154,7 +154,7 @@ public function run( array $input ): array|\WP_Error {
154154 'offset ' => $ step_stage === $ stage ? $ offset : 0 ,
155155 )
156156 );
157- $ step = $ this ->drain_pages ($ step_config ['ability ' ], $ step_input , $ apply , $ deadline );
157+ $ step = $ this ->drain_pages ($ step_config ['ability ' ], $ step_input , $ apply , $ deadline, $ active_no_signal_drain );
158158 if ( is_wp_error ($ step ) ) {
159159 return $ step ;
160160 }
@@ -174,8 +174,12 @@ public function run( array $input ): array|\WP_Error {
174174 return $ bounded ;
175175 }
176176 $ result ['evidence ' ]['budget_exhausted ' ] = $ this ->budget_expired ($ deadline );
177- $ result ['continuation ' ] = $ this ->build_continuation ($ step_stage , $ step , $ limit , $ passes , $ force , $ until_budget , $ active_no_signal_drain );
178- $ result ['next_commands ' ][] = (string ) $ result ['continuation ' ]['next_command ' ];
177+ if ( ! empty ($ step ['adaptive_stop ' ]) && is_array ($ step ['adaptive_stop ' ]) ) {
178+ $ result ['evidence ' ]['adaptive_stop ' ] = $ step ['adaptive_stop ' ];
179+ $ result ['summary ' ]['stop_reason ' ] = (string ) ( $ step ['adaptive_stop ' ]['reason ' ] ?? 'no_progress_in_stage ' );
180+ }
181+ $ result ['continuation ' ] = $ this ->build_continuation ($ step_stage , $ step , $ limit , $ passes , $ force , $ until_budget , $ active_no_signal_drain );
182+ $ result ['next_commands ' ][] = (string ) $ result ['continuation ' ]['next_command ' ];
179183 break 2 ;
180184 }
181185 }
@@ -524,6 +528,7 @@ private function build_continuation( string $stage, array $step, int $limit, int
524528 $ restart = ! empty ($ pagination ['partial ' ]) && $ next_offset <= $ current && $ mutated > 0 ;
525529 $ command_offset = $ restart ? 0 : $ next_offset ;
526530 $ command = $ this ->build_continuation_command ($ stage , $ command_offset , $ limit , $ passes , $ force , $ until_budget , $ active_no_signal_drain );
531+ $ adaptive = (array ) ( $ step ['adaptive_stop ' ] ?? array () );
527532
528533 $ continuation = array (
529534 'stage ' => $ stage ,
@@ -549,6 +554,13 @@ private function build_continuation( string $stage, array $step, int $limit, int
549554 );
550555 $ continuation ['next_command_label ' ] = 'Restart this stage from offset 0 because the cleanup candidate set changed. ' ;
551556 }
557+ if ( ! empty ($ adaptive ) ) {
558+ $ continuation ['reason ' ] = (string ) ( $ adaptive ['reason ' ] ?? 'no_progress_in_stage ' );
559+ $ continuation ['reason_description ' ] = (string ) ( $ adaptive ['reason_description ' ] ?? 'The previous stage page scanned rows but produced no cleanup mutations, so the drain stopped before spending more budget on low-yield pages. ' );
560+ $ continuation ['recommendation ' ] = (string ) ( $ adaptive ['recommendation ' ] ?? 'Stop this drain for now, or run next_command to continue this stage from the next page if you want a deeper scan. ' );
561+ $ continuation ['progress_delta ' ] = (array ) ( $ adaptive ['progress_delta ' ] ?? array () );
562+ $ continuation ['next_command_label ' ] = 'Continue this stage despite the no-progress adaptive stop. ' ;
563+ }
552564
553565 return $ continuation ;
554566 }
@@ -569,7 +581,7 @@ private function build_continuation_command( string $stage, int $offset, int $li
569581 return sprintf ('studio wp datamachine-code workspace worktree %s --apply%s --stage=%s --offset=%d --limit=%d --passes=%d%s --format=json ' , $ operation , $ force ? ' --force ' : '' , $ stage , $ offset , $ limit , $ passes , '' !== $ until_budget ? ' --until-budget= ' . $ until_budget : '' );
570582 }
571583
572- private function drain_pages ( object $ ability , array $ base_input , bool $ apply , ?float $ deadline = null ): array |\WP_Error {
584+ private function drain_pages ( object $ ability , array $ base_input , bool $ apply , ?float $ deadline = null , bool $ stop_on_no_progress = false ): array |\WP_Error {
573585 $ pages = array ();
574586 $ summary = array ();
575587 $ pagination = array ();
@@ -609,6 +621,7 @@ private function drain_pages( object $ability, array $base_input, bool $apply, ?
609621
610622 $ next_offset = isset ($ pagination ['next_offset ' ]) ? (int ) $ pagination ['next_offset ' ] : null ;
611623 $ mutation_count = (int ) ( $ result ['summary ' ]['written ' ] ?? 0 ) + (int ) ( $ result ['summary ' ]['removed ' ] ?? 0 );
624+ $ inspected = (int ) ( $ result ['summary ' ]['inspected ' ] ?? $ result ['summary ' ]['processed ' ] ?? 0 );
612625 if ( null === $ next_offset || ( $ next_offset <= $ offset && ( $ mutation_count <= 0 || empty ($ pagination ['partial ' ]) ) ) ) {
613626 break ;
614627 }
@@ -618,6 +631,23 @@ private function drain_pages( object $ability, array $base_input, bool $apply, ?
618631 break ;
619632 }
620633
634+ if ( $ stop_on_no_progress && $ mutation_count <= 0 && $ inspected > 0 ) {
635+ $ last_result ['adaptive_stop ' ] = array (
636+ 'reason ' => 'no_progress_in_stage ' ,
637+ 'reason_description ' => 'This stage scanned a page and produced no cleanup metadata writes or removals, so the drain stopped before spending more budget on low-yield pages. ' ,
638+ 'recommendation ' => 'Stop this drain for now, or run next_command to continue this stage from the next page if you want a deeper scan. ' ,
639+ 'progress_delta ' => array (
640+ 'inspected ' => $ inspected ,
641+ 'written ' => (int ) ( $ result ['summary ' ]['written ' ] ?? 0 ),
642+ 'removed ' => (int ) ( $ result ['summary ' ]['removed ' ] ?? 0 ),
643+ 'total_mutations ' => $ mutation_count ,
644+ 'previous_offset ' => $ offset ,
645+ 'next_offset ' => $ next_offset ,
646+ ),
647+ );
648+ break ;
649+ }
650+
621651 $ offset = $ next_offset ;
622652 }
623653
0 commit comments