From 787b11f50aa72cafe2019a0ddb94b48c00fd9ed2 Mon Sep 17 00:00:00 2001 From: "homeboy-ci[bot]" <266378653+homeboy-ci[bot]@users.noreply.github.com> Date: Thu, 25 Jun 2026 08:09:50 -0400 Subject: [PATCH 1/3] Compact workspace cleanup JSON output --- inc/Cli/Commands/WorkspaceCommand.php | 26 ++- inc/Cli/WorkspaceCompactOutput.php | 308 ++++++++++++++++++++++++++ tests/workspace-compact-output.php | 133 +++++++++++ 3 files changed, 465 insertions(+), 2 deletions(-) create mode 100644 inc/Cli/WorkspaceCompactOutput.php create mode 100644 tests/workspace-compact-output.php diff --git a/inc/Cli/Commands/WorkspaceCommand.php b/inc/Cli/Commands/WorkspaceCommand.php index 79a47bb..f16eec7 100644 --- a/inc/Cli/Commands/WorkspaceCommand.php +++ b/inc/Cli/Commands/WorkspaceCommand.php @@ -19,6 +19,7 @@ use DataMachine\Cli\BaseCommand; use DataMachineCode\Cli\CliResponseRenderer; use DataMachineCode\Cli\CliRepeatableOptionParser; +use DataMachineCode\Cli\WorkspaceCompactOutput; use DataMachineCode\Cleanup\CompositeCleanupRunEvidenceStore; use DataMachineCode\Cleanup\CleanupRunEvidenceStoreInterface; use DataMachineCode\Workspace\Workspace; @@ -1247,7 +1248,7 @@ private function render_cleanup_run_status( string $run_id, array $assoc_args, b return; } - $this->render_cleanup_control_result($output, $assoc_args); + $this->render_cleanup_control_result($output, $assoc_args, $evidence); } private function cleanup_run_evidence_store(): CleanupRunEvidenceStoreInterface { @@ -1321,13 +1322,16 @@ private function cleanup_run_control_job_ids( string $operation, int $job_id ): return array_values(array_unique(array_filter(array_merge(array( $job_id ), $pending_ids, $processing_ids)))); } - private function render_cleanup_control_result( array $result, array $assoc_args ): void { + private function render_cleanup_control_result( array $result, array $assoc_args, bool $full_evidence = false ): void { $result = $this->attach_current_workspace_lock_status($result); $format = (string) ( $assoc_args['format'] ?? 'table' ); if ( ! empty($assoc_args['summary']) ) { $result = $this->build_cleanup_operator_summary($result); } if ( 'json' === $format ) { + if ( empty($assoc_args['verbose']) && empty($assoc_args['summary']) && ! $full_evidence ) { + $result = WorkspaceCompactOutput::cleanup_control_result($result); + } $this->renderer()->json($result); return; } @@ -4399,6 +4403,9 @@ function ( $wt ) { */ private function render_workspace_lock_result( array $result, array $assoc_args, bool $prune ): void { if ( 'json' === (string) ( $assoc_args['format'] ?? '' ) ) { + if ( empty($assoc_args['verbose']) ) { + $result = WorkspaceCompactOutput::lock_result($result); + } $this->renderer()->json($result); return; } @@ -4638,6 +4645,9 @@ private function resolve_owner_context_session_id( array $owner_context ): strin private function render_workspace_hygiene_report( array $report, array $assoc_args ): void { $format = isset($assoc_args['format']) ? (string) $assoc_args['format'] : 'table'; if ( 'json' === $format ) { + if ( empty($assoc_args['verbose']) ) { + $report = WorkspaceCompactOutput::hygiene_report($report); + } $this->renderer()->json($report); return; } @@ -4898,6 +4908,9 @@ private function render_worktree_cleanup_result( array $result, array $assoc_arg $report = $this->filter_worktree_cleanup_report($result, $only); if ( 'json' === $format ) { + if ( empty($assoc_args['verbose']) ) { + $report = WorkspaceCompactOutput::cleanup_result($report); + } $this->renderer()->json($report); return; } @@ -5783,6 +5796,9 @@ private function render_worktree_active_no_signal_remote_clean_apply_result( arr private function render_worktree_artifact_cleanup_result( array $result, array $assoc_args ): void { $format = isset($assoc_args['format']) ? (string) $assoc_args['format'] : 'table'; if ( 'json' === $format ) { + if ( empty($assoc_args['verbose']) ) { + $result = WorkspaceCompactOutput::cleanup_result($result); + } $this->renderer()->json($result); return; } @@ -6084,6 +6100,9 @@ private function render_worktree_bounded_cleanup_eligible_apply_result( array $r */ private function render_worktree_cleanup_eligible_drain_result( array $result, array $assoc_args ): void { if ( 'json' === (string) ( $assoc_args['format'] ?? '' ) ) { + if ( empty($assoc_args['verbose']) ) { + $result = WorkspaceCompactOutput::cleanup_result($result); + } $this->renderer()->json($result); return; } @@ -6448,6 +6467,9 @@ private function compact_cleanup_row( array $row ): array { private function render_worktree_emergency_cleanup_result( array $result, array $assoc_args ): void { $format = isset($assoc_args['format']) ? (string) $assoc_args['format'] : 'table'; if ( 'json' === $format ) { + if ( empty($assoc_args['verbose']) ) { + $result = WorkspaceCompactOutput::cleanup_result($result); + } $this->renderer()->json($result); return; } diff --git a/inc/Cli/WorkspaceCompactOutput.php b/inc/Cli/WorkspaceCompactOutput.php new file mode 100644 index 0000000..f232410 --- /dev/null +++ b/inc/Cli/WorkspaceCompactOutput.php @@ -0,0 +1,308 @@ + (bool) ( $result['success'] ?? true ), + 'mode' => $result['mode'] ?? null, + 'dry_run' => isset($result['dry_run']) ? (bool) $result['dry_run'] : null, + 'destructive' => isset($result['destructive']) ? (bool) $result['destructive'] : null, + 'workspace_path' => $result['workspace_path'] ?? null, + 'generated_at' => $result['generated_at'] ?? null, + 'summary' => $summary, + 'row_counts' => self::row_counts($result), + 'blockers' => self::blocker_buckets($skipped, (array) ( $summary['skipped_by_reason'] ?? array() )), + 'bytes' => self::byte_summary($summary), + 'samples' => array( + 'candidates' => self::compact_rows($candidates), + 'removed' => self::compact_rows($removed), + 'skipped' => self::compact_rows($skipped), + ), + 'pagination' => self::compact_pagination((array) ( $result['pagination'] ?? $summary['pagination'] ?? array() )), + 'continuation' => self::compact_pagination((array) ( $result['continuation'] ?? array() )), + 'next_commands' => self::next_commands($result, $summary), + 'full_detail_hint' => 'Re-run with --verbose --format=json for full row arrays and evidence.', + ) + ); + } + + public static function cleanup_control_result( array $result ): array { + $cleanup_items = (array) ( $result['cleanup_items'] ?? $result['evidence']['cleanup_items'] ?? array() ); + $remaining = (array) ( $result['remaining_work_summary'] ?? array() ); + + return self::filter_empty( + array( + 'success' => (bool) ( $result['success'] ?? true ), + 'run_id' => $result['run_id'] ?? null, + 'job_id' => $result['job_id'] ?? null, + 'mode' => $result['mode'] ?? $result['evidence']['engine_data']['cleanup_run']['mode'] ?? null, + 'state' => $result['state'] ?? null, + 'status' => $result['status'] ?? null, + 'progress' => $result['progress'] ?? null, + 'cleanup_counts' => array( + 'planned' => (int) ( $cleanup_items['planned_rows'] ?? 0 ), + 'applied' => (int) ( $cleanup_items['applied_rows'] ?? 0 ), + 'skipped' => (int) ( $cleanup_items['skipped_rows'] ?? 0 ), + 'failed' => (int) ( $cleanup_items['failed_rows'] ?? 0 ), + 'bytes_reclaimed' => (int) ( $cleanup_items['bytes_reclaimed'] ?? 0 ), + ), + 'remaining_work_summary' => $remaining, + 'commands' => $result['commands'] ?? $remaining['recommended_commands'] ?? null, + 'locks' => isset($result['locks']) ? self::lock_result((array) $result['locks']) : null, + 'full_detail_hint' => 'Use workspace cleanup evidence --format=json for full evidence, or status with --verbose for detailed status.', + ) + ); + } + + public static function hygiene_report( array $report ): array { + $cleanup = (array) ( $report['cleanup'] ?? array() ); + $size = (array) ( $report['size'] ?? array() ); + + return self::filter_empty( + array( + 'success' => (bool) ( $report['success'] ?? true ), + 'generated_at' => $report['generated_at'] ?? null, + 'workspace_path' => $report['workspace_path'] ?? null, + 'destructive' => (bool) ( $report['destructive'] ?? false ), + 'fast_stats' => $report['fast_stats'] ?? null, + 'disk' => $report['disk'] ?? null, + 'inventory' => $report['inventory'] ?? null, + 'worktrees' => $report['worktrees'] ?? null, + 'worktree_status_mode' => $report['worktree_status_mode'] ?? null, + 'locks' => isset($report['locks']) ? self::lock_result((array) $report['locks']) : null, + 'cleanup' => array( + 'summary' => (array) ( $cleanup['summary'] ?? array() ), + 'biggest_candidates' => self::compact_rows((array) ( $cleanup['biggest_candidates'] ?? array() )), + ), + 'size' => array( + 'mode' => $size['mode'] ?? null, + 'total_bytes' => $size['total_bytes'] ?? null, + 'total_human' => $size['total_human'] ?? null, + 'scan_complete' => $size['scan_complete'] ?? null, + 'entry_count' => count((array) ( $size['entries'] ?? array() )), + 'top_entries' => self::compact_rows((array) ( $size['top_entries'] ?? array() )), + ), + 'suggested_cleanup_command' => $report['suggested_cleanup_command'] ?? null, + 'suggested_size_command' => $report['suggested_size_command'] ?? null, + 'notes' => $report['notes'] ?? null, + 'full_detail_hint' => 'Re-run with --verbose --format=json for full hygiene arrays.', + ) + ); + } + + public static function lock_result( array $result ): array { + $status = isset($result['after']) && is_array($result['after']) ? (array) $result['after'] : $result; + $fs = (array) ( $status['filesystem'] ?? array() ); + $db = (array) ( $status['database'] ?? array() ); + + return self::filter_empty( + array( + 'success' => $result['success'] ?? null, + 'dry_run' => $result['dry_run'] ?? null, + 'active' => (int) ( $status['active'] ?? 0 ), + 'stale' => (int) ( $status['stale'] ?? 0 ), + 'database' => array( + 'total' => (int) ( $db['total'] ?? count((array) ( $db['locks'] ?? array() )) ), + 'active' => (int) ( $db['active'] ?? 0 ), + 'stale' => (int) ( $db['stale'] ?? 0 ), + 'lock_samples' => self::compact_lock_rows((array) ( $db['locks'] ?? array() )), + ), + 'filesystem' => array( + 'total' => (int) ( $fs['total'] ?? count((array) ( $fs['locks'] ?? array() )) ), + 'active' => (int) ( $fs['active'] ?? 0 ), + 'stale' => (int) ( $fs['stale'] ?? 0 ), + 'recent' => (int) ( $fs['recent'] ?? 0 ), + 'lock_samples' => self::compact_lock_rows((array) ( $fs['locks'] ?? array() )), + 'guidance' => $fs['guidance'] ?? null, + 'removed_count' => $result['filesystem']['removed_count'] ?? null, + 'skipped_count' => $result['filesystem']['skipped_count'] ?? null, + ), + 'stale_locks' => self::compact_stale_locks((array) ( $status['stale_locks'] ?? array() )), + 'recovery_guidance' => $status['recovery_guidance'] ?? null, + 'full_detail_hint' => 'Re-run with --verbose --format=json for full lock evidence arrays.', + ) + ); + } + + private static function row_counts( array $result ): array { + $counts = array(); + foreach ( array( 'candidates', 'artifact_candidates', 'worktree_candidates', 'removed', 'removed_artifacts', 'removed_worktrees', 'skipped', 'written', 'proposals', 'pass_results' ) as $key ) { + if ( isset($result[ $key ]) && is_array($result[ $key ]) ) { + $counts[ $key ] = count($result[ $key ]); + } + } + return $counts; + } + + private static function byte_summary( array $summary ): array { + $bytes = array(); + foreach ( array( 'bytes_reclaimed', 'total_size_bytes', 'artifact_size_bytes', 'worktree_size_bytes', 'removed_size_bytes' ) as $field ) { + if ( array_key_exists($field, $summary) ) { + $bytes[ $field ] = (int) $summary[ $field ]; + } + } + return $bytes; + } + + private static function blocker_buckets( array $rows, array $counts = array() ): array { + $buckets = array(); + foreach ( $counts as $reason => $count ) { + $buckets[ (string) $reason ] = array( + 'count' => (int) $count, + 'examples' => array(), + ); + } + foreach ( $rows as $row ) { + if ( ! is_array($row) ) { + continue; + } + $reason = (string) ( $row['reason_code'] ?? $row['reason'] ?? 'unknown' ); + $buckets[ $reason ] ??= array( + 'count' => 0, + 'examples' => array(), + ); + if ( ! isset($counts[ $reason ]) ) { + ++$buckets[ $reason ]['count']; + } + if ( count($buckets[ $reason ]['examples']) < 3 ) { + $buckets[ $reason ]['examples'][] = self::compact_row($row); + } + } + ksort($buckets); + return $buckets; + } + + private static function next_commands( array $result, array $summary ): array { + $commands = array_merge( + (array) ( $result['next_commands'] ?? array() ), + (array) ( $summary['next_commands'] ?? array() ), + (array) ( $summary['skipped_next_commands'] ?? array() ) + ); + foreach ( array( 'apply_command', 'next_command', 'status_command', 'suggested_cleanup_command' ) as $field ) { + if ( ! empty($result[ $field ]) ) { + $commands[] = (string) $result[ $field ]; + } + if ( ! empty($summary[ $field ]) ) { + $commands[] = (string) $summary[ $field ]; + } + } + $deduped = array(); + $seen = array(); + foreach ( $commands as $command ) { + if ( is_array($command) ) { + $key = (string) ( $command['reason_code'] ?? $command['bucket'] ?? '' ) . '|' . (string) ( $command['command'] ?? '' ) . '|' . (string) ( $command['apply'] ?? '' ); + if ( '||' === $key ) { + continue; + } + } else { + $key = (string) $command; + if ( '' === $key ) { + continue; + } + } + if ( isset($seen[ $key ]) ) { + continue; + } + $seen[ $key ] = true; + $deduped[] = $command; + } + + return $deduped; + } + + private static function compact_pagination( array $pagination ): array { + foreach ( array( 'handles', 'remaining_handles' ) as $field ) { + $handles = array_values(array_filter(array_map('strval', (array) ( $pagination[ $field ] ?? array() )))); + if ( array() === $handles ) { + unset($pagination[ $field ]); + continue; + } + $pagination[ $field . '_count' ] = count($handles); + $pagination[ $field . '_examples' ] = array_slice($handles, 0, self::ROW_SAMPLE_LIMIT); + $pagination[ $field . '_truncated' ] = count($handles) > self::ROW_SAMPLE_LIMIT; + unset($pagination[ $field ]); + } + return $pagination; + } + + private static function compact_stale_locks( array $report ): array { + if ( array() === $report ) { + return array(); + } + return self::filter_empty( + array( + 'count' => (int) ( $report['count'] ?? 0 ), + 'database_count' => (int) ( $report['database_count'] ?? count((array) ( $report['database'] ?? array() )) ), + 'filesystem_count' => (int) ( $report['filesystem_count'] ?? count((array) ( $report['filesystem'] ?? array() )) ), + 'preview_command' => $report['preview_command'] ?? null, + 'apply_command' => $report['apply_command'] ?? null, + 'safety' => $report['safety'] ?? null, + 'database_samples' => self::compact_lock_rows((array) ( $report['database'] ?? array() )), + 'filesystem_samples' => self::compact_lock_rows((array) ( $report['filesystem'] ?? array() )), + ) + ); + } + + private static function compact_lock_rows( array $rows ): array { + return array_map( + static function ( $row ): array { + $row = (array) $row; + return self::filter_empty( + array( + 'lock_key' => $row['lock_key'] ?? null, + 'scope' => $row['scope'] ?? null, + 'state' => $row['state'] ?? $row['status'] ?? null, + 'owner' => $row['owner'] ?? null, + 'age_seconds' => $row['age_seconds'] ?? null, + 'safe_to_prune' => $row['safe_to_prune'] ?? null, + 'live_flock_present' => $row['live_flock_present'] ?? null, + ) + ); + }, + array_slice($rows, 0, self::ROW_SAMPLE_LIMIT) + ); + } + + private static function compact_rows( array $rows ): array { + return array_map(static fn( $row ) => self::compact_row((array) $row), array_slice($rows, 0, self::ROW_SAMPLE_LIMIT)); + } + + private static function compact_row( array $row ): array { + $compact = array( + 'handle' => $row['handle'] ?? null, + 'repo' => $row['repo'] ?? null, + 'branch' => $row['branch'] ?? null, + 'reason_code' => $row['reason_code'] ?? $row['signal'] ?? null, + 'path' => $row['path'] ?? null, + 'pr_url' => $row['pr_url'] ?? null, + ); + foreach ( array( 'size_bytes', 'artifact_size_bytes', 'bytes_reclaimed', 'dirty', 'unpushed', 'age_days', 'created_at', 'liveness' ) as $field ) { + if ( array_key_exists($field, $row) ) { + $compact[ $field ] = $row[ $field ]; + } + } + return self::filter_empty($compact); + } + + private static function filter_empty( array $data ): array { + return array_filter($data, static fn( $value ) => null !== $value && '' !== $value && array() !== $value); + } +} diff --git a/tests/workspace-compact-output.php b/tests/workspace-compact-output.php new file mode 100644 index 0000000..c493b59 --- /dev/null +++ b/tests/workspace-compact-output.php @@ -0,0 +1,133 @@ + 'repo@branch-' . $i, + 'repo' => 'repo', + 'branch' => 'branch-' . $i, + 'path' => '/tmp/repo@branch-' . $i, + 'reason_code' => 0 === $i % 2 ? 'dirty_worktree' : 'unpushed_commits', + 'reason' => str_repeat('large evidence ', 80), + 'size_bytes' => 1024 * ( $i + 1 ), + 'artifact_size_bytes' => 512 * ( $i + 1 ), + 'evidence' => array_fill(0, 20, str_repeat('x', 100)), + ); + } + return $rows; +} + +$large_rows = compact_output_large_rows(40); +$cleanup = WorkspaceCompactOutput::cleanup_result( + array( + 'success' => true, + 'dry_run' => true, + 'candidates' => $large_rows, + 'skipped' => $large_rows, + 'summary' => array( + 'would_remove' => 40, + 'skipped' => 40, + 'total_size_bytes' => 123456, + 'artifact_size_bytes' => 654321, + 'skipped_by_reason' => array( + 'dirty_worktree' => 20, + 'unpushed_commits' => 20, + ), + 'skipped_next_commands' => array( + array( + 'reason_code' => 'dirty_worktree', + 'command' => 'git -C status --short', + ), + ), + ), + ) +); + +compact_output_assert(! isset($cleanup['candidates']), 'Compact cleanup output must omit full candidates array.'); +compact_output_assert(! isset($cleanup['skipped']), 'Compact cleanup output must omit full skipped array.'); +compact_output_assert(40 === ( $cleanup['row_counts']['candidates'] ?? null ), 'Compact cleanup output must preserve candidate count.'); +compact_output_assert(123456 === ( $cleanup['bytes']['total_size_bytes'] ?? null ), 'Compact cleanup output must preserve total bytes.'); +compact_output_assert(20 === ( $cleanup['blockers']['dirty_worktree']['count'] ?? null ), 'Compact cleanup output must preserve blocker counts.'); +compact_output_assert(count((array) ( $cleanup['samples']['skipped'] ?? array() )) <= 5, 'Compact cleanup output must sample skipped rows.'); +compact_output_assert(! empty($cleanup['next_commands']), 'Compact cleanup output must preserve next commands.'); + +$locks = WorkspaceCompactOutput::lock_result( + array( + 'active' => 2, + 'stale' => 40, + 'database' => array( + 'total' => 40, + 'active' => 1, + 'stale' => 39, + 'locks' => $large_rows, + ), + 'filesystem' => array( + 'total' => 40, + 'active' => 1, + 'stale' => 39, + 'locks' => $large_rows, + 'guidance' => array( + 'dry_run_command' => 'wp datamachine-code workspace worktree locks --prune-stale --dry-run --format=json', + ), + ), + 'stale_locks' => array( + 'count' => 40, + 'preview_command' => 'wp datamachine-code workspace worktree locks --prune-stale --dry-run --format=json', + 'apply_command' => 'wp datamachine-code workspace worktree locks --prune-stale --format=json', + 'database' => $large_rows, + 'filesystem' => $large_rows, + ), + ) +); + +compact_output_assert(40 === ( $locks['database']['total'] ?? null ), 'Compact lock output must preserve database lock count.'); +compact_output_assert(count((array) ( $locks['database']['lock_samples'] ?? array() )) <= 5, 'Compact lock output must sample database locks.'); +compact_output_assert(! isset($locks['database']['locks']), 'Compact lock output must omit full database locks array.'); +compact_output_assert('wp datamachine-code workspace worktree locks --prune-stale --format=json' === ( $locks['stale_locks']['apply_command'] ?? null ), 'Compact lock output must keep prune command.'); + +$hygiene = WorkspaceCompactOutput::hygiene_report( + array( + 'success' => true, + 'workspace_path' => '/workspace', + 'disk' => array( 'free_bytes' => 999 ), + 'worktrees' => array( 'worktrees' => 40, 'protected_dirty' => 20 ), + 'locks' => array( 'active' => 2, 'stale' => 40, 'database' => array( 'locks' => $large_rows ) ), + 'cleanup' => array( + 'summary' => array( 'would_remove' => 40, 'artifact_size_bytes' => 654321 ), + 'biggest_candidates' => $large_rows, + ), + 'size' => array( + 'total_bytes' => 123456, + 'entries' => $large_rows, + 'top_entries' => $large_rows, + ), + 'suggested_cleanup_command' => 'wp datamachine-code workspace worktree cleanup --dry-run --format=json', + ) +); + +compact_output_assert(40 === ( $hygiene['worktrees']['worktrees'] ?? null ), 'Compact hygiene output must preserve worktree counts.'); +compact_output_assert(123456 === ( $hygiene['size']['total_bytes'] ?? null ), 'Compact hygiene output must preserve size bytes.'); +compact_output_assert(40 === ( $hygiene['size']['entry_count'] ?? null ), 'Compact hygiene output must preserve size entry count.'); +compact_output_assert(count((array) ( $hygiene['cleanup']['biggest_candidates'] ?? array() )) <= 5, 'Compact hygiene output must sample cleanup candidates.'); + +echo "workspace compact output test passed.\n"; From 381deafe83987de82cb45d1ccd4edfcf3c2cd971 Mon Sep 17 00:00:00 2001 From: "homeboy-ci[bot]" <266378653+homeboy-ci[bot]@users.noreply.github.com> Date: Thu, 25 Jun 2026 08:18:58 -0400 Subject: [PATCH 2/3] Fix compact output PHPCS spacing --- inc/Cli/WorkspaceCompactOutput.php | 102 ++++++++++++++--------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/inc/Cli/WorkspaceCompactOutput.php b/inc/Cli/WorkspaceCompactOutput.php index f232410..5edf86e 100644 --- a/inc/Cli/WorkspaceCompactOutput.php +++ b/inc/Cli/WorkspaceCompactOutput.php @@ -23,22 +23,22 @@ public static function cleanup_result( array $result ): array { array( 'success' => (bool) ( $result['success'] ?? true ), 'mode' => $result['mode'] ?? null, - 'dry_run' => isset($result['dry_run']) ? (bool) $result['dry_run'] : null, - 'destructive' => isset($result['destructive']) ? (bool) $result['destructive'] : null, + 'dry_run' => isset( $result['dry_run'] ) ? (bool) $result['dry_run'] : null, + 'destructive' => isset( $result['destructive'] ) ? (bool) $result['destructive'] : null, 'workspace_path' => $result['workspace_path'] ?? null, 'generated_at' => $result['generated_at'] ?? null, 'summary' => $summary, - 'row_counts' => self::row_counts($result), - 'blockers' => self::blocker_buckets($skipped, (array) ( $summary['skipped_by_reason'] ?? array() )), - 'bytes' => self::byte_summary($summary), + 'row_counts' => self::row_counts( $result ), + 'blockers' => self::blocker_buckets( $skipped, (array) ( $summary['skipped_by_reason'] ?? array() ) ), + 'bytes' => self::byte_summary( $summary ), 'samples' => array( - 'candidates' => self::compact_rows($candidates), - 'removed' => self::compact_rows($removed), - 'skipped' => self::compact_rows($skipped), + 'candidates' => self::compact_rows( $candidates ), + 'removed' => self::compact_rows( $removed ), + 'skipped' => self::compact_rows( $skipped ), ), - 'pagination' => self::compact_pagination((array) ( $result['pagination'] ?? $summary['pagination'] ?? array() )), - 'continuation' => self::compact_pagination((array) ( $result['continuation'] ?? array() )), - 'next_commands' => self::next_commands($result, $summary), + 'pagination' => self::compact_pagination( (array) ( $result['pagination'] ?? $summary['pagination'] ?? array() ) ), + 'continuation' => self::compact_pagination( (array) ( $result['continuation'] ?? array() ) ), + 'next_commands' => self::next_commands( $result, $summary ), 'full_detail_hint' => 'Re-run with --verbose --format=json for full row arrays and evidence.', ) ); @@ -66,7 +66,7 @@ public static function cleanup_control_result( array $result ): array { ), 'remaining_work_summary' => $remaining, 'commands' => $result['commands'] ?? $remaining['recommended_commands'] ?? null, - 'locks' => isset($result['locks']) ? self::lock_result((array) $result['locks']) : null, + 'locks' => isset( $result['locks'] ) ? self::lock_result( (array) $result['locks'] ) : null, 'full_detail_hint' => 'Use workspace cleanup evidence --format=json for full evidence, or status with --verbose for detailed status.', ) ); @@ -87,18 +87,18 @@ public static function hygiene_report( array $report ): array { 'inventory' => $report['inventory'] ?? null, 'worktrees' => $report['worktrees'] ?? null, 'worktree_status_mode' => $report['worktree_status_mode'] ?? null, - 'locks' => isset($report['locks']) ? self::lock_result((array) $report['locks']) : null, + 'locks' => isset( $report['locks'] ) ? self::lock_result( (array) $report['locks'] ) : null, 'cleanup' => array( 'summary' => (array) ( $cleanup['summary'] ?? array() ), - 'biggest_candidates' => self::compact_rows((array) ( $cleanup['biggest_candidates'] ?? array() )), + 'biggest_candidates' => self::compact_rows( (array) ( $cleanup['biggest_candidates'] ?? array() ) ), ), 'size' => array( 'mode' => $size['mode'] ?? null, 'total_bytes' => $size['total_bytes'] ?? null, 'total_human' => $size['total_human'] ?? null, 'scan_complete' => $size['scan_complete'] ?? null, - 'entry_count' => count((array) ( $size['entries'] ?? array() )), - 'top_entries' => self::compact_rows((array) ( $size['top_entries'] ?? array() )), + 'entry_count' => count( (array) ( $size['entries'] ?? array() ) ), + 'top_entries' => self::compact_rows( (array) ( $size['top_entries'] ?? array() ) ), ), 'suggested_cleanup_command' => $report['suggested_cleanup_command'] ?? null, 'suggested_size_command' => $report['suggested_size_command'] ?? null, @@ -109,35 +109,35 @@ public static function hygiene_report( array $report ): array { } public static function lock_result( array $result ): array { - $status = isset($result['after']) && is_array($result['after']) ? (array) $result['after'] : $result; + $status = isset( $result['after'] ) && is_array( $result['after'] ) ? (array) $result['after'] : $result; $fs = (array) ( $status['filesystem'] ?? array() ); $db = (array) ( $status['database'] ?? array() ); return self::filter_empty( array( - 'success' => $result['success'] ?? null, - 'dry_run' => $result['dry_run'] ?? null, - 'active' => (int) ( $status['active'] ?? 0 ), - 'stale' => (int) ( $status['stale'] ?? 0 ), - 'database' => array( - 'total' => (int) ( $db['total'] ?? count((array) ( $db['locks'] ?? array() )) ), + 'success' => $result['success'] ?? null, + 'dry_run' => $result['dry_run'] ?? null, + 'active' => (int) ( $status['active'] ?? 0 ), + 'stale' => (int) ( $status['stale'] ?? 0 ), + 'database' => array( + 'total' => (int) ( $db['total'] ?? count( (array) ( $db['locks'] ?? array() ) ) ), 'active' => (int) ( $db['active'] ?? 0 ), 'stale' => (int) ( $db['stale'] ?? 0 ), - 'lock_samples' => self::compact_lock_rows((array) ( $db['locks'] ?? array() )), + 'lock_samples' => self::compact_lock_rows( (array) ( $db['locks'] ?? array() ) ), ), - 'filesystem' => array( - 'total' => (int) ( $fs['total'] ?? count((array) ( $fs['locks'] ?? array() )) ), + 'filesystem' => array( + 'total' => (int) ( $fs['total'] ?? count( (array) ( $fs['locks'] ?? array() ) ) ), 'active' => (int) ( $fs['active'] ?? 0 ), 'stale' => (int) ( $fs['stale'] ?? 0 ), 'recent' => (int) ( $fs['recent'] ?? 0 ), - 'lock_samples' => self::compact_lock_rows((array) ( $fs['locks'] ?? array() )), + 'lock_samples' => self::compact_lock_rows( (array) ( $fs['locks'] ?? array() ) ), 'guidance' => $fs['guidance'] ?? null, 'removed_count' => $result['filesystem']['removed_count'] ?? null, 'skipped_count' => $result['filesystem']['skipped_count'] ?? null, ), - 'stale_locks' => self::compact_stale_locks((array) ( $status['stale_locks'] ?? array() )), + 'stale_locks' => self::compact_stale_locks( (array) ( $status['stale_locks'] ?? array() ) ), 'recovery_guidance' => $status['recovery_guidance'] ?? null, - 'full_detail_hint' => 'Re-run with --verbose --format=json for full lock evidence arrays.', + 'full_detail_hint' => 'Re-run with --verbose --format=json for full lock evidence arrays.', ) ); } @@ -145,8 +145,8 @@ public static function lock_result( array $result ): array { private static function row_counts( array $result ): array { $counts = array(); foreach ( array( 'candidates', 'artifact_candidates', 'worktree_candidates', 'removed', 'removed_artifacts', 'removed_worktrees', 'skipped', 'written', 'proposals', 'pass_results' ) as $key ) { - if ( isset($result[ $key ]) && is_array($result[ $key ]) ) { - $counts[ $key ] = count($result[ $key ]); + if ( isset( $result[ $key ] ) && is_array( $result[ $key ] ) ) { + $counts[ $key ] = count( $result[ $key ] ); } } return $counts; @@ -155,7 +155,7 @@ private static function row_counts( array $result ): array { private static function byte_summary( array $summary ): array { $bytes = array(); foreach ( array( 'bytes_reclaimed', 'total_size_bytes', 'artifact_size_bytes', 'worktree_size_bytes', 'removed_size_bytes' ) as $field ) { - if ( array_key_exists($field, $summary) ) { + if ( array_key_exists( $field, $summary ) ) { $bytes[ $field ] = (int) $summary[ $field ]; } } @@ -171,7 +171,7 @@ private static function blocker_buckets( array $rows, array $counts = array() ): ); } foreach ( $rows as $row ) { - if ( ! is_array($row) ) { + if ( ! is_array( $row ) ) { continue; } $reason = (string) ( $row['reason_code'] ?? $row['reason'] ?? 'unknown' ); @@ -179,14 +179,14 @@ private static function blocker_buckets( array $rows, array $counts = array() ): 'count' => 0, 'examples' => array(), ); - if ( ! isset($counts[ $reason ]) ) { + if ( ! isset( $counts[ $reason ] ) ) { ++$buckets[ $reason ]['count']; } - if ( count($buckets[ $reason ]['examples']) < 3 ) { - $buckets[ $reason ]['examples'][] = self::compact_row($row); + if ( count( $buckets[ $reason ]['examples'] ) < 3 ) { + $buckets[ $reason ]['examples'][] = self::compact_row( $row ); } } - ksort($buckets); + ksort( $buckets ); return $buckets; } @@ -207,7 +207,7 @@ private static function next_commands( array $result, array $summary ): array { $deduped = array(); $seen = array(); foreach ( $commands as $command ) { - if ( is_array($command) ) { + if ( is_array( $command ) ) { $key = (string) ( $command['reason_code'] ?? $command['bucket'] ?? '' ) . '|' . (string) ( $command['command'] ?? '' ) . '|' . (string) ( $command['apply'] ?? '' ); if ( '||' === $key ) { continue; @@ -230,14 +230,14 @@ private static function next_commands( array $result, array $summary ): array { private static function compact_pagination( array $pagination ): array { foreach ( array( 'handles', 'remaining_handles' ) as $field ) { - $handles = array_values(array_filter(array_map('strval', (array) ( $pagination[ $field ] ?? array() )))); + $handles = array_values( array_filter( array_map( 'strval', (array) ( $pagination[ $field ] ?? array() ) ) ) ); if ( array() === $handles ) { unset($pagination[ $field ]); continue; } - $pagination[ $field . '_count' ] = count($handles); - $pagination[ $field . '_examples' ] = array_slice($handles, 0, self::ROW_SAMPLE_LIMIT); - $pagination[ $field . '_truncated' ] = count($handles) > self::ROW_SAMPLE_LIMIT; + $pagination[ $field . '_count' ] = count( $handles ); + $pagination[ $field . '_examples' ] = array_slice( $handles, 0, self::ROW_SAMPLE_LIMIT ); + $pagination[ $field . '_truncated' ] = count( $handles ) > self::ROW_SAMPLE_LIMIT; unset($pagination[ $field ]); } return $pagination; @@ -250,13 +250,13 @@ private static function compact_stale_locks( array $report ): array { return self::filter_empty( array( 'count' => (int) ( $report['count'] ?? 0 ), - 'database_count' => (int) ( $report['database_count'] ?? count((array) ( $report['database'] ?? array() )) ), - 'filesystem_count' => (int) ( $report['filesystem_count'] ?? count((array) ( $report['filesystem'] ?? array() )) ), + 'database_count' => (int) ( $report['database_count'] ?? count( (array) ( $report['database'] ?? array() ) ) ), + 'filesystem_count' => (int) ( $report['filesystem_count'] ?? count( (array) ( $report['filesystem'] ?? array() ) ) ), 'preview_command' => $report['preview_command'] ?? null, 'apply_command' => $report['apply_command'] ?? null, 'safety' => $report['safety'] ?? null, - 'database_samples' => self::compact_lock_rows((array) ( $report['database'] ?? array() )), - 'filesystem_samples' => self::compact_lock_rows((array) ( $report['filesystem'] ?? array() )), + 'database_samples' => self::compact_lock_rows( (array) ( $report['database'] ?? array() ) ), + 'filesystem_samples' => self::compact_lock_rows( (array) ( $report['filesystem'] ?? array() ) ), ) ); } @@ -277,12 +277,12 @@ static function ( $row ): array { ) ); }, - array_slice($rows, 0, self::ROW_SAMPLE_LIMIT) + array_slice( $rows, 0, self::ROW_SAMPLE_LIMIT ) ); } private static function compact_rows( array $rows ): array { - return array_map(static fn( $row ) => self::compact_row((array) $row), array_slice($rows, 0, self::ROW_SAMPLE_LIMIT)); + return array_map( static fn( $row ) => self::compact_row( (array) $row ), array_slice( $rows, 0, self::ROW_SAMPLE_LIMIT ) ); } private static function compact_row( array $row ): array { @@ -295,14 +295,14 @@ private static function compact_row( array $row ): array { 'pr_url' => $row['pr_url'] ?? null, ); foreach ( array( 'size_bytes', 'artifact_size_bytes', 'bytes_reclaimed', 'dirty', 'unpushed', 'age_days', 'created_at', 'liveness' ) as $field ) { - if ( array_key_exists($field, $row) ) { + if ( array_key_exists( $field, $row ) ) { $compact[ $field ] = $row[ $field ]; } } - return self::filter_empty($compact); + return self::filter_empty( $compact ); } private static function filter_empty( array $data ): array { - return array_filter($data, static fn( $value ) => null !== $value && '' !== $value && array() !== $value); + return array_filter( $data, static fn( $value ) => null !== $value && '' !== $value && array() !== $value ); } } From fa0f0711c8d27aaa498e6709649165a6b164b3a3 Mon Sep 17 00:00:00 2001 From: "homeboy-ci[bot]" <266378653+homeboy-ci[bot]@users.noreply.github.com> Date: Thu, 25 Jun 2026 08:20:37 -0400 Subject: [PATCH 3/3] Align compact output assignments --- inc/Cli/WorkspaceCompactOutput.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/Cli/WorkspaceCompactOutput.php b/inc/Cli/WorkspaceCompactOutput.php index 5edf86e..3eb32f8 100644 --- a/inc/Cli/WorkspaceCompactOutput.php +++ b/inc/Cli/WorkspaceCompactOutput.php @@ -174,7 +174,7 @@ private static function blocker_buckets( array $rows, array $counts = array() ): if ( ! is_array( $row ) ) { continue; } - $reason = (string) ( $row['reason_code'] ?? $row['reason'] ?? 'unknown' ); + $reason = (string) ( $row['reason_code'] ?? $row['reason'] ?? 'unknown' ); $buckets[ $reason ] ??= array( 'count' => 0, 'examples' => array(),