Skip to content

Commit d0d2a18

Browse files
Make workspace hygiene size scans opt-in (#783)
* Make workspace hygiene size scans opt-in * Fix workspace hygiene lint findings --------- Co-authored-by: homeboy-ci[bot] <266378653+homeboy-ci[bot]@users.noreply.github.com>
1 parent f65a652 commit d0d2a18

9 files changed

Lines changed: 116 additions & 17 deletions

data-machine-code.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,8 +407,7 @@ function datamachine_code_load_chat_tools() {
407407
'task_params' => array(
408408
'source' => 'recurring_schedule',
409409
'include_cleanup' => true,
410-
'include_sizes' => true,
411-
'size_limit' => 200,
410+
'include_sizes' => false,
412411
),
413412
);
414413
return $schedules;

inc/Abilities/WorkspaceAbilities.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1523,7 +1523,7 @@ private function registerAbilities(): void {
15231523
),
15241524
'include_sizes' => array(
15251525
'type' => 'boolean',
1526-
'description' => 'Include best-effort top-level workspace size data. Default true.',
1526+
'description' => 'Include best-effort top-level workspace size data. Default false for huge-workspace safety.',
15271527
),
15281528
'include_worktree_status' => array(
15291529
'type' => 'boolean',
@@ -1535,7 +1535,7 @@ private function registerAbilities(): void {
15351535
),
15361536
'size_limit' => array(
15371537
'type' => 'integer',
1538-
'description' => 'Maximum top-level workspace entries to size. Default 1000.',
1538+
'description' => 'Maximum top-level workspace entries to size when include_sizes is true. Default 1000.',
15391539
),
15401540
),
15411541
),
@@ -1556,6 +1556,7 @@ private function registerAbilities(): void {
15561556
'locks' => array( 'type' => 'object' ),
15571557
'cleanup' => array( 'type' => 'object' ),
15581558
'suggested_cleanup_command' => array( 'type' => 'string' ),
1559+
'suggested_size_command' => array( 'type' => 'string' ),
15591560
'notes' => array( 'type' => 'array' ),
15601561
),
15611562
),
@@ -3838,9 +3839,8 @@ public static function workspaceCleanupRun( array $input ): array|\WP_Error {
38383839
'task_type' => 'workspace_hygiene_report',
38393840
'params' => array(
38403841
'include_cleanup' => true,
3841-
'include_sizes' => true,
3842+
'include_sizes' => false,
38423843
'include_worktree_status' => false,
3843-
'size_limit' => 200,
38443844
),
38453845
),
38463846
'artifacts' => array(

inc/Cli/Commands/WorkspaceCommand.php

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,9 +1157,8 @@ private function run_cleanup_review( array $assoc_args ): void {
11571157
$result = $ability ? $ability->execute(
11581158
array(
11591159
'include_cleanup' => true,
1160-
'include_sizes' => true,
1160+
'include_sizes' => false,
11611161
'include_worktree_status' => false,
1162-
'size_limit' => 200,
11631162
)
11641163
) : new \WP_Error('workspace_hygiene_ability_missing', 'Workspace hygiene ability not registered.');
11651164
$this->render_workspace_hygiene_report_from_ability($result, $assoc_args);
@@ -2084,8 +2083,8 @@ public function remove_repo( array $args, array $assoc_args ): void {
20842083
* [--skip-cleanup]
20852084
* : Skip the local cleanup dry-run summary.
20862085
*
2087-
* [--skip-sizes]
2088-
* : Skip best-effort workspace size collection.
2086+
* [--include-sizes]
2087+
* : Include best-effort workspace size collection. This can be expensive on huge workspaces; combine with --size-limit.
20892088
*
20902089
* [--include-worktree-status]
20912090
* : Include full per-worktree git status. This can be expensive on huge workspaces.
@@ -2115,7 +2114,7 @@ public function hygiene( array $args, array $assoc_args ): void { // phpcs:ign
21152114

21162115
$input = array(
21172116
'include_cleanup' => empty($assoc_args['skip-cleanup']),
2118-
'include_sizes' => empty($assoc_args['skip-sizes']),
2117+
'include_sizes' => ! empty($assoc_args['include-sizes']),
21192118
'include_worktree_status' => ! empty($assoc_args['include-worktree-status']),
21202119
'refresh_inventory' => ! empty($assoc_args['refresh-inventory']),
21212120
);
@@ -4848,6 +4847,12 @@ private function render_workspace_hygiene_report( array $report, array $assoc_ar
48484847
WP_CLI::log( (string) $report['suggested_cleanup_command']);
48494848
}
48504849

4850+
if ( ! empty($report['suggested_size_command']) ) {
4851+
WP_CLI::log('');
4852+
WP_CLI::log('Suggested bounded size review:');
4853+
WP_CLI::log( (string) $report['suggested_size_command']);
4854+
}
4855+
48514856
foreach ( (array) ( $report['notes'] ?? array() ) as $note ) {
48524857
WP_CLI::log('Note: ' . $note);
48534858
}

inc/Tasks/WorkspaceHygieneReportTask.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,9 @@ public function executeTask( int $jobId, array $params ): void {
9191
$result = $workspace->workspace_hygiene_report(
9292
array(
9393
'include_cleanup' => array_key_exists('include_cleanup', $params) ? (bool) $params['include_cleanup'] : true,
94-
'include_sizes' => array_key_exists('include_sizes', $params) ? (bool) $params['include_sizes'] : true,
94+
'include_sizes' => array_key_exists('include_sizes', $params) ? (bool) $params['include_sizes'] : false,
9595
'include_worktree_status' => array_key_exists('include_worktree_status', $params) ? (bool) $params['include_worktree_status'] : false,
96-
'size_limit' => isset($params['size_limit']) ? (int) $params['size_limit'] : 200,
96+
'size_limit' => isset($params['size_limit']) ? (int) $params['size_limit'] : Workspace::HYGIENE_DEFAULT_SIZE_LIMIT,
9797
)
9898
);
9999

inc/Workspace/WorkspaceHygieneReport.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,20 @@ trait WorkspaceHygieneReport {
1818
*
1919
* The report intentionally defaults to local-only cleanup detection so an
2020
* on-demand or scheduled run never depends on GitHub API availability. Size
21-
* collection is best-effort and bounded by a top-level entry limit.
21+
* collection is best-effort, bounded by a top-level entry limit, and opt-in
22+
* so the default path stays cheap on very large workspaces.
2223
*
2324
* @param array $opts {
2425
* @type bool $include_cleanup Whether to include a cleanup dry-run. Default true.
25-
* @type bool $include_sizes Whether to include best-effort `du` sizes. Default true.
26+
* @type bool $include_sizes Whether to include best-effort `du` sizes. Default false.
2627
* @type bool $include_worktree_status Whether to include full git worktree status. Default false.
2728
* @type int $size_limit Maximum top-level workspace entries to size. Default 1000.
2829
* }
2930
* @return array<string,mixed>|\WP_Error
3031
*/
3132
public function workspace_hygiene_report( array $opts = array() ): array|\WP_Error {
3233
$include_cleanup = array_key_exists('include_cleanup', $opts) ? (bool) $opts['include_cleanup'] : true;
33-
$include_sizes = array_key_exists('include_sizes', $opts) ? (bool) $opts['include_sizes'] : true;
34+
$include_sizes = array_key_exists('include_sizes', $opts) ? (bool) $opts['include_sizes'] : false;
3435
$include_worktree_status = array_key_exists('include_worktree_status', $opts) ? (bool) $opts['include_worktree_status'] : false;
3536
$refresh_inventory = ! empty($opts['refresh_inventory']);
3637
$size_limit = isset($opts['size_limit']) ? max(0, (int) $opts['size_limit']) : self::HYGIENE_DEFAULT_SIZE_LIMIT;
@@ -100,10 +101,11 @@ public function workspace_hygiene_report( array $opts = array() ): array|\WP_Err
100101
'locks' => $locks,
101102
'cleanup' => $this->summarize_workspace_cleanup($cleanup, $cleanup_error, (array) ( $size_report['entries'] ?? array() )),
102103
'suggested_cleanup_command' => 'wp datamachine-code workspace worktree cleanup --dry-run --inventory-only --skip-github --format=json',
104+
'suggested_size_command' => 'wp datamachine-code workspace hygiene --include-sizes --size-limit=100 --format=json',
103105
'notes' => array_values(
104106
array_filter(
105107
array(
106-
$include_sizes ? (string) ( $size_report['mode_note'] ?? '' ) : 'Size scan disabled by request.',
108+
$include_sizes ? (string) ( $size_report['mode_note'] ?? '' ) : 'Size scan skipped by default for large-workspace safety; pass --include-sizes with --size-limit for a bounded size pass.',
107109
$include_worktree_status ? 'Full worktree status enabled; this may run git status across every worktree.' : 'Worktree status uses cheap top-level inventory; pass --include-worktree-status for full git status.',
108110
$include_cleanup ? 'Cleanup summary uses inventory-only dry-run detection (--inventory-only --skip-github); no per-worktree git probes or GitHub API lookups are required.' : 'Cleanup dry-run disabled by request.',
109111
! empty($worktree_summary['stale_primaries']) ? 'One or more primary checkouts are behind their configured upstream according to local remote refs; refresh before using a primary for verification.' : '',

phpstan.neon

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
parameters:
22
scanFiles:
33
- stubs/phpstan/datamachine-base-command.php
4+
- stubs/phpstan/datamachine-plugin-settings.php
5+
- stubs/phpstan/datamachine-system-task.php
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
/**
3+
* PHPStan stub for Data Machine plugin settings.
4+
*
5+
* @package DataMachineCode\Stubs
6+
*/
7+
8+
namespace DataMachine\Core;
9+
10+
defined('ABSPATH') || exit;
11+
12+
/**
13+
* Static-analysis shape for the external Data Machine PluginSettings class.
14+
*/
15+
class PluginSettings {
16+
17+
/**
18+
* @param mixed $fallback Fallback value returned when the setting is unset.
19+
* @return mixed
20+
*/
21+
public static function get( string $key, mixed $fallback = null ): mixed {
22+
return $fallback;
23+
}
24+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
/**
3+
* PHPStan stub for Data Machine system tasks.
4+
*
5+
* @package DataMachineCode\Stubs
6+
*/
7+
8+
namespace DataMachine\Engine\AI\System\Tasks;
9+
10+
defined('ABSPATH') || exit;
11+
12+
/**
13+
* Static-analysis shape for the external Data Machine SystemTask class.
14+
*/
15+
abstract class SystemTask {
16+
17+
/**
18+
* @param array<string,mixed> $result Job result payload.
19+
*/
20+
protected function completeJob( int $jobId, array $result = array() ): void {
21+
}
22+
23+
protected function failJob( int $jobId, string $message ): void {
24+
}
25+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
$root = dirname(__DIR__);
6+
7+
function workspace_hygiene_contract_assert_contains( string $needle, string $haystack, string $message ): void {
8+
if ( ! str_contains($haystack, $needle) ) {
9+
throw new RuntimeException($message . ' Missing: ' . $needle);
10+
}
11+
}
12+
13+
function workspace_hygiene_contract_assert_not_contains( string $needle, string $haystack, string $message ): void {
14+
if ( str_contains($haystack, $needle) ) {
15+
throw new RuntimeException($message . ' Unexpected: ' . $needle);
16+
}
17+
}
18+
19+
$hygiene = file_get_contents($root . '/inc/Workspace/WorkspaceHygieneReport.php');
20+
$cli = file_get_contents($root . '/inc/Cli/Commands/WorkspaceCommand.php');
21+
$task = file_get_contents($root . '/inc/Tasks/WorkspaceHygieneReportTask.php');
22+
$plugin = file_get_contents($root . '/data-machine-code.php');
23+
$ability = file_get_contents($root . '/inc/Abilities/WorkspaceAbilities.php');
24+
25+
if ( false === $hygiene || false === $cli || false === $task || false === $plugin || false === $ability ) {
26+
throw new RuntimeException('Unable to read workspace hygiene source files.');
27+
}
28+
29+
workspace_hygiene_contract_assert_contains("array_key_exists('include_sizes', \$opts) ? (bool) \$opts['include_sizes'] : false", $hygiene, 'Workspace hygiene must skip du sizing by default.');
30+
workspace_hygiene_contract_assert_contains('Size scan skipped by default for large-workspace safety', $hygiene, 'Default report must explain why size data is partial.');
31+
workspace_hygiene_contract_assert_contains('suggested_size_command', $hygiene, 'Default report must expose a continuation command for bounded sizing.');
32+
workspace_hygiene_contract_assert_contains('--include-sizes --size-limit=100 --format=json', $hygiene, 'Continuation command must keep sizing bounded.');
33+
34+
workspace_hygiene_contract_assert_contains("'include_sizes' => ! empty(\$assoc_args['include-sizes'])", $cli, 'CLI hygiene must require explicit size opt-in.');
35+
workspace_hygiene_contract_assert_contains("'include_sizes' => false", $cli, 'Cleanup-run inventory mode must not force size scans.');
36+
workspace_hygiene_contract_assert_not_contains("'include_sizes' => true", $cli, 'CLI wrappers must not opt into size scans by default.');
37+
38+
workspace_hygiene_contract_assert_contains("array_key_exists('include_sizes', \$params) ? (bool) \$params['include_sizes'] : false", $task, 'Scheduled hygiene task must skip sizing unless requested.');
39+
workspace_hygiene_contract_assert_contains("'include_sizes' => false", $plugin, 'Registered weekly hygiene schedule must remain cheap by default.');
40+
workspace_hygiene_contract_assert_contains('Default false for huge-workspace safety', $ability, 'Ability schema must document cheap size default.');
41+
42+
echo "workspace-hygiene-timeout-safe-contract: ok\n";

0 commit comments

Comments
 (0)