@@ -1368,7 +1368,8 @@ private function apply_worktree_cleanup_plan_candidates( array $candidates, bool
13681368
13691369 foreach ( $ candidates as $ candidate ) {
13701370 ++$ processed ;
1371- $ revalidated = $ this ->revalidate_bounded_cleanup_eligible_candidate ($ candidate , $ force , $ stale_liveness_only , $ discard_unpushed );
1371+ $ reviewed_lifecycle_snapshot = is_array ($ candidate ['metadata ' ] ?? null ) ? $ candidate ['metadata ' ] : array ();
1372+ $ revalidated = $ this ->revalidate_bounded_cleanup_eligible_candidate ($ candidate , $ force , $ stale_liveness_only , $ discard_unpushed , $ reviewed_lifecycle_snapshot );
13721373 if ( isset ($ revalidated ['skipped ' ]) ) {
13731374 $ skipped [] = $ revalidated ['skipped ' ];
13741375 continue ;
@@ -1461,15 +1462,22 @@ function () use ( $repo, $branch, $wt_path, $force, $remove_timeout_seconds ) {
14611462 *
14621463 * @param array $candidate Inventory candidate row.
14631464 * @param bool $force Allow dirty worktrees.
1464- * @return array<string,mixed>
1465- */
1466- private function revalidate_bounded_cleanup_eligible_candidate ( array $ candidate , bool $ force , bool $ stale_liveness_only = false , bool $ discard_unpushed = false ): array {
1465+ * @param bool $stale_liveness_only Limit removal to stale worktrees.
1466+ * @param bool $discard_unpushed Allow removal of reviewed unpushed commits.
1467+ * @param array<string,mixed>|null $reviewed_lifecycle_snapshot Lifecycle metadata captured in a reviewed plan.
1468+ * @return array<string,mixed>
1469+ */
1470+ private function revalidate_bounded_cleanup_eligible_candidate ( array $ candidate , bool $ force , bool $ stale_liveness_only = false , bool $ discard_unpushed = false , ?array $ reviewed_lifecycle_snapshot = null ): array {
14671471 $ handle = (string ) ( $ candidate ['handle ' ] ?? '' );
14681472 $ repo = (string ) ( $ candidate ['repo ' ] ?? '' );
14691473 $ branch = (string ) ( $ candidate ['branch ' ] ?? '' );
14701474 $ wt_path = (string ) ( $ candidate ['path ' ] ?? '' );
14711475 $ liveness = (string ) ( $ candidate ['liveness ' ] ?? '' );
14721476 $ metadata = is_array ($ candidate ['metadata ' ] ?? null ) ? $ candidate ['metadata ' ] : array ();
1477+ if ( null !== $ reviewed_lifecycle_snapshot && function_exists ('get_option ' ) ) {
1478+ $ current_metadata = WorktreeContextInjector::get_metadata ($ handle );
1479+ $ metadata = is_array ($ current_metadata ) ? $ current_metadata : array ();
1480+ }
14731481
14741482 if ( $ stale_liveness_only && 'stale ' !== $ liveness ) {
14751483 return array (
@@ -1513,7 +1521,7 @@ private function revalidate_bounded_cleanup_eligible_candidate( array $candidate
15131521 }
15141522
15151523 $ recent_activity = $ this ->worktree_cleanup_recent_activity_protection ($ metadata );
1516- if ( null !== $ recent_activity ) {
1524+ if ( null !== $ recent_activity && ( null === $ reviewed_lifecycle_snapshot || ! $ this -> worktree_cleanup_lifecycle_matches_reviewed_plan ( $ reviewed_lifecycle_snapshot , $ metadata ) ) ) {
15171525 return array (
15181526 'skipped ' => array_merge (
15191527 array (
@@ -1834,6 +1842,23 @@ private function worktree_cleanup_recent_activity_protection( array $metadata ):
18341842 );
18351843 }
18361844
1845+ /**
1846+ * Determine whether a reviewed row still has the lifecycle state the operator saw.
1847+ *
1848+ * @param array<string,mixed> $reviewed_metadata Lifecycle metadata captured in the reviewed plan.
1849+ * @param array<string,mixed> $current_metadata Current lifecycle metadata.
1850+ * @return bool
1851+ */
1852+ private function worktree_cleanup_lifecycle_matches_reviewed_plan ( array $ reviewed_metadata , array $ current_metadata ): bool {
1853+ foreach ( array ( 'finalized_at ' , 'cleanup_eligible_at ' , 'created_at ' , 'lifecycle_state ' ) as $ field ) {
1854+ if ( (string ) ( $ reviewed_metadata [ $ field ] ?? '' ) !== (string ) ( $ current_metadata [ $ field ] ?? '' ) ) {
1855+ return false ;
1856+ }
1857+ }
1858+
1859+ return true ;
1860+ }
1861+
18371862 /**
18381863 * Fail-safe open-PR protection for remote-tracking-clean cleanup rows.
18391864 *
0 commit comments