Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion inc/Workspace/CleanupRunService.php
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,22 @@ public function until_empty( array $opts = array() ): array|\WP_Error {
);

if ( 0 === $run_applied ) {
$blocked_summary = (array) ( $apply['remaining_work_summary']['skipped_by_reason'] ?? array() );
if ( ( $total_applied > 0 || $total_bytes > 0 ) && $run_skipped > 0 ) {
return $this->until_empty_result(
'completed_with_skips',
$passes,
$total_bytes,
$total_applied,
$total_skipped,
array(
'run_id' => $run_id,
'remaining_blocked_count' => $run_skipped,
'remaining_blocked_reasons' => $blocked_summary,
)
);
}

return $this->until_empty_result('no_progress', $passes, $total_bytes, $total_applied, $total_skipped, array( 'run_id' => $run_id ));
}
}
Expand Down Expand Up @@ -440,7 +456,7 @@ private function cleanup_rows_fingerprint( array $rows ): string {
*/
private function until_empty_result( string $state, array $passes, int $bytes, int $applied, int $skipped, array $extra = array() ): array {
return array(
'success' => in_array($state, array( 'completed', 'budget_exhausted', 'max_passes_reached' ), true),
'success' => in_array($state, array( 'completed', 'completed_with_skips', 'budget_exhausted', 'max_passes_reached' ), true),
'state' => $state,
'status' => $state,
'passes' => $passes,
Expand Down
72 changes: 72 additions & 0 deletions tests/smoke-cleanup-run-storage.php
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,66 @@ public function worktree_cleanup_artifacts( array $opts = array() ): array|WP_Er
}
}

class DataMachineCodeCleanupUntilEmptyBlockedWorkspace extends DataMachineCodeCleanupUntilEmptyWorkspace
{
public function workspace_cleanup_plan( array $opts = array() ): array|WP_Error
{
++$this->plan_calls;
if ($this->plan_calls > 3 ) {
return array(
'success' => true,
'mode' => 'cleanup_plan',
'plan_id' => 'cleanup-plan-empty-' . $this->plan_calls,
'rows' => array( 'artifact_cleanup' => array(), 'worktree_removal' => array(), 'resolver' => array() ),
'summary' => array( 'total_rows' => 0, 'recommended_commands' => array() ),
);
}

$blocked = 3 === $this->plan_calls;
return array(
'success' => true,
'mode' => 'cleanup_plan',
'plan_id' => 'cleanup-plan-until-blocked-' . $this->plan_calls,
'rows' => array(
'artifact_cleanup' => array(
array(
'handle' => $blocked ? 'demo@blocked-artifact' : 'demo@artifact-' . $this->plan_calls,
'repo' => 'demo',
'branch' => 'main',
'path' => '/tmp/demo@artifact-' . $this->plan_calls,
'row_type' => 'artifact_cleanup',
'reason_code' => 'profile_artifact',
'artifact_size_bytes' => 10,
'artifacts' => array( array( 'path' => 'target', 'size_bytes' => 10 ) ),
),
),
'worktree_removal' => array(),
'resolver' => array(),
),
'summary' => array( 'total_rows' => 1, 'recommended_commands' => array() ),
);
}

public function worktree_cleanup_artifacts( array $opts = array() ): array|WP_Error
{
$this->artifact_calls[] = $opts;
$removed = array();
$skipped = array();
foreach ( (array) ( $opts['apply_plan']['candidates'] ?? array() ) as $candidate ) {
if (is_array($candidate) && 'demo@blocked-artifact' === (string) ( $candidate['handle'] ?? '' ) ) {
$skipped[] = array(
'handle' => 'demo@blocked-artifact',
'reason_code' => 'dirty_worktree',
'reason' => 'working tree is dirty',
);
continue;
}
$removed[] = is_array($candidate) ? $candidate : array();
}
return array( 'removed' => $removed, 'skipped' => $skipped, 'summary' => array() );
}
}

function datamachine_code_cleanup_run_assert( bool $condition, string $message ): void
{
if (! $condition ) {
Expand Down Expand Up @@ -385,4 +445,16 @@ function datamachine_code_cleanup_run_assert( bool $condition, string $message )
datamachine_code_cleanup_run_assert(false === (bool) ( $repeat_result['success'] ?? true ), 'repeated candidate loop is not reported as clean success');
datamachine_code_cleanup_run_assert(1 === (int) ( $repeat_result['pass_count'] ?? 0 ), 'repeat detection stops before applying the repeated pass');

$GLOBALS['wpdb'] = new DataMachineCodeCleanupRunFakeWpdb();
$blocked_workspace = new DataMachineCodeCleanupUntilEmptyBlockedWorkspace();
$blocked_service = new \DataMachineCode\Workspace\CleanupRunService(new \DataMachineCode\Storage\CleanupRunRepository(), $blocked_workspace);
$blocked_result = $blocked_service->until_empty(array( 'mode' => 'artifacts', 'limit' => 1, 'max_passes' => 5 ));
datamachine_code_cleanup_run_assert('completed_with_skips' === (string) ( $blocked_result['state'] ?? '' ), 'until-empty reports completed_with_skips after earlier progress and final blocked skips');
datamachine_code_cleanup_run_assert(true === (bool) ( $blocked_result['success'] ?? false ), 'completed_with_skips is reported as top-level success');
datamachine_code_cleanup_run_assert(3 === (int) ( $blocked_result['pass_count'] ?? 0 ), 'completed_with_skips includes the blocked final pass');
datamachine_code_cleanup_run_assert(2 === (int) ( $blocked_result['applied'] ?? 0 ), 'completed_with_skips preserves cumulative applied rows');
datamachine_code_cleanup_run_assert(20 === (int) ( $blocked_result['bytes_reclaimed'] ?? 0 ), 'completed_with_skips preserves cumulative reclaimed bytes');
datamachine_code_cleanup_run_assert(1 === (int) ( $blocked_result['remaining_blocked_count'] ?? 0 ), 'completed_with_skips reports remaining blocked count');
datamachine_code_cleanup_run_assert(1 === (int) ( $blocked_result['remaining_blocked_reasons']['dirty_worktree']['count'] ?? 0 ), 'completed_with_skips reports blocked reasons from final skipped rows');

echo "DB-backed cleanup run storage smoke passed.\n";
Loading