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
45 changes: 45 additions & 0 deletions inc/Cli/Commands/WorkspaceCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -3316,6 +3316,9 @@ private function merge_worktree_abandoned_blockers( array $existing, array $inco
*/
private function render_worktree_abandoned_result( array $result, array $assoc_args ): void {
if ( 'json' === (string) ( $assoc_args['format'] ?? '' ) ) {
if ( empty($assoc_args['verbose']) ) {
$result = $this->compact_worktree_abandoned_result($result);
}
$json = wp_json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
WP_CLI::log(false === $json ? '{}' : $json);
return;
Expand Down Expand Up @@ -3390,6 +3393,48 @@ private function render_worktree_abandoned_result( array $result, array $assoc_a
}
}

/**
* Trim large abandoned cleanup JSON output while preserving summary counts.
*
* @param array<string,mixed> $result Abandoned cleanup result.
* @return array<string,mixed>
*/
private function compact_worktree_abandoned_result( array $result ): array {
$blocked = (array) ( $result['blocked'] ?? array() );
if ( count($blocked) <= 25 ) {
return $result;
}

$examples_by_reason = array();
foreach ( $blocked as $row ) {
if ( ! is_array($row) ) {
continue;
}

$reason = (string) ( $row['reason_code'] ?? 'unknown' );
if ( count($examples_by_reason[ $reason ] ?? array()) >= 3 ) {
continue;
}

$examples_by_reason[ $reason ][] = array(
'handle' => (string) ( $row['handle'] ?? '' ),
'repo' => (string) ( $row['repo'] ?? '' ),
'branch' => (string) ( $row['branch'] ?? '' ),
'reason_code' => $reason,
'reason' => (string) ( $row['reason'] ?? '' ),
'unpushed' => isset($row['unpushed']) ? (int) $row['unpushed'] : null,
);
}

$result['blocked_examples'] = $examples_by_reason;
$result['evidence']['blocked_truncated'] = true;
$result['evidence']['blocked_full_rows'] = count($blocked);
$result['evidence']['blocked_full_hint'] = 'Re-run with --verbose --format=json to include full blocked rows.';
$result['blocked'] = array();

return $result;
}

/**
* Render CLI output for worktree operations.
*
Expand Down
57 changes: 45 additions & 12 deletions tests/smoke-worktree-cleanup-cli.php
Original file line number Diff line number Diff line change
Expand Up @@ -539,23 +539,13 @@ class FakeBoundedCleanupEligibleApplyAbility
{
public array $last_input = array();
public array $inputs = array();
public int $extra_skipped = 0;

public function execute( array $input ): array
{
$this->last_input = $input;
$this->inputs[] = $input;
return array(
'success' => true,
'mode' => 'bounded_cleanup_eligible_apply',
'dry_run' => ! empty($input['dry_run']),
'summary' => array(
'inspected' => 3,
'would_remove' => ! empty($input['dry_run']) ? 1 : 0,
'removed' => empty($input['dry_run']) ? 1 : 0,
'skipped' => 2,
'bytes_reclaimed' => empty($input['dry_run']) ? 4096 : 0,
),
'skipped' => array(
$skipped = array(
array(
'handle' => 'repo@dirty',
'repo' => 'repo',
Expand All @@ -576,7 +566,30 @@ public function execute( array $input ): array
'dirty' => 0,
'unpushed' => 2,
),
);
for ( $i = 0; $i < $this->extra_skipped; ++$i ) {
$skipped[] = array(
'handle' => 'repo@blocked-' . $i,
'repo' => 'repo',
'branch' => 'blocked-' . $i,
'path' => '/workspace/repo@blocked-' . $i,
'reason_code' => 0 === $i % 2 ? 'active_no_signal' : 'needs_metadata_reconcile',
'reason' => 'large blocked output fixture',
);
}

return array(
'success' => true,
'mode' => 'bounded_cleanup_eligible_apply',
'dry_run' => ! empty($input['dry_run']),
'summary' => array(
'inspected' => 3,
'would_remove' => ! empty($input['dry_run']) ? 1 : 0,
'removed' => empty($input['dry_run']) ? 1 : 0,
'skipped' => 2,
'bytes_reclaimed' => empty($input['dry_run']) ? 4096 : 0,
),
'skipped' => $skipped,
);
}
}
Expand Down Expand Up @@ -1145,6 +1158,26 @@ public function execute( array $input ): array
datamachine_code_cleanup_assert(2 === (int) ( $abandoned_json['summary']['blocked'] ?? 0 ), 'abandoned summary reports blocked rows');
datamachine_code_cleanup_assert(1 === (int) ( $abandoned_json['summary']['blocked_by_reason']['unpushed_commits'] ?? 0 ), 'abandoned preserves unpushed-commit blocker evidence');

$bounded_apply_ability->extra_skipped = 30;
WP_CLI::$logs = array();
WP_CLI::$successes = array();
$command->worktree(array( 'abandoned' ), array( 'apply' => true, 'force' => true, 'stage' => 'bounded', 'limit' => 10, 'passes' => 1, 'format' => 'json' ));
$abandoned_compact_json = json_decode(WP_CLI::$logs[0] ?? '', true);
datamachine_code_cleanup_assert(JSON_ERROR_NONE === json_last_error(), 'abandoned compact JSON output parses cleanly');
datamachine_code_cleanup_assert(32 === (int) ( $abandoned_compact_json['summary']['blocked'] ?? 0 ), 'abandoned compact JSON keeps blocked summary count');
datamachine_code_cleanup_assert(array() === ( $abandoned_compact_json['blocked'] ?? null ), 'abandoned compact JSON omits full blocked rows');
datamachine_code_cleanup_assert(true === ( $abandoned_compact_json['evidence']['blocked_truncated'] ?? false ), 'abandoned compact JSON records blocked truncation evidence');
datamachine_code_cleanup_assert(isset($abandoned_compact_json['blocked_examples']['active_no_signal'][0]['handle']), 'abandoned compact JSON includes grouped blocked examples');

WP_CLI::$logs = array();
WP_CLI::$successes = array();
$command->worktree(array( 'abandoned' ), array( 'apply' => true, 'force' => true, 'stage' => 'bounded', 'limit' => 10, 'passes' => 1, 'verbose' => true, 'format' => 'json' ));
$abandoned_verbose_json = json_decode(WP_CLI::$logs[0] ?? '', true);
datamachine_code_cleanup_assert(JSON_ERROR_NONE === json_last_error(), 'abandoned verbose JSON output parses cleanly');
datamachine_code_cleanup_assert(32 === count($abandoned_verbose_json['blocked'] ?? array()), 'abandoned verbose JSON keeps full blocked rows');
datamachine_code_cleanup_assert(! isset($abandoned_verbose_json['evidence']['blocked_truncated']), 'abandoned verbose JSON does not report truncation');
$bounded_apply_ability->extra_skipped = 0;

$reconcile_call_count = count($reconcile_metadata_ability->inputs);
WP_CLI::$logs = array();
WP_CLI::$successes = array();
Expand Down
Loading