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
10 changes: 6 additions & 4 deletions inc/Tasks/WorkspaceHygieneReportTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,17 +112,19 @@ public function executeTask( int $jobId, array $params ): void {
$this->failJob($jobId, $result->get_error_message());
return;
}
$worktrees = (array) ( $result['worktrees'] ?? array() );
$cleanup = (array) ( $result['cleanup']['summary'] ?? array() );
$worktrees = (array) ( $result['worktrees'] ?? array() );
$cleanup = (array) ( $result['cleanup']['summary'] ?? array() );
$fast_counts = (array) ( $result['fast_stats']['counts'] ?? array() );
$inventory_cleanup_candidates = (int) ( $cleanup['inventory_cleanup_candidate_count'] ?? $fast_counts['cleanup_eligible_unprobed_count'] ?? 0 );
do_action(
'datamachine_log',
'info',
sprintf(
'Workspace hygiene report: %s used, %s free, %d worktree(s), %d cleanup candidate(s).',
'Workspace hygiene report: %s used, %s free, %d worktree(s), %d inventory cleanup candidate(s) pending fresh safety probes.',
$result['size']['total_human'] ?? 'unknown size',
$result['disk']['free_human'] ?? 'unknown disk',
(int) ( $worktrees['worktrees'] ?? 0 ),
(int) ( $cleanup['would_remove'] ?? 0 )
$inventory_cleanup_candidates
),
array(
'task' => $this->getTaskType(),
Expand Down
35 changes: 19 additions & 16 deletions inc/Workspace/WorkspaceWorktreeCleanupEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -1842,22 +1842,25 @@ private function build_worktree_cleanup_summary(
arsort($size_by_repo);
arsort($artifact_by_repo);

$summary = array(
'would_remove' => count($candidates),
'removed' => count($removed),
'skipped' => count($skipped),
'skipped_by_reason' => $skipped_by_reason,
'skipped_next_commands' => $this->worktree_cleanup_skipped_next_commands($skipped_by_reason),
'cleanup_buckets' => $this->worktree_cleanup_buckets(count($candidates), $candidates_by_signal, $skipped_by_reason, $candidate_bucket),
'candidates_by_signal' => $candidates_by_signal,
'stale_reasons' => $stale_reasons,
'liveness' => $liveness,
'total_size_bytes' => $total_size_bytes,
'artifact_size_bytes' => $total_artifact_bytes,
'size_by_repo' => $size_by_repo,
'artifact_size_by_repo' => $artifact_by_repo,
'top_by_size' => $this->summarize_top_worktree_rows($all_rows, 'size_bytes'),
'top_by_age' => $this->summarize_top_worktree_rows($all_rows, 'age_days'),
$candidate_count = count($candidates);
$summary = array(
'would_remove' => $candidate_count,
'inventory_cleanup_candidate_count' => WorktreeCleanupClassifier::BUCKET_CLEANUP_ELIGIBLE_UNPROBED === $candidate_bucket ? $candidate_count : 0,
'fresh_safe_removable_count' => WorktreeCleanupClassifier::BUCKET_SAFE_TO_REMOVE_NOW === $candidate_bucket ? $candidate_count : 0,
'removed' => count($removed),
'skipped' => count($skipped),
'skipped_by_reason' => $skipped_by_reason,
'skipped_next_commands' => $this->worktree_cleanup_skipped_next_commands($skipped_by_reason),
'cleanup_buckets' => $this->worktree_cleanup_buckets($candidate_count, $candidates_by_signal, $skipped_by_reason, $candidate_bucket),
'candidates_by_signal' => $candidates_by_signal,
'stale_reasons' => $stale_reasons,
'liveness' => $liveness,
'total_size_bytes' => $total_size_bytes,
'artifact_size_bytes' => $total_artifact_bytes,
'size_by_repo' => $size_by_repo,
'artifact_size_by_repo' => $artifact_by_repo,
'top_by_size' => $this->summarize_top_worktree_rows($all_rows, 'size_bytes'),
'top_by_age' => $this->summarize_top_worktree_rows($all_rows, 'age_days'),
);

if ( null !== $age_filter ) {
Expand Down
10 changes: 7 additions & 3 deletions tests/workspace-compact-output.php
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,11 @@ function compact_output_large_rows( int $count ): array {
),
),
'summary' => array(
'would_remove' => 40,
'artifact_size_bytes' => 654321,
'cleanup_buckets' => array(
'would_remove' => 40,
'inventory_cleanup_candidate_count' => 40,
'fresh_safe_removable_count' => 0,
'artifact_size_bytes' => 654321,
'cleanup_buckets' => array(
'cleanup_eligible_pending_revalidation' => 40,
'safe_to_remove_now' => 0,
),
Expand All @@ -221,6 +223,8 @@ function compact_output_large_rows( int $count ): array {
compact_output_assert(40 === ( $hygiene['worktrees']['worktrees'] ?? null ), 'Compact hygiene output must preserve worktree counts.');
compact_output_assert(40 === ( $hygiene['fast_stats']['counts']['cleanup_eligible_unprobed_count'] ?? null ), 'Compact hygiene output must label cheap cleanup candidates as unprobed.');
compact_output_assert(! isset($hygiene['fast_stats']['counts']['safe_removable_count']), 'Compact hygiene output must not expose misleading safe_removable_count for cheap inventory.');
compact_output_assert(40 === ( $hygiene['cleanup']['summary']['inventory_cleanup_candidate_count'] ?? null ), 'Compact cleanup summary must expose inventory cleanup candidates separately.');
compact_output_assert(0 === ( $hygiene['cleanup']['summary']['fresh_safe_removable_count'] ?? null ), 'Compact cleanup summary must not mark inventory cleanup candidates as fresh safe removals.');
compact_output_assert(20 === ( $hygiene['fast_stats']['counts']['inventory_known_dirty_count'] ?? null ), 'Compact hygiene output must preserve inventory-known dirty counts.');
compact_output_assert(3 === ( $hygiene['fast_stats']['counts']['inventory_known_blocker_count'] ?? null ), 'Compact hygiene output must preserve inventory-known blocker counts.');
compact_output_assert(0 === ( $hygiene['fast_stats']['counts']['fresh_probed_blocker_count'] ?? null ), 'Compact hygiene output must distinguish fresh-probed blocker counts.');
Expand Down
26 changes: 26 additions & 0 deletions tests/worktree-cleanup-candidate-classifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
require_once dirname(__DIR__) . '/inc/Workspace/WorktreeCleanupSignal.php';
require_once dirname(__DIR__) . '/inc/Workspace/WorktreeCleanupClassifier.php';
require_once dirname(__DIR__) . '/inc/Workspace/WorktreeCleanupCandidateClassifier.php';
require_once dirname(__DIR__) . '/inc/Workspace/WorkspaceWorktreeCleanupEngine.php';

use DataMachineCode\Workspace\WorktreeAgeFilter;
use DataMachineCode\Workspace\WorktreeCleanupClassifier;
Expand Down Expand Up @@ -100,4 +101,29 @@ function (): array {
$probed_buckets = WorktreeCleanupClassifier::buckets(2, array(), array());
worktree_cleanup_candidate_assert_same(2, $probed_buckets['safe_to_remove_now'], 'probed cleanup candidates keep the safe-to-remove bucket');

$engine = new class {
use DataMachineCode\Workspace\WorkspaceWorktreeCleanupEngine;

public function summary( array $candidates, string $bucket ): array {
$method = new ReflectionMethod($this, 'build_worktree_cleanup_summary');
return $method->invoke($this, $candidates, array(), array(), null, $bucket);
}

private function worktree_cleanup_skipped_next_commands( array $skipped_by_reason ): array {
return array();
}

private function summarize_top_worktree_rows( array $rows, string $field ): array {
return array();
}
};

$inventory_summary = $engine->summary(array( array( 'signal' => 'cleanup_eligible' ) ), WorktreeCleanupClassifier::BUCKET_CLEANUP_ELIGIBLE_UNPROBED);
worktree_cleanup_candidate_assert_same(1, $inventory_summary['inventory_cleanup_candidate_count'], 'inventory summary exposes cheap cleanup candidates separately');
worktree_cleanup_candidate_assert_same(0, $inventory_summary['fresh_safe_removable_count'], 'inventory summary does not label unprobed candidates fresh safe');

$fresh_summary = $engine->summary(array( array( 'signal' => 'github-merged-pr' ) ), WorktreeCleanupClassifier::BUCKET_SAFE_TO_REMOVE_NOW);
worktree_cleanup_candidate_assert_same(0, $fresh_summary['inventory_cleanup_candidate_count'], 'fresh summary does not count inventory-only candidates');
worktree_cleanup_candidate_assert_same(1, $fresh_summary['fresh_safe_removable_count'], 'fresh summary exposes freshly probed safe removals');

echo "worktree-cleanup-candidate-classifier: ok\n";
Loading